diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 0301b08e1d..522c286571 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -424,6 +424,8 @@ type ChangesPayload struct { Body *ChangesFromPayload `json:"body,omitempty"` // Changes made to the reference Ref *ChangesFromPayload `json:"ref,omitempty"` + // Changes made to the repository name + Name *ChangesFromPayload `json:"name,omitempty"` // Changes made to the labels added AddedLabels []*Label `json:"added_labels"` // Changes made to the labels removed @@ -508,6 +510,8 @@ const ( HookRepoCreated HookRepoAction = "created" // HookRepoDeleted deleted HookRepoDeleted HookRepoAction = "deleted" + // HookRepoRenamed renamed + HookRepoRenamed HookRepoAction = "renamed" ) // RepositoryPayload payload for repository webhooks @@ -520,6 +524,8 @@ type RepositoryPayload struct { Organization *User `json:"organization"` // The user who performed the action Sender *User `json:"sender"` + // Changes made to the repository + Changes *ChangesPayload `json:"changes,omitempty"` } // JSONPayload JSON representation of the payload diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index ae14deee3a..df4086bd5c 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2298,7 +2298,7 @@ "repo.settings.event_force_push": "Force Push", "repo.settings.event_push_desc": "Git push to a repository.", "repo.settings.event_repository": "Repository", - "repo.settings.event_repository_desc": "Repository created or deleted.", + "repo.settings.event_repository_desc": "Repository created, deleted, or renamed.", "repo.settings.event_header_issue": "Issue Events", "repo.settings.event_issues": "Issues", "repo.settings.event_issues_desc": "Issue opened, closed, reopened, edited or deleted.", diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index 0403fdaa0f..d9991a2736 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -152,6 +152,9 @@ func (dc dingtalkConvertor) Repository(p *api.RepositoryPayload) (DingtalkPayloa Content: title, }, }, nil + case api.HookRepoRenamed: + title := fmt.Sprintf("[%s] Repository renamed from %s", p.Repository.FullName, getRepoRenamedFrom(p)) + return createDingtalkPayload(title, title, "view repository", p.Repository.HTMLURL), nil } return DingtalkPayload{}, nil diff --git a/services/webhook/discord.go b/services/webhook/discord.go index 9bbeab3fad..1b3911263a 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -241,6 +241,10 @@ func (d discordConvertor) Repository(p *api.RepositoryPayload) (DiscordPayload, case api.HookRepoDeleted: title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) color = redColor + case api.HookRepoRenamed: + title = fmt.Sprintf("[%s] Repository renamed from %s", p.Repository.FullName, getRepoRenamedFrom(p)) + url = p.Repository.HTMLURL + color = greenColor } return d.createPayload(p.Sender, title, "", url, color), nil diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 18651ade4a..f200e16bba 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -148,6 +148,9 @@ func (fc feishuConvertor) Repository(p *api.RepositoryPayload) (FeishuPayload, e case api.HookRepoDeleted: text = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) return newFeishuTextPayload(text), nil + case api.HookRepoRenamed: + text = fmt.Sprintf("[%s] Repository renamed from %s", p.Repository.FullName, getRepoRenamedFrom(p)) + return newFeishuTextPayload(text), nil } return FeishuPayload{}, nil diff --git a/services/webhook/general.go b/services/webhook/general.go index 327e36c120..127df37bdd 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -293,6 +293,13 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo return text, issueTitle, color } +func getRepoRenamedFrom(p *api.RepositoryPayload) string { + if p.Changes == nil || p.Changes.Name == nil { + return "" + } + return p.Changes.Name.From +} + func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) { refLink := linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version) diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index e9dc17aeaf..43f785326d 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -227,6 +227,8 @@ func (m matrixConvertor) Repository(p *api.RepositoryPayload) (MatrixPayload, er text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink) case api.HookRepoDeleted: text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink) + case api.HookRepoRenamed: + text = fmt.Sprintf("[%s] Repository renamed from %s by %s", repoLink, getRepoRenamedFrom(p), senderLink) } return m.newPayload(text) } diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index c7881444db..84085cf25f 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -259,6 +259,10 @@ func (m msteamsConvertor) Repository(p *api.RepositoryPayload) (MSTeamsPayload, case api.HookRepoDeleted: title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) color = yellowColor + case api.HookRepoRenamed: + title = fmt.Sprintf("[%s] Repository renamed from %s", p.Repository.FullName, getRepoRenamedFrom(p)) + url = p.Repository.HTMLURL + color = greenColor } return createMSTeamsPayload( diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 2586d09798..44cbe7747d 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -134,6 +134,18 @@ func (m *webhookNotifier) DeleteRepository(ctx context.Context, doer *user_model } } +func (m *webhookNotifier) RenameRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldRepoName string) { + if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ + Action: api.HookRepoRenamed, + Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}), + Organization: convert.ToUser(ctx, repo.MustOwner(ctx), nil), + Sender: convert.ToUser(ctx, doer, nil), + Changes: &api.ChangesPayload{Name: &api.ChangesFromPayload{From: oldRepoName}}, + }); err != nil { + log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err) + } +} + func (m *webhookNotifier) MigrateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) { // Add to hook queue for created repo after session commit. if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ diff --git a/services/webhook/slack.go b/services/webhook/slack.go index 23fd5a5b16..293336059e 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -276,6 +276,8 @@ func (s slackConvertor) Repository(p *api.RepositoryPayload) (SlackPayload, erro text = fmt.Sprintf("[%s] Repository created by %s", repoLink, senderLink) case api.HookRepoDeleted: text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink) + case api.HookRepoRenamed: + text = fmt.Sprintf("[%s] Repository renamed from %s by %s", repoLink, getRepoRenamedFrom(p), senderLink) } return s.createPayload(text, nil), nil diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index f49e7203e0..99f8aa7ced 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -114,6 +114,12 @@ func TestSlackPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[] Repository created by ", pl.Text) + + p.Action = api.HookRepoRenamed + p.Changes = &api.ChangesPayload{Name: &api.ChangesFromPayload{From: "old-repo"}} + pl, err = sc.Repository(p) + require.NoError(t, err) + assert.Equal(t, "[] Repository renamed from old-repo by ", pl.Text) }) t.Run("Package", func(t *testing.T) { diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go index cae9a883c7..4f1d98407d 100644 --- a/services/webhook/telegram.go +++ b/services/webhook/telegram.go @@ -154,6 +154,9 @@ func (t telegramConvertor) Repository(p *api.RepositoryPayload) (TelegramPayload case api.HookRepoDeleted: title = fmt.Sprintf("[%s] Repository deleted", html.EscapeString(p.Repository.FullName)) return createTelegramPayloadHTML(title), nil + case api.HookRepoRenamed: + title = fmt.Sprintf("[%s] Repository renamed from %s", htmlLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName), html.EscapeString(getRepoRenamedFrom(p))) + return createTelegramPayloadHTML(title), nil } return TelegramPayload{}, nil } diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go index 938be3b499..2bcfe1f3c8 100644 --- a/services/webhook/wechatwork.go +++ b/services/webhook/wechatwork.go @@ -150,6 +150,9 @@ func (wc wechatworkConvertor) Repository(p *api.RepositoryPayload) (WechatworkPa case api.HookRepoDeleted: title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) return newWechatworkMarkdownPayload(title), nil + case api.HookRepoRenamed: + title = fmt.Sprintf("[%s] Repository renamed from %s", p.Repository.FullName, getRepoRenamedFrom(p)) + return newWechatworkMarkdownPayload(title), nil } return WechatworkPayload{}, nil diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 0c58c0a4fc..6263f3ea17 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -859,6 +859,20 @@ func Test_WebhookRepository(t *testing.T) { assert.Equal(t, "org3", payloads[0].Organization.UserName) assert.Equal(t, "repo_new", payloads[0].Repository.Name) assert.Equal(t, "org3/repo_new", payloads[0].Repository.FullName) + + // 4. rename the repository and validate the webhook is triggered again + newName := "repo_renamed" + req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/org3/repo_new", &api.EditRepoOption{ + Name: &newName, + }).AddTokenAuth(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)) + MakeRequest(t, req, http.StatusOK) + + require.Len(t, payloads, 2) + assert.Equal(t, api.HookRepoRenamed, payloads[1].Action) + assert.Equal(t, newName, payloads[1].Repository.Name) + assert.Equal(t, "org3/"+newName, payloads[1].Repository.FullName) + require.NotNil(t, payloads[1].Changes.Name) + assert.Equal(t, "repo_new", payloads[1].Changes.Name.From) }) }