0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-21 16:45:03 +02:00

fix workflow name in workflow payload

* add workflow_run action trigger + webhook test
* loads the workflow and fill the name key if present
This commit is contained in:
Christopher Homberger 2025-05-03 01:07:54 +02:00
parent 3e3be37337
commit 37c96b05c5
6 changed files with 281 additions and 44 deletions

View File

@ -74,3 +74,23 @@
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 802
title: "workflow run list"
repo_id: 4
owner_id: 1
workflow_id: "test.yaml"
index: 191
trigger_user_id: 1
ref: "refs/heads/test"
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

View File

@ -69,3 +69,19 @@
status: 5
started: 1683636528
stopped: 1683636626
-
id: 203
run_id: 802
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

View File

@ -5,6 +5,7 @@
package convert
import (
"bytes"
"context"
"fmt"
"net/url"
@ -34,6 +35,7 @@ import (
"code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/gitdiff"
"github.com/nektos/act/pkg/model"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
)
@ -423,9 +425,25 @@ func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, co
createdAt := commit.Author.When
updatedAt := commit.Author.When
content, err := actions.GetContentFromEntry(entry)
name := entry.Name()
if err == nil {
workflow, err := model.ReadWorkflow(bytes.NewReader(content))
if err == nil {
// Only use the name when specified in the workflow file
if workflow.Name != "" {
name = workflow.Name
}
} else {
log.Error("getActionWorkflowEntry: Failed to parse workflow: %v", err)
}
} else {
log.Error("getActionWorkflowEntry: Failed to get content from entry: %v", err)
}
return &api.ActionWorkflow{
ID: entry.Name(),
Name: entry.Name(),
Name: name,
Path: path.Join(folder, entry.Name()),
State: state,
CreatedAt: createdAt,
@ -464,7 +482,7 @@ func GetActionWorkflow(ctx context.Context, gitrepo *git.Repository, repo *repo_
}
for _, entry := range entries {
if entry.Name == workflowID {
if entry.ID == workflowID {
return entry, nil
}
}

View File

@ -719,7 +719,7 @@ func TestWorkflowDispatchPublicApi(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch
jobs:
@ -799,7 +799,7 @@ func TestWorkflowDispatchPublicApiWithInputs(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
jobs:
@ -890,7 +890,7 @@ func TestWorkflowDispatchPublicApiJSON(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
jobs:
@ -976,7 +976,7 @@ func TestWorkflowDispatchPublicApiWithInputsJSON(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
jobs:
@ -1070,7 +1070,7 @@ func TestWorkflowDispatchPublicApiWithInputsNonDefaultBranchJSON(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch
jobs:
@ -1106,7 +1106,7 @@ jobs:
{
Operation: "update",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
jobs:
@ -1207,7 +1207,7 @@ func TestWorkflowApi(t *testing.T) {
{
Operation: "create",
TreePath: ".gitea/workflows/dispatch.yml",
ContentReader: strings.NewReader(`name: test
ContentReader: strings.NewReader(`
on:
workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
jobs:

View File

@ -749,3 +749,204 @@ jobs:
assert.Len(t, payloads[6].WorkflowJob.Steps, 2)
})
}
type workflowRunWebhook struct {
URL string
payloads []api.WorkflowRunPayload
triggeredEvent string
}
func Test_WebhookWorkflowRun(t *testing.T) {
webhookData := &workflowRunWebhook{}
provider := newMockWebhookProvider(func(r *http.Request) {
assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_run", "X-GitHub-Event-Type should contain workflow_run")
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_run", "X-Gitea-Event-Type should contain workflow_run")
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_run", "X-Gogs-Event-Type should contain workflow_run")
content, _ := io.ReadAll(r.Body)
var payload api.WorkflowRunPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
webhookData.payloads = append(webhookData.payloads, payload)
webhookData.triggeredEvent = "workflow_run"
}, http.StatusOK)
defer provider.Close()
webhookData.URL = provider.URL()
tests := []struct {
name string
callback func(t *testing.T, webhookData *workflowRunWebhook)
}{
{
name: "WorkflowRun",
callback: testWebhookWorkflowRun,
},
{
name: "WorkflowRunDepthLimit",
callback: testWebhookWorkflowRunDepthLimit,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
webhookData.payloads = nil
webhookData.triggeredEvent = ""
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
test.callback(t, webhookData)
})
})
}
}
func testWebhookWorkflowRun(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
assert.NoError(t, err)
runner := newMockRunner()
runner.registerAsRepoRunner(t, "user2", "repo1", "mock-runner", []string{"ubuntu-latest"}, false)
// 2.1 add workflow_run workflow file to the repo
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+"dispatch.yml", `
on:
workflow_run:
workflows: ["Push"]
types:
- completed
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
`)
createWorkflowFile(t, token, "user2", "repo1", ".gitea/workflows/dispatch.yml", opts)
// 2.2 trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `name: Push
on: push
jobs:
wf1-job:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
wf2-job:
runs-on: ubuntu-latest
needs: wf1-job
steps:
- run: echo 'cmd 1'
- run: echo 'cmd 2'
`
opts = getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 1)
assert.Equal(t, "requested", webhookData.payloads[0].Action)
assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[0].Repo.FullName)
// 4. Execute two Jobs
task := runner.fetchTask(t)
outcome := &mockTaskOutcome{
result: runnerv1.Result_RESULT_SUCCESS,
execTime: time.Millisecond,
}
runner.execTask(t, task, outcome)
task = runner.fetchTask(t)
outcome = &mockTaskOutcome{
result: runnerv1.Result_RESULT_FAILURE,
execTime: time.Millisecond,
}
runner.execTask(t, task, outcome)
// 7. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 3)
assert.Equal(t, "completed", webhookData.payloads[1].Action)
assert.Equal(t, "push", webhookData.payloads[1].WorkflowRun.Event)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 3)
assert.Equal(t, "requested", webhookData.payloads[2].Action)
assert.Equal(t, "queued", webhookData.payloads[2].WorkflowRun.Status)
assert.Equal(t, "workflow_run", webhookData.payloads[2].WorkflowRun.Event)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[2].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[2].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[2].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[2].Repo.FullName)
}
func testWebhookWorkflowRunDepthLimit(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
assert.NoError(t, err)
// 2. trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `name: Endless Loop
on:
push:
workflow_run:
types:
- requested
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
`
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
// 1x push + 5x workflow_run requested chain
assert.Len(t, webhookData.payloads, 6)
for i := 0; i < 6; i++ {
assert.Equal(t, "requested", webhookData.payloads[i].Action)
assert.Equal(t, "queued", webhookData.payloads[i].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[i].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[i].WorkflowRun.HeadSha)
if i == 0 {
assert.Equal(t, "push", webhookData.payloads[i].WorkflowRun.Event)
} else {
assert.Equal(t, "workflow_run", webhookData.payloads[i].WorkflowRun.Event)
}
assert.Equal(t, "repo1", webhookData.payloads[i].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[i].Repo.FullName)
}
}

View File

@ -25,7 +25,9 @@ func TestAPIWorkflowRunRepoApi(t *testing.T) {
runnerList := api.ActionWorkflowRunsResponse{}
DecodeJSON(t, runnerListResp, &runnerList)
assert.Len(t, runnerList.Entries, 4)
assert.Len(t, runnerList.Entries, 5)
foundRun := false
for _, run := range runnerList.Entries {
req := NewRequest(t, "GET", fmt.Sprintf("%s/%s", run.URL, "jobs")).AddTokenAuth(token)
@ -33,40 +35,20 @@ func TestAPIWorkflowRunRepoApi(t *testing.T) {
jobList := api.ActionWorkflowJobsResponse{}
DecodeJSON(t, jobsResp, &jobList)
// assert.NotEmpty(t, jobList.Entries)
for _, job := range jobList.Entries {
req := NewRequest(t, "GET", job.URL).AddTokenAuth(token)
jobsResp := MakeRequest(t, req, http.StatusOK)
apiJob := api.ActionWorkflowJob{}
DecodeJSON(t, jobsResp, &apiJob)
assert.Equal(t, job.ID, apiJob.ID)
assert.Equal(t, job.RunID, apiJob.RunID)
assert.Equal(t, job.Status, apiJob.Status)
assert.Equal(t, job.Conclusion, apiJob.Conclusion)
if run.ID == 802 {
foundRun = true
assert.Len(t, jobList.Entries, 1)
for _, job := range jobList.Entries {
req := NewRequest(t, "GET", job.URL).AddTokenAuth(token)
jobsResp := MakeRequest(t, req, http.StatusOK)
apiJob := api.ActionWorkflowJob{}
DecodeJSON(t, jobsResp, &apiJob)
assert.Equal(t, job.ID, apiJob.ID)
assert.Equal(t, job.RunID, apiJob.RunID)
assert.Equal(t, job.Status, apiJob.Status)
assert.Equal(t, job.Conclusion, apiJob.Conclusion)
}
}
// assert.NotEmpty(t, run.ID)
// assert.NotEmpty(t, run.Status)
// assert.NotEmpty(t, run.Event)
// assert.NotEmpty(t, run.WorkflowID)
// assert.NotEmpty(t, run.HeadBranch)
// assert.NotEmpty(t, run.HeadSHA)
// assert.NotEmpty(t, run.CreatedAt)
// assert.NotEmpty(t, run.UpdatedAt)
// assert.NotEmpty(t, run.URL)
// assert.NotEmpty(t, run.HTMLURL)
// assert.NotEmpty(t, run.PullRequests)
// assert.NotEmpty(t, run.WorkflowURL)
// assert.NotEmpty(t, run.HeadCommit)
// assert.NotEmpty(t, run.HeadRepository)
// assert.NotEmpty(t, run.Repository)
// assert.NotEmpty(t, run.HeadRepository)
// assert.NotEmpty(t, run.HeadRepository.Owner)
// assert.NotEmpty(t, run.HeadRepository.Name)
// assert.NotEmpty(t, run.Repository.Owner)
// assert.NotEmpty(t, run.Repository.Name)
// assert.NotEmpty(t, run.HeadRepository.Owner.Login)
// assert.NotEmpty(t, run.HeadRepository.Name)
// assert.NotEmpty(t, run.Repository.Owner.Login)
// assert.NotEmpty(t, run.Repository.Name)
}
assert.True(t, foundRun, "Expected to find run with ID 802")
}