Skip to content

Commit a10db47

Browse files
committed
Use proxy-janky-build actions
1 parent 6042e51 commit a10db47

File tree

5 files changed

+229
-8
lines changed

5 files changed

+229
-8
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: 'Trigger a CI Job on Janky'
2+
description: 'Action to trigger and poll a Janky CI job'
3+
inputs:
4+
janky-token:
5+
description: 'Token for making request to Janky'
6+
required: true
7+
job-name:
8+
description: 'The name of the job to run'
9+
required: true
10+
branch-name:
11+
description: 'The name of the branch to use'
12+
required: true
13+
envVars:
14+
description: 'Comma separated list of key value pairs to pass to Janky - ex: key1=value1,key2=value2,key3=value3'
15+
required: false
16+
runs:
17+
using: 'composite'
18+
steps:
19+
- uses: actions/setup-go@a3d889c34c5d4e071b33595c5fe8edfcaaad8260
20+
with:
21+
go-version: '1.21'
22+
- run: |
23+
go run main.go \
24+
-token ${{ inputs.janky-token }} \
25+
-job ${{ inputs.job-name }} \
26+
-branch ${{ inputs.branch-name }} \
27+
-envVars ${{ inputs.envVars }}
28+
shell: bash
29+
working-directory: .github/actions/proxy-janky-build
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module github.com/github/enterprise2/actions/proxy-janky-build
2+
3+
go 1.21
4+
5+
require github.com/hashicorp/go-retryablehttp v0.7.2
6+
7+
require github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
3+
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
4+
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
5+
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
6+
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
7+
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
8+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"encoding/json"
7+
"flag"
8+
"fmt"
9+
"io"
10+
"log"
11+
"net/http"
12+
"regexp"
13+
"strings"
14+
"time"
15+
16+
"github.com/hashicorp/go-retryablehttp"
17+
)
18+
19+
// Define our Janky Response Structs
20+
type JankyBuildStruct struct {
21+
Result string
22+
Url string
23+
}
24+
type JankyStatusStruct struct {
25+
Id string
26+
Green bool
27+
Completed bool
28+
StartedAt string
29+
CompletedAt string
30+
Sha string
31+
BuildableName string
32+
}
33+
34+
const (
35+
pollWaitTime = 10 * time.Second
36+
jankyPollTimeout = 5 * time.Hour
37+
jankyHttpRetryMax = 5
38+
jankyUrl = "https://janky.githubapp.com"
39+
)
40+
41+
func main() {
42+
// Parse command-line arguments
43+
job := flag.String("job", "", "Name of the Janky job")
44+
token := flag.String("token", "", "Name of the Janky token")
45+
branch := flag.String("branch", "", "Name of the Git branch")
46+
envVars := flag.String("envVars", "", "Comma separated list of key value pairs to pass to Janky - ex: key1=value1,key2=value2,key3=value3")
47+
flag.Parse()
48+
49+
// Validate command-line arguments
50+
if *job == "" || *token == "" || *branch == "" {
51+
log.Fatal("job, token and branch flags must be specified")
52+
}
53+
54+
// Set up the token + request payload
55+
authToken := base64.StdEncoding.EncodeToString([]byte(":" + *token))
56+
type buildRequestObject struct {
57+
BuildableName string `json:"buildable_name"`
58+
BranchName string `json:"branch_name"`
59+
EnvVars map[string]string `json:"env_vars"`
60+
}
61+
62+
requestBody := buildRequestObject{
63+
BuildableName: *job,
64+
BranchName: *branch,
65+
}
66+
67+
// Parse the envVars flag into a map and add to the request payload
68+
fmt.Println("Environment Variables:")
69+
fmt.Println(*envVars)
70+
if *envVars != "" {
71+
envVarsMap := make(map[string]string)
72+
for _, envVar := range strings.Split(*envVars, ",") {
73+
envVarSplit := strings.Split(envVar, "=")
74+
envVarsMap[envVarSplit[0]] = envVarSplit[1]
75+
}
76+
requestBody.EnvVars = envVarsMap
77+
}
78+
79+
payloadBytes, err := json.Marshal(requestBody)
80+
if err != nil {
81+
log.Fatal("Failed to marshal the JSON payload!\n" + err.Error())
82+
}
83+
84+
// Send build request to Janky
85+
buildRequest, err := http.NewRequest("POST", jankyUrl+"/api/builds", bytes.NewBuffer(payloadBytes))
86+
if err != nil {
87+
log.Fatal("Failed to create build request!\n" + err.Error())
88+
}
89+
buildRequest.Header.Set("Content-Type", "application/json")
90+
buildRequest.Header.Set("Authorization", "Basic "+authToken)
91+
retryClient := retryablehttp.NewClient()
92+
retryClient.RetryMax = jankyHttpRetryMax
93+
retryClient.Logger = nil // disable debug logging
94+
client := retryClient.StandardClient() // uses *http.Client
95+
resp, err := client.Do(buildRequest)
96+
if err != nil {
97+
log.Fatal("Failed to send build request!\n" + err.Error())
98+
}
99+
defer resp.Body.Close()
100+
body, err := io.ReadAll(resp.Body)
101+
if err != nil {
102+
log.Fatal("Error reading build response!\n" + err.Error())
103+
}
104+
105+
// Check if the build was triggered successfully
106+
if resp.StatusCode == 404 {
107+
log.Fatal("Failed to trigger build! Either " + *job + " is not the name of a Janky job or " + *branch + " is not a branch for the repository that job belongs to.")
108+
}
109+
if resp.StatusCode != 201 {
110+
log.Fatal("Failed to trigger build! Got exception: " + string(body))
111+
}
112+
113+
// Parse the build request response
114+
var buildResponse JankyBuildStruct
115+
json.Unmarshal(body, &buildResponse)
116+
log.Println("Succesfully triggered janky!\n" + buildResponse.Result)
117+
118+
// Parse the request response for the buildId
119+
r, err := regexp.Compile("/[0-9]+/")
120+
if err != nil {
121+
log.Fatal("Failed to trigger build!\n" + err.Error())
122+
}
123+
buildId := strings.Trim(r.FindString(buildResponse.Result), "/")
124+
125+
// Setup our second HTTP client for reuse in during status polling
126+
jankyStatusUrl := jankyUrl + "/api/" + buildId + "/status"
127+
statusRequest, err := http.NewRequest("GET", jankyStatusUrl, nil)
128+
if err != nil {
129+
log.Fatal("Failed to create status request!\n" + err.Error())
130+
}
131+
statusRequest.Header.Set("Content-Type", "application/json")
132+
statusRequest.Header.Set("Authorization", "Basic "+authToken)
133+
retryClient2 := retryablehttp.NewClient()
134+
retryClient2.RetryMax = jankyHttpRetryMax
135+
retryClient2.Logger = nil // disable debug logging
136+
client2 := retryClient2.StandardClient() // uses *http.Client
137+
138+
// Wait for a completed status from Janky or break the loop after a certain amount of time
139+
timeout := time.NewTimer(jankyPollTimeout)
140+
poll := time.NewTicker(pollWaitTime)
141+
142+
jobLoop:
143+
for {
144+
select {
145+
case <-timeout.C:
146+
log.Fatal("Failed to poll for build status after " + jankyPollTimeout.String() + "hours")
147+
case <-poll.C:
148+
// Send build status request to Janky
149+
statusResponse, err := client2.Do(statusRequest)
150+
if err != nil {
151+
log.Fatal("Failed to send status request!\n" + err.Error())
152+
}
153+
defer statusResponse.Body.Close()
154+
statusBody, err := io.ReadAll(statusResponse.Body)
155+
if err != nil {
156+
log.Fatal("Error reading status response!\n" + err.Error())
157+
}
158+
159+
// Parse the status response for a green completed build
160+
var jankyStatusResponse JankyStatusStruct
161+
json.Unmarshal(statusBody, &jankyStatusResponse)
162+
//fmt.Println("Janky Status Response:")
163+
//fmt.Println(string(statusBody))
164+
if jankyStatusResponse.Completed && jankyStatusResponse.Green {
165+
log.Println("Janky build Succeeded!")
166+
break jobLoop
167+
}
168+
if jankyStatusResponse.Completed && !jankyStatusResponse.Green {
169+
log.Fatal("Build failed, see Janky for more info: " + buildResponse.Url)
170+
}
171+
172+
// wait for a bit and try again
173+
log.Println("Build still in progress, will poll for status again in [" + pollWaitTime.String() + "]")
174+
continue
175+
}
176+
}
177+
}

