mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-03 19:58:56 +02:00
fix(actions): cap dynamic matrix expansion at MaxJobNumPerRun
A matrix built from a dependent job's runtime output (fromJson(needs.*.outputs.*)) has no inherent size bound, so a large or buggy upstream output could insert an arbitrary number of jobs in one transaction. The per-attempt AttemptJobIDs also back the job-index URLs, which must stay below MaxJobNumPerRun. Skip the expansion with a clear reason when the resulting jobs would exceed that limit. Also rename a test variable that tripped the misspell linter and clarify the deferred-placeholder commit-status comment. Co-Authored-By: Claude (Opus 4.8) <noreply@anthropic.com>
This commit is contained in:
@@ -573,9 +573,9 @@ jobs:
|
||||
func TestExpandMatrixWithNeeds(t *testing.T) {
|
||||
buildJob := func(t *testing.T, matrixYAML, runsOn string, needs []string) *Job {
|
||||
t.Helper()
|
||||
var strat Strategy
|
||||
require.NoError(t, yaml.Unmarshal([]byte(matrixYAML), &strat))
|
||||
job := &Job{Name: "build", Strategy: strat}
|
||||
var strategy Strategy
|
||||
require.NoError(t, yaml.Unmarshal([]byte(matrixYAML), &strategy))
|
||||
job := &Job{Name: "build", Strategy: strategy}
|
||||
require.NoError(t, job.RawRunsOn.Encode(runsOn))
|
||||
require.NoError(t, job.RawNeeds.Encode(needs))
|
||||
return job
|
||||
|
||||
@@ -47,7 +47,7 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action
|
||||
|
||||
for _, job := range jobs {
|
||||
// A deferred-matrix placeholder's name changes when it expands, so a status created now
|
||||
// would be orphaned; the expanded combos get theirs at expansion time.
|
||||
// would be orphaned; the job emitter creates the combos' statuses after expansion.
|
||||
if job.RawStrategy != "" && !job.IsMatrixEvaluated {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -152,14 +152,21 @@ func ReEvaluateMatrixForJobWithNeeds(ctx context.Context, job *actions_model.Act
|
||||
return nil, markMatrixAsEvaluatedAndSkip(ctx, job, "matrix expanded to no combinations")
|
||||
}
|
||||
|
||||
// Cap expansion at MaxJobNumPerRun: a runtime fromJson() value must not create unbounded jobs,
|
||||
// and the AttemptJobIDs (used in job URLs) must stay below the limit. Siblings take the IDs
|
||||
// above the current max, so that highest value is what must stay in range.
|
||||
maxAttemptJobID, err := actions_model.GetMaxAttemptJobID(ctx, job.RunID, job.RunAttemptID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get max attempt job id for job %d: %w", job.ID, err)
|
||||
}
|
||||
if maxAttemptJobID+int64(len(combos))-1 >= actions_model.MaxJobNumPerRun {
|
||||
return nil, markMatrixAsEvaluatedAndSkip(ctx, job, fmt.Sprintf("matrix expansion to %d combinations would exceed the per-run job limit of %d", len(combos), actions_model.MaxJobNumPerRun))
|
||||
}
|
||||
|
||||
// Reuse the placeholder as the first combination and insert the rest as siblings: no phantom
|
||||
// skipped job is left to poison downstream needs, and siblings inherit attempt + permissions.
|
||||
var children []*actions_model.ActionRunJob
|
||||
if err := db.WithTx(ctx, func(txCtx context.Context) error {
|
||||
maxAttemptJobID, err := actions_model.GetMaxAttemptJobID(txCtx, job.RunID, job.RunAttemptID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < len(combos); i++ {
|
||||
children = append(children, &actions_model.ActionRunJob{
|
||||
RunID: job.RunID,
|
||||
|
||||
Reference in New Issue
Block a user