From 000d7c1ccb4db8041d2ca59b187a2d3c29e17be1 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:57:08 +0000 Subject: [PATCH] Improve timeline entries for WIP prefix changes in pull requests (#36518) Add new timeline event types when the WIP prefix is added or removed, replacing the previous ugly title change messages. Fixes: https://github.com/go-gitea/gitea/issues/36517 --------- Co-authored-by: silverwind Co-authored-by: wxiaoguang --- .gitignore | 2 - AGENTS.md | 2 - CLAUDE.md | 1 + models/issues/pull.go | 12 +++- modules/templates/util_render_comment.go | 48 +++++++++++++++ modules/templates/util_render_comment_test.go | 31 ++++++++++ options/locale/locale_en-US.json | 2 + routers/web/repo/pull.go | 61 ++++++++++--------- .../repo/issue/view_content/comments.tmpl | 6 +- 9 files changed, 127 insertions(+), 38 deletions(-) create mode 100644 CLAUDE.md create mode 100644 modules/templates/util_render_comment.go create mode 100644 modules/templates/util_render_comment_test.go diff --git a/.gitignore b/.gitignore index 11af4543bd..aa08e47aec 100644 --- a/.gitignore +++ b/.gitignore @@ -121,8 +121,6 @@ prime/ /.goosehints /.windsurfrules /.github/copilot-instructions.md -/AGENT.md -/CLAUDE.md /llms.txt # Ignore worktrees when working on multiple branches diff --git a/AGENTS.md b/AGENTS.md index f4414bfc8c..d0912c6bde 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,6 @@ # Instructions for agents - Use `make help` to find available development targets -- Use the latest Golang stable release when working on Go code -- Use the latest Node.js LTS release when working on TypeScript code - Before committing `.go` changes, run `make fmt` to format, and run `make lint-go` to lint - Before committing `.ts` changes, run `make lint-js` to lint - Before committing `go.mod` changes, run `make tidy` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/models/issues/pull.go b/models/issues/pull.go index 18977ed212..9f180f9ac9 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -658,12 +658,18 @@ func (pr *PullRequest) IsWorkInProgress(ctx context.Context) bool { // HasWorkInProgressPrefix determines if the given PR title has a Work In Progress prefix func HasWorkInProgressPrefix(title string) bool { + _, ok := CutWorkInProgressPrefix(title) + return ok +} + +func CutWorkInProgressPrefix(title string) (origTitle string, ok bool) { for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes { - if strings.HasPrefix(strings.ToUpper(title), strings.ToUpper(prefix)) { - return true + prefixLen := len(prefix) + if prefixLen <= len(title) && util.AsciiEqualFold(title[:prefixLen], prefix) { + return title[len(prefix):], true } } - return false + return title, false } // IsFilesConflicted determines if the Pull Request has changes conflicting with the target branch. diff --git a/modules/templates/util_render_comment.go b/modules/templates/util_render_comment.go new file mode 100644 index 0000000000..73f36ad21c --- /dev/null +++ b/modules/templates/util_render_comment.go @@ -0,0 +1,48 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates + +import ( + "html/template" + "strings" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/htmlutil" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/util" +) + +func commentTimelineEventIsWipToggle(c *issues_model.Comment) (isToggle, isWip bool) { + title1, ok1 := issues_model.CutWorkInProgressPrefix(c.OldTitle) + title2, ok2 := issues_model.CutWorkInProgressPrefix(c.NewTitle) + return ok1 != ok2 && strings.TrimSpace(title1) == strings.TrimSpace(title2), ok2 +} + +func (ut *RenderUtils) RenderTimelineEventBadge(c *issues_model.Comment) template.HTML { + if c.Type == issues_model.CommentTypeChangeTitle { + isToggle, isWip := commentTimelineEventIsWipToggle(c) + if !isToggle { + return svg.RenderHTML("octicon-pencil") + } + return util.Iif(isWip, svg.RenderHTML("octicon-git-pull-request-draft"), svg.RenderHTML("octicon-eye")) + } + setting.PanicInDevOrTesting("unimplemented comment type %v: %v", c.Type, c) + return htmlutil.HTMLFormat("(CommentType:%v)", c.Type) +} + +func (ut *RenderUtils) RenderTimelineEventComment(c *issues_model.Comment, createdStr template.HTML) template.HTML { + if c.Type == issues_model.CommentTypeChangeTitle { + locale := ut.ctx.Value(translation.ContextKey).(translation.Locale) + isToggle, isWip := commentTimelineEventIsWipToggle(c) + if !isToggle { + return locale.Tr("repo.issues.change_title_at", ut.RenderEmoji(c.OldTitle), ut.RenderEmoji(c.NewTitle), createdStr) + } + trKey := util.Iif(isWip, "repo.pulls.marked_as_work_in_progress_at", "repo.pulls.marked_as_ready_for_review_at") + return locale.Tr(trKey, createdStr) + } + setting.PanicInDevOrTesting("unimplemented comment type %v: %v", c.Type, c) + return htmlutil.HTMLFormat("(Comment:%v,%v)", c.Type, c.Content) +} diff --git a/modules/templates/util_render_comment_test.go b/modules/templates/util_render_comment_test.go new file mode 100644 index 0000000000..27e67bd354 --- /dev/null +++ b/modules/templates/util_render_comment_test.go @@ -0,0 +1,31 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates + +import ( + "html/template" + "testing" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/reqctx" + "code.gitea.io/gitea/modules/translation" + + "github.com/stretchr/testify/assert" +) + +func TestRenderTimelineEventComment(t *testing.T) { + ctx := reqctx.NewRequestContextForTest(t.Context()) + ctx.SetContextValue(translation.ContextKey, &translation.MockLocale{}) + ut := &RenderUtils{ctx: ctx} + var createdStr template.HTML = "(created-at)" + + c := &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "WIP: title", NewTitle: "title"} + assert.Equal(t, "repo.pulls.marked_as_ready_for_review_at:(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) + + c = &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "title", NewTitle: "WIP: title"} + assert.Equal(t, "repo.pulls.marked_as_work_in_progress_at:(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) + + c = &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "title", NewTitle: "WIP: new title"} + assert.Equal(t, "repo.issues.change_title_at:title,WIP: new title,(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) +} diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 417698544f..9ad81d5a8d 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -1778,6 +1778,8 @@ "repo.pulls.title_desc": "wants to merge %[1]d commits from %[2]s into %[3]s", "repo.pulls.merged_title_desc": "merged %[1]d commits from %[2]s into %[3]s %[4]s", "repo.pulls.change_target_branch_at": "changed target branch from %s to %s %s", + "repo.pulls.marked_as_work_in_progress_at": "marked the pull request as work in progress %s", + "repo.pulls.marked_as_ready_for_review_at": "marked the pull request as ready for review %s", "repo.pulls.tab_conversation": "Conversation", "repo.pulls.tab_commits": "Commits", "repo.pulls.tab_files": "Files Changed", diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index cff501ad71..d306927001 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -344,6 +344,35 @@ func (d *pullCommitStatusCheckData) CommitStatusCheckPrompt(locale translation.L return locale.TrString("repo.pulls.status_checking") } +func getViewPullHeadBranchInfo(ctx *context.Context, pull *issues_model.PullRequest, baseGitRepo *git.Repository) (headCommitID string, headCommitExists bool, err error) { + if pull.HeadRepo == nil { + return "", false, nil + } + headGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pull.HeadRepo) + if err != nil { + return "", false, util.Iif(errors.Is(err, util.ErrNotExist), nil, err) + } + defer closer.Close() + + if pull.Flow == issues_model.PullRequestFlowGithub { + headCommitExists, _ = git_model.IsBranchExist(ctx, pull.HeadRepo.ID, pull.HeadBranch) + } else { + headCommitExists = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitHeadRefName()) + } + + if headCommitExists { + if pull.Flow != issues_model.PullRequestFlowGithub { + headCommitID, err = baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) + } else { + headCommitID, err = headGitRepo.GetBranchCommitID(pull.HeadBranch) + } + if err != nil { + return "", false, util.Iif(errors.Is(err, util.ErrNotExist), nil, err) + } + } + return headCommitID, headCommitExists, nil +} + // prepareViewPullInfo show meta information for a pull request preview page func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_service.CompareInfo { ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes @@ -430,34 +459,10 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_s return compareInfo } - var headBranchExist bool - var headBranchSha string - // HeadRepo may be missing - if pull.HeadRepo != nil { - headGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pull.HeadRepo) - if err != nil { - ctx.ServerError("RepositoryFromContextOrOpen", err) - return nil - } - defer closer.Close() - - if pull.Flow == issues_model.PullRequestFlowGithub { - headBranchExist, _ = git_model.IsBranchExist(ctx, pull.HeadRepo.ID, pull.HeadBranch) - } else { - headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitHeadRefName()) - } - - if headBranchExist { - if pull.Flow != issues_model.PullRequestFlowGithub { - headBranchSha, err = baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) - } else { - headBranchSha, err = headGitRepo.GetBranchCommitID(pull.HeadBranch) - } - if err != nil { - ctx.ServerError("GetBranchCommitID", err) - return nil - } - } + headBranchSha, headBranchExist, err := getViewPullHeadBranchInfo(ctx, pull, baseGitRepo) + if err != nil { + ctx.ServerError("getViewPullHeadBranchInfo", err) + return nil } if headBranchExist { diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 0eeb10cba7..e7b4c8758d 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -1,5 +1,5 @@ {{template "base/alert"}} -{{range .Issue.Comments}} +{{range $comment := .Issue.Comments}} {{if call $.ShouldShowCommentType .Type}} {{$createdStr:= DateUtils.TimeSince .CreatedUnix}} @@ -220,11 +220,11 @@ {{else if eq .Type 10}}
- {{svg "octicon-pencil"}} + {{ctx.RenderUtils.RenderTimelineEventBadge $comment}} {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.change_title_at" (.OldTitle|ctx.RenderUtils.RenderEmoji) (.NewTitle|ctx.RenderUtils.RenderEmoji) $createdStr}} + {{ctx.RenderUtils.RenderTimelineEventComment $comment $createdStr}}
{{else if eq .Type 11}}