Invalidate downstream results when stages are replaced

This commit is contained in:
2026-08-10 01:38:24 +00:00
parent df40cbec6e
commit 0d6f2dd0ce
7 changed files with 509 additions and 528 deletions

View File

@@ -171,9 +171,7 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
for _, d := range decisions {
s := d.Stage
runNames = append(runNames, s.Name())
if d.Action == stageActionSkip && !stageSucceeded(m, s.Name()) {
d.Action = stageActionRun
}
d.Action = decideStageAction(s, m, opts.Force)
if d.Action == stageActionSkip {
if validator, ok := s.(stage.ResumeValidator); ok {
@@ -186,7 +184,7 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
staleAt := nowUTC()
m.MarkStageStale(s.Name(), staleAt, validation.Reason)
invalidateDownstreamSucceededStagesWithReason(
m, s.Name(), staleAt, "upstream stage result was not resumable",
m, s.Name(), staleAt, staleReasonNotResumable,
)
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
return nil, fmt.Errorf("save manifest after resume validation for stage %q: %w", s.Name(), err)
@@ -209,6 +207,7 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
continue
}
executed = append(executed, s.Name())
priorOutcome := capturePriorStageOutcome(m, s.Name())
now := nowUTC()
runManifest.SetStageAction(s.Name(), manifest.RunStageActionRun, now)
@@ -217,6 +216,9 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
return nil, fmt.Errorf("save run manifest before stage %q: %w", s.Name(), err)
}
m.MarkStageRunning(s.Name(), now)
if opts.Force {
invalidateDownstreamSucceededStagesWithReason(m, s.Name(), now, staleReasonForcedReplacement)
}
env.Logger.Info("starting stage", "stage", s.Name())
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
return nil, fmt.Errorf("save manifest before stage %q: %w", s.Name(), err)
@@ -230,6 +232,7 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
if err != nil {
failedAt := nowUTC()
m.MarkStageFailed(s.Name(), failedAt, err.Error())
invalidateDownstreamSucceededStagesWithReason(m, s.Name(), failedAt, staleReasonFailure)
if saveErr := env.ManifestStore.Save(ctx, manifestPath, m); saveErr != nil {
return nil, fmt.Errorf("stage %q failed (%v) and manifest save failed (%v)", s.Name(), err, saveErr)
}
@@ -247,6 +250,9 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
m.MarkStageSkipped(s.Name(), skippedAt, result.SkipReason)
clearStageResultDetails(m.Stages[s.Name()])
applyStageResultToManifest(m, s.Name(), result)
if !priorOutcome.isSameSelfSkip(result.SkipReason) {
invalidateDownstreamSucceededStagesWithReason(m, s.Name(), skippedAt, staleReasonSelfSkip)
}
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
return nil, fmt.Errorf("save manifest after self-skip %q: %w", s.Name(), err)
}
@@ -265,8 +271,8 @@ func executeStages(ctx context.Context, cfg *config.Config, stages []stage.Stage
succeededAt := nowUTC()
m.MarkStageSucceeded(s.Name(), succeededAt, outputs)
applyStageResultToManifest(m, s.Name(), result)
if opts.Force {
invalidateDownstreamSucceededStages(m, s.Name(), succeededAt)
if !priorOutcome.exists || priorOutcome.status != manifest.StatusSucceeded {
invalidateDownstreamSucceededStagesWithReason(m, s.Name(), succeededAt, staleReasonChangedResult)
}
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {