[GITEA] Add support for shields.io-based badges
Adds a new `/{username}/{repo}/badges` family of routes, which redirect to various shields.io badges. The goal is to not reimplement badge generation, and delegate it to shields.io (or a similar service), which are already used by many. This way, we get all the goodies that come with it: different styles, colors, logos, you name it. So these routes are just thin wrappers around shields.io that make it easier to display the information we want. The URL is configurable via `app.ini`, and is templatable, allowing to use alternative badge generator services with slightly different URL patterns. Additionally, for compatibility with GitHub, there's an `/{username}/{repo}/actions/workflows/{workflow_file}/badge.svg` route that works much the same way as on GitHub. Change the hostname in the URL, and done. Fixes gitea#5633, gitea#23688, and also fixes #126. Work sponsored by Codeberg e.V. Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu> (cherry picked from commit fcd0f61212d8febd4bdfc27e61a4e13cbdd16d49) (cherry picked from commit 20d14f784490a880c51ca0f0a6a5988a01887635) (cherry picked from commit 4359741431bb39de4cf24de8b0cfb513f5233f55) (cherry picked from commit 35cff45eb86177e750cd22e82a201880a5efe045) (cherry picked from commit 2fc0d0b8a302d24177a00ab48b42ce083b52e506)
This commit is contained in:
parent
92f41d11dd
commit
f90b802634
|
@ -912,6 +912,14 @@ LEVEL = Info
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[badges]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Enable repository badges (via shields.io or a similar generator)
|
||||||
|
;ENABLED = true
|
||||||
|
;; Template for the badge generator.
|
||||||
|
;GENERATOR_URL_TEMPLATE = https://img.shields.io/badge/{{.label}}-{{.text}}-{{.color}}
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[repository]
|
;[repository]
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -323,6 +323,21 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) {
|
||||||
return &run, nil
|
return &run, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, workflowFile, event string) (*ActionRun, error) {
|
||||||
|
var run ActionRun
|
||||||
|
q := db.GetEngine(ctx).Where("repo_id=?", repoID).And("ref=?", branch).And("workflow_id=?", workflowFile)
|
||||||
|
if event != "" {
|
||||||
|
q = q.And("event=?", event)
|
||||||
|
}
|
||||||
|
has, err := q.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
|
||||||
|
}
|
||||||
|
|
||||||
func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
|
func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
|
||||||
var run ActionRun
|
var run ActionRun
|
||||||
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
|
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
|
||||||
|
|
24
modules/setting/badges.go
Normal file
24
modules/setting/badges.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Badges settings
|
||||||
|
var Badges = struct {
|
||||||
|
Enabled bool `ini:"ENABLED"`
|
||||||
|
GeneratorURLTemplate string `ini:"GENERATOR_URL_TEMPLATE"`
|
||||||
|
GeneratorURLTemplateTemplate *template.Template `ini:"-"`
|
||||||
|
}{
|
||||||
|
Enabled: true,
|
||||||
|
GeneratorURLTemplate: "https://img.shields.io/badge/{{.label}}-{{.text}}-{{.color}}",
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBadgesFrom(rootCfg ConfigProvider) {
|
||||||
|
mustMapSetting(rootCfg, "badges", &Badges)
|
||||||
|
|
||||||
|
Badges.GeneratorURLTemplateTemplate = template.Must(template.New("").Parse(Badges.GeneratorURLTemplate))
|
||||||
|
}
|
|
@ -147,6 +147,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error {
|
||||||
loadUIFrom(cfg)
|
loadUIFrom(cfg)
|
||||||
loadAdminFrom(cfg)
|
loadAdminFrom(cfg)
|
||||||
loadAPIFrom(cfg)
|
loadAPIFrom(cfg)
|
||||||
|
loadBadgesFrom(cfg)
|
||||||
loadMetricsFrom(cfg)
|
loadMetricsFrom(cfg)
|
||||||
loadCamoFrom(cfg)
|
loadCamoFrom(cfg)
|
||||||
loadI18nFrom(cfg)
|
loadI18nFrom(cfg)
|
||||||
|
|
165
routers/web/repo/badges/badges.go
Normal file
165
routers/web/repo/badges/badges.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package badges
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
context_module "code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getBadgeURL(ctx *context_module.Context, label, text, color string) string {
|
||||||
|
sb := &strings.Builder{}
|
||||||
|
_ = setting.Badges.GeneratorURLTemplateTemplate.Execute(sb, map[string]string{
|
||||||
|
"label": url.PathEscape(label),
|
||||||
|
"text": url.PathEscape(text),
|
||||||
|
"color": url.PathEscape(color),
|
||||||
|
})
|
||||||
|
|
||||||
|
badgeURL := sb.String()
|
||||||
|
q := ctx.Req.URL.Query()
|
||||||
|
// Remove any `branch` or `event` query parameters. They're used by the
|
||||||
|
// workflow badge route, and do not need forwarding to the badge generator.
|
||||||
|
delete(q, "branch")
|
||||||
|
delete(q, "event")
|
||||||
|
if len(q) > 0 {
|
||||||
|
return fmt.Sprintf("%s?%s", badgeURL, q.Encode())
|
||||||
|
}
|
||||||
|
return badgeURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectToBadge(ctx *context_module.Context, label, text, color string) {
|
||||||
|
ctx.Redirect(getBadgeURL(ctx, label, text, color))
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorBadge(ctx *context_module.Context, label, text string) {
|
||||||
|
ctx.Redirect(getBadgeURL(ctx, label, text, "crimson"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWorkflowBadge(ctx *context_module.Context) {
|
||||||
|
branch := ctx.Req.URL.Query().Get("branch")
|
||||||
|
if branch == "" {
|
||||||
|
branch = ctx.Repo.Repository.DefaultBranch
|
||||||
|
}
|
||||||
|
branch = fmt.Sprintf("refs/heads/%s", branch)
|
||||||
|
event := ctx.Req.URL.Query().Get("event")
|
||||||
|
|
||||||
|
workflowFile := ctx.Params("workflow_name")
|
||||||
|
run, err := actions_model.GetLatestRunForBranchAndWorkflow(ctx, ctx.Repo.Repository.ID, branch, workflowFile, event)
|
||||||
|
if err != nil {
|
||||||
|
errorBadge(ctx, workflowFile, "Not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var color string
|
||||||
|
switch run.Status {
|
||||||
|
case actions_model.StatusUnknown:
|
||||||
|
color = "lightgrey"
|
||||||
|
case actions_model.StatusWaiting:
|
||||||
|
color = "lightgrey"
|
||||||
|
case actions_model.StatusRunning:
|
||||||
|
color = "gold"
|
||||||
|
case actions_model.StatusSuccess:
|
||||||
|
color = "brightgreen"
|
||||||
|
case actions_model.StatusFailure:
|
||||||
|
color = "crimson"
|
||||||
|
case actions_model.StatusCancelled:
|
||||||
|
color = "orange"
|
||||||
|
case actions_model.StatusSkipped:
|
||||||
|
color = "blue"
|
||||||
|
case actions_model.StatusBlocked:
|
||||||
|
color = "yellow"
|
||||||
|
default:
|
||||||
|
color = "lightgrey"
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToBadge(ctx, workflowFile, run.Status.String(), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIssueOrPullBadge(ctx *context_module.Context, label, variant string, num int) {
|
||||||
|
var text string
|
||||||
|
if len(variant) > 0 {
|
||||||
|
text = fmt.Sprintf("%d %s", num, variant)
|
||||||
|
} else {
|
||||||
|
text = fmt.Sprintf("%d", num)
|
||||||
|
}
|
||||||
|
redirectToBadge(ctx, label, text, "blue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIssueBadge(ctx *context_module.Context, variant string, num int) {
|
||||||
|
if !ctx.Repo.CanRead(unit.TypeIssues) &&
|
||||||
|
!ctx.Repo.CanRead(unit.TypeExternalTracker) {
|
||||||
|
errorBadge(ctx, "issues", "Not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker)
|
||||||
|
if err == nil {
|
||||||
|
errorBadge(ctx, "issues", "Not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getIssueOrPullBadge(ctx, "issues", variant, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPullBadge(ctx *context_module.Context, variant string, num int) {
|
||||||
|
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
|
||||||
|
errorBadge(ctx, "pulls", "Not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getIssueOrPullBadge(ctx, "pulls", variant, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOpenIssuesBadge(ctx *context_module.Context) {
|
||||||
|
getIssueBadge(ctx, "open", ctx.Repo.Repository.NumOpenIssues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClosedIssuesBadge(ctx *context_module.Context) {
|
||||||
|
getIssueBadge(ctx, "closed", ctx.Repo.Repository.NumClosedIssues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTotalIssuesBadge(ctx *context_module.Context) {
|
||||||
|
getIssueBadge(ctx, "", ctx.Repo.Repository.NumIssues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOpenPullsBadge(ctx *context_module.Context) {
|
||||||
|
getPullBadge(ctx, "open", ctx.Repo.Repository.NumOpenPulls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClosedPullsBadge(ctx *context_module.Context) {
|
||||||
|
getPullBadge(ctx, "closed", ctx.Repo.Repository.NumClosedPulls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTotalPullsBadge(ctx *context_module.Context) {
|
||||||
|
getPullBadge(ctx, "", ctx.Repo.Repository.NumPulls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStarsBadge(ctx *context_module.Context) {
|
||||||
|
redirectToBadge(ctx, "stars", fmt.Sprintf("%d", ctx.Repo.Repository.NumStars), "blue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLatestReleaseBadge(ctx *context_module.Context) {
|
||||||
|
release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||||
|
if err != nil {
|
||||||
|
if repo_model.IsErrReleaseNotExist(err) {
|
||||||
|
errorBadge(ctx, "release", "Not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.ServerError("GetLatestReleaseByRepoID", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := release.LoadAttributes(ctx); err != nil {
|
||||||
|
ctx.ServerError("LoadAttributes", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToBadge(ctx, "release", release.TagName, "blue")
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import (
|
||||||
org_setting "code.gitea.io/gitea/routers/web/org/setting"
|
org_setting "code.gitea.io/gitea/routers/web/org/setting"
|
||||||
"code.gitea.io/gitea/routers/web/repo"
|
"code.gitea.io/gitea/routers/web/repo"
|
||||||
"code.gitea.io/gitea/routers/web/repo/actions"
|
"code.gitea.io/gitea/routers/web/repo/actions"
|
||||||
|
"code.gitea.io/gitea/routers/web/repo/badges"
|
||||||
repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
|
repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
|
||||||
"code.gitea.io/gitea/routers/web/user"
|
"code.gitea.io/gitea/routers/web/user"
|
||||||
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
||||||
|
@ -1316,6 +1317,24 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Get("/packages", repo.Packages)
|
m.Get("/packages", repo.Packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.Badges.Enabled {
|
||||||
|
m.Group("/badges", func() {
|
||||||
|
m.Get("/workflows/{workflow_name}/badge.svg", badges.GetWorkflowBadge)
|
||||||
|
m.Group("/issues", func() {
|
||||||
|
m.Get(".svg", badges.GetTotalIssuesBadge)
|
||||||
|
m.Get("/open.svg", badges.GetOpenIssuesBadge)
|
||||||
|
m.Get("/closed.svg", badges.GetClosedIssuesBadge)
|
||||||
|
})
|
||||||
|
m.Group("/pulls", func() {
|
||||||
|
m.Get(".svg", badges.GetTotalPullsBadge)
|
||||||
|
m.Get("/open.svg", badges.GetOpenPullsBadge)
|
||||||
|
m.Get("/closed.svg", badges.GetClosedPullsBadge)
|
||||||
|
})
|
||||||
|
m.Get("/stars.svg", badges.GetStarsBadge)
|
||||||
|
m.Get("/release.svg", badges.GetLatestReleaseBadge)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
m.Group("/projects", func() {
|
m.Group("/projects", func() {
|
||||||
m.Get("", repo.Projects)
|
m.Get("", repo.Projects)
|
||||||
m.Get("/{id}", repo.ViewProject)
|
m.Get("/{id}", repo.ViewProject)
|
||||||
|
@ -1367,6 +1386,8 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
m.Get("/workflows/{workflow_name}/badge.svg", badges.GetWorkflowBadge)
|
||||||
}, reqRepoActionsReader, actions.MustEnableActions)
|
}, reqRepoActionsReader, actions.MustEnableActions)
|
||||||
|
|
||||||
m.Group("/wiki", func() {
|
m.Group("/wiki", func() {
|
||||||
|
|
237
tests/integration/repo_badges_test.go
Normal file
237
tests/integration/repo_badges_test.go
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
unit_model "code.gitea.io/gitea/models/unit"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func assertBadge(t *testing.T, resp *httptest.ResponseRecorder, badge string) {
|
||||||
|
assert.Equal(t, fmt.Sprintf("https://img.shields.io/badge/%s", badge), test.RedirectURL(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMinimalRepo(t *testing.T) func() {
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
|
||||||
|
// Create a new repository
|
||||||
|
repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
|
||||||
|
Name: "minimal",
|
||||||
|
Description: "minimal repo for badge testing",
|
||||||
|
AutoInit: true,
|
||||||
|
Gitignores: "Go",
|
||||||
|
License: "MIT",
|
||||||
|
Readme: "Default",
|
||||||
|
DefaultBranch: "main",
|
||||||
|
IsPrivate: false,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, repo)
|
||||||
|
|
||||||
|
// Enable Actions, and disable Issues, PRs and Releases
|
||||||
|
err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: unit_model.TypeActions,
|
||||||
|
}}, []unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests, unit_model.TypeReleases})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
repo_service.DeleteRepository(db.DefaultContext, user2, repo, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addWorkflow(t *testing.T) {
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "minimal")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Add a workflow file to the repo
|
||||||
|
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
|
||||||
|
Files: []*files_service.ChangeRepoFile{
|
||||||
|
{
|
||||||
|
Operation: "create",
|
||||||
|
TreePath: ".gitea/workflows/pr.yml",
|
||||||
|
ContentReader: strings.NewReader("name: test\non:\n push:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Message: "add workflow",
|
||||||
|
OldBranch: "main",
|
||||||
|
NewBranch: "main",
|
||||||
|
Author: &files_service.IdentityOptions{
|
||||||
|
Name: user2.Name,
|
||||||
|
Email: user2.Email,
|
||||||
|
},
|
||||||
|
Committer: &files_service.IdentityOptions{
|
||||||
|
Name: user2.Name,
|
||||||
|
Email: user2.Email,
|
||||||
|
},
|
||||||
|
Dates: &files_service.CommitDateOptions{
|
||||||
|
Author: time.Now(),
|
||||||
|
Committer: time.Now(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, addWorkflowToBaseResp)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkflowBadges(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
defer createMinimalRepo(t)()
|
||||||
|
|
||||||
|
addWorkflow(t)
|
||||||
|
|
||||||
|
// Actions disabled
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg")
|
||||||
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "test.yaml-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg?branch=no-such-branch")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "test.yaml-Not%20found-crimson")
|
||||||
|
|
||||||
|
// Actions enabled
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-waiting-lightgrey")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?branch=main")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-waiting-lightgrey")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?branch=no-such-branch")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?event=cron")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-Not%20found-crimson")
|
||||||
|
|
||||||
|
// GitHub compatibility
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-waiting-lightgrey")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?branch=main")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-waiting-lightgrey")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?branch=no-such-branch")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?event=cron")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pr.yml-Not%20found-crimson")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadges(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
t.Run("Stars", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/badges/stars.svg")
|
||||||
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
assertBadge(t, resp, "stars-0-blue")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Issues", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
defer createMinimalRepo(t)()
|
||||||
|
|
||||||
|
// Issues enabled
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/badges/issues.svg")
|
||||||
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-2-blue")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/repo1/badges/issues/open.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-1%20open-blue")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/repo1/badges/issues/closed.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-1%20closed-blue")
|
||||||
|
|
||||||
|
// Issues disabled
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/issues.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/issues/open.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/issues/closed.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "issues-Not%20found-crimson")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pulls", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
defer createMinimalRepo(t)()
|
||||||
|
|
||||||
|
// Pull requests enabled
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/badges/pulls.svg")
|
||||||
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-3-blue")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/open.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-3%20open-blue")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/closed.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-0%20closed-blue")
|
||||||
|
|
||||||
|
// Pull requests disabled
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/pulls.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/pulls/open.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-Not%20found-crimson")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/pulls/closed.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "pulls-Not%20found-crimson")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Release", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
defer createMinimalRepo(t)()
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/badges/release.svg")
|
||||||
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "release-v1.1-blue")
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/minimal/badges/release.svg")
|
||||||
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
assertBadge(t, resp, "release-Not%20found-crimson")
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue