mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-01 07:10:36 +00:00
38b07d0c29
Repository create and delete already fire `repository` webhooks, rename did not. This adds the `renamed` action with `changes.name.from` carrying the previous name, and renders it in the chat converters. Actions workflows are unaffected, they still do not trigger on rename. AI assistance was used for the implementation and tests. Fixes https://github.com/go-gitea/gitea/issues/34891. Co-authored-by: roman s <roman.sukach@dust-labs.com> Co-authored-by: silverwind <me@silverwind.io>
218 lines
7.7 KiB
Go
218 lines
7.7 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package webhook
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
webhook_model "gitea.dev/models/webhook"
|
|
"gitea.dev/modules/git"
|
|
api "gitea.dev/modules/structs"
|
|
"gitea.dev/modules/util"
|
|
webhook_module "gitea.dev/modules/webhook"
|
|
|
|
dingtalk "gitea.com/lunny/dingtalk_webhook"
|
|
)
|
|
|
|
type (
|
|
DingtalkPayload dingtalk.Payload
|
|
dingtalkConvertor struct{}
|
|
)
|
|
|
|
// Create implements PayloadConvertor Create method
|
|
func (dc dingtalkConvertor) Create(p *api.CreatePayload) (DingtalkPayload, error) {
|
|
// created tag/branch
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
|
|
|
|
return createDingtalkPayload(title, title, "view ref "+refName, p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
|
|
}
|
|
|
|
// Delete implements PayloadConvertor Delete method
|
|
func (dc dingtalkConvertor) Delete(p *api.DeletePayload) (DingtalkPayload, error) {
|
|
// created tag/branch
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
|
|
|
|
return createDingtalkPayload(title, title, "view ref "+refName, p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
|
|
}
|
|
|
|
// Fork implements PayloadConvertor Fork method
|
|
func (dc dingtalkConvertor) Fork(p *api.ForkPayload) (DingtalkPayload, error) {
|
|
title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
|
|
|
|
return createDingtalkPayload(title, title, "view forked repo "+p.Repo.FullName, p.Repo.HTMLURL), nil
|
|
}
|
|
|
|
// Push implements PayloadConvertor Push method
|
|
func (dc dingtalkConvertor) Push(p *api.PushPayload) (DingtalkPayload, error) {
|
|
var (
|
|
branchName = git.RefName(p.Ref).ShortName()
|
|
commitDesc string
|
|
)
|
|
|
|
var titleLink, linkText string
|
|
if p.TotalCommits == 1 {
|
|
commitDesc = "1 new commit"
|
|
titleLink = p.Commits[0].URL
|
|
linkText = "view commit"
|
|
} else {
|
|
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
|
titleLink = p.CompareURL
|
|
linkText = "view commits"
|
|
}
|
|
if titleLink == "" {
|
|
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
|
|
}
|
|
|
|
title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
|
|
|
|
var text strings.Builder
|
|
// for each commit, generate attachment text
|
|
for i, commit := range p.Commits {
|
|
var authorName string
|
|
if commit.Author != nil {
|
|
authorName = " - " + commit.Author.Name
|
|
}
|
|
text.WriteString(fmt.Sprintf("[%s](%s) %s", commit.ID[:7], commit.URL,
|
|
strings.TrimRight(commit.Message, "\r\n")) + authorName)
|
|
// add linebreak to each commit but the last
|
|
if i < len(p.Commits)-1 {
|
|
text.WriteString("\r\n")
|
|
}
|
|
}
|
|
|
|
return createDingtalkPayload(title, text.String(), linkText, titleLink), nil
|
|
}
|
|
|
|
// Issue implements PayloadConvertor Issue method
|
|
func (dc dingtalkConvertor) Issue(p *api.IssuePayload) (DingtalkPayload, error) {
|
|
text, issueTitle, extraMarkdown, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+extraMarkdown, "view issue", p.Issue.HTMLURL), nil
|
|
}
|
|
|
|
// Wiki implements PayloadConvertor Wiki method
|
|
func (dc dingtalkConvertor) Wiki(p *api.WikiPayload) (DingtalkPayload, error) {
|
|
text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true)
|
|
url := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
|
|
|
|
return createDingtalkPayload(text, text, "view wiki", url), nil
|
|
}
|
|
|
|
// IssueComment implements PayloadConvertor IssueComment method
|
|
func (dc dingtalkConvertor) IssueComment(p *api.IssueCommentPayload) (DingtalkPayload, error) {
|
|
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+p.Comment.Body, "view issue comment", p.Comment.HTMLURL), nil
|
|
}
|
|
|
|
// PullRequest implements PayloadConvertor PullRequest method
|
|
func (dc dingtalkConvertor) PullRequest(p *api.PullRequestPayload) (DingtalkPayload, error) {
|
|
text, issueTitle, extraMarkdown, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+extraMarkdown, "view pull request", p.PullRequest.HTMLURL), nil
|
|
}
|
|
|
|
// Review implements PayloadConvertor Review method
|
|
func (dc dingtalkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (DingtalkPayload, error) {
|
|
var text, title string
|
|
switch p.Action {
|
|
case api.HookIssueReviewed:
|
|
action, err := parseHookPullRequestEventType(event)
|
|
if err != nil {
|
|
return DingtalkPayload{}, err
|
|
}
|
|
|
|
title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
|
|
text = p.Review.Content
|
|
}
|
|
|
|
return createDingtalkPayload(title, title+"\r\n\r\n"+text, "view pull request", p.PullRequest.HTMLURL), nil
|
|
}
|
|
|
|
// Repository implements PayloadConvertor Repository method
|
|
func (dc dingtalkConvertor) Repository(p *api.RepositoryPayload) (DingtalkPayload, error) {
|
|
switch p.Action {
|
|
case api.HookRepoCreated:
|
|
title := fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
|
|
return createDingtalkPayload(title, title, "view repository", p.Repository.HTMLURL), nil
|
|
case api.HookRepoDeleted:
|
|
title := fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
|
|
return DingtalkPayload{
|
|
MsgType: "text",
|
|
Text: struct {
|
|
Content string `json:"content"`
|
|
}{
|
|
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
|
|
}
|
|
|
|
// Release implements PayloadConvertor Release method
|
|
func (dc dingtalkConvertor) Release(p *api.ReleasePayload) (DingtalkPayload, error) {
|
|
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
|
|
}
|
|
|
|
func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, error) {
|
|
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
|
|
}
|
|
|
|
func (dc dingtalkConvertor) Status(p *api.CommitStatusPayload) (DingtalkPayload, error) {
|
|
text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(text, text, "Status Changed", p.TargetURL), nil
|
|
}
|
|
|
|
func (dingtalkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (DingtalkPayload, error) {
|
|
text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(text, text, "Workflow Run", p.WorkflowRun.HTMLURL), nil
|
|
}
|
|
|
|
func (dingtalkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (DingtalkPayload, error) {
|
|
text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
|
|
|
|
return createDingtalkPayload(text, text, "Workflow Job", p.WorkflowJob.HTMLURL), nil
|
|
}
|
|
|
|
func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload {
|
|
return DingtalkPayload{
|
|
MsgType: "actionCard",
|
|
ActionCard: dingtalk.ActionCard{
|
|
Text: strings.TrimSpace(text),
|
|
Title: strings.TrimSpace(title),
|
|
HideAvatar: "0",
|
|
SingleTitle: singleTitle,
|
|
|
|
// https://developers.dingtalk.com/document/app/message-link-description
|
|
// to open the link in browser, we should use this URL, otherwise the page is displayed inside DingTalk client, very difficult to visit non-public URLs.
|
|
SingleURL: "dingtalk://dingtalkclient/page/link?pc_slide=false&url=" + url.QueryEscape(singleURL),
|
|
},
|
|
}
|
|
}
|
|
|
|
func newDingtalkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
|
var pc payloadConvertor[DingtalkPayload] = dingtalkConvertor{}
|
|
return newJSONRequest(pc, w, t, true)
|
|
}
|
|
|
|
func init() {
|
|
RegisterWebhookRequester(webhook_module.DINGTALK, newDingtalkRequest)
|
|
}
|