[FEAT] sourcehut webhooks
This commit is contained in:
@@ -115,6 +115,16 @@ type Repository struct {
|
||||
RepoTransfer *RepoTransfer `json:"repo_transfer"`
|
||||
}
|
||||
|
||||
// GetName implements the gitrepo.Repository interface
|
||||
func (r Repository) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// GetOwnerName implements the gitrepo.Repository interface
|
||||
func (r Repository) GetOwnerName() string {
|
||||
return r.Owner.UserName
|
||||
}
|
||||
|
||||
// CreateRepoOption options when creating repository
|
||||
// swagger:model
|
||||
type CreateRepoOption struct {
|
||||
|
||||
+13
-12
@@ -73,18 +73,19 @@ type HookType = string
|
||||
|
||||
// Types of webhooks
|
||||
const (
|
||||
FORGEJO HookType = "forgejo"
|
||||
GITEA HookType = "gitea"
|
||||
GOGS HookType = "gogs"
|
||||
SLACK HookType = "slack"
|
||||
DISCORD HookType = "discord"
|
||||
DINGTALK HookType = "dingtalk"
|
||||
TELEGRAM HookType = "telegram"
|
||||
MSTEAMS HookType = "msteams"
|
||||
FEISHU HookType = "feishu"
|
||||
MATRIX HookType = "matrix"
|
||||
WECHATWORK HookType = "wechatwork"
|
||||
PACKAGIST HookType = "packagist"
|
||||
FORGEJO HookType = "forgejo"
|
||||
GITEA HookType = "gitea"
|
||||
GOGS HookType = "gogs"
|
||||
SLACK HookType = "slack"
|
||||
DISCORD HookType = "discord"
|
||||
DINGTALK HookType = "dingtalk"
|
||||
TELEGRAM HookType = "telegram"
|
||||
MSTEAMS HookType = "msteams"
|
||||
FEISHU HookType = "feishu"
|
||||
MATRIX HookType = "matrix"
|
||||
WECHATWORK HookType = "wechatwork"
|
||||
PACKAGIST HookType = "packagist"
|
||||
SOURCEHUT_BUILDS HookType = "sourcehut_builds" //nolint:revive
|
||||
)
|
||||
|
||||
// HookStatus is the status of a web hook
|
||||
|
||||
@@ -640,6 +640,8 @@ target_branch_not_exist = Target branch does not exist.
|
||||
|
||||
admin_cannot_delete_self = You cannot delete yourself when you are an admin. Please remove your admin privileges first.
|
||||
|
||||
required_prefix = Input must start with "%s"
|
||||
|
||||
[user]
|
||||
change_avatar = Change your avatar…
|
||||
joined_on = Joined on %s
|
||||
@@ -2267,6 +2269,7 @@ settings.delete_team_tip = This team has access to all repositories and can't be
|
||||
settings.remove_team_success = The team's access to the repository has been removed.
|
||||
settings.add_webhook = Add webhook
|
||||
settings.add_webhook.invalid_channel_name = Webhook channel name cannot be empty and cannot contain only a # character.
|
||||
settings.add_webhook.invalid_path = Path must not contain a part that is "." or ".." or the empty string. It cannot start or end with a slash.
|
||||
settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server when certain Forgejo events trigger. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
|
||||
settings.webhook_deletion = Remove webhook
|
||||
settings.webhook_deletion_desc = Removing a webhook deletes its settings and delivery history. Continue?
|
||||
@@ -2382,6 +2385,12 @@ settings.web_hook_name_packagist = Packagist
|
||||
settings.packagist_username = Packagist username
|
||||
settings.packagist_api_token = API token
|
||||
settings.packagist_package_url = Packagist package URL
|
||||
settings.web_hook_name_sourcehut_builds = SourceHut Builds
|
||||
settings.sourcehut_builds.manifest_path = Build manifest path
|
||||
settings.sourcehut_builds.graphql_url = GraphQL URL (e.g. https://builds.sr.ht/query)
|
||||
settings.sourcehut_builds.visibility = Job visibility
|
||||
settings.sourcehut_builds.secrets = Secrets
|
||||
settings.sourcehut_builds.secrets_helper = Give the job access to the build secrets (requires the SECRETS:RO grant)
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
path { fill: black; }
|
||||
@media (prefers-color-scheme: dark) { path { fill: white; } }
|
||||
</style>
|
||||
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 345 B |
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -19,6 +20,8 @@ import (
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
)
|
||||
|
||||
var ErrPayloadTypeNotSupported = errors.New("unsupported webhook event")
|
||||
|
||||
// PayloadConvertor defines the interface to convert system payload to webhook payload
|
||||
type PayloadConvertor[T any] interface {
|
||||
Create(*api.CreatePayload) (T, error)
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sourcehut
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/webhook/shared"
|
||||
|
||||
"gitea.com/go-chi/binding"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type BuildsHandler struct{}
|
||||
|
||||
func (BuildsHandler) Type() webhook_module.HookType { return webhook_module.SOURCEHUT_BUILDS }
|
||||
func (BuildsHandler) Metadata(w *webhook_model.Webhook) any {
|
||||
s := &BuildsMeta{}
|
||||
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
||||
log.Error("sourcehut.BuildsHandler.Metadata(%d): %v", w.ID, err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (BuildsHandler) Icon(size int) template.HTML {
|
||||
return shared.ImgIcon("sourcehut.svg", size)
|
||||
}
|
||||
|
||||
type buildsForm struct {
|
||||
forms.WebhookCoreForm
|
||||
PayloadURL string `binding:"Required;ValidUrl"`
|
||||
ManifestPath string `binding:"Required"`
|
||||
Visibility string `binding:"Required;In(PUBLIC,UNLISTED,PRIVATE)"`
|
||||
Secrets bool
|
||||
}
|
||||
|
||||
var _ binding.Validator = &buildsForm{}
|
||||
|
||||
// Validate implements binding.Validator.
|
||||
func (f *buildsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||
ctx := gitea_context.GetWebContext(req)
|
||||
if !fs.ValidPath(f.ManifestPath) {
|
||||
errs = append(errs, binding.Error{
|
||||
FieldNames: []string{"ManifestPath"},
|
||||
Classification: "",
|
||||
Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_path"),
|
||||
})
|
||||
}
|
||||
if !strings.HasPrefix(f.AuthorizationHeader, "Bearer ") {
|
||||
errs = append(errs, binding.Error{
|
||||
FieldNames: []string{"AuthorizationHeader"},
|
||||
Classification: "",
|
||||
Message: ctx.Locale.TrString("form.required_prefix", "Bearer "),
|
||||
})
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (BuildsHandler) UnmarshalForm(bind func(any)) forms.WebhookForm {
|
||||
var form buildsForm
|
||||
bind(&form)
|
||||
|
||||
return forms.WebhookForm{
|
||||
WebhookCoreForm: form.WebhookCoreForm,
|
||||
URL: form.PayloadURL,
|
||||
ContentType: webhook_model.ContentTypeJSON,
|
||||
Secret: "",
|
||||
HTTPMethod: http.MethodPost,
|
||||
Metadata: &BuildsMeta{
|
||||
ManifestPath: form.ManifestPath,
|
||||
Visibility: form.Visibility,
|
||||
Secrets: form.Secrets,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
graphqlPayload[V any] struct {
|
||||
Query string `json:"query,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Variables V `json:"variables,omitempty"`
|
||||
}
|
||||
// buildsVariables according to https://man.sr.ht/builds.sr.ht/graphql.md
|
||||
buildsVariables struct {
|
||||
Manifest string `json:"manifest"`
|
||||
Tags []string `json:"tags"`
|
||||
Note string `json:"note"`
|
||||
Secrets bool `json:"secrets"`
|
||||
Execute bool `json:"execute"`
|
||||
Visibility string `json:"visibility"`
|
||||
}
|
||||
|
||||
// BuildsMeta contains the metadata for the webhook
|
||||
BuildsMeta struct {
|
||||
ManifestPath string `json:"manifest_path"`
|
||||
Visibility string `json:"visibility"`
|
||||
Secrets bool `json:"secrets"`
|
||||
}
|
||||
)
|
||||
|
||||
type sourcehutConvertor struct {
|
||||
ctx context.Context
|
||||
meta BuildsMeta
|
||||
}
|
||||
|
||||
var _ shared.PayloadConvertor[graphqlPayload[buildsVariables]] = sourcehutConvertor{}
|
||||
|
||||
func (BuildsHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
||||
meta := BuildsMeta{}
|
||||
if err := json.Unmarshal([]byte(w.Meta), &meta); err != nil {
|
||||
return nil, nil, fmt.Errorf("newSourcehutRequest meta json: %w", err)
|
||||
}
|
||||
pc := sourcehutConvertor{
|
||||
ctx: ctx,
|
||||
meta: meta,
|
||||
}
|
||||
return shared.NewJSONRequest(pc, w, t, false)
|
||||
}
|
||||
|
||||
// Create implements PayloadConvertor Create method
|
||||
func (pc sourcehutConvertor) Create(p *api.CreatePayload) (graphqlPayload[buildsVariables], error) {
|
||||
return pc.newPayload(p.Repo, p.Sha, p.Ref, p.RefType+" "+git.RefName(p.Ref).ShortName()+" created", true)
|
||||
}
|
||||
|
||||
// Delete implements PayloadConvertor Delete method
|
||||
func (pc sourcehutConvertor) Delete(_ *api.DeletePayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Fork implements PayloadConvertor Fork method
|
||||
func (pc sourcehutConvertor) Fork(_ *api.ForkPayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Push implements PayloadConvertor Push method
|
||||
func (pc sourcehutConvertor) Push(p *api.PushPayload) (graphqlPayload[buildsVariables], error) {
|
||||
return pc.newPayload(p.Repo, p.HeadCommit.ID, p.Ref, p.HeadCommit.Message, true)
|
||||
}
|
||||
|
||||
// Issue implements PayloadConvertor Issue method
|
||||
func (pc sourcehutConvertor) Issue(_ *api.IssuePayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// IssueComment implements PayloadConvertor IssueComment method
|
||||
func (pc sourcehutConvertor) IssueComment(_ *api.IssueCommentPayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// PullRequest implements PayloadConvertor PullRequest method
|
||||
func (pc sourcehutConvertor) PullRequest(_ *api.PullRequestPayload) (graphqlPayload[buildsVariables], error) {
|
||||
// TODO
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Review implements PayloadConvertor Review method
|
||||
func (pc sourcehutConvertor) Review(_ *api.PullRequestPayload, _ webhook_module.HookEventType) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Repository implements PayloadConvertor Repository method
|
||||
func (pc sourcehutConvertor) Repository(_ *api.RepositoryPayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Wiki implements PayloadConvertor Wiki method
|
||||
func (pc sourcehutConvertor) Wiki(_ *api.WikiPayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// Release implements PayloadConvertor Release method
|
||||
func (pc sourcehutConvertor) Release(_ *api.ReleasePayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
func (pc sourcehutConvertor) Package(_ *api.PackagePayload) (graphqlPayload[buildsVariables], error) {
|
||||
return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported
|
||||
}
|
||||
|
||||
// mustBuildManifest adjusts the manifest to submit to the builds service
|
||||
//
|
||||
// in case of an error the Error field will be set, to be visible by the end-user under recent deliveries
|
||||
func (pc sourcehutConvertor) newPayload(repo *api.Repository, commitID, ref, note string, trusted bool) (graphqlPayload[buildsVariables], error) {
|
||||
manifest, err := pc.buildManifest(repo, commitID, ref)
|
||||
if err != nil {
|
||||
if len(manifest) == 0 {
|
||||
return graphqlPayload[buildsVariables]{}, err
|
||||
}
|
||||
// the manifest contains an error for the user: log the actual error and construct the payload
|
||||
// the error will be visible under the "recent deliveries" of the webhook settings.
|
||||
log.Warn("sourcehut.builds: could not construct manifest for %s: %v", repo.FullName, err)
|
||||
msg := fmt.Sprintf("%s:%s %s", repo.FullName, ref, manifest)
|
||||
return graphqlPayload[buildsVariables]{
|
||||
Error: msg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
gitRef := git.RefName(ref)
|
||||
return graphqlPayload[buildsVariables]{
|
||||
Query: `mutation (
|
||||
$manifest: String!
|
||||
$tags: [String!]
|
||||
$note: String!
|
||||
$secrets: Boolean!
|
||||
$execute: Boolean!
|
||||
$visibility: Visibility!
|
||||
) {
|
||||
submit(
|
||||
manifest: $manifest
|
||||
tags: $tags
|
||||
note: $note
|
||||
secrets: $secrets
|
||||
execute: $execute
|
||||
visibility: $visibility
|
||||
) {
|
||||
id
|
||||
}
|
||||
}`, Variables: buildsVariables{
|
||||
Manifest: string(manifest),
|
||||
Tags: []string{repo.FullName, gitRef.RefType() + "/" + gitRef.ShortName(), pc.meta.ManifestPath},
|
||||
Note: note,
|
||||
Secrets: pc.meta.Secrets && trusted,
|
||||
Execute: trusted,
|
||||
Visibility: cmp.Or(pc.meta.Visibility, "PRIVATE"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildManifest adjusts the manifest to submit to the builds service
|
||||
// in case of an error the []byte might contain an error that can be displayed to the user
|
||||
func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRef string) ([]byte, error) {
|
||||
gitRepo, err := gitrepo.OpenRepository(pc.ctx, repo)
|
||||
if err != nil {
|
||||
msg := "could not open repository"
|
||||
return []byte(msg), fmt.Errorf(msg+": %w", err)
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
commit, err := gitRepo.GetCommit(commitID)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not get commit %q", commitID)
|
||||
return []byte(msg), fmt.Errorf(msg+": %w", err)
|
||||
}
|
||||
entry, err := commit.GetTreeEntryByPath(pc.meta.ManifestPath)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not open manifest %q", pc.meta.ManifestPath)
|
||||
return []byte(msg), fmt.Errorf(msg+": %w", err)
|
||||
}
|
||||
r, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not read manifest %q", pc.meta.ManifestPath)
|
||||
return []byte(msg), fmt.Errorf(msg+": %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
var manifest struct {
|
||||
Image string `yaml:"image"`
|
||||
Arch string `yaml:"arch,omitempty"`
|
||||
Packages []string `yaml:"packages,omitempty"`
|
||||
Repositories map[string]string `yaml:"repositories,omitempty"`
|
||||
Artifacts []string `yaml:"artifacts,omitempty"`
|
||||
Shell bool `yaml:"shell,omitempty"`
|
||||
Sources []string `yaml:"sources"`
|
||||
Tasks []map[string]string `yaml:"tasks"`
|
||||
Triggers []string `yaml:"triggers,omitempty"`
|
||||
Environment map[string]string `yaml:"environment"`
|
||||
Secrets []string `yaml:"secrets,omitempty"`
|
||||
Oauth string `yaml:"oauth,omitempty"`
|
||||
}
|
||||
if err := yaml.NewDecoder(r).Decode(&manifest); err != nil {
|
||||
msg := fmt.Sprintf("could not decode manifest %q", pc.meta.ManifestPath)
|
||||
return []byte(msg), fmt.Errorf(msg+": %w", err)
|
||||
}
|
||||
|
||||
if manifest.Environment == nil {
|
||||
manifest.Environment = make(map[string]string)
|
||||
}
|
||||
manifest.Environment["BUILD_SUBMITTER"] = "forgejo"
|
||||
manifest.Environment["BUILD_SUBMITTER_URL"] = setting.AppURL
|
||||
manifest.Environment["GIT_REF"] = gitRef
|
||||
|
||||
source := repo.CloneURL + "#" + commitID
|
||||
found := false
|
||||
for i, s := range manifest.Sources {
|
||||
if s == repo.CloneURL {
|
||||
manifest.Sources[i] = source
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
manifest.Sources = append(manifest.Sources, source)
|
||||
}
|
||||
|
||||
return yaml.Marshal(manifest)
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sourcehut
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
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/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
"code.gitea.io/gitea/services/webhook/shared"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func gitInit(t testing.TB) {
|
||||
if setting.Git.HomePath != "" {
|
||||
return
|
||||
}
|
||||
t.Cleanup(test.MockVariableValue(&setting.Git.HomePath, t.TempDir()))
|
||||
assert.NoError(t, git.InitSimple(context.Background()))
|
||||
}
|
||||
|
||||
func TestSourcehutBuildsPayload(t *testing.T) {
|
||||
gitInit(t)
|
||||
defer test.MockVariableValue(&setting.RepoRootPath, ".")()
|
||||
defer test.MockVariableValue(&setting.AppURL, "https://example.forgejo.org/")()
|
||||
|
||||
repo := &api.Repository{
|
||||
HTMLURL: "http://localhost:3000/testdata/repo",
|
||||
Name: "repo",
|
||||
FullName: "testdata/repo",
|
||||
Owner: &api.User{
|
||||
UserName: "testdata",
|
||||
},
|
||||
CloneURL: "http://localhost:3000/testdata/repo.git",
|
||||
}
|
||||
|
||||
pc := sourcehutConvertor{
|
||||
ctx: git.DefaultContext,
|
||||
meta: BuildsMeta{
|
||||
ManifestPath: "adjust me in each test",
|
||||
Visibility: "UNLISTED",
|
||||
Secrets: true,
|
||||
},
|
||||
}
|
||||
t.Run("Create/branch", func(t *testing.T) {
|
||||
p := &api.CreatePayload{
|
||||
Sha: "58771003157b81abc6bf41df0c5db4147a3e3c83",
|
||||
Ref: "refs/heads/test",
|
||||
RefType: "branch",
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
pc.meta.ManifestPath = "simple.yml"
|
||||
pl, err := pc.Create(p)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, buildsVariables{
|
||||
Manifest: `image: alpine/edge
|
||||
sources:
|
||||
- http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
|
||||
tasks:
|
||||
- say-hello: |
|
||||
echo hello
|
||||
- say-world: echo world
|
||||
environment:
|
||||
BUILD_SUBMITTER: forgejo
|
||||
BUILD_SUBMITTER_URL: https://example.forgejo.org/
|
||||
GIT_REF: refs/heads/test
|
||||
`,
|
||||
Note: "branch test created",
|
||||
Tags: []string{"testdata/repo", "branch/test", "simple.yml"},
|
||||
Secrets: true,
|
||||
Execute: true,
|
||||
Visibility: "UNLISTED",
|
||||
}, pl.Variables)
|
||||
})
|
||||
t.Run("Create/tag", func(t *testing.T) {
|
||||
p := &api.CreatePayload{
|
||||
Sha: "58771003157b81abc6bf41df0c5db4147a3e3c83",
|
||||
Ref: "refs/tags/v1.0.0",
|
||||
RefType: "tag",
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
pc.meta.ManifestPath = "simple.yml"
|
||||
pl, err := pc.Create(p)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, buildsVariables{
|
||||
Manifest: `image: alpine/edge
|
||||
sources:
|
||||
- http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
|
||||
tasks:
|
||||
- say-hello: |
|
||||
echo hello
|
||||
- say-world: echo world
|
||||
environment:
|
||||
BUILD_SUBMITTER: forgejo
|
||||
BUILD_SUBMITTER_URL: https://example.forgejo.org/
|
||||
GIT_REF: refs/tags/v1.0.0
|
||||
`,
|
||||
Note: "tag v1.0.0 created",
|
||||
Tags: []string{"testdata/repo", "tag/v1.0.0", "simple.yml"},
|
||||
Secrets: true,
|
||||
Execute: true,
|
||||
Visibility: "UNLISTED",
|
||||
}, pl.Variables)
|
||||
})
|
||||
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
p := &api.DeletePayload{}
|
||||
|
||||
pl, err := pc.Delete(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Fork", func(t *testing.T) {
|
||||
p := &api.ForkPayload{}
|
||||
|
||||
pl, err := pc.Fork(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Push/simple", func(t *testing.T) {
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/main",
|
||||
HeadCommit: &api.PayloadCommit{
|
||||
ID: "58771003157b81abc6bf41df0c5db4147a3e3c83",
|
||||
Message: "add simple",
|
||||
},
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
pc.meta.ManifestPath = "simple.yml"
|
||||
pl, err := pc.Push(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, buildsVariables{
|
||||
Manifest: `image: alpine/edge
|
||||
sources:
|
||||
- http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83
|
||||
tasks:
|
||||
- say-hello: |
|
||||
echo hello
|
||||
- say-world: echo world
|
||||
environment:
|
||||
BUILD_SUBMITTER: forgejo
|
||||
BUILD_SUBMITTER_URL: https://example.forgejo.org/
|
||||
GIT_REF: refs/heads/main
|
||||
`,
|
||||
Note: "add simple",
|
||||
Tags: []string{"testdata/repo", "branch/main", "simple.yml"},
|
||||
Secrets: true,
|
||||
Execute: true,
|
||||
Visibility: "UNLISTED",
|
||||
}, pl.Variables)
|
||||
})
|
||||
t.Run("Push/complex", func(t *testing.T) {
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/main",
|
||||
HeadCommit: &api.PayloadCommit{
|
||||
ID: "69b217caa89166a02b8cd368b64fb83a44720e14",
|
||||
Message: "replace simple with complex",
|
||||
},
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
pc.meta.ManifestPath = "complex.yaml"
|
||||
pc.meta.Visibility = "PRIVATE"
|
||||
pc.meta.Secrets = false
|
||||
pl, err := pc.Push(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, buildsVariables{
|
||||
Manifest: `image: archlinux
|
||||
packages:
|
||||
- nodejs
|
||||
- npm
|
||||
- rsync
|
||||
sources:
|
||||
- http://localhost:3000/testdata/repo.git#69b217caa89166a02b8cd368b64fb83a44720e14
|
||||
tasks: []
|
||||
environment:
|
||||
BUILD_SUBMITTER: forgejo
|
||||
BUILD_SUBMITTER_URL: https://example.forgejo.org/
|
||||
GIT_REF: refs/heads/main
|
||||
deploy: synapse@synapse-bt.org
|
||||
secrets:
|
||||
- 7ebab768-e5e4-4c9d-ba57-ec41a72c5665
|
||||
`,
|
||||
Note: "replace simple with complex",
|
||||
Tags: []string{"testdata/repo", "branch/main", "complex.yaml"},
|
||||
Secrets: false,
|
||||
Execute: true,
|
||||
Visibility: "PRIVATE",
|
||||
}, pl.Variables)
|
||||
})
|
||||
|
||||
t.Run("Push/error", func(t *testing.T) {
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/main",
|
||||
HeadCommit: &api.PayloadCommit{
|
||||
ID: "58771003157b81abc6bf41df0c5db4147a3e3c83",
|
||||
Message: "add simple",
|
||||
},
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
pc.meta.ManifestPath = "non-existing.yml"
|
||||
pl, err := pc.Push(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, graphqlPayload[buildsVariables]{
|
||||
Error: "testdata/repo:refs/heads/main could not open manifest \"non-existing.yml\"",
|
||||
}, pl)
|
||||
})
|
||||
|
||||
t.Run("Issue", func(t *testing.T) {
|
||||
p := &api.IssuePayload{}
|
||||
|
||||
p.Action = api.HookIssueOpened
|
||||
pl, err := pc.Issue(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
|
||||
p.Action = api.HookIssueClosed
|
||||
pl, err = pc.Issue(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("IssueComment", func(t *testing.T) {
|
||||
p := &api.IssueCommentPayload{}
|
||||
|
||||
pl, err := pc.IssueComment(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("PullRequest", func(t *testing.T) {
|
||||
p := &api.PullRequestPayload{}
|
||||
|
||||
pl, err := pc.PullRequest(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("PullRequestComment", func(t *testing.T) {
|
||||
p := &api.IssueCommentPayload{
|
||||
IsPull: true,
|
||||
}
|
||||
|
||||
pl, err := pc.IssueComment(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Review", func(t *testing.T) {
|
||||
p := &api.PullRequestPayload{}
|
||||
p.Action = api.HookIssueReviewed
|
||||
|
||||
pl, err := pc.Review(p, webhook_module.HookEventPullRequestReviewApproved)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Repository", func(t *testing.T) {
|
||||
p := &api.RepositoryPayload{}
|
||||
|
||||
pl, err := pc.Repository(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Package", func(t *testing.T) {
|
||||
p := &api.PackagePayload{}
|
||||
|
||||
pl, err := pc.Package(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Wiki", func(t *testing.T) {
|
||||
p := &api.WikiPayload{}
|
||||
|
||||
p.Action = api.HookWikiCreated
|
||||
pl, err := pc.Wiki(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
|
||||
p.Action = api.HookWikiEdited
|
||||
pl, err = pc.Wiki(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
|
||||
p.Action = api.HookWikiDeleted
|
||||
pl, err = pc.Wiki(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
|
||||
t.Run("Release", func(t *testing.T) {
|
||||
p := &api.ReleasePayload{}
|
||||
|
||||
pl, err := pc.Release(p)
|
||||
require.Equal(t, err, shared.ErrPayloadTypeNotSupported)
|
||||
require.Equal(t, pl, graphqlPayload[buildsVariables]{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSourcehutJSONPayload(t *testing.T) {
|
||||
gitInit(t)
|
||||
defer test.MockVariableValue(&setting.RepoRootPath, ".")()
|
||||
defer test.MockVariableValue(&setting.AppURL, "https://example.forgejo.org/")()
|
||||
|
||||
repo := &api.Repository{
|
||||
HTMLURL: "http://localhost:3000/testdata/repo",
|
||||
Name: "repo",
|
||||
FullName: "testdata/repo",
|
||||
Owner: &api.User{
|
||||
UserName: "testdata",
|
||||
},
|
||||
CloneURL: "http://localhost:3000/testdata/repo.git",
|
||||
}
|
||||
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/main",
|
||||
HeadCommit: &api.PayloadCommit{
|
||||
ID: "58771003157b81abc6bf41df0c5db4147a3e3c83",
|
||||
Message: "json test",
|
||||
},
|
||||
Repo: repo,
|
||||
}
|
||||
data, err := p.JSONPayload()
|
||||
require.NoError(t, err)
|
||||
|
||||
hook := &webhook_model.Webhook{
|
||||
RepoID: 3,
|
||||
IsActive: true,
|
||||
Type: webhook_module.MATRIX,
|
||||
URL: "https://sourcehut.example.com/api/jobs",
|
||||
Meta: `{"manifest_path":"simple.yml"}`,
|
||||
}
|
||||
task := &webhook_model.HookTask{
|
||||
HookID: hook.ID,
|
||||
EventType: webhook_module.HookEventPush,
|
||||
PayloadContent: string(data),
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
|
||||
req, reqBody, err := BuildsHandler{}.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, "/api/jobs", req.URL.Path)
|
||||
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
|
||||
var body graphqlPayload[buildsVariables]
|
||||
err = json.NewDecoder(req.Body).Decode(&body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "json test", body.Variables.Note)
|
||||
}
|
||||
|
||||
func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string) {
|
||||
t.Helper()
|
||||
|
||||
// Create a new repository
|
||||
repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{
|
||||
Name: name,
|
||||
Description: "Temporary Repo",
|
||||
AutoInit: true,
|
||||
Gitignores: "",
|
||||
License: "WTFPL",
|
||||
Readme: "Default",
|
||||
DefaultBranch: "main",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, repo)
|
||||
t.Cleanup(func() {
|
||||
repo_service.DeleteRepository(db.DefaultContext, owner, repo, false)
|
||||
})
|
||||
|
||||
if enabledUnits != nil || disabledUnits != nil {
|
||||
units := make([]repo_model.RepoUnit, len(enabledUnits))
|
||||
for i, unitType := range enabledUnits {
|
||||
units[i] = repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unitType,
|
||||
}
|
||||
}
|
||||
|
||||
err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, units, disabledUnits)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
var sha string
|
||||
if len(files) > 0 {
|
||||
resp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, owner, &files_service.ChangeRepoFilesOptions{
|
||||
Files: files,
|
||||
Message: "add files",
|
||||
OldBranch: "main",
|
||||
NewBranch: "main",
|
||||
Author: &files_service.IdentityOptions{
|
||||
Name: owner.Name,
|
||||
Email: owner.Email,
|
||||
},
|
||||
Committer: &files_service.IdentityOptions{
|
||||
Name: owner.Name,
|
||||
Email: owner.Email,
|
||||
},
|
||||
Dates: &files_service.CommitDateOptions{
|
||||
Author: time.Now(),
|
||||
Committer: time.Now(),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
sha = resp.Commit.SHA
|
||||
}
|
||||
|
||||
return repo, sha
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ref: refs/heads/main
|
||||
@@ -0,0 +1,4 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = true
|
||||
@@ -0,0 +1 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
||||
@@ -0,0 +1,6 @@
|
||||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+1
@@ -0,0 +1 @@
|
||||
x•NKjÃ0ìZ§xûBÑçɶ ”¬z�ççQã[FQÚ?"=A3óѲmk#ÏüÒ*@š²L3&²)ú”'D$ #²Î’ƒæ�Š½Ñ¼,#/³„8²Ov‰ƒzIN<Áu'¨‘[;—JŸ¥~á»Ð{þ#'Üe;.xëòƒÜ輋#[K¯Ö[kôy¯áßASq\DA›ìkƵÑïÚÎÔûúØ�~P¯kÙÍÂVO�
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
x=�Α‚0D=χ+φnBΊX�ΓΙ�hιVk¨%¥?_PγmήαΝΜ”b°ΗCΙΜ Ή±Dδ{΄
|
||||
;ƒµF’&«”q®λ™m¥“Β<Κ5e8§|α[‚ΑΓΘ/—™«␍O€„5¶¤ GYK)¦Ο\αiOΞKJ3—PΖ�ηjρΖU>�έVΣΟΫXΓήΡάƒηµ�7\p;Ό
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
x=ŽÍnà „{æ)ö^ÉZ,EUN}�ï&T¶A„¶yüÒõ6ßa¾™Tö=w˜ê�ˆÌ‚ŽÄ¢5‹çO‚ ²\ôm\¼uFT¥ÆG¼×ˆF;ƒ¦˜NQ¬^“[£ÕÖ“a“‚QôÞo¥ÁkiW~+p–ßpáíuãiàh¯ça²ˆðŒ3¢J?÷:7([þàVKÙà|ÍýòÍ™ÛT…ÖIÚ7 ÿëªÆu£Ä°Ó‘…ï>s¿ÁP޽Û=—C}Ë¢O»
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+1
@@ -0,0 +1 @@
|
||||
x=ЋKnГ0D»Ц)ё`иkЙ@PdХ{P2™Ё°-AQ›їк]АYјЗIeЯsmэKoDђЖ)¤™µ‘ґ8Ѕp gg44вlЉИFQ±ССБп•”F9ѓВ�жИV,“[ЈUЦО¤`~ф[iрVЪ•ЮњщчёРчєС4к+(Їф0Y)б$µ”"эМлФ lщ“Z-eѓу5чЛwПФ¦КёNЬюЩY»?V4Є&‚ЏМtпрИэC9ю=aШо№в™,PЎ
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
xENInÃ0ìY¯Ð»®—D§þ#È�¢ Û�,
|
||||
"$¿¯�¦É\fÁ™9ئ9~,+Lä-œã’¶»É€×=oìg�ô#ÿ&¯OUä‘Ðoß·³jöU!Î,ê¿êº®”DGP¨
|
||||
e>L‹¹Š·ç‹¡t[
|
||||
§•’þŽ”#?¼ÝßCú~²zà2!,¤¯qCtÔQëZ<�.@78Âö†»¾ïŒù\«I
|
||||
@@ -0,0 +1 @@
|
||||
69b217caa89166a02b8cd368b64fb83a44720e14
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/webhook/sourcehut"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
@@ -53,6 +54,7 @@ var webhookHandlers = []Handler{
|
||||
matrixHandler{},
|
||||
wechatworkHandler{},
|
||||
packagistHandler{},
|
||||
sourcehut.BuildsHandler{},
|
||||
}
|
||||
|
||||
// GetWebhookHandler return the handler for a given webhook type (nil if not found)
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
{{template "webhook/new/wechatwork" .}}
|
||||
{{else if eq .HookType "packagist"}}
|
||||
{{template "webhook/new/packagist" .}}
|
||||
{{else if eq .HookType "sourcehut_builds"}}
|
||||
{{template "webhook/new/sourcehut_builds" .}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<p>{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://sourcehut.org/" (ctx.Locale.Tr "repo.settings.web_hook_name_sourcehut_builds")}}</p>
|
||||
<form class="ui form" action="{{.BaseLink}}/{{or .Webhook.ID "sourcehut_builds/new"}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
|
||||
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.sourcehut_builds.graphql_url"}}</label>
|
||||
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
||||
</div>
|
||||
<div class="required field {{if .Err_ManifestPath}}error{{end}}">
|
||||
<label for="manifest_path">{{ctx.Locale.Tr "repo.settings.sourcehut_builds.manifest_path"}}</label>
|
||||
<input id="manifest_path" name="manifest_path" type="text" value="{{.HookMetadata.ManifestPath}}" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.sourcehut_builds.visibility"}}</label>
|
||||
<div class="ui selection dropdown">
|
||||
<input type="hidden" id="visibility" name="visibility" value="{{if .HookMetadata.Visibility}}{{.HookMetadata.Visibility}}{{else}}PRIVATE{{end}}">
|
||||
<div class="default text"></div>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<div class="item" data-value="PUBLIC">PUBLIC</div>
|
||||
<div class="item" data-value="UNLISTED">UNLISTED</div>
|
||||
<div class="item" data-value="PRIVATE">PRIVATE</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input name="secrets" type="checkbox" {{if .HookMetadata.Secrets}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.sourcehut_builds.secrets"}}</label>
|
||||
<span class="help">{{ctx.Locale.Tr "repo.settings.sourcehut_builds.secrets_helper"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{template "repo/settings/webhook/settings" .}}
|
||||
</form>
|
||||
@@ -238,6 +238,34 @@ func TestWebhookForms(t *testing.T) {
|
||||
"branch_filter": "packagist/*",
|
||||
"authorization_header": "Bearer 123456",
|
||||
}))
|
||||
|
||||
t.Run("sourcehut_builds/required", testWebhookForms("sourcehut_builds", session, map[string]string{
|
||||
"payload_url": "https://sourcehut_builds.example.com",
|
||||
"manifest_path": ".build.yml",
|
||||
"visibility": "PRIVATE",
|
||||
"authorization_header": "Bearer 123456",
|
||||
}, map[string]string{
|
||||
"authorization_header": "",
|
||||
}, map[string]string{
|
||||
"authorization_header": "token ",
|
||||
}, map[string]string{
|
||||
"manifest_path": "",
|
||||
}, map[string]string{
|
||||
"manifest_path": "/absolute",
|
||||
}, map[string]string{
|
||||
"visibility": "",
|
||||
}, map[string]string{
|
||||
"visibility": "INVALID",
|
||||
}))
|
||||
t.Run("sourcehut_builds/optional", testWebhookForms("sourcehut_builds", session, map[string]string{
|
||||
"payload_url": "https://sourcehut_builds.example.com",
|
||||
"manifest_path": ".build.yml",
|
||||
"visibility": "PRIVATE",
|
||||
"secrets": "on",
|
||||
|
||||
"branch_filter": "srht/*",
|
||||
"authorization_header": "Bearer 123456",
|
||||
}))
|
||||
}
|
||||
|
||||
func assertInput(t testing.TB, form *goquery.Selection, name string) string {
|
||||
@@ -247,7 +275,15 @@ func assertInput(t testing.TB, form *goquery.Selection, name string) string {
|
||||
t.Log(form.Html())
|
||||
t.Errorf("field <input name=%q /> found %d times, expected once", name, input.Length())
|
||||
}
|
||||
return input.AttrOr("value", "")
|
||||
switch input.AttrOr("type", "") {
|
||||
case "checkbox":
|
||||
if _, checked := input.Attr("checked"); checked {
|
||||
return "on"
|
||||
}
|
||||
return ""
|
||||
default:
|
||||
return input.AttrOr("value", "")
|
||||
}
|
||||
}
|
||||
|
||||
func testWebhookForms(name string, session *TestSession, validFields map[string]string, invalidPatches ...map[string]string) func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user