mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-15 00:03:42 -04:00
Compare commits
10 Commits
457946d595
...
c71e8abbc3
Author | SHA1 | Date | |
---|---|---|---|
|
c71e8abbc3 | ||
|
72c60f94c1 | ||
|
02ef14bea2 | ||
|
ddf96f68cc | ||
|
da6df0d063 | ||
|
d44a415bf0 | ||
|
48e5a74f21 | ||
|
e409e14bdf | ||
|
50dc2d5f5f | ||
|
d5f007539a |
@ -25,10 +25,11 @@ env:
|
||||
es2022: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*"]
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
process: false # https://github.com/webpack/webpack/issues/15833
|
||||
- files: ["web_src/**/*", "docs/**/*"]
|
||||
env:
|
||||
browser: true
|
||||
|
9
Makefile
9
Makefile
@ -926,11 +926,18 @@ node_modules: package-lock.json
|
||||
|
||||
.PHONY: npm-update
|
||||
npm-update: node-check | node_modules
|
||||
npx updates -cu
|
||||
npx updates -u -f package.json
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
@touch node_modules
|
||||
|
||||
.PHONY: poetry-update
|
||||
poetry-update: node-check | node_modules
|
||||
npx updates -u -f pyproject.toml
|
||||
rm -rf .venv poetry.lock
|
||||
poetry install
|
||||
@touch .venv
|
||||
|
||||
.PHONY: fomantic
|
||||
fomantic:
|
||||
rm -rf $(FOMANTIC_WORK_DIR)/build
|
||||
|
@ -108,6 +108,14 @@ Admin operations:
|
||||
- `--all`, `-A`: Force a password change for all users
|
||||
- `--exclude username`, `-e username`: Exclude the given user. Can be set multiple times.
|
||||
- `--unset`: Revoke forced password change for the given users
|
||||
- `generate-access-token`:
|
||||
- Options:
|
||||
- `--username value`, `-u value`: Username. Required.
|
||||
- `--token-name value`, `-t value`: Token name. Required.
|
||||
- `--scopes value`: Comma-separated list of scopes. Scopes follow the format `[read|write]:<block>` or `all` where `<block>` is one of the available visual groups you can see when opening the API page showing the available routes (for example `repo`).
|
||||
- Examples:
|
||||
- `gitea admin user generate-access-token --username myname --token-name mytoken`
|
||||
- `gitea admin user generate-access-token --help`
|
||||
- `regenerate`
|
||||
- Options:
|
||||
- `hooks`: Regenerate Git Hooks for all repositories
|
||||
|
15
main.go
15
main.go
@ -47,7 +47,9 @@ func init() {
|
||||
// ./gitea -h
|
||||
// ./gitea web help
|
||||
// ./gitea web -h (due to cli lib limitation, this won't call our cmdHelp, so no extra info)
|
||||
// ./gitea admin help auth
|
||||
// ./gitea admin
|
||||
// ./gitea admin help
|
||||
// ./gitea admin auth help
|
||||
// ./gitea -c /tmp/app.ini -h
|
||||
// ./gitea -c /tmp/app.ini help
|
||||
// ./gitea help -c /tmp/app.ini
|
||||
@ -156,11 +158,7 @@ func prepareSubcommands(command *cli.Command, defaultFlags []cli.Flag) {
|
||||
|
||||
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
|
||||
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
|
||||
func prepareWorkPathAndCustomConf(a any) func(ctx *cli.Context) error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
action := a.(func(*cli.Context) error)
|
||||
func prepareWorkPathAndCustomConf(action any) func(ctx *cli.Context) error {
|
||||
return func(ctx *cli.Context) error {
|
||||
var args setting.ArgWorkPathAndCustomConf
|
||||
curCtx := ctx
|
||||
@ -177,10 +175,11 @@ func prepareWorkPathAndCustomConf(a any) func(ctx *cli.Context) error {
|
||||
curCtx = curCtx.Parent()
|
||||
}
|
||||
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
||||
if ctx.Bool("help") {
|
||||
if ctx.Bool("help") || action == nil {
|
||||
// the default behavior of "urfave/cli": "nil action" means "show help"
|
||||
return cmdHelp.Action.(func(ctx *cli.Context) error)(ctx)
|
||||
}
|
||||
return action(ctx)
|
||||
return action.(func(*cli.Context) error)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,13 @@ type ActionRun struct {
|
||||
TriggerUser *user_model.User `xorm:"-"`
|
||||
Ref string
|
||||
CommitSHA string
|
||||
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
|
||||
NeedApproval bool // may need approval if it's a fork pull request
|
||||
ApprovedBy int64 `xorm:"index"` // who approved
|
||||
Event webhook_module.HookEventType
|
||||
EventPayload string `xorm:"LONGTEXT"`
|
||||
Status Status `xorm:"index"`
|
||||
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
|
||||
NeedApproval bool // may need approval if it's a fork pull request
|
||||
ApprovedBy int64 `xorm:"index"` // who approved
|
||||
Event webhook_module.HookEventType // the webhook event that causes the workflow to run
|
||||
EventPayload string `xorm:"LONGTEXT"`
|
||||
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
|
||||
Status Status `xorm:"index"`
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
|
@ -503,9 +503,10 @@ var migrations = []Migration{
|
||||
|
||||
// v260 -> v261
|
||||
NewMigration("Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
|
||||
|
||||
// v261 -> v262
|
||||
NewMigration("Add variable table", v1_21.CreateVariableTable),
|
||||
// v262 -> v263
|
||||
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
16
models/migrations/v1_21/v262.go
Normal file
16
models/migrations/v1_21/v262.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_21 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddTriggerEventToActionRun(x *xorm.Engine) error {
|
||||
type ActionRun struct {
|
||||
TriggerEvent string
|
||||
}
|
||||
|
||||
return x.Sync(new(ActionRun))
|
||||
}
|
@ -8,33 +8,33 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
githubEventPullRequest = "pull_request"
|
||||
githubEventPullRequestTarget = "pull_request_target"
|
||||
githubEventPullRequestReviewComment = "pull_request_review_comment"
|
||||
githubEventPullRequestReview = "pull_request_review"
|
||||
githubEventRegistryPackage = "registry_package"
|
||||
githubEventCreate = "create"
|
||||
githubEventDelete = "delete"
|
||||
githubEventFork = "fork"
|
||||
githubEventPush = "push"
|
||||
githubEventIssues = "issues"
|
||||
githubEventIssueComment = "issue_comment"
|
||||
githubEventRelease = "release"
|
||||
githubEventPullRequestComment = "pull_request_comment"
|
||||
githubEventGollum = "gollum"
|
||||
GithubEventPullRequest = "pull_request"
|
||||
GithubEventPullRequestTarget = "pull_request_target"
|
||||
GithubEventPullRequestReviewComment = "pull_request_review_comment"
|
||||
GithubEventPullRequestReview = "pull_request_review"
|
||||
GithubEventRegistryPackage = "registry_package"
|
||||
GithubEventCreate = "create"
|
||||
GithubEventDelete = "delete"
|
||||
GithubEventFork = "fork"
|
||||
GithubEventPush = "push"
|
||||
GithubEventIssues = "issues"
|
||||
GithubEventIssueComment = "issue_comment"
|
||||
GithubEventRelease = "release"
|
||||
GithubEventPullRequestComment = "pull_request_comment"
|
||||
GithubEventGollum = "gollum"
|
||||
)
|
||||
|
||||
// canGithubEventMatch check if the input Github event can match any Gitea event.
|
||||
func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEventType) bool {
|
||||
switch eventName {
|
||||
case githubEventRegistryPackage:
|
||||
case GithubEventRegistryPackage:
|
||||
return triggedEvent == webhook_module.HookEventPackage
|
||||
|
||||
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#gollum
|
||||
case githubEventGollum:
|
||||
case GithubEventGollum:
|
||||
return triggedEvent == webhook_module.HookEventWiki
|
||||
|
||||
case githubEventIssues:
|
||||
case GithubEventIssues:
|
||||
switch triggedEvent {
|
||||
case webhook_module.HookEventIssues,
|
||||
webhook_module.HookEventIssueAssign,
|
||||
@ -46,7 +46,7 @@ func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEvent
|
||||
return false
|
||||
}
|
||||
|
||||
case githubEventPullRequest, githubEventPullRequestTarget:
|
||||
case GithubEventPullRequest, GithubEventPullRequestTarget:
|
||||
switch triggedEvent {
|
||||
case webhook_module.HookEventPullRequest,
|
||||
webhook_module.HookEventPullRequestSync,
|
||||
@ -58,7 +58,7 @@ func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEvent
|
||||
return false
|
||||
}
|
||||
|
||||
case githubEventPullRequestReview:
|
||||
case GithubEventPullRequestReview:
|
||||
switch triggedEvent {
|
||||
case webhook_module.HookEventPullRequestReviewApproved,
|
||||
webhook_module.HookEventPullRequestReviewComment,
|
||||
|
@ -21,85 +21,85 @@ func TestCanGithubEventMatch(t *testing.T) {
|
||||
// registry_package event
|
||||
{
|
||||
"registry_package matches",
|
||||
githubEventRegistryPackage,
|
||||
GithubEventRegistryPackage,
|
||||
webhook_module.HookEventPackage,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"registry_package cannot match",
|
||||
githubEventRegistryPackage,
|
||||
GithubEventRegistryPackage,
|
||||
webhook_module.HookEventPush,
|
||||
false,
|
||||
},
|
||||
// issues event
|
||||
{
|
||||
"issue matches",
|
||||
githubEventIssues,
|
||||
GithubEventIssues,
|
||||
webhook_module.HookEventIssueLabel,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"issue cannot match",
|
||||
githubEventIssues,
|
||||
GithubEventIssues,
|
||||
webhook_module.HookEventIssueComment,
|
||||
false,
|
||||
},
|
||||
// issue_comment event
|
||||
{
|
||||
"issue_comment matches",
|
||||
githubEventIssueComment,
|
||||
GithubEventIssueComment,
|
||||
webhook_module.HookEventIssueComment,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"issue_comment cannot match",
|
||||
githubEventIssueComment,
|
||||
GithubEventIssueComment,
|
||||
webhook_module.HookEventIssues,
|
||||
false,
|
||||
},
|
||||
// pull_request event
|
||||
{
|
||||
"pull_request matches",
|
||||
githubEventPullRequest,
|
||||
GithubEventPullRequest,
|
||||
webhook_module.HookEventPullRequestSync,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"pull_request cannot match",
|
||||
githubEventPullRequest,
|
||||
GithubEventPullRequest,
|
||||
webhook_module.HookEventPullRequestComment,
|
||||
false,
|
||||
},
|
||||
// pull_request_target event
|
||||
{
|
||||
"pull_request_target matches",
|
||||
githubEventPullRequest,
|
||||
GithubEventPullRequest,
|
||||
webhook_module.HookEventPullRequest,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"pull_request_target cannot match",
|
||||
githubEventPullRequest,
|
||||
GithubEventPullRequest,
|
||||
webhook_module.HookEventPullRequestComment,
|
||||
false,
|
||||
},
|
||||
// pull_request_review event
|
||||
{
|
||||
"pull_request_review matches",
|
||||
githubEventPullRequestReview,
|
||||
GithubEventPullRequestReview,
|
||||
webhook_module.HookEventPullRequestReviewComment,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"pull_request_review cannot match",
|
||||
githubEventPullRequestReview,
|
||||
GithubEventPullRequestReview,
|
||||
webhook_module.HookEventPullRequestComment,
|
||||
false,
|
||||
},
|
||||
// other events
|
||||
{
|
||||
"create event",
|
||||
githubEventCreate,
|
||||
GithubEventCreate,
|
||||
webhook_module.HookEventCreate,
|
||||
true,
|
||||
},
|
||||
|
@ -20,6 +20,14 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type DetectedWorkflow struct {
|
||||
EntryName string
|
||||
TriggerEvent string
|
||||
Commit *git.Commit
|
||||
Ref string
|
||||
Content []byte
|
||||
}
|
||||
|
||||
func init() {
|
||||
model.OnDecodeNodeError = func(node yaml.Node, out interface{}, err error) {
|
||||
// Log the error instead of panic or fatal.
|
||||
@ -89,13 +97,13 @@ func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) {
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) {
|
||||
func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) ([]*DetectedWorkflow, error) {
|
||||
entries, err := ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workflows := make(map[string][]byte, len(entries))
|
||||
workflows := make([]*DetectedWorkflow, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
content, err := GetContentFromEntry(entry)
|
||||
if err != nil {
|
||||
@ -109,7 +117,13 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy
|
||||
for _, evt := range events {
|
||||
log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent)
|
||||
if detectMatched(commit, triggedEvent, payload, evt) {
|
||||
workflows[entry.Name()] = content
|
||||
dwf := &DetectedWorkflow{
|
||||
EntryName: entry.Name(),
|
||||
TriggerEvent: evt.Name,
|
||||
Commit: commit,
|
||||
Content: content,
|
||||
}
|
||||
workflows = append(workflows, dwf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,77 +23,77 @@ func TestDetectMatched(t *testing.T) {
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "HookEventCreate(create) matches githubEventCreate(create)",
|
||||
desc: "HookEventCreate(create) matches GithubEventCreate(create)",
|
||||
triggedEvent: webhook_module.HookEventCreate,
|
||||
payload: nil,
|
||||
yamlOn: "on: create",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventIssues(issues) `opened` action matches githubEventIssues(issues)",
|
||||
desc: "HookEventIssues(issues) `opened` action matches GithubEventIssues(issues)",
|
||||
triggedEvent: webhook_module.HookEventIssues,
|
||||
payload: &api.IssuePayload{Action: api.HookIssueOpened},
|
||||
yamlOn: "on: issues",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventIssues(issues) `milestoned` action matches githubEventIssues(issues)",
|
||||
desc: "HookEventIssues(issues) `milestoned` action matches GithubEventIssues(issues)",
|
||||
triggedEvent: webhook_module.HookEventIssues,
|
||||
payload: &api.IssuePayload{Action: api.HookIssueMilestoned},
|
||||
yamlOn: "on: issues",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequestSync(pull_request_sync) matches githubEventPullRequest(pull_request)",
|
||||
desc: "HookEventPullRequestSync(pull_request_sync) matches GithubEventPullRequest(pull_request)",
|
||||
triggedEvent: webhook_module.HookEventPullRequestSync,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueSynchronized},
|
||||
yamlOn: "on: pull_request",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequest(pull_request) `label_updated` action doesn't match githubEventPullRequest(pull_request) with no activity type",
|
||||
desc: "HookEventPullRequest(pull_request) `label_updated` action doesn't match GithubEventPullRequest(pull_request) with no activity type",
|
||||
triggedEvent: webhook_module.HookEventPullRequest,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated},
|
||||
yamlOn: "on: pull_request",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequest(pull_request) `label_updated` action matches githubEventPullRequest(pull_request) with `label` activity type",
|
||||
desc: "HookEventPullRequest(pull_request) `label_updated` action matches GithubEventPullRequest(pull_request) with `label` activity type",
|
||||
triggedEvent: webhook_module.HookEventPullRequest,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated},
|
||||
yamlOn: "on:\n pull_request:\n types: [labeled]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequestReviewComment(pull_request_review_comment) matches githubEventPullRequestReviewComment(pull_request_review_comment)",
|
||||
desc: "HookEventPullRequestReviewComment(pull_request_review_comment) matches GithubEventPullRequestReviewComment(pull_request_review_comment)",
|
||||
triggedEvent: webhook_module.HookEventPullRequestReviewComment,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed},
|
||||
yamlOn: "on:\n pull_request_review_comment:\n types: [created]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPullRequestReviewRejected(pull_request_review_rejected) doesn't match githubEventPullRequestReview(pull_request_review) with `dismissed` activity type (we don't support `dismissed` at present)",
|
||||
desc: "HookEventPullRequestReviewRejected(pull_request_review_rejected) doesn't match GithubEventPullRequestReview(pull_request_review) with `dismissed` activity type (we don't support `dismissed` at present)",
|
||||
triggedEvent: webhook_module.HookEventPullRequestReviewRejected,
|
||||
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed},
|
||||
yamlOn: "on:\n pull_request_review:\n types: [dismissed]",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventRelease(release) `published` action matches githubEventRelease(release) with `published` activity type",
|
||||
desc: "HookEventRelease(release) `published` action matches GithubEventRelease(release) with `published` activity type",
|
||||
triggedEvent: webhook_module.HookEventRelease,
|
||||
payload: &api.ReleasePayload{Action: api.HookReleasePublished},
|
||||
yamlOn: "on:\n release:\n types: [published]",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "HookEventPackage(package) `created` action doesn't match githubEventRegistryPackage(registry_package) with `updated` activity type",
|
||||
desc: "HookEventPackage(package) `created` action doesn't match GithubEventRegistryPackage(registry_package) with `updated` activity type",
|
||||
triggedEvent: webhook_module.HookEventPackage,
|
||||
payload: &api.PackagePayload{Action: api.HookPackageCreated},
|
||||
yamlOn: "on:\n registry_package:\n types: [updated]",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "HookEventWiki(wiki) matches githubEventGollum(gollum)",
|
||||
desc: "HookEventWiki(wiki) matches GithubEventGollum(gollum)",
|
||||
triggedEvent: webhook_module.HookEventWiki,
|
||||
payload: nil,
|
||||
yamlOn: "on: gollum",
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
// Notifier defines an interface to notify receiver
|
||||
type Notifier interface {
|
||||
Run()
|
||||
NotifyAdoptRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository)
|
||||
NotifyCreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository)
|
||||
NotifyMigrateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository)
|
||||
NotifyDeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository)
|
||||
|
@ -145,6 +145,10 @@ func (*NullNotifier) NotifyIssueChangeLabels(ctx context.Context, doer *user_mod
|
||||
func (*NullNotifier) NotifyCreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
|
||||
}
|
||||
|
||||
// NotifyAdoptRepository places a place holder function
|
||||
func (*NullNotifier) NotifyAdoptRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
|
||||
}
|
||||
|
||||
// NotifyDeleteRepository places a place holder function
|
||||
func (*NullNotifier) NotifyDeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository) {
|
||||
}
|
||||
|
@ -29,6 +29,10 @@ func NewNotifier() base.Notifier {
|
||||
return &indexerNotifier{}
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) NotifyAdoptRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
|
||||
r.NotifyMigrateRepository(ctx, doer, u, repo)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) NotifyCreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
|
||||
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
|
||||
) {
|
||||
|
@ -274,6 +274,13 @@ func NotifyCreateRepository(ctx context.Context, doer, u *user_model.User, repo
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyAdoptRepository notifies the adoption of a repository to notifiers
|
||||
func NotifyAdoptRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyAdoptRepository(ctx, doer, u, repo)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyMigrateRepository notifies create repository to notifiers
|
||||
func NotifyMigrateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
|
||||
for _, notifier := range notifiers {
|
||||
|
@ -696,6 +696,7 @@ requires_activation = Requires activation
|
||||
primary_email = Make Primary
|
||||
activate_email = Send Activation
|
||||
activations_pending = Activations Pending
|
||||
can_not_add_email_activations_pending = There is a pending activation, try again in a few minutes if you want to add a new email.
|
||||
delete_email = Remove
|
||||
email_deletion = Remove Email Address
|
||||
email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
|
||||
|
@ -3122,7 +3122,7 @@ notices.delete_success=システム通知を削除しました。
|
||||
|
||||
[action]
|
||||
create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
|
||||
rename_repo=がリポジトリ名を <code>%[1]s</code> から <a href="%[2]s">[3]s</a> へ変更しました
|
||||
rename_repo=がリポジトリ名を <code>%[1]s</code> から <a href="%[2]s">%[3]s</a> へ変更しました
|
||||
commit_repo=が <a href="%[1]s">%[4]s</a> の <a href="%[2]s">%[3]s</a> にプッシュしました
|
||||
create_issue=`がイシュー <a href="%[1]s">%[3]s#%[2]s</a> をオープンしました`
|
||||
close_issue=`がイシュー <a href="%[1]s">%[3]s#%[2]s</a> をクローズしました`
|
||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -41,6 +41,7 @@
|
||||
"swagger-ui-dist": "5.0.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vue": "3.3.4",
|
||||
@ -77,7 +78,7 @@
|
||||
"stylelint-declaration-strict-value": "1.9.2",
|
||||
"stylelint-stylistic": "0.4.2",
|
||||
"svgo": "3.0.2",
|
||||
"updates": "14.2.4",
|
||||
"updates": "14.2.8",
|
||||
"vitest": "0.32.2"
|
||||
},
|
||||
"engines": {
|
||||
@ -10122,6 +10123,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toastify-js": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
|
||||
"integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
@ -10363,9 +10369,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/updates": {
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-14.2.4.tgz",
|
||||
"integrity": "sha512-r54h4Q12lUAmQ9dENy7BnY22AnTfW4YGEZw73gv6RvNEWgcZ3qS88jPLc1ckPAzt/8TPKWwLkSVpbEpgGwglJw==",
|
||||
"version": "14.2.8",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-14.2.8.tgz",
|
||||
"integrity": "sha512-Ca+M1vKKBBRiQSi3yrN8OdncmP9osIf1oJM/HpEIHeDvyGLs/noTi9X2LS4zl50VXRTSCqssF5CZN0XWzSPigg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"updates": "bin/updates.js"
|
||||
|
@ -40,6 +40,7 @@
|
||||
"swagger-ui-dist": "5.0.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vue": "3.3.4",
|
||||
@ -76,7 +77,7 @@
|
||||
"stylelint-declaration-strict-value": "1.9.2",
|
||||
"stylelint-stylistic": "0.4.2",
|
||||
"svgo": "3.0.2",
|
||||
"updates": "14.2.4",
|
||||
"updates": "14.2.8",
|
||||
"vitest": "0.32.2"
|
||||
},
|
||||
"browserslist": [
|
||||
|
8
poetry.lock
generated
8
poetry.lock
generated
@ -42,13 +42,13 @@ six = ">=1.13.0"
|
||||
|
||||
[[package]]
|
||||
name = "djlint"
|
||||
version = "1.31.0"
|
||||
version = "1.31.1"
|
||||
description = "HTML Template Linter and Formatter"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0,<4.0.0"
|
||||
files = [
|
||||
{file = "djlint-1.31.0-py3-none-any.whl", hash = "sha256:2b9200c67103b79835b7547ff732e910888d1f0ef684f5b329eb64b14d09c046"},
|
||||
{file = "djlint-1.31.0.tar.gz", hash = "sha256:8acb4b751b429c5aabb1aef5b6007bdf53224eceda25c5fbe04c42cc57c0a7ba"},
|
||||
{file = "djlint-1.31.1-py3-none-any.whl", hash = "sha256:9b2e2fc3a059a8e5a62f309edea15c1aeee331a279ab2699b9fb51a31d8c0934"},
|
||||
{file = "djlint-1.31.1.tar.gz", hash = "sha256:a11739e2f919f760b3986eb13d06e00171f3bd342b8d88e9bd914a4260eaa8ce"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -328,4 +328,4 @@ telegram = ["requests"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "22c4af11eadd8784b613951d6160d67be0f33500238a450741c3d75beb218dad"
|
||||
content-hash = "f03ad8e7c4f6e797ac3c04630db8cc16438cd59642653c26fd401633cd62d696"
|
||||
|
@ -8,7 +8,7 @@ authors = []
|
||||
python = "^3.8"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
djlint = "1.31.0"
|
||||
djlint = "1.31.1"
|
||||
|
||||
[tool.djlint]
|
||||
profile="golang"
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
actions_module "code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@ -54,8 +55,10 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
|
||||
|
||||
func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string {
|
||||
secrets := map[string]string{}
|
||||
if task.Job.Run.IsForkPullRequest {
|
||||
if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
||||
// ignore secrets for fork pull request
|
||||
// for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch
|
||||
// see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
return secrets
|
||||
}
|
||||
|
||||
@ -116,6 +119,14 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
|
||||
event := map[string]interface{}{}
|
||||
_ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event)
|
||||
|
||||
// TriggerEvent is added in https://github.com/go-gitea/gitea/pull/25229
|
||||
// This fallback is for the old ActionRun that doesn't have the TriggerEvent field
|
||||
// and should be removed in 1.22
|
||||
eventName := t.Job.Run.TriggerEvent
|
||||
if eventName == "" {
|
||||
eventName = t.Job.Run.Event.Event()
|
||||
}
|
||||
|
||||
baseRef := ""
|
||||
headRef := ""
|
||||
if pullPayload, err := t.Job.Run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil {
|
||||
@ -137,7 +148,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
|
||||
"base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
|
||||
"env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
|
||||
"event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload.
|
||||
"event_name": t.Job.Run.Event.Event(), // string, The name of the event that triggered the workflow run.
|
||||
"event_name": eventName, // string, The name of the event that triggered the workflow run.
|
||||
"event_path": "", // string, The path to the file on the runner that contains the full event webhook payload.
|
||||
"graphql_url": "", // string, The URL of the GitHub GraphQL API.
|
||||
"head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
|
||||
|
@ -383,7 +383,7 @@ func SignOut(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
HandleSignOut(ctx)
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/")
|
||||
}
|
||||
|
||||
// SignUp render the register page
|
||||
|
@ -142,13 +142,46 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||
}
|
||||
|
||||
var detectedWorkflows []*actions_module.DetectedWorkflow
|
||||
workflows, err := actions_module.DetectWorkflows(commit, input.Event, input.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||
}
|
||||
|
||||
if len(workflows) == 0 {
|
||||
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID)
|
||||
} else {
|
||||
for _, wf := range workflows {
|
||||
if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
||||
wf.Ref = ref
|
||||
detectedWorkflows = append(detectedWorkflows, wf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if input.PullRequest != nil {
|
||||
// detect pull_request_target workflows
|
||||
baseRef := git.BranchPrefix + input.PullRequest.BaseBranch
|
||||
baseCommit, err := gitRepo.GetCommit(baseRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||
}
|
||||
baseWorkflows, err := actions_module.DetectWorkflows(baseCommit, input.Event, input.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||
}
|
||||
if len(baseWorkflows) == 0 {
|
||||
log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RepoPath(), baseCommit.ID)
|
||||
} else {
|
||||
for _, wf := range baseWorkflows {
|
||||
if wf.TriggerEvent == actions_module.GithubEventPullRequestTarget {
|
||||
wf.Ref = baseRef
|
||||
detectedWorkflows = append(detectedWorkflows, wf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(detectedWorkflows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -172,18 +205,19 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
}
|
||||
}
|
||||
|
||||
for id, content := range workflows {
|
||||
for _, dwf := range detectedWorkflows {
|
||||
run := &actions_model.ActionRun{
|
||||
Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0],
|
||||
RepoID: input.Repo.ID,
|
||||
OwnerID: input.Repo.OwnerID,
|
||||
WorkflowID: id,
|
||||
WorkflowID: dwf.EntryName,
|
||||
TriggerUserID: input.Doer.ID,
|
||||
Ref: ref,
|
||||
CommitSHA: commit.ID.String(),
|
||||
Ref: dwf.Ref,
|
||||
CommitSHA: dwf.Commit.ID.String(),
|
||||
IsForkPullRequest: isForkPullRequest,
|
||||
Event: input.Event,
|
||||
EventPayload: string(p),
|
||||
TriggerEvent: dwf.TriggerEvent,
|
||||
Status: actions_model.StatusWaiting,
|
||||
}
|
||||
if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil {
|
||||
@ -193,7 +227,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
run.NeedApproval = need
|
||||
}
|
||||
|
||||
jobs, err := jobparser.Parse(content)
|
||||
jobs, err := jobparser.Parse(dwf.Content)
|
||||
if err != nil {
|
||||
log.Error("jobparser.Parse: %v", err)
|
||||
continue
|
||||
@ -259,8 +293,10 @@ func notifyPackage(ctx context.Context, sender *user_model.User, pd *packages_mo
|
||||
}
|
||||
|
||||
func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *repo_model.Repository, user *user_model.User) (bool, error) {
|
||||
// don't need approval if it's not a fork PR
|
||||
if !run.IsForkPullRequest {
|
||||
// 1. don't need approval if it's not a fork PR
|
||||
// 2. don't need approval if the event is `pull_request_target` since the workflow will run in the context of base branch
|
||||
// see https://docs.github.com/en/actions/managing-workflow-runs/approving-workflow-runs-from-public-forks#about-workflow-runs-from-public-forks
|
||||
if !run.IsForkPullRequest || run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -70,9 +70,17 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts repo_mo
|
||||
if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
|
||||
|
||||
// Re-fetch the repository from database before updating it (else it would
|
||||
// override changes that were done earlier with sql)
|
||||
if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
|
||||
return fmt.Errorf("getRepositoryByID: %w", err)
|
||||
}
|
||||
|
||||
if err := adoptRepository(ctx, repoPath, doer, repo, opts.DefaultBranch); err != nil {
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
|
||||
if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
|
||||
return fmt.Errorf("checkDaemonExportOK: %w", err)
|
||||
}
|
||||
@ -95,12 +103,12 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts repo_mo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notification.NotifyCreateRepository(ctx, doer, u, repo)
|
||||
notification.NotifyAdoptRepository(ctx, doer, u, repo)
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts repo_module.CreateRepoOptions) (err error) {
|
||||
func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, defaultBranch string) (err error) {
|
||||
isExist, err := util.IsExist(repoPath)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
|
||||
@ -114,12 +122,6 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
|
||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||
}
|
||||
|
||||
// Re-fetch the repository from database before updating it (else it would
|
||||
// override changes that were done earlier with sql)
|
||||
if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
|
||||
return fmt.Errorf("getRepositoryByID: %w", err)
|
||||
}
|
||||
|
||||
repo.IsEmpty = false
|
||||
|
||||
// Don't bother looking this repo in the context it won't be there
|
||||
@ -129,8 +131,8 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
if len(opts.DefaultBranch) > 0 {
|
||||
repo.DefaultBranch = opts.DefaultBranch
|
||||
if len(defaultBranch) > 0 {
|
||||
repo.DefaultBranch = defaultBranch
|
||||
|
||||
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
||||
return fmt.Errorf("setDefaultBranch: %w", err)
|
||||
|
@ -68,7 +68,7 @@
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout" data-redirect="{{AppSubUrl}}/">
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{.locale.Tr "sign_out"}}
|
||||
</a>
|
||||
@ -186,7 +186,7 @@
|
||||
{{end}}
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout" data-redirect="{{AppSubUrl}}/">
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{.locale.Tr "sign_out"}}
|
||||
</a>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{{template "base/head" .}}
|
||||
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
|
||||
<div class="page-content devtest ui container">
|
||||
<div>
|
||||
<h1>Button</h1>
|
||||
@ -14,11 +15,6 @@
|
||||
<label><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label>
|
||||
</div>
|
||||
<div id="devtest-button-samples">
|
||||
<style>
|
||||
.button-sample-groups { margin: 0; padding: 0; }
|
||||
.button-sample-groups .sample-group { list-style: none; margin: 0; padding: 0; }
|
||||
.button-sample-groups .sample-group .ui.button { margin-bottom: 5px; }
|
||||
</style>
|
||||
<ul class="button-sample-groups">
|
||||
<li class="sample-group">
|
||||
<h2>General purpose:</h2>
|
||||
@ -242,17 +238,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>Toast</h1>
|
||||
<div>
|
||||
<button class="ui button" id="info-toast">Show Info Toast</button>
|
||||
<button class="ui button" id="warning-toast">Show Warning Toast</button>
|
||||
<button class="ui button" id="error-toast">Show Error Toast</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>ComboMarkdownEditor</h1>
|
||||
<div>ps: no JS code attached, so just a layout</div>
|
||||
{{template "shared/combomarkdowneditor" .}}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h1, h2 {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
||||
<script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
||||
|
@ -3,9 +3,9 @@
|
||||
<div class="ui middle very relaxed page grid">
|
||||
<div class="column">
|
||||
{{template "repo/migrate/helper" .}}
|
||||
<div class="ui three stackable cards">
|
||||
<div class="ui cards migrate-entries">
|
||||
{{range .Services}}
|
||||
<a class="ui card gt-df gt-ac" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
|
||||
<a class="ui card migrate-entry gt-df gt-ac" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
|
||||
{{if eq .Name "github"}}
|
||||
{{svg "octicon-mark-github" 184 "gt-p-4"}}
|
||||
{{else if eq .Name "gitlab"}}
|
||||
|
@ -128,6 +128,10 @@
|
||||
{{.locale.Tr "settings.add_email"}}
|
||||
</button>
|
||||
</form>
|
||||
{{/* if ActivationsPending is false, then CanAddEmails must be true, so if CanAddEmails is false, ActivationsPending must be true */}}
|
||||
{{if not .CanAddEmails}}
|
||||
<div class="ui warning message">{{.locale.Tr "settings.can_not_add_email_activations_pending"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<h4 class="ui top attached error header">
|
||||
|
144
tests/integration/actions_trigger_test.go
Normal file
144
tests/integration/actions_trigger_test.go
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
actions_module "code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPullRequestTargetEvent(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the base repo
|
||||
user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the forked repo
|
||||
|
||||
// create the base repo
|
||||
baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_module.CreateRepoOptions{
|
||||
Name: "repo-pull-request-target",
|
||||
Description: "test pull-request-target event",
|
||||
AutoInit: true,
|
||||
Gitignores: "Go",
|
||||
License: "MIT",
|
||||
Readme: "Default",
|
||||
DefaultBranch: "main",
|
||||
IsPrivate: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, baseRepo)
|
||||
|
||||
// enable actions
|
||||
err = repo_model.UpdateRepositoryUnits(baseRepo, []repo_model.RepoUnit{{
|
||||
RepoID: baseRepo.ID,
|
||||
Type: unit_model.TypeActions,
|
||||
}}, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// create the forked repo
|
||||
forkedRepo, err := repo_service.ForkRepository(git.DefaultContext, user2, user3, repo_service.ForkRepoOptions{
|
||||
BaseRepo: baseRepo,
|
||||
Name: "forked-repo-pull-request-target",
|
||||
Description: "test pull-request-target event",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, forkedRepo)
|
||||
|
||||
// add workflow file to the base repo
|
||||
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, baseRepo, user2, &files_service.ChangeRepoFilesOptions{
|
||||
Files: []*files_service.ChangeRepoFile{
|
||||
{
|
||||
Operation: "create",
|
||||
TreePath: ".gitea/workflows/pr.yml",
|
||||
Content: "name: test\non: pull_request_target\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n",
|
||||
},
|
||||
},
|
||||
Message: "add workflow",
|
||||
OldBranch: "main",
|
||||
NewBranch: "main",
|
||||
Author: &files_service.IdentityOptions{
|
||||
Name: user2.Name,
|
||||
Email: user2.Email,
|
||||
},
|
||||
Committer: &files_service.IdentityOptions{
|
||||
Name: user2.Name,
|
||||
Email: user2.Email,
|
||||
},
|
||||
Dates: &files_service.CommitDateOptions{
|
||||
Author: time.Now(),
|
||||
Committer: time.Now(),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, addWorkflowToBaseResp)
|
||||
|
||||
// add a new file to the forked repo
|
||||
addFileToForkedResp, err := files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, user3, &files_service.ChangeRepoFilesOptions{
|
||||
Files: []*files_service.ChangeRepoFile{
|
||||
{
|
||||
Operation: "create",
|
||||
TreePath: "file_1.txt",
|
||||
Content: "file1",
|
||||
},
|
||||
},
|
||||
Message: "add file1",
|
||||
OldBranch: "main",
|
||||
NewBranch: "fork-branch-1",
|
||||
Author: &files_service.IdentityOptions{
|
||||
Name: user3.Name,
|
||||
Email: user3.Email,
|
||||
},
|
||||
Committer: &files_service.IdentityOptions{
|
||||
Name: user3.Name,
|
||||
Email: user3.Email,
|
||||
},
|
||||
Dates: &files_service.CommitDateOptions{
|
||||
Author: time.Now(),
|
||||
Committer: time.Now(),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, addFileToForkedResp)
|
||||
|
||||
// create Pull
|
||||
pullIssue := &issues_model.Issue{
|
||||
RepoID: baseRepo.ID,
|
||||
Title: "Test pull-request-target-event",
|
||||
PosterID: user3.ID,
|
||||
Poster: user3,
|
||||
IsPull: true,
|
||||
}
|
||||
pullRequest := &issues_model.PullRequest{
|
||||
HeadRepoID: forkedRepo.ID,
|
||||
BaseRepoID: baseRepo.ID,
|
||||
HeadBranch: "fork-branch-1",
|
||||
BaseBranch: "main",
|
||||
HeadRepo: forkedRepo,
|
||||
BaseRepo: baseRepo,
|
||||
Type: issues_model.PullRequestGitea,
|
||||
}
|
||||
err = pull_service.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// load and compare ActionRun
|
||||
actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: baseRepo.ID})
|
||||
assert.Equal(t, addWorkflowToBaseResp.Commit.SHA, actionRun.CommitSHA)
|
||||
assert.Equal(t, actions_module.GithubEventPullRequestTarget, actionRun.TriggerEvent)
|
||||
})
|
||||
}
|
@ -16,7 +16,7 @@ func TestSignOut(t *testing.T) {
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
req := NewRequest(t, "POST", "/user/logout")
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// try to view a private repo, should fail
|
||||
req = NewRequest(t, "GET", "/user2/repo2")
|
||||
|
@ -8,6 +8,7 @@
|
||||
@import "./modules/card.css";
|
||||
@import "./modules/comment.css";
|
||||
@import "./modules/navbar.css";
|
||||
@import "./modules/toast.css";
|
||||
|
||||
@import "./shared/issuelist.css";
|
||||
@import "./shared/milestone.css";
|
||||
|
78
web_src/css/modules/toast.css
Normal file
78
web_src/css/modules/toast.css
Normal file
@ -0,0 +1,78 @@
|
||||
.toastify {
|
||||
color: var(--color-white);
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
transition: all .2s ease;
|
||||
z-index: 500;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 8px 24px var(--color-shadow);
|
||||
display: flex;
|
||||
max-width: 50vw;
|
||||
min-width: 300px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.toastify.on {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast-body {
|
||||
flex: 1;
|
||||
padding: 5px 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.toast-close,
|
||||
.toast-icon {
|
||||
color: currentcolor;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toast-close:hover {
|
||||
background: var(--color-hover);
|
||||
}
|
||||
|
||||
.toast-close:active {
|
||||
background: var(--color-active);
|
||||
}
|
||||
|
||||
.toastify-right {
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.toastify-left {
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.toastify-top {
|
||||
top: -150px;
|
||||
}
|
||||
|
||||
.toastify-bottom {
|
||||
bottom: -150px;
|
||||
}
|
||||
|
||||
.toastify-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.toastify-right, .toastify-left {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: fit-content;
|
||||
}
|
||||
}
|
@ -3100,18 +3100,37 @@ tbody.commit-list {
|
||||
}
|
||||
}
|
||||
|
||||
.repository.migrate .card {
|
||||
.migrate-entries {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.migrate-entries {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.migrate-entry {
|
||||
transition: all 0.1s ease-in-out;
|
||||
box-shadow: none !important;
|
||||
border: 1px solid var(--color-secondary);
|
||||
color: var(--color-text);
|
||||
color: var(--color-text) !important;
|
||||
width: auto !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.repository.migrate .card:hover {
|
||||
.migrate-entry:hover {
|
||||
transform: scale(105%);
|
||||
box-shadow: 0 0.5rem 1rem var(--color-shadow) !important;
|
||||
}
|
||||
|
||||
.migrate-entry .description {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.repository.file.list #repo-files-table .entry,
|
||||
.repository.file.list #repo-files-table .commit-list {
|
||||
|
16
web_src/css/standalone/devtest.css
Normal file
16
web_src/css/standalone/devtest.css
Normal file
@ -0,0 +1,16 @@
|
||||
.button-sample-groups {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
|
||||
.button-sample-groups .sample-group {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
}
|
||||
|
||||
.button-sample-groups .sample-group .ui.button {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
@ -9,6 +9,7 @@ import {hideElem, showElem, toggleElem} from '../utils/dom.js';
|
||||
import {htmlEscape} from 'escape-goat';
|
||||
import {createTippy} from '../modules/tippy.js';
|
||||
import {confirmModal} from './comp/ConfirmModal.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
|
||||
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
||||
|
||||
@ -439,7 +440,7 @@ export function initGlobalButtons() {
|
||||
return;
|
||||
}
|
||||
// should never happen, otherwise there is a bug in code
|
||||
alert('Nothing to hide');
|
||||
showErrorToast('Nothing to hide');
|
||||
});
|
||||
|
||||
initGlobalShowModal();
|
||||
|
@ -8,6 +8,7 @@ import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js';
|
||||
import {renderPreviewPanelContent} from '../repo-editor.js';
|
||||
import {easyMDEToolbarActions} from './EasyMDEToolbarActions.js';
|
||||
import {initTextExpander} from './TextExpander.js';
|
||||
import {showErrorToast} from '../../modules/toast.js';
|
||||
|
||||
let elementIdCounter = 0;
|
||||
|
||||
@ -26,7 +27,7 @@ export function validateTextareaNonEmpty($textarea) {
|
||||
$form[0]?.reportValidity();
|
||||
} else {
|
||||
// The alert won't hurt users too much, because we are dropping the EasyMDE and the check only occurs in a few places.
|
||||
alert('Require non-empty content');
|
||||
showErrorToast('Require non-empty content');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import {svg} from '../svg.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
|
||||
const {appSubUrl, csrfToken} = window.config;
|
||||
let i18nTextEdited;
|
||||
@ -39,12 +40,12 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
|
||||
if (resp.ok) {
|
||||
$dialog.modal('hide');
|
||||
} else {
|
||||
alert(resp.message);
|
||||
showErrorToast(resp.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else { // required by eslint
|
||||
window.alert(`unknown option item: ${optionItem}`);
|
||||
showErrorToast(`unknown option item: ${optionItem}`);
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
|
@ -4,6 +4,7 @@ import {toggleElem} from '../utils/dom.js';
|
||||
import {htmlEscape} from 'escape-goat';
|
||||
import {Sortable} from 'sortablejs';
|
||||
import {confirmModal} from './comp/ConfirmModal.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
|
||||
function initRepoIssueListCheckboxes() {
|
||||
const $issueSelectAll = $('.issue-checkbox-all');
|
||||
@ -75,7 +76,7 @@ function initRepoIssueListCheckboxes() {
|
||||
).then(() => {
|
||||
window.location.reload();
|
||||
}).catch((reason) => {
|
||||
window.alert(reason.responseJSON.error);
|
||||
showErrorToast(reason.responseJSON.error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
60
web_src/js/modules/toast.js
Normal file
60
web_src/js/modules/toast.js
Normal file
@ -0,0 +1,60 @@
|
||||
import {htmlEscape} from 'escape-goat';
|
||||
import {svg} from '../svg.js';
|
||||
|
||||
const levels = {
|
||||
info: {
|
||||
icon: 'octicon-check',
|
||||
background: 'var(--color-green)',
|
||||
duration: 2500,
|
||||
},
|
||||
warning: {
|
||||
icon: 'gitea-exclamation',
|
||||
background: 'var(--color-orange)',
|
||||
duration: -1, // requires dismissal to hide
|
||||
},
|
||||
error: {
|
||||
icon: 'gitea-exclamation',
|
||||
background: 'var(--color-red)',
|
||||
duration: -1, // requires dismissal to hide
|
||||
},
|
||||
};
|
||||
|
||||
// See https://github.com/apvarun/toastify-js#api for options
|
||||
async function showToast(message, level, {gravity, position, duration, ...other} = {}) {
|
||||
if (!message) return;
|
||||
|
||||
const {default: Toastify} = await import(/* webpackChunkName: 'toastify' */'toastify-js');
|
||||
const {icon, background, duration: levelDuration} = levels[level ?? 'info'];
|
||||
|
||||
const toast = Toastify({
|
||||
text: `
|
||||
<div class='toast-icon'>${svg(icon)}</div>
|
||||
<div class='toast-body'>${htmlEscape(message)}</div>
|
||||
<button class='toast-close'>${svg('octicon-x')}</button>
|
||||
`,
|
||||
escapeMarkup: false,
|
||||
gravity: gravity ?? 'top',
|
||||
position: position ?? 'center',
|
||||
duration: duration ?? levelDuration,
|
||||
style: {background},
|
||||
...other,
|
||||
});
|
||||
|
||||
toast.showToast();
|
||||
|
||||
toast.toastElement.querySelector('.toast-close').addEventListener('click', () => {
|
||||
toast.removeElement(toast.toastElement);
|
||||
});
|
||||
}
|
||||
|
||||
export async function showInfoToast(message, opts) {
|
||||
return await showToast(message, 'info', opts);
|
||||
}
|
||||
|
||||
export async function showWarningToast(message, opts) {
|
||||
return await showToast(message, 'warning', opts);
|
||||
}
|
||||
|
||||
export async function showErrorToast(message, opts) {
|
||||
return await showToast(message, 'error', opts);
|
||||
}
|
17
web_src/js/modules/toast.test.js
Normal file
17
web_src/js/modules/toast.test.js
Normal file
@ -0,0 +1,17 @@
|
||||
import {test, expect} from 'vitest';
|
||||
import {showInfoToast, showErrorToast, showWarningToast} from './toast.js';
|
||||
|
||||
test('showInfoToast', async () => {
|
||||
await showInfoToast('success 😀', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('showWarningToast', async () => {
|
||||
await showWarningToast('warning 😐', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('showErrorToast', async () => {
|
||||
await showErrorToast('error 🙁', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
11
web_src/js/standalone/devtest.js
Normal file
11
web_src/js/standalone/devtest.js
Normal file
@ -0,0 +1,11 @@
|
||||
import {showInfoToast, showWarningToast, showErrorToast} from '../modules/toast.js';
|
||||
|
||||
document.getElementById('info-toast').addEventListener('click', () => {
|
||||
showInfoToast('success 😀');
|
||||
});
|
||||
document.getElementById('warning-toast').addEventListener('click', () => {
|
||||
showWarningToast('warning 😐');
|
||||
});
|
||||
document.getElementById('error-toast').addEventListener('click', () => {
|
||||
showErrorToast('error 🙁');
|
||||
});
|
@ -73,6 +73,12 @@ export default {
|
||||
'eventsource.sharedworker': [
|
||||
fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)),
|
||||
],
|
||||
...(!isProduction && {
|
||||
devtest: [
|
||||
fileURLToPath(new URL('web_src/js/standalone/devtest.js', import.meta.url)),
|
||||
fileURLToPath(new URL('web_src/css/standalone/devtest.css', import.meta.url)),
|
||||
],
|
||||
}),
|
||||
...themes,
|
||||
},
|
||||
devtool: false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user