From cc84f633c2347d22a7cfa5f12128452435177fb1 Mon Sep 17 00:00:00 2001
From: David May <1301201+wass3r@users.noreply.github.com>
Date: Tue, 18 Feb 2020 16:59:07 +0000
Subject: [PATCH] enhancement(badge): add ability to target branch (#106)
---
api/badge.go | 69 ++++++++-----------------
database/build.go | 21 ++++++++
database/build_test.go | 71 ++++++++++++++++++++++++++
database/database.go | 3 ++
database/dml/dml_test.go | 2 +
database/dml/postgres/build.go | 13 +++++
database/dml/postgres/build_test.go | 1 +
database/dml/postgres/postgres_test.go | 1 +
database/dml/postgres_test.go | 2 +
database/dml/sqlite/build.go | 12 +++++
database/dml/sqlite/build_test.go | 1 +
database/dml/sqlite/sqlite_test.go | 1 +
database/dml/sqlite_test.go | 2 +
go.mod | 2 +-
go.sum | 2 +
router/router.go | 2 +-
16 files changed, 154 insertions(+), 51 deletions(-)
diff --git a/api/badge.go b/api/badge.go
index da50fef57..2dc195727 100644
--- a/api/badge.go
+++ b/api/badge.go
@@ -5,8 +5,6 @@
package api
import (
- "bytes"
- "html/template"
"net/http"
"github.com/go-vela/server/database"
@@ -17,71 +15,44 @@ import (
"github.com/gin-gonic/gin"
)
-// Badge represents the API handler to
+// GetBadge represents the API handler to
// return a build status badge.
-func Badge(c *gin.Context) {
- // TODO: allow getting lastbuild by branch and then allow query via `?branch=...`
+func GetBadge(c *gin.Context) {
// capture middleware values
r := repo.Retrieve(c)
+ branch := c.DefaultQuery("branch", r.GetBranch())
- logrus.Infof("Creating badge for latest build on %s", r.GetFullName())
+ logrus.Infof("Creating badge for latest build on %s for branch %s", r.GetFullName(), branch)
- // set default badge
- badge := buildBadge("unknown", "#9f9f9f")
-
- // send API call to capture the last build for the repo
- b, err := database.FromContext(c).GetLastBuild(r)
+ // send API call to capture the last build for the repo and branch
+ b, err := database.FromContext(c).GetLastBuildByBranch(r, branch)
if err != nil {
- c.String(http.StatusOK, badge)
+ c.String(http.StatusOK, constants.BadgeUnknown)
return
}
+ badge := badgeForStatus(b.GetStatus())
+
// set headers to prevent caching
c.Header("Content-Type", "image/svg+xml")
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.Header("Expires", "0") // passing invalid date sets resource as expired
- switch b.GetStatus() {
+ c.String(http.StatusOK, badge)
+}
+
+// badgeForStatus is a helper to match the build status with a badge
+func badgeForStatus(s string) string {
+ switch s {
case constants.StatusRunning, constants.StatusPending:
- badge = buildBadge("running", "#dfb317")
+ return constants.BadgeRunning
case constants.StatusFailure, constants.StatusKilled:
- badge = buildBadge("failed", "#e05d44")
+ return constants.BadgeFailed
case constants.StatusSuccess:
- badge = buildBadge("success", "#44cc11")
+ return constants.BadgeSuccess
case constants.StatusError:
- badge = buildBadge("error", "#fe7d37")
+ return constants.BadgeError
default:
- c.String(http.StatusOK, badge)
- return
- }
-
- c.String(http.StatusOK, badge)
-}
-
-// buildBadge is a helper that actually builds creates the SVG for the badge
-func buildBadge(title, color string) string {
- const (
- t = ``
- tFallback = ``
- )
-
- tmpl, err := template.New("StatusBadge").Parse(t)
- if err != nil {
- return tFallback
+ return constants.BadgeUnknown
}
-
- buffer := &bytes.Buffer{}
-
- err = tmpl.Execute(buffer, struct {
- Title string
- Color string
- }{
- title,
- color,
- })
- if err != nil {
- return tFallback
- }
-
- return buffer.String()
}
diff --git a/database/build.go b/database/build.go
index a2b6d41f9..db6f7c26f 100644
--- a/database/build.go
+++ b/database/build.go
@@ -50,6 +50,27 @@ func (c *client) GetLastBuild(r *library.Repo) (*library.Build, error) {
return b.ToLibrary(), err
}
+// GetLastBuildByBranch gets the last build ran by repo ID and branch from the database.
+func (c *client) GetLastBuildByBranch(r *library.Repo, branch string) (*library.Build, error) {
+ logrus.Tracef("Getting last build for repo %s from the database", r.GetFullName())
+
+ // variable to store query results
+ b := new(database.Build)
+
+ // send query to the database and store result in variable
+ err := c.Database.
+ Table(constants.TableBuild).
+ Raw(c.DML.BuildService.Select["lastByBranch"], r.GetID(), branch).
+ Scan(b).Error
+
+ // the record will not exist if it's a new repo
+ if gorm.IsRecordNotFoundError(err) {
+ return nil, nil
+ }
+
+ return b.ToLibrary(), err
+}
+
// GetBuildCount gets the count of all builds from the database.
func (c *client) GetBuildCount() (int64, error) {
logrus.Trace("Count of builds from the database")
diff --git a/database/build_test.go b/database/build_test.go
index 0cea33e77..5b15bcf5a 100644
--- a/database/build_test.go
+++ b/database/build_test.go
@@ -129,6 +129,77 @@ func TestDatabase_Client_GetLastBuild_NotFound(t *testing.T) {
}
}
+func TestDatabase_Client_GetLastBuildByBranch(t *testing.T) {
+ // setup types
+ r := testRepo()
+ r.SetID(1)
+ r.SetOrg("foo")
+ r.SetName("bar")
+ r.SetFullName("foo/bar")
+
+ want := testBuild()
+ want.SetID(1)
+ want.SetRepoID(1)
+ want.SetNumber(1)
+ want.SetBranch("pr42")
+
+ b := testBuild()
+ b.SetID(2)
+ b.SetRepoID(1)
+ b.SetNumber(2)
+ b.SetBranch("master")
+
+ // setup database
+ database, _ := NewTest()
+
+ defer func() {
+ database.Database.Exec("delete from builds;")
+ database.Database.Close()
+ }()
+
+ _ = database.CreateBuild(want)
+ _ = database.CreateBuild(b)
+
+ // run test
+ got, err := database.GetLastBuildByBranch(r, "pr42")
+
+ if err != nil {
+ t.Errorf("GetLastBuild returned err: %v", err)
+ }
+
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("GetLastBuild is %v, want %v", got, want)
+ }
+}
+
+func TestDatabase_Client_GetLastBuildByBranch_NotFound(t *testing.T) {
+ // setup types
+ r := testRepo()
+ r.SetID(1)
+ r.SetOrg("foo")
+ r.SetName("bar")
+ r.SetFullName("foo/bar")
+
+ // setup database
+ database, _ := NewTest()
+
+ defer func() {
+ database.Database.Exec("delete from builds;")
+ database.Database.Close()
+ }()
+
+ // run test
+ got, err := database.GetLastBuildByBranch(r, "pr42")
+
+ if err != nil {
+ t.Errorf("GetLastBuild returned err: %v", err)
+ }
+
+ if got != nil {
+ t.Errorf("GetLastBuild is %v, want nil", got)
+ }
+}
+
func TestDatabase_Client_GetBuildList(t *testing.T) {
// setup types
bOne := testBuild()
diff --git a/database/database.go b/database/database.go
index efc302222..210182533 100644
--- a/database/database.go
+++ b/database/database.go
@@ -19,6 +19,9 @@ type Service interface {
// GetLastBuild defines a function that
// gets the last build ran by repo ID.
GetLastBuild(*library.Repo) (*library.Build, error)
+ // GetLastBuildByBranch defines a function that
+ // gets the last build ran by repo ID and branch.
+ GetLastBuildByBranch(*library.Repo, string) (*library.Build, error)
// GetBuildCount defines a function that
// gets the count of builds.
GetBuildCount() (int64, error)
diff --git a/database/dml/dml_test.go b/database/dml/dml_test.go
index 2b544c4b5..19f6bd1af 100644
--- a/database/dml/dml_test.go
+++ b/database/dml/dml_test.go
@@ -26,6 +26,7 @@ func TestDML_NewMap_Postgres(t *testing.T) {
Select: map[string]string{
"repo": postgres.SelectRepoBuild,
"last": postgres.SelectLastRepoBuild,
+ "lastByBranch": postgres.SelectLastRepoBuildByBranch,
"count": postgres.SelectBuildsCount,
"countByStatus": postgres.SelectBuildsCountByStatus,
"countByRepo": postgres.SelectRepoBuildCount,
@@ -148,6 +149,7 @@ func TestDML_NewMap_Sqlite(t *testing.T) {
Select: map[string]string{
"repo": sqlite.SelectRepoBuild,
"last": sqlite.SelectLastRepoBuild,
+ "lastByBranch": sqlite.SelectLastRepoBuildByBranch,
"count": sqlite.SelectBuildsCount,
"countByStatus": sqlite.SelectBuildsCountByStatus,
"countByRepo": sqlite.SelectRepoBuildCount,
diff --git a/database/dml/postgres/build.go b/database/dml/postgres/build.go
index 902e9bb2f..97038ca63 100644
--- a/database/dml/postgres/build.go
+++ b/database/dml/postgres/build.go
@@ -54,6 +54,18 @@ FROM builds
WHERE repo_id = $1
ORDER BY number DESC
LIMIT 1;
+`
+
+ // SelectLastRepoBuildByBranch represents a query to
+ // select the last build for a repo_id and branch name
+ // in the database
+ SelectLastRepoBuildByBranch = `
+SELECT *
+FROM builds
+WHERE repo_id = $1
+AND branch = $2
+ORDER BY number DESC
+LIMIT 1;
`
// SelectBuildsCount represents a query to select
@@ -109,6 +121,7 @@ func createBuildService() *Service {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/postgres/build_test.go b/database/dml/postgres/build_test.go
index 048f07ae2..26d09456d 100644
--- a/database/dml/postgres/build_test.go
+++ b/database/dml/postgres/build_test.go
@@ -20,6 +20,7 @@ func TestPostgres_createBuildService(t *testing.T) {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/postgres/postgres_test.go b/database/dml/postgres/postgres_test.go
index ce8ab15ab..ff31aa126 100644
--- a/database/dml/postgres/postgres_test.go
+++ b/database/dml/postgres/postgres_test.go
@@ -21,6 +21,7 @@ func TestPostgres_NewMap(t *testing.T) {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/postgres_test.go b/database/dml/postgres_test.go
index a88fcfa22..471452b49 100644
--- a/database/dml/postgres_test.go
+++ b/database/dml/postgres_test.go
@@ -23,6 +23,7 @@ func TestDML_mapFromPostgres(t *testing.T) {
Select: map[string]string{
"repo": postgres.SelectRepoBuild,
"last": postgres.SelectLastRepoBuild,
+ "lastByBranch": postgres.SelectLastRepoBuildByBranch,
"count": postgres.SelectBuildsCount,
"countByStatus": postgres.SelectBuildsCountByStatus,
"countByRepo": postgres.SelectRepoBuildCount,
@@ -141,6 +142,7 @@ func TestDML_serviceFromPostgres(t *testing.T) {
Select: map[string]string{
"repo": postgres.SelectRepoBuild,
"last": postgres.SelectLastRepoBuild,
+ "lastByBranch": postgres.SelectLastRepoBuildByBranch,
"count": postgres.SelectBuildsCount,
"countByStatus": postgres.SelectBuildsCountByStatus,
"countByRepo": postgres.SelectRepoBuildCount,
diff --git a/database/dml/sqlite/build.go b/database/dml/sqlite/build.go
index 2d7ccff98..f623a1af7 100644
--- a/database/dml/sqlite/build.go
+++ b/database/dml/sqlite/build.go
@@ -54,6 +54,17 @@ FROM builds
WHERE repo_id = ?
ORDER BY number DESC
LIMIT 1;
+`
+ // SelectLastRepoBuildByBranch represents a query to
+ // select the last build for a repo_id and branch name
+ // in the database
+ SelectLastRepoBuildByBranch = `
+SELECT *
+FROM builds
+WHERE repo_id = ?
+AND branch = ?
+ORDER BY number DESC
+LIMIT 1;
`
// SelectBuildsCount represents a query to select
@@ -109,6 +120,7 @@ func createBuildService() *Service {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/sqlite/build_test.go b/database/dml/sqlite/build_test.go
index 4e569ca86..ae6da8db1 100644
--- a/database/dml/sqlite/build_test.go
+++ b/database/dml/sqlite/build_test.go
@@ -20,6 +20,7 @@ func TestSqlite_createBuildService(t *testing.T) {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/sqlite/sqlite_test.go b/database/dml/sqlite/sqlite_test.go
index 82d8b8af2..d0bceb392 100644
--- a/database/dml/sqlite/sqlite_test.go
+++ b/database/dml/sqlite/sqlite_test.go
@@ -21,6 +21,7 @@ func TestSqlite_NewMap(t *testing.T) {
Select: map[string]string{
"repo": SelectRepoBuild,
"last": SelectLastRepoBuild,
+ "lastByBranch": SelectLastRepoBuildByBranch,
"count": SelectBuildsCount,
"countByStatus": SelectBuildsCountByStatus,
"countByRepo": SelectRepoBuildCount,
diff --git a/database/dml/sqlite_test.go b/database/dml/sqlite_test.go
index da65815b7..34f7990e8 100644
--- a/database/dml/sqlite_test.go
+++ b/database/dml/sqlite_test.go
@@ -23,6 +23,7 @@ func TestDML_mapFromSqlite(t *testing.T) {
Select: map[string]string{
"repo": sqlite.SelectRepoBuild,
"last": sqlite.SelectLastRepoBuild,
+ "lastByBranch": sqlite.SelectLastRepoBuildByBranch,
"count": sqlite.SelectBuildsCount,
"countByStatus": sqlite.SelectBuildsCountByStatus,
"countByRepo": sqlite.SelectRepoBuildCount,
@@ -141,6 +142,7 @@ func TestDML_serviceFromSqlite(t *testing.T) {
Select: map[string]string{
"repo": sqlite.SelectRepoBuild,
"last": sqlite.SelectLastRepoBuild,
+ "lastByBranch": sqlite.SelectLastRepoBuildByBranch,
"count": sqlite.SelectBuildsCount,
"countByStatus": sqlite.SelectBuildsCountByStatus,
"countByRepo": sqlite.SelectRepoBuildCount,
diff --git a/go.mod b/go.mod
index 96fbcbdc6..8c476637d 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
github.com/gin-gonic/gin v1.5.0
github.com/go-redis/redis v6.15.6+incompatible
github.com/go-vela/compiler v0.3.0-rc2
- github.com/go-vela/types v0.3.0-rc2
+ github.com/go-vela/types v0.3.0-rc3
github.com/google/go-github/v29 v29.0.3
github.com/google/uuid v1.1.1
github.com/hashicorp/go-hclog v0.10.0 // indirect
diff --git a/go.sum b/go.sum
index cbd07c762..a945b253f 100644
--- a/go.sum
+++ b/go.sum
@@ -90,6 +90,8 @@ github.com/go-vela/compiler v0.3.0-rc2 h1:S+KbcDshQabpwOhbeyol3B+WNml6l5TIPsLMXn
github.com/go-vela/compiler v0.3.0-rc2/go.mod h1:58jRH4hlIu3dKCImBzTIbpM+RLdLL1I0qsDYpinCMgQ=
github.com/go-vela/types v0.3.0-rc2 h1:FrDTX01K+2M6ertsv9GUAP76YWQANdcrA2N6oBIRX+E=
github.com/go-vela/types v0.3.0-rc2/go.mod h1:LNDrn7/nV38H0HmRfYv6YfPNb881R5NRE1YKXeVqpF4=
+github.com/go-vela/types v0.3.0-rc3 h1:G7+eKZBjN1cCvAKC2LzoDIDkmooLFfUoQPwt/7fT9AM=
+github.com/go-vela/types v0.3.0-rc3/go.mod h1:LNDrn7/nV38H0HmRfYv6YfPNb881R5NRE1YKXeVqpF4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
diff --git a/router/router.go b/router/router.go
index 433f79f86..9156ef449 100644
--- a/router/router.go
+++ b/router/router.go
@@ -31,7 +31,7 @@ func Load(options ...gin.HandlerFunc) *gin.Engine {
r.Use(options...)
// Badge endpoint
- r.GET("/badge/:org/:repo/status.svg", repo.Establish(), api.Badge)
+ r.GET("/badge/:org/:repo/status.svg", repo.Establish(), api.GetBadge)
// Health endpoint
r.GET("/health", api.Health)