114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/stage"
|
|
)
|
|
|
|
type stageAction string
|
|
|
|
const (
|
|
stageActionRun stageAction = "run"
|
|
stageActionSkip stageAction = "skip"
|
|
)
|
|
|
|
type stageDecision struct {
|
|
Stage stage.Stage
|
|
Action stageAction
|
|
}
|
|
|
|
func decideStageActions(stages []stage.Stage, m *manifest.Manifest, force bool) []stageDecision {
|
|
out := make([]stageDecision, 0, len(stages))
|
|
for _, s := range stages {
|
|
action := stageActionRun
|
|
// TODO: incorporate stale detection once checksum/input change tracking is implemented.
|
|
if !force && stageSucceeded(m, s.Name()) {
|
|
action = stageActionSkip
|
|
}
|
|
out = append(out, stageDecision{
|
|
Stage: s,
|
|
Action: action,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func stageSucceeded(m *manifest.Manifest, name string) bool {
|
|
if m == nil || m.Stages == nil {
|
|
return false
|
|
}
|
|
sr := m.Stages[name]
|
|
return sr != nil && sr.Status == manifest.StatusSucceeded
|
|
}
|
|
|
|
func loadManifestIfPresent(ctx context.Context, cfg *config.Config) (*manifest.Manifest, error) {
|
|
path := artifacts.SessionManifestPathForCampaign(
|
|
cfg.Pipeline.Workspace.Root,
|
|
cfg.Session.Campaign,
|
|
cfg.Session.SessionID,
|
|
)
|
|
exists, err := fileExists(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("check manifest %q: %w", path, err)
|
|
}
|
|
if !exists {
|
|
return nil, nil
|
|
}
|
|
store := &manifest.LocalStore{}
|
|
m, err := store.Load(ctx, path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load manifest %q: %w", path, err)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func canonicalStageNames() []string {
|
|
all := stage.All()
|
|
out := make([]string, 0, len(all))
|
|
for _, s := range all {
|
|
if s == nil {
|
|
continue
|
|
}
|
|
out = append(out, s.Name())
|
|
}
|
|
return out
|
|
}
|
|
|
|
func downstreamStageNames(stageName string) []string {
|
|
names := canonicalStageNames()
|
|
for i, name := range names {
|
|
if name != stageName {
|
|
continue
|
|
}
|
|
return append([]string(nil), names[i+1:]...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func invalidateDownstreamSucceededStages(m *manifest.Manifest, upstreamStage string, at time.Time) []string {
|
|
return invalidateDownstreamSucceededStagesWithReason(m, upstreamStage, at, "upstream stage rerun with force")
|
|
}
|
|
|
|
func invalidateDownstreamSucceededStagesWithReason(m *manifest.Manifest, upstreamStage string, at time.Time, reason string) []string {
|
|
if m == nil || m.Stages == nil {
|
|
return nil
|
|
}
|
|
|
|
invalidated := make([]string, 0)
|
|
for _, downstream := range downstreamStageNames(upstreamStage) {
|
|
sr := m.Stages[downstream]
|
|
if sr == nil || sr.Status != manifest.StatusSucceeded {
|
|
continue
|
|
}
|
|
m.MarkStageStale(downstream, at, reason)
|
|
invalidated = append(invalidated, downstream)
|
|
}
|
|
return invalidated
|
|
}
|