mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-01 01:20:17 +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>
416 lines
11 KiB
Go
416 lines
11 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package webhook
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"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"
|
|
)
|
|
|
|
type (
|
|
// MSTeamsFact for Fact Structure
|
|
MSTeamsFact struct {
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// MSTeamsSection is a MessageCard section
|
|
MSTeamsSection struct {
|
|
ActivityTitle string `json:"activityTitle"`
|
|
ActivitySubtitle string `json:"activitySubtitle"`
|
|
ActivityImage string `json:"activityImage"`
|
|
Facts []MSTeamsFact `json:"facts"`
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// MSTeamsAction is an action (creates buttons, links etc)
|
|
MSTeamsAction struct {
|
|
Type string `json:"@type"`
|
|
Name string `json:"name"`
|
|
Targets []MSTeamsActionTarget `json:"targets,omitempty"`
|
|
}
|
|
|
|
// MSTeamsActionTarget is the actual link to follow, etc
|
|
MSTeamsActionTarget struct {
|
|
Os string `json:"os"`
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
// MSTeamsPayload is the parent object
|
|
MSTeamsPayload struct {
|
|
Type string `json:"@type"`
|
|
Context string `json:"@context"`
|
|
ThemeColor string `json:"themeColor"`
|
|
Title string `json:"title"`
|
|
Summary string `json:"summary"`
|
|
Sections []MSTeamsSection `json:"sections"`
|
|
PotentialAction []MSTeamsAction `json:"potentialAction"`
|
|
}
|
|
)
|
|
|
|
type msteamsConvertor struct{}
|
|
|
|
// Create implements PayloadConvertor Create method
|
|
func (m msteamsConvertor) Create(p *api.CreatePayload) (MSTeamsPayload, error) {
|
|
// created tag/branch
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
|
|
greenColor,
|
|
&MSTeamsFact{p.RefType + ":", refName},
|
|
), nil
|
|
}
|
|
|
|
// Delete implements PayloadConvertor Delete method
|
|
func (m msteamsConvertor) Delete(p *api.DeletePayload) (MSTeamsPayload, error) {
|
|
// deleted tag/branch
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
|
|
yellowColor,
|
|
&MSTeamsFact{p.RefType + ":", refName},
|
|
), nil
|
|
}
|
|
|
|
// Fork implements PayloadConvertor Fork method
|
|
func (m msteamsConvertor) Fork(p *api.ForkPayload) (MSTeamsPayload, error) {
|
|
title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Repo.HTMLURL,
|
|
greenColor,
|
|
&MSTeamsFact{"Forkee:", p.Forkee.FullName},
|
|
), nil
|
|
}
|
|
|
|
// Push implements PayloadConvertor Push method
|
|
func (m msteamsConvertor) Push(p *api.PushPayload) (MSTeamsPayload, error) {
|
|
var (
|
|
branchName = git.RefName(p.Ref).ShortName()
|
|
commitDesc string
|
|
)
|
|
|
|
var titleLink string
|
|
if p.TotalCommits == 1 {
|
|
commitDesc = "1 new commit"
|
|
titleLink = p.Commits[0].URL
|
|
} else {
|
|
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
|
titleLink = p.CompareURL
|
|
}
|
|
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 {
|
|
fmt.Fprintf(&text, "[%s](%s) %s - %s", commit.ID[:7], commit.URL,
|
|
strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
|
|
// add linebreak to each commit but the last
|
|
if i < len(p.Commits)-1 {
|
|
text.WriteString("\n\n")
|
|
}
|
|
}
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
text.String(),
|
|
titleLink,
|
|
greenColor,
|
|
&MSTeamsFact{"Commit count:", strconv.Itoa(p.TotalCommits)},
|
|
), nil
|
|
}
|
|
|
|
// Issue implements PayloadConvertor Issue method
|
|
func (m msteamsConvertor) Issue(p *api.IssuePayload) (MSTeamsPayload, error) {
|
|
title, _, extraMarkdown, color := getIssuesPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
extraMarkdown,
|
|
p.Issue.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"Issue #:", strconv.FormatInt(p.Issue.ID, 10)},
|
|
), nil
|
|
}
|
|
|
|
// IssueComment implements PayloadConvertor IssueComment method
|
|
func (m msteamsConvertor) IssueComment(p *api.IssueCommentPayload) (MSTeamsPayload, error) {
|
|
title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
p.Comment.Body,
|
|
p.Comment.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"Issue #:", strconv.FormatInt(p.Issue.ID, 10)},
|
|
), nil
|
|
}
|
|
|
|
// PullRequest implements PayloadConvertor PullRequest method
|
|
func (m msteamsConvertor) PullRequest(p *api.PullRequestPayload) (MSTeamsPayload, error) {
|
|
title, _, extraMarkdown, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
facts := []*MSTeamsFact{
|
|
{"Pull request:", fmt.Sprintf("[#%d](%s)", p.PullRequest.Index, p.PullRequest.HTMLURL)},
|
|
}
|
|
|
|
if (p.Action == api.HookIssueReviewRequested || p.Action == api.HookIssueReviewRequestRemoved) && p.RequestedReviewer != nil {
|
|
reviewerName := p.RequestedReviewer.UserName
|
|
if p.RequestedReviewer.FullName != "" {
|
|
reviewerName += " (" + p.RequestedReviewer.FullName + ")"
|
|
}
|
|
facts = append(facts, &MSTeamsFact{"Requested Reviewer:", reviewerName})
|
|
}
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
extraMarkdown,
|
|
p.PullRequest.HTMLURL,
|
|
color,
|
|
facts...,
|
|
), nil
|
|
}
|
|
|
|
// Review implements PayloadConvertor Review method
|
|
func (m msteamsConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (MSTeamsPayload, error) {
|
|
var text, title string
|
|
var color int
|
|
switch p.Action {
|
|
case api.HookIssueReviewed:
|
|
action, err := parseHookPullRequestEventType(event)
|
|
if err != nil {
|
|
return MSTeamsPayload{}, err
|
|
}
|
|
|
|
title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
|
|
text = p.Review.Content
|
|
|
|
switch event {
|
|
case webhook_module.HookEventPullRequestReviewApproved:
|
|
color = greenColor
|
|
case webhook_module.HookEventPullRequestReviewRejected:
|
|
color = redColor
|
|
case webhook_module.HookEventPullRequestReviewComment:
|
|
color = greyColor
|
|
default:
|
|
color = yellowColor
|
|
}
|
|
}
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
text,
|
|
p.PullRequest.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"Pull request:", fmt.Sprintf("[#%d](%s)", p.PullRequest.Index, p.PullRequest.HTMLURL)},
|
|
), nil
|
|
}
|
|
|
|
// Repository implements PayloadConvertor Repository method
|
|
func (m msteamsConvertor) Repository(p *api.RepositoryPayload) (MSTeamsPayload, error) {
|
|
var title, url string
|
|
var color int
|
|
switch p.Action {
|
|
case api.HookRepoCreated:
|
|
title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
|
|
url = p.Repository.HTMLURL
|
|
color = greenColor
|
|
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(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
url,
|
|
color,
|
|
nil,
|
|
), nil
|
|
}
|
|
|
|
// Wiki implements PayloadConvertor Wiki method
|
|
func (m msteamsConvertor) Wiki(p *api.WikiPayload) (MSTeamsPayload, error) {
|
|
title, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page),
|
|
color,
|
|
), nil
|
|
}
|
|
|
|
// Release implements PayloadConvertor Release method
|
|
func (m msteamsConvertor) Release(p *api.ReleasePayload) (MSTeamsPayload, error) {
|
|
title, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Release.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"Tag:", p.Release.TagName},
|
|
), nil
|
|
}
|
|
|
|
func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error) {
|
|
title, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repository,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.Package.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"Package:", p.Package.Name},
|
|
), nil
|
|
}
|
|
|
|
func (m msteamsConvertor) Status(p *api.CommitStatusPayload) (MSTeamsPayload, error) {
|
|
title, color := getStatusPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.TargetURL,
|
|
color,
|
|
&MSTeamsFact{"CommitStatus:", p.Context},
|
|
), nil
|
|
}
|
|
|
|
func (msteamsConvertor) WorkflowRun(p *api.WorkflowRunPayload) (MSTeamsPayload, error) {
|
|
title, color := getWorkflowRunPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.WorkflowRun.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"WorkflowRun:", p.WorkflowRun.DisplayTitle},
|
|
), nil
|
|
}
|
|
|
|
func (msteamsConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MSTeamsPayload, error) {
|
|
title, color := getWorkflowJobPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
return createMSTeamsPayload(
|
|
p.Repo,
|
|
p.Sender,
|
|
title,
|
|
"",
|
|
p.WorkflowJob.HTMLURL,
|
|
color,
|
|
&MSTeamsFact{"WorkflowJob:", p.WorkflowJob.Name},
|
|
), nil
|
|
}
|
|
|
|
func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, extraFacts ...*MSTeamsFact) MSTeamsPayload {
|
|
facts := make([]MSTeamsFact, 0, len(extraFacts)+1)
|
|
if r != nil {
|
|
facts = append(facts, MSTeamsFact{
|
|
Name: "Repository:",
|
|
Value: fmt.Sprintf("[%s](%s)", r.FullName, r.HTMLURL),
|
|
})
|
|
}
|
|
for _, f := range extraFacts {
|
|
if f != nil {
|
|
facts = append(facts, *f)
|
|
}
|
|
}
|
|
|
|
return MSTeamsPayload{
|
|
Type: "MessageCard",
|
|
Context: "https://schema.org/extensions",
|
|
ThemeColor: fmt.Sprintf("%x", color),
|
|
Title: title,
|
|
Summary: title,
|
|
Sections: []MSTeamsSection{
|
|
{
|
|
ActivityTitle: s.FullName,
|
|
ActivitySubtitle: s.UserName,
|
|
ActivityImage: s.AvatarURL,
|
|
Text: text,
|
|
Facts: facts,
|
|
},
|
|
},
|
|
PotentialAction: []MSTeamsAction{
|
|
{
|
|
Type: "OpenUri",
|
|
Name: "View in Gitea",
|
|
Targets: []MSTeamsActionTarget{
|
|
{
|
|
Os: "default",
|
|
URI: actionTarget,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func newMSTeamsRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
|
var pc payloadConvertor[MSTeamsPayload] = msteamsConvertor{}
|
|
return newJSONRequest(pc, w, t, true)
|
|
}
|
|
|
|
func init() {
|
|
RegisterWebhookRequester(webhook_module.MSTEAMS, newMSTeamsRequest)
|
|
}
|