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 <bircni@icloud.com>
Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
Giteabot
2026-08-20 12:37:30 -07:00
committed by GitHub
parent eee37e0560
commit 883ed3b0bd
2 changed files with 26 additions and 4 deletions
+5 -4
View File
@@ -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
+21
View File
@@ -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) {