From 2c6fa6c1e05437b5515c7be4f516ac4e4a59bf3c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2024 11:02:42 +0800 Subject: [PATCH 1/7] Fix template bug of pull request view (#32072) Caused by #31924 --- templates/repo/issue/view_content/sidebar.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 49c40be5a94d..e49e90df56c9 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -109,7 +109,7 @@
- + {{svg (printf "octicon-%s" .Type.Icon) 16 (printf "text %s" (.HTMLTypeColorName))}}
From aadbe0488f454b9f7f5a56765f4530f9d1e2c6ec Mon Sep 17 00:00:00 2001 From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:38:08 -0700 Subject: [PATCH 2/7] Truncate commit message during Discord webhook push events (#31970) Resolves #31668. --- services/webhook/discord.go | 11 +++++++++-- services/webhook/discord_test.go | 14 ++++++++++++++ services/webhook/general_test.go | 10 +++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/services/webhook/discord.go b/services/webhook/discord.go index f27b39dac371..59e87a7e1fb6 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -11,6 +11,7 @@ import ( "net/url" "strconv" "strings" + "unicode/utf8" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" @@ -154,8 +155,14 @@ func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) { var text string // for each commit, generate attachment text for i, commit := range p.Commits { - text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, - strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name) + // limit the commit message display to just the summary, otherwise it would be hard to read + message := strings.TrimRight(strings.SplitN(commit.Message, "\n", 1)[0], "\r") + + // a limit of 50 is set because GitHub does the same + if utf8.RuneCountInString(message) > 50 { + message = fmt.Sprintf("%.47s...", message) + } + text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, message, commit.Author.Name) // add linebreak to each commit but the last if i < len(p.Commits)-1 { text += "\n" diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go index c04b95383bf1..fbb4b24ef12d 100644 --- a/services/webhook/discord_test.go +++ b/services/webhook/discord_test.go @@ -80,6 +80,20 @@ func TestDiscordPayload(t *testing.T) { assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) + t.Run("PushWithLongCommitMessage", func(t *testing.T) { + p := pushTestMultilineCommitMessagePayload() + + pl, err := dc.Push(p) + require.NoError(t, err) + + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.Embeds[0].Title) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1", pl.Embeds[0].Description) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) + }) + t.Run("Issue", func(t *testing.T) { p := issueTestPayload() diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index fc6e7140e7be..d6a77c944256 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -64,9 +64,17 @@ func forkTestPayload() *api.ForkPayload { } func pushTestPayload() *api.PushPayload { + return pushTestPayloadWithCommitMessage("commit message") +} + +func pushTestMultilineCommitMessagePayload() *api.PushPayload { + return pushTestPayloadWithCommitMessage("This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好 ⚠️⚠️️\n\nThis is the message body.") +} + +func pushTestPayloadWithCommitMessage(message string) *api.PushPayload { commit := &api.PayloadCommit{ ID: "2020558fe2e34debb818a514715839cabd25e778", - Message: "commit message", + Message: message, URL: "http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778", Author: &api.PayloadUser{ Name: "user1", From e2f365b55cca7cf986a1f04e4d9e43d7e7aef7ad Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2024 12:14:57 +0800 Subject: [PATCH 3/7] Display head branch more comfortable on pull request view (#32000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR do some minor improvements for head branch display on pull request view UI. - [x] Remove the link if the head branch has been deleted with a tooltip, so that users will not result in a 404 page - [x] Display a label if this pull request is an agit based one. ![图片](https://github.com/user-attachments/assets/872f26b6-f1cf-4427-9e41-e3a5b176dfa4) --- models/issues/pull.go | 4 ++++ options/locale/locale_en-US.ini | 1 + routers/web/repo/pull.go | 14 +++++++++++++- templates/repo/issue/view_title.tmpl | 9 +++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/models/issues/pull.go b/models/issues/pull.go index 5fe95d56cc25..4a5782f836c8 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -268,6 +268,10 @@ func (pr *PullRequest) LoadAttributes(ctx context.Context) (err error) { return nil } +func (pr *PullRequest) IsAgitFlow() bool { + return pr.Flow == PullRequestFlowAGit +} + // LoadHeadRepo loads the head repository, pr.HeadRepo will remain nil if it does not exist // and thus ErrRepoNotExist will never be returned func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 957e73b171d3..f77fd203a2d6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1927,6 +1927,7 @@ pulls.delete.text = Do you really want to delete this pull request? (This will p pulls.recently_pushed_new_branches = You pushed on branch %[1]s %[2]s pull.deleted_branch = (deleted):%s +pull.agit_documentation = Review documentation about AGit comments.edit.already_changed = Unable to save changes to the comment. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 95299b44d5a2..ced0bbc15a00 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -164,7 +164,19 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) { ctx.Data["HeadTarget"] = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch } ctx.Data["BaseTarget"] = pull.BaseBranch - ctx.Data["HeadBranchLink"] = pull.GetHeadBranchLink(ctx) + headBranchLink := "" + if pull.Flow == issues_model.PullRequestFlowGithub { + b, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, pull.HeadBranch) + switch { + case err == nil: + if !b.IsDeleted { + headBranchLink = pull.GetHeadBranchLink(ctx) + } + case !git_model.IsErrBranchNotExist(err): + log.Error("GetBranch: %v", err) + } + } + ctx.Data["HeadBranchLink"] = headBranchLink ctx.Data["BaseBranchLink"] = pull.GetBaseBranchLink(ctx) } diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 1243681f3a21..7fa77b3f1cf3 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -50,9 +50,14 @@ {{if .Issue.IsPull}} {{$headHref := .HeadTarget}} {{if .HeadBranchLink}} - {{$headHref = HTMLFormat `%s` .HeadBranchLink $headHref}} + {{$headHref = HTMLFormat `%s ` .HeadBranchLink $headHref (ctx.Locale.Tr "copy_branch") .HeadTarget (svg "octicon-copy" 14)}} + {{else}} + {{if .Issue.PullRequest.IsAgitFlow}} + {{$headHref = HTMLFormat `%s AGit` $headHref "https://docs.gitea.com/usage/agit" (ctx.Locale.Tr "repo.pull.agit_documentation")}} + {{else}} + {{$headHref = HTMLFormat `%s` (ctx.Locale.Tr "form.target_branch_not_exist") $headHref}} + {{end}} {{end}} - {{$headHref = HTMLFormat `%s ` $headHref (ctx.Locale.Tr "copy_branch") .HeadTarget (svg "octicon-copy" 14)}} {{$baseHref := .BaseTarget}} {{if .BaseBranchLink}} {{$baseHref = HTMLFormat `%s` .BaseBranchLink $baseHref}} From 4947bec8360c152daca23e120eae1732d3848492 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2024 15:12:06 +0800 Subject: [PATCH 4/7] Include collaboration repositories on dashboard source/forks/mirrors list (#31946) Fix #13489 In the original implementation, only `All` will display your owned and collaborated repositories. For other filters like `Source`, `Mirrors` and etc. will only display your owned repositories. This PR removed the limitations. Now except `collbrations`, other filters will always display your owned and collaborated repositories. --- web_src/js/components/DashboardRepoList.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index afc58d36892f..ce165b1b3df3 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -78,7 +78,6 @@ const sfc = { searchURL() { return `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery }&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode - }${this.reposFilter !== 'all' ? '&exclusive=1' : '' }${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : '' }${this.privateFilter === 'private' ? '&is_private=true' : ''}${this.privateFilter === 'public' ? '&is_private=false' : '' }`; From 5a8568459d22e57cac506465463660526ca6a08f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2024 15:42:08 +0800 Subject: [PATCH 5/7] Fix bug when deleting a migrated branch (#32075) After migrating a repository with pull request, the branch is missed and after the pull request merged, the branch cannot be deleted. --- services/repository/branch.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/services/repository/branch.go b/services/repository/branch.go index 7fc99930776a..f5cdb72a7bb0 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -483,13 +483,12 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R } rawBranch, err := git_model.GetBranch(ctx, repo.ID, branchName) - if err != nil { + if err != nil && !git_model.IsErrBranchNotExist(err) { return fmt.Errorf("GetBranch: %vc", err) } - if rawBranch.IsDeleted { - return nil - } + // database branch record not exist or it's a deleted branch + notExist := git_model.IsErrBranchNotExist(err) || rawBranch.IsDeleted commit, err := gitRepo.GetBranchCommit(branchName) if err != nil { @@ -497,8 +496,10 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R } if err := db.WithTx(ctx, func(ctx context.Context) error { - if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil { - return err + if !notExist { + if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil { + return err + } } return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ From 6fa962f409c84477a7a4cf35b4a38a4a93fc3224 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 25 Sep 2024 03:34:08 +0900 Subject: [PATCH 6/7] Fix wrong status of `Set up Job` when first step is skipped (#32120) Fix #32089 --- modules/actions/task_state.go | 51 ++++++++++++++++++------------ modules/actions/task_state_test.go | 19 +++++++++++ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/modules/actions/task_state.go b/modules/actions/task_state.go index 31a74be3fd36..1f36e021a59a 100644 --- a/modules/actions/task_state.go +++ b/modules/actions/task_state.go @@ -18,8 +18,32 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { return fullStepsOfEmptySteps(task) } - firstStep := task.Steps[0] + // firstStep is the first step that has run or running, not include preStep. + // For example, + // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): firstStep is step1. + // 2. preStep(Success) -> step1(Skipped) -> step2(Success) -> postStep(Success): firstStep is step2. + // 3. preStep(Success) -> step1(Running) -> step2(Waiting) -> postStep(Waiting): firstStep is step1. + // 4. preStep(Success) -> step1(Skipped) -> step2(Skipped) -> postStep(Skipped): firstStep is nil. + // 5. preStep(Success) -> step1(Cancelled) -> step2(Cancelled) -> postStep(Cancelled): firstStep is nil. + var firstStep *actions_model.ActionTaskStep + // lastHasRunStep is the last step that has run. + // For example, + // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1. + // 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3. + // 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2. + // So its Stopped is the Started of postStep when there are no more steps to run. + var lastHasRunStep *actions_model.ActionTaskStep + var logIndex int64 + for _, step := range task.Steps { + if firstStep == nil && (step.Status.HasRun() || step.Status.IsRunning()) { + firstStep = step + } + if step.Status.HasRun() { + lastHasRunStep = step + } + logIndex += step.LogLength + } preStep := &actions_model.ActionTaskStep{ Name: preStepName, @@ -28,32 +52,17 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { Status: actions_model.StatusRunning, } - if firstStep.Status.HasRun() || firstStep.Status.IsRunning() { + // No step has run or is running, so preStep is equal to the task + if firstStep == nil { + preStep.Stopped = task.Stopped + preStep.Status = task.Status + } else { preStep.LogLength = firstStep.LogIndex preStep.Stopped = firstStep.Started preStep.Status = actions_model.StatusSuccess - } else if task.Status.IsDone() { - preStep.Stopped = task.Stopped - preStep.Status = actions_model.StatusFailure - if task.Status.IsSkipped() { - preStep.Status = actions_model.StatusSkipped - } } logIndex += preStep.LogLength - // lastHasRunStep is the last step that has run. - // For example, - // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1. - // 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3. - // 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2. - // So its Stopped is the Started of postStep when there are no more steps to run. - var lastHasRunStep *actions_model.ActionTaskStep - for _, step := range task.Steps { - if step.Status.HasRun() { - lastHasRunStep = step - } - logIndex += step.LogLength - } if lastHasRunStep == nil { lastHasRunStep = preStep } diff --git a/modules/actions/task_state_test.go b/modules/actions/task_state_test.go index 28213d781bef..ff0fd5719511 100644 --- a/modules/actions/task_state_test.go +++ b/modules/actions/task_state_test.go @@ -137,6 +137,25 @@ func TestFullSteps(t *testing.T) { {Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, }, }, + { + name: "first step is skipped", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + }, + Status: actions_model.StatusSuccess, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 3269b04d61ffe6a7ce462cd05ee150e4491124e8 Mon Sep 17 00:00:00 2001 From: charles <30816317+charles7668@users.noreply.github.com> Date: Wed, 25 Sep 2024 03:06:52 +0800 Subject: [PATCH 7/7] Notify the user when the file path contains leading or trailing spaces and fix the error message for invalid file names. (#31507) close #31478 --- routers/web/repo/editor.go | 2 +- web_src/js/features/repo-editor.ts | 49 +++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 4ff86b5a6647..9396115b0d24 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -317,7 +317,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b case git.EntryModeBlob: ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplEditFile, &form) default: - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", fileErr.Path), tplEditFile, &form) } } else { ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/web_src/js/features/repo-editor.ts b/web_src/js/features/repo-editor.ts index dadbd802bc7f..e4d179d3aeab 100644 --- a/web_src/js/features/repo-editor.ts +++ b/web_src/js/features/repo-editor.ts @@ -75,23 +75,62 @@ export function initRepoEditor() { } filenameInput.addEventListener('input', function () { const parts = filenameInput.value.split('/'); + const links = Array.from(document.querySelectorAll('.breadcrumb span.section')); + const dividers = Array.from(document.querySelectorAll('.breadcrumb .breadcrumb-divider')); + let warningDiv = document.querySelector('.ui.warning.message.flash-message.flash-warning.space-related'); + let containSpace = false; if (parts.length > 1) { for (let i = 0; i < parts.length; ++i) { const value = parts[i]; + const trimValue = value.trim(); + if (trimValue === '..') { + // remove previous tree path + if (links.length > 0) { + const link = links.pop(); + const divider = dividers.pop(); + link.remove(); + divider.remove(); + } + continue; + } if (i < parts.length - 1) { - if (value.length) { - filenameInput.before(createElementFromHTML( + if (trimValue.length) { + const linkElement = createElementFromHTML( `${htmlEscape(value)}`, - )); - filenameInput.before(createElementFromHTML( + ); + const dividerElement = createElementFromHTML( ``, - )); + ); + links.push(linkElement); + dividers.push(dividerElement); + filenameInput.before(linkElement); + filenameInput.before(dividerElement); } } else { filenameInput.value = value; } this.setSelectionRange(0, 0); + containSpace |= (trimValue !== value && trimValue !== ''); + } + } + containSpace |= Array.from(links).some((link) => { + const value = link.querySelector('a').textContent; + return value.trim() !== value; + }); + containSpace |= parts[parts.length - 1].trim() !== parts[parts.length - 1]; + if (containSpace) { + if (!warningDiv) { + warningDiv = document.createElement('div'); + warningDiv.classList.add('ui', 'warning', 'message', 'flash-message', 'flash-warning', 'space-related'); + warningDiv.innerHTML = '

File path contains leading or trailing whitespace.

'; + // Add display 'block' because display is set to 'none' in formantic\build\semantic.css + warningDiv.style.display = 'block'; + const inputContainer = document.querySelector('.repo-editor-header'); + inputContainer.insertAdjacentElement('beforebegin', warningDiv); } + showElem(warningDiv); + } else if (warningDiv) { + hideElem(warningDiv); } joinTreePath(); });