Skip to content

Commit 60de8d0

Browse files
authored
ci: add skip directives for long tests (#3151)
This PR introduces many CI optimizations: 1. The `[ci-skip]` PR body directive to skip the Postgres and end to end tests 2. Improved caching that cuts the Go test matrix in half 3. Increasing Go test parallelism for ~20% gains 4. Enable caching in webpack (4x frontend build)
1 parent 5578fac commit 60de8d0

File tree

8 files changed

+191
-19
lines changed

8 files changed

+191
-19
lines changed

.github/workflows/coder.yaml

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,31 @@ jobs:
6060
echo "${{ toJSON(steps.filter )}}"
6161
6262
# Debug step
63-
show-changes:
64-
needs: changes
63+
debug-inputs:
64+
needs:
65+
- changes
66+
- pr-context
6567
runs-on: ubuntu-latest
6668
steps:
6769
- id: log
6870
run: |
69-
echo "${{ toJSON(needs.changes) }}"
71+
echo "${{ toJSON(needs) }}"
72+
echo "${{ contains(needs.pr-context.outputs.skips, 'postgres') }} "
73+
74+
pr-context:
75+
runs-on: ubuntu-latest
76+
outputs:
77+
skips: ${{ steps.pr-context.outputs.skips }}
78+
steps:
79+
- uses: actions/checkout@v3
80+
- uses: actions/setup-go@v3
81+
with:
82+
go-version: "~1.18"
83+
- id: pr-context
84+
env:
85+
GITHUB_CONTEXT: ${{ toJSON(github) }}
86+
run: |
87+
go run github.com/coder/coder/.github/workflows/prcontext
7088
7189
style-lint-golangci:
7290
name: style/lint/golangci
@@ -157,6 +175,25 @@ jobs:
157175
- uses: actions/setup-go@v3
158176
with:
159177
go-version: "~1.18"
178+
179+
- name: Echo Go Cache Paths
180+
id: go-cache-paths
181+
run: |
182+
echo "::set-output name=go-build::$(go env GOCACHE)"
183+
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
184+
185+
- name: Go Build Cache
186+
uses: actions/cache@v3
187+
with:
188+
path: ${{ steps.go-cache-paths.outputs.go-build }}
189+
key: ${{ github.job }}-go-build-${{ hashFiles('**/go.sum', '**/**.go') }}
190+
191+
- name: Go Mod Cache
192+
uses: actions/cache@v3
193+
with:
194+
path: ${{ steps.go-cache-paths.outputs.go-mod }}
195+
key: ${{ github.job }}-go-mod-${{ hashFiles('**/go.sum') }}
196+
160197
- run: |
161198
curl -sSL https://github.com/kyleconroy/sqlc/releases/download/v1.13.0/sqlc_1.13.0_linux_amd64.tar.gz | sudo tar -C /usr/bin -xz sqlc
162199
@@ -229,7 +266,7 @@ jobs:
229266
uses: actions/cache@v3
230267
with:
231268
path: ${{ steps.go-cache-paths.outputs.go-build }}
232-
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
269+
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.**', '**.go') }}
233270

234271
- name: Go Mod Cache
235272
uses: actions/cache@v3
@@ -251,14 +288,23 @@ jobs:
251288
terraform_wrapper: false
252289

253290
- name: Test with Mock Database
291+
id: test
254292
shell: bash
255-
run: gotestsum --junitfile="gotests.xml" --packages="./..." --
256-
-covermode=atomic -coverprofile="gotests.coverage"
257-
-coverpkg=./...,github.com/coder/coder/codersdk
258-
-timeout=5m -short -failfast
293+
run: |
294+
# Code coverage is more computationally expensive and also
295+
# prevents test caching, so we disable it on alternate operating
296+
# systems.
297+
if [ "${{ matrix.os }}" == "ubuntu-latest" ]; then
298+
echo ::set-output name=cover::true
299+
export COVERAGE_FLAGS='-covermode=atomic -coverprofile="gotests.coverage" -coverpkg=./...,github.com/coder/coder/codersdk'
300+
else
301+
echo ::set-output name=cover::false
302+
fi
303+
set -x
304+
gotestsum --junitfile="gotests.xml" --packages="./..." -- -parallel=8 -timeout=5m -short -failfast $COVERAGE_FLAGS
259305
260306
- name: Upload DataDog Trace
261-
if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
307+
if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
262308
env:
263309
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
264310
DD_DATABASE: fake
@@ -272,16 +318,20 @@ jobs:
272318
# that is no guarantee, see:
273319
# https://github.com/codecov/codecov-action/issues/788
274320
continue-on-error: true
275-
if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
321+
if: steps.test.outputs.cover && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
276322
with:
277323
token: ${{ secrets.CODECOV_TOKEN }}
278324
files: ./gotests.coverage
279325
flags: unittest-go-${{ matrix.os }}
280326

281327
test-go-postgres:
282328
name: "test/go/postgres"
283-
needs: changes
284-
if: needs.changes.outputs.docs-only == 'false'
329+
needs:
330+
- changes
331+
- pr-context
332+
if: >
333+
needs.changes.outputs.docs-only == 'false' &&
334+
contains(needs.pr-context.outputs.skips, github.job) == 'false'
285335
runs-on: ubuntu-latest
286336
# This timeout must be greater than the timeout set by `go test` in
287337
# `make test-postgres` to ensure we receive a trace of running
@@ -305,7 +355,7 @@ jobs:
305355
uses: actions/cache@v3
306356
with:
307357
path: ${{ steps.go-cache-paths.outputs.go-build }}
308-
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
358+
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum', '**/**.go') }}
309359

310360
- name: Go Mod Cache
311361
uses: actions/cache@v3
@@ -517,8 +567,10 @@ jobs:
517567

518568
test-e2e:
519569
name: "test/e2e/${{ matrix.os }}"
520-
needs: changes
521-
if: needs.changes.outputs.docs-only == 'false'
570+
needs:
571+
- changes
572+
- pr-context
573+
if: needs.changes.outputs.docs-only == 'false' && !contains(needs.pr-context.outputs.skips, 'test/e2e')
522574
runs-on: ${{ matrix.os }}
523575
timeout-minutes: 20
524576
strategy:
@@ -535,9 +587,7 @@ jobs:
535587
path: |
536588
**/node_modules
537589
.eslintcache
538-
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
539-
restore-keys: |
540-
js-${{ runner.os }}-
590+
key: js-${{ runner.os }}-e2e-${{ hashFiles('**/yarn.lock') }}
541591

542592
# Go is required for uploading the test results to datadog
543593
- uses: actions/setup-go@v3
@@ -573,6 +623,7 @@ jobs:
573623

574624
- name: Build
575625
run: |
626+
sudo npm install -g prettier
576627
make -B site/out/index.html
577628
578629
- run: yarn playwright:install

.github/workflows/prcontext/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# prcontext
2+
3+
`prcontext` is a simple Go program that extracts CI directives from PRs for a
4+
more efficient merge cycle.
5+
6+
Right now it only supports the `[ci-skip [job ...]]` directive. Since skips are
7+
only possible within PRs, the full suite will still run on merge.

.github/workflows/prcontext/main.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"strings"
8+
9+
"github.com/coder/flog"
10+
)
11+
12+
// githubContext is structured as documented here:
13+
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context.
14+
type githubContext struct {
15+
EventName string `json:"event_name"`
16+
Event struct {
17+
PullRequest struct {
18+
Body string `json:"body"`
19+
} `json:"pull_request"`
20+
} `json:"event"`
21+
}
22+
23+
func main() {
24+
var c githubContext
25+
err := json.Unmarshal([]byte(os.Getenv("GITHUB_CONTEXT")), &c)
26+
if err != nil {
27+
flog.Fatal("decode stdin: %+v", err)
28+
}
29+
flog.Info("detected event %q", c.EventName)
30+
if c.EventName != "pull_request" {
31+
flog.Info("aborting since not Pull Request")
32+
return
33+
}
34+
35+
_, _ = fmt.Printf("::group::{PR Body}\n%s\n::endgroup::\n", c.Event.PullRequest.Body)
36+
37+
skips := parseBody(c.Event.PullRequest.Body)
38+
_, _ = fmt.Printf("::echo::on\n::set-output name=skips::[%s]\n", strings.Join(skips, " "))
39+
}

