Skip to content

Commit d39927d

Browse files
authored
Merge pull request #109 from equinor/master
147-distinguish-tags-from-branches-when-triggering-builds (#108)
2 parents 9b306fc + 8f5c074 commit d39927d

File tree

12 files changed

+122
-50
lines changed

12 files changed

+122
-50
lines changed

.github/workflows/pr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: golangci-lint
2828
uses: golangci/golangci-lint-action@v6
2929
with:
30-
version: v1.60.3
30+
version: v1.64.3
3131

3232
test:
3333
name: Unit Test

Dockerfile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# Build stage
2-
FROM docker.io/golang:1.22-alpine3.20 as builder
1+
FROM --platform=$BUILDPLATFORM docker.io/golang:1.24.2-alpine3.21 AS builder
2+
ARG TARGETARCH
33
ENV CGO_ENABLED=0 \
4-
GOOS=linux
4+
GOOS=linux \
5+
GOARCH=${TARGETARCH}
6+
57
WORKDIR /src
68
COPY go.mod go.sum ./
79
RUN go mod download
810
COPY . .
9-
RUN go build -ldflags "-s -w" -o /build/radix-github-webhook
11+
RUN go build -ldflags="-s -w" -o /build/radix-github-webhook
1012

1113
# Final stage, ref https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md for distroless
1214
FROM gcr.io/distroless/static

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ docker-build: $(addsuffix -image,$(IMAGES))
3636
docker-push: $(addsuffix -push,$(IMAGES))
3737

3838
%-push:
39+
az acr login --name $(DOCKER_REGISTRY)
3940
docker push $(DOCKER_REGISTRY)/$*:$(IMAGE_TAG)
4041

42+
.PHONY: deploy
43+
deploy: docker-build docker-push
44+
4145
.PHONY: mocks
4246
mocks: bootstrap
4347
mockgen -source ./radix/api_server.go -destination ./radix/api_server_mock.go -package radix
@@ -55,7 +59,7 @@ HAS_MOCKGEN := $(shell command -v mockgen;)
5559
.PHONY: bootstrap
5660
bootstrap:
5761
ifndef HAS_GOLANGCI_LINT
58-
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.58.2
62+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin v1.64.3
5963
endif
6064
ifndef HAS_MOCKGEN
6165
go install github.com/golang/mock/mockgen@v1.6.0

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module github.com/equinor/radix-github-webhook
22

3-
go 1.22.0
3+
go 1.24.0
44

5-
toolchain go1.23.0
5+
toolchain go1.24.2
66

77
require (
88
github.com/equinor/radix-common v1.9.7

handler/webhook_handler.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ var (
4242
createPipelineJobErrorMessage = func(appName string, apiError error) string {
4343
return fmt.Sprintf("Failed to create pipeline job for Radix application %s. ApiError was: %s", appName, apiError)
4444
}
45-
createPipelineJobSuccessMessage = func(jobName, appName, branch, commitID string) string {
46-
return fmt.Sprintf("Pipeline job %s created for Radix application %s on branch %s for commit %s", jobName, appName, branch, commitID)
45+
createPipelineJobSuccessMessage = func(jobName, appName, gitRefs, gitRefsType, commitID string) string {
46+
return fmt.Sprintf("Pipeline job %s created for Radix application %s on %s %s for commit %s", jobName, appName, gitRefsType, gitRefs, commitID)
4747
}
4848
)
4949

@@ -116,12 +116,12 @@ func (wh *webhookHandler) HandleFunc(c *gin.Context) {
116116

117117
switch e := payload.(type) {
118118
case *github.PushEvent:
119-
branch := getBranch(e)
119+
gitRef, gitRefType := getGitRefWithType(e)
120120
commitID := getCommitID(e)
121121
sshURL := e.Repo.GetSSHURL()
122122
triggeredBy := getPushTriggeredBy(e)
123123

124-
metrics.IncreasePushGithubEventTypeCounter(sshURL, branch, commitID)
124+
metrics.IncreasePushGithubEventTypeCounter(sshURL, gitRef, gitRefType, commitID)
125125

126126
if isPushEventForRefDeletion(e) {
127127
writeSuccessResponse(http.StatusAccepted, refDeletionPushEventUnsupportedMessage(*e.Ref))
@@ -135,19 +135,19 @@ func (wh *webhookHandler) HandleFunc(c *gin.Context) {
135135
return
136136
}
137137

138-
metrics.IncreasePushGithubEventTypeTriggerPipelineCounter(sshURL, branch, commitID, applicationSummary.Name)
139-
jobSummary, err := wh.apiServer.TriggerPipeline(c.Request.Context(), applicationSummary.Name, branch, commitID, triggeredBy)
138+
metrics.IncreasePushGithubEventTypeTriggerPipelineCounter(sshURL, gitRef, gitRefType, commitID, applicationSummary.Name)
139+
jobSummary, err := wh.apiServer.TriggerPipeline(c.Request.Context(), applicationSummary.Name, gitRef, gitRefType, commitID, triggeredBy)
140140
if err != nil {
141141
if e, ok := err.(*radix.ApiError); ok && e.Code == 400 {
142142
writeSuccessResponse(http.StatusAccepted, createPipelineJobErrorMessage(applicationSummary.Name, err))
143143
return
144144
}
145-
metrics.IncreasePushGithubEventTypeFailedTriggerPipelineCounter(sshURL, branch, commitID)
145+
metrics.IncreasePushGithubEventTypeFailedTriggerPipelineCounter(sshURL, gitRef, gitRefType, commitID)
146146
writeErrorResponse(http.StatusBadRequest, errors.New(createPipelineJobErrorMessage(applicationSummary.Name, err)))
147147
return
148148
}
149149

150-
writeSuccessResponse(http.StatusOK, createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.Branch, jobSummary.CommitID))
150+
writeSuccessResponse(http.StatusOK, createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.GetGitRefOrDefault(), jobSummary.GetGitRefTypeOrDefault(), jobSummary.CommitID))
151151

152152
case *github.PingEvent:
153153
// sshURL := getSSHUrlFromPingURL(*e.Hook.URL)
@@ -170,6 +170,16 @@ func (wh *webhookHandler) HandleFunc(c *gin.Context) {
170170
}
171171
}
172172

173+
func getApiGitRefType(gitRefsType string) string {
174+
switch gitRefsType {
175+
case "heads":
176+
return "branch"
177+
case "tags":
178+
return "tag"
179+
}
180+
return ""
181+
}
182+
173183
func getCommitID(e *github.PushEvent) string {
174184
if e.Ref != nil && strings.HasPrefix(*e.Ref, "refs/tags/") && e.BaseRef == nil {
175185
// The property After has not an existing commit-ID, but other object ID
@@ -254,10 +264,11 @@ func getPushTriggeredBy(pushEvent *github.PushEvent) string {
254264
return ""
255265
}
256266

257-
func getBranch(pushEvent *github.PushEvent) string {
258-
// Remove refs/heads from ref
267+
func getGitRefWithType(pushEvent *github.PushEvent) (string, string) {
259268
ref := strings.Split(*pushEvent.Ref, "/")
260-
return strings.Join(ref[2:], "/")
269+
gitRef := strings.Join(ref[2:], "/") // Remove refs/heads from ref
270+
gitRefType := ref[1]
271+
return gitRef, getApiGitRefType(gitRefType)
261272
}
262273

263274
func isPushEventForRefDeletion(pushEvent *github.PushEvent) bool {

handler/webhook_handler_test.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func (s *handlerTestSuite) Test_PushEventUnmatchedRepo() {
301301
Times(1)
302302
jobSummary := models.JobSummary{Name: "jobname", AppName: expectAppDetail.appName, Branch: "master", CommitID: commitID, TriggeredBy: ""}
303303
s.apiServer.EXPECT().
304-
TriggerPipeline(gomock.Any(), expectAppDetail.appName, "master", commitID, "").
304+
TriggerPipeline(gomock.Any(), expectAppDetail.appName, "master", "branch", commitID, "").
305305
Return(&jobSummary, nil).
306306
Times(1)
307307
}
@@ -457,7 +457,7 @@ func (s *handlerTestSuite) Test_PushEventTriggerPipelineReturnsError() {
457457

458458
s.apiServer.EXPECT().ShowApplications(gomock.Any(), "git@github.com:equinor/repo-4.git").Return([]*models.ApplicationSummary{&appSummary}, nil).Times(1)
459459
s.apiServer.EXPECT().GetApplication(gomock.Any(), appName).Return(appDetail, nil).Times(1)
460-
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, "master", commitID, "").Return(nil, scenario.apiError).Times(1)
460+
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, "master", "branch", commitID, "").Return(nil, scenario.apiError).Times(1)
461461

462462
sut := NewWebHookHandler(s.apiServer)
463463
req, _ := http.NewRequest("POST", "/", bytes.NewReader(payload))
@@ -490,7 +490,7 @@ func (s *handlerTestSuite) Test_PushEventCorrectSecret() {
490490
jobSummary := models.JobSummary{Name: "jobname", AppName: "jobappname", Branch: "jobbranchname", CommitID: "jobcommitID", TriggeredBy: "anyuser"}
491491
s.apiServer.EXPECT().ShowApplications(gomock.Any(), "git@github.com:equinor/repo-4.git").Return([]*models.ApplicationSummary{&appSummary}, nil).Times(1)
492492
s.apiServer.EXPECT().GetApplication(gomock.Any(), appName).Return(appDetail, nil).Times(1)
493-
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, "master", commitID, "").Return(&jobSummary, nil).Times(1)
493+
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, "master", "branch", commitID, "").Return(&jobSummary, nil).Times(1)
494494

495495
sut := NewWebHookHandler(s.apiServer)
496496
req, _ := http.NewRequest("POST", "/", bytes.NewReader(payload))
@@ -502,7 +502,7 @@ func (s *handlerTestSuite) Test_PushEventCorrectSecret() {
502502
var res response
503503
err := json.Unmarshal(s.w.Body.Bytes(), &res)
504504
require.NoError(s.T(), err)
505-
s.Equal(createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.Branch, jobSummary.CommitID), res.Message)
505+
s.Equal(createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.Branch, "branch", jobSummary.CommitID), res.Message)
506506
s.ctrl.Finish()
507507
}
508508

@@ -528,9 +528,18 @@ func (s *handlerTestSuite) Test_PushEventWithRefDeleted() {
528528
}
529529

530530
func Test_GetBranch_RemovesRefsHead(t *testing.T) {
531-
assert.Equal(t, "master", getBranch(&github.PushEvent{Ref: strPtr("refs/heads/master")}))
532-
assert.Equal(t, "feature/RA-326-TestBranch", getBranch(&github.PushEvent{Ref: strPtr("refs/heads/feature/RA-326-TestBranch")}))
533-
assert.Equal(t, "hotfix/api/refs/heads/fix1", getBranch(&github.PushEvent{Ref: strPtr("refs/heads/hotfix/api/refs/heads/fix1")}))
531+
gitRef, gitRefType := getGitRefWithType(&github.PushEvent{Ref: strPtr("refs/tags/v1.0.2")})
532+
assert.Equal(t, "v1.0.2", gitRef)
533+
assert.Equal(t, "tag", gitRefType)
534+
gitRef, gitRefType = getGitRefWithType(&github.PushEvent{Ref: strPtr("refs/heads/master")})
535+
assert.Equal(t, "master", gitRef)
536+
assert.Equal(t, "branch", gitRefType)
537+
gitRef, gitRefType = getGitRefWithType(&github.PushEvent{Ref: strPtr("refs/heads/feature/RA-326-TestBranch")})
538+
assert.Equal(t, "feature/RA-326-TestBranch", gitRef)
539+
assert.Equal(t, "branch", gitRefType)
540+
gitRef, gitRefType = getGitRefWithType(&github.PushEvent{Ref: strPtr("refs/heads/hotfix/api/refs/heads/fix1")})
541+
assert.Equal(t, "hotfix/api/refs/heads/fix1", gitRef)
542+
assert.Equal(t, "branch", gitRefType)
534543
}
535544

536545
func (s *handlerTestSuite) Test_PushEventWithAnnotatedTag() {
@@ -550,7 +559,7 @@ func (s *handlerTestSuite) Test_PushEventWithAnnotatedTag() {
550559
jobSummary := models.JobSummary{Name: "jobname", AppName: "jobappname", Branch: "jobbranchname", CommitID: headCommitID, TriggeredBy: "anyuser"}
551560
s.apiServer.EXPECT().ShowApplications(gomock.Any(), "git@github.com:equinor/repo-1.git").Return([]*models.ApplicationSummary{&appSummary}, nil).Times(1)
552561
s.apiServer.EXPECT().GetApplication(gomock.Any(), appName).Return(appDetail, nil).Times(1)
553-
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, tag, headCommitID, "").Return(&jobSummary, nil).Times(1)
562+
s.apiServer.EXPECT().TriggerPipeline(gomock.Any(), appName, tag, "tag", headCommitID, "").Return(&jobSummary, nil).Times(1)
554563

555564
sut := NewWebHookHandler(s.apiServer)
556565
req, _ := http.NewRequest("POST", "/", bytes.NewReader(payload))
@@ -562,7 +571,7 @@ func (s *handlerTestSuite) Test_PushEventWithAnnotatedTag() {
562571
var res response
563572
err := json.Unmarshal(s.w.Body.Bytes(), &res)
564573
require.NoError(s.T(), err)
565-
s.Equal(createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.Branch, jobSummary.CommitID), res.Message)
574+
s.Equal(createPipelineJobSuccessMessage(jobSummary.Name, jobSummary.AppName, jobSummary.Branch, "branch", jobSummary.CommitID), res.Message)
566575
s.ctrl.Finish()
567576
}
568577

@@ -571,7 +580,7 @@ type response struct {
571580
Error string `json:"error"`
572581
}
573582

574-
// GitHubPayloadBuilder Handles construction of github payload
583+
// GitHubPayloadBuilder Handles construction of GitHub payload
575584
type GitHubPayloadBuilder interface {
576585
withRef(refs string) GitHubPayloadBuilder
577586
withAfter(after string) GitHubPayloadBuilder

metrics/custom_metrics.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import (
55
)
66

77
const (
8-
sshURLLabel = "radix_webhook_request_ssh_url"
9-
branchLabel = "radix_webhook_request_branch"
10-
commitIDLabel = "radix_webhook_request_commit_id"
11-
appNameLabel = "radix_webhook_request_app_name"
8+
sshURLLabel = "radix_webhook_request_ssh_url"
9+
gitRefsLabel = "radix_webhook_request_branch"
10+
gitRefsTypeLabel = "radix_webhook_request_refs_type"
11+
commitIDLabel = "radix_webhook_request_commit_id"
12+
appNameLabel = "radix_webhook_request_app_name"
1213
)
1314

1415
var (
@@ -55,21 +56,21 @@ var (
5556
Name: "radix_webhook_request_push_github_event_type_counter",
5657
Help: "Counter for push GitHub event type requests",
5758
},
58-
[]string{sshURLLabel, branchLabel, commitIDLabel},
59+
[]string{sshURLLabel, gitRefsLabel, gitRefsTypeLabel, commitIDLabel},
5960
)
6061
pushEventTypeTriggerPipelineCounter = prometheus.NewCounterVec(
6162
prometheus.CounterOpts{
6263
Name: "radix_webhook_request_push_github_event_type_trigger_pipeline_counter",
6364
Help: "Counter for push GitHub event type trigger pipeline requests",
6465
},
65-
[]string{sshURLLabel, branchLabel, commitIDLabel, appNameLabel},
66+
[]string{sshURLLabel, gitRefsLabel, gitRefsTypeLabel, commitIDLabel, appNameLabel},
6667
)
6768
pushEventTypeFailedTriggerPipelineCounter = prometheus.NewCounterVec(
6869
prometheus.CounterOpts{
6970
Name: "radix_webhook_request_push_github_event_type_failed_trigger_pipeline_counter",
7071
Help: "Counter for push GitHub event type failed trigger pipeline requests",
7172
},
72-
[]string{sshURLLabel, branchLabel, commitIDLabel},
73+
[]string{sshURLLabel, gitRefsLabel, gitRefsTypeLabel, commitIDLabel},
7374
)
7475
)
7576

@@ -116,16 +117,16 @@ func IncreaseFailedCloneURLValidationCounter(sshURL string) {
116117
}
117118

118119
// IncreasePushGithubEventTypeCounter increases all GitHub push event type request counter
119-
func IncreasePushGithubEventTypeCounter(sshURL, branch, commitID string) {
120-
pushEventTypeCounter.With(prometheus.Labels{sshURLLabel: sshURL, branchLabel: branch, commitIDLabel: commitID}).Inc()
120+
func IncreasePushGithubEventTypeCounter(sshURL, gitRefs, gitRefsType, commitID string) {
121+
pushEventTypeCounter.With(prometheus.Labels{sshURLLabel: sshURL, gitRefsLabel: gitRefs, gitRefsTypeLabel: gitRefsType, commitIDLabel: commitID}).Inc()
121122
}
122123

123124
// IncreasePushGithubEventTypeTriggerPipelineCounter increases GitHub push event type trigger pipeline request counter
124-
func IncreasePushGithubEventTypeTriggerPipelineCounter(sshURL, branch, commitID, appName string) {
125-
pushEventTypeTriggerPipelineCounter.With(prometheus.Labels{sshURLLabel: sshURL, branchLabel: branch, commitIDLabel: commitID, appNameLabel: appName}).Inc()
125+
func IncreasePushGithubEventTypeTriggerPipelineCounter(sshURL, gitRefs, gitRefsType, commitID, appName string) {
126+
pushEventTypeTriggerPipelineCounter.With(prometheus.Labels{sshURLLabel: sshURL, gitRefsLabel: gitRefs, gitRefsTypeLabel: gitRefsType, commitIDLabel: commitID, appNameLabel: appName}).Inc()
126127
}
127128

128129
// IncreasePushGithubEventTypeFailedTriggerPipelineCounter increases GitHub push event type failed trigger pipeline request counter
129-
func IncreasePushGithubEventTypeFailedTriggerPipelineCounter(sshURL, branch, commitID string) {
130-
pushEventTypeFailedTriggerPipelineCounter.With(prometheus.Labels{sshURLLabel: sshURL, branchLabel: branch, commitIDLabel: commitID}).Inc()
130+
func IncreasePushGithubEventTypeFailedTriggerPipelineCounter(sshURL, gitRefs, gitRefsType, commitID string) {
131+
pushEventTypeFailedTriggerPipelineCounter.With(prometheus.Labels{sshURLLabel: sshURL, gitRefsLabel: gitRefs, gitRefsTypeLabel: gitRefsType, commitIDLabel: commitID}).Inc()
131132
}

models/job_summary.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,41 @@ type JobSummary struct {
88
// AppName of the application
99
AppName string `json:"appName"`
1010

11-
// Branch branch to build from
11+
// Branch to build from
1212
Branch string `json:"branch"`
1313

14+
// GitRef Branch or tag to build from
15+
//
16+
// example: master
17+
GitRef string `json:"gitRef,omitempty"`
18+
19+
// GitRefType When the pipeline job should be built from branch or tag specified in GitRef:
20+
// - branch
21+
// - tag
22+
// - <empty> - either branch or tag
23+
//
24+
// example: "branch"
25+
GitRefType string `json:"gitRefType,omitempty"`
26+
1427
// CommitID the commit ID of the branch to build
1528
CommitID string `json:"commitID"`
1629

1730
// TriggeredBy of the job
1831
TriggeredBy string `json:"triggeredBy"`
1932
}
33+
34+
// GetGitRefOrDefault returns the GitRef if set, otherwise returns the Branch
35+
func (jobSummary JobSummary) GetGitRefOrDefault() string {
36+
if jobSummary.GitRef != "" {
37+
return jobSummary.GitRef
38+
}
39+
return jobSummary.Branch
40+
}
41+
42+
// GetGitRefTypeOrDefault returns the GitRefType if set, otherwise returns the Branch
43+
func (jobSummary JobSummary) GetGitRefTypeOrDefault() string {
44+
if jobSummary.GitRefType != "" {
45+
return jobSummary.GitRefType
46+
}
47+
return "branch"
48+
}

models/pipeline_parameters.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,20 @@ type PipelineParameters struct {
1919
// required: true
2020
// example: 4faca8595c5283a9d0f17a623b9255a0d9866a2e
2121
TriggeredBy string `json:"triggeredBy"`
22+
23+
// GitRef Branch or tag to build from
24+
//
25+
// required: false
26+
// example: master
27+
GitRef string `json:"gitRef,omitempty"`
28+
29+
// GitRefType When the pipeline job should be built from branch or tag specified in GitRef:
30+
// - branch
31+
// - tag
32+
// - <empty> - either branch or tag
33+
//
34+
// required false
35+
// enum: branch,tag,""
36+
// example: "branch"
37+
GitRefType string `json:"gitRefType,omitempty"`
2238
}

radix/api_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ import (
1010
type APIServer interface {
1111
ShowApplications(ctx context.Context, sshURL string) ([]*models.ApplicationSummary, error)
1212
GetApplication(ctx context.Context, appName string) (*models.Application, error)
13-
TriggerPipeline(ctx context.Context, appName, branch, commitID, triggeredBy string) (*models.JobSummary, error)
13+
TriggerPipeline(ctx context.Context, appName, gitRef, gitRefType, commitID, triggeredBy string) (*models.JobSummary, error)
1414
}

0 commit comments

Comments
 (0)