Skip to content

Commit cda90ec

Browse files
Add workflow_run api + webhook (#33964)
Implements - https://docs.github.com/en/rest/actions/workflow-jobs?apiVersion=2022-11-28#list-jobs-for-a-workflow-run--code-samples - https://docs.github.com/en/rest/actions/workflow-jobs?apiVersion=2022-11-28#get-a-job-for-a-workflow-run--code-samples - https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository - https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run - `/actions/runs` for global + user + org (Gitea only) - `/actions/jobs` for global + user + org + repository (Gitea only) - workflow_run webhook + action trigger - limitations - workflow id is assigned to a string, this may result into problems in strongly typed clients Fixes - workflow_job webhook url to no longer contain the `runs/<run>` part to align with api - workflow instance does now use it's name inside the file instead of filename if set Refactoring - Moved a lot of logic from workflows/workflow_job into a shared module used by both webhook and api TODO - [x] Verify Keda Compatibility - [x] Edit Webhook API bug is resolved Closes #23670 Closes #23796 Closes #24898 Replaces #28047 and is much more complete --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent d462ce1 commit cda90ec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2814
-234
lines changed

models/actions/run.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err
166166
return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
167167
}
168168

169+
func (run *ActionRun) GetWorkflowRunEventPayload() (*api.WorkflowRunPayload, error) {
170+
if run.Event == webhook_module.HookEventWorkflowRun {
171+
var payload api.WorkflowRunPayload
172+
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
173+
return nil, err
174+
}
175+
return &payload, nil
176+
}
177+
return nil, fmt.Errorf("event %s is not a workflow run event", run.Event)
178+
}
179+
169180
func (run *ActionRun) IsSchedule() bool {
170181
return run.ScheduleID > 0
171182
}

models/actions/run_job_list.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,31 @@ type FindRunJobOptions struct {
8080
func (opts FindRunJobOptions) ToConds() builder.Cond {
8181
cond := builder.NewCond()
8282
if opts.RunID > 0 {
83-
cond = cond.And(builder.Eq{"run_id": opts.RunID})
83+
cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
8484
}
8585
if opts.RepoID > 0 {
86-
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
87-
}
88-
if opts.OwnerID > 0 {
89-
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
86+
cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
9087
}
9188
if opts.CommitSHA != "" {
92-
cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
89+
cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
9390
}
9491
if len(opts.Statuses) > 0 {
95-
cond = cond.And(builder.In("status", opts.Statuses))
92+
cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
9693
}
9794
if opts.UpdatedBefore > 0 {
98-
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
95+
cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
9996
}
10097
return cond
10198
}
99+
100+
func (opts FindRunJobOptions) ToJoins() []db.JoinFunc {
101+
if opts.OwnerID > 0 {
102+
return []db.JoinFunc{
103+
func(sess db.Engine) error {
104+
sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
105+
return nil
106+
},
107+
}
108+
}
109+
return nil
110+
}

models/actions/run_list.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,39 +72,50 @@ type FindRunOptions struct {
7272
TriggerEvent webhook_module.HookEventType
7373
Approved bool // not util.OptionalBool, it works only when it's true
7474
Status []Status
75+
CommitSHA string
7576
}
7677

7778
func (opts FindRunOptions) ToConds() builder.Cond {
7879
cond := builder.NewCond()
7980
if opts.RepoID > 0 {
80-
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
81-
}
82-
if opts.OwnerID > 0 {
83-
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
81+
cond = cond.And(builder.Eq{"`action_run`.repo_id": opts.RepoID})
8482
}
8583
if opts.WorkflowID != "" {
86-
cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID})
84+
cond = cond.And(builder.Eq{"`action_run`.workflow_id": opts.WorkflowID})
8785
}
8886
if opts.TriggerUserID > 0 {
89-
cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
87+
cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
9088
}
9189
if opts.Approved {
92-
cond = cond.And(builder.Gt{"approved_by": 0})
90+
cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
9391
}
9492
if len(opts.Status) > 0 {
95-
cond = cond.And(builder.In("status", opts.Status))
93+
cond = cond.And(builder.In("`action_run`.status", opts.Status))
9694
}
9795
if opts.Ref != "" {
98-
cond = cond.And(builder.Eq{"ref": opts.Ref})
96+
cond = cond.And(builder.Eq{"`action_run`.ref": opts.Ref})
9997
}
10098
if opts.TriggerEvent != "" {
101-
cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
99+
cond = cond.And(builder.Eq{"`action_run`.trigger_event": opts.TriggerEvent})
100+
}
101+
if opts.CommitSHA != "" {
102+
cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
102103
}
103104
return cond
104105
}
105106

107+
func (opts FindRunOptions) ToJoins() []db.JoinFunc {
108+
if opts.OwnerID > 0 {
109+
return []db.JoinFunc{func(sess db.Engine) error {
110+
sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
111+
return nil
112+
}}
113+
}
114+
return nil
115+
}
116+
106117
func (opts FindRunOptions) ToOrders() string {
107-
return "`id` DESC"
118+
return "`action_run`.`id` DESC"
108119
}
109120

110121
type StatusInfo struct {

models/fixtures/action_run.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
ref: "refs/heads/master"
1010
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
1111
event: "push"
12+
trigger_event: "push"
1213
is_fork_pull_request: 0
1314
status: 1
1415
started: 1683636528
@@ -28,6 +29,7 @@
2829
ref: "refs/heads/master"
2930
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
3031
event: "push"
32+
trigger_event: "push"
3133
is_fork_pull_request: 0
3234
status: 1
3335
started: 1683636528
@@ -47,6 +49,7 @@
4749
ref: "refs/heads/master"
4850
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
4951
event: "push"
52+
trigger_event: "push"
5053
is_fork_pull_request: 0
5154
status: 6 # running
5255
started: 1683636528
@@ -66,6 +69,47 @@
6669
ref: "refs/heads/test"
6770
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
6871
event: "push"
72+
trigger_event: "push"
73+
is_fork_pull_request: 0
74+
status: 1
75+
started: 1683636528
76+
stopped: 1683636626
77+
created: 1683636108
78+
updated: 1683636626
79+
need_approval: 0
80+
approved_by: 0
81+
-
82+
id: 802
83+
title: "workflow run list"
84+
repo_id: 5
85+
owner_id: 3
86+
workflow_id: "test.yaml"
87+
index: 191
88+
trigger_user_id: 1
89+
ref: "refs/heads/test"
90+
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
91+
event: "push"
92+
trigger_event: "push"
93+
is_fork_pull_request: 0
94+
status: 1
95+
started: 1683636528
96+
stopped: 1683636626
97+
created: 1683636108
98+
updated: 1683636626
99+
need_approval: 0
100+
approved_by: 0
101+
-
102+
id: 803
103+
title: "workflow run list for user"
104+
repo_id: 2
105+
owner_id: 0
106+
workflow_id: "test.yaml"
107+
index: 192
108+
trigger_user_id: 1
109+
ref: "refs/heads/test"
110+
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
111+
event: "push"
112+
trigger_event: "push"
69113
is_fork_pull_request: 0
70114
status: 1
71115
started: 1683636528
@@ -86,6 +130,7 @@
86130
ref: "refs/heads/test"
87131
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
88132
event: "push"
133+
trigger_event: "push"
89134
is_fork_pull_request: 0
90135
status: 2
91136
started: 1683636528

models/fixtures/action_run_job.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,33 @@
9999
status: 2
100100
started: 1683636528
101101
stopped: 1683636626
102+
-
103+
id: 203
104+
run_id: 802
105+
repo_id: 5
106+
owner_id: 0
107+
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
108+
is_fork_pull_request: 0
109+
name: job2
110+
attempt: 1
111+
job_id: job2
112+
needs: '["job1"]'
113+
task_id: 51
114+
status: 5
115+
started: 1683636528
116+
stopped: 1683636626
117+
-
118+
id: 204
119+
run_id: 803
120+
repo_id: 2
121+
owner_id: 0
122+
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
123+
is_fork_pull_request: 0
124+
name: job2
125+
attempt: 1
126+
job_id: job2
127+
needs: '["job1"]'
128+
task_id: 51
129+
status: 5
130+
started: 1683636528
131+
stopped: 1683636626

models/webhook/webhook_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestWebhook_EventsArray(t *testing.T) {
7373
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
7474
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
7575
"pull_request_review_comment", "pull_request_sync", "pull_request_review_request", "wiki", "repository", "release",
76-
"package", "status", "workflow_job",
76+
"package", "status", "workflow_run", "workflow_job",
7777
},
7878
(&Webhook{
7979
HookEvent: &webhook_module.HookEvent{SendEverything: true},

modules/actions/workflows.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web
246246
webhook_module.HookEventPackage:
247247
return matchPackageEvent(payload.(*api.PackagePayload), evt)
248248

249+
case // workflow_run
250+
webhook_module.HookEventWorkflowRun:
251+
return matchWorkflowRunEvent(payload.(*api.WorkflowRunPayload), evt)
252+
249253
default:
250254
log.Warn("unsupported event %q", triggedEvent)
251255
return false
@@ -691,3 +695,53 @@ func matchPackageEvent(payload *api.PackagePayload, evt *jobparser.Event) bool {
691695
}
692696
return matchTimes == len(evt.Acts())
693697
}
698+
699+
func matchWorkflowRunEvent(payload *api.WorkflowRunPayload, evt *jobparser.Event) bool {
700+
// with no special filter parameters
701+
if len(evt.Acts()) == 0 {
702+
return true
703+
}
704+
705+
matchTimes := 0
706+
// all acts conditions should be satisfied
707+
for cond, vals := range evt.Acts() {
708+
switch cond {
709+
case "types":
710+
action := payload.Action
711+
for _, val := range vals {
712+
if glob.MustCompile(val, '/').Match(action) {
713+
matchTimes++
714+
break
715+
}
716+
}
717+
case "workflows":
718+
workflow := payload.Workflow
719+
patterns, err := workflowpattern.CompilePatterns(vals...)
720+
if err != nil {
721+
break
722+
}
723+
if !workflowpattern.Skip(patterns, []string{workflow.Name}, &workflowpattern.EmptyTraceWriter{}) {
724+
matchTimes++
725+
}
726+
case "branches":
727+
patterns, err := workflowpattern.CompilePatterns(vals...)
728+
if err != nil {
729+
break
730+
}
731+
if !workflowpattern.Skip(patterns, []string{payload.WorkflowRun.HeadBranch}, &workflowpattern.EmptyTraceWriter{}) {
732+
matchTimes++
733+
}
734+
case "branches-ignore":
735+
patterns, err := workflowpattern.CompilePatterns(vals...)
736+
if err != nil {
737+
break
738+
}
739+
if !workflowpattern.Filter(patterns, []string{payload.WorkflowRun.HeadBranch}, &workflowpattern.EmptyTraceWriter{}) {
740+
matchTimes++
741+
}
742+
default:
743+
log.Warn("workflow run event unsupported condition %q", cond)
744+
}
745+
}
746+
return matchTimes == len(evt.Acts())
747+
}

modules/structs/hook.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,22 @@ func (p *CommitStatusPayload) JSONPayload() ([]byte, error) {
470470
return json.MarshalIndent(p, "", " ")
471471
}
472472

473+
// WorkflowRunPayload represents a payload information of workflow run event.
474+
type WorkflowRunPayload struct {
475+
Action string `json:"action"`
476+
Workflow *ActionWorkflow `json:"workflow"`
477+
WorkflowRun *ActionWorkflowRun `json:"workflow_run"`
478+
PullRequest *PullRequest `json:"pull_request,omitempty"`
479+
Organization *Organization `json:"organization,omitempty"`
480+
Repo *Repository `json:"repository"`
481+
Sender *User `json:"sender"`
482+
}
483+
484+
// JSONPayload implements Payload
485+
func (p *WorkflowRunPayload) JSONPayload() ([]byte, error) {
486+
return json.MarshalIndent(p, "", " ")
487+
}
488+
473489
// WorkflowJobPayload represents a payload information of workflow job event.
474490
type WorkflowJobPayload struct {
475491
Action string `json:"action"`

modules/structs/repo_actions.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,39 @@ type ActionArtifact struct {
8686

8787
// ActionWorkflowRun represents a WorkflowRun
8888
type ActionWorkflowRun struct {
89-
ID int64 `json:"id"`
90-
RepositoryID int64 `json:"repository_id"`
91-
HeadSha string `json:"head_sha"`
89+
ID int64 `json:"id"`
90+
URL string `json:"url"`
91+
HTMLURL string `json:"html_url"`
92+
DisplayTitle string `json:"display_title"`
93+
Path string `json:"path"`
94+
Event string `json:"event"`
95+
RunAttempt int64 `json:"run_attempt"`
96+
RunNumber int64 `json:"run_number"`
97+
RepositoryID int64 `json:"repository_id,omitempty"`
98+
HeadSha string `json:"head_sha"`
99+
HeadBranch string `json:"head_branch,omitempty"`
100+
Status string `json:"status"`
101+
Actor *User `json:"actor,omitempty"`
102+
TriggerActor *User `json:"trigger_actor,omitempty"`
103+
Repository *Repository `json:"repository,omitempty"`
104+
HeadRepository *Repository `json:"head_repository,omitempty"`
105+
Conclusion string `json:"conclusion,omitempty"`
106+
// swagger:strfmt date-time
107+
StartedAt time.Time `json:"started_at"`
108+
// swagger:strfmt date-time
109+
CompletedAt time.Time `json:"completed_at"`
110+
}
111+
112+
// ActionWorkflowRunsResponse returns ActionWorkflowRuns
113+
type ActionWorkflowRunsResponse struct {
114+
Entries []*ActionWorkflowRun `json:"workflow_runs"`
115+
TotalCount int64 `json:"total_count"`
116+
}
117+
118+
// ActionWorkflowJobsResponse returns ActionWorkflowJobs
119+
type ActionWorkflowJobsResponse struct {
120+
Entries []*ActionWorkflowJob `json:"jobs"`
121+
TotalCount int64 `json:"total_count"`
92122
}
93123

94124
// ActionArtifactsResponse returns ActionArtifacts

modules/webhook/type.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const (
3838
HookEventPullRequestReview HookEventType = "pull_request_review"
3939
// Actions event only
4040
HookEventSchedule HookEventType = "schedule"
41+
HookEventWorkflowRun HookEventType = "workflow_run"
4142
HookEventWorkflowJob HookEventType = "workflow_job"
4243
)
4344

@@ -67,6 +68,7 @@ func AllEvents() []HookEventType {
6768
HookEventRelease,
6869
HookEventPackage,
6970
HookEventStatus,
71+
HookEventWorkflowRun,
7072
HookEventWorkflowJob,
7173
}
7274
}

options/locale/locale_en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,6 +2403,8 @@ settings.event_pull_request_review_request_desc = Pull request review requested
24032403
settings.event_pull_request_approvals = Pull Request Approvals
24042404
settings.event_pull_request_merge = Pull Request Merge
24052405
settings.event_header_workflow = Workflow Events
2406+
settings.event_workflow_run = Workflow Run
2407+
settings.event_workflow_run_desc = Gitea Actions Workflow run queued, waiting, in progress, or completed.
24062408
settings.event_workflow_job = Workflow Jobs
24072409
settings.event_workflow_job_desc = Gitea Actions Workflow job queued, waiting, in progress, or completed.
24082410
settings.event_package = Package

0 commit comments

Comments
 (0)