.github/workflows/prcontext/parse.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
)
7+
8+
const ciSkipPrefix = "ci-skip"
9+
10+
var skipDirective = regexp.MustCompile(`\[` + ciSkipPrefix + ` ([\w-\/ ]+)]`)
11+
12+
func parseBody(body string) (skips []string) {
13+
matches := skipDirective.FindAllStringSubmatch(body, -1)
14+
// flog.Info("matches: %+v", matches)
15+
16+
var skipMatches []string
17+
for i := range matches {
18+
for j := range matches[i] {
19+
v := matches[i][j]
20+
// flog.Info("%q", v)
21+
if !strings.Contains(v, ciSkipPrefix) {
22+
skipMatches = append(skipMatches, strings.Split(v, " ")...)
23+
}
24+
}
25+
}
26+
27+
return skipMatches
28+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func Test_parseBody_basic(t *testing.T) {
9+
parseBody(`
10+
This is a test PR.
11+
12+
[ci-skip postgres windows]
13+
`)
14+
}
15+
16+
func Test_parseBody(t *testing.T) {
17+
type args struct {
18+
body string
19+
}
20+
tests := []struct {
21+
name string
22+
args args
23+
wantSkips []string
24+
}{
25+
{"no directive", args{"test pr 123\n\n"}, nil},
26+
{"single dir single skip", args{"test pr [ci-skip dog] 123\n\n"}, []string{"dog"}},
27+
{"double dir double skip", args{"test pr [ci-skip dog] [ci-skip cat] 123\n\n"}, []string{"dog", "cat"}},
28+
{"single dir double skip", args{"test pr [ci-skip test/go/postgres cat] 123\n\n"}, []string{"test/go/postgres", "cat"}},
29+
{"confuse", args{"ci ci [ci-skip] dog [ci-skip test/go/postgres test/e2e/ubuntu-latest] 123\n\n"}, []string{"test/go/postgres", "test/e2e/ubuntu-latest"}},
30+
}
31+
for _, tt := range tests {
32+
t.Run(tt.name, func(t *testing.T) {
33+
if gotSkips := parseBody(tt.args.body); !reflect.DeepEqual(gotSkips, tt.wantSkips) {
34+
t.Errorf("parseBody() = %v, want %v", gotSkips, tt.wantSkips)
35+
}
36+
})
37+
}
38+
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ require (
135135
tailscale.com v1.26.2
136136
)
137137

138-
require github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
138+
require (
139+
github.com/coder/flog v1.0.0 // indirect
140+
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
141+
)
139142

140143
require (
141144
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
344344
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
345345
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
346346
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
347+
github.com/coder/flog v1.0.0 h1:gqr4jYDQWYmsvFD0RV6Vs+SAj1Kbn0HGlV7UghfxP+8=
348+
github.com/coder/flog v1.0.0/go.mod h1:UQlQvrkJBvnRGo69Le8E24Tcl5SJleAAR7gYEHzAmdQ=
347349
github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1 h1:UqBrPWSYvRI2s5RtOul20JukUEpu4ip9u7biBL+ntgk=
348350
github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
349351
github.com/coder/retry v1.3.0 h1:5lAAwt/2Cm6lVmnfBY7sOMXcBOwcwJhmV5QGSELIVWY=

site/webpack.common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ export const createCommonWebpackConfig = (options?: { skipTypecheck: boolean }):
9393
],
9494
},
9595

96+
cache: {
97+
type: "filesystem",
98+
},
99+
96100
// resolve extend/modify how modules are resolved.
97101
//
98102
// REMARK: Do not add aliases here, unless they cannot be defined in a

0 commit comments

Comments
 (0)