From 177f114aa2118ba61c663dbbc8ba1fe4123d53e4 Mon Sep 17 00:00:00 2001 From: Davide <84925446+davidetacchini@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:58:12 +0100 Subject: [PATCH] Fix workflow_run events not creating commit statuses Workflows triggered by `workflow_run` events did not produce commit statuses because `getCommitStatusEventNameAndCommitID` had no case for `HookEventWorkflowRun`. The function returned empty strings, so `CreateCommitStatusForRunJobs` silently skipped status creation. Add a `workflow_run` case that walks up the parent run chain (up to 5 levels) to find the original triggering event's commit SHA and attaches the status there. This makes chained `workflow_run` workflows visible in the commit status popover alongside the root workflow. --- services/actions/commit_status.go | 26 ++++- services/actions/commit_status_test.go | 149 +++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 services/actions/commit_status_test.go diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 7271f58091..98e89dbb83 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -34,7 +34,7 @@ func CreateCommitStatusForRunJobs(ctx context.Context, run *actions_model.Action return } - event, commitID, err := getCommitStatusEventNameAndCommitID(run) + event, commitID, err := getCommitStatusEventNameAndCommitID(ctx, run) if err != nil { log.Error("GetCommitStatusEventNameAndSHA: %v", err) } @@ -81,7 +81,7 @@ func GetRunsFromCommitStatuses(ctx context.Context, statuses []*git_model.Commit return runs, nil } -func getCommitStatusEventNameAndCommitID(run *actions_model.ActionRun) (event, commitID string, _ error) { +func getCommitStatusEventNameAndCommitID(ctx context.Context, run *actions_model.ActionRun) (event, commitID string, _ error) { switch run.Event { case webhook_module.HookEventPush: event = "push" @@ -118,6 +118,28 @@ func getCommitStatusEventNameAndCommitID(run *actions_model.ActionRun) (event, c case webhook_module.HookEventRelease: event = string(run.Event) commitID = run.CommitSHA + case webhook_module.HookEventWorkflowRun: + event = "workflow_run" + currentRun := run + for range 5 { + payload, err := currentRun.GetWorkflowRunEventPayload() + if err != nil { + return "", "", fmt.Errorf("GetWorkflowRunEventPayload: %w", err) + } + if payload.WorkflowRun == nil { + return "", "", nil + } + parentRun, err := actions_model.GetRunByRepoAndID(ctx, currentRun.RepoID, payload.WorkflowRun.ID) + if err != nil { + return "", "", fmt.Errorf("GetRunByRepoAndID: %w", err) + } + if parentRun.Event != webhook_module.HookEventWorkflowRun { + _, commitID, err = getCommitStatusEventNameAndCommitID(ctx, parentRun) + return event, commitID, err + } + currentRun = parentRun + } + return "", "", nil default: // do nothing, return empty } return event, commitID, nil diff --git a/services/actions/commit_status_test.go b/services/actions/commit_status_test.go new file mode 100644 index 0000000000..411ab11bff --- /dev/null +++ b/services/actions/commit_status_test.go @@ -0,0 +1,149 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/json" + api "code.gitea.io/gitea/modules/structs" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetCommitStatusEventNameAndCommitID(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + ctx := t.Context() + repoID := int64(1) + pushCommitSHA := "abc123def456abc123def456abc123def456abc1" + + pushPayload, err := json.Marshal(&api.PushPayload{ + HeadCommit: &api.PayloadCommit{ID: pushCommitSHA}, + }) + require.NoError(t, err) + + nextIndex := func() int64 { + idx, err := db.GetNextResourceIndex(ctx, "action_run_index", repoID) + require.NoError(t, err) + return idx + } + + pushRun := &actions_model.ActionRun{ + Index: nextIndex(), + RepoID: repoID, + Event: webhook_module.HookEventPush, + EventPayload: string(pushPayload), + TriggerUserID: 1, + CommitSHA: pushCommitSHA, + Ref: "refs/heads/main", + WorkflowID: "push.yml", + Title: "push run", + } + require.NoError(t, db.Insert(ctx, pushRun)) + + t.Run("WorkflowRunWithPushParent", func(t *testing.T) { + wrPayload, err := json.Marshal(&api.WorkflowRunPayload{ + WorkflowRun: &api.ActionWorkflowRun{ + ID: pushRun.ID, + }, + }) + require.NoError(t, err) + + wrRun := &actions_model.ActionRun{ + Index: nextIndex(), + RepoID: repoID, + Event: webhook_module.HookEventWorkflowRun, + EventPayload: string(wrPayload), + TriggerUserID: 1, + CommitSHA: "default-branch-head-000000000000000000000", + Ref: "refs/heads/main", + WorkflowID: "wr.yml", + Title: "workflow_run run", + } + require.NoError(t, db.Insert(ctx, wrRun)) + + event, commitID, err := getCommitStatusEventNameAndCommitID(ctx, wrRun) + require.NoError(t, err) + assert.Equal(t, "workflow_run", event) + assert.Equal(t, pushCommitSHA, commitID) + }) + + t.Run("WorkflowRunChainedTwoLevels", func(t *testing.T) { + midPayload, err := json.Marshal(&api.WorkflowRunPayload{ + WorkflowRun: &api.ActionWorkflowRun{ + ID: pushRun.ID, + }, + }) + require.NoError(t, err) + + midRun := &actions_model.ActionRun{ + Index: nextIndex(), + RepoID: repoID, + Event: webhook_module.HookEventWorkflowRun, + EventPayload: string(midPayload), + TriggerUserID: 1, + CommitSHA: "default-branch-head-000000000000000000000", + Ref: "refs/heads/main", + WorkflowID: "mid.yml", + Title: "mid workflow_run", + } + require.NoError(t, db.Insert(ctx, midRun)) + + leafPayload, err := json.Marshal(&api.WorkflowRunPayload{ + WorkflowRun: &api.ActionWorkflowRun{ + ID: midRun.ID, + }, + }) + require.NoError(t, err) + + leafRun := &actions_model.ActionRun{ + Index: nextIndex(), + RepoID: repoID, + Event: webhook_module.HookEventWorkflowRun, + EventPayload: string(leafPayload), + TriggerUserID: 1, + CommitSHA: "default-branch-head-200000000000000000000", + Ref: "refs/heads/main", + WorkflowID: "leaf.yml", + Title: "leaf workflow_run", + } + require.NoError(t, db.Insert(ctx, leafRun)) + + event, commitID, err := getCommitStatusEventNameAndCommitID(ctx, leafRun) + require.NoError(t, err) + assert.Equal(t, "workflow_run", event) + assert.Equal(t, pushCommitSHA, commitID) + }) + + t.Run("WorkflowRunNilWorkflowRun", func(t *testing.T) { + payload, err := json.Marshal(&api.WorkflowRunPayload{ + WorkflowRun: nil, + }) + require.NoError(t, err) + + run := &actions_model.ActionRun{ + Index: nextIndex(), + RepoID: repoID, + Event: webhook_module.HookEventWorkflowRun, + EventPayload: string(payload), + TriggerUserID: 1, + CommitSHA: "some-sha-0000000000000000000000000000000", + Ref: "refs/heads/main", + WorkflowID: "nil.yml", + Title: "nil workflow_run", + } + require.NoError(t, db.Insert(ctx, run)) + + event, commitID, err := getCommitStatusEventNameAndCommitID(ctx, run) + require.NoError(t, err) + assert.Empty(t, event) + assert.Empty(t, commitID) + }) +}