From 74466eb1334386148caa50ccfd18b0f218e413d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Fri, 27 Jan 2023 19:40:17 -0300 Subject: [PATCH 01/67] Fixes accessibility of empty repository commit status (#22632) Avoid empty labelled anchor in repo without commits. Contributed by @forgejo. --- templates/repo/commit_statuses.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl index d68270908..4fe644ff0 100644 --- a/templates/repo/commit_statuses.tmpl +++ b/templates/repo/commit_statuses.tmpl @@ -1,4 +1,4 @@ -{{template "repo/commit_status" .Status}} +{{if eq (len .Statuses) 1}}{{$status := index .Statuses 0}}{{if $status.TargetURL}}{{template "repo/commit_status" .Status}}{{end}}{{end}}
{{range .Statuses}} From 95d9fbdcf39db7595a23a69ca48bfb49b845874a Mon Sep 17 00:00:00 2001 From: "Otto Richter (fnetX)" Date: Sat, 28 Jan 2023 08:59:46 +0100 Subject: [PATCH 02/67] Fix error on account activation with wrong passwd (#22609) On activating local accounts, the error message didn't differentiate between using a wrong or expired token, or a wrong password. The result could already be obtained from the behaviour (different screens were presented), but the error message was misleading and lead to confusion for new users on Codeberg with Forgejo. Now, entering a wrong password for a valid token prints a different error message. The problem was introduced in 0f14f69e6070c9aca09f57c419e7d6007d0e520b. Co-authored-by: Lunny Xiao --- options/locale/locale_en-US.ini | 1 + routers/web/auth/auth.go | 6 +++--- templates/user/auth/activate.tmpl | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 43a8aeb08..6ccbbc1c0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -322,6 +322,7 @@ email_not_associate = The email address is not associated with any account. send_reset_mail = Send Account Recovery Email reset_password = Account Recovery invalid_code = Your confirmation code is invalid or has expired. +invalid_password = Your password does not match the password that was used to create the account. reset_password_helper = Recover Account reset_password_wrong_user = You are signed in as %s, but the account recovery link is for %s password_too_short = Password length cannot be less than %d characters. diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 71a62bce6..48b7dc686 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -633,7 +633,7 @@ func Activate(ctx *context.Context) { user := user_model.VerifyUserActiveCode(code) // if code is wrong if user == nil { - ctx.Data["IsActivateFailed"] = true + ctx.Data["IsCodeInvalid"] = true ctx.HTML(http.StatusOK, TplActivate) return } @@ -660,7 +660,7 @@ func ActivatePost(ctx *context.Context) { user := user_model.VerifyUserActiveCode(code) // if code is wrong if user == nil { - ctx.Data["IsActivateFailed"] = true + ctx.Data["IsCodeInvalid"] = true ctx.HTML(http.StatusOK, TplActivate) return } @@ -675,7 +675,7 @@ func ActivatePost(ctx *context.Context) { return } if !user.ValidatePassword(password) { - ctx.Data["IsActivateFailed"] = true + ctx.Data["IsPasswordInvalid"] = true ctx.HTML(http.StatusOK, TplActivate) return } diff --git a/templates/user/auth/activate.tmpl b/templates/user/auth/activate.tmpl index eba9e3229..ef72ef1e5 100644 --- a/templates/user/auth/activate.tmpl +++ b/templates/user/auth/activate.tmpl @@ -30,8 +30,10 @@ {{else if .IsSendRegisterMail}}

{{.locale.Tr "auth.confirmation_mail_sent_prompt" (.Email|Escape) .ActiveCodeLives | Str2html}}

- {{else if .IsActivateFailed}} + {{else if .IsCodeInvalid}}

{{.locale.Tr "auth.invalid_code"}}

+ {{else if .IsPasswordInvalid}} +

{{.locale.Tr "auth.invalid_password"}}

{{else if .ManualActivationOnly}}

{{.locale.Tr "auth.manual_activation_only"}}

{{else}} From 48f5d519088c2b33b48eb35f6ef3261e3ec677a1 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Sat, 28 Jan 2023 17:28:55 +0800 Subject: [PATCH 03/67] fix permission check for creating comment while mail (#22524) only creating comment on locked issue request write permission, for others, read permission is enough. related to https://github.com/go-gitea/gitea/pull/22056 /cc @KN4CK3R --------- Signed-off-by: a1012112796 <1012112796@qq.com> Co-authored-by: delvh Co-authored-by: Lunny Xiao --- services/mailer/incoming/incoming_handler.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go index 173b362a5..d89a5eab3 100644 --- a/services/mailer/incoming/incoming_handler.go +++ b/services/mailer/incoming/incoming_handler.go @@ -71,11 +71,17 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u return err } - if !perm.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsLocked && !doer.IsAdmin { + // Locked issues require write permissions + if issue.IsLocked && !perm.CanWriteIssuesOrPulls(issue.IsPull) && !doer.IsAdmin { log.Debug("can't write issue or pull") return nil } + if !perm.CanReadIssuesOrPulls(issue.IsPull) { + log.Debug("can't read issue or pull") + return nil + } + switch r := ref.(type) { case *issues_model.Issue: attachmentIDs := make([]string, 0, len(content.Attachments)) From e9cd18b5578cdf84264f3d69a5f166a33d74b4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Sat, 28 Jan 2023 08:16:46 -0300 Subject: [PATCH 04/67] Link issue and pull requests status change in UI notifications directly to their event in the timelined view. (#22627) Adding the related comment to the issue and pull request status change in the UI notifications allows to navigate directly to the specific event in its dedicated view, easing the reading of last comments and to the editor for additional comments if desired. --- modules/notification/ui/ui.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 4b85f17b6..73ea92274 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -97,6 +97,7 @@ func (ns *notificationService) NotifyIssueChangeStatus(ctx context.Context, doer _ = ns.issueQueue.Push(issueNotificationOpts{ IssueID: issue.ID, NotificationAuthorID: doer.ID, + CommentID: actionComment.ID, }) } From 78e6b21c1a4c9867dd3054d6c167cc80407b020d Mon Sep 17 00:00:00 2001 From: zeripath Date: Sat, 28 Jan 2023 15:54:40 +0000 Subject: [PATCH 05/67] Improve checkIfPRContentChanged (#22611) The code for checking if a commit has caused a change in a PR is extremely inefficient and affects the head repository instead of using a temporary repository. This PR therefore makes several significant improvements: * A temporary repo like that used in merging. * The diff code is then significant improved to use a three-way diff instead of comparing diffs (possibly binary) line-by-line - in memory... Ref #22578 Signed-off-by: Andrew Thornton --- modules/util/io.go | 22 +++++++++++ services/pull/pull.go | 88 ++++++++++++++++++------------------------- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/modules/util/io.go b/modules/util/io.go index e5d7561be..69b1d6314 100644 --- a/modules/util/io.go +++ b/modules/util/io.go @@ -4,6 +4,7 @@ package util import ( + "errors" "io" ) @@ -17,3 +18,24 @@ func ReadAtMost(r io.Reader, buf []byte) (n int, err error) { } return n, err } + +// ErrNotEmpty is an error reported when there is a non-empty reader +var ErrNotEmpty = errors.New("not-empty") + +// IsEmptyReader reads a reader and ensures it is empty +func IsEmptyReader(r io.Reader) (err error) { + var buf [1]byte + + for { + n, err := r.Read(buf[:]) + if err != nil { + if err == io.EOF { + return nil + } + return err + } + if n > 0 { + return ErrNotEmpty + } + } +} diff --git a/services/pull/pull.go b/services/pull/pull.go index 7f81def6d..c983c4f3e 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -4,14 +4,12 @@ package pull import ( - "bufio" - "bytes" "context" "fmt" "io" + "os" "regexp" "strings" - "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" @@ -29,6 +27,7 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/modules/util" issue_service "code.gitea.io/gitea/services/issue" ) @@ -351,69 +350,56 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, // checkIfPRContentChanged checks if diff to target branch has changed by push // A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { - if err = pr.LoadHeadRepo(ctx); err != nil { - return false, fmt.Errorf("LoadHeadRepo: %w", err) - } else if pr.HeadRepo == nil { - // corrupt data assumed changed - return true, nil + tmpBasePath, err := createTemporaryRepo(ctx, pr) + if err != nil { + log.Error("CreateTemporaryRepo: %v", err) + return false, err } + defer func() { + if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil { + log.Error("checkIfPRContentChanged: RemoveTemporaryPath: %s", err) + } + }() - if err = pr.LoadBaseRepo(ctx); err != nil { - return false, fmt.Errorf("LoadBaseRepo: %w", err) - } - - headGitRepo, err := git.OpenRepository(ctx, pr.HeadRepo.RepoPath()) + tmpRepo, err := git.OpenRepository(ctx, tmpBasePath) if err != nil { return false, fmt.Errorf("OpenRepository: %w", err) } - defer headGitRepo.Close() + defer tmpRepo.Close() - // Add a temporary remote. - tmpRemote := "checkIfPRContentChanged-" + fmt.Sprint(time.Now().UnixNano()) - if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath(), true); err != nil { - return false, fmt.Errorf("AddRemote: %s/%s-%s: %w", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err) - } - defer func() { - if err := headGitRepo.RemoveRemote(tmpRemote); err != nil { - log.Error("checkIfPRContentChanged: RemoveRemote: %s/%s-%s: %v", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err) - } - }() - // To synchronize repo and get a base ref - _, base, err := headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch) + // Find the merge-base + _, base, err := tmpRepo.GetMergeBase("", "base", "tracking") if err != nil { return false, fmt.Errorf("GetMergeBase: %w", err) } - diffBefore := &bytes.Buffer{} - diffAfter := &bytes.Buffer{} - if err := headGitRepo.GetDiffFromMergeBase(base, oldCommitID, diffBefore); err != nil { - // If old commit not found, assume changed. - log.Debug("GetDiffFromMergeBase: %v", err) - return true, nil - } - if err := headGitRepo.GetDiffFromMergeBase(base, newCommitID, diffAfter); err != nil { - // New commit should be found - return false, fmt.Errorf("GetDiffFromMergeBase: %w", err) + cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base) + stdoutReader, stdoutWriter, err := os.Pipe() + if err != nil { + return false, fmt.Errorf("unable to open pipe for to run diff: %w", err) } - diffBeforeLines := bufio.NewScanner(diffBefore) - diffAfterLines := bufio.NewScanner(diffAfter) - - for diffBeforeLines.Scan() && diffAfterLines.Scan() { - if strings.HasPrefix(diffBeforeLines.Text(), "index") && strings.HasPrefix(diffAfterLines.Text(), "index") { - // file hashes can change without the diff changing - continue - } else if strings.HasPrefix(diffBeforeLines.Text(), "@@") && strings.HasPrefix(diffAfterLines.Text(), "@@") { - // the location of the difference may change - continue - } else if !bytes.Equal(diffBeforeLines.Bytes(), diffAfterLines.Bytes()) { + if err := cmd.Run(&git.RunOpts{ + Dir: tmpBasePath, + Stdout: stdoutWriter, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + defer func() { + _ = stdoutReader.Close() + }() + return util.IsEmptyReader(stdoutReader) + }, + }); err != nil { + if err == util.ErrNotEmpty { return true, nil } - } - if diffBeforeLines.Scan() || diffAfterLines.Scan() { - // Diffs not of equal length - return true, nil + log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v", + newCommitID, oldCommitID, base, + pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch, + err) + + return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err) } return false, nil From c0015979a692b795bcf7416196bec01c375d7aa2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 29 Jan 2023 02:12:10 +0800 Subject: [PATCH 06/67] Support system hook API (#14537) This add system hook API --- models/webhook/webhook.go | 76 -------------- models/webhook/webhook_system.go | 81 ++++++++++++++ routers/api/v1/admin/hooks.go | 174 +++++++++++++++++++++++++++++++ routers/api/v1/api.go | 7 ++ routers/api/v1/utils/hook.go | 38 +++++++ routers/web/admin/hooks.go | 2 +- routers/web/repo/webhook.go | 2 +- templates/swagger/v1_json.tmpl | 148 ++++++++++++++++++++++++++ 8 files changed, 450 insertions(+), 78 deletions(-) create mode 100644 models/webhook/webhook_system.go create mode 100644 routers/api/v1/admin/hooks.go diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index c24404c42..64119f149 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -463,41 +463,6 @@ func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) { return db.GetEngine(db.DefaultContext).Where(opts.toCond()).Count(&Webhook{}) } -// GetDefaultWebhooks returns all admin-default webhooks. -func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false). - Find(&webhooks) -} - -// GetSystemOrDefaultWebhook returns admin system or default webhook by given ID. -func GetSystemOrDefaultWebhook(id int64) (*Webhook, error) { - webhook := &Webhook{ID: id} - has, err := db.GetEngine(db.DefaultContext). - Where("repo_id=? AND org_id=?", 0, 0). - Get(webhook) - if err != nil { - return nil, err - } else if !has { - return nil, ErrWebhookNotExist{ID: id} - } - return webhook, nil -} - -// GetSystemWebhooks returns all admin system webhooks. -func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - if isActive.IsNone() { - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). - Find(&webhooks) - } - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). - Find(&webhooks) -} - // UpdateWebhook updates information of webhook. func UpdateWebhook(w *Webhook) error { _, err := db.GetEngine(db.DefaultContext).ID(w.ID).AllCols().Update(w) @@ -545,44 +510,3 @@ func DeleteWebhookByOrgID(orgID, id int64) error { OrgID: orgID, }) } - -// DeleteDefaultSystemWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0) -func DeleteDefaultSystemWebhook(id int64) error { - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return err - } - defer committer.Close() - - count, err := db.GetEngine(ctx). - Where("repo_id=? AND org_id=?", 0, 0). - Delete(&Webhook{ID: id}) - if err != nil { - return err - } else if count == 0 { - return ErrWebhookNotExist{ID: id} - } - - if _, err := db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil { - return err - } - - return committer.Commit() -} - -// CopyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo -func CopyDefaultWebhooksToRepo(ctx context.Context, repoID int64) error { - ws, err := GetDefaultWebhooks(ctx) - if err != nil { - return fmt.Errorf("GetDefaultWebhooks: %w", err) - } - - for _, w := range ws { - w.ID = 0 - w.RepoID = repoID - if err := CreateWebhook(ctx, w); err != nil { - return fmt.Errorf("CreateWebhook: %w", err) - } - } - return nil -} diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go new file mode 100644 index 000000000..21dc0406a --- /dev/null +++ b/models/webhook/webhook_system.go @@ -0,0 +1,81 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package webhook + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/util" +) + +// GetDefaultWebhooks returns all admin-default webhooks. +func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { + webhooks := make([]*Webhook, 0, 5) + return webhooks, db.GetEngine(ctx). + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false). + Find(&webhooks) +} + +// GetSystemOrDefaultWebhook returns admin system or default webhook by given ID. +func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) { + webhook := &Webhook{ID: id} + has, err := db.GetEngine(ctx). + Where("repo_id=? AND org_id=?", 0, 0). + Get(webhook) + if err != nil { + return nil, err + } else if !has { + return nil, ErrWebhookNotExist{ID: id} + } + return webhook, nil +} + +// GetSystemWebhooks returns all admin system webhooks. +func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webhook, error) { + webhooks := make([]*Webhook, 0, 5) + if isActive.IsNone() { + return webhooks, db.GetEngine(ctx). + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). + Find(&webhooks) + } + return webhooks, db.GetEngine(ctx). + Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). + Find(&webhooks) +} + +// DeleteDefaultSystemWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0) +func DeleteDefaultSystemWebhook(ctx context.Context, id int64) error { + return db.WithTx(ctx, func(ctx context.Context) error { + count, err := db.GetEngine(ctx). + Where("repo_id=? AND org_id=?", 0, 0). + Delete(&Webhook{ID: id}) + if err != nil { + return err + } else if count == 0 { + return ErrWebhookNotExist{ID: id} + } + + _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}) + return err + }) +} + +// CopyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo +func CopyDefaultWebhooksToRepo(ctx context.Context, repoID int64) error { + ws, err := GetDefaultWebhooks(ctx) + if err != nil { + return fmt.Errorf("GetDefaultWebhooks: %v", err) + } + + for _, w := range ws { + w.ID = 0 + w.RepoID = repoID + if err := CreateWebhook(ctx, w); err != nil { + return fmt.Errorf("CreateWebhook: %v", err) + } + } + return nil +} diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go new file mode 100644 index 000000000..2aed4139f --- /dev/null +++ b/routers/api/v1/admin/hooks.go @@ -0,0 +1,174 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package admin + +import ( + "net/http" + + "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" + webhook_service "code.gitea.io/gitea/services/webhook" +) + +// ListHooks list system's webhooks +func ListHooks(ctx *context.APIContext) { + // swagger:operation GET /admin/hooks admin adminListHooks + // --- + // summary: List system's webhooks + // produces: + // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/HookList" + + sysHooks, err := webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) + return + } + hooks := make([]*api.Hook, len(sysHooks)) + for i, hook := range sysHooks { + h, err := webhook_service.ToHook(setting.AppURL+"/admin", hook) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) + return + } + hooks[i] = h + } + ctx.JSON(http.StatusOK, hooks) +} + +// GetHook get an organization's hook by id +func GetHook(ctx *context.APIContext) { + // swagger:operation GET /admin/hooks/{id} admin adminGetHook + // --- + // summary: Get a hook + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: id of the hook to get + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // "$ref": "#/responses/Hook" + + hookID := ctx.ParamsInt64(":id") + hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + return + } + h, err := webhook_service.ToHook("/admin/", hook) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) + return + } + ctx.JSON(http.StatusOK, h) +} + +// CreateHook create a hook for an organization +func CreateHook(ctx *context.APIContext) { + // swagger:operation POST /admin/hooks admin adminCreateHook + // --- + // summary: Create a hook + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: body + // in: body + // required: true + // schema: + // "$ref": "#/definitions/CreateHookOption" + // responses: + // "201": + // "$ref": "#/responses/Hook" + + form := web.GetForm(ctx).(*api.CreateHookOption) + // TODO in body params + if !utils.CheckCreateHookOption(ctx, form) { + return + } + utils.AddSystemHook(ctx, form) +} + +// EditHook modify a hook of a repository +func EditHook(ctx *context.APIContext) { + // swagger:operation PATCH /admin/hooks/{id} admin adminEditHook + // --- + // summary: Update a hook + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: id of the hook to update + // type: integer + // format: int64 + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/EditHookOption" + // responses: + // "200": + // "$ref": "#/responses/Hook" + + form := web.GetForm(ctx).(*api.EditHookOption) + + // TODO in body params + hookID := ctx.ParamsInt64(":id") + utils.EditSystemHook(ctx, form, hookID) +} + +// DeleteHook delete a system hook +func DeleteHook(ctx *context.APIContext) { + // swagger:operation DELETE /amdin/hooks/{id} admin adminDeleteHook + // --- + // summary: Delete a hook + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: id of the hook to delete + // type: integer + // format: int64 + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + + hookID := ctx.ParamsInt64(":id") + if err := webhook.DeleteDefaultSystemWebhook(ctx, hookID); err != nil { + if webhook.IsErrWebhookNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "DeleteDefaultSystemWebhook", err) + } + return + } + ctx.Status(http.StatusNoContent) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 21bc2e2de..eef2a6424 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1222,6 +1222,13 @@ func Routes(ctx gocontext.Context) *web.Route { m.Post("/{username}/{reponame}", admin.AdoptRepository) m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) + m.Group("/hooks", func() { + m.Combo("").Get(admin.ListHooks). + Post(bind(api.CreateHookOption{}), admin.CreateHook) + m.Combo("/{id}").Get(admin.GetHook). + Patch(bind(api.EditHookOption{}), admin.EditHook). + Delete(admin.DeleteHook) + }) }, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin()) m.Group("/topics", func() { diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 44fba22b5..f6aaf74af 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -67,6 +68,19 @@ func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) return true } +// AddSystemHook add a system hook +func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) { + hook, ok := addHook(ctx, form, 0, 0) + if ok { + h, err := webhook_service.ToHook(setting.AppSubURL+"/admin", hook) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) + return + } + ctx.JSON(http.StatusCreated, h) + } +} + // AddOrgHook add a hook to an organization. Writes to `ctx` accordingly func AddOrgHook(ctx *context.APIContext, form *api.CreateHookOption) { org := ctx.Org.Organization @@ -196,6 +210,30 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID return w, true } +// EditSystemHook edit system webhook `w` according to `form`. Writes to `ctx` accordingly +func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) { + hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + return + } + if !editHook(ctx, form, hook) { + ctx.Error(http.StatusInternalServerError, "editHook", err) + return + } + updated, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + return + } + h, err := webhook_service.ToHook(setting.AppURL+"/admin", updated) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) + return + } + ctx.JSON(http.StatusOK, h) +} + // EditOrgHook edit webhook `w` according to `form`. Writes to `ctx` accordingly func EditOrgHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) { org := ctx.Org.Organization diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index e8db9a3de..57cf5f49e 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -60,7 +60,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { // DeleteDefaultOrSystemWebhook handler to delete an admin-defined system or default webhook func DeleteDefaultOrSystemWebhook(ctx *context.Context) { - if err := webhook.DeleteDefaultSystemWebhook(ctx.FormInt64("id")); err != nil { + if err := webhook.DeleteDefaultSystemWebhook(ctx, ctx.FormInt64("id")); err != nil { ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go index 96261af67..d3826c3f3 100644 --- a/routers/web/repo/webhook.go +++ b/routers/web/repo/webhook.go @@ -591,7 +591,7 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) { } else if orCtx.OrgID > 0 { w, err = webhook.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id")) } else if orCtx.IsAdmin { - w, err = webhook.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id")) + w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id")) } if err != nil || w == nil { if webhook.IsErrWebhookNotExist(err) { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index cd64b7070..726b771cf 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -138,6 +138,127 @@ } } }, + "/admin/hooks": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List system's webhooks", + "operationId": "adminListHooks", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/HookList" + } + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create a hook", + "operationId": "adminCreateHook", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/CreateHookOption" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/Hook" + } + } + } + }, + "/admin/hooks/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get a hook", + "operationId": "adminGetHook", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "id of the hook to get", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Hook" + } + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update a hook", + "operationId": "adminEditHook", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "id of the hook to update", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/EditHookOption" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/Hook" + } + } + } + }, "/admin/orgs": { "get": { "produces": [ @@ -601,6 +722,33 @@ } } }, + "/amdin/hooks/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete a hook", + "operationId": "adminDeleteHook", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "id of the hook to delete", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + } + } + } + }, "/markdown": { "post": { "consumes": [ From 2b1e47e2a2a5a31a0fc5039ed7dbb192a4a51dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Sat, 28 Jan 2023 22:29:10 -0300 Subject: [PATCH 07/67] Improve accessibility of navigation bar and footer (#22635) Added ARIA navigation landmark to navigation bar and aria label for both nav bar and footer. Contributed by @forgejo. --------- Co-authored-by: Lunny Xiao --- options/locale/locale_en-US.ini | 6 ++++++ templates/base/footer_content.tmpl | 6 +++--- templates/base/head_navbar.tmpl | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6ccbbc1c0..98922db80 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -111,6 +111,12 @@ never = Never rss_feed = RSS Feed +[aria] +navbar = Navigation Bar +footer = Footer +footer.software = About Software +footer.links = Links + [filter] string.asc = A - Z string.desc = Z - A diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 89be60922..c3b96a024 100644 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -1,6 +1,6 @@ -
+
-
+ - {{.locale.Tr "repo.issues.filter_milestone_no_select"}} {{range .Milestones}} - {{.Name}} + {{.Name}} + {{end}} +
+
+ + + @@ -86,9 +104,9 @@ {{svg "octicon-search" 16}}
- {{.locale.Tr "repo.issues.filter_poster_no_select"}} + {{.locale.Tr "repo.issues.filter_poster_no_select"}} {{range .Posters}} - + {{avatar .}} {{.GetDisplayName}} {{end}} @@ -106,9 +124,9 @@ {{svg "octicon-search" 16}}
- {{.locale.Tr "repo.issues.filter_assginee_no_select"}} + {{.locale.Tr "repo.issues.filter_assginee_no_select"}} {{range .Assignees}} - + {{avatar .}} {{.GetDisplayName}} {{end}} @@ -123,12 +141,12 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}} @@ -141,14 +159,14 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}} diff --git a/templates/repo/issue/openclose.tmpl b/templates/repo/issue/openclose.tmpl index 758fc447c..9299fe1cb 100644 --- a/templates/repo/issue/openclose.tmpl +++ b/templates/repo/issue/openclose.tmpl @@ -1,5 +1,5 @@ diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index e1f53072b..5b7b9ec5f 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -433,6 +433,21 @@ + {{if .EnableActions}} + {{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}} +
+ + {{if .UnitTypeActions.UnitGlobalDisabled}} +
+ {{else}} +
+ {{end}} + + +
+
+ {{end}} + {{if not .IsMirror}}
{{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}} diff --git a/templates/repo/settings/runner_edit.tmpl b/templates/repo/settings/runner_edit.tmpl new file mode 100644 index 000000000..10d410f09 --- /dev/null +++ b/templates/repo/settings/runner_edit.tmpl @@ -0,0 +1,9 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} + {{template "repo/settings/navbar" .}} +
+ {{template "shared/actions/runner_edit" .}} +
+
+{{template "base/footer" .}} diff --git a/templates/repo/settings/runners.tmpl b/templates/repo/settings/runners.tmpl new file mode 100644 index 000000000..d3a0b64fc --- /dev/null +++ b/templates/repo/settings/runners.tmpl @@ -0,0 +1,9 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} + {{template "repo/settings/navbar" .}} +
+ {{template "shared/actions/runner_list" .}} +
+
+{{template "base/footer" .}} diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl new file mode 100644 index 000000000..5cb65eb0e --- /dev/null +++ b/templates/shared/actions/runner_edit.tmpl @@ -0,0 +1,102 @@ +
+ {{template "base/alert" .}} +

