From fddb66da3ae01779577074717dc061b7cbf1fab9 Mon Sep 17 00:00:00 2001 From: bytedream <bytedream@protonmail.com> Date: Sat, 29 Mar 2025 11:34:54 +0100 Subject: [PATCH 1/7] Add badge flat-square style --- modules/badge/badge.go | 9 +++++++++ routers/web/repo/actions/badge.go | 17 ++++++++++++++--- templates/devtest/badge-actions-svg.tmpl | 13 ++++++++++--- .../actions/runner_badge_flat-square.tmpl | 15 +++++++++++++++ ...runner_badge.tmpl => runner_badge_flat.tmpl} | 0 5 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 templates/shared/actions/runner_badge_flat-square.tmpl rename templates/shared/actions/{runner_badge.tmpl => runner_badge_flat.tmpl} (100%) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index fdf9866f607a5..d2fd2fdec63c8 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -49,11 +49,20 @@ func (b Badge) Width() int { return b.Label.width + b.Message.width } +// Style represents the style of a badge +type Style string + +const ( + StyleFlat = "flat" + StyleFlatSquare = "flat-square" +) + const ( defaultOffset = 10 defaultFontSize = 11 DefaultColor = "#9f9f9f" // Grey DefaultFontFamily = "DejaVu Sans,Verdana,Geneva,sans-serif" + DefaultStyle = StyleFlat ) var StatusColorMap = map[actions_model.Status]string{ diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index e920ecaf58063..1dd57a0b015d7 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "path/filepath" + "slices" "strings" actions_model "code.gitea.io/gitea/models/actions" @@ -25,15 +26,25 @@ func GetWorkflowBadge(ctx *context.Context) { branchRef := fmt.Sprintf("refs/heads/%s", branch) event := ctx.Req.URL.Query().Get("event") - badge, err := getWorkflowBadge(ctx, workflowFile, branchRef, event) + style := ctx.Req.URL.Query().Get("style") + if !slices.Contains([]badge.Style{badge.StyleFlat, badge.StyleFlatSquare}, badge.Style(style)) { + style = badge.DefaultStyle + } + + b, err := getWorkflowBadge(ctx, workflowFile, branchRef, event) if err != nil { ctx.ServerError("GetWorkflowBadge", err) return } - ctx.Data["Badge"] = badge + ctx.Data["Badge"] = b ctx.RespHeader().Set("Content-Type", "image/svg+xml") - ctx.HTML(http.StatusOK, "shared/actions/runner_badge") + switch ctx.Req.URL.Query().Get("style") { + case badge.StyleFlat: + ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat") + case badge.StyleFlatSquare: + ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat-square") + } } func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (badge.Badge, error) { diff --git a/templates/devtest/badge-actions-svg.tmpl b/templates/devtest/badge-actions-svg.tmpl index 8125793bb3fa4..5be4fb313128d 100644 --- a/templates/devtest/badge-actions-svg.tmpl +++ b/templates/devtest/badge-actions-svg.tmpl @@ -3,9 +3,16 @@ <div> <h1>Actions SVG</h1> <form class="tw-my-3"> - {{range $fontName := .BadgeFontFamilyNames}} - <label><input name="font" type="radio" value="{{$fontName}}" {{Iif (eq $.SelectedFontFamilyName $fontName) "checked"}}>{{$fontName}}</label> - {{end}} + <div class="tw-mb-2"> + {{range $fontName := .BadgeFontFamilyNames}} + <label><input name="font" type="radio" value="{{$fontName}}" {{Iif (eq $.SelectedFontFamilyName $fontName) "checked"}}>{{$fontName}}</label> + {{end}} + </div> + <div class="tw-mb-2"> + {{range $style := .BadgeStyles}} + <label><input name="style" type="radio" value="{{$style}}" {{Iif (eq $.SelectedStyle $style) "checked"}}>{{$style}}</label> + {{end}} + </div> <button>submit</button> </form> <div class="flex-text-block tw-flex-wrap"> diff --git a/templates/shared/actions/runner_badge_flat-square.tmpl b/templates/shared/actions/runner_badge_flat-square.tmpl new file mode 100644 index 0000000000000..cc1dc1e8f3a90 --- /dev/null +++ b/templates/shared/actions/runner_badge_flat-square.tmpl @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{.Badge.Width}}" height="20" + role="img" aria-label="{{.Badge.Label.Text}}: {{.Badge.Message.Text}}"> + <title>{{.Badge.Label.Text}}: {{.Badge.Message.Text}}</title> + <g shape-rendering="crispEdges"> + <rect width="{{.Badge.Label.Width}}" height="20" fill="#555" /> + <rect x="{{.Badge.Label.Width}}" width="{{.Badge.Message.Width}}" height="20" fill="{{.Badge.Color}}" /> + </g> + <g fill="#fff" text-anchor="middle" font-family="{{.Badge.FontFamily}}" + text-rendering="geometricPrecision" font-size="{{.Badge.FontSize}}"> + <text x="{{.Badge.Label.X}}" y="140" + transform="scale(.1)" fill="#fff" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text> + <text x="{{.Badge.Message.X}}" y="140" transform="scale(.1)" fill="#fff" + textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text> + </g> +</svg> diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge_flat.tmpl similarity index 100% rename from templates/shared/actions/runner_badge.tmpl rename to templates/shared/actions/runner_badge_flat.tmpl From 8a210af901dc9d64197217e46a0c900c81432679 Mon Sep 17 00:00:00 2001 From: bytedream <bytedream@protonmail.com> Date: Sat, 29 Mar 2025 11:35:06 +0100 Subject: [PATCH 2/7] Add devtest for new style --- routers/web/devtest/devtest.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index 063ff42409fa9..6c01b37bd7717 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -4,6 +4,7 @@ package devtest import ( + "fmt" "html/template" "net/http" "path" @@ -127,7 +128,9 @@ func prepareMockDataBadgeCommitSign(ctx *context.Context) { func prepareMockDataBadgeActionsSvg(ctx *context.Context) { fontFamilyNames := strings.Split(badge.DefaultFontFamily, ",") + styles := []badge.Style{badge.StyleFlat, badge.StyleFlatSquare} selectedFontFamilyName := ctx.FormString("font", fontFamilyNames[0]) + selectedStyle := ctx.FormString("style", badge.DefaultStyle) var badges []badge.Badge badges = append(badges, badge.GenerateBadge("啊啊啊啊啊啊啊啊啊啊啊啊", "🌞🌞🌞🌞🌞", "green")) for r := rune(0); r < 256; r++ { @@ -141,7 +144,16 @@ func prepareMockDataBadgeActionsSvg(ctx *context.Context) { for i, b := range badges { b.IDPrefix = "devtest-" + strconv.FormatInt(int64(i), 10) + "-" b.FontFamily = selectedFontFamilyName - h, err := ctx.RenderToHTML("shared/actions/runner_badge", map[string]any{"Badge": b}) + var h template.HTML + var err error + switch selectedStyle { + case badge.StyleFlat: + h, err = ctx.RenderToHTML("shared/actions/runner_badge_flat", map[string]any{"Badge": b}) + case badge.StyleFlatSquare: + h, err = ctx.RenderToHTML("shared/actions/runner_badge_flat-square", map[string]any{"Badge": b}) + default: + err = fmt.Errorf("unknown badge style: %s", selectedStyle) + } if err != nil { ctx.ServerError("RenderToHTML", err) return @@ -151,6 +163,8 @@ func prepareMockDataBadgeActionsSvg(ctx *context.Context) { ctx.Data["BadgeSVGs"] = badgeSVGs ctx.Data["BadgeFontFamilyNames"] = fontFamilyNames ctx.Data["SelectedFontFamilyName"] = selectedFontFamilyName + ctx.Data["BadgeStyles"] = styles + ctx.Data["SelectedStyle"] = selectedStyle } func prepareMockData(ctx *context.Context) { From ad7e126966784c0cf596420f97ae5f78021109ce Mon Sep 17 00:00:00 2001 From: bytedream <bytedream@protonmail.com> Date: Sat, 29 Mar 2025 11:45:39 +0100 Subject: [PATCH 3/7] Fix endpoint style switch --- routers/web/repo/actions/badge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 1dd57a0b015d7..9fded1d0c13b5 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -39,7 +39,7 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.Data["Badge"] = b ctx.RespHeader().Set("Content-Type", "image/svg+xml") - switch ctx.Req.URL.Query().Get("style") { + switch style { case badge.StyleFlat: ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat") case badge.StyleFlatSquare: From 1d846b9a8f3bc3c8588cd3c968c4fe2cfa84e64a Mon Sep 17 00:00:00 2001 From: bytedream <bytedream@protonmail.com> Date: Mon, 31 Mar 2025 15:10:50 +0200 Subject: [PATCH 4/7] Add global slice with all styles --- modules/badge/badge.go | 24 ++++++++++++++---------- routers/web/devtest/devtest.go | 3 +-- routers/web/repo/actions/badge.go | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index d2fd2fdec63c8..e3a39b0efd74f 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -65,16 +65,20 @@ const ( DefaultStyle = StyleFlat ) -var StatusColorMap = map[actions_model.Status]string{ - actions_model.StatusSuccess: "#4c1", // Green - actions_model.StatusSkipped: "#dfb317", // Yellow - actions_model.StatusUnknown: "#97ca00", // Light Green - actions_model.StatusFailure: "#e05d44", // Red - actions_model.StatusCancelled: "#fe7d37", // Orange - actions_model.StatusWaiting: "#dfb317", // Yellow - actions_model.StatusRunning: "#dfb317", // Yellow - actions_model.StatusBlocked: "#dfb317", // Yellow -} +var ( + StatusColorMap = map[actions_model.Status]string{ + actions_model.StatusSuccess: "#4c1", // Green + actions_model.StatusSkipped: "#dfb317", // Yellow + actions_model.StatusUnknown: "#97ca00", // Light Green + actions_model.StatusFailure: "#e05d44", // Red + actions_model.StatusCancelled: "#fe7d37", // Orange + actions_model.StatusWaiting: "#dfb317", // Yellow + actions_model.StatusRunning: "#dfb317", // Yellow + actions_model.StatusBlocked: "#dfb317", // Yellow + } + + AllStyles = []Style{StyleFlat, StyleFlatSquare} +) // GenerateBadge generates badge with given template func GenerateBadge(label, message, color string) Badge { diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index 6c01b37bd7717..f1a1314f6619c 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -128,7 +128,6 @@ func prepareMockDataBadgeCommitSign(ctx *context.Context) { func prepareMockDataBadgeActionsSvg(ctx *context.Context) { fontFamilyNames := strings.Split(badge.DefaultFontFamily, ",") - styles := []badge.Style{badge.StyleFlat, badge.StyleFlatSquare} selectedFontFamilyName := ctx.FormString("font", fontFamilyNames[0]) selectedStyle := ctx.FormString("style", badge.DefaultStyle) var badges []badge.Badge @@ -163,7 +162,7 @@ func prepareMockDataBadgeActionsSvg(ctx *context.Context) { ctx.Data["BadgeSVGs"] = badgeSVGs ctx.Data["BadgeFontFamilyNames"] = fontFamilyNames ctx.Data["SelectedFontFamilyName"] = selectedFontFamilyName - ctx.Data["BadgeStyles"] = styles + ctx.Data["BadgeStyles"] = badge.AllStyles ctx.Data["SelectedStyle"] = selectedStyle } diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 9fded1d0c13b5..4984bd45a17d8 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -27,7 +27,7 @@ func GetWorkflowBadge(ctx *context.Context) { event := ctx.Req.URL.Query().Get("event") style := ctx.Req.URL.Query().Get("style") - if !slices.Contains([]badge.Style{badge.StyleFlat, badge.StyleFlatSquare}, badge.Style(style)) { + if !slices.Contains(badge.AllStyles, badge.Style(style)) { style = badge.DefaultStyle } From ad0f9e2ad8584fdc6af6975df8507e48aef08447 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 1 Apr 2025 09:58:47 +0800 Subject: [PATCH 5/7] optimize, lazy init, default style fallback --- modules/badge/badge.go | 22 +++++++++++++--------- modules/badge/badge_glyph_width.go | 6 ++---- routers/web/devtest/devtest.go | 2 +- routers/web/repo/actions/badge.go | 26 +++++++++----------------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index e3a39b0efd74f..a1bd768805df5 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -5,6 +5,7 @@ package badge import ( "strings" + "sync" "unicode" actions_model "code.gitea.io/gitea/models/actions" @@ -49,9 +50,6 @@ func (b Badge) Width() int { return b.Label.width + b.Message.width } -// Style represents the style of a badge -type Style string - const ( StyleFlat = "flat" StyleFlatSquare = "flat-square" @@ -65,8 +63,13 @@ const ( DefaultStyle = StyleFlat ) -var ( - StatusColorMap = map[actions_model.Status]string{ +var GlobalVars = sync.OnceValue(func() (ret struct { + StatusColorMap map[actions_model.Status]string + DejaVuGlyphWidthData map[rune]uint8 + AllStyles []string +}, +) { + ret.StatusColorMap = map[actions_model.Status]string{ actions_model.StatusSuccess: "#4c1", // Green actions_model.StatusSkipped: "#dfb317", // Yellow actions_model.StatusUnknown: "#97ca00", // Light Green @@ -76,9 +79,10 @@ var ( actions_model.StatusRunning: "#dfb317", // Yellow actions_model.StatusBlocked: "#dfb317", // Yellow } - - AllStyles = []Style{StyleFlat, StyleFlatSquare} -) + ret.DejaVuGlyphWidthData = dejaVuGlyphWidthDataFunc() + ret.AllStyles = []string{StyleFlat, StyleFlatSquare} + return ret +}) // GenerateBadge generates badge with given template func GenerateBadge(label, message, color string) Badge { @@ -106,7 +110,7 @@ func GenerateBadge(label, message, color string) Badge { func calculateTextWidth(text string) int { width := 0 - widthData := DejaVuGlyphWidthData() + widthData := GlobalVars().DejaVuGlyphWidthData for _, char := range strings.TrimSpace(text) { charWidth, ok := widthData[char] if !ok { diff --git a/modules/badge/badge_glyph_width.go b/modules/badge/badge_glyph_width.go index e8e43ec9cb950..0d950c5a70e7f 100644 --- a/modules/badge/badge_glyph_width.go +++ b/modules/badge/badge_glyph_width.go @@ -3,8 +3,6 @@ package badge -import "sync" - // DejaVuGlyphWidthData is generated by `sfnt.Face.GlyphAdvance(nil, <rune>, 11, font.HintingNone)` with DejaVu Sans // v2.37 (https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-sans-ttf-2.37.zip). // @@ -13,7 +11,7 @@ import "sync" // // A devtest page "/devtest/badge-actions-svg" could be used to check the rendered images. -var DejaVuGlyphWidthData = sync.OnceValue(func() map[rune]uint8 { +func dejaVuGlyphWidthDataFunc() map[rune]uint8 { return map[rune]uint8{ 32: 3, 33: 4, @@ -205,4 +203,4 @@ var DejaVuGlyphWidthData = sync.OnceValue(func() map[rune]uint8 { 254: 7, 255: 7, } -}) +} diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index f1a1314f6619c..995f9694264d1 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -162,7 +162,7 @@ func prepareMockDataBadgeActionsSvg(ctx *context.Context) { ctx.Data["BadgeSVGs"] = badgeSVGs ctx.Data["BadgeFontFamilyNames"] = fontFamilyNames ctx.Data["SelectedFontFamilyName"] = selectedFontFamilyName - ctx.Data["BadgeStyles"] = badge.AllStyles + ctx.Data["BadgeStyles"] = badge.GlobalVars().AllStyles ctx.Data["SelectedStyle"] = selectedStyle } diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 4984bd45a17d8..d268a8df8af37 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -5,33 +5,25 @@ package actions import ( "errors" - "fmt" "net/http" "path/filepath" - "slices" "strings" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/modules/badge" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) func GetWorkflowBadge(ctx *context.Context) { workflowFile := ctx.PathParam("workflow_name") - branch := ctx.Req.URL.Query().Get("branch") - if branch == "" { - branch = ctx.Repo.Repository.DefaultBranch - } - branchRef := fmt.Sprintf("refs/heads/%s", branch) - event := ctx.Req.URL.Query().Get("event") - - style := ctx.Req.URL.Query().Get("style") - if !slices.Contains(badge.AllStyles, badge.Style(style)) { - style = badge.DefaultStyle - } + branch := ctx.FormString("branch", ctx.Repo.Repository.DefaultBranch) + event := ctx.FormString("event") + style := ctx.FormString("style") - b, err := getWorkflowBadge(ctx, workflowFile, branchRef, event) + branchRef := git.RefNameFromBranch(branch) + b, err := getWorkflowBadge(ctx, workflowFile, branchRef.String(), event) if err != nil { ctx.ServerError("GetWorkflowBadge", err) return @@ -40,10 +32,10 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.Data["Badge"] = b ctx.RespHeader().Set("Content-Type", "image/svg+xml") switch style { - case badge.StyleFlat: - ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat") case badge.StyleFlatSquare: ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat-square") + default: // defaults to badge.StyleFlat + ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat") } } @@ -59,7 +51,7 @@ func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event stri return badge.Badge{}, err } - color, ok := badge.StatusColorMap[run.Status] + color, ok := badge.GlobalVars().StatusColorMap[run.Status] if !ok { return badge.GenerateBadge(workflowName, "unknown status", badge.DefaultColor), nil } From 3da42bcc6824ae67c0ffb27470a6970af0f18e51 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 1 Apr 2025 17:14:14 +0800 Subject: [PATCH 6/7] add comment for style --- modules/badge/badge.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index a1bd768805df5..e27efa2e1399b 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -50,6 +50,7 @@ func (b Badge) Width() int { return b.Label.width + b.Message.width } +// styles follows https://shields.io/badges const ( StyleFlat = "flat" StyleFlatSquare = "flat-square" From ed20f51351abf0b441e92270abd8acff6f71f47f Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 1 Apr 2025 17:16:01 +0800 Subject: [PATCH 7/7] Update modules/badge/badge.go --- modules/badge/badge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index e27efa2e1399b..d2e9bd9d1bcc6 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -50,7 +50,7 @@ func (b Badge) Width() int { return b.Label.width + b.Message.width } -// styles follows https://shields.io/badges +// Style follows https://shields.io/badges const ( StyleFlat = "flat" StyleFlatSquare = "flat-square"