Skip to content

Commit a4676db

Browse files
authored
Fix commit status events (#33320)
Fix #32873 Fix #33201 ~Fix #33244~ ~Fix #33302~ depends on ~#33396~ A part of this PR should be backported to v1.23 manually.
1 parent 3c46cd6 commit a4676db

File tree

16 files changed

+130
-0
lines changed

16 files changed

+130
-0
lines changed

options/locale/locale_en-US.ini

+2
Original file line numberDiff line numberDiff line change
@@ -2331,6 +2331,8 @@ settings.event_fork = Fork
23312331
settings.event_fork_desc = Repository forked.
23322332
settings.event_wiki = Wiki
23332333
settings.event_wiki_desc = Wiki page created, renamed, edited or deleted.
2334+
settings.event_statuses = Statuses
2335+
settings.event_statuses_desc = Commit Status updated from the API.
23342336
settings.event_release = Release
23352337
settings.event_release_desc = Release published, updated or deleted in a repository.
23362338
settings.event_push = Push

routers/web/repo/setting/webhook.go

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
184184
webhook_module.HookEventWiki: form.Wiki,
185185
webhook_module.HookEventRepository: form.Repository,
186186
webhook_module.HookEventPackage: form.Package,
187+
webhook_module.HookEventStatus: form.Status,
187188
},
188189
BranchFilter: form.BranchFilter,
189190
}

services/forms/repo_form.go

+1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ type WebhookForm struct {
243243
Repository bool
244244
Release bool
245245
Package bool
246+
Status bool
246247
Active bool
247248
BranchFilter string `binding:"GlobPattern"`
248249
AuthorizationHeader string

services/webhook/dingtalk.go

+6
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, err
170170
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
171171
}
172172

173+
func (dc dingtalkConvertor) Status(p *api.CommitStatusPayload) (DingtalkPayload, error) {
174+
text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
175+
176+
return createDingtalkPayload(text, text, "Status Changed", p.TargetURL), nil
177+
}
178+
173179
func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload {
174180
return DingtalkPayload{
175181
MsgType: "actionCard",

services/webhook/discord.go

+6
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error)
265265
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
266266
}
267267

268+
func (d discordConvertor) Status(p *api.CommitStatusPayload) (DiscordPayload, error) {
269+
text, color := getStatusPayloadInfo(p, noneLinkFormatter, false)
270+
271+
return d.createPayload(p.Sender, text, "", p.TargetURL, color), nil
272+
}
273+
268274
func newDiscordRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
269275
meta := &DiscordMeta{}
270276
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {

services/webhook/feishu.go

+6
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error)
166166
return newFeishuTextPayload(text), nil
167167
}
168168

169+
func (fc feishuConvertor) Status(p *api.CommitStatusPayload) (FeishuPayload, error) {
170+
text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
171+
172+
return newFeishuTextPayload(text), nil
173+
}
174+
169175
func newFeishuRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
170176
var pc payloadConvertor[FeishuPayload] = feishuConvertor{}
171177
return newJSONRequest(pc, w, t, true)

services/webhook/general.go

+12
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,18 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, w
307307
return text, color
308308
}
309309

310+
func getStatusPayloadInfo(p *api.CommitStatusPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
311+
refLink := linkFormatter(p.TargetURL, p.Context+"["+p.SHA+"]:"+p.Description)
312+
313+
text = fmt.Sprintf("Commit Status changed: %s", refLink)
314+
color = greenColor
315+
if withSender {
316+
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
317+
}
318+
319+
return text, color
320+
}
321+
310322
// ToHook convert models.Webhook to api.Hook
311323
// This function is not part of the convert package to prevent an import cycle
312324
func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {

services/webhook/matrix.go

+7
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,13 @@ func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) {
244244
return m.newPayload(text)
245245
}
246246

247+
func (m matrixConvertor) Status(p *api.CommitStatusPayload) (MatrixPayload, error) {
248+
refLink := htmlLinkFormatter(p.TargetURL, p.Context+"["+p.SHA+"]:"+p.Description)
249+
text := fmt.Sprintf("Commit Status changed: %s", refLink)
250+
251+
return m.newPayload(text)
252+
}
253+
247254
var urlRegex = regexp.MustCompile(`<a [^>]*?href="([^">]*?)">(.*?)</a>`)
248255

249256
func getMessageBody(htmlText string) string {

services/webhook/msteams.go

+14
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error)
303303
), nil
304304
}
305305

