From 883ed3b0bd80a8862bcd2c5b7236d8f10dbb0bb9 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Thu, 20 Aug 2026 12:37:30 -0700 Subject: [PATCH] fix(actions): show "Complete job" logs when the last step is skipped (#38939) (#39003) Backport #38939 by @bircni `FullSteps` only gave the synthetic "Complete job" step the remaining log range when the last step that had run was also the final step of the job. A skipped step does not count as having run, so any job ending in a skipped step left the post step with an empty range: its logs were stored but never rendered, and the duration showed as `0s`. Reproducible with any job whose last step is skipped, which is common for failure notifications: ```yaml steps: - run: echo hello - run: echo never if: failure() ``` The gate now checks whether the final step is done, which preserves the behaviour from https://github.com/go-gitea/gitea/pull/29926 of showing the post step as waiting while steps are still pending. --> Regression from https://github.com/go-gitea/gitea/pull/29926 Signed-off-by: bircni Co-authored-by: bircni --- modules/actions/task_state.go | 9 +++++---- modules/actions/task_state_test.go | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/modules/actions/task_state.go b/modules/actions/task_state.go index cb7f5724fe1..457f949bf31 100644 --- a/modules/actions/task_state.go +++ b/modules/actions/task_state.go @@ -30,8 +30,9 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { // For example, // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1. // 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3. - // 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2. - // So its Stopped is the Started of postStep when there are no more steps to run. + // 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step2. + // 4. preStep(Success) -> step1(Success) -> step2(Skipped) -> postStep(Success): lastHasRunStep is step1, because a skipped step never ran. + // So its Stopped is the Started of postStep once no step is left to run. var lastHasRunStep *actions_model.ActionTaskStep var logIndex int64 @@ -71,8 +72,8 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { Name: postStepName, Status: actions_model.StatusWaiting, } - // If the lastHasRunStep is the last step, or it has failed, postStep has started. - if lastHasRunStep.Status.IsFailure() || lastHasRunStep == task.Steps[len(task.Steps)-1] { + // If no step is left to run, or the lastHasRunStep has failed, postStep has started. + if lastHasRunStep.Status.IsFailure() || task.Steps[len(task.Steps)-1].Status.IsDone() { postStep.LogIndex = logIndex postStep.LogLength = task.LogLength - postStep.LogIndex postStep.Started = lastHasRunStep.Stopped diff --git a/modules/actions/task_state_test.go b/modules/actions/task_state_test.go index 7b8087a1475..93a0d598cdc 100644 --- a/modules/actions/task_state_test.go +++ b/modules/actions/task_state_test.go @@ -156,6 +156,27 @@ func TestFullSteps(t *testing.T) { {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, }, }, + { + name: "last step is skipped", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 20, Started: 10010, Stopped: 10020}, + {Status: actions_model.StatusSuccess, LogIndex: 30, LogLength: 60, Started: 10020, Stopped: 10090}, + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + Status: actions_model.StatusSuccess, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 20, Started: 10010, Stopped: 10020}, + {Status: actions_model.StatusSuccess, LogIndex: 30, LogLength: 60, Started: 10020, Stopped: 10090}, + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {