From 5b9251150cd2f7201115a718b006ef9458b0e09a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jun 2026 03:53:01 -0700 Subject: [PATCH] fix(actions): address workflow status badge review feedback (#38241) Follow https://github.com/go-gitea/gitea/pull/38196#discussion_r3487219492 --------- Co-authored-by: wxiaoguang Co-authored-by: bircni --- options/locale/locale_en-US.json | 2 - routers/web/repo/actions/actions.go | 27 ++---- routers/web/repo/actions/actions_test.go | 11 +-- templates/repo/actions/list.tmpl | 102 +++++++++++++---------- web_src/js/features/repo-actions.test.ts | 3 +- web_src/js/features/repo-actions.ts | 41 +++++---- web_src/js/index.ts | 4 +- 7 files changed, 88 insertions(+), 102 deletions(-) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 737b4e7d3ce..9373aa83a90 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -3863,8 +3863,6 @@ "actions.workflow.create_status_badge": "Create status badge", "actions.workflow.status_badge": "Status Badge", "actions.workflow.status_badge_url": "Badge URL", - "actions.workflow.status_badge_markdown": "Markdown", - "actions.workflow.status_badge_html": "HTML", "actions.workflow.not_found": "Workflow '%s' not found.", "actions.workflow.run_success": "Workflow '%s' run successfully.", "actions.workflow.from_ref": "Use workflow from", diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 80b85256220..1bdcffab789 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -8,7 +8,6 @@ import ( stdCtx "context" "errors" "fmt" - "html" "net/http" "net/url" "slices" @@ -49,12 +48,9 @@ type WorkflowInfo struct { } type workflowBadge struct { - URL string - WorkflowURL string - Markdown string - MarkdownAltText string - HTML string - HTMLAltText string + DisplayName string + BadgeURL string + WorkflowURL string } // DisplayName returns the workflow name from the YAML file if present, otherwise the filename. @@ -616,28 +612,19 @@ func prepareWorkflowBadgeTemplate(ctx *context.Context, workflowID, displayName if workflowID == "" { return } - if displayName == "" { - displayName = workflowID - } + displayName = util.IfZero(displayName, workflowID) repoURL := ctx.Repo.Repository.HTMLURL(ctx) badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", repoURL, util.PathEscapeSegments(workflowID), url.QueryEscape(ctx.Repo.Repository.DefaultBranch)) workflowURL := fmt.Sprintf("%s/actions?workflow=%s", repoURL, url.QueryEscape(workflowID)) ctx.Data["WorkflowBadge"] = workflowBadge{ - URL: badgeURL, - WorkflowURL: workflowURL, - Markdown: fmt.Sprintf("[![%s](%s)](%s)", escapeMarkdownImageAltText(displayName), badgeURL, workflowURL), - MarkdownAltText: escapeMarkdownImageAltText(displayName), - HTML: fmt.Sprintf(`%s`, html.EscapeString(workflowURL), html.EscapeString(badgeURL), html.EscapeString(displayName)), - HTMLAltText: displayName, + BadgeURL: badgeURL, + WorkflowURL: workflowURL, + DisplayName: displayName, } } -func escapeMarkdownImageAltText(s string) string { - return strings.NewReplacer(`\`, `\\`, `[`, `\[`, `]`, `\]`).Replace(s) -} - // loadIsRefDeleted loads the IsRefDeleted field for each run in the list. // TODO: move this function to models/actions/run_list.go but now it will result in a circular import. func loadIsRefDeleted(ctx stdCtx.Context, repoID int64, runs actions_model.RunList) error { diff --git a/routers/web/repo/actions/actions_test.go b/routers/web/repo/actions/actions_test.go index 5997baae5c0..77753621685 100644 --- a/routers/web/repo/actions/actions_test.go +++ b/routers/web/repo/actions/actions_test.go @@ -184,10 +184,7 @@ func Test_loadIsRefDeleted(t *testing.T) { } func TestPrepareWorkflowBadgeTemplate(t *testing.T) { - defer test.MockVariableValue(&setting.IsInTesting, true)() defer test.MockVariableValue(&setting.AppURL, "https://gitea.example.com/")() - defer test.MockVariableValue(&setting.AppSubURL, "")() - defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLNever)() t.Run("no workflow selected", func(t *testing.T) { ctx := newWorkflowBadgeTestContext(t) @@ -203,13 +200,9 @@ func TestPrepareWorkflowBadgeTemplate(t *testing.T) { prepareWorkflowBadgeTemplate(ctx, "build/test workflow.yml", `CI [prod]\build "fast" `) assert.Equal(t, workflowBadge{ - URL: "https://gitea.example.com/user1/repo1/actions/workflows/build/test%20workflow.yml/badge.svg?branch=release%2F1.0+%26+hotfix", + BadgeURL: "https://gitea.example.com/user1/repo1/actions/workflows/build/test%20workflow.yml/badge.svg?branch=release%2F1.0+%26+hotfix", WorkflowURL: "https://gitea.example.com/user1/repo1/actions?workflow=build%2Ftest+workflow.yml", - Markdown: `[![CI \[prod\]\\build "fast" ](https://gitea.example.com/user1/repo1/actions/workflows/build/test%20workflow.yml/badge.svg?branch=release%2F1.0+%26+hotfix)]` + - `(https://gitea.example.com/user1/repo1/actions?workflow=build%2Ftest+workflow.yml)`, - MarkdownAltText: `CI \[prod\]\\build "fast" `, - HTML: `CI [prod]\build "fast" <ok>`, - HTMLAltText: `CI [prod]\build "fast" `, + DisplayName: `CI [prod]\build "fast" `, }, ctx.Data["WorkflowBadge"]) }) } diff --git a/templates/repo/actions/list.tmpl b/templates/repo/actions/list.tmpl index eb87abe747e..cb1b1c7fb5e 100644 --- a/templates/repo/actions/list.tmpl +++ b/templates/repo/actions/list.tmpl @@ -1,4 +1,8 @@ {{template "base/head" .}} + +{{$showCreateWorkflowBadge := Iif .WorkflowBadge true false}} +{{$showEnableDisableWorkflow := and .AllowDisableOrEnableWorkflow .CurWorkflowIsListed $.CurWorkflow}} +
{{template "repo/header" .}}
@@ -124,16 +128,16 @@
- {{if or .WorkflowBadge (and .AllowDisableOrEnableWorkflow .CurWorkflowIsListed $.CurWorkflow)}} + {{if or $showCreateWorkflowBadge $showEnableDisableWorkflow}} - - -
- - -
-
- - -
- - - - {{end}} {{else}} @@ -201,4 +162,53 @@ {{end}} + +{{if $showCreateWorkflowBadge}} + +{{end}} + {{template "base/footer" .}} diff --git a/web_src/js/features/repo-actions.test.ts b/web_src/js/features/repo-actions.test.ts index bcb2063fb7a..38873241a66 100644 --- a/web_src/js/features/repo-actions.test.ts +++ b/web_src/js/features/repo-actions.test.ts @@ -5,8 +5,7 @@ test('updateWorkflowBadgeFields updates badge snippets for selected branch', ()
diff --git a/web_src/js/features/repo-actions.ts b/web_src/js/features/repo-actions.ts index 8a204c35322..89ed94e0788 100644 --- a/web_src/js/features/repo-actions.ts +++ b/web_src/js/features/repo-actions.ts @@ -1,36 +1,35 @@ import {createApp} from 'vue'; import RepoActionView from '../components/RepoActionView.vue'; -import {queryElems} from '../utils/dom.ts'; - -function escapeHTMLAttribute(value: string): string { - return value.replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>'); -} +import {registerGlobalInitFunc} from '../modules/observer.ts'; +import {html} from '../utils/html.ts'; export function updateWorkflowBadgeFields(form: HTMLElement, branch: string): void { - const badgeURL = new URL(form.getAttribute('data-badge-url')!); - badgeURL.searchParams.set('branch', branch); + const badgeURLParsed = new URL(form.getAttribute('data-badge-url')!); + badgeURLParsed.searchParams.set('branch', branch); - const badgeURLString = badgeURL.href; + const badgeURL = badgeURLParsed.href; const workflowURL = form.getAttribute('data-workflow-url')!; - const markdownAltText = form.getAttribute('data-markdown-alt-text')!; - const htmlAltText = form.getAttribute('data-html-alt-text')!; + const displayName = form.getAttribute('data-workflow-display-name')!; + const markdownAltText = displayName.replaceAll(/[\\[\]]/g, (c) => `\\${c}`); - form.querySelector('[data-workflow-badge-image]')!.src = badgeURLString; - form.querySelector('#workflow-badge-url')!.value = badgeURLString; - form.querySelector('#workflow-badge-markdown')!.value = `[![${markdownAltText}](${badgeURLString})](${workflowURL})`; - form.querySelector('#workflow-badge-html')!.value = `${escapeHTMLAttribute(htmlAltText)}`; + form.querySelector('[data-workflow-badge-image]')!.src = badgeURL; + form.querySelector('#workflow-badge-url')!.value = badgeURL; + form.querySelector('#workflow-badge-markdown')!.value = `[![${markdownAltText}](${badgeURL})](${workflowURL})`; + form.querySelector('#workflow-badge-html')!.value = html`${displayName}`; } -function initWorkflowBadgeBranchSelection(): void { - queryElems(document, '[data-workflow-badge-form]', (form) => { - const branchInput = form.querySelector('[data-workflow-badge-branch]')!; - branchInput.addEventListener('change', () => updateWorkflowBadgeFields(form, branchInput.value)); - }); +function initWorkflowBadgeForm(form: HTMLElement): void { + const branchInput = form.querySelector('[data-workflow-badge-branch]')!; + branchInput.addEventListener('change', () => updateWorkflowBadgeFields(form, branchInput.value)); + updateWorkflowBadgeFields(form, branchInput.value); } -export function initRepositoryActionView() { - initWorkflowBadgeBranchSelection(); +export function initRepositoryActions() { + registerGlobalInitFunc('initWorkflowBadgeForm', initWorkflowBadgeForm); + initRepositoryActionsView(); +} +function initRepositoryActionsView() { const el = document.querySelector('#repo-action-view'); if (!el) return; diff --git a/web_src/js/index.ts b/web_src/js/index.ts index 0ca1deb0d3a..3289238b0d6 100644 --- a/web_src/js/index.ts +++ b/web_src/js/index.ts @@ -42,7 +42,7 @@ import {initCommonOrganization} from './features/common-organization.ts'; import {initRepoWikiForm} from './features/repo-wiki.ts'; import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts'; import {initCaptcha} from './features/captcha.ts'; -import {initRepositoryActionView} from './features/repo-actions.ts'; +import {initRepositoryActions} from './features/repo-actions.ts'; import {initGlobalTooltips} from './modules/tippy.ts'; import {initGiteaFomantic} from './modules/fomantic.ts'; import {initRepoIssueList} from './features/repo-issue-list.ts'; @@ -138,7 +138,7 @@ const initPerformanceTracer = callInitFunctions([ initRepoViewFileTree, initRepoWikiForm, initRepository, - initRepositoryActionView, + initRepositoryActions, initRepositorySearch, initRepoContributors, initRepoCodeFrequency,