306+
func (m msteamsConvertor) Status(p *api.CommitStatusPayload) (MSTeamsPayload, error) {
307+
title, color := getStatusPayloadInfo(p, noneLinkFormatter, false)
308+
309+
return createMSTeamsPayload(
310+
p.Repo,
311+
p.Sender,
312+
title,
313+
"",
314+
p.TargetURL,
315+
color,
316+
&MSTeamsFact{"CommitStatus:", p.Context},
317+
), nil
318+
}
319+
306320
func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) MSTeamsPayload {
307321
facts := make([]MSTeamsFact, 0, 2)
308322
if r != nil {

services/webhook/packagist.go

+4
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ func (pc packagistConvertor) Package(_ *api.PackagePayload) (PackagistPayload, e
110110
return PackagistPayload{}, nil
111111
}
112112

113+
func (pc packagistConvertor) Status(_ *api.CommitStatusPayload) (PackagistPayload, error) {
114+
return PackagistPayload{}, nil
115+
}
116+
113117
func newPackagistRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
114118
meta := &PackagistMeta{}
115119
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {

services/webhook/payloader.go

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type payloadConvertor[T any] interface {
2828
Release(*api.ReleasePayload) (T, error)
2929
Wiki(*api.WikiPayload) (T, error)
3030
Package(*api.PackagePayload) (T, error)
31+
Status(*api.CommitStatusPayload) (T, error)
3132
}
3233

3334
func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (t T, err error) {
@@ -77,6 +78,8 @@ func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module
7778
return convertUnmarshalledJSON(rc.Wiki, data)
7879
case webhook_module.HookEventPackage:
7980
return convertUnmarshalledJSON(rc.Package, data)
81+
case webhook_module.HookEventStatus:
82+
return convertUnmarshalledJSON(rc.Status, data)
8083
}
8184
return t, fmt.Errorf("newPayload unsupported event: %s", event)
8285
}

services/webhook/slack.go

+6
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ func (s slackConvertor) Package(p *api.PackagePayload) (SlackPayload, error) {
167167
return s.createPayload(text, nil), nil
168168
}
169169

170+
func (s slackConvertor) Status(p *api.CommitStatusPayload) (SlackPayload, error) {
171+
text, _ := getStatusPayloadInfo(p, SlackLinkFormatter, true)
172+
173+
return s.createPayload(text, nil), nil
174+
}
175+
170176
// Push implements payloadConvertor Push method
171177
func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) {
172178
// n new commits

services/webhook/telegram.go

+6
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro
174174
return createTelegramPayloadHTML(text), nil
175175
}
176176

177+
func (t telegramConvertor) Status(p *api.CommitStatusPayload) (TelegramPayload, error) {
178+
text, _ := getStatusPayloadInfo(p, htmlLinkFormatter, true)
179+
180+
return createTelegramPayloadHTML(text), nil
181+
}
182+
177183
func createTelegramPayloadHTML(msgHTML string) TelegramPayload {
178184
// https://core.telegram.org/bots/api#formatting-options
179185
return TelegramPayload{

services/webhook/wechatwork.go

+6
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload,
175175
return newWechatworkMarkdownPayload(text), nil
176176
}
177177

178+
func (wc wechatworkConvertor) Status(p *api.CommitStatusPayload) (WechatworkPayload, error) {
179+
text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
180+
181+
return newWechatworkMarkdownPayload(text), nil
182+
}
183+
178184
func newWechatworkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
179185
var pc payloadConvertor[WechatworkPayload] = wechatworkConvertor{}
180186
return newJSONRequest(pc, w, t, true)

templates/repo/settings/webhook/settings.tmpl

+11
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@
109109
</div>
110110
</div>
111111

112+
<!-- Status -->
113+
<div class="seven wide column">
114+
<div class="field">
115+
<div class="ui checkbox">
116+
<input name="status" type="checkbox" {{if .Webhook.HookEvents.Get "status"}}checked{{end}}>
117+
<label>{{ctx.Locale.Tr "repo.settings.event_statuses"}}</label>
118+
<span class="help">{{ctx.Locale.Tr "repo.settings.event_statuses_desc"}}</span>
119+
</div>
120+
</div>
121+
</div>
122+
112123
<!-- Issue Events -->
113124
<div class="fourteen wide column">
114125
<label>{{ctx.Locale.Tr "repo.settings.event_header_issue"}}</label>

tests/integration/repo_webhook_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
auth_model "code.gitea.io/gitea/models/auth"
1717
"code.gitea.io/gitea/models/repo"
1818
"code.gitea.io/gitea/models/unittest"
19+
"code.gitea.io/gitea/models/webhook"
1920
"code.gitea.io/gitea/modules/gitrepo"
2021
"code.gitea.io/gitea/modules/json"
2122
api "code.gitea.io/gitea/modules/structs"
@@ -66,6 +67,19 @@ func testAPICreateWebhookForRepo(t *testing.T, session *TestSession, userName, r
6667
MakeRequest(t, req, http.StatusCreated)
6768
}
6869

70+
func testCreateWebhookForRepo(t *testing.T, session *TestSession, webhookType, userName, repoName, url, eventKind string) {
71+
csrf := GetUserCSRFToken(t, session)
72+
req := NewRequestWithValues(t, "POST", "/"+userName+"/"+repoName+"/settings/hooks/"+webhookType+"/new", map[string]string{
73+
"_csrf": csrf,
74+
"payload_url": url,
75+
"events": eventKind,
76+
"active": "true",
77+
"content_type": fmt.Sprintf("%d", webhook.ContentTypeJSON),
78+
"http_method": "POST",
79+
})
80+
session.MakeRequest(t, req, http.StatusSeeOther)
81+
}
82+
6983
func testAPICreateWebhookForOrg(t *testing.T, session *TestSession, userName, url, event string) {
7084
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
7185
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs/"+userName+"/hooks", api.CreateHookOption{
@@ -562,3 +576,28 @@ func Test_WebhookStatus(t *testing.T) {
562576
assert.EqualValues(t, commitID, payloads[0].SHA)
563577
})
564578
}
579+
580+
func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
581+
var trigger string
582+
provider := newMockWebhookProvider(func(r *http.Request) {
583+
assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
584+
assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status")
585+
assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status")
586+
trigger = "push"
587+
}, http.StatusOK)
588+
defer provider.Close()
589+
590+
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
591+
// 1. create a new webhook with special webhook for repo1
592+
session := loginUser(t, "user2")
593+
594+
// create a push_only webhook from web UI
595+
testCreateWebhookForRepo(t, session, "gitea", "user2", "repo1", provider.URL(), "push_only")
596+
597+
// 2. trigger the webhook with a push action
598+
testCreateFile(t, session, "user2", "repo1", "master", "test_webhook_push.md", "# a test file for webhook push")
599+
600+
// 3. validate the webhook is triggered with right event
601+
assert.EqualValues(t, "push", trigger)
602+
})
603+
}

0 commit comments

Comments
 (0)