From f510354b217b83f1857b5f6d9465ea54a85639d1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 22 Sep 2023 11:57:22 +0800 Subject: [PATCH 01/24] Provide actions badge svgs --- models/actions/run.go | 15 ++++++ .../assets/img/svg/gitea_actions_failed.svg | 20 ++++++++ public/assets/img/svg/gitea_actions_pass.svg | 20 ++++++++ .../assets/img/svg/gitea_actions_pending.svg | 20 ++++++++ routers/web/repo/actions/badge.go | 48 +++++++++++++++++++ routers/web/web.go | 3 ++ 6 files changed, 126 insertions(+) create mode 100644 public/assets/img/svg/gitea_actions_failed.svg create mode 100644 public/assets/img/svg/gitea_actions_pass.svg create mode 100644 public/assets/img/svg/gitea_actions_pending.svg create mode 100644 routers/web/repo/actions/badge.go diff --git a/models/actions/run.go b/models/actions/run.go index 8078613fb8f5b..cfb36eeb75352 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -335,6 +335,21 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) return run, nil } +func GetRepoBranchLastRun(ctx context.Context, repoID int64, branch, workflowFile string) (*ActionRun, error) { + var run ActionRun + has, err := db.GetEngine(ctx).Where("repo_id=?", repoID). + And("ref = ?", branch). + And("workflow_id = ?", workflowFile). + Desc("id"). + Get(&run) + if err != nil { + return nil, err + } else if !has { + return nil, util.NewNotExistErrorf("run with repo_id %d, ref %s, workflow_id %s", repoID, branch, workflowFile) + } + return &run, nil +} + // UpdateRun updates a run. // It requires the inputted run has Version set. // It will return error if the version is not matched (it means the run has been changed after loaded). diff --git a/public/assets/img/svg/gitea_actions_failed.svg b/public/assets/img/svg/gitea_actions_failed.svg new file mode 100644 index 0000000000000..f862e6935ecf8 --- /dev/null +++ b/public/assets/img/svg/gitea_actions_failed.svg @@ -0,0 +1,20 @@ + + Gitea Actions: FAILED + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/img/svg/gitea_actions_pass.svg b/public/assets/img/svg/gitea_actions_pass.svg new file mode 100644 index 0000000000000..cf9f0154f4deb --- /dev/null +++ b/public/assets/img/svg/gitea_actions_pass.svg @@ -0,0 +1,20 @@ + + Gitea Actions: PASS + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/img/svg/gitea_actions_pending.svg b/public/assets/img/svg/gitea_actions_pending.svg new file mode 100644 index 0000000000000..1fdadfedfb3de --- /dev/null +++ b/public/assets/img/svg/gitea_actions_pending.svg @@ -0,0 +1,20 @@ + + Gitea Actions: PENDING + + + + + + + + + + + + + \ No newline at end of file diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go new file mode 100644 index 0000000000000..a9bca5209b0a1 --- /dev/null +++ b/routers/web/repo/actions/badge.go @@ -0,0 +1,48 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "errors" + "fmt" + "strings" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" +) + +func GetWorkflowBadge(ctx *context.Context) { + workflowFile := ctx.PathParamRaw("*") + if !strings.HasSuffix(workflowFile, "/badge.svg") { + ctx.NotFound("Not found", fmt.Errorf("%s not a badge request", ctx.Req.URL.Path)) + return + } + + workflowFile = strings.TrimSuffix(workflowFile, "/badge.svg") + run, err := actions_model.GetRepoBranchLastRun(ctx, ctx.Repo.Repository.ID, + git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch).String(), + workflowFile) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) + return + } + ctx.ServerError("GetWorkflowBadge", err) + return + } + + switch run.Status { + case actions_model.StatusSuccess, actions_model.StatusSkipped: + ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_pass.svg") + case actions_model.StatusUnknown, actions_model.StatusFailure, actions_model.StatusCancelled: + ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_failed.svg") + case actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked: + ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_pending.svg") + default: + ctx.NotFound("Not found", fmt.Errorf("unknown status %d", run.Status)) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index f8b745fb10b55..135a214bd6dc3 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1331,6 +1331,9 @@ func registerRoutes(m *web.Route) { m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) + m.Group("/workflows", func() { + m.Get("/*", actions.GetWorkflowBadge) + }) }, reqRepoActionsReader, actions.MustEnableActions) m.Group("/wiki", func() { From dab670a46087ac40856956e41ae0a6c493802c9c Mon Sep 17 00:00:00 2001 From: lng2020 Date: Thu, 16 Nov 2023 18:45:29 +0800 Subject: [PATCH 02/24] draw badge --- models/actions/run.go | 2 +- routers/web/repo/actions/badge.go | 213 +++++++++++++++++++-- routers/web/web.go | 5 +- templates/shared/actions/runner_badge.tmpl | 26 +++ 4 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 templates/shared/actions/runner_badge.tmpl diff --git a/models/actions/run.go b/models/actions/run.go index cfb36eeb75352..7afff309c180d 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -338,7 +338,7 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) func GetRepoBranchLastRun(ctx context.Context, repoID int64, branch, workflowFile string) (*ActionRun, error) { var run ActionRun has, err := db.GetEngine(ctx).Where("repo_id=?", repoID). - And("ref = ?", branch). + In("ref", branch, ""). // cron job has no branch And("workflow_id = ?", workflowFile). Desc("id"). Get(&run) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index a9bca5209b0a1..81e6d90010160 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -6,26 +6,99 @@ package actions import ( "errors" "fmt" + "math" + "net/http" + "path/filepath" + "strconv" "strings" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "golang.org/x/image/font" + "golang.org/x/image/font/basicfont" ) -func GetWorkflowBadge(ctx *context.Context) { - workflowFile := ctx.PathParamRaw("*") - if !strings.HasSuffix(workflowFile, "/badge.svg") { - ctx.NotFound("Not found", fmt.Errorf("%s not a badge request", ctx.Req.URL.Path)) - return +type Badge struct { + Label string + Message string + Color string + Width string + LabelWidth string + MessageWidth string + LabelX string + MessageX string + LabelLength string + MessageLength string + MessageColor string + MessageShadow string + FontSize string +} + +const ( + DEFAULT_OFFSET = 9 + DEFAULT_SPACING = 0 + DEFAULT_FONT_SIZE = 11 + + COLOR_BLUE = "#007ec6" + COLOR_BRIGHTGREEN = "#4c1" + COLOR_GREEN = "#97ca00" + COLOR_GREY = "#555" + COLOR_LIGHTGREY = "#9f9f9f" + COLOR_ORANGE = "#fe7d37" + COLOR_RED = "#e05d44" + COLOR_YELLOW = "#dfb317" + COLOR_YELLOWGREEN = "#a4a61d" + + COLOR_SUCCESS = "#4c1" + COLOR_IMPORTANT = "#fe7d37" + COLOR_CRITICAL = "#e05d44" + COLOR_INFORMATIONAL = "#007ec6" + COLOR_INACTIVE = "#9f9f9f" +) + +var ( + drawer = &font.Drawer{ + Face: basicfont.Face7x13, } +) + +type badgeInfo struct { + color string + status string +} - workflowFile = strings.TrimSuffix(workflowFile, "/badge.svg") - run, err := actions_model.GetRepoBranchLastRun(ctx, ctx.Repo.Repository.ID, - git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch).String(), - workflowFile) +var statusBadgeInfoMap = map[actions_model.Status]badgeInfo{ + actions_model.StatusSuccess: {COLOR_SUCCESS, "success"}, + actions_model.StatusSkipped: {COLOR_YELLOW, "skipped"}, + actions_model.StatusUnknown: {COLOR_GREEN, "unknown"}, + actions_model.StatusFailure: {COLOR_CRITICAL, "failure"}, + actions_model.StatusCancelled: {COLOR_IMPORTANT, "cancelled"}, + actions_model.StatusWaiting: {COLOR_YELLOW, "waiting"}, + actions_model.StatusRunning: {COLOR_YELLOW, "running"}, + actions_model.StatusBlocked: {COLOR_YELLOW, "blocked"}, +} + +func GetDefaultBranchWorkflowBadge(ctx *context.Context) { + workflowFile := ctx.Params("workflow_name") + branchName := git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch).String() + badge, err := getWorkflowBadge(ctx, workflowFile, branchName) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) + return + } + ctx.ServerError("GetWorkflowBadge", err) + return + } + ctx.Data["Badge"] = badge + ctx.HTML(http.StatusOK, "shared/actions/runner_badge") +} +func GetWorkflowBadge(ctx *context.Context) { + workflowFile := ctx.Params("workflow_name") + branchName := ctx.Params("branch_name") + badge, err := getWorkflowBadge(ctx, workflowFile, branchName) if err != nil { if errors.Is(err, util.ErrNotExist) { ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) @@ -34,15 +107,117 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.ServerError("GetWorkflowBadge", err) return } + ctx.Data["Badge"] = badge + ctx.HTML(http.StatusOK, "shared/actions/runner_badge") +} + +func getWorkflowBadge(ctx *context.Context, workflowFile string, branchName string) (Badge, error) { + run, err := actions_model.GetRepoBranchLastRun(ctx, ctx.Repo.Repository.ID, branchName, workflowFile) + if err != nil { + return Badge{}, err + } - switch run.Status { - case actions_model.StatusSuccess, actions_model.StatusSkipped: - ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_pass.svg") - case actions_model.StatusUnknown, actions_model.StatusFailure, actions_model.StatusCancelled: - ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_failed.svg") - case actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked: - ctx.Redirect(setting.AbsoluteAssetURL + "/assets/img/svg/gitea_actions_pending.svg") - default: - ctx.NotFound("Not found", fmt.Errorf("unknown status %d", run.Status)) + badgeInfo, ok := statusBadgeInfoMap[run.Status] + if !ok { + return Badge{}, fmt.Errorf("unknown status %d", run.Status) } + + extension := filepath.Ext(workflowFile) + workflowName := strings.TrimSuffix(workflowFile, extension) + return generateBadge(workflowName, badgeInfo.status, badgeInfo.color), nil +} + +//utils for badge generation ------------------------------------- + +// generateBadge generates badge with given template +func generateBadge(label, message, color string) Badge { + c := parseColor(color) + gF := float64(DEFAULT_OFFSET) + lW := float64(drawer.MeasureString(label)>>6) + gF + mW := float64(drawer.MeasureString(message)>>6) + gF + fW := lW + mW + lX := (lW/2 + 1) * 10 + mX := (lW + (mW / 2) - 1) * 10 + lL := (lW - gF) * (10.0 + DEFAULT_SPACING - 0.5) + mL := (mW - gF) * (10.0 + DEFAULT_SPACING - 0.5) + fS := DEFAULT_FONT_SIZE * 10 + + mC, mS := getMessageColors(c) + + return Badge{ + Label: label, + Message: message, + Color: formatColor(c), + Width: formatFloat(fW), + LabelWidth: formatFloat(lW), + MessageWidth: formatFloat(mW), + LabelX: formatFloat(lX), + MessageX: formatFloat(mX), + LabelLength: formatFloat(lL), + MessageLength: formatFloat(mL), + MessageColor: mC, + MessageShadow: mS, + FontSize: strconv.Itoa(fS), + } +} + +// parseColor parses hex color +func parseColor(c string) int64 { + if strings.HasPrefix(c, "#") { + c = strings.TrimLeft(c, "#") + } + + // Shorthand hex color + if len(c) == 3 { + c = strings.Repeat(string(c[0]), 2) + + strings.Repeat(string(c[1]), 2) + + strings.Repeat(string(c[2]), 2) + } + + i, _ := strconv.ParseInt(c, 16, 32) + + return i +} + +// formatColor formats color +func formatColor(c int64) string { + k := fmt.Sprintf("%06x", c) + + if k[0] == k[1] && k[2] == k[3] && k[4] == k[5] { + k = k[0:1] + k[2:3] + k[4:5] + } + + return "#" + k +} + +// formatFloat formats float values +func formatFloat(v float64) string { + return strconv.FormatFloat(v, 'f', 0, 64) +} + +// getMessageColors returns message text and shadow colors based on color of badge +func getMessageColors(c int64) (string, string) { + if c == 0 || calcLuminance(c) < 0.65 { + return "#fff", "#010101" + } + + return "#333", "#ccc" +} + +// calcLuminance calculates relative luminance +func calcLuminance(color int64) float64 { + r := calcLumColor(float64(color>>16&0xFF) / 255) + g := calcLumColor(float64(color>>8&0xFF) / 255) + b := calcLumColor(float64(color&0xFF) / 255) + + return 0.2126*r + 0.7152*g + 0.0722*b +} + +// calcLumColor calculates luminance for one color +func calcLumColor(c float64) float64 { + if c <= 0.03928 { + return c / 12.92 + } + + return math.Pow(((c + 0.055) / 1.055), 2.4) } diff --git a/routers/web/web.go b/routers/web/web.go index 135a214bd6dc3..fb648ae8eab8a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1331,8 +1331,9 @@ func registerRoutes(m *web.Route) { m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) - m.Group("/workflows", func() { - m.Get("/*", actions.GetWorkflowBadge) + m.Group("/workflows/{workflow_name}", func() { + m.Get("/badge.svg", actions.GetDefaultBranchWorkflowBadge) + m.Get("/{branch_name}/badge.svg", actions.GetWorkflowBadge) }) }, reqRepoActionsReader, actions.MustEnableActions) diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl new file mode 100644 index 0000000000000..28acab069bcb9 --- /dev/null +++ b/templates/shared/actions/runner_badge.tmpl @@ -0,0 +1,26 @@ +{{$ := .Badge}} + + {$.Label}: {$.Message} + + + + + + + + + + + + + + + {{$.Label}}{{$.Message}} + \ No newline at end of file From 78056528fb82f7332fddabccded855a6f6c6af06 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 13:57:18 +0800 Subject: [PATCH 03/24] change field --- routers/web/repo/actions/badge.go | 50 +++++++++++-------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 81e6d90010160..5bbb92ee4b351 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -24,16 +24,16 @@ type Badge struct { Label string Message string Color string - Width string - LabelWidth string - MessageWidth string - LabelX string - MessageX string - LabelLength string - MessageLength string + Width int + LabelWidth int + MessageWidth int + LabelX int + MessageX int + LabelLength int + MessageLength int MessageColor string MessageShadow string - FontSize string + FontSize int } const ( @@ -147,17 +147,17 @@ func generateBadge(label, message, color string) Badge { return Badge{ Label: label, Message: message, - Color: formatColor(c), - Width: formatFloat(fW), - LabelWidth: formatFloat(lW), - MessageWidth: formatFloat(mW), - LabelX: formatFloat(lX), - MessageX: formatFloat(mX), - LabelLength: formatFloat(lL), - MessageLength: formatFloat(mL), + Color: color, + Width: int(fW), + LabelWidth: int(lW), + MessageWidth: int(mW), + LabelX: int(lX), + MessageX: int(mX), + LabelLength: int(lL), + MessageLength: int(mL), MessageColor: mC, MessageShadow: mS, - FontSize: strconv.Itoa(fS), + FontSize: fS, } } @@ -179,22 +179,6 @@ func parseColor(c string) int64 { return i } -// formatColor formats color -func formatColor(c int64) string { - k := fmt.Sprintf("%06x", c) - - if k[0] == k[1] && k[2] == k[3] && k[4] == k[5] { - k = k[0:1] + k[2:3] + k[4:5] - } - - return "#" + k -} - -// formatFloat formats float values -func formatFloat(v float64) string { - return strconv.FormatFloat(v, 'f', 0, 64) -} - // getMessageColors returns message text and shadow colors based on color of badge func getMessageColors(c int64) (string, string) { if c == 0 || calcLuminance(c) < 0.65 { From 41d016978aa91c34b7d78f7eef5680c1fa2d36a7 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 14:13:35 +0800 Subject: [PATCH 04/24] simpilfy code --- routers/web/repo/actions/badge.go | 94 +++++++++++-------------------- 1 file changed, 32 insertions(+), 62 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 5bbb92ee4b351..10546ed22ff77 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -40,22 +40,6 @@ const ( DEFAULT_OFFSET = 9 DEFAULT_SPACING = 0 DEFAULT_FONT_SIZE = 11 - - COLOR_BLUE = "#007ec6" - COLOR_BRIGHTGREEN = "#4c1" - COLOR_GREEN = "#97ca00" - COLOR_GREY = "#555" - COLOR_LIGHTGREY = "#9f9f9f" - COLOR_ORANGE = "#fe7d37" - COLOR_RED = "#e05d44" - COLOR_YELLOW = "#dfb317" - COLOR_YELLOWGREEN = "#a4a61d" - - COLOR_SUCCESS = "#4c1" - COLOR_IMPORTANT = "#fe7d37" - COLOR_CRITICAL = "#e05d44" - COLOR_INFORMATIONAL = "#007ec6" - COLOR_INACTIVE = "#9f9f9f" ) var ( @@ -64,20 +48,15 @@ var ( } ) -type badgeInfo struct { - color string - status string -} - -var statusBadgeInfoMap = map[actions_model.Status]badgeInfo{ - actions_model.StatusSuccess: {COLOR_SUCCESS, "success"}, - actions_model.StatusSkipped: {COLOR_YELLOW, "skipped"}, - actions_model.StatusUnknown: {COLOR_GREEN, "unknown"}, - actions_model.StatusFailure: {COLOR_CRITICAL, "failure"}, - actions_model.StatusCancelled: {COLOR_IMPORTANT, "cancelled"}, - actions_model.StatusWaiting: {COLOR_YELLOW, "waiting"}, - actions_model.StatusRunning: {COLOR_YELLOW, "running"}, - actions_model.StatusBlocked: {COLOR_YELLOW, "blocked"}, +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 } func GetDefaultBranchWorkflowBadge(ctx *context.Context) { @@ -117,21 +96,20 @@ func getWorkflowBadge(ctx *context.Context, workflowFile string, branchName stri return Badge{}, err } - badgeInfo, ok := statusBadgeInfoMap[run.Status] + color, ok := statusColorMap[run.Status] if !ok { return Badge{}, fmt.Errorf("unknown status %d", run.Status) } extension := filepath.Ext(workflowFile) workflowName := strings.TrimSuffix(workflowFile, extension) - return generateBadge(workflowName, badgeInfo.status, badgeInfo.color), nil + return generateBadge(workflowName, run.Status.String(), color), nil } //utils for badge generation ------------------------------------- // generateBadge generates badge with given template func generateBadge(label, message, color string) Badge { - c := parseColor(color) gF := float64(DEFAULT_OFFSET) lW := float64(drawer.MeasureString(label)>>6) + gF mW := float64(drawer.MeasureString(message)>>6) + gF @@ -142,7 +120,7 @@ func generateBadge(label, message, color string) Badge { mL := (mW - gF) * (10.0 + DEFAULT_SPACING - 0.5) fS := DEFAULT_FONT_SIZE * 10 - mC, mS := getMessageColors(c) + mC, mS := determineMessageColorsFromHex(color) return Badge{ Label: label, @@ -161,47 +139,39 @@ func generateBadge(label, message, color string) Badge { } } -// parseColor parses hex color -func parseColor(c string) int64 { - if strings.HasPrefix(c, "#") { - c = strings.TrimLeft(c, "#") +// determineMessageColorsFromHex takes a hex color string and returns text and shadow colors +func determineMessageColorsFromHex(hexColor string) (string, string) { + hexColor = strings.TrimPrefix(hexColor, "#") + // Check for shorthand hex color + if len(hexColor) == 3 { + hexColor = strings.Repeat(string(hexColor[0]), 2) + + strings.Repeat(string(hexColor[1]), 2) + + strings.Repeat(string(hexColor[2]), 2) } - // Shorthand hex color - if len(c) == 3 { - c = strings.Repeat(string(c[0]), 2) + - strings.Repeat(string(c[1]), 2) + - strings.Repeat(string(c[2]), 2) - } - - i, _ := strconv.ParseInt(c, 16, 32) - - return i -} + badgeColor, _ := strconv.ParseInt(hexColor, 16, 32) -// getMessageColors returns message text and shadow colors based on color of badge -func getMessageColors(c int64) (string, string) { - if c == 0 || calcLuminance(c) < 0.65 { + if badgeColor == 0 || computeLuminance(badgeColor) < 0.65 { return "#fff", "#010101" } return "#333", "#ccc" } -// calcLuminance calculates relative luminance -func calcLuminance(color int64) float64 { - r := calcLumColor(float64(color>>16&0xFF) / 255) - g := calcLumColor(float64(color>>8&0xFF) / 255) - b := calcLumColor(float64(color&0xFF) / 255) +// computeLuminance calculates the relative luminance of a color +func computeLuminance(inputColor int64) float64 { + r := singleColorLuminance(float64(inputColor>>16&0xFF) / 255) + g := singleColorLuminance(float64(inputColor>>8&0xFF) / 255) + b := singleColorLuminance(float64(inputColor&0xFF) / 255) return 0.2126*r + 0.7152*g + 0.0722*b } -// calcLumColor calculates luminance for one color -func calcLumColor(c float64) float64 { - if c <= 0.03928 { - return c / 12.92 +// singleColorLuminance calculates luminance for a single color +func singleColorLuminance(colorValue float64) float64 { + if colorValue <= 0.03928 { + return colorValue / 12.92 } - return math.Pow(((c + 0.055) / 1.055), 2.4) + return math.Pow((colorValue+0.055)/1.055, 2.4) } From d705e21ec5d2edc03f307e40ab2db6434bf3578c Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 14:29:19 +0800 Subject: [PATCH 05/24] lint --- routers/web/repo/actions/badge.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 10546ed22ff77..b4ddf08f344d8 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -74,6 +74,7 @@ func GetDefaultBranchWorkflowBadge(ctx *context.Context) { ctx.Data["Badge"] = badge ctx.HTML(http.StatusOK, "shared/actions/runner_badge") } + func GetWorkflowBadge(ctx *context.Context) { workflowFile := ctx.Params("workflow_name") branchName := ctx.Params("branch_name") From 40ccec91aed69a8f35750a13c131b3325c1fec98 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 14:32:48 +0800 Subject: [PATCH 06/24] delete file --- .../assets/img/svg/gitea_actions_failed.svg | 20 ------------------- public/assets/img/svg/gitea_actions_pass.svg | 20 ------------------- .../assets/img/svg/gitea_actions_pending.svg | 20 ------------------- 3 files changed, 60 deletions(-) delete mode 100644 public/assets/img/svg/gitea_actions_failed.svg delete mode 100644 public/assets/img/svg/gitea_actions_pass.svg delete mode 100644 public/assets/img/svg/gitea_actions_pending.svg diff --git a/public/assets/img/svg/gitea_actions_failed.svg b/public/assets/img/svg/gitea_actions_failed.svg deleted file mode 100644 index f862e6935ecf8..0000000000000 --- a/public/assets/img/svg/gitea_actions_failed.svg +++ /dev/null @@ -1,20 +0,0 @@ - - Gitea Actions: FAILED - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/assets/img/svg/gitea_actions_pass.svg b/public/assets/img/svg/gitea_actions_pass.svg deleted file mode 100644 index cf9f0154f4deb..0000000000000 --- a/public/assets/img/svg/gitea_actions_pass.svg +++ /dev/null @@ -1,20 +0,0 @@ - - Gitea Actions: PASS - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/assets/img/svg/gitea_actions_pending.svg b/public/assets/img/svg/gitea_actions_pending.svg deleted file mode 100644 index 1fdadfedfb3de..0000000000000 --- a/public/assets/img/svg/gitea_actions_pending.svg +++ /dev/null @@ -1,20 +0,0 @@ - - Gitea Actions: PENDING - - - - - - - - - - - - - \ No newline at end of file From 2319c96d037bdb43bb439455d1bb9adaf1212201 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 14:48:46 +0800 Subject: [PATCH 07/24] apply camel case --- routers/web/repo/actions/badge.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index b4ddf08f344d8..0960d856133e2 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -37,9 +37,9 @@ type Badge struct { } const ( - DEFAULT_OFFSET = 9 - DEFAULT_SPACING = 0 - DEFAULT_FONT_SIZE = 11 + defaultOffset = 9 + defaultSpacing = 0 + defaultFontSize = 11 ) var ( @@ -111,15 +111,15 @@ func getWorkflowBadge(ctx *context.Context, workflowFile string, branchName stri // generateBadge generates badge with given template func generateBadge(label, message, color string) Badge { - gF := float64(DEFAULT_OFFSET) + gF := float64(defaultOffset) lW := float64(drawer.MeasureString(label)>>6) + gF mW := float64(drawer.MeasureString(message)>>6) + gF fW := lW + mW lX := (lW/2 + 1) * 10 mX := (lW + (mW / 2) - 1) * 10 - lL := (lW - gF) * (10.0 + DEFAULT_SPACING - 0.5) - mL := (mW - gF) * (10.0 + DEFAULT_SPACING - 0.5) - fS := DEFAULT_FONT_SIZE * 10 + lL := (lW - gF) * (10.0 + defaultSpacing - 0.5) + mL := (mW - gF) * (10.0 + defaultSpacing - 0.5) + fS := defaultFontSize * 10 mC, mS := determineMessageColorsFromHex(color) From 7c52b0bcfb0c1576aeb53dd80f8ac40201cfc47c Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 14:53:19 +0800 Subject: [PATCH 08/24] fmt --- routers/web/repo/actions/badge.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 0960d856133e2..9d4411466d9be 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" + "golang.org/x/image/font" "golang.org/x/image/font/basicfont" ) @@ -42,11 +43,9 @@ const ( defaultFontSize = 11 ) -var ( - drawer = &font.Drawer{ - Face: basicfont.Face7x13, - } -) +var drawer = &font.Drawer{ + Face: basicfont.Face7x13, +} var statusColorMap = map[actions_model.Status]string{ actions_model.StatusSuccess: "#4c1", // Green @@ -91,7 +90,7 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.HTML(http.StatusOK, "shared/actions/runner_badge") } -func getWorkflowBadge(ctx *context.Context, workflowFile string, branchName string) (Badge, error) { +func getWorkflowBadge(ctx *context.Context, workflowFile, branchName string) (Badge, error) { run, err := actions_model.GetRepoBranchLastRun(ctx, ctx.Repo.Repository.ID, branchName, workflowFile) if err != nil { return Badge{}, err @@ -107,7 +106,7 @@ func getWorkflowBadge(ctx *context.Context, workflowFile string, branchName stri return generateBadge(workflowName, run.Status.String(), color), nil } -//utils for badge generation ------------------------------------- +// utils for badge generation ------------------------------------- // generateBadge generates badge with given template func generateBadge(label, message, color string) Badge { From 0139320bf0f4ffc6e47b77d0ee4d25185387a0f8 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 15:19:32 +0800 Subject: [PATCH 09/24] lint --- templates/shared/actions/runner_badge.tmpl | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl index 28acab069bcb9..b874d6fb8beb1 100644 --- a/templates/shared/actions/runner_badge.tmpl +++ b/templates/shared/actions/runner_badge.tmpl @@ -1,26 +1,26 @@ {{$ := .Badge}} - {$.Label}: {$.Message} - - - - - - - - - - - - - - - {{$.Label}}{{$.Message}} + role="img" aria-label="{{$.Label}}: {{$.Message}}"> + {$.Label}: {$.Message} + + + + + + + + + + + + + + + {{$.Label}}{{$.Message}} \ No newline at end of file From e7cc38b51c3bfe2a8eed94b18d19d908c20b0ed4 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 17 Nov 2023 15:37:00 +0800 Subject: [PATCH 10/24] lint --- templates/shared/actions/runner_badge.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl index b874d6fb8beb1..d58dd40bd93d4 100644 --- a/templates/shared/actions/runner_badge.tmpl +++ b/templates/shared/actions/runner_badge.tmpl @@ -23,4 +23,4 @@ x="{{$.MessageX}}" y="140" fill="{{$.MessageShadow}}" fill-opacity=".3" transform="scale(.1)" textLength="{{$.MessageLength}}">{{$.Message}}{{$.Message}} - \ No newline at end of file + From 772a59c568473d457e0e15d96b51010dd108560b Mon Sep 17 00:00:00 2001 From: lng2020 Date: Fri, 24 Nov 2023 13:42:16 +0800 Subject: [PATCH 11/24] change sql --- models/actions/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/actions/run.go b/models/actions/run.go index 7afff309c180d..0a01059791098 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -338,7 +338,7 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) func GetRepoBranchLastRun(ctx context.Context, repoID int64, branch, workflowFile string) (*ActionRun, error) { var run ActionRun has, err := db.GetEngine(ctx).Where("repo_id=?", repoID). - In("ref", branch, ""). // cron job has no branch + And("ref = ? or schedule_id != ?", branch, 0). // schedule_id != 0 indicates cron job And("workflow_id = ?", workflowFile). Desc("id"). Get(&run) From 2073d50cc01f6364d8491958ce545aae7d71fb99 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Thu, 22 Feb 2024 03:17:06 +0800 Subject: [PATCH 12/24] Apply reviews --- models/actions/run.go | 14 +- routers/web/repo/actions/badge.go | 176 +++++++++------------ routers/web/web.go | 3 +- templates/shared/actions/runner_badge.tmpl | 27 ++-- 4 files changed, 97 insertions(+), 123 deletions(-) diff --git a/models/actions/run.go b/models/actions/run.go index 4c0f32c63f9d8..7b3125949b82d 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -339,13 +339,15 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) return run, nil } -func GetRepoBranchLastRun(ctx context.Context, repoID int64, branch, workflowFile string) (*ActionRun, error) { +func GetWorkflowLatestRun(ctx context.Context, repoID int64, workflowFile, branch, event string) (*ActionRun, error) { var run ActionRun - has, err := db.GetEngine(ctx).Where("repo_id=?", repoID). - And("ref = ? or schedule_id != ?", branch, 0). // schedule_id != 0 indicates cron job - And("workflow_id = ?", workflowFile). - Desc("id"). - Get(&run) + q := db.GetEngine(ctx).Where("repo_id=?", repoID). + And("ref = ?", branch). + And("workflow_id = ?", workflowFile) + if event != "" { + q.And("event = ?", event) + } + has, err := q.Desc("id").Get(&run) if err != nil { return nil, err } else if !has { diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 9d4411466d9be..2672bdb2b7756 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -6,40 +6,74 @@ package actions import ( "errors" "fmt" - "math" "net/http" "path/filepath" - "strconv" "strings" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" "golang.org/x/image/font" "golang.org/x/image/font/basicfont" ) +type Label struct { + text string + width float64 +} + +func (l Label) Label() string { + return l.text +} + +func (l Label) Width() int { + return int(l.width) +} + +func (l Label) TextLength() int { + return int(l.width * 9.5) // 95% of the width +} + +func (l Label) X() int { + return int((l.width/2 + 1) * 10) // scale 10 times to be more accurate +} + +type Message struct { + text string + width float64 + x int +} + +func (m Message) Message() string { + return m.text +} + +func (m Message) Width() int { + return int(m.width) +} + +func (m Message) X() int { + return m.x +} + +func (m Message) TextLength() int { + return int(m.width * 9.5) +} + type Badge struct { - Label string - Message string - Color string - Width int - LabelWidth int - MessageWidth int - LabelX int - MessageX int - LabelLength int - MessageLength int - MessageColor string - MessageShadow string - FontSize int + Color string + FontSize int + Label Label + Message Message +} + +func (b Badge) Width() int { + return b.Label.Width() + b.Message.Width() } const ( defaultOffset = 9 - defaultSpacing = 0 defaultFontSize = 11 ) @@ -58,26 +92,15 @@ var statusColorMap = map[actions_model.Status]string{ actions_model.StatusBlocked: "#dfb317", // Yellow } -func GetDefaultBranchWorkflowBadge(ctx *context.Context) { - workflowFile := ctx.Params("workflow_name") - branchName := git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch).String() - badge, err := getWorkflowBadge(ctx, workflowFile, branchName) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) - return - } - ctx.ServerError("GetWorkflowBadge", err) - return - } - ctx.Data["Badge"] = badge - ctx.HTML(http.StatusOK, "shared/actions/runner_badge") -} - func GetWorkflowBadge(ctx *context.Context) { workflowFile := ctx.Params("workflow_name") - branchName := ctx.Params("branch_name") - badge, err := getWorkflowBadge(ctx, workflowFile, branchName) + 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") + badge, err := getWorkflowBadge(ctx, workflowFile, branchRef, event) if err != nil { if errors.Is(err, util.ErrNotExist) { ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) @@ -86,12 +109,14 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.ServerError("GetWorkflowBadge", err) return } + ctx.Data["Badge"] = badge + ctx.RespHeader().Set("Content-Type", "image/svg+xml") ctx.HTML(http.StatusOK, "shared/actions/runner_badge") } -func getWorkflowBadge(ctx *context.Context, workflowFile, branchName string) (Badge, error) { - run, err := actions_model.GetRepoBranchLastRun(ctx, ctx.Repo.Repository.ID, branchName, workflowFile) +func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (Badge, error) { + run, err := actions_model.GetWorkflowLatestRun(ctx, ctx.Repo.Repository.ID, workflowFile, branchName, event) if err != nil { return Badge{}, err } @@ -106,72 +131,21 @@ func getWorkflowBadge(ctx *context.Context, workflowFile, branchName string) (Ba return generateBadge(workflowName, run.Status.String(), color), nil } -// utils for badge generation ------------------------------------- - // generateBadge generates badge with given template func generateBadge(label, message, color string) Badge { - gF := float64(defaultOffset) - lW := float64(drawer.MeasureString(label)>>6) + gF - mW := float64(drawer.MeasureString(message)>>6) + gF - fW := lW + mW - lX := (lW/2 + 1) * 10 - mX := (lW + (mW / 2) - 1) * 10 - lL := (lW - gF) * (10.0 + defaultSpacing - 0.5) - mL := (mW - gF) * (10.0 + defaultSpacing - 0.5) - fS := defaultFontSize * 10 - - mC, mS := determineMessageColorsFromHex(color) - + lw := float64(drawer.MeasureString(label)>>6) + float64(defaultOffset) + mw := float64(drawer.MeasureString(message)>>6) + float64(defaultOffset) + x := int((lw + (mw / 2) - 1) * 10) return Badge{ - Label: label, - Message: message, - Color: color, - Width: int(fW), - LabelWidth: int(lW), - MessageWidth: int(mW), - LabelX: int(lX), - MessageX: int(mX), - LabelLength: int(lL), - MessageLength: int(mL), - MessageColor: mC, - MessageShadow: mS, - FontSize: fS, - } -} - -// determineMessageColorsFromHex takes a hex color string and returns text and shadow colors -func determineMessageColorsFromHex(hexColor string) (string, string) { - hexColor = strings.TrimPrefix(hexColor, "#") - // Check for shorthand hex color - if len(hexColor) == 3 { - hexColor = strings.Repeat(string(hexColor[0]), 2) + - strings.Repeat(string(hexColor[1]), 2) + - strings.Repeat(string(hexColor[2]), 2) + Label: Label{ + text: label, + width: lw, + }, + Message: Message{ + text: message, + width: mw, + x: x, + }, + FontSize: defaultFontSize * 10, } - - badgeColor, _ := strconv.ParseInt(hexColor, 16, 32) - - if badgeColor == 0 || computeLuminance(badgeColor) < 0.65 { - return "#fff", "#010101" - } - - return "#333", "#ccc" -} - -// computeLuminance calculates the relative luminance of a color -func computeLuminance(inputColor int64) float64 { - r := singleColorLuminance(float64(inputColor>>16&0xFF) / 255) - g := singleColorLuminance(float64(inputColor>>8&0xFF) / 255) - b := singleColorLuminance(float64(inputColor&0xFF) / 255) - - return 0.2126*r + 0.7152*g + 0.0722*b -} - -// singleColorLuminance calculates luminance for a single color -func singleColorLuminance(colorValue float64) float64 { - if colorValue <= 0.03928 { - return colorValue / 12.92 - } - - return math.Pow((colorValue+0.055)/1.055, 2.4) } diff --git a/routers/web/web.go b/routers/web/web.go index 58fbc0a3b8300..d069ff874b97d 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1372,8 +1372,7 @@ func registerRoutes(m *web.Route) { m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) m.Group("/workflows/{workflow_name}", func() { - m.Get("/badge.svg", actions.GetDefaultBranchWorkflowBadge) - m.Get("/{branch_name}/badge.svg", actions.GetWorkflowBadge) + m.Get("/badge.svg", actions.GetWorkflowBadge) }) }, reqRepoActionsReader, actions.MustEnableActions) diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl index d58dd40bd93d4..8a3602b68d7cc 100644 --- a/templates/shared/actions/runner_badge.tmpl +++ b/templates/shared/actions/runner_badge.tmpl @@ -1,7 +1,6 @@ -{{$ := .Badge}} - - {$.Label}: {$.Message} + + {.Badge.Label}: {.Badge.Message} @@ -9,18 +8,18 @@ - + - - - + + + {{$.Label}}{{$.Message}} + font-size="{{.Badge.FontSize}}">{{.Badge.Label.Text}}{{.Badge.Message}} From 2e416616485d15af416a3544e70a16e45e3dced3 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Thu, 22 Feb 2024 04:19:06 +0800 Subject: [PATCH 13/24] improvement --- routers/web/repo/actions/badge.go | 39 ++++++++++++---------- templates/shared/actions/runner_badge.tmpl | 8 ++--- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 2672bdb2b7756..5b9e862eb8127 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -23,7 +23,7 @@ type Label struct { width float64 } -func (l Label) Label() string { +func (l Label) Text() string { return l.text } @@ -32,11 +32,11 @@ func (l Label) Width() int { } func (l Label) TextLength() int { - return int(l.width * 9.5) // 95% of the width + return int((l.width - defaultOffset) * 9.5) } func (l Label) X() int { - return int((l.width/2 + 1) * 10) // scale 10 times to be more accurate + return int((l.width/2 + 1) * 10) } type Message struct { @@ -45,7 +45,7 @@ type Message struct { x int } -func (m Message) Message() string { +func (m Message) Text() string { return m.text } @@ -58,7 +58,7 @@ func (m Message) X() int { } func (m Message) TextLength() int { - return int(m.width * 9.5) + return int((m.width - defaultOffset) * 9.5) } type Badge struct { @@ -69,12 +69,13 @@ type Badge struct { } func (b Badge) Width() int { - return b.Label.Width() + b.Message.Width() + return int(b.Label.width + b.Message.width) } const ( - defaultOffset = 9 + defaultOffset = float64(9) defaultFontSize = 11 + noStatusColor = "#9f9f9f" // Grey ) var drawer = &font.Drawer{ @@ -100,12 +101,9 @@ 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) if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.NotFound("Not found", fmt.Errorf("%s not found", workflowFile)) - return - } ctx.ServerError("GetWorkflowBadge", err) return } @@ -116,25 +114,29 @@ func GetWorkflowBadge(ctx *context.Context) { } func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (Badge, error) { + extension := filepath.Ext(workflowFile) + workflowName := strings.TrimSuffix(workflowFile, extension) + run, err := actions_model.GetWorkflowLatestRun(ctx, ctx.Repo.Repository.ID, workflowFile, branchName, event) if err != nil { - return Badge{}, err + if errors.Is(err, util.ErrNotExist) { + return generateBadge(workflowName, "no status", noStatusColor), nil + } else { + return Badge{}, err + } } color, ok := statusColorMap[run.Status] if !ok { - return Badge{}, fmt.Errorf("unknown status %d", run.Status) + return generateBadge(workflowName, "unknown status", noStatusColor), nil } - - extension := filepath.Ext(workflowFile) - workflowName := strings.TrimSuffix(workflowFile, extension) return generateBadge(workflowName, run.Status.String(), color), nil } // generateBadge generates badge with given template func generateBadge(label, message, color string) Badge { - lw := float64(drawer.MeasureString(label)>>6) + float64(defaultOffset) - mw := float64(drawer.MeasureString(message)>>6) + float64(defaultOffset) + lw := float64(drawer.MeasureString(label)>>6) + defaultOffset + mw := float64(drawer.MeasureString(message)>>6) + defaultOffset x := int((lw + (mw / 2) - 1) * 10) return Badge{ Label: Label{ @@ -147,5 +149,6 @@ func generateBadge(label, message, color string) Badge { x: x, }, FontSize: defaultFontSize * 10, + Color: color, } } diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl index 8a3602b68d7cc..816e87e177a32 100644 --- a/templates/shared/actions/runner_badge.tmpl +++ b/templates/shared/actions/runner_badge.tmpl @@ -1,6 +1,6 @@ - {.Badge.Label}: {.Badge.Message} + role="img" aria-label="{{.Badge.Label.Text}}: {{.Badge.Message.Text}}"> + {{.Badge.Label.Text}}: {{.Badge.Message.Text}} @@ -20,6 +20,6 @@ transform="scale(.1)" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}{{.Badge.Label.Text}}{{.Badge.Message}} + textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}{{.Badge.Message.Text}} From 58c8fb716db55c288ad9943d689b8e298c078a7c Mon Sep 17 00:00:00 2001 From: Nanguan Lin <70063547+lng2020@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:33:22 +0800 Subject: [PATCH 14/24] fix lint --- routers/web/repo/actions/badge.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 5b9e862eb8127..9fee0fdfa255f 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -121,9 +121,8 @@ func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event stri if err != nil { if errors.Is(err, util.ErrNotExist) { return generateBadge(workflowName, "no status", noStatusColor), nil - } else { - return Badge{}, err } + return Badge{}, err } color, ok := statusColorMap[run.Status] From 9c271a6772177beda18ff3a22c3a2b23ef9030ae Mon Sep 17 00:00:00 2001 From: lng2020 Date: Sun, 25 Feb 2024 02:43:28 +0800 Subject: [PATCH 15/24] restructure file --- modules/badge/badge.go | 105 +++++++++++++++++++++++++++ routers/web/repo/actions/badge.go | 113 +++--------------------------- 2 files changed, 113 insertions(+), 105 deletions(-) create mode 100644 modules/badge/badge.go diff --git a/modules/badge/badge.go b/modules/badge/badge.go new file mode 100644 index 0000000000000..d4cfc31ab7611 --- /dev/null +++ b/modules/badge/badge.go @@ -0,0 +1,105 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package badge + +import ( + actions_model "code.gitea.io/gitea/models/actions" + "golang.org/x/image/font" + "golang.org/x/image/font/basicfont" +) + +type Label struct { + text string + width float64 +} + +func (l Label) Text() string { + return l.text +} + +func (l Label) Width() int { + return int(l.width) +} + +func (l Label) TextLength() int { + return int((l.width - defaultOffset) * 9.5) +} + +func (l Label) X() int { + return int((l.width/2 + 1) * 10) +} + +type Message struct { + text string + width float64 + x int +} + +func (m Message) Text() string { + return m.text +} + +func (m Message) Width() int { + return int(m.width) +} + +func (m Message) X() int { + return m.x +} + +func (m Message) TextLength() int { + return int((m.width - defaultOffset) * 9.5) +} + +type Badge struct { + Color string + FontSize int + Label Label + Message Message +} + +func (b Badge) Width() int { + return int(b.Label.width + b.Message.width) +} + +const ( + defaultOffset = float64(9) + defaultFontSize = 11 + DefaultColor = "#9f9f9f" // Grey +) + +var drawer = &font.Drawer{ + Face: basicfont.Face7x13, +} + +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 +} + +// GenerateBadge generates badge with given template +func GenerateBadge(label, message, color string) Badge { + lw := float64(drawer.MeasureString(label)>>6) + defaultOffset + mw := float64(drawer.MeasureString(message)>>6) + defaultOffset + x := int((lw + (mw / 2) - 1) * 10) + return Badge{ + Label: Label{ + text: label, + width: lw, + }, + Message: Message{ + text: message, + width: mw, + x: x, + }, + FontSize: defaultFontSize * 10, + Color: color, + } +} diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go index 9fee0fdfa255f..3f74da82e57d2 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package actions @@ -11,88 +11,11 @@ import ( "strings" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/modules/badge" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" - - "golang.org/x/image/font" - "golang.org/x/image/font/basicfont" -) - -type Label struct { - text string - width float64 -} - -func (l Label) Text() string { - return l.text -} - -func (l Label) Width() int { - return int(l.width) -} - -func (l Label) TextLength() int { - return int((l.width - defaultOffset) * 9.5) -} - -func (l Label) X() int { - return int((l.width/2 + 1) * 10) -} - -type Message struct { - text string - width float64 - x int -} - -func (m Message) Text() string { - return m.text -} - -func (m Message) Width() int { - return int(m.width) -} - -func (m Message) X() int { - return m.x -} - -func (m Message) TextLength() int { - return int((m.width - defaultOffset) * 9.5) -} - -type Badge struct { - Color string - FontSize int - Label Label - Message Message -} - -func (b Badge) Width() int { - return int(b.Label.width + b.Message.width) -} - -const ( - defaultOffset = float64(9) - defaultFontSize = 11 - noStatusColor = "#9f9f9f" // Grey ) -var drawer = &font.Drawer{ - Face: basicfont.Face7x13, -} - -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 -} - func GetWorkflowBadge(ctx *context.Context) { workflowFile := ctx.Params("workflow_name") branch := ctx.Req.URL.Query().Get("branch") @@ -113,41 +36,21 @@ func GetWorkflowBadge(ctx *context.Context) { ctx.HTML(http.StatusOK, "shared/actions/runner_badge") } -func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (Badge, error) { +func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (badge.Badge, error) { extension := filepath.Ext(workflowFile) workflowName := strings.TrimSuffix(workflowFile, extension) run, err := actions_model.GetWorkflowLatestRun(ctx, ctx.Repo.Repository.ID, workflowFile, branchName, event) if err != nil { if errors.Is(err, util.ErrNotExist) { - return generateBadge(workflowName, "no status", noStatusColor), nil + return badge.GenerateBadge(workflowName, "no status", badge.DefaultColor), nil } - return Badge{}, err + return badge.Badge{}, err } - color, ok := statusColorMap[run.Status] + color, ok := badge.StatusColorMap[run.Status] if !ok { - return generateBadge(workflowName, "unknown status", noStatusColor), nil - } - return generateBadge(workflowName, run.Status.String(), color), nil -} - -// generateBadge generates badge with given template -func generateBadge(label, message, color string) Badge { - lw := float64(drawer.MeasureString(label)>>6) + defaultOffset - mw := float64(drawer.MeasureString(message)>>6) + defaultOffset - x := int((lw + (mw / 2) - 1) * 10) - return Badge{ - Label: Label{ - text: label, - width: lw, - }, - Message: Message{ - text: message, - width: mw, - x: x, - }, - FontSize: defaultFontSize * 10, - Color: color, + return badge.GenerateBadge(workflowName, "unknown status", badge.DefaultColor), nil } + return badge.GenerateBadge(workflowName, run.Status.String(), color), nil } From b58ec247cc7f7756ae25c880bbecbebb02307d41 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Sun, 25 Feb 2024 03:28:47 +0800 Subject: [PATCH 16/24] add doc --- docs/content/usage/badge.en-us.md | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/content/usage/badge.en-us.md diff --git a/docs/content/usage/badge.en-us.md b/docs/content/usage/badge.en-us.md new file mode 100644 index 0000000000000..d9c328e18c3b8 --- /dev/null +++ b/docs/content/usage/badge.en-us.md @@ -0,0 +1,36 @@ +--- +date: "2023-02-25T00:00:00+00:00" +title: "Badge" +slug: "badge" +sidebar_position: 11 +toc: false +draft: false +aliases: + - /en-us/badge +menu: + sidebar: + parent: "usage" + name: "Badge" + sidebar_position: 11 + identifier: "Badge" +--- + +# Badge + +Gitea has its builtin Badge system which allows you to display the status of your repository in other places. You can use the following badges: + +## Workflow Badge + +Gitea Actions workflow badge is a badge that shows the status of the latest workflow run. It designed to be compatible with [GitHub Actions workflow badge](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge). + +You can use the following URL to get the badge: + +``` +https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file}?branch={branch}&event={event} +``` + +- `{owner}`: The owner of the repository. +- `{repo}`: The name of the repository. +- `{workflow_file}`: The name of the workflow file. +- `{branch}`: Optional. The branch of the workflow. Default to your repository's default branch. +- `{event}`: Optional. The event of the workflow. Default to none. \ No newline at end of file From e63180095832d85a6511a8eff6850c76e255b154 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Sun, 25 Feb 2024 15:23:03 +0800 Subject: [PATCH 17/24] lint --- modules/badge/badge.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index d4cfc31ab7611..43f996a2305cd 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -5,6 +5,7 @@ package badge import ( actions_model "code.gitea.io/gitea/models/actions" + "golang.org/x/image/font" "golang.org/x/image/font/basicfont" ) From eae95d3c396b26174f548c69f0433ccb6ce866a0 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Sun, 25 Feb 2024 15:33:03 +0800 Subject: [PATCH 18/24] lint md --- docs/content/usage/badge.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/usage/badge.en-us.md b/docs/content/usage/badge.en-us.md index d9c328e18c3b8..2e060a984b706 100644 --- a/docs/content/usage/badge.en-us.md +++ b/docs/content/usage/badge.en-us.md @@ -33,4 +33,4 @@ https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file} - `{repo}`: The name of the repository. - `{workflow_file}`: The name of the workflow file. - `{branch}`: Optional. The branch of the workflow. Default to your repository's default branch. -- `{event}`: Optional. The event of the workflow. Default to none. \ No newline at end of file +- `{event}`: Optional. The event of the workflow. Default to none. From c1c897bd2ae88551eedc41c8b696a0dbb0e670d6 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Mon, 26 Feb 2024 01:04:12 +0800 Subject: [PATCH 19/24] simplify font width --- modules/badge/badge.go | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index 43f996a2305cd..0548646436f71 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -5,14 +5,11 @@ package badge import ( actions_model "code.gitea.io/gitea/models/actions" - - "golang.org/x/image/font" - "golang.org/x/image/font/basicfont" ) type Label struct { text string - width float64 + width int } func (l Label) Text() string { @@ -24,16 +21,16 @@ func (l Label) Width() int { } func (l Label) TextLength() int { - return int((l.width - defaultOffset) * 9.5) + return int(float64(l.width-defaultOffset) * 9.5) } func (l Label) X() int { - return int((l.width/2 + 1) * 10) + return l.width*5 + 10 } type Message struct { text string - width float64 + width int x int } @@ -42,7 +39,7 @@ func (m Message) Text() string { } func (m Message) Width() int { - return int(m.width) + return m.width } func (m Message) X() int { @@ -50,7 +47,7 @@ func (m Message) X() int { } func (m Message) TextLength() int { - return int((m.width - defaultOffset) * 9.5) + return int(float64(m.width-defaultOffset) * 9.5) } type Badge struct { @@ -61,19 +58,16 @@ type Badge struct { } func (b Badge) Width() int { - return int(b.Label.width + b.Message.width) + return b.Label.width + b.Message.width } const ( - defaultOffset = float64(9) - defaultFontSize = 11 - DefaultColor = "#9f9f9f" // Grey + defaultOffset = 9 + defaultFontSize = 11 + DefaultColor = "#9f9f9f" // Grey + defaultFontWidth = 7 // approximate speculation ) -var drawer = &font.Drawer{ - Face: basicfont.Face7x13, -} - var StatusColorMap = map[actions_model.Status]string{ actions_model.StatusSuccess: "#4c1", // Green actions_model.StatusSkipped: "#dfb317", // Yellow @@ -87,9 +81,9 @@ var StatusColorMap = map[actions_model.Status]string{ // GenerateBadge generates badge with given template func GenerateBadge(label, message, color string) Badge { - lw := float64(drawer.MeasureString(label)>>6) + defaultOffset - mw := float64(drawer.MeasureString(message)>>6) + defaultOffset - x := int((lw + (mw / 2) - 1) * 10) + lw := defaultFontWidth*len(label) + defaultOffset + mw := defaultFontWidth*len(message) + defaultOffset + x := lw*10 + mw*5 - 10 return Badge{ Label: Label{ text: label, From c5612a6d9eb060ae8d91f41eba36484b2ef5e015 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Mon, 26 Feb 2024 01:17:52 +0800 Subject: [PATCH 20/24] fix lint --- 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 0548646436f71..cb031415ace8a 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -17,7 +17,7 @@ func (l Label) Text() string { } func (l Label) Width() int { - return int(l.width) + return l.width } func (l Label) TextLength() int { From aea7e85fb2316fdc46ef1b08541247b5dd4be593 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Tue, 27 Feb 2024 15:25:50 +0800 Subject: [PATCH 21/24] Fix package import path in recent merged PR --- 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 3f74da82e57d2..6fa951826c31b 100644 --- a/routers/web/repo/actions/badge.go +++ b/routers/web/repo/actions/badge.go @@ -12,8 +12,8 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/modules/badge" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) func GetWorkflowBadge(ctx *context.Context) { From 76e35163c26d4278a71fac9081521a751dabe560 Mon Sep 17 00:00:00 2001 From: Nanguan Lin Date: Tue, 27 Feb 2024 18:09:30 +0800 Subject: [PATCH 22/24] Update docs/content/usage/badge.en-us.md Co-authored-by: delvh --- docs/content/usage/badge.en-us.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/usage/badge.en-us.md b/docs/content/usage/badge.en-us.md index 2e060a984b706..212134e01c173 100644 --- a/docs/content/usage/badge.en-us.md +++ b/docs/content/usage/badge.en-us.md @@ -21,7 +21,8 @@ Gitea has its builtin Badge system which allows you to display the status of you ## Workflow Badge -Gitea Actions workflow badge is a badge that shows the status of the latest workflow run. It designed to be compatible with [GitHub Actions workflow badge](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge). +The Gitea Actions workflow badge is a badge that shows the status of the latest workflow run. +It is designed to be compatible with [GitHub Actions workflow badge](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge). You can use the following URL to get the badge: From f1dfad8360139400ab0f80c3e458a15ace785a62 Mon Sep 17 00:00:00 2001 From: lng2020 Date: Tue, 27 Feb 2024 18:32:13 +0800 Subject: [PATCH 23/24] Add comment to explain the badge layout --- modules/badge/badge.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/badge/badge.go b/modules/badge/badge.go index cb031415ace8a..bea1fdb984c2c 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -7,6 +7,10 @@ import ( actions_model "code.gitea.io/gitea/models/actions" ) +// The Badge layout: |offset|label|message| +// We use 10x scale to calculate more precisely +// Then scale down to noraml size in tmpl file + type Label struct { text string width int From 815dee897d4840638ba19eea87112be598113cf4 Mon Sep 17 00:00:00 2001 From: Nanguan Lin Date: Tue, 27 Feb 2024 18:38:28 +0800 Subject: [PATCH 24/24] Update modules/badge/badge.go Co-authored-by: delvh --- 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 bea1fdb984c2c..b30d0b472945f 100644 --- a/modules/badge/badge.go +++ b/modules/badge/badge.go @@ -9,7 +9,7 @@ import ( // The Badge layout: |offset|label|message| // We use 10x scale to calculate more precisely -// Then scale down to noraml size in tmpl file +// Then scale down to normal size in tmpl file type Label struct { text string