From c350af645b51546914b5d106176a8e3461a0d629 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 14 May 2022 14:30:09 +0200 Subject: [PATCH] Move Badge & CCMenue into own packages (#908) * move badge generation into own package * move ccmenue away from models * fix misspell --- server/api/badge.go | 36 ++++++----------------- server/badges/badges.go | 41 +++++++++++++++++++++++++++ server/{model => ccmenu}/cc.go | 22 ++++++++++----- server/{model => ccmenu}/cc_test.go | 44 +++++++++++++++-------------- server/store/datastore/build.go | 2 +- 5 files changed, 88 insertions(+), 57 deletions(-) create mode 100644 server/badges/badges.go rename server/{model => ccmenu}/cc.go (75%) rename server/{model => ccmenu}/cc_test.go (73%) diff --git a/server/api/badge.go b/server/api/badge.go index 5dc5535bc..7aeaaa731 100644 --- a/server/api/badge.go +++ b/server/api/badge.go @@ -19,24 +19,18 @@ package api import ( "fmt" + "net/http" "github.com/rs/zerolog/log" "github.com/gin-gonic/gin" "github.com/woodpecker-ci/woodpecker/server" - "github.com/woodpecker-ci/woodpecker/server/model" + "github.com/woodpecker-ci/woodpecker/server/badges" + "github.com/woodpecker-ci/woodpecker/server/ccmenu" "github.com/woodpecker-ci/woodpecker/server/store" ) -var ( - badgeSuccess = `buildbuildsuccesssuccess` - badgeFailure = `buildbuildfailurefailure` - badgeStarted = `buildbuildstartedstarted` - badgeError = `buildbuilderrorerror` - badgeNone = `buildbuildnonenone` -) - func GetBadge(c *gin.Context) { _store := store.FromContext(c) repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name")) @@ -45,10 +39,6 @@ func GetBadge(c *gin.Context) { return } - // an SVG response is always served, even when error, so - // we can go ahead and set the content type appropriately. - c.Writer.Header().Set("Content-Type", "image/svg+xml") - // if no commit was found then display // the 'none' badge, instead of throwing // an error response @@ -60,22 +50,12 @@ func GetBadge(c *gin.Context) { build, err := _store.GetBuildLast(repo, branch) if err != nil { log.Warn().Err(err).Msg("") - c.String(200, badgeNone) - return + build = nil } - switch build.Status { - case model.StatusSuccess: - c.String(200, badgeSuccess) - case model.StatusFailure: - c.String(200, badgeFailure) - case model.StatusError, model.StatusKilled: - c.String(200, badgeError) - case model.StatusPending, model.StatusRunning: - c.String(200, badgeStarted) - default: - c.String(200, badgeNone) - } + // we serve an SVG, so set content type appropriately. + c.Writer.Header().Set("Content-Type", "image/svg+xml") + c.String(http.StatusOK, badges.Generate(build)) } func GetCC(c *gin.Context) { @@ -93,6 +73,6 @@ func GetCC(c *gin.Context) { } url := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, builds[0].Number) - cc := model.NewCC(repo, builds[0], url) + cc := ccmenu.New(repo, builds[0], url) c.XML(200, cc) } diff --git a/server/badges/badges.go b/server/badges/badges.go new file mode 100644 index 000000000..9364a25d8 --- /dev/null +++ b/server/badges/badges.go @@ -0,0 +1,41 @@ +// Copyright 2022 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package badges + +import "github.com/woodpecker-ci/woodpecker/server/model" + +var ( + badgeSuccess = `buildbuildsuccesssuccess` + badgeFailure = `buildbuildfailurefailure` + badgeStarted = `buildbuildstartedstarted` + badgeError = `buildbuilderrorerror` + badgeNone = `buildbuildnonenone` +) + +// Generate an SVG badge based on a build +func Generate(build *model.Build) string { + switch build.Status { + case model.StatusSuccess: + return badgeSuccess + case model.StatusFailure: + return badgeFailure + case model.StatusError, model.StatusKilled: + return badgeError + case model.StatusPending, model.StatusRunning: + return badgeStarted + default: + return badgeNone + } +} diff --git a/server/model/cc.go b/server/ccmenu/cc.go similarity index 75% rename from server/model/cc.go rename to server/ccmenu/cc.go index cb3296571..8525740e6 100644 --- a/server/model/cc.go +++ b/server/ccmenu/cc.go @@ -1,3 +1,4 @@ +// Copyright 2022 Woodpecker Authors // Copyright 2018 Drone.IO Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,14 +13,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -package model +package ccmenu import ( "encoding/xml" "strconv" "time" + + "github.com/woodpecker-ci/woodpecker/server/model" ) +// CCMenu displays the build status of projects on a ci server as an item in the Mac's menu bar. +// It started as part of the CruiseControl project that built the first continuous integration server. +// +// http://ccmenu.org/ + type CCProjects struct { XMLName xml.Name `xml:"Projects"` Project *CCProject `xml:"Project"` @@ -35,7 +43,7 @@ type CCProject struct { WebURL string `xml:"webUrl,attr"` } -func NewCC(r *Repo, b *Build, link string) *CCProjects { +func New(r *model.Repo, b *model.Build, link string) *CCProjects { proj := &CCProject{ Name: r.FullName, WebURL: link, @@ -46,8 +54,8 @@ func NewCC(r *Repo, b *Build, link string) *CCProjects { // if the build is not currently running then // we can return the latest build status. - if b.Status != StatusPending && - b.Status != StatusRunning { + if b.Status != model.StatusPending && + b.Status != model.StatusRunning { proj.Activity = "Sleeping" proj.LastBuildTime = time.Unix(b.Started, 0).Format(time.RFC3339) proj.LastBuildLabel = strconv.FormatInt(b.Number, 10) @@ -56,11 +64,11 @@ func NewCC(r *Repo, b *Build, link string) *CCProjects { // ensure the last build Status accepts a valid // ccmenu enumeration switch b.Status { - case StatusError, StatusKilled: + case model.StatusError, model.StatusKilled: proj.LastBuildStatus = "Exception" - case StatusSuccess: + case model.StatusSuccess: proj.LastBuildStatus = "Success" - case StatusFailure: + case model.StatusFailure: proj.LastBuildStatus = "Failure" } diff --git a/server/model/cc_test.go b/server/ccmenu/cc_test.go similarity index 73% rename from server/model/cc_test.go rename to server/ccmenu/cc_test.go index 7291cd38b..c1f6be23c 100644 --- a/server/model/cc_test.go +++ b/server/ccmenu/cc_test.go @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package model +package ccmenu import ( "testing" "time" "github.com/franela/goblin" + + "github.com/woodpecker-ci/woodpecker/server/model" ) func TestCC(t *testing.T) { @@ -27,15 +29,15 @@ func TestCC(t *testing.T) { g.It("Should create a project", func() { now := time.Now().Unix() nowFmt := time.Unix(now, 0).Format(time.RFC3339) - r := &Repo{ + r := &model.Repo{ FullName: "foo/bar", } - b := &Build{ - Status: StatusSuccess, + b := &model.Build{ + Status: model.StatusSuccess, Number: 1, Started: now, } - cc := NewCC(r, b, "http://localhost/foo/bar/1") + cc := New(r, b, "http://localhost/foo/bar/1") g.Assert(cc.Project.Name).Equal("foo/bar") g.Assert(cc.Project.Activity).Equal("Sleeping") @@ -46,49 +48,49 @@ func TestCC(t *testing.T) { }) g.It("Should properly label exceptions", func() { - r := &Repo{FullName: "foo/bar"} - b := &Build{ - Status: StatusError, + r := &model.Repo{FullName: "foo/bar"} + b := &model.Build{ + Status: model.StatusError, Number: 1, Started: 1257894000, } - cc := NewCC(r, b, "http://localhost/foo/bar/1") + cc := New(r, b, "http://localhost/foo/bar/1") g.Assert(cc.Project.LastBuildStatus).Equal("Exception") g.Assert(cc.Project.Activity).Equal("Sleeping") }) g.It("Should properly label success", func() { - r := &Repo{FullName: "foo/bar"} - b := &Build{ - Status: StatusSuccess, + r := &model.Repo{FullName: "foo/bar"} + b := &model.Build{ + Status: model.StatusSuccess, Number: 1, Started: 1257894000, } - cc := NewCC(r, b, "http://localhost/foo/bar/1") + cc := New(r, b, "http://localhost/foo/bar/1") g.Assert(cc.Project.LastBuildStatus).Equal("Success") g.Assert(cc.Project.Activity).Equal("Sleeping") }) g.It("Should properly label failure", func() { - r := &Repo{FullName: "foo/bar"} - b := &Build{ - Status: StatusFailure, + r := &model.Repo{FullName: "foo/bar"} + b := &model.Build{ + Status: model.StatusFailure, Number: 1, Started: 1257894000, } - cc := NewCC(r, b, "http://localhost/foo/bar/1") + cc := New(r, b, "http://localhost/foo/bar/1") g.Assert(cc.Project.LastBuildStatus).Equal("Failure") g.Assert(cc.Project.Activity).Equal("Sleeping") }) g.It("Should properly label running", func() { - r := &Repo{FullName: "foo/bar"} - b := &Build{ - Status: StatusRunning, + r := &model.Repo{FullName: "foo/bar"} + b := &model.Build{ + Status: model.StatusRunning, Number: 1, Started: 1257894000, } - cc := NewCC(r, b, "http://localhost/foo/bar/1") + cc := New(r, b, "http://localhost/foo/bar/1") g.Assert(cc.Project.Activity).Equal("Building") g.Assert(cc.Project.LastBuildStatus).Equal("Unknown") g.Assert(cc.Project.LastBuildLabel).Equal("Unknown") diff --git a/server/store/datastore/build.go b/server/store/datastore/build.go index 979b34c10..2664282dd 100644 --- a/server/store/datastore/build.go +++ b/server/store/datastore/build.go @@ -56,7 +56,7 @@ func (s storage) GetBuildLast(repo *model.Repo, branch string) (*model.Build, er build := &model.Build{ RepoID: repo.ID, Branch: branch, - Event: "push", + Event: model.EventPush, } return build, wrapGet(s.engine.Desc("build_number").Get(build)) }