Repository navigation
654 lines (588 loc) · 27.6 KB
/
Copy pathci.yml
File metadata and controls
654 lines (588 loc) · 27.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
# Nightly: what can break without a push to this repository (a new
# advisory, a new vulnerability in the image's base, GitHub's API
# descriptions, the hub template, every supported Gitea and Forgejo), and
# the long runs of distribute.
schedule:
- cron: '41 2 * * *'
workflow_dispatch:
permissions:
contents: read
# A push to a pull request while the previous run is still going restarts it;
# the old one is answering a question nobody is asking any more. A run on
# master is left to finish. The nightly and manual runs, which take hours,
# keep to groups of their own, so that a push to master never waits for them.
concurrency:
group: ci-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
# Test the Go version the matrix names, never one go.mod would download.
GOTOOLCHAIN: local
defaults:
run:
shell: bash
jobs:
lint:
# Everything that runs no test, first: the jobs of the required check
# wait for it. golangci-lint and govulncheck run at the versions of the
# Makefile (make check).
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: gofmt
run: |
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
echo "::error::gofmt -l reports unformatted files"
echo "$unformatted"
exit 1
fi
# go vet checks the files of one platform at a time: here those of every
# platform a release ships for, with and without the tag e2e of the live
# tests, so that a change that breaks them fails on the pull request,
# not at night.
- name: go vet
run: |
set -euo pipefail
for goos in linux darwin windows; do
echo "::group::GOOS=$goos"
GOOS=$goos go vet ./...
GOOS=$goos go vet -tags e2e ./...
echo "::endgroup::"
done
- uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: v2.14.0
# The docs toolchain's lock file, as the org's lint jobs check theirs:
# a uv.lock out of step with pyproject.toml fails here, not after the
# merge, where docs.yml's uv sync would resolve it again.
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: uv lock --check
run: uv lock --check
# The workflows parse, and their run: scripts pass the shellcheck that
# actionlint bundles.
- name: actionlint
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 -color
# Every script of the repository, with the shellcheck of the same image.
- name: shellcheck
run: |
docker run --rm -v "$PWD:/repo" -w /repo --entrypoint shellcheck \
rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 \
-x scripts/*/*.sh
# .hadolint.yaml says which rule is off and why.
- name: Dockerfile lint
run: docker run --rm -v "$PWD:/repo" -w /repo hadolint/hadolint:v2.15.1@sha256:32dac94127fd60b7b7e3fbfc65e1383b9b5e25c9bfd7b8536de7a539fe68a12d hadolint Dockerfile
test:
name: test (${{ matrix.os }}, go ${{ matrix.go }})
needs: lint
if: github.event_name != 'schedule'
runs-on: ${{ matrix.os }}
# The simulations of distribute and the lifecycle tests of the CLI run
# real git against the fake's git server: under the race detector each of
# the two packages takes minutes, more than go test's default limit of
# ten minutes per package on a busy 4-vCPU runner.
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.26.x', '1.27.x']
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: ${{ matrix.go }}
cache: true
# The race detector needs cgo; one OS is enough to catch races. The
# heavy tests run on Linux only (TOUCHMARK_HEAVY_TESTS). The newest Go
# on Linux also measures the coverage of the whole suite and holds it
# to the gate of make test (COVERAGE_MIN in the Makefile).
- name: go test -race, the coverage and its gate
if: runner.os == 'Linux' && matrix.go == '1.27.x'
run: make test
- name: go test -race
if: runner.os == 'Linux' && matrix.go != '1.27.x'
run: go test -race -timeout 45m ./...
- name: go test
if: runner.os != 'Linux'
run: go test -timeout 45m ./...
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
if: runner.os == 'Linux' && matrix.go == '1.27.x'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
disable_search: true
continue-on-error: true
contracts:
# The contract checks, on every pull request: the GitHub driver's
# GraphQL documents against the public schemas of github.com and GHES
# 3.19 (through the fake's subset, TestSchemaSubset), and its REST
# routes and query parameters against github/rest-api-description
# (TestRESTContracts). The files are downloaded fresh, so that a change
# on GitHub's side fails here, not in a live run; a failed download
# fails the job (curl -f, set -e) instead of skipping the tests.
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: GitHub's schemas and REST descriptions
run: |
set -euo pipefail
dir="$RUNNER_TEMP/github-contracts"
mkdir -p "$dir"
curl -fsSL --retry 3 -o "$dir/fpt.graphql" https://docs.github.com/public/fpt/schema.docs.graphql
curl -fsSL --retry 3 -o "$dir/ghes.graphql" https://docs.github.com/public/ghes-3.19/schema.docs-enterprise.graphql
rest=https://raw.githubusercontent.com/github/rest-api-description/main/descriptions
curl -fsSL --retry 3 -o "$dir/api.github.com.json" "$rest/api.github.com/api.github.com.json"
curl -fsSL --retry 3 -o "$dir/ghes-3.19.json" "$rest/ghes-3.19/ghes-3.19.json"
{
echo "GHFAKE_SCHEMA=$dir/fpt.graphql"
echo "GHFAKE_SCHEMA_GHES=$dir/ghes.graphql"
echo "GHFAKE_REST_DESCRIPTION=$dir/api.github.com.json"
echo "GHFAKE_REST_DESCRIPTION_GHES=$dir/ghes-3.19.json"
} >> "$GITHUB_ENV"
# -v and the grep make sure the checks ran rather than skipped.
- name: go test (contracts)
run: |
set -euo pipefail
go test -count=1 -v -run '^(TestSchemaSubset|TestGraphQLContracts|TestRESTContracts)$' \
./internal/platform/github/ ./internal/platform/github/ghfake/ | tee contracts.log
if grep -q -- '--- SKIP' contracts.log; then
echo "::error::a contract check skipped"
exit 1
fi
govulncheck:
# Known vulnerabilities in the code paths touchmark reaches, in its two
# dependencies and in the standard library of the Go it is built with
# (docs/project/threat-model.md). Nightly too: a new advisory fails a run
# without a push.
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
# Run outside the module's dependencies: go run with a version never
# touches go.mod. The version is the Makefile's (make check).
- name: govulncheck
run: go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
template:
# The hub template, bedrock-python/engineering-assets-template, whose
# own repository runs no workflows (docs/project/release.md, "After a release"):
# its scripts/validate.sh with touchmark from this commit (check on a
# fresh copy, actionlint, and with --public no pointer to notes a public
# reader cannot open), and the dry run of its GitHub workflow against
# the fake, job by job (TestTemplateWorkflow). Its GitLab and Gitea
# pipelines run in the live harnesses (docs/project/e2e.md, "The hub template").
# Nightly too: a change to the template fails here without a push to
# touchmark. Until the template repository is public, the job says so
# and checks nothing.
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Is the template public?
id: template
run: |
if git ls-remote --exit-code https://github.com/bedrock-python/engineering-assets-template HEAD >/dev/null 2>&1; then
echo "public=true" >> "$GITHUB_OUTPUT"
else
echo "::notice::bedrock-python/engineering-assets-template is not public yet: the template checks are skipped"
echo "public=false" >> "$GITHUB_OUTPUT"
fi
- if: steps.template.outputs.public == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- if: steps.template.outputs.public == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: bedrock-python/engineering-assets-template
path: .ci/template
persist-credentials: false
- if: steps.template.outputs.public == 'true'
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: scripts/validate.sh of the template
if: steps.template.outputs.public == 'true'
run: |
set -euo pipefail
go build -o "$RUNNER_TEMP/bin/touchmark" ./cmd/touchmark
TOUCHMARK="$RUNNER_TEMP/bin/touchmark" bash .ci/template/scripts/validate.sh --public
# -v and the grep make sure the dry run ran rather than skipped.
- name: go test -run TestTemplateWorkflow
if: steps.template.outputs.public == 'true'
run: |
set -euo pipefail
TOUCHMARK_E2E_TEMPLATE="$PWD/.ci/template" go test -count=1 -v -run '^TestTemplateWorkflow$' ./internal/e2e/github/ | tee template.log
if grep -q -- '--- SKIP' template.log; then
echo "::error::TestTemplateWorkflow skipped"
exit 1
fi
# Skips while the published template has no bitbucket-pipelines.yml.
- name: go test -run TestTemplatePipelines
if: steps.template.outputs.public == 'true'
run: TOUCHMARK_E2E_TEMPLATE="$PWD/.ci/template" go test -count=1 -v -run '^TestTemplatePipelines$' ./internal/e2e/bitbucket/
# Skips while the published template has no azure-pipelines.yml.
- name: go test -run TestTemplateAzurePipelines
if: steps.template.outputs.public == 'true'
run: TOUCHMARK_E2E_TEMPLATE="$PWD/.ci/template" go test -count=1 -v -run '^TestTemplateAzurePipelines$' ./internal/e2e/azure/
release-snapshot:
# The release without publishing anything (docs/project/release.md):
# the scripts of the release and of the Action by their tests;
# goreleaser check and a snapshot of every release file; the image as
# publish.yml builds it, from the snapshot's Linux archives
# (scripts/release/build-image.sh), pushed by digest to a registry on
# the runner, checked (scripts/release/check-image.sh) and scanned, and the
# Dockerfile's own build from source; then the Action, run from this
# tree against that registry, with a stand-in gh that accepts the
# attestation check (a snapshot has none) and records what the Action
# asked it. Nightly too: the scan then sees a new vulnerability in the
# base image before a release does.
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 30
services:
registry:
image: registry:3.1.2@sha256:ddf754342cfc8acc51a56d5d0ab6af06826461864460636d8bd5c546dab2a7b8
ports:
- 5000:5000
env:
# The version the snapshot stamps (GORELEASER_CURRENT_TAG) and the
# Action runs: any version but the 0.0.0 the Action refuses.
TAG: v0.0.1-ci
LOCAL_IMAGE: localhost:5000/touchmark
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: The Action's step and the release scripts
run: |
set -euo pipefail
bash scripts/action/run_test.sh
bash scripts/release/tools_test.sh
- uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
with:
syft-version: v1.54.0
# The same binfmt and BuildKit as publish.yml, pinned by digest and
# without the Actions cache, so that CI tests what a release uses. The
# builder shares the runner's network to push to the registry on
# localhost.
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
image: docker.io/tonistiigi/binfmt:qemu-v10.2.3@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0
platforms: arm64
cache-image: false
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
with:
driver-opts: |
image=docker.io/moby/buildkit:v0.33.1@sha256:cec9f139f45e93c5c69c60f8b07cfad9f43f4ef6b6a6cd917527fea5ff2e3dea
network=host
cache-binary: false
- uses: goreleaser/goreleaser-action@a4f02ab5355a589de60d917a659ce617ed19b3de # v7.3.0
with:
distribution: goreleaser
version: v2.18.2
args: check
# --snapshot publishes nothing; keyless signing needs the publish
# workflow's identity, so it is skipped here.
- uses: goreleaser/goreleaser-action@a4f02ab5355a589de60d917a659ce617ed19b3de # v7.3.0
with:
distribution: goreleaser
version: v2.18.2
args: release --snapshot --clean --skip=sign
env:
GORELEASER_CURRENT_TAG: ${{ env.TAG }}
- name: The image, as publish.yml builds it
id: image
run: bash scripts/release/build-image.sh dist "$LOCAL_IMAGE" "$TAG" | tee -a "$GITHUB_OUTPUT"
- name: Check the image
env:
DIGEST: ${{ steps.image.outputs.digest }}
run: |
set -euo pipefail
bash scripts/release/check-image.sh "$LOCAL_IMAGE@$DIGEST" "$TAG"
# Captured first: grep -q stops reading at the first match, and
# under pipefail docker's broken pipe would fail the step.
sbom="$(docker buildx imagetools inspect "$LOCAL_IMAGE@$DIGEST" --format '{{ json .SBOM }}')"
grep -q '"SPDX"' <<<"$sbom"
provenance="$(docker buildx imagetools inspect "$LOCAL_IMAGE@$DIGEST" --format '{{ json .Provenance }}')"
grep -q '"SLSA"' <<<"$provenance"
# The Dockerfile's own build from source, which docs and
# contributors use, passes the same checks.
docker buildx build --load -t touchmark:source --build-arg VERSION="$TAG" .
bash scripts/release/check-image.sh touchmark:source "$TAG"
# Known vulnerabilities in the image publish.yml would push, Alpine's
# packages and the binary's modules, as the org's images are scanned:
# a CRITICAL or HIGH one with a fix fails the check. This is the scan
# that blocks: publish.yml scans the release's image the same way, but
# only reports, since the release's tag exists by then.
- name: Scan the image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: ${{ env.LOCAL_IMAGE }}@${{ steps.image.outputs.digest }}
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: '1'
version: v0.70.0
cache: 'false'
# The Action resolves its version's tag: tag the digest as the tags
# job of publish.yml does, and give a copy of the Action this image
# and version. The stand-in gh accepts only the attestation check of
# that digest with the signer's policy, and only with a token.
- name: A copy of the Action for the local image
env:
DIGEST: ${{ steps.image.outputs.digest }}
run: |
set -euo pipefail
version=${TAG#v}
docker buildx imagetools create --prefer-index=false --tag "$LOCAL_IMAGE:$version" "$LOCAL_IMAGE@$DIGEST"
mkdir -p .ci/action/scripts/action "$RUNNER_TEMP/fake-gh"
cp scripts/action/run.sh .ci/action/scripts/action/
sed -e "s|^ TM_IMAGE: .*| TM_IMAGE: $LOCAL_IMAGE|" \
-e "s|^ TM_VERSION: .*| TM_VERSION: \"$version\"|" action.yml >.ci/action/action.yml
grep -n 'TM_IMAGE:\|TM_VERSION:' .ci/action/action.yml
want="attestation verify oci://$LOCAL_IMAGE@$DIGEST --bundle-from-oci --repo bedrock-python/touchmark"
want="$want --signer-workflow github.com/bedrock-python/touchmark/.github/workflows/publish.yml"
want="$want --source-ref refs/heads/master --deny-self-hosted-runners"
printf '%s\n' "$want" >"$RUNNER_TEMP/fake-gh/want"
cat >"$RUNNER_TEMP/fake-gh/gh" <<'EOF'
#!/usr/bin/env bash
echo "gh $*" >>"$RUNNER_TEMP/gh.log"
[ -n "${GH_TOKEN:-}" ] || { echo "stand-in gh: no GH_TOKEN" >&2; exit 4; }
[ "$*" = "$(cat "$RUNNER_TEMP/fake-gh/want")" ] || { echo "stand-in gh: unexpected $*" >&2; exit 1; }
EOF
chmod +x "$RUNNER_TEMP/fake-gh/gh"
echo "$RUNNER_TEMP/fake-gh" >>"$GITHUB_PATH"
# A hub that check accepts.
git init -q -b master .ci/hub
printf 'version: 1\nid: ci-smoke\n' >.ci/hub/hub.yml
printf 'version: 1\ndefaults:\n packs: [base]\ntargets:\n - repo: acme/demo\n' >.ci/hub/targets.yml
mkdir -p .ci/hub/packs/base
printf 'This file is shipped by the ci-smoke hub.\nIt is long enough to prove where it came from.\n' \
>.ci/hub/packs/base/SHARED.md
git -C .ci/hub add -A
git -C .ci/hub -c user.name=ci -c user.email=ci@example.invalid commit -q -m hub
- name: The Action, version
uses: ./.ci/action
with:
command: version
- name: The Action, check
uses: ./.ci/action
with:
command: check
hub: .ci/hub
format: json
- name: The Action verified the image it ran
run: |
set -euo pipefail
cat "$RUNNER_TEMP/gh.log"
test "$(grep -c '^gh attestation verify ' "$RUNNER_TEMP/gh.log")" = 2
fuzz:
needs: lint
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
# Every Fuzz* target in every package runs for 30 seconds. Go's fuzzer
# sometimes ends a timed run with "context deadline exceeded" although
# no input failed: that is a warning; a failing input is an error.
- name: go test -fuzz
run: |
set -euo pipefail
for pkg in $(go list ./...); do
listed="$(go test -list '^Fuzz' "$pkg")"
for target in $(printf '%s\n' "$listed" | grep '^Fuzz' || true); do
echo "::group::$pkg $target"
if ! go test -run '^$' -fuzz "^${target}\$" -fuzztime 30s "$pkg" > fuzz.log 2>&1; then
cat fuzz.log
if grep -q 'context deadline exceeded' fuzz.log && ! grep -q 'Failing input written to' fuzz.log; then
echo "::warning::$pkg $target: the fuzzer stopped at its deadline with no failing input"
else
exit 1
fi
else
cat fuzz.log
fi
echo "::endgroup::"
done
done
- name: Upload failing fuzz inputs
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: fuzz-failures
path: '**/testdata/fuzz/**'
if-no-files-found: ignore
# A dependency a pull request adds or bumps is looked up before it is
# merged: a known vulnerability of high severity fails the check.
dependency-review:
name: New dependencies carry no known vulnerability
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
fail-on-severity: high
# The jobs below are outside the required check: the live forges, nightly
# and on every push, and the long runs of distribute, nightly. A pull request
# labelled e2e-gitlab runs the GitLab ones (e2e-gitlab.yml).
e2e-forge:
# The live e2e tests (internal/e2e) against Gitea and Forgejo in Docker:
# the platform conformance suite on the gitea driver, plan and distribute
# end to end, and the platform facts the driver relies on. The latest
# Gitea and Forgejo on every pull request and push, every supported
# version (Gitea 1.26, 1.27 and 28, Forgejo 15 LTS and 16) nightly and by
# hand. docs/project/e2e.md explains how to run them locally.
name: e2e (${{ matrix.image }})
runs-on: ubuntu-latest
# The job pulls golang:1.27 (1.3 GB) and the forge, boots the forge (at
# most 3 minutes), seeds it and builds the tests with the race detector
# from a cold cache before go test starts with its own limit of
# TOUCHMARK_E2E_TEST_TIMEOUT. The job's limit leaves that much room, so
# that a hung run ends by go test's timeout, with its goroutine dump, not
# by the runner killing the job. The timeout's panic never returns to
# TestMain, so a watchdog there prints the FINDINGS gathered so far a
# minute before it.
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
image: ${{ fromJSON((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '["docker.gitea.com/gitea:1.26.4","docker.gitea.com/gitea:1.27.3","docker.gitea.com/gitea:28.1.0","codeberg.org/forgejo/forgejo:15.0.9","codeberg.org/forgejo/forgejo:16.0.5"]' || '["docker.gitea.com/gitea:28.1.0","codeberg.org/forgejo/forgejo:16.0.5"]') }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The script starts the forge, seeds it and runs go test in
# golang:1.27 next to it; it removes everything it created on exit.
- name: scripts/e2e/gitea.sh
env:
IMAGE: ${{ matrix.image }}
TOUCHMARK_E2E_TEST_TIMEOUT: 25m
run: bash scripts/e2e/gitea.sh "$IMAGE"
property:
# TestPropertyDistribute at full size: 10 000 scenarios, seeds 1 to
# 10 000, in four shards. The default run of the test job covers the
# first 100.
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
name: property (shard ${{ matrix.shard }})
runs-on: ubuntu-latest
timeout-minutes: 240
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: go test -run TestPropertyDistribute
env:
SHARD: ${{ matrix.shard }}
SCENARIOS: '2500'
run: |
set -euo pipefail
export TOUCHMARK_PROPERTY_SCENARIOS="$SCENARIOS"
export TOUCHMARK_PROPERTY_SEED="$((1 + SHARD * SCENARIOS))"
go test -count=1 -timeout 230m -run '^TestPropertyDistribute$' ./internal/distribute/
scale:
# The scale test at twice the test job's size, nightly: distribute
# over 10 000 targets of the fake on a GitHub and a GitLab
# that rate limit, fail and time out, run after run until the fleet is
# done (TestScale), and the plan of a hub pull request that changes one
# pack over the same fleet (TestPlanScopeScale), under the race
# detector. The test job runs both at 5000 targets.
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.27.x'
cache: true
- name: go test -run 'TestScale|TestPlanScopeScale'
env:
TOUCHMARK_SCALE_TARGETS: '10000'
run: go test -race -count=1 -timeout 50m -run '^(TestScale|TestPlanScopeScale)$' ./internal/distribute/
# Sentinel job — use this as the single required status check in branch protection.
all-checks-passed:
name: All checks passed
if: always()
needs: [lint, test, contracts, govulncheck, template, release-snapshot, fuzz, dependency-review]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
# Every job in needs must succeed. Skipped passes only for a job whose
# own if: leaves this event out: the nightly run has no test or fuzz,
# and only a pull request has dependency-review. Anything else, a job
# skipped by a wrong if: included, fails the check.
- name: Every job it needs succeeded
env:
NEEDS: ${{ toJSON(needs) }}
MAY_SKIP: >-
${{ github.event_name == 'schedule' && 'test fuzz dependency-review'
|| github.event_name != 'pull_request' && 'dependency-review' || '' }}
run: |
jq -r 'to_entries[] | "\(.key): \(.value.result)"' <<<"$NEEDS"
jq -e --arg may_skip "$MAY_SKIP" '
($may_skip | split(" ")) as $skip
| all(to_entries[];
.value.result == "success"
or (.value.result == "skipped" and (.key as $job | any($skip[]; . == $job))))
' <<<"$NEEDS" >/dev/null || {
echo "::error::a job the check needs did not succeed"
exit 1
}