diff --git a/modules/badge/badge.go b/modules/badge/badge.go index fdf9866f60..d2e9bd9d1b 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,23 +50,40 @@ func (b Badge) Width() int { return b.Label.width + b.Message.width } +// Style follows https://shields.io/badges +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{ - 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 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 + 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 + } + ret.DejaVuGlyphWidthData = dejaVuGlyphWidthDataFunc() + ret.AllStyles = []string{StyleFlat, StyleFlatSquare} + return ret +}) // GenerateBadge generates badge with given template func GenerateBadge(label, message, color string) Badge { @@ -93,7 +111,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 e8e43ec9cb..0d950c5a70 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, , 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 063ff42409..995f969426 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" @@ -128,6 +129,7 @@ func prepareMockDataBadgeCommitSign(ctx *context.Context) { func prepareMockDataBadgeActionsSvg(ctx *context.Context) { fontFamilyNames := strings.Split(badge.DefaultFontFamily, ",") 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 +143,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 +162,8 @@ func prepareMockDataBadgeActionsSvg(ctx *context.Context) { ctx.Data["BadgeSVGs"] = badgeSVGs ctx.Data["BadgeFontFamilyNames"] = fontFamilyNames ctx.Data["SelectedFontFamilyName"] = selectedFontFamilyName + ctx.Data["BadgeStyles"] = badge.GlobalVars().AllStyles + ctx.Data["SelectedStyle"] = selectedStyle } func prepareMockData(ctx *context.Context) { diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index e920ecaf58..d268a8df8a 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -5,35 +5,38 @@ package actions import ( "errors" - "fmt" "net/http" "path/filepath" "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") + branch := ctx.FormString("branch", ctx.Repo.Repository.DefaultBranch) + event := ctx.FormString("event") + style := ctx.FormString("style") - badge, 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 } - 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 style { + 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") + } } func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (badge.Badge, error) { @@ -48,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 } diff --git a/templates/devtest/badge-actions-svg.tmpl b/templates/devtest/badge-actions-svg.tmpl index 8125793bb3..5be4fb3131 100644 --- a/templates/devtest/badge-actions-svg.tmpl +++ b/templates/devtest/badge-actions-svg.tmpl @@ -3,9 +3,16 @@

Actions SVG

- {{range $fontName := .BadgeFontFamilyNames}} - - {{end}} +
+ {{range $fontName := .BadgeFontFamilyNames}} + + {{end}} +
+
+ {{range $style := .BadgeStyles}} + + {{end}} +
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 0000000000..cc1dc1e8f3 --- /dev/null +++ b/templates/shared/actions/runner_badge_flat-square.tmpl @@ -0,0 +1,15 @@ + + {{.Badge.Label.Text}}: {{.Badge.Message.Text}} + + + + + + {{.Badge.Label.Text}} + {{.Badge.Message.Text}} + + 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