+ {{.locale.Tr "actions.runners.runner_title"}} {{.Runner.ID}} {{.Runner.Name}} +

+
+
+ {{template "base/disable_form_autofill"}} + {{.CsrfTokenHtml}} +
+
+ + {{.Runner.StatusLocaleName $.locale}} +
+
+ + {{TimeSinceUnix .Runner.LastOnline $.locale}} +
+
+ + + {{range .Runner.AgentLabels}} + {{.}} + {{end}} + +
+
+ + {{.Runner.OwnType}} +
+
+ +
+ +
+ + +
+
+ + +

{{.locale.Tr "actions.runners.custom_labels_helper"}}

+
+ +
+ +
+ + +
+
+
+ +

+ {{.locale.Tr "actions.runners.task_list"}} +

+
+ + + + + + + + + + + + {{range .Tasks}} + + + + + + + + {{end}} + {{if not .Tasks}} + + + + {{end}} + +
{{.locale.Tr "actions.runners.task_list.run"}}{{.locale.Tr "actions.runners.task_list.status"}}{{.locale.Tr "actions.runners.task_list.repository"}}{{.locale.Tr "actions.runners.task_list.commit"}}{{.locale.Tr "actions.runners.task_list.done_at"}}
{{.ID}}{{.Status.LocaleString $.locale}}{{.GetRepoName}} + {{ShortSha .CommitSHA}} + {{if .IsStopped}} + {{TimeSinceUnix .Stopped $.locale}} + {{else}}-{{end}}
{{.locale.Tr "runners.task_list.no_tasks"}}
+ {{template "base/paginate" .}} +
+ +
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl new file mode 100644 index 000000000..eabddbb30 --- /dev/null +++ b/templates/shared/actions/runner_list.tmpl @@ -0,0 +1,90 @@ +
+ {{template "base/alert" .}} + +

+ {{.locale.Tr "actions.runners.runner_manage_panel"}} ({{.locale.Tr "admin.total" .Total}}) +
+ + +
+

+
+
+ +
+ + +
+
+
+
+ + + + + + + + + + + + + + {{if .Runners}} + {{range .Runners}} + + + + + + + + + + {{end}} + {{else}} + + + + {{end}} + +
{{.locale.Tr "actions.runners.status"}}{{.locale.Tr "actions.runners.id"}}{{.locale.Tr "actions.runners.name"}}{{.locale.Tr "actions.runners.owner_type"}}{{.locale.Tr "actions.runners.labels"}}{{.locale.Tr "actions.runners.last_online"}}
+ {{.StatusLocaleName $.locale}} + {{.ID}}

{{.Name}}

{{.OwnType}} + {{range .AllLabels}}{{.}}{{end}} + {{if .LastOnline}}{{TimeSinceUnix .LastOnline $.locale}}{{else}}{{$.locale.Tr "never"}}{{end}} + {{if .Editable $.RunnerOnwerID $.RunnerRepoID}} + {{svg "octicon-pencil"}} + {{end}} +
{{.locale.Tr "actions.runners.none"}}
+
+ + {{template "base/paginate" .}} + +
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue new file mode 100644 index 000000000..673e5b0d8 --- /dev/null +++ b/web_src/js/components/RepoActionView.vue @@ -0,0 +1,398 @@ + + + + + + diff --git a/web_src/js/index.js b/web_src/js/index.js index a86618420..14483f3fa 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -90,6 +90,7 @@ import {initRepoCommentForm, initRepository} from './features/repo-legacy.js'; import {initFormattingReplacements} from './features/formatting.js'; import {initMcaptcha} from './features/mcaptcha.js'; import {initCopyContent} from './features/copycontent.js'; +import {initRepositoryActionView} from './components/RepoActionView.vue'; // Run time-critical code as soon as possible. This is safe to do because this // script appears at the end of and rendered HTML is accessible at that point. @@ -187,6 +188,7 @@ $(document).ready(() => { initRepoTopicBar(); initRepoWikiForm(); initRepository(); + initRepositoryActionView(); initCommitStatuses(); initMcaptcha(); diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 0f08f64e8..2132ad312 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -25,8 +25,16 @@ import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collaps import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.svg'; import octiconTriangleDown from '../../public/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/img/svg/octicon-x.svg'; +import octiconCheckCircleFill from '../../public/img/svg/octicon-check-circle-fill.svg'; +import octiconXCircleFill from '../../public/img/svg/octicon-x-circle-fill.svg'; +import octiconSkip from '../../public/img/svg/octicon-skip.svg'; +import octiconMeter from '../../public/img/svg/octicon-meter.svg'; +import octiconBlocked from '../../public/img/svg/octicon-blocked.svg'; +import octiconSync from '../../public/img/svg/octicon-sync.svg'; export const svgs = { + 'octicon-blocked': octiconBlocked, + 'octicon-check-circle-fill': octiconCheckCircleFill, 'octicon-chevron-down': octiconChevronDown, 'octicon-chevron-right': octiconChevronRight, 'octicon-clock': octiconClock, @@ -44,6 +52,7 @@ export const svgs = { 'octicon-kebab-horizontal': octiconKebabHorizontal, 'octicon-link': octiconLink, 'octicon-lock': octiconLock, + 'octicon-meter': octiconMeter, 'octicon-milestone': octiconMilestone, 'octicon-mirror': octiconMirror, 'octicon-project': octiconProject, @@ -52,8 +61,11 @@ export const svgs = { 'octicon-repo-template': octiconRepoTemplate, 'octicon-sidebar-collapse': octiconSidebarCollapse, 'octicon-sidebar-expand': octiconSidebarExpand, + 'octicon-skip': octiconSkip, + 'octicon-sync': octiconSync, 'octicon-triangle-down': octiconTriangleDown, 'octicon-x': octiconX, + 'octicon-x-circle-fill': octiconXCircleFill, }; const parser = new DOMParser(); diff --git a/web_src/less/_actions.less b/web_src/less/_actions.less new file mode 100644 index 000000000..1acad06a6 --- /dev/null +++ b/web_src/less/_actions.less @@ -0,0 +1,43 @@ +@import "variables.less"; + +// TODO: the parent element's full height doesn't work well now +body > div.full.height { + padding-bottom: 0; +} + +.job-status-rotate { + animation: job-status-rotate-keyframes 1s linear infinite; +} +@keyframes job-status-rotate-keyframes { + 100% { + transform: rotate(360deg); + } +} + +.job-step-section { + margin: 10px; + .job-step-logs { + font-family: monospace; + .job-log-line { + display: flex; + .line-num { + width: 48px; + color: var(--color-grey-light); + text-align: right; + } + .log-time { + color: var(--color-grey-light); + margin-left: 10px; + white-space: nowrap; + } + .log-msg { + flex: 1; + word-break: break-all; + white-space: break-spaces; + margin-left: 10px; + } + } + + // TODO: group support + } +} diff --git a/web_src/less/_base.less b/web_src/less/_base.less index 697bc0ee7..26fc83785 100644 --- a/web_src/less/_base.less +++ b/web_src/less/_base.less @@ -1797,7 +1797,7 @@ footer { .ui { &.left, &.right { - line-height: 40px; + line-height: 39px; // there is a border-top on the footer, so make the line-height 1px less } } } diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 7aa42b1f0..5d30d0d81 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -2619,7 +2619,8 @@ } &.webhooks .list > .item:not(:first-child), - &.githooks .list > .item:not(:first-child) { + &.githooks .list > .item:not(:first-child), + &.runners .list > .item:not(:first-child) { padding: .25rem 1rem; margin: 12px -1rem -1rem; } diff --git a/web_src/less/_runner.less b/web_src/less/_runner.less new file mode 100644 index 000000000..74d917fc8 --- /dev/null +++ b/web_src/less/_runner.less @@ -0,0 +1,45 @@ +@import "variables.less"; + +.runner-container { + padding-bottom: 30px; + .runner-ops > a { + margin-left: .5em; + } + .runner-ops-delete { + color: var(--color-red-light); + } + .runner-basic-info .dib { + margin-right: 1em; + } + .runner-status-online { + .ui.label; + background-color: var(--color-green); + color: var(--color-white); + } + .runner-new-text { + color: var(--color-white); + } + #runner-new:hover .runner-new-text { + color: var(--color-white) !important; + } + .runner-new-menu { + width: 300px; + } + .task-status-success { + background-color: var(--color-green); + color: var(--color-white); + } + .task-status-failure { + background-color: var(--color-red-light); + color: var(--color-white); + } + .task-status-running { + background-color: var(--color-blue); + color: var(--color-white); + } + .task-status-cancelled, + .task-status-blocked { + background-color: var(--color-yellow); + color: var(--color-white); + } +} diff --git a/web_src/less/index.less b/web_src/less/index.less index 185bf7ca3..29cff15c5 100644 --- a/web_src/less/index.less +++ b/web_src/less/index.less @@ -37,5 +37,7 @@ @import "_explore"; @import "_review"; @import "_package"; +@import "_runner"; +@import "_actions"; @import "./helpers.less"; From 03f37d82fe82493edbed81c92c404043a3d779d6 Mon Sep 17 00:00:00 2001 From: crystal <71373843+CrystalCommunication@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:21:29 -0700 Subject: [PATCH 18/67] Fix README TOC links (#22577) Fixes anchored markup links by adding `user-content-` (which is prepended to IDs) Closes https://codeberg.org/Codeberg/Community/issues/894 --- modules/markup/html.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 73ae768d7..bcb38f99e 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -358,12 +358,19 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output } func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node) { - // Add user-content- to IDs if they don't already have them + // Add user-content- to IDs and "#" links if they don't already have them for idx, attr := range node.Attr { - if attr.Key == "id" && !(strings.HasPrefix(attr.Val, "user-content-") || blackfridayExtRegex.MatchString(attr.Val)) { + val := strings.TrimPrefix(attr.Val, "#") + notHasPrefix := !(strings.HasPrefix(val, "user-content-") || blackfridayExtRegex.MatchString(val)) + + if attr.Key == "id" && notHasPrefix { node.Attr[idx].Val = "user-content-" + attr.Val } + if attr.Key == "href" && strings.HasPrefix(attr.Val, "#") && notHasPrefix { + node.Attr[idx].Val = "#user-content-" + val + } + if attr.Key == "class" && attr.Val == "emoji" { textProcs = nil } From f47ea60c075ddc09d39bc7817efc7c4d870dcd19 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 31 Jan 2023 16:05:25 +0900 Subject: [PATCH 19/67] set user visibility class to basic (#22674) Same to https://github.com/go-gitea/gitea/pull/22605 ![user package](https://user-images.githubusercontent.com/18380374/215669905-71fe01c3-c011-4867-97a6-3df5f940a6bf.PNG) ![user projects](https://user-images.githubusercontent.com/18380374/215669909-1a4f74f1-bbde-4913-9ba5-51c44cc63862.PNG) These two page are both used at user and org, so if i fixed the org page, the user page will be also be fixed. --- templates/user/overview/header.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index 8fb882718..18fbd3070 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -8,8 +8,8 @@ {{avatar . 100}} {{.DisplayName}} - {{if .Visibility.IsLimited}}
{{$.locale.Tr "org.settings.visibility.limited_shortname"}}
{{end}} - {{if .Visibility.IsPrivate}}
{{$.locale.Tr "org.settings.visibility.private_shortname"}}
{{end}} + {{if .Visibility.IsLimited}}
{{$.locale.Tr "org.settings.visibility.limited_shortname"}}
{{end}} + {{if .Visibility.IsPrivate}}
{{$.locale.Tr "org.settings.visibility.private_shortname"}}
{{end}}
From 36dc11869d0401b796a7a3f74627fec842a4a89a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 31 Jan 2023 17:08:05 +0800 Subject: [PATCH 20/67] Use correct captured group range when parsing cross-reference (#22672) Fixes #22666 (Replace #22668) Co-authored-by: Lunny Xiao Co-authored-by: KN4CK3R Co-authored-by: zeripath --- modules/references/references.go | 6 +++--- modules/references/references_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/references/references.go b/modules/references/references.go index 1022e5af3..68662425c 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -35,12 +35,12 @@ var ( // issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234 issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$))`) // crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository - // e.g. gogits/gogs#12345 + // e.g. org/repo#12345 crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) // crossReferenceCommitPattern matches a string that references a commit in a different repository // e.g. go-gitea/gitea@d8a994ef, go-gitea/gitea@d8a994ef243349f321568f9e36d5c3f444b99cae (7-40 characters) crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,40})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) - // spaceTrimmedPattern let's us find the trailing space + // spaceTrimmedPattern let's find the trailing space spaceTrimmedPattern = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`) // timeLogPattern matches string for time tracking timeLogPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@([0-9]+([\.,][0-9]+)?(w|d|m|h))+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) @@ -365,7 +365,7 @@ func FindRenderizableCommitCrossReference(content string) (bool, *RenderizableRe Owner: content[m[2]:m[3]], Name: content[m[4]:m[5]], CommitSha: content[m[6]:m[7]], - RefLocation: &RefSpan{Start: m[0], End: m[1]}, + RefLocation: &RefSpan{Start: m[2], End: m[7]}, } } diff --git a/modules/references/references_test.go b/modules/references/references_test.go index 1159e14b3..75e002c99 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -352,7 +352,7 @@ func TestFindRenderizableCommitCrossReference(t *testing.T) { Owner: "go-gitea", Name: "gitea", CommitSha: "abcd1234", - RefLocation: &RefSpan{Start: 4, End: 29}, + RefLocation: &RefSpan{Start: 5, End: 28}, }, }, } From c14d3e80e8f4243768eef8af6a6fa9df9ea5fedd Mon Sep 17 00:00:00 2001 From: Francesco Siddi Date: Tue, 31 Jan 2023 13:58:34 +0100 Subject: [PATCH 21/67] Remove label color from global issue filters (#22660) The use of ui colors (red, green, etc) should be limited to actionable or dismissable entries. Before this commit, a green/red label was used to display issues count on each repository. This did not add any meaningful information to the list. Removing the label reduces ambiguity and makes the list easier to scan visually. ![label_compare](https://user-images.githubusercontent.com/451841/215360696-a881b765-207d-4ffa-8bec-398f8e5dab1e.jpg) --------- Co-authored-by: delvh --- templates/user/dashboard/issues.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 1a0271d8f..4ff6772a7 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -30,7 +30,7 @@
All -
{{CountFmt .TotalIssueCount}}
+ {{CountFmt .TotalIssueCount}}
{{range .Repos}} {{with $Repo := .}} @@ -49,7 +49,7 @@ {{- end -}} ]&sort={{$.SortType}}&state={{$.State}}&q={{$.Keyword}}" title="{{.FullName}}"> {{$Repo.FullName}} -
{{CountFmt (index $.Counts $Repo.ID)}}
+ {{CountFmt (index $.Counts $Repo.ID)}} {{end}} {{end}} From b80538f37d6142470efcadf9164a9c55d1888d41 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 31 Jan 2023 14:56:22 +0100 Subject: [PATCH 22/67] Disable test for incoming email (#22686) Disable this test for the moment because the used imap container image seems unstable which results in many failed CI builds. Co-authored-by: Jason Song --- tests/mysql.ini.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index 44914d087..b7ed98c34 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -126,7 +126,7 @@ INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.h ENABLED = true [email.incoming] -ENABLED = true +ENABLED = false HOST = smtpimap PORT = 993 USERNAME = debug@localdomain.test From 85016af1fee8c11a3b2f2b84b20f74eaa5730ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Tue, 31 Jan 2023 12:28:43 -0300 Subject: [PATCH 23/67] Fixes accessibility behavior of Watching, Staring and Fork buttons (#22634) Add tabindex to buttons of repository views. --- templates/repo/header.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 29ae8d284..0e5016948 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -68,7 +68,7 @@ {{end}}
{{$.CsrfTokenHtml}} -
+
@@ -80,7 +80,7 @@ {{if not $.DisableStars}} {{$.CsrfTokenHtml}} -
+
@@ -100,7 +100,7 @@ {{else if and (not $.CanSignedUserFork) (eq (len $.UserAndOrgForks) 0)}} data-content="{{$.locale.Tr "repo.fork_from_self"}}" {{end}} - data-position="top center" tabindex="0"> + data-position="top center"> Date: Tue, 31 Jan 2023 18:40:22 +0100 Subject: [PATCH 24/67] Hide collapse icon in diff with no lines (#21094) Sometimes (e.g. renaming a file) nothing of the content is changed. In this case, it makes no sense to show the collapse icon in a diff. --- templates/repo/diff/box.tmpl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 8482c31cd..f74714499 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -79,13 +79,15 @@

- - {{if $file.ShouldBeHidden}} - {{svg "octicon-chevron-right" 18}} - {{else}} - {{svg "octicon-chevron-down" 18}} - {{end}} - + {{if or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}} + + {{if $file.ShouldBeHidden}} + {{svg "octicon-chevron-right" 18}} + {{else}} + {{svg "octicon-chevron-down" 18}} + {{end}} + + {{end}}
{{if $file.IsBin}} From 706f4686b8eeced9fbd9634ead9a7eef47395610 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 1 Feb 2023 00:42:41 +0300 Subject: [PATCH 25/67] Fix typo in storage.en-us.md (#22694) --- docs/content/doc/packages/storage.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/packages/storage.en-us.md b/docs/content/doc/packages/storage.en-us.md index b04a7595f..8b05b8a45 100644 --- a/docs/content/doc/packages/storage.en-us.md +++ b/docs/content/doc/packages/storage.en-us.md @@ -71,7 +71,7 @@ The patterns are case-insensitive which matches the behaviour of the package reg ### How the cleanup rules work -The cleanup rules are part of the [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}) and run periodicly. +The cleanup rules are part of the [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}) and run periodically. The cleanup rule: From 66877aed5439de3b26510bc0d45e434510be598b Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 1 Feb 2023 00:43:12 +0300 Subject: [PATCH 26/67] Fix typo in guidelines-backend.en-us.md (#22690) --- docs/content/doc/developers/guidelines-backend.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/developers/guidelines-backend.en-us.md b/docs/content/doc/developers/guidelines-backend.en-us.md index 913898be8..f35111522 100644 --- a/docs/content/doc/developers/guidelines-backend.en-us.md +++ b/docs/content/doc/developers/guidelines-backend.en-us.md @@ -54,7 +54,7 @@ To maintain understandable code and avoid circular dependencies it is important ### Package Dependencies -Since Golang don't support import cycles, we have to decide the package dependencies carefully. There are some levels between those packages. Below is the ideal package dependencies direction. +Since Golang doesn't support import cycles, we have to decide the package dependencies carefully. There are some levels between those packages. Below is the ideal package dependencies direction. `cmd` -> `routers` -> `services` -> `models` -> `modules` From f518b42d4c165cf52d5efdcaa362044a2507c5d2 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 1 Feb 2023 00:43:27 +0300 Subject: [PATCH 27/67] Fix typo in linked-references.en-us.md (#22682) --- docs/content/doc/usage/linked-references.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/usage/linked-references.en-us.md b/docs/content/doc/usage/linked-references.en-us.md index bb40acdcd..df2813792 100644 --- a/docs/content/doc/usage/linked-references.en-us.md +++ b/docs/content/doc/usage/linked-references.en-us.md @@ -24,7 +24,7 @@ in search for references. These references will be shown as links in the Issue V and, in some cases, produce certain _actions_. Likewise, commit messages are parsed when they are listed, and _actions_ -are can be triggered when they are pushed to the main branch. +can be triggered when they are pushed to the main branch. To prevent the creation of unintended references, there are certain rules for them to be recognized. For example, they should not be included inside code From 519939fa8c325bf2112286a652fed3256051b260 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 1 Feb 2023 00:43:39 +0300 Subject: [PATCH 28/67] Fix typo in command-line.en-us.md (#22681) Co-authored-by: Lunny Xiao --- docs/content/doc/usage/command-line.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/usage/command-line.en-us.md b/docs/content/doc/usage/command-line.en-us.md index 5f05bc4c3..d9d397df3 100644 --- a/docs/content/doc/usage/command-line.en-us.md +++ b/docs/content/doc/usage/command-line.en-us.md @@ -382,7 +382,7 @@ Currently there are a check list below: Sometimes if you moved or renamed your Gitea binary when upgrade and you haven't run `Update the '.ssh/authorized_keys' file with Gitea SSH keys. (Not needed for the built-in SSH server.)` on your Admin Panel. Then all pull/push via SSH will not be work. This check will help you to check if it works well. -For contributors, if you want to add more checks, you can wrie ad new function like `func(ctx *cli.Context) ([]string, error)` and +For contributors, if you want to add more checks, you can write a new function like `func(ctx *cli.Context) ([]string, error)` and append it to `doctor.go`. ```go From f1f0430f9fd731310713ad723bed9618bcd53cda Mon Sep 17 00:00:00 2001 From: zeripath Date: Tue, 31 Jan 2023 22:10:54 +0000 Subject: [PATCH 29/67] Add Contributed backport command (#22643) This PR provides a contributed backport command to help create backports for Gitea. It represents a significant improvement on my previously described shell-script. It can be installed using `go install contrib/backport/backport.go`. Signed-off-by: Andrew Thornton --- CONTRIBUTING.md | 20 +- contrib/backport/README | 41 ++++ contrib/backport/backport.go | 438 +++++++++++++++++++++++++++++++++++ 3 files changed, 481 insertions(+), 18 deletions(-) create mode 100644 contrib/backport/README create mode 100644 contrib/backport/backport.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbf2a331d..7a23448e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -267,26 +267,10 @@ with the rest of the summary matching the original PR. Similarly for frontports --- -The below is a script that may be helpful in creating backports. YMMV. +A command to help create backports can be found in `contrib/backport` and can be installed (from inside the gitea repo root directory) using: ```bash -#!/bin/sh -PR="$1" -SHA="$2" -VERSION="$3" - -if [ -z "$SHA" ]; then - SHA=$(gh api /repos/go-gitea/gitea/pulls/$PR -q '.merge_commit_sha') -fi - -if [ -z "$VERSION" ]; then - VERSION="v1.16" -fi - -echo git checkout origin/release/"$VERSION" -b backport-$PR-$VERSION -git checkout origin/release/"$VERSION" -b backport-$PR-$VERSION -git cherry-pick $SHA && git commit --amend && git push zeripath backport-$PR-$VERSION && xdg-open https://github.com/go-gitea/gitea/compare/release/"$VERSION"...zeripath:backport-$PR-$VERSION - +go install contrib/backport/backport.go ``` ## Developer Certificate of Origin (DCO) diff --git a/contrib/backport/README b/contrib/backport/README new file mode 100644 index 000000000..1e84c1bb9 --- /dev/null +++ b/contrib/backport/README @@ -0,0 +1,41 @@ +`backport` +========== + +`backport` is a command to help create backports of PRs. It backports a +provided PR from main on to a released version. + +It will create a backport branch, cherry-pick the PR's merge commit, adjust +the commit message and then push this back up to your fork's remote. + +The default version will read from `docs/config.yml`. You can override this +using the option `--version`. + +The upstream branches will be fetched, using the remote `origin`. This can +be overrided using `--upstream`, and fetching can be avoided using +`--no-fetch`. + +By default the branch created will be called `backport-$PR-$VERSION`. You +can override this using the option `--backport-branch`. This branch will +be created from `--release-branch` which is `release/$(VERSION)` +by default and will be pulled from `$(UPSTREAM)`. + +The merge-commit as determined by the github API will be used as the SHA to +cherry-pick. You can override this using `--cherry-pick`. + +The commit message will be amended to add the `Backport` header. +`--no-amend-message` can be set to stop this from happening. + +If cherry-pick is successful the backported branch will be pushed up to your +fork using your remote. These will be determined using `git remote -v`. You +can set your fork name using `--fork-user` and your remote name using +`--remote`. You can avoid pushing using `--no-push`. + +If the push is successful, `xdg-open` will be called to open a backport url. +You can stop this using `--no-xdg-open`. + +Installation +============ + +```bash +go install contrib/backport/backport.go +``` diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go new file mode 100644 index 000000000..c35bf9e30 --- /dev/null +++ b/contrib/backport/backport.go @@ -0,0 +1,438 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "os" + "os/exec" + "os/signal" + "path" + "strconv" + "strings" + "syscall" + + "github.com/google/go-github/v45/github" + "github.com/urfave/cli" + "gopkg.in/yaml.v3" +) + +const defaultVersion = "v1.18" // to backport to + +func main() { + app := cli.NewApp() + app.Name = "backport" + app.Usage = "Backport provided PR-number on to the current or previous released version" + app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version` + app.ArgsUsage = "" + + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "version", + Usage: "Version branch to backport on to", + }, + cli.StringFlag{ + Name: "upstream", + Value: "origin", + Usage: "Upstream remote for the Gitea upstream", + }, + cli.StringFlag{ + Name: "release-branch", + Value: "", + Usage: "Release branch to backport on. Will default to release/", + }, + cli.StringFlag{ + Name: "cherry-pick", + Usage: "SHA to cherry-pick as backport", + }, + cli.StringFlag{ + Name: "backport-branch", + Usage: "Backport branch to backport on to (default: backport--", + }, + cli.StringFlag{ + Name: "remote", + Value: "", + Usage: "Remote for your fork of the Gitea upstream", + }, + cli.StringFlag{ + Name: "fork-user", + Value: "", + Usage: "Forked user name on Github", + }, + cli.BoolFlag{ + Name: "no-fetch", + Usage: "Set this flag to prevent fetch of remote branches", + }, + cli.BoolFlag{ + Name: "no-amend-message", + Usage: "Set this flag to prevent automatic amendment of the commit message", + }, + cli.BoolFlag{ + Name: "no-push", + Usage: "Set this flag to prevent pushing the backport up to your fork", + }, + cli.BoolFlag{ + Name: "no-xdg-open", + Usage: "Set this flag to not use xdg-open to open the PR URL", + }, + } + cli.AppHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} +USAGE: + {{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} + {{if len .Authors}} +AUTHOR: + {{range .Authors}}{{ . }}{{end}} + {{end}}{{if .Commands}} +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + + app.Action = runBackport + + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err) + } +} + +func runBackport(c *cli.Context) error { + ctx, cancel := installSignals() + defer cancel() + + version := c.String("version") + if version == "" { + version = readVersion() + } + if version == "" { + version = defaultVersion + } + + upstream := c.String("upstream") + if upstream == "" { + upstream = "origin" + } + + forkUser := c.String("fork-user") + remote := c.String("remote") + if remote == "" && !c.Bool("--no-push") { + var err error + remote, forkUser, err = determineRemote(ctx, forkUser) + if err != nil { + return err + } + } + + upstreamReleaseBranch := c.String("release-branch") + if upstreamReleaseBranch == "" { + upstreamReleaseBranch = path.Join("release", version) + } + + localReleaseBranch := path.Join(upstream, upstreamReleaseBranch) + + args := c.Args() + if len(args) == 0 { + return fmt.Errorf("no PR number provided\nProvide a PR number to backport") + } else if len(args) != 1 { + return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args) + } + + pr := args[0] + + backportBranch := c.String("backport-branch") + if backportBranch == "" { + backportBranch = "backport-" + pr + "-" + version + } + + fmt.Printf("* Backporting %s to %s as %s\n", pr, localReleaseBranch, backportBranch) + + sha := c.String("cherry-pick") + if sha == "" { + var err error + sha, err = determineSHAforPR(ctx, pr) + if err != nil { + return err + } + } + if sha == "" { + return fmt.Errorf("unable to determine sha for cherry-pick of %s", pr) + } + + if !c.Bool("no-fetch") { + if err := fetchRemoteAndMain(ctx, upstream, upstreamReleaseBranch); err != nil { + return err + } + } + + if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil { + return err + } + + if err := cherrypick(ctx, sha); err != nil { + return err + } + + if !c.Bool("no-amend-message") { + if err := amendCommit(ctx, pr); err != nil { + return err + } + } + + if !c.Bool("no-push") { + url := "https://github.com/go-gitea/gitea/compare/" + upstreamReleaseBranch + "..." + forkUser + ":" + backportBranch + + if err := gitPushUp(ctx, remote, backportBranch); err != nil { + return err + } + + if !c.Bool("no-xdg-open") { + if err := xdgOpen(ctx, url); err != nil { + return err + } + } else { + fmt.Printf("* Navigate to %s to open PR\n", url) + } + } + return nil +} + +func xdgOpen(ctx context.Context, url string) error { + fmt.Printf("* `xdg-open %s`\n", url) + out, err := exec.CommandContext(ctx, "xdg-open", url).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "%s", string(out)) + return fmt.Errorf("unable to xdg-open to %s: %w", url, err) + } + return nil +} + +func gitPushUp(ctx context.Context, remote, backportBranch string) error { + fmt.Printf("* `git push -u %s %s`\n", remote, backportBranch) + out, err := exec.CommandContext(ctx, "git", "push", "-u", remote, backportBranch).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "%s", string(out)) + return fmt.Errorf("unable to push up to %s: %w", remote, err) + } + return nil +} + +func amendCommit(ctx context.Context, pr string) error { + fmt.Printf("* Amending commit to prepend `Backport #%s` to body\n", pr) + out, err := exec.CommandContext(ctx, "git", "log", "-1", "--pretty=format:%B").Output() + if err != nil { + fmt.Fprintf(os.Stderr, "%s", string(out)) + return fmt.Errorf("unable to get last log message: %w", err) + } + + parts := strings.SplitN(string(out), "\n", 2) + + if len(parts) != 2 { + return fmt.Errorf("unable to interpret log message:\n%s", string(out)) + } + subject, body := parts[0], parts[1] + if !strings.HasSuffix(subject, " (#"+pr+")") { + subject = subject + " (#" + pr + ")" + } + + out, err = exec.CommandContext(ctx, "git", "commit", "--amend", "-m", subject+"\n\nBackport #"+pr+"\n"+body).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "%s", string(out)) + return fmt.Errorf("unable to amend last log message: %w", err) + } + return nil +} + +func cherrypick(ctx context.Context, sha string) error { + // Check if a CHERRY_PICK_HEAD exists + if _, err := os.Stat(".git/CHERRY_PICK_HEAD"); err == nil { + // Assume that we are in the middle of cherry-pick - continue it + fmt.Println("* Attempting git cherry-pick --continue") + out, err := exec.CommandContext(ctx, "git", "cherry-pick", "--continue").Output() + if err != nil { + fmt.Fprintf(os.Stderr, "git cherry-pick --continue failed:\n%s\n", string(out)) + return fmt.Errorf("unable to continue cherry-pick: %w", err) + } + return nil + } + + fmt.Printf("* Attempting git cherry-pick %s\n", sha) + out, err := exec.CommandContext(ctx, "git", "cherry-pick", sha).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "git cherry-pick %s failed:\n%s\n", sha, string(out)) + return fmt.Errorf("git cherry-pick %s failed: %w", sha, err) + } + return nil +} + +func checkoutBackportBranch(ctx context.Context, backportBranch, releaseBranch string) error { + out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output() + if err != nil { + return fmt.Errorf("unable to check current branch %w", err) + } + + currentBranch := strings.TrimSpace(string(out)) + fmt.Printf("* Current branch is %s\n", currentBranch) + if currentBranch == backportBranch { + fmt.Printf("* Current branch is %s - not checking out\n", currentBranch) + return nil + } + + if _, err := exec.CommandContext(ctx, "git", "rev-list", "-1", backportBranch).Output(); err == nil { + fmt.Printf("* Branch %s already exists. Checking it out...\n", backportBranch) + return exec.CommandContext(ctx, "git", "checkout", "-f", backportBranch).Run() + } + + fmt.Printf("* `git checkout -b %s %s`\n", backportBranch, releaseBranch) + return exec.CommandContext(ctx, "git", "checkout", "-b", backportBranch, releaseBranch).Run() +} + +func fetchRemoteAndMain(ctx context.Context, remote, releaseBranch string) error { + fmt.Printf("* `git fetch %s main`\n", remote) + out, err := exec.CommandContext(ctx, "git", "fetch", remote, "main").Output() + if err != nil { + fmt.Println(string(out)) + return fmt.Errorf("unable to fetch %s from %s: %w", "main", remote, err) + } + fmt.Println(string(out)) + + fmt.Printf("* `git fetch %s %s`\n", remote, releaseBranch) + out, err = exec.CommandContext(ctx, "git", "fetch", remote, releaseBranch).Output() + if err != nil { + fmt.Println(string(out)) + return fmt.Errorf("unable to fetch %s from %s: %w", releaseBranch, remote, err) + } + fmt.Println(string(out)) + + return nil +} + +func determineRemote(ctx context.Context, forkUser string) (string, string, error) { + out, err := exec.CommandContext(ctx, "git", "remote", "-v").Output() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out)) + return "", "", fmt.Errorf("unable to determine forked remote: %w", err) + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + fields := strings.Split(line, "\t") + name, remote := fields[0], fields[1] + // only look at pushers + if !strings.HasSuffix(remote, " (push)") { + continue + } + // only look at github.com pushes + if !strings.Contains(remote, "github.com") { + continue + } + // ignore go-gitea/gitea + if strings.Contains(remote, "go-gitea/gitea") { + continue + } + if !strings.Contains(remote, forkUser) { + continue + } + if strings.HasPrefix(remote, "git@github.com:") { + forkUser = strings.TrimPrefix(remote, "git@github.com:") + } else if strings.HasPrefix(remote, "https://github.com/") { + forkUser = strings.TrimPrefix(remote, "https://github.com/") + } else if strings.HasPrefix(remote, "https://www.github.com/") { + forkUser = strings.TrimPrefix(remote, "https://www.github.com/") + } else if forkUser == "" { + return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote) + } + idx := strings.Index(forkUser, "/") + if idx >= 0 { + forkUser = forkUser[:idx] + } + return name, forkUser, nil + } + return "", "", fmt.Errorf("unable to find appropriate remote in:\n%s", string(out)) +} + +func readVersion() string { + bs, err := os.ReadFile("docs/config.yaml") + if err != nil { + if err == os.ErrNotExist { + log.Println("`docs/config.yaml` not present") + return "" + } + fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err) + return "" + } + + type params struct { + Version string + } + type docConfig struct { + Params params + } + dc := &docConfig{} + if err := yaml.Unmarshal(bs, dc); err != nil { + fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err) + return "" + } + + if dc.Params.Version == "" { + fmt.Fprintf(os.Stderr, "No version in `docs/config.yaml`") + return "" + } + + version := dc.Params.Version + if version[0] != 'v' { + version = "v" + version + } + + split := strings.SplitN(version, ".", 3) + + return strings.Join(split[:2], ".") +} + +func determineSHAforPR(ctx context.Context, prStr string) (string, error) { + prNum, err := strconv.Atoi(prStr) + if err != nil { + return "", err + } + + client := github.NewClient(http.DefaultClient) + + pr, _, err := client.PullRequests.Get(ctx, "go-gitea", "gitea", prNum) + if err != nil { + return "", err + } + + if pr.Merged == nil || !*pr.Merged { + return "", fmt.Errorf("PR #%d is not yet merged - cannot determine sha to backport", prNum) + } + + if pr.MergeCommitSHA != nil { + return *pr.MergeCommitSHA, nil + } + + return "", nil +} + +func installSignals() (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + go func() { + // install notify + signalChannel := make(chan os.Signal, 1) + + signal.Notify( + signalChannel, + syscall.SIGINT, + syscall.SIGTERM, + ) + select { + case <-signalChannel: + case <-ctx.Done(): + } + cancel() + signal.Reset() + }() + + return ctx, cancel +} From cc910014ab0df4d252ee61995df37b4a4cb92e06 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 1 Feb 2023 06:11:48 +0800 Subject: [PATCH 30/67] Fix wrong hint when deleting a branch successfully from pull request UI (#22673) Fix #18785 --- routers/web/repo/pull.go | 2 +- tests/integration/pull_merge_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c2208120f..71bc98d13 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1395,7 +1395,7 @@ func CleanUpPullRequest(ctx *context.Context) { } func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) { - fullBranchName := pr.HeadRepo.Owner.Name + "/" + pr.HeadBranch + fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch if err := repo_service.DeleteBranch(ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil { switch { case git.IsErrBranchNotExist(err): diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 491fc0e0a..e80822a75 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -187,7 +187,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() - assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg) + assert.EqualValues(t, "Branch 'user1/repo1:feature/test' has been deleted.", resultMsg) }) } From 1947409ef07f2dc4e0eab7482368816261510133 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 31 Jan 2023 23:40:38 +0100 Subject: [PATCH 31/67] Fix missing title and filter in issue sidebar project menu (#22557) These exist in the new issue form but were seemingly forgotten here. --- templates/repo/issue/view_content/sidebar.tmpl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index ca947e361..10bb6a07f 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -212,6 +212,13 @@ {{end}}