Compare commits

..

16 commits

Author SHA1 Message Date
Earl Warren f7b53ef959 Merge pull request 'Fix user mention processing' (#3565) from algernon/forgejo:call-me-maybe into forgejo
Some checks failed
/ release (push) Has been cancelled
testing / backend-checks (push) Has been cancelled
testing / frontend-checks (push) Has been cancelled
testing / test-unit (push) Has been cancelled
testing / test-mysql (push) Has been cancelled
testing / test-pgsql (push) Has been cancelled
testing / test-sqlite (push) Has been cancelled
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3565
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-04-30 12:24:07 +00:00
Earl Warren 9a3a3feb4c Merge pull request 'Add inline attachments to comments and prevent double handling of mails' (#3504) from Beowulf/forgejo:mail-inline-attachments-not-added-to-comment into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3504
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-04-30 12:19:42 +00:00
Gergely Nagy 9a01062ae2
Fix user mention processing
When mentioning a user, the markup post-processor did not handle the
case where the mentioned user did not exist well: it tried to skip to
the next node, which in turn, ended up skipping the rest of the line.

To fix this, lets skip just the mentioned, but non-existing user, and
continue processing the current node from there.

Fixes #3535.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
2024-04-30 12:51:30 +02:00
Beowulf 34134df3a7
added release notes 2024-04-30 12:05:22 +02:00
oliverpool 6ba60f61cb Merge pull request 'fix: webhook: send short ref on gitea create/delete payload' (#3558) from oliverpool/forgejo:webhook_gitea_fix into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3558
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-04-30 09:10:39 +00:00
Beowulf aeb544aff7
added test for reading inline attachments 2024-04-30 10:47:48 +02:00
oliverpool 0d3a9e6491 webhook: send short ref on gitea create/delete payload 2024-04-30 10:41:42 +02:00
oliverpool cb0f361171 test: webhook gitea tag creation ref 2024-04-30 10:41:38 +02:00
Earl Warren afb3bcaa8b Merge pull request 'fix: webhook getPayloadBranch' (#3555) from oliverpool/forgejo:webhook_ref_compat into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3555
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-04-30 08:15:42 +00:00
oliverpool df06904f4a webhook: fix getPayloadBranch 2024-04-30 09:38:35 +02:00
Earl Warren 425d64a023 Merge pull request 'Update module connectrpc.com/connect to v1.16.1' (#3491) from renovate/connectrpc.com-connect-1.x into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3491
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-04-30 07:10:13 +00:00
Earl Warren e58a4c9a76 Merge pull request 'Update dependency @stylistic/stylelint-plugin to v2.1.2' (#3544) from renovate/stylistic-stylelint-plugin-2.x into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3544
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2024-04-30 07:04:10 +00:00
Renovate Bot aba5fe36d8 Update dependency @stylistic/stylelint-plugin to v2.1.2 2024-04-30 00:11:35 +00:00
Beowulf b796694cd5
Skip already handled incoming emails
It seems like (at least on my machine) that every mail is processed
twice. Added a check if the email is already handled and if so, skip it.
2024-04-28 14:21:06 +02:00
Beowulf 162b840100
Add inline attachments to comments
If incoming email is configured and an email is sent, inline
attachments are currently not added to the comment if it has the
`Content-Disposition: inline` instead of
`Content-Disposition: attachment` as e.g. with Apple Mail.

This adds inline attachments (`Content-Disposition: inline`) that have a
filename as attachment to the comment.

Fixes #3496
2024-04-28 00:33:03 +02:00
Renovate Bot 01d9faefa5 Update module connectrpc.com/connect to v1.16.1 2024-04-27 00:07:16 +00:00
12 changed files with 346 additions and 30 deletions

2
go.mod
View file

@ -7,7 +7,7 @@ require (
code.gitea.io/gitea-vet v0.2.3 code.gitea.io/gitea-vet v0.2.3
code.gitea.io/sdk/gitea v0.17.1 code.gitea.io/sdk/gitea v0.17.1
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.15.0 connectrpc.com/connect v1.16.1
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669
gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098

4
go.sum
View file

@ -43,8 +43,8 @@ code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
connectrpc.com/connect v1.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo= connectrpc.com/connect v1.16.1 h1:rOdrK/RTI/7TVnn3JsVxt3n028MlTRwmK5Q4heSpjis=
connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA= connectrpc.com/connect v1.16.1/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=

View file

@ -623,10 +623,10 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) { if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention")) replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling node = node.NextSibling.NextSibling
start = 0
} else { } else {
node = node.NextSibling start = loc.End
} }
start = 0
} }
} }

View file

@ -50,6 +50,11 @@ func TestApostrophesInMentions(t *testing.T) {
assert.EqualValues(t, template.HTML("<p><a href=\"/mention-user\" rel=\"nofollow\">@mention-user</a>&#39;s comment</p>\n"), rendered) assert.EqualValues(t, template.HTML("<p><a href=\"/mention-user\" rel=\"nofollow\">@mention-user</a>&#39;s comment</p>\n"), rendered)
} }
func TestNonExistantUserMention(t *testing.T) {
rendered := RenderMarkdownToHtml(context.Background(), "@ThisUserDoesNotExist @mention-user")
assert.EqualValues(t, template.HTML("<p>@ThisUserDoesNotExist <a href=\"/mention-user\" rel=\"nofollow\">@mention-user</a></p>\n"), rendered)
}
func TestRenderCommitBody(t *testing.T) { func TestRenderCommitBody(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context

18
package-lock.json generated
View file

@ -68,7 +68,7 @@
"@playwright/test": "1.43.0", "@playwright/test": "1.43.0",
"@stoplight/spectral-cli": "6.11.1", "@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "1.7.2", "@stylistic/eslint-plugin-js": "1.7.2",
"@stylistic/stylelint-plugin": "2.1.1", "@stylistic/stylelint-plugin": "2.1.2",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"@vue/test-utils": "2.4.5", "@vue/test-utils": "2.4.5",
"eslint": "8.57.0", "eslint": "8.57.0",
@ -2145,19 +2145,19 @@
} }
}, },
"node_modules/@stylistic/stylelint-plugin": { "node_modules/@stylistic/stylelint-plugin": {
"version": "2.1.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.2.tgz",
"integrity": "sha512-xqHTmQZN7EbnFDW7jw0rAsdFNO4IRqvXhrh3qhUlIwF/x09Zm7kgs/ADktHxsTJYcw346PpGihsB0t4pZhpeHw==", "integrity": "sha512-JsSqu0Y3vsX+PBl+DwULxC0cIv9C1yIcq1MXkx7pBOGtTqU26a75I8MPYMiEYvrsXgsKLi65xVgy1iLVSZquJA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.3", "@csstools/css-tokenizer": "^2.2.4",
"@csstools/media-query-list-parser": "^2.1.7", "@csstools/media-query-list-parser": "^2.1.9",
"is-plain-object": "^5.0.0", "is-plain-object": "^5.0.0",
"postcss-selector-parser": "^6.0.15", "postcss-selector-parser": "^6.0.16",
"postcss-value-parser": "^4.2.0", "postcss-value-parser": "^4.2.0",
"style-search": "^0.1.0", "style-search": "^0.1.0",
"stylelint": "^16.2.1" "stylelint": "^16.4.0"
}, },
"engines": { "engines": {
"node": "^18.12 || >=20.9" "node": "^18.12 || >=20.9"

View file

@ -67,7 +67,7 @@
"@playwright/test": "1.43.0", "@playwright/test": "1.43.0",
"@stoplight/spectral-cli": "6.11.1", "@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "1.7.2", "@stylistic/eslint-plugin-js": "1.7.2",
"@stylistic/stylelint-plugin": "2.1.1", "@stylistic/stylelint-plugin": "2.1.2",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"@vue/test-utils": "2.4.5", "@vue/test-utils": "2.4.5",
"eslint": "8.57.0", "eslint": "8.57.0",

View file

@ -0,0 +1 @@
Fixed that inline attachments of emails (as they occur for example with Apple Mail) are not attached to comments.

View file

@ -219,6 +219,11 @@ loop:
} }
err := func() error { err := func() error {
if isAlreadyHandled(handledSet, msg) {
log.Debug("Skipping already handled message")
return nil
}
r := msg.GetBody(section) r := msg.GetBody(section)
if r == nil { if r == nil {
return fmt.Errorf("could not get body from message: %w", err) return fmt.Errorf("could not get body from message: %w", err)
@ -277,6 +282,11 @@ loop:
return nil return nil
} }
// isAlreadyHandled tests if the message was already handled
func isAlreadyHandled(handledSet *imap.SeqSet, msg *imap.Message) bool {
return handledSet.Contains(msg.SeqNum)
}
// isAutomaticReply tests if the headers indicate an automatic reply // isAutomaticReply tests if the headers indicate an automatic reply
func isAutomaticReply(env *enmime.Envelope) bool { func isAutomaticReply(env *enmime.Envelope) bool {
autoSubmitted := env.GetHeader("Auto-Submitted") autoSubmitted := env.GetHeader("Auto-Submitted")
@ -367,6 +377,14 @@ func getContentFromMailReader(env *enmime.Envelope) *MailContent {
Content: attachment.Content, Content: attachment.Content,
}) })
} }
for _, inline := range env.Inlines {
if inline.FileName != "" {
attachments = append(attachments, &Attachment{
Name: inline.FileName,
Content: inline.Content,
})
}
}
return &MailContent{ return &MailContent{
Content: reply.FromText(env.Text), Content: reply.FromText(env.Text),

View file

@ -7,10 +7,24 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/emersion/go-imap"
"github.com/jhillyerd/enmime" "github.com/jhillyerd/enmime"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestNotHandleTwice(t *testing.T) {
handledSet := new(imap.SeqSet)
msg := imap.NewMessage(90, []imap.FetchItem{imap.FetchBody})
handled := isAlreadyHandled(handledSet, msg)
assert.Equal(t, false, handled)
handledSet.AddNum(msg.SeqNum)
handled = isAlreadyHandled(handledSet, msg)
assert.Equal(t, true, handled)
}
func TestIsAutomaticReply(t *testing.T) { func TestIsAutomaticReply(t *testing.T) {
cases := []struct { cases := []struct {
Headers map[string]string Headers map[string]string
@ -95,6 +109,32 @@ func TestGetContentFromMailReader(t *testing.T) {
assert.Equal(t, "attachment.txt", content.Attachments[0].Name) assert.Equal(t, "attachment.txt", content.Attachments[0].Name)
assert.Equal(t, []byte("attachment content"), content.Attachments[0].Content) assert.Equal(t, []byte("attachment content"), content.Attachments[0].Content)
mailString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" +
"\r\n" +
"--message-boundary\r\n" +
"Content-Type: multipart/alternative; boundary=text-boundary\r\n" +
"\r\n" +
"--text-boundary\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Disposition: inline\r\n" +
"\r\n" +
"mail content\r\n" +
"--text-boundary--\r\n" +
"--message-boundary\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Disposition: inline; filename=attachment.txt\r\n" +
"\r\n" +
"attachment content\r\n" +
"--message-boundary--\r\n"
env, err = enmime.ReadEnvelope(strings.NewReader(mailString))
assert.NoError(t, err)
content = getContentFromMailReader(env)
assert.Equal(t, "mail content", content.Content)
assert.Len(t, content.Attachments, 1)
assert.Equal(t, "attachment.txt", content.Attachments[0].Name)
assert.Equal(t, []byte("attachment content"), content.Attachments[0].Content)
mailString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" + mailString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" +
"\r\n" + "\r\n" +
"--message-boundary\r\n" + "--message-boundary\r\n" +

View file

@ -12,6 +12,8 @@ import (
"strings" "strings"
webhook_model "code.gitea.io/gitea/models/webhook" webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/svg"
webhook_module "code.gitea.io/gitea/modules/webhook" webhook_module "code.gitea.io/gitea/modules/webhook"
@ -67,6 +69,18 @@ func (defaultHandler) UnmarshalForm(bind func(any)) forms.WebhookForm {
} }
func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) {
payloadContent := t.PayloadContent
if w.Type == webhook_module.GITEA &&
(t.EventType == webhook_module.HookEventCreate || t.EventType == webhook_module.HookEventDelete) {
// Woodpecker expects the ref to be short on tag creation only
// https://github.com/woodpecker-ci/woodpecker/blob/00ccec078cdced80cf309cd4da460a5041d7991a/server/forge/gitea/helper.go#L134
// see https://codeberg.org/codeberg/community/issues/1556
payloadContent, err = substituteRefShortName(payloadContent)
if err != nil {
return nil, nil, fmt.Errorf("could not substiture ref: %w", err)
}
}
switch w.HTTPMethod { switch w.HTTPMethod {
case "": case "":
log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID) log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID)
@ -74,7 +88,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook,
case http.MethodPost: case http.MethodPost:
switch w.ContentType { switch w.ContentType {
case webhook_model.ContentTypeJSON: case webhook_model.ContentTypeJSON:
req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent)) req, err = http.NewRequest("POST", w.URL, strings.NewReader(payloadContent))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -82,7 +96,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook,
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
case webhook_model.ContentTypeForm: case webhook_model.ContentTypeForm:
forms := url.Values{ forms := url.Values{
"payload": []string{t.PayloadContent}, "payload": []string{payloadContent},
} }
req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode())) req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode()))
@ -100,7 +114,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook,
return nil, nil, fmt.Errorf("invalid URL: %w", err) return nil, nil, fmt.Errorf("invalid URL: %w", err)
} }
vals := u.Query() vals := u.Query()
vals["payload"] = []string{t.PayloadContent} vals["payload"] = []string{payloadContent}
u.RawQuery = vals.Encode() u.RawQuery = vals.Encode()
req, err = http.NewRequest("GET", u.String(), nil) req, err = http.NewRequest("GET", u.String(), nil)
if err != nil { if err != nil {
@ -109,12 +123,12 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook,
case http.MethodPut: case http.MethodPut:
switch w.Type { switch w.Type {
case webhook_module.MATRIX: // used when t.Version == 1 case webhook_module.MATRIX: // used when t.Version == 1
txnID, err := getMatrixTxnID([]byte(t.PayloadContent)) txnID, err := getMatrixTxnID([]byte(payloadContent))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID))
req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent)) req, err = http.NewRequest("PUT", url, strings.NewReader(payloadContent))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -125,6 +139,22 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook,
return nil, nil, fmt.Errorf("invalid http method: %v", w.HTTPMethod) return nil, nil, fmt.Errorf("invalid http method: %v", w.HTTPMethod)
} }
body = []byte(t.PayloadContent) body = []byte(payloadContent)
return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body) return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body)
} }
func substituteRefShortName(body string) (string, error) {
var m map[string]any
if err := json.Unmarshal([]byte(body), &m); err != nil {
return body, err
}
ref, ok := m["ref"].(string)
if !ok {
return body, fmt.Errorf("expected string 'ref', got %T", m["ref"])
}
m["ref"] = git.RefName(ref).ShortName()
buf, err := json.Marshal(m)
return string(buf), err
}

