From 678a97022ad4e53a41856d64fda942f86fcfc039 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sat, 11 Oct 2025 00:48:06 +0200 Subject: [PATCH 01/14] Cleanup ActionRun creation * share more logic --- services/actions/notifier_helper.go | 60 ++-------------------------- services/actions/run.go | 59 +++++++++++++++++++++++++++ services/actions/schedule_tasks.go | 41 ++----------------- services/actions/workflow.go | 62 +---------------------------- 4 files changed, 67 insertions(+), 155 deletions(-) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index fc1894c5d889f..57f6aa6724771 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -29,7 +29,6 @@ import ( "code.gitea.io/gitea/services/convert" notify_service "code.gitea.io/gitea/services/notify" - "github.com/nektos/act/pkg/jobparser" "github.com/nektos/act/pkg/model" ) @@ -346,44 +345,8 @@ func handleWorkflows( run.NeedApproval = need - if err := run.LoadAttributes(ctx); err != nil { - log.Error("LoadAttributes: %v", err) - continue - } - - vars, err := actions_model.GetVariablesOfRun(ctx, run) - if err != nil { - log.Error("GetVariablesOfRun: %v", err) - continue - } - - wfRawConcurrency, err := jobparser.ReadWorkflowRawConcurrency(dwf.Content) - if err != nil { - log.Error("ReadWorkflowRawConcurrency: %v", err) - continue - } - if wfRawConcurrency != nil { - err = EvaluateRunConcurrencyFillModel(ctx, run, wfRawConcurrency, vars) - if err != nil { - log.Error("EvaluateRunConcurrencyFillModel: %v", err) - continue - } - } - - giteaCtx := GenerateGiteaContext(run, nil) - - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext())) - if err != nil { - log.Error("jobparser.Parse: %v", err) - continue - } - - if len(jobs) > 0 && jobs[0].RunName != "" { - run.Title = jobs[0].RunName - } - - if err := InsertRun(ctx, run, jobs); err != nil { - log.Error("InsertRun: %v", err) + if err := PrepareRun(ctx, dwf.Content, run, nil); err != nil { + log.Error("PrepareRun: %v", err) continue } @@ -392,6 +355,7 @@ func handleWorkflows( log.Error("FindRunJobs: %v", err) continue } + // FIXME PERF skip this for schedule, dispatch etc. CreateCommitStatus(ctx, alljobs...) if len(alljobs) > 0 { job := alljobs[0] @@ -559,24 +523,6 @@ func handleSchedules( Content: dwf.Content, } - vars, err := actions_model.GetVariablesOfRun(ctx, run.ToActionRun()) - if err != nil { - log.Error("GetVariablesOfRun: %v", err) - continue - } - - giteaCtx := GenerateGiteaContext(run.ToActionRun(), nil) - - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext())) - if err != nil { - log.Error("jobparser.Parse: %v", err) - continue - } - - if len(jobs) > 0 && jobs[0].RunName != "" { - run.Title = jobs[0].RunName - } - crons = append(crons, run) } diff --git a/services/actions/run.go b/services/actions/run.go index a3356d71c15d9..852daa2761b8a 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -9,12 +9,71 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" + notify_service "code.gitea.io/gitea/services/notify" "github.com/nektos/act/pkg/jobparser" "gopkg.in/yaml.v3" ) +// PrepareRun prepares a run before inserting it into the database +// It parses the workflow content, evaluates concurrency if needed, and inserts the run and its jobs into the database. +// The title will be cut off at 255 characters if it's longer than 255 characters. +func PrepareRun(ctx context.Context, content []byte, run *actions_model.ActionRun, inputsWithDefaults map[string]any) error { + if err := run.LoadAttributes(ctx); err != nil { + return fmt.Errorf("LoadAttributes: %w", err) + } + + vars, err := actions_model.GetVariablesOfRun(ctx, run) + if err != nil { + return fmt.Errorf("GetVariablesOfRun: %w", err) + } + + wfRawConcurrency, err := jobparser.ReadWorkflowRawConcurrency(content) + if err != nil { + return fmt.Errorf("ReadWorkflowRawConcurrency: %w", err) + } + + if wfRawConcurrency != nil { + err = EvaluateRunConcurrencyFillModel(ctx, run, wfRawConcurrency, vars) + if err != nil { + return fmt.Errorf("EvaluateRunConcurrencyFillModel: %w", err) + } + } + + giteaCtx := GenerateGiteaContext(run, nil) + + jobs, err := jobparser.Parse(content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext()), jobparser.WithInputs(inputsWithDefaults)) + if err != nil { + return fmt.Errorf("parse workflow: %w", err) + } + + if len(jobs) > 0 && jobs[0].RunName != "" { + run.Title = jobs[0].RunName + } + + if err := InsertRun(ctx, run, jobs); err != nil { + return fmt.Errorf("InsertRun: %w", err) + } + + allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) + if err != nil { + log.Error("FindRunJobs: %v", err) + } + err = run.LoadAttributes(ctx) + if err != nil { + log.Error("LoadAttributes: %v", err) + } + notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run) + for _, job := range allJobs { + notify_service.WorkflowJobStatusUpdate(ctx, run.Repo, run.TriggerUser, job, nil) + } + + // Return nil if no errors occurred + return nil +} + // InsertRun inserts a run // The title will be cut off at 255 characters if it's longer than 255 characters. func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobparser.SingleWorkflow) error { diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 3b37b44ac4338..17e52a5865485 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -15,9 +15,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" webhook_module "code.gitea.io/gitea/modules/webhook" - notify_service "code.gitea.io/gitea/services/notify" - - "github.com/nektos/act/pkg/jobparser" ) // StartScheduleTasks start the task @@ -119,44 +116,12 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) Status: actions_model.StatusWaiting, } - vars, err := actions_model.GetVariablesOfRun(ctx, run) - if err != nil { - log.Error("GetVariablesOfRun: %v", err) - return err - } - - // Parse the workflow specification from the cron schedule - workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) - if err != nil { - return err - } - wfRawConcurrency, err := jobparser.ReadWorkflowRawConcurrency(cron.Content) - if err != nil { - return err - } - if wfRawConcurrency != nil { - err = EvaluateRunConcurrencyFillModel(ctx, run, wfRawConcurrency, vars) - if err != nil { - return fmt.Errorf("EvaluateRunConcurrencyFillModel: %w", err) - } - } - + // FIXME cron.Content might be outdated if the workflow file has been changed. + // Load the latest sha from default branch // Insert the action run and its associated jobs into the database - if err := InsertRun(ctx, run, workflows); err != nil { + if err := PrepareRun(ctx, cron.Content, run, nil); err != nil { return err } - allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) - if err != nil { - log.Error("FindRunJobs: %v", err) - } - err = run.LoadAttributes(ctx) - if err != nil { - log.Error("LoadAttributes: %v", err) - } - notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run) - for _, job := range allJobs { - notify_service.WorkflowJobStatusUpdate(ctx, run.Repo, run.TriggerUser, job, nil) - } // Return nil if no errors occurred return nil diff --git a/services/actions/workflow.go b/services/actions/workflow.go index e3e60d496755b..1547342ff1fed 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -8,7 +8,6 @@ import ( "strings" actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" @@ -16,13 +15,11 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/reqctx" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - notify_service "code.gitea.io/gitea/services/notify" "github.com/nektos/act/pkg/jobparser" "github.com/nektos/act/pkg/model" @@ -98,9 +95,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re } // find workflow from commit - var workflows []*jobparser.SingleWorkflow var entry *git.TreeEntry - var wfRawConcurrency *model.RawConcurrency run := &actions_model.ActionRun{ Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0], @@ -153,29 +148,6 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re } } - giteaCtx := GenerateGiteaContext(run, nil) - - workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext()), jobparser.WithInputs(inputsWithDefaults)) - if err != nil { - return err - } - - if len(workflows) > 0 && workflows[0].RunName != "" { - run.Title = workflows[0].RunName - } - - if len(workflows) == 0 { - return util.ErrorWrapLocale( - util.NewNotExistErrorf("workflow %q doesn't exist", workflowID), - "actions.workflow.not_found", workflowID, - ) - } - - wfRawConcurrency, err = jobparser.ReadWorkflowRawConcurrency(content) - if err != nil { - return err - } - // ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event // https://docs.github.com/en/actions/learn-github-actions/contexts#github-context // https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch @@ -193,39 +165,9 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re } run.EventPayload = string(eventPayload) - // cancel running jobs of the same concurrency group - if wfRawConcurrency != nil { - vars, err := actions_model.GetVariablesOfRun(ctx, run) - if err != nil { - return fmt.Errorf("GetVariablesOfRun: %w", err) - } - err = EvaluateRunConcurrencyFillModel(ctx, run, wfRawConcurrency, vars) - if err != nil { - return fmt.Errorf("EvaluateRunConcurrencyFillModel: %w", err) - } - } - // Insert the action run and its associated jobs into the database - if err := InsertRun(ctx, run, workflows); err != nil { - return fmt.Errorf("InsertRun: %w", err) - } - - allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) - if err != nil { - log.Error("FindRunJobs: %v", err) - } - CreateCommitStatus(ctx, allJobs...) - if len(allJobs) > 0 { - job := allJobs[0] - err := job.LoadRun(ctx) - if err != nil { - log.Error("LoadRun: %v", err) - } else { - notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) - } - } - for _, job := range allJobs { - notify_service.WorkflowJobStatusUpdate(ctx, repo, doer, job, nil) + if err := PrepareRun(ctx, content, run, inputsWithDefaults); err != nil { + return fmt.Errorf("PrepareRun: %w", err) } return nil } From 0aec839b8a7b1b8b1d7d293c0ed69853b6775b6d Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sat, 11 Oct 2025 11:57:12 +0200 Subject: [PATCH 02/14] cleanup --- services/actions/notifier_helper.go | 21 --------------------- services/actions/run.go | 5 +++++ 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 57f6aa6724771..3f921b62c5856 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -27,7 +27,6 @@ import ( api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/services/convert" - notify_service "code.gitea.io/gitea/services/notify" "github.com/nektos/act/pkg/model" ) @@ -349,26 +348,6 @@ func handleWorkflows( log.Error("PrepareRun: %v", err) continue } - - alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) - if err != nil { - log.Error("FindRunJobs: %v", err) - continue - } - // FIXME PERF skip this for schedule, dispatch etc. - CreateCommitStatus(ctx, alljobs...) - if len(alljobs) > 0 { - job := alljobs[0] - err := job.LoadRun(ctx) - if err != nil { - log.Error("LoadRun: %v", err) - continue - } - notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) - } - for _, job := range alljobs { - notify_service.WorkflowJobStatusUpdate(ctx, input.Repo, input.Doer, job, nil) - } } return nil } diff --git a/services/actions/run.go b/services/actions/run.go index 852daa2761b8a..39e63ad456578 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -57,10 +57,15 @@ func PrepareRun(ctx context.Context, content []byte, run *actions_model.ActionRu return fmt.Errorf("InsertRun: %w", err) } + // FIXME PERF do we need this db round trip? allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) if err != nil { log.Error("FindRunJobs: %v", err) } + + // FIXME PERF skip this for schedule, dispatch etc. + CreateCommitStatus(ctx, allJobs...) + err = run.LoadAttributes(ctx) if err != nil { log.Error("LoadAttributes: %v", err) From 948f65073c3073e7057c10d782337a24adfbfb0c Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Sat, 11 Oct 2025 20:49:54 +0200 Subject: [PATCH 03/14] Update services/actions/run.go Co-authored-by: delvh Signed-off-by: ChristopherHX --- services/actions/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/actions/run.go b/services/actions/run.go index 39e63ad456578..af7ef571c7d0b 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -17,7 +17,7 @@ import ( "gopkg.in/yaml.v3" ) -// PrepareRun prepares a run before inserting it into the database +// PrepareRun prepares a run and inserts it into the database // It parses the workflow content, evaluates concurrency if needed, and inserts the run and its jobs into the database. // The title will be cut off at 255 characters if it's longer than 255 characters. func PrepareRun(ctx context.Context, content []byte, run *actions_model.ActionRun, inputsWithDefaults map[string]any) error { From 0cc6d31fdb02052d8b98d82c2ec692e8b4265296 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sat, 11 Oct 2025 21:10:49 +0200 Subject: [PATCH 04/14] rename func --- services/actions/notifier_helper.go | 2 +- services/actions/run.go | 4 ++-- services/actions/schedule_tasks.go | 2 +- services/actions/workflow.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 3f921b62c5856..ca7c304b2593b 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -344,7 +344,7 @@ func handleWorkflows( run.NeedApproval = need - if err := PrepareRun(ctx, dwf.Content, run, nil); err != nil { + if err := PrepareRunAndInsert(ctx, dwf.Content, run, nil); err != nil { log.Error("PrepareRun: %v", err) continue } diff --git a/services/actions/run.go b/services/actions/run.go index af7ef571c7d0b..a82b74ac15eb5 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -17,10 +17,10 @@ import ( "gopkg.in/yaml.v3" ) -// PrepareRun prepares a run and inserts it into the database +// PrepareRunAndInsert prepares a run and inserts it into the database // It parses the workflow content, evaluates concurrency if needed, and inserts the run and its jobs into the database. // The title will be cut off at 255 characters if it's longer than 255 characters. -func PrepareRun(ctx context.Context, content []byte, run *actions_model.ActionRun, inputsWithDefaults map[string]any) error { +func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model.ActionRun, inputsWithDefaults map[string]any) error { if err := run.LoadAttributes(ctx); err != nil { return fmt.Errorf("LoadAttributes: %w", err) } diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 17e52a5865485..037bf5cddd187 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -119,7 +119,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) // FIXME cron.Content might be outdated if the workflow file has been changed. // Load the latest sha from default branch // Insert the action run and its associated jobs into the database - if err := PrepareRun(ctx, cron.Content, run, nil); err != nil { + if err := PrepareRunAndInsert(ctx, cron.Content, run, nil); err != nil { return err } diff --git a/services/actions/workflow.go b/services/actions/workflow.go index 1547342ff1fed..25801d6fa1da1 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -166,7 +166,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re run.EventPayload = string(eventPayload) // Insert the action run and its associated jobs into the database - if err := PrepareRun(ctx, content, run, inputsWithDefaults); err != nil { + if err := PrepareRunAndInsert(ctx, content, run, inputsWithDefaults); err != nil { return fmt.Errorf("PrepareRun: %w", err) } return nil From a5aea1b1e2d6ddd9248b701770093992c3e1cff4 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sat, 11 Oct 2025 21:12:47 +0200 Subject: [PATCH 05/14] do not query vars twice --- services/actions/run.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/services/actions/run.go b/services/actions/run.go index a82b74ac15eb5..22d7410debc5b 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -53,7 +53,7 @@ func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model run.Title = jobs[0].RunName } - if err := InsertRun(ctx, run, jobs); err != nil { + if err := InsertRun(ctx, run, jobs, vars); err != nil { return fmt.Errorf("InsertRun: %w", err) } @@ -81,7 +81,7 @@ func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model // InsertRun inserts a run // The title will be cut off at 255 characters if it's longer than 255 characters. -func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobparser.SingleWorkflow) error { +func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobparser.SingleWorkflow, vars map[string]string) error { return db.WithTx(ctx, func(ctx context.Context) error { index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID) if err != nil { @@ -108,12 +108,6 @@ func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobpar return err } - // query vars for evaluating job concurrency groups - vars, err := actions_model.GetVariablesOfRun(ctx, run) - if err != nil { - return fmt.Errorf("get run %d variables: %w", run.ID, err) - } - runJobs := make([]*actions_model.ActionRunJob, 0, len(jobs)) var hasWaitingJobs bool for _, v := range jobs { From f8ad8c0a58d1a6a17dd446a9300df0a12bd82820 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 11:50:51 +0800 Subject: [PATCH 06/14] Update services/actions/notifier_helper.go Signed-off-by: wxiaoguang --- services/actions/notifier_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index ca7c304b2593b..d17955b029081 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -345,7 +345,7 @@ func handleWorkflows( run.NeedApproval = need if err := PrepareRunAndInsert(ctx, dwf.Content, run, nil); err != nil { - log.Error("PrepareRun: %v", err) + log.Error("PrepareRunAndInsert: %v", err) continue } } From 1a52277c5c182f7cf2e9d3f99d3730c9044ba8de Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 12:03:04 +0800 Subject: [PATCH 07/14] fix comment and fixme --- services/actions/run.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/services/actions/run.go b/services/actions/run.go index 22d7410debc5b..e8719533d09f5 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -9,7 +9,6 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" notify_service "code.gitea.io/gitea/services/notify" @@ -53,29 +52,27 @@ func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model run.Title = jobs[0].RunName } - if err := InsertRun(ctx, run, jobs, vars); err != nil { + if err = InsertRun(ctx, run, jobs, vars); err != nil { return fmt.Errorf("InsertRun: %w", err) } - // FIXME PERF do we need this db round trip? + // Load the newly inserted jobs with all fields from database (the job models in InsertRun are partial, so load again) allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) if err != nil { - log.Error("FindRunJobs: %v", err) + return fmt.Errorf("FindRunJob: %w", err) } // FIXME PERF skip this for schedule, dispatch etc. - CreateCommitStatus(ctx, allJobs...) - - err = run.LoadAttributes(ctx) - if err != nil { - log.Error("LoadAttributes: %v", err) + const shouldCreateCommitStatus = true + if shouldCreateCommitStatus { + CreateCommitStatus(ctx, allJobs...) } + notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run) for _, job := range allJobs { notify_service.WorkflowJobStatusUpdate(ctx, run.Repo, run.TriggerUser, job, nil) } - // Return nil if no errors occurred return nil } From 432e2045b7d9b0dc3016a323113ccf0072d87c28 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sun, 12 Oct 2025 10:48:20 +0200 Subject: [PATCH 08/14] Add ShouldCreateCommitStatus --- services/actions/commit_status.go | 53 +++++++++++++++++++++---------- services/actions/run.go | 5 ++- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index ef241e5091a2f..dad94ad7bf1ac 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -33,26 +33,16 @@ func CreateCommitStatus(ctx context.Context, jobs ...*actions_model.ActionRunJob } } -func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { - if err := job.LoadAttributes(ctx); err != nil { - return fmt.Errorf("load run: %w", err) - } - - run := job.Run - - var ( - sha string - event string - ) +func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha string, _ error) { switch run.Event { case webhook_module.HookEventPush: event = "push" payload, err := run.GetPushEventPayload() if err != nil { - return fmt.Errorf("GetPushEventPayload: %w", err) + return "", "", fmt.Errorf("GetPushEventPayload: %w", err) } if payload.HeadCommit == nil { - return errors.New("head commit is missing in event payload") + return "", "", errors.New("head commit is missing in event payload") } sha = payload.HeadCommit.ID case // pull_request @@ -69,18 +59,49 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er } payload, err := run.GetPullRequestEventPayload() if err != nil { - return fmt.Errorf("GetPullRequestEventPayload: %w", err) + return "", "", fmt.Errorf("GetPullRequestEventPayload: %w", err) } if payload.PullRequest == nil { - return errors.New("pull request is missing in event payload") + return "", "", errors.New("pull request is missing in event payload") } else if payload.PullRequest.Head == nil { - return errors.New("head of pull request is missing in event payload") + return "", "", errors.New("head of pull request is missing in event payload") } sha = payload.PullRequest.Head.Sha case webhook_module.HookEventRelease: event = string(run.Event) sha = run.CommitSHA default: + return "", "", nil + } + return +} + +// ShouldCreateCommitStatus checks whether we should create a commit status for the given run. +// It returns true if the event is supported and the SHA is not empty. +func ShouldCreateCommitStatus(run *actions_model.ActionRun) bool { + event, sha, err := getCommitStatusEventNameAndSHA(run) + if err != nil { + log.Error("ShouldCreateCommitStatus: %s", err.Error()) + return false + } else if event == "" || sha == "" { + // Unsupported event, do nothing + return false + } + return true +} + +func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { + if err := job.LoadAttributes(ctx); err != nil { + return fmt.Errorf("load run: %w", err) + } + + run := job.Run + + event, sha, err := getCommitStatusEventNameAndSHA(run) + if err != nil { + return fmt.Errorf("getCommitStatusEventNameAndSHA: %w", err) + } else if event == "" || sha == "" { + // Unsupported event, do nothing return nil } diff --git a/services/actions/run.go b/services/actions/run.go index e8719533d09f5..e0afeb1879e84 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -62,9 +62,8 @@ func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model return fmt.Errorf("FindRunJob: %w", err) } - // FIXME PERF skip this for schedule, dispatch etc. - const shouldCreateCommitStatus = true - if shouldCreateCommitStatus { + // Create commit status for all jobs if needed + if ShouldCreateCommitStatus(run) { CreateCommitStatus(ctx, allJobs...) } From 9f600e19c2da6164b59ddba02210f6e778bb2cb5 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sun, 12 Oct 2025 11:04:01 +0200 Subject: [PATCH 09/14] avoid bare return --- services/actions/commit_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index dad94ad7bf1ac..e5d3fa132befc 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -73,7 +73,7 @@ func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha st default: return "", "", nil } - return + return event, sha, nil } // ShouldCreateCommitStatus checks whether we should create a commit status for the given run. From 3951ba8b920005a67470cf975b674966454925e1 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 17:42:26 +0800 Subject: [PATCH 10/14] call getCommitStatusEventNameAndSHA once for a run --- routers/api/actions/runner/runner.go | 5 +- routers/web/repo/actions/view.go | 8 +-- services/actions/clear_tasks.go | 23 ++++++--- services/actions/commit_status.go | 74 +++++++++++----------------- services/actions/job_emitter.go | 2 +- services/actions/run.go | 5 +- services/actions/task.go | 2 +- 7 files changed, 53 insertions(+), 66 deletions(-) diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index 46c5147d99cea..86bab4b340c27 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -217,10 +217,7 @@ func (s *Service) UpdateTask( return nil, status.Errorf(codes.Internal, "load run: %v", err) } - // don't create commit status for cron job - if task.Job.Run.ScheduleID == 0 { - actions_service.CreateCommitStatus(ctx, task.Job) - } + actions_service.CreateCommitStatusForRunJobs(ctx, task.Job.Run, task.Job) if task.Status.IsDone() { notify_service.WorkflowJobStatusUpdate(ctx, task.Job.Run.Repo, task.Job.Run.TriggerUser, task.Job, task) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 232c627709c6c..b409e887bee34 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -541,7 +541,7 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shou return err } - actions_service.CreateCommitStatus(ctx, job) + actions_service.CreateCommitStatusForRunJobs(ctx, job.Run, job) notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) return nil @@ -569,7 +569,7 @@ func Logs(ctx *context_module.Context) { func Cancel(ctx *context_module.Context) { runIndex := getRunIndex(ctx) - _, jobs := getRunJobs(ctx, runIndex, -1) + firstJob, jobs := getRunJobs(ctx, runIndex, -1) if ctx.Written() { return } @@ -588,7 +588,7 @@ func Cancel(ctx *context_module.Context) { return } - actions_service.CreateCommitStatus(ctx, jobs...) + actions_service.CreateCommitStatusForRunJobs(ctx, firstJob.Run, jobs...) actions_service.EmitJobsIfReadyByJobs(updatedJobs) for _, job := range updatedJobs { @@ -642,7 +642,7 @@ func Approve(ctx *context_module.Context) { return } - actions_service.CreateCommitStatus(ctx, jobs...) + actions_service.CreateCommitStatusForRunJobs(ctx, current.Run, jobs...) if len(updatedJobs) > 0 { job := updatedJobs[0] diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go index 727a18443d93b..97d5732dc81c7 100644 --- a/services/actions/clear_tasks.go +++ b/services/actions/clear_tasks.go @@ -37,15 +37,19 @@ func StopEndlessTasks(ctx context.Context) error { } func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.ActionRunJob) { - if len(jobs) > 0 { - CreateCommitStatus(ctx, jobs...) - for _, job := range jobs { - _ = job.LoadAttributes(ctx) - notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) + if len(jobs) == 0 { + return + } + for _, job := range jobs { + if err := job.LoadAttributes(ctx); err != nil { + log.Error("Failed to load job attributes: %v", err) } - job := jobs[0] - notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) + CreateCommitStatusForRunJobs(ctx, job.Run, job) + notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) } + + job := jobs[0] + notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) } func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error { @@ -208,7 +212,10 @@ func CancelAbandonedJobs(ctx context.Context) error { log.Warn("cancel abandoned job %v: %v", job.ID, err) // go on } - CreateCommitStatus(ctx, job) + if job.Run == nil { + continue // error occurs during loading attributes, the following code that depends on "Run" will fail, so ignore and skip + } + CreateCommitStatusForRunJobs(ctx, job.Run, job) if updated { updatedJobs = append(updatedJobs, job) notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index e5d3fa132befc..66f6141c84bd2 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -12,10 +12,10 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/commitstatus" - git "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" webhook_module "code.gitea.io/gitea/modules/webhook" commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" @@ -23,11 +23,28 @@ import ( "github.com/nektos/act/pkg/jobparser" ) -// CreateCommitStatus creates a commit status for the given job. +// CreateCommitStatusForRunJobs creates a commit status for the given job if it has a supported event and related commit. // It won't return an error failed, but will log it, because it's not critical. -func CreateCommitStatus(ctx context.Context, jobs ...*actions_model.ActionRunJob) { +func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.ActionRun, jobs ...*actions_model.ActionRunJob) { + // don't create commit status for cron job + if run.ScheduleID != 0 { + return + } + + event, commitID, err := getCommitStatusEventNameAndSHA(run) + if err != nil { + log.Error("GetCommitStatusEventNameAndSHA: %v", err) + } else if event == "" || commitID == "" { + return // Unsupported event, do nothing + } + + if err = run.LoadAttributes(ctx); err != nil { + log.Error("run.LoadAttributes: %v", err) + return + } + for _, job := range jobs { - if err := createCommitStatus(ctx, job); err != nil { + if err = createCommitStatus(ctx, run.Repo, event, commitID, run, job); err != nil { log.Error("Failed to create commit status for job %d: %v", job.ID, err) } } @@ -76,46 +93,17 @@ func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha st return event, sha, nil } -// ShouldCreateCommitStatus checks whether we should create a commit status for the given run. -// It returns true if the event is supported and the SHA is not empty. -func ShouldCreateCommitStatus(run *actions_model.ActionRun) bool { - event, sha, err := getCommitStatusEventNameAndSHA(run) - if err != nil { - log.Error("ShouldCreateCommitStatus: %s", err.Error()) - return false - } else if event == "" || sha == "" { - // Unsupported event, do nothing - return false - } - return true -} - -func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) error { - if err := job.LoadAttributes(ctx); err != nil { - return fmt.Errorf("load run: %w", err) - } - - run := job.Run - - event, sha, err := getCommitStatusEventNameAndSHA(run) - if err != nil { - return fmt.Errorf("getCommitStatusEventNameAndSHA: %w", err) - } else if event == "" || sha == "" { - // Unsupported event, do nothing - return nil - } - - repo := run.Repo +func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, commitID string, run *actions_model.ActionRun, job *actions_model.ActionRunJob) error { // TODO: store workflow name as a field in ActionRun to avoid parsing runName := path.Base(run.WorkflowID) if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 { runName = wfs[0].Name } - ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) + ctxName := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) state := toCommitStatus(job.Status) - if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil { + if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll); err == nil { for _, v := range statuses { - if v.Context == ctxname { + if v.Context == ctxName { if v.State == state { // no need to update return nil @@ -144,6 +132,8 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er description = "Waiting to run" case actions_model.StatusBlocked: description = "Blocked by required conditions" + default: + description = "Unknown status: " + job.Status.String() } index, err := getIndexOfJob(ctx, job) @@ -152,20 +142,16 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er } creator := user_model.NewActionsUser() - commitID, err := git.NewIDFromString(sha) - if err != nil { - return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err) - } status := git_model.CommitStatus{ - SHA: sha, + SHA: commitID, TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index), Description: description, - Context: ctxname, + Context: ctxName, CreatorID: creator.ID, State: state, } - return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID.String(), &status) + return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID, &status) } func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState { diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index 762d28e9cc238..74a8a127ef58d 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -89,7 +89,7 @@ func checkJobsByRunID(ctx context.Context, runID int64) error { }); err != nil { return err } - CreateCommitStatus(ctx, jobs...) + CreateCommitStatusForRunJobs(ctx, run, jobs...) for _, job := range updatedJobs { _ = job.LoadAttributes(ctx) notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) diff --git a/services/actions/run.go b/services/actions/run.go index e0afeb1879e84..90413e9bc23e3 100644 --- a/services/actions/run.go +++ b/services/actions/run.go @@ -62,10 +62,7 @@ func PrepareRunAndInsert(ctx context.Context, content []byte, run *actions_model return fmt.Errorf("FindRunJob: %w", err) } - // Create commit status for all jobs if needed - if ShouldCreateCommitStatus(run) { - CreateCommitStatus(ctx, allJobs...) - } + CreateCommitStatusForRunJobs(ctx, run, allJobs...) notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run) for _, job := range allJobs { diff --git a/services/actions/task.go b/services/actions/task.go index 6a547c1c12863..cf2164f456606 100644 --- a/services/actions/task.go +++ b/services/actions/task.go @@ -97,7 +97,7 @@ func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return nil, false, nil } - CreateCommitStatus(ctx, job) + CreateCommitStatusForRunJobs(ctx, job.Run, job) notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, actionTask) return task, true, nil From 6cd6cb04e639bd0feab9f5fe8540ced42cae677e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 17:51:13 +0800 Subject: [PATCH 11/14] avoid nil access --- services/actions/clear_tasks.go | 10 ++++++---- services/actions/commit_status.go | 15 +++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go index 97d5732dc81c7..e49bda1b16489 100644 --- a/services/actions/clear_tasks.go +++ b/services/actions/clear_tasks.go @@ -43,13 +43,15 @@ func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.Ac for _, job := range jobs { if err := job.LoadAttributes(ctx); err != nil { log.Error("Failed to load job attributes: %v", err) + continue } CreateCommitStatusForRunJobs(ctx, job.Run, job) notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil) } - job := jobs[0] - notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) + if job := jobs[0]; job.Run != nil && job.Run.Repo != nil { + notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run) + } } func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error { @@ -212,8 +214,8 @@ func CancelAbandonedJobs(ctx context.Context) error { log.Warn("cancel abandoned job %v: %v", job.ID, err) // go on } - if job.Run == nil { - continue // error occurs during loading attributes, the following code that depends on "Run" will fail, so ignore and skip + if job.Run == nil || job.Run.Repo == nil { + continue // error occurs during loading attributes, the following code that depends on "Run.Repo" will fail, so ignore and skip } CreateCommitStatusForRunJobs(ctx, job.Run, job) if updated { diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 66f6141c84bd2..89a8d5e78ea4e 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -31,7 +31,7 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action return } - event, commitID, err := getCommitStatusEventNameAndSHA(run) + event, commitID, err := getCommitStatusEventNameAndCommitID(run) if err != nil { log.Error("GetCommitStatusEventNameAndSHA: %v", err) } else if event == "" || commitID == "" { @@ -50,7 +50,7 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action } } -func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha string, _ error) { +func getCommitStatusEventNameAndCommitID(run *actions_model.ActionRun) (event, commitID string, _ error) { switch run.Event { case webhook_module.HookEventPush: event = "push" @@ -61,7 +61,7 @@ func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha st if payload.HeadCommit == nil { return "", "", errors.New("head commit is missing in event payload") } - sha = payload.HeadCommit.ID + commitID = payload.HeadCommit.ID case // pull_request webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, @@ -83,14 +83,13 @@ func getCommitStatusEventNameAndSHA(run *actions_model.ActionRun) (event, sha st } else if payload.PullRequest.Head == nil { return "", "", errors.New("head of pull request is missing in event payload") } - sha = payload.PullRequest.Head.Sha + commitID = payload.PullRequest.Head.Sha case webhook_module.HookEventRelease: event = string(run.Event) - sha = run.CommitSHA - default: - return "", "", nil + commitID = run.CommitSHA + default: // do nothing, return empty } - return event, sha, nil + return event, commitID, nil } func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, commitID string, run *actions_model.ActionRun, job *actions_model.ActionRunJob) error { From d854bb07b221fcc0419ec2037291d830cca5cd4b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 17:52:07 +0800 Subject: [PATCH 12/14] fix lint --- services/actions/commit_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 89a8d5e78ea4e..aa9f5b04d6b94 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -114,7 +114,7 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, return fmt.Errorf("GetLatestCommitStatus: %w", err) } - description := "" + var description string switch job.Status { // TODO: if we want support description in different languages, we need to support i18n placeholders in it case actions_model.StatusSuccess: From 27f379135e62d8ceb68d7d54cb7de6c9dde7201e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 17:58:01 +0800 Subject: [PATCH 13/14] fix check --- services/actions/commit_status.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index aa9f5b04d6b94..0b716253a17c1 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -34,8 +34,9 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action event, commitID, err := getCommitStatusEventNameAndCommitID(run) if err != nil { log.Error("GetCommitStatusEventNameAndSHA: %v", err) - } else if event == "" || commitID == "" { - return // Unsupported event, do nothing + } + if event == "" || commitID == "" { + return // unsupported event, or no commit id, or error occurs, do nothing } if err = run.LoadAttributes(ctx); err != nil { From 38dd43264311ff7b444ade30099af328c9bb0bae Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 12 Oct 2025 18:03:18 +0800 Subject: [PATCH 14/14] fix unknown description --- services/actions/commit_status.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 0b716253a17c1..d3f2b0f3cc403 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "path" + "strconv" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" @@ -133,7 +134,7 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event, case actions_model.StatusBlocked: description = "Blocked by required conditions" default: - description = "Unknown status: " + job.Status.String() + description = "Unknown status: " + strconv.Itoa(int(job.Status)) } index, err := getIndexOfJob(ctx, job)