Repository navigation
Expand file tree
/
Copy pathTaskfile.yml
More file actions
198 lines (181 loc) · 10.4 KB
/
Copy pathTaskfile.yml
File metadata and controls
198 lines (181 loc) · 10.4 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
# go-tools - the Go the spin-stack repositories build with, and nothing any of them ships.
#
# task test the Go tests, under the race detector, held to a coverage floor
# task lint golangci-lint with spin's rules, at spin's version, this repository's own
# gates run on itself: ctxlife, testquality, refs, sfw, taskgate, testidentity,
# and actionlint
# task mutate break what the change against BASE (origin/main) touched, and ask the tests
# task coverage coverage.out's numbers, to the terminal or the workflow run's summary
# task trace where a workflow run's time went (ghatrace), to its summary and _output/ghatrace
# task versions which pins in versions.yaml are behind; task bump NAME=... moves one
# task site the documentation site under site/, built into site/dist
version: '3'
silent: true
vars:
OUT: '{{.ROOT_DIR}}/_output'
VERSIONS: go run ./cmd/versions
OWNER:
sh: echo "$(id -u):$(id -g)"
tasks:
test:
desc: The Go tests, under the race detector, with their coverage in coverage.out and held to a floor.
vars:
# Below today's 61.4%. Raise it when the number has moved up and stayed; never lower it to
# let a change through, or it is a gate that reports whatever it is given.
COVERAGE_FLOOR: 60
cmds:
# Each package and each test of a second or more a span of the run's trace (task trace);
# off a runner only go test.
- go run ./cmd/ghatrace gotest -name "go test" -- go test -race -covermode=atomic -coverprofile=coverage.out ./...
# The anchor makes it the total line, not a function with "total" in its name.
- |
total=$(go tool cover -func=coverage.out | awk '/^total:/ {print $3}' | tr -d '%')
awk -v got="$total" -v floor={{.COVERAGE_FLOOR}} 'BEGIN {
if (got + 0 < floor + 0) { printf "coverage is %s%%, below the floor of %s%%\n", got, floor; exit 1 }
printf "coverage: %s%% (floor %s%%)\n", got, floor
}'
lint:
desc: golangci-lint with spin's rules (.golangci.yml), at spin's version, over the Go.
cmds:
# As this user, on this host's module and build caches: the container fetches nothing a
# build here has not, and leaves nothing root's behind. Its own cache is under _output.
# GOTOOLCHAIN=local: the linter reads the code with the Go its image carries; go.mod's
# toolchain line is what builds and tests run under, and fetching it in here has no sumdb.
- mkdir -p {{.OUT}}/golangci-lint
# A span of the lint step in the run's trace (task trace); off a runner it only runs.
- >-
go run ./cmd/ghatrace span -name golangci-lint --
docker run --rm -u {{.OWNER}} -v {{.ROOT_DIR}}:/work -w /work
-v "$(go env GOMODCACHE)":/go/pkg/mod -v "$(go env GOCACHE)":/cache/go-build
-e GOMODCACHE=/go/pkg/mod -e GOCACHE=/cache/go-build -e GOLANGCI_LINT_CACHE=/work/_output/golangci-lint
-e HOME=/tmp -e GOTOOLCHAIN=local
$({{.VERSIONS}} ref golangci-lint) golangci-lint run ./...
# What this repository gives the others, held to itself first.
- go run ./cmd/ctxlife
- go run ./cmd/testquality ./...
- go run ./cmd/refs
- go run ./cmd/mutate -stale-exempts
- go run ./cmd/sfw check
- go run ./cmd/taskgate -allow .github/taskgate-allow.txt -task {{.TASK_EXE}}
# A commit since main whose author is a test's: what a test's git wrote into the checkout's
# config. On main, the range is empty.
- go run ./cmd/testidentity -base origin/main
# A vulnerability the code calls and that has a fix fails, the standard library's too: the
# answer is the upgrade. It asks vuln.go.dev, so lint needs the network.
- go run ./cmd/vulngate ./...
# The trusted root every repository verifies with is renewed here, the day Sigstore moves.
- go run ./cmd/trustroot -check
- task: lint:workflows
lint:workflows:
desc: actionlint over .github/, and every action pinned by its commit.
cmds:
# -shellcheck= because actionlint runs whatever shellcheck is on PATH: a runner has one and
# a laptop may not, and a gate whose answer depends on the machine is not a gate.
- go run github.com/rhysd/actionlint/cmd/actionlint@$({{.VERSIONS}} version actionlint) -shellcheck=
# The site's CI page shows this workflow for a repository to copy.
- go run github.com/rhysd/actionlint/cmd/actionlint@$({{.VERSIONS}} version actionlint) -shellcheck= site/examples/github/.github/workflows/*.yml
# A tag is whatever its owner points it at; the comment is the version Dependabot moves.
- cmd: |
if grep -rnE '^\s*(- )?uses: [^./][^ ]*@' .github site/examples/github/.github | grep -vE '@[0-9a-f]{40} # v[0-9]'; then
echo "an action above is used by a tag its owner can move: pin it by commit SHA, with '# vX.Y.Z' after it"
exit 1
fi
mutate:
desc: Break what the change against BASE touched and fail on an edit no test refuses (BASE=origin/main; in CI, what the run's event diffs against).
cmds:
# In a workflow, the base is the event's: a pull request's is what it merges into, a push's
# what the branch was before it (BEFORE, which the workflow hands in), or the commit before
# when that is not in the clone - a branch's first push.
- |
base="{{.BASE}}"
if [ -z "$base" ] && [ "${GITHUB_EVENT_NAME:-}" = pull_request ]; then
base="origin/$GITHUB_BASE_REF"
elif [ -z "$base" ] && [ -n "${GITHUB_ACTIONS:-}" ]; then
base="${BEFORE:-}"
git rev-parse --verify -q "$base^{commit}" >/dev/null || base=HEAD~1
fi
go run ./cmd/mutate -base "${base:-origin/main}" -max 200
coverage:
desc: Write coverage.out's total and per-function numbers to the run's summary ($GITHUB_STEP_SUMMARY, or the terminal) and coverage.html.
cmds:
- |
out="${GITHUB_STEP_SUMMARY:-/dev/stdout}"
[ -f coverage.out ] || { echo "coverage.out was not produced; the tests may have failed before coverage ran." >> "$out"; exit 0; }
func=$(go tool cover -func=coverage.out)
printf '## Go coverage\n\n**%s** total\n\n<details><summary>Function detail</summary>\n\n```\n%s\n```\n</details>\n' "$(awk '/^total:/ {print $3}' <<< "$func")" "$func" >> "$out"
go tool cover -html=coverage.out -o coverage.html
trace:
desc: Where the run's time went (ghatrace), to its summary and _output/ghatrace; its own job's spans are in $RUNNER_TEMP.
cmds:
- go run ./cmd/ghatrace run -spans "${RUNNER_TEMP:-}"
release:build:
desc: Every command, for each platform a repository runs them on, as _output/release/go-tools_<os>_<arch>.tar.gz.
# What a repository's `go tool task` (cmd/task) unpacks onto Task's PATH, so that no repository
# compiles these commands or takes their modules. Stripped and without cgo: a program that runs
# on any machine of its platform, the same bytes from the same commit. The archive's members
# are sorted, owned by nobody and dated by the commit, for the same reason. cmd/task is not in
# it: it is the command a repository compiles, and its directory is first on Task's PATH, where
# a second task would stand in front of Task.
vars:
PLATFORMS: linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
COMMANDS:
sh: go list ./cmd/... | grep -v '/cmd/task$' | tr '\n' ' '
cmds:
- rm -rf {{.OUT}}/release
- for: {var: PLATFORMS, split: ' '}
cmd: >-
CGO_ENABLED=0 GOOS={{index (splitList "/" .ITEM) 0}} GOARCH={{index (splitList "/" .ITEM) 1}}
go build -trimpath -ldflags='-s -w' -o {{.OUT}}/release/{{replace "/" "_" .ITEM}}/ {{.COMMANDS}}
- for: {var: PLATFORMS, split: ' '}
cmd: >-
tar --sort=name --owner=0 --group=0 --numeric-owner --mtime="@$(git show -s --format=%ct HEAD)"
-C {{.OUT}}/release/{{replace "/" "_" .ITEM}} -czf {{.OUT}}/release/go-tools_{{replace "/" "_" .ITEM}}.tar.gz .
# What cmd/task holds a download of the latest release to, no versions.yaml pinning it.
- cd {{.OUT}}/release && sha256sum go-tools_*.tar.gz > checksums.txt
release:name:
desc: The release SHA is - released already, or the next v<YYYYMMDD>.<NN> today - as version= and new= for $GITHUB_OUTPUT.
requires:
vars: [SHA]
cmds:
- go run ./internal/release/cmd/releasename {{.SHA}} >> "${GITHUB_OUTPUT:-/dev/stdout}"
release:publish:
desc: Publish _output/release as the GitHub release VERSION of SHA, which tags it (GH_TOKEN).
# The tag is made by the release, last: a release that failed to publish is no tag that names a
# commit with no files.
requires:
vars: [SHA, VERSION]
cmds:
- >-
gh release create {{.VERSION}} {{.OUT}}/release/*.tar.gz {{.OUT}}/release/checksums.txt
--target {{.SHA}} --title {{.VERSION}}
--notes "Every go-tools command at {{.SHA}}, attested as built by release.yml. A repository's \`go tool task\` runs the latest."
site:
desc: Build the documentation site (site/, Astro Starlight) into site/dist; task site:dev serves it with live reload.
cmds:
# From the root, where ./cmd/sfw is: site/ is a module of its own (site/go.mod says why).
- go run ./cmd/sfw pnpm --dir site install --frozen-lockfile
- pnpm --dir site build
site:dev:
desc: Serve the documentation site with live reload.
cmds:
- go run ./cmd/sfw pnpm --dir site install --frozen-lockfile
- pnpm --dir site dev
versions:
desc: Say which of versions.yaml's pins are behind their upstream, and of those cmd/sfw and cmd/exposure carry (exits 1 when any is).
cmds:
# Every file, whichever is behind: the others are the pins go tool sfw and go tool exposure
# embed for every repository.
- |
status=0
{{.VERSIONS}} check || status=1
{{.VERSIONS}} check -file cmd/sfw/versions.yaml || status=1
{{.VERSIONS}} check -file cmd/exposure/versions.yaml || status=1
exit $status
bump:
desc: Pin one entry of versions.yaml at a version, the newest by default (task bump NAME=golangci-lint [VERSION=v2.14.0]; FILE=cmd/sfw/versions.yaml or cmd/exposure/versions.yaml for what those commands embed).
requires:
vars: [NAME]
vars:
FILE: '{{.FILE | default "versions.yaml"}}'
cmds:
- '{{.VERSIONS}} bump -file {{.FILE}} {{.NAME}} {{.VERSION}}'