0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2024-12-22 15:23:14 -05:00

Fix missing outputs for jobs with matrix (#32823)

Fix #32795

If a job uses a matrix, multiple `ActionRunJobs` may have the same
`JobID`. We need to merge the outputs of these jobs to make them
available to the jobs that need them.

(cherry picked from commit 7269130d2878d51dcdf11f7081a591f85bd493e8)

Conflicts:
	models/fixtures/action_run.yml
	models/fixtures/action_run_job.yml
  trivial context conflicts
This commit is contained in:
Zettat123 2024-12-14 10:22:30 +08:00 committed by Earl Warren
parent 4be37a986e
commit 96a7f0a3f0
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
8 changed files with 233 additions and 18 deletions

View file

@ -137,7 +137,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
if err != nil { if err != nil {
return 0, err return 0, err
} }
run.Status = aggregateJobStatus(jobs) run.Status = AggregateJobStatus(jobs)
if run.Started.IsZero() && run.Status.IsRunning() { if run.Started.IsZero() && run.Status.IsRunning() {
run.Started = timeutil.TimeStampNow() run.Started = timeutil.TimeStampNow()
} }
@ -152,7 +152,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
return affected, nil return affected, nil
} }
func aggregateJobStatus(jobs []*ActionRunJob) Status { func AggregateJobStatus(jobs []*ActionRunJob) Status {
allDone := true allDone := true
allWaiting := true allWaiting := true
hasFailure := false hasFailure := false

View file

@ -413,6 +413,25 @@
}, },
"total_commits": 0 "total_commits": 0
} }
-
id: 793
title: "job output"
repo_id: 4
owner_id: 1
workflow_id: "test.yaml"
index: 189
trigger_user_id: 1
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
- -
id: 891 id: 891
title: "update actions" title: "update actions"

View file

@ -26,6 +26,49 @@
status: 1 status: 1
started: 1683636528 started: 1683636528
stopped: 1683636626 stopped: 1683636626
-
id: 194
run_id: 793
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
name: job1 (1)
attempt: 1
job_id: job1
task_id: 49
status: 1
started: 1683636528
stopped: 1683636626
-
id: 195
run_id: 793
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
name: job1 (2)
attempt: 1
job_id: job1
task_id: 50
status: 1
started: 1683636528
stopped: 1683636626
-
id: 196
run_id: 793
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
name: job2
attempt: 1
job_id: job2
needs: [job1]
task_id: 51
status: 5
started: 1683636528
stopped: 1683636626
- -
id: 292 id: 292
run_id: 891 run_id: 891

View file

@ -57,3 +57,63 @@
log_length: 707 log_length: 707
log_size: 90179 log_size: 90179
log_expired: 0 log_expired: 0
-
id: 49
job_id: 194
attempt: 1
runner_id: 1
status: 1 # success
started: 1683636528
stopped: 1683636626
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784220
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 707
log_size: 90179
log_expired: 0
-
id: 50
job_id: 195
attempt: 1
runner_id: 1
status: 1 # success
started: 1683636528
stopped: 1683636626
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784221
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 707
log_size: 90179
log_expired: 0
-
id: 51
job_id: 196
attempt: 1
runner_id: 1
status: 6 # running
started: 1683636528
stopped: 1683636626
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 707
log_size: 90179
log_expired: 0

View file

@ -0,0 +1,20 @@
-
id: 1
task_id: 49
output_key: output_a
output_value: abc
-
id: 2
task_id: 49
output_key: output_b
output_value: ''
-
id: 3
task_id: 50
output_key: output_a
output_value: ''
-
id: 4
task_id: 50
output_key: output_b
output_value: bbb

View file

@ -0,0 +1,16 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package runner
import (
"testing"
"code.gitea.io/gitea/models/unittest"
_ "code.gitea.io/gitea/models/forgefed"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}

View file

@ -162,28 +162,56 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str
return nil, fmt.Errorf("FindRunJobs: %w", err) return nil, fmt.Errorf("FindRunJobs: %w", err)
} }
ret := make(map[string]*runnerv1.TaskNeed, len(needs)) jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
for _, job := range jobs { for _, job := range jobs {
if !needs.Contains(job.JobID) { jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
}
ret := make(map[string]*runnerv1.TaskNeed, len(needs))
for jobID, jobsWithSameID := range jobIDJobs {
if !needs.Contains(jobID) {
continue continue
} }
if job.TaskID == 0 || !job.Status.IsDone() { var jobOutputs map[string]string
// it shouldn't happen, or the job has been rerun for _, job := range jobsWithSameID {
continue if job.TaskID == 0 || !job.Status.IsDone() {
// it shouldn't happen, or the job has been rerun
continue
}
got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
if err != nil {
return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
}
outputs := make(map[string]string, len(got))
for _, v := range got {
outputs[v.OutputKey] = v.OutputValue
}
if len(jobOutputs) == 0 {
jobOutputs = outputs
} else {
jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
}
} }
outputs := make(map[string]string) ret[jobID] = &runnerv1.TaskNeed{
got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID) Outputs: jobOutputs,
if err != nil { Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)),
return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
}
for _, v := range got {
outputs[v.OutputKey] = v.OutputValue
}
ret[job.JobID] = &runnerv1.TaskNeed{
Outputs: outputs,
Result: runnerv1.Result(job.Status),
} }
} }
return ret, nil return ret, nil
} }
// mergeTwoOutputs merges two outputs from two different ActionRunJobs
// Values with the same output name may be overridden. The user should ensure the output names are unique.
// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
ret := make(map[string]string, len(o1))
for k1, v1 := range o1 {
if len(v1) > 0 {
ret[k1] = v1
} else {
ret[k1] = o2[k1]
}
}
return ret
}

View file

@ -0,0 +1,29 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package runner
import (
"context"
"testing"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_findTaskNeeds(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51})
ret, err := findTaskNeeds(context.Background(), task)
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Contains(t, ret, "job1")
assert.Len(t, ret["job1"].Outputs, 2)
assert.Equal(t, "abc", ret["job1"].Outputs["output_a"])
assert.Equal(t, "bbb", ret["job1"].Outputs["output_b"])
}