0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-12-03 14:27:36 +01:00

Add support for Bark webhook integration

This commit introduces the Bark notification integration, allowing users to configure Bark webhooks in repositories. It includes form models, templates, routing changes, and test cases to handle Bark webhook functionality.
This commit is contained in:
ZeroDeng 2025-10-21 12:13:56 +08:00
parent b2ee5be52e
commit e72d74244e
14 changed files with 646 additions and 1 deletions

View File

@ -35,7 +35,7 @@ func loadWebhookFrom(rootCfg ConfigProvider) {
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist", "bark"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
if Webhook.ProxyURL != "" {

View File

@ -114,6 +114,7 @@ const (
MATRIX HookType = "matrix"
WECHATWORK HookType = "wechatwork"
PACKAGIST HookType = "packagist"
BARK HookType = "bark"
)
// HookStatus is the status of a web hook

View File

@ -2467,9 +2467,14 @@ settings.web_hook_name_feishu = Feishu
settings.web_hook_name_larksuite = Lark Suite
settings.web_hook_name_wechatwork = WeCom (Wechat Work)
settings.web_hook_name_packagist = Packagist
settings.web_hook_name_bark = Bark
settings.packagist_username = Packagist username
settings.packagist_api_token = API token
settings.packagist_package_url = Packagist package URL
settings.bark_url = Bark URL
settings.bark_url_help = Full Bark URL including device key (e.g., https://api.day.app/your_device_key/)
settings.bark_sound = Sound (optional)
settings.bark_group = Group (optional)
settings.deploy_keys = Deploy Keys
settings.add_deploy_key = Add Deploy Key
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.

View File

@ -2465,9 +2465,14 @@ settings.web_hook_name_feishu=飞书
settings.web_hook_name_larksuite=Lark Suite
settings.web_hook_name_wechatwork=企业微信
settings.web_hook_name_packagist=Packagist
settings.web_hook_name_bark=Bark
settings.packagist_username=Packagist 用户名
settings.packagist_api_token=API 令牌
settings.packagist_package_url=Packagist 软件包 URL
settings.bark_url=Bark URL
settings.bark_url_help=完整的 Bark URL包含设备密钥例如https://api.day.app/your_device_key/
settings.bark_sound=提示音(可选)
settings.bark_group=分组(可选)
settings.deploy_keys=部署密钥
settings.add_deploy_key=添加部署密钥
settings.deploy_key_desc=部署密钥具有对仓库的只读拉取权限。

BIN
public/assets/img/bark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

View File

@ -581,6 +581,32 @@ func packagistHookParams(ctx *context.Context) webhookParams {
}
}
// BarkHooksNewPost response for creating Bark webhook
func BarkHooksNewPost(ctx *context.Context) {
createWebhook(ctx, barkHookParams(ctx))
}
// BarkHooksEditPost response for editing Bark webhook
func BarkHooksEditPost(ctx *context.Context) {
editWebhook(ctx, barkHookParams(ctx))
}
func barkHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewBarkHookForm)
return webhookParams{
Type: webhook_module.BARK,
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
HTTPMethod: http.MethodPost,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.BarkMeta{
Sound: form.Sound,
Group: form.Group,
},
}
}
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
orCtx, err := getOwnerRepoCtx(ctx)
if err != nil {
@ -619,6 +645,8 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
case webhook_module.PACKAGIST:
ctx.Data["PackagistHook"] = webhook_service.GetPackagistHook(w)
case webhook_module.BARK:
ctx.Data["BarkHook"] = webhook_service.GetBarkHook(w)
}
ctx.Data["History"], err = w.History(ctx, 1)

View File

@ -434,6 +434,7 @@ func registerWebRoutes(m *web.Router) {
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksNewPost)
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksNewPost)
m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksNewPost)
m.Post("/bark/new", web.Bind(forms.NewBarkHookForm{}), repo_setting.BarkHooksNewPost)
}
addWebhookEditRoutes := func() {
@ -448,6 +449,7 @@ func registerWebRoutes(m *web.Router) {
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksEditPost)
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksEditPost)
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksEditPost)
m.Post("/bark/{id}", web.Bind(forms.NewBarkHookForm{}), repo_setting.BarkHooksEditPost)
}
addSettingsVariablesRoutes := func() {

View File

@ -410,6 +410,20 @@ func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
// NewBarkHookForm form for creating Bark hook
type NewBarkHookForm struct {
PayloadURL string `binding:"Required;ValidUrl" form:"payload_url"`
Sound string `form:"sound"`
Group string `form:"group"`
WebhookForm
}
// Validate validates the fields
func (f *NewBarkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \

349
services/webhook/bark.go Normal file
View File

@ -0,0 +1,349 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package webhook
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
)
type (
// BarkPayload represents the payload for Bark notifications
BarkPayload struct {
Title string `json:"title"`
Body string `json:"body"`
URL string `json:"url,omitempty"`
Group string `json:"group,omitempty"`
Sound string `json:"sound,omitempty"`
Icon string `json:"icon,omitempty"`
}
// BarkMeta contains the metadata for the webhook
BarkMeta struct {
Sound string `json:"sound"`
Group string `json:"group"`
}
barkConvertor struct {
Sound string
Group string
}
)
// GetBarkHook returns bark metadata
func GetBarkHook(w *webhook_model.Webhook) *BarkMeta {
s := &BarkMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetBarkHook(%d): %v", w.ID, err)
}
return s
}
func (bc barkConvertor) getGroup(defaultGroup string) string {
if bc.Group != "" {
return bc.Group
}
return defaultGroup
}
// Create implements PayloadConvertor Create method
func (bc barkConvertor) Create(p *api.CreatePayload) (BarkPayload, error) {
refName := git.RefName(p.Ref).ShortName()
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
body := fmt.Sprintf("%s created %s %s", p.Sender.UserName, p.RefType, refName)
return BarkPayload{
Title: title,
Body: body,
URL: p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(refName),
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// Delete implements PayloadConvertor Delete method
func (bc barkConvertor) Delete(p *api.DeletePayload) (BarkPayload, error) {
refName := git.RefName(p.Ref).ShortName()
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
body := fmt.Sprintf("%s deleted %s %s", p.Sender.UserName, p.RefType, refName)
return BarkPayload{
Title: title,
Body: body,
URL: p.Repo.HTMLURL,
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// Fork implements PayloadConvertor Fork method
func (bc barkConvertor) Fork(p *api.ForkPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Repository forked", p.Forkee.FullName)
body := fmt.Sprintf("%s forked %s to %s", p.Sender.UserName, p.Forkee.FullName, p.Repo.FullName)
return BarkPayload{
Title: title,
Body: body,
URL: p.Repo.HTMLURL,
Group: bc.getGroup(p.Forkee.FullName),
Sound: bc.Sound,
}, nil
}
// Push implements PayloadConvertor Push method
func (bc barkConvertor) Push(p *api.PushPayload) (BarkPayload, error) {
branchName := git.RefName(p.Ref).ShortName()
var titleLink string
if p.TotalCommits == 1 {
titleLink = p.Commits[0].URL
} else {
titleLink = p.CompareURL
}
if titleLink == "" {
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
}
title := fmt.Sprintf("[%s:%s] %d new commit(s)", p.Repo.FullName, branchName, p.TotalCommits)
var body strings.Builder
body.WriteString(fmt.Sprintf("%s pushed to %s\n", p.Pusher.UserName, branchName))
for i, commit := range p.Commits {
body.WriteString(fmt.Sprintf("%s: %s", commit.ID[:7], strings.TrimRight(commit.Message, "\r\n")))
if commit.Author != nil {
body.WriteString(fmt.Sprintf(" - %s", commit.Author.Name))
}
if i < len(p.Commits)-1 {
body.WriteString("\n")
}
}
return BarkPayload{
Title: title,
Body: body.String(),
URL: titleLink,
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// Issue implements PayloadConvertor Issue method
func (bc barkConvertor) Issue(p *api.IssuePayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Issue #%d: %s", p.Repository.FullName, p.Index, p.Action)
body := fmt.Sprintf("%s %s issue #%d: %s", p.Sender.UserName, p.Action, p.Index, p.Issue.Title)
return BarkPayload{
Title: title,
Body: body,
URL: p.Issue.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Wiki implements PayloadConvertor Wiki method
func (bc barkConvertor) Wiki(p *api.WikiPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Wiki %s", p.Repository.FullName, p.Action)
body := fmt.Sprintf("%s %s wiki page: %s", p.Sender.UserName, p.Action, p.Page)
wikiURL := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
return BarkPayload{
Title: title,
Body: body,
URL: wikiURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// IssueComment implements PayloadConvertor IssueComment method
func (bc barkConvertor) IssueComment(p *api.IssueCommentPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] New comment on #%d", p.Repository.FullName, p.Issue.Index)
body := fmt.Sprintf("%s commented on issue #%d: %s\n%s",
p.Sender.UserName, p.Issue.Index, p.Issue.Title,
truncateString(p.Comment.Body, 100))
return BarkPayload{
Title: title,
Body: body,
URL: p.Comment.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// PullRequest implements PayloadConvertor PullRequest method
func (bc barkConvertor) PullRequest(p *api.PullRequestPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] PR #%d: %s", p.Repository.FullName, p.Index, p.Action)
body := fmt.Sprintf("%s %s pull request #%d: %s",
p.Sender.UserName, p.Action, p.Index, p.PullRequest.Title)
return BarkPayload{
Title: title,
Body: body,
URL: p.PullRequest.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Review implements PayloadConvertor Review method
func (bc barkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (BarkPayload, error) {
var action string
switch p.Action {
case api.HookIssueReviewed:
var err error
action, err = parseHookPullRequestEventType(event)
if err != nil {
return BarkPayload{}, err
}
}
title := fmt.Sprintf("[%s] PR #%d review %s", p.Repository.FullName, p.Index, action)
body := fmt.Sprintf("PR #%d: %s", p.Index, p.PullRequest.Title)
if p.Review != nil && p.Review.Content != "" {
body += "\n" + truncateString(p.Review.Content, 100)
}
return BarkPayload{
Title: title,
Body: body,
URL: p.PullRequest.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Repository implements PayloadConvertor Repository method
func (bc barkConvertor) Repository(p *api.RepositoryPayload) (BarkPayload, error) {
var title, body string
switch p.Action {
case api.HookRepoCreated:
title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
body = fmt.Sprintf("%s created repository", p.Sender.UserName)
case api.HookRepoDeleted:
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
body = fmt.Sprintf("%s deleted repository", p.Sender.UserName)
default:
return BarkPayload{}, nil
}
return BarkPayload{
Title: title,
Body: body,
URL: p.Repository.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Release implements PayloadConvertor Release method
func (bc barkConvertor) Release(p *api.ReleasePayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Release %s", p.Repository.FullName, p.Action)
body := fmt.Sprintf("%s %s release %s", p.Sender.UserName, p.Action, p.Release.TagName)
if p.Release.Title != "" {
body += ": " + p.Release.Title
}
return BarkPayload{
Title: title,
Body: body,
URL: p.Release.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Package implements PayloadConvertor Package method
func (bc barkConvertor) Package(p *api.PackagePayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Package %s", p.Repository.FullName, p.Action)
body := fmt.Sprintf("%s %s package %s:%s",
p.Sender.UserName, p.Action, p.Package.Name, p.Package.Version)
return BarkPayload{
Title: title,
Body: body,
URL: p.Package.HTMLURL,
Group: bc.getGroup(p.Repository.FullName),
Sound: bc.Sound,
}, nil
}
// Status implements PayloadConvertor Status method
func (bc barkConvertor) Status(p *api.CommitStatusPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Commit status: %s", p.Repo.FullName, p.State)
body := fmt.Sprintf("Commit %s: %s", base.ShortSha(p.SHA), p.Description)
if p.Context != "" {
body = fmt.Sprintf("%s (%s)", body, p.Context)
}
return BarkPayload{
Title: title,
Body: body,
URL: p.TargetURL,
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// WorkflowRun implements PayloadConvertor WorkflowRun method
func (bc barkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Workflow %s", p.Repo.FullName, p.WorkflowRun.Status)
body := fmt.Sprintf("Workflow '%s' %s", p.WorkflowRun.DisplayTitle, p.WorkflowRun.Status)
return BarkPayload{
Title: title,
Body: body,
URL: p.WorkflowRun.HTMLURL,
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// WorkflowJob implements PayloadConvertor WorkflowJob method
func (bc barkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (BarkPayload, error) {
title := fmt.Sprintf("[%s] Job %s", p.Repo.FullName, p.WorkflowJob.Status)
body := fmt.Sprintf("Job '%s' %s", p.WorkflowJob.Name, p.WorkflowJob.Status)
return BarkPayload{
Title: title,
Body: body,
URL: p.WorkflowJob.HTMLURL,
Group: bc.getGroup(p.Repo.FullName),
Sound: bc.Sound,
}, nil
}
// truncateString truncates a string to the specified length
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
func newBarkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
meta := &BarkMeta{}
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
return nil, nil, fmt.Errorf("newBarkRequest meta json: %w", err)
}
var pc payloadConvertor[BarkPayload] = barkConvertor{
Sound: meta.Sound,
Group: meta.Group,
}
return newJSONRequest(pc, w, t, true)
}
func init() {
RegisterWebhookRequester(webhook_module.BARK, newBarkRequest)
}

View File

@ -0,0 +1,214 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package webhook
import (
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBarkPayload(t *testing.T) {
bc := barkConvertor{}
t.Run("Create", func(t *testing.T) {
p := createTestPayload()
pl, err := bc.Create(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] branch test created", pl.Title)
assert.Equal(t, "user1 created branch test", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Delete", func(t *testing.T) {
p := deleteTestPayload()
pl, err := bc.Delete(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] branch test deleted", pl.Title)
assert.Equal(t, "user1 deleted branch test", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Fork", func(t *testing.T) {
p := forkTestPayload()
pl, err := bc.Fork(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo2] Repository forked", pl.Title)
assert.Equal(t, "user1 forked test/repo2 to test/repo", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo", pl.URL)
assert.Equal(t, "test/repo2", pl.Group)
})
t.Run("Push", func(t *testing.T) {
p := pushTestPayload()
pl, err := bc.Push(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo:test] 2 new commit(s)", pl.Title)
assert.Contains(t, pl.Body, "user1 pushed to test")
assert.Contains(t, pl.Body, "2020558: commit message - user1")
assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Issue", func(t *testing.T) {
p := issueTestPayload()
p.Action = api.HookIssueOpened
pl, err := bc.Issue(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Issue #2: opened", pl.Title)
assert.Equal(t, "user1 opened issue #2: crash", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
p.Action = api.HookIssueClosed
pl, err = bc.Issue(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Issue #2: closed", pl.Title)
assert.Equal(t, "user1 closed issue #2: crash", pl.Body)
})
t.Run("IssueComment", func(t *testing.T) {
p := issueCommentTestPayload()
pl, err := bc.IssueComment(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] New comment on #2", pl.Title)
assert.Contains(t, pl.Body, "user1 commented on issue #2: crash")
assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("PullRequest", func(t *testing.T) {
p := pullRequestTestPayload()
pl, err := bc.PullRequest(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] PR #12: opened", pl.Title)
assert.Equal(t, "user1 opened pull request #12: Fix bug", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("PullRequestComment", func(t *testing.T) {
p := pullRequestCommentTestPayload()
pl, err := bc.IssueComment(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] New comment on #12", pl.Title)
assert.Contains(t, pl.Body, "user1 commented")
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Review", func(t *testing.T) {
p := pullRequestTestPayload()
p.Action = api.HookIssueReviewed
pl, err := bc.Review(p, webhook_module.HookEventPullRequestReviewApproved)
require.NoError(t, err)
assert.Equal(t, "[test/repo] PR #12 review approved", pl.Title)
assert.Equal(t, "PR #12: Fix bug", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Repository", func(t *testing.T) {
p := repositoryTestPayload()
pl, err := bc.Repository(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Repository created", pl.Title)
assert.Equal(t, "user1 created repository", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Package", func(t *testing.T) {
p := packageTestPayload()
pl, err := bc.Package(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Package published", pl.Title)
assert.Contains(t, pl.Body, "user1 published package")
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Wiki", func(t *testing.T) {
p := wikiTestPayload()
p.Action = api.HookWikiCreated
pl, err := bc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Wiki created", pl.Title)
assert.Equal(t, "user1 created wiki page: index", pl.Body)
assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
pl, err := bc.Release(p)
require.NoError(t, err)
assert.Equal(t, "[test/repo] Release published", pl.Title)
assert.Contains(t, pl.Body, "user1 published release v1.0")
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.URL)
assert.Equal(t, "test/repo", pl.Group)
})
}
func TestBarkJSONPayload(t *testing.T) {
p := pushTestPayload()
data, err := p.JSONPayload()
require.NoError(t, err)
hook := &webhook_model.Webhook{
RepoID: 3,
IsActive: true,
Type: webhook_module.BARK,
URL: "https://api.day.app/devicekey/",
Meta: `{}`,
HTTPMethod: "POST",
}
task := &webhook_model.HookTask{
HookID: hook.ID,
EventType: webhook_module.HookEventPush,
PayloadContent: string(data),
PayloadVersion: 2,
}
req, reqBody, err := newBarkRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://api.day.app/devicekey/", req.URL.String())
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
}

View File

@ -0,0 +1,20 @@
{{if eq .HookType "bark"}}
<p>{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://github.com/Finb/Bark" (ctx.Locale.Tr "repo.settings.web_hook_name_bark")}}</p>
<form class="ui form" action="{{.BaseLink}}/bark/{{or .Webhook.ID "new"}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.bark_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" placeholder="https://api.day.app/your_device_key/" autofocus required>
<p class="help">{{ctx.Locale.Tr "repo.settings.bark_url_help"}}</p>
</div>
<div class="field">
<label for="sound">{{ctx.Locale.Tr "repo.settings.bark_sound"}}</label>
<input id="sound" name="sound" value="{{.BarkHook.Sound}}" placeholder="bell">
</div>
<div class="field">
<label for="group">{{ctx.Locale.Tr "repo.settings.bark_group"}}</label>
<input id="group" name="group" value="{{.BarkHook.Group}}" placeholder="Gitea">
</div>
{{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" false}}
</form>
{{end}}

View File

@ -47,4 +47,8 @@
{{template "shared/webhook/icon" (dict "HookType" "packagist" "Size" $size)}}
{{ctx.Locale.Tr "repo.settings.web_hook_name_packagist"}}
</a>
<a class="item" href="{{.BaseLinkNew}}/bark/new">
{{template "shared/webhook/icon" (dict "HookType" "bark" "Size" $size)}}
{{ctx.Locale.Tr "repo.settings.web_hook_name_bark"}}
</a>
</div>

View File

@ -24,4 +24,6 @@
<img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/wechatwork.png">
{{else if eq .HookType "packagist"}}
<img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/packagist.png">
{{else if eq .HookType "bark"}}
<img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/bark.png">
{{end}}

View File

@ -21,5 +21,6 @@
{{template "repo/settings/webhook/matrix" .ctxData}}
{{template "repo/settings/webhook/wechatwork" .ctxData}}
{{template "repo/settings/webhook/packagist" .ctxData}}
{{template "repo/settings/webhook/bark" .ctxData}}
</div>
{{template "repo/settings/webhook/history" .ctxData}}