.github/workflows/integration-tests.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ jobs:
3131
with:
3232
fetch-depth: 1
3333
- name: Queue ${{ matrix.jankyJobName }} build
34-
run: |
35-
backup_utils_branch="${{ env.SOURCE_BRANCH }}"
36-
branch_name="${{ env.TARGET_BRANCH }}"
37-
curl -v -X POST \
38-
-u "hubot:${{ secrets.API_AUTH_TOKEN }}" \
39-
-H "Content-Type: application/json" \
40-
-d '{"buildable_name":"${{ matrix.jankyJobName }}","repo":"${{ github.repository }}","branch_name": "'"$branch_name"'","env_vars":{"JANKY_ENV_BACKUP_UTILS_BRANCH": "'"$backup_utils_branch"'" },"force":"true","room_id":"#builds"}' \
41-
"https://janky.githubapp.com/api/builds"
34+
uses: ./.github/actions/proxy-janky-build
35+
id: proxy-janky-build
36+
with:
37+
janky-token: '${{ secrets.API_AUTH_TOKEN }}'
38+
job-name: '${{ matrix.jankyJobName }}'
39+
branch-name: '${{ env.TARGET_BRANCH }}'
40+
envVars: "JANKY_ENV_BACKUP_UTILS_BRANCH=${{ env.SOURCE_BRANCH }}"

0 commit comments

Comments
 (0)