View file

@ -0,0 +1,224 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package webhook
import (
"context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/json"
webhook_module "code.gitea.io/gitea/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGiteaPayload(t *testing.T) {
dh := defaultHandler{
forgejo: false,
}
hook := &webhook_model.Webhook{
RepoID: 3,
IsActive: true,
Type: webhook_module.GITEA,
URL: "https://gitea.example.com/",
Meta: ``,
HTTPMethod: "POST",
ContentType: webhook_model.ContentTypeJSON,
}
// Woodpecker expects the ref to be short on tag creation only
// https://github.com/woodpecker-ci/woodpecker/blob/00ccec078cdced80cf309cd4da460a5041d7991a/server/forge/gitea/helper.go#L134
// see https://codeberg.org/codeberg/community/issues/1556
t.Run("Create", func(t *testing.T) {
p := createTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventCreate,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://gitea.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "test", body.Ref) // short ref
})
t.Run("Push", func(t *testing.T) {
p := pushTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventPush,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://gitea.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "refs/heads/test", body.Ref) // full ref
})
t.Run("Delete", func(t *testing.T) {
p := deleteTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventDelete,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://gitea.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "test", body.Ref) // short ref
})
}
func TestForgejoPayload(t *testing.T) {
dh := defaultHandler{
forgejo: true,
}
hook := &webhook_model.Webhook{
RepoID: 3,
IsActive: true,
Type: webhook_module.FORGEJO,
URL: "https://forgejo.example.com/",
Meta: ``,
HTTPMethod: "POST",
ContentType: webhook_model.ContentTypeJSON,
}
// always return the full ref for consistency
t.Run("Create", func(t *testing.T) {
p := createTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventCreate,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://forgejo.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "refs/heads/test", body.Ref) // full ref
})
t.Run("Push", func(t *testing.T) {
p := pushTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventPush,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://forgejo.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "refs/heads/test", body.Ref) // full ref
})
t.Run("Delete", func(t *testing.T) {
p := deleteTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventDelete,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := dh.NewRequest(context.Background(), hook, task)
require.NoError(t, err)
require.NotNil(t, req)
require.NotNil(t, reqBody)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://forgejo.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body struct {
Ref string `json:"ref"`
}
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "refs/heads/test", body.Ref) // full ref
})
}

View file

@ -82,19 +82,17 @@ var hookQueue *queue.WorkerPoolQueue[int64]
// getPayloadBranch returns branch for hook event, if applicable. // getPayloadBranch returns branch for hook event, if applicable.
func getPayloadBranch(p api.Payloader) string { func getPayloadBranch(p api.Payloader) string {
var ref string
switch pp := p.(type) { switch pp := p.(type) {
case *api.CreatePayload: case *api.CreatePayload:
if pp.RefType == "branch" { ref = pp.Ref
return pp.Ref
}
case *api.DeletePayload: case *api.DeletePayload:
if pp.RefType == "branch" { ref = pp.Ref
return pp.Ref
}
case *api.PushPayload: case *api.PushPayload:
if strings.HasPrefix(pp.Ref, git.BranchPrefix) { ref = pp.Ref
return pp.Ref[len(git.BranchPrefix):] }
} if strings.HasPrefix(ref, git.BranchPrefix) {
return ref[len(git.BranchPrefix):]
} }
return "" return ""
} }