mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-24 12:14:27 +02:00
fix(actions): reject jobs without runs-on (#39480)
Align job and `runs-on` validation with github.com, as implemented by the parser in https://github.com/actions/runner. A job without `runs-on` could be claimed by any runner, so a job meant for a container could run on the host. - Jobs without `runs-on` fail with `Required property is missing: runs-on`, called workflows included - Unknown job keys and callers (`uses:`) mixed with steps-only keys like `runs-on` are rejected - Empty, null and nested `runs-on` values are rejected - A `runs-on` evaluating to such a value fails only that job - Called workflows are validated at run creation, an invalid one fails the run as an invalid workflow file - Zero labels (`runs-on: []` or `{}`) never match a runner, including jobs queued before upgrading <img width="960" alt="image" src="https://github.com/user-attachments/assets/e746ce5a-b711-4e8b-aab8-81336ff53d86" /> **Behavior Change:** workflows that omit `runs-on`, use unknown job keys or mix `uses` with `runs-on` stop running until fixed. --------- Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
23 files changed
+410
-98
No files matched your search
@@ -32,40 +32,46 @@ func handleInvalidWorkflows(ctx context.Context, input *notifyInput, ref git.Ref
|
||||
if actionsConfig.IsWorkflowDisabled(entryName) {
|
||||
continue
|
||||
}
|
||||
now := timeutil.TimeStampNow()
|
||||
run := &actions_model.ActionRun{
|
||||
Title: util.EllipsisDisplayString(commit.MessageTitle(), 255), RepoID: input.Repo.ID, Repo: input.Repo, OwnerID: input.Repo.OwnerID,
|
||||
insertInvalidWorkflowRun(ctx, &actions_model.ActionRun{
|
||||
Title: commit.MessageTitle(), RepoID: input.Repo.ID, Repo: input.Repo, OwnerID: input.Repo.OwnerID,
|
||||
WorkflowID: entryName, TriggerUserID: input.Doer.ID, TriggerUser: input.Doer, Ref: ref.String(),
|
||||
CommitSHA: commit.ID.String(), Event: input.Event, TriggerEvent: string(input.Event), EventPayload: string(payload),
|
||||
WorkflowRepoID: input.Repo.ID, WorkflowCommitSHA: commit.ID.String(), Status: actions_model.StatusFailure, Started: now, Stopped: now,
|
||||
}
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if run.Index, err = db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Insert(ctx, run); err != nil {
|
||||
return err
|
||||
}
|
||||
attempt := &actions_model.ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 1, TriggerUserID: run.TriggerUserID, Status: run.Status, Started: now, Stopped: now}
|
||||
if err := db.Insert(ctx, attempt); err != nil {
|
||||
return err
|
||||
}
|
||||
run.LatestAttemptID = attempt.ID
|
||||
if err := actions_model.UpdateRun(ctx, run, "latest_attempt_id"); err != nil {
|
||||
return err
|
||||
}
|
||||
content := fmt.Sprintf("**Invalid workflow file: %s**\n\n```\n%v\n```\n", entryName, parseErr)
|
||||
return db.Insert(ctx, &actions_model.ActionRunJobSummary{
|
||||
RepoID: run.RepoID, RunID: run.ID, RunAttemptID: attempt.ID, Content: content, ContentSize: int64(len(content)), ContentType: actions_model.JobSummaryContentTypeMarkdown,
|
||||
})
|
||||
}); err != nil {
|
||||
log.Error("insert run for invalid workflow %q: %v", entryName, err)
|
||||
continue
|
||||
}
|
||||
if err := createWorkflowCommitStatus(ctx, run.Repo, run.CommitSHA, entryName+" ("+run.TriggerEvent+")", run.WorkflowID,
|
||||
commitstatus.CommitStatusFailure, run.Link(), "Invalid workflow file", false); err != nil {
|
||||
log.Error("create commit status for invalid workflow %q: %v", entryName, err)
|
||||
}
|
||||
NotifyWorkflowRunStatusUpdate(ctx, run)
|
||||
WorkflowRepoID: input.Repo.ID, WorkflowCommitSHA: commit.ID.String(),
|
||||
}, parseErr)
|
||||
}
|
||||
}
|
||||
|
||||
// insertInvalidWorkflowRun records run as failed with parseErr as its summary.
|
||||
func insertInvalidWorkflowRun(ctx context.Context, run *actions_model.ActionRun, parseErr error) {
|
||||
now := timeutil.TimeStampNow()
|
||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||
run.Status, run.Started, run.Stopped = actions_model.StatusFailure, now, now
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) (err error) {
|
||||
if run.Index, err = db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Insert(ctx, run); err != nil {
|
||||
return err
|
||||
}
|
||||
attempt := &actions_model.ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 1, TriggerUserID: run.TriggerUserID, Status: run.Status, Started: now, Stopped: now}
|
||||
if err := db.Insert(ctx, attempt); err != nil {
|
||||
return err
|
||||
}
|
||||
run.LatestAttemptID = attempt.ID
|
||||
if err := actions_model.UpdateRun(ctx, run, "latest_attempt_id"); err != nil {
|
||||
return err
|
||||
}
|
||||
content := fmt.Sprintf("**Invalid workflow file: %s**\n\n```\n%v\n```\n", run.WorkflowID, parseErr)
|
||||
return db.Insert(ctx, &actions_model.ActionRunJobSummary{
|
||||
RepoID: run.RepoID, RunID: run.ID, RunAttemptID: attempt.ID, Content: content, ContentSize: int64(len(content)), ContentType: actions_model.JobSummaryContentTypeMarkdown,
|
||||
})
|
||||
}); err != nil {
|
||||
log.Error("insert run for invalid workflow %q: %v", run.WorkflowID, err)
|
||||
return
|
||||
}
|
||||
if err := createWorkflowCommitStatus(ctx, run.Repo, run.CommitSHA, run.WorkflowID+" ("+run.TriggerEvent+")", run.WorkflowID,
|
||||
commitstatus.CommitStatusFailure, run.Link(), "Invalid workflow file", false); err != nil {
|
||||
log.Error("create commit status for invalid workflow %q: %v", run.WorkflowID, err)
|
||||
}
|
||||
NotifyWorkflowRunStatusUpdate(ctx, run)
|
||||
}
|
||||
Reference in new issue
Block a user