-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathTaskfile.yaml
More file actions
393 lines (365 loc) · 17 KB
/
Copy pathTaskfile.yaml
File metadata and controls
393 lines (365 loc) · 17 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
version: "3"
vars:
# The frontend image the `# syntax=` line of every descriptor names.
FRONTEND_IMAGE: docker/sandbox-kit
FRONTEND_TAG: "3"
# Registry prefix kit images are pushed under, e.g. docker.io/example.
# No default: pushing to someone else's namespace should never be one
# forgotten variable away.
REGISTRY: ""
# Example kits and the tag each pushes as (override per invocation:
# `task kit:push KIT=gh TAG=2.98.0 REGISTRY=docker.io/me`).
# `team`, `claude-acp-set`, and `codex-acp-set` are deliberately absent:
# they are set examples, and the kits they list are registry references,
# so they only build once those kits have been pushed to the namespace
# their `registry` arg names. Build one on its own:
# `task kit:build KIT=team BUILD_ARGS='--build-arg registry=docker.io/me'`.
KITS: hello gh tool shell alpine builder motd wordpress claude claude-mixin claude-acp codex codex-mixin codex-acp cursor cursor-mixin devin devin-mixin docker-agent docker-agent-mixin gemini gemini-mixin opencode opencode-mixin skills dev-tools optional-cache git-signing github-ssh review-skill
# Empty means "the kit's own version answers": build and push refresh an
# upstream-tracked descriptor from the vendor's latest release and tag
# with it, so an image tag can never name a version the kit did not
# install. Set TAG to pin one build instead.
TAG: ""
# Extra flags for one kit's build, by the kit's own arg names — the
# frontend validates them and hands the Dockerfile its declared
# buildArg name: BUILD_ARGS='--build-arg version=2.99.0'.
BUILD_ARGS: ""
# SBOM generator image for buildx attestations (Docker Scout indexer,
# hardened build from the DHI catalog).
SBOM_GENERATOR: dhi.io/scout-sbom-indexer:1
# The commit a frontend build stamps into its binary, and from there
# into every kit that frontend publishes. It travels as a build arg
# because the build cannot read it: .dockerignore excludes .git.
REVISION:
sh: |
sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
# A tree with uncommitted edits is not the commit it names, and a
# stamp that hides that is worse than no stamp at all. Untracked
# files count: the build context copies them, so a new .go file
# changes the binary while `git diff` still reports HEAD clean.
# --porcelain lists them and honours .gitignore, which leaves the
# build outputs .dockerignore drops out of the context anyway.
[ -z "$(git status --porcelain 2>/dev/null)" ] || sha="$sha-dirty"
printf '%s' "$sha"
# Flags every published artifact carries. Defined once: platforms,
# provenance and the SBOM generator drifting between the frontend and
# the kits, or between two push targets, is the kind of difference
# nobody notices until an artifact is missing an attestation.
PUBLISH_FLAGS: "--sbom=generator={{.SBOM_GENERATOR}} --provenance=true --platform=linux/amd64,linux/arm64"
# The build stamp every frontend build carries. Defined once for the
# same reason as PUBLISH_FLAGS: a build that forgets it produces a
# frontend, and kits, that cannot say what made them. The version sits
# beside it per target — only a push knows the release it publishes as.
STAMP_FLAGS: "--build-arg REVISION={{.REVISION}}"
tasks:
default:
silent: true
cmds:
- task --list
test:
desc: Run all Go tests
cmds:
- go test ./...
test:unit:
desc: >-
Every Go test except the sandbox conformance suite, which drives a
fake runtime through hundreds of sandbox lifecycles and takes
minutes. This is the fast signal.
cmds:
# Discovery is judged before filtering: piping go list into grep
# reports the grep's status, and even capturing the list is not
# enough, since a failed assignment does not end the block. A
# broken listing must fail the run, not narrow it.
- |
set -e
packages=$(go list ./...)
go test $(printf '%s\n' "$packages" | grep -v /tck/sandbox)
test:tck:
desc: The sandbox conformance suite, judged against the fake adapter
cmds:
- go test ./tck/sandbox/...
test:e2e:
desc: >-
Run end-to-end guidance and set builds against a throwaway registry.
Needs Docker with buildx; `task test` skips them.
cmds:
- KIT_E2E=1 go test ./cmd/frontend/ -run 'Test(Guidance|Set)BuildsEndToEnd' -v -count=1 -timeout 15m
lint:
desc: golangci-lint and markdownlint, as CI runs them
cmds:
- task: lint:go
- task: lint:md
lint:go:
desc: golangci-lint with the repository's .golangci.yml
cmds:
- golangci-lint run
lint:md:
desc: >-
markdownlint over the file set .markdownlint-cli2.yaml names. The
image is pinned by digest (v0.23.2) rather than tag, so a local run
and CI judge alike and a moved tag cannot change the verdict. CI
runs this same target.
cmds:
- docker run --rm -v "$(pwd)":/workdir davidanson/markdownlint-cli2@sha256:839558fd0d36c46da0e01ea84fd1d20a2822b5a8a60c16dc9708f0bb7c9e903b
validate:
desc: gofmt + go vet across the module
cmds:
- test -z "$(gofmt -l .)" || { gofmt -l .; echo "gofmt failures above"; exit 1; }
- go vet ./...
tck:kit:
desc: >-
Check one kit against the specification: task tck:kit REF=docker.io/me/sbx-kit-gh:1.0.0.
The same checks run inside the frontend during a build; running them
here judges what the exporter and the registry produced, and any kit
this frontend did not build.
requires:
vars: [REF]
cmds:
- go run ./cmd/kit-tck validate {{.REF}}
tck:kit:layout:
desc: >-
Check a kit in an OCI layout directory, the shape
`buildx --output type=oci` writes: task tck:kit:layout DIR=/tmp/out TAG=sha256:...
requires:
vars: [DIR, TAG]
cmds:
- go run ./cmd/kit-tck validate --layout {{.DIR}} {{.TAG}}
tck:runtime:
desc: >-
Check a runtime against the capability pages through its adapter:
task tck:runtime ADAPTER=./my-adapter. The contract the adapter
implements is docs/spec/conformance.md.
requires:
vars: [ADAPTER]
cmds:
- go run ./cmd/kit-tck runtime --adapter {{.ADAPTER}}
tck:runtime:sbx:
desc: >-
The same suite against Docker Sandboxes, through the adapter in
tck/adapters/sbx. Needs the sbx CLI installed and signed in; the
suite drives it through hundreds of sandbox lifecycles, and the
first run also builds every fixture kit, which is why the deadline
is generous. Pass suite flags after --, as
`task tck:runtime:sbx -- --verbose`.
vars:
TIMEOUT: '{{.TIMEOUT | default "2h"}}'
cmds:
- go run ./cmd/kit-tck runtime --adapter {{.ROOT_DIR}}/tck/adapters/sbx --timeout {{.TIMEOUT}} {{.CLI_ARGS}}
frontend:build:
desc: >-
Verify the frontend builds for both platforms. It produces no image:
a multi-platform build cannot load into the local store, so the
output is discarded and this is a check, not an artifact. For a
local image use `docker build -t {{.FRONTEND_IMAGE}}:{{.FRONTEND_TAG}} .`,
or frontend:dev while iterating; to publish, frontend:push, which
carries the guard a build target must not.
cmds:
- docker buildx b -t {{.FRONTEND_IMAGE}}:{{.FRONTEND_TAG}} . {{.PUBLISH_FLAGS}} {{.STAMP_FLAGS}} --output type=cacheonly
frontend:dev:
desc: >-
Build the frontend under a unique dev tag and print the syntax line to
use. BuildKit caches the frontend image resolution per reference, so
rebuilding under a reused tag (like :3) can keep dispatching the stale
binary; a fresh tag sidesteps that during frontend development.
vars:
DEV_TAG:
sh: date +dev-%s
cmds:
# No VERSION: a dev tag is a cache-busting device, not a release,
# and the revision is what makes the build traceable anyway.
- docker build -t {{.FRONTEND_IMAGE}}:{{.DEV_TAG}} . {{.STAMP_FLAGS}}
- 'echo "frontend built — use in descriptors under test:"'
- 'echo " # syntax={{.FRONTEND_IMAGE}}:{{.DEV_TAG}}"'
frontend:push:
desc: >-
Build and publish the frontend image under FRONTEND_VERSION:
FRONTEND_VERSION=3.0.0 FRONTEND_PUSH_OK=1 task frontend:push
Always pushes docker/sandbox-kit:<FRONTEND_VERSION>. Also pushes
floating docker/sandbox-kit:<major> when FRONTEND_PROMOTE_MAJOR=1
(release workflow sets this only for the highest stable v3.X.Y).
One buildx invocation does both: a multi-platform build produces no
local image, so a separate docker push has nothing to send.
requires:
vars: [FRONTEND_VERSION]
preconditions:
- sh: test -n "{{.FRONTEND_PUSH_OK}}"
msg: >-
{{.FRONTEND_IMAGE}}:{{.FRONTEND_VERSION}} is a public syntax
reference; pass FRONTEND_PUSH_OK=1 to confirm the push is
intentional (override FRONTEND_IMAGE to push elsewhere).
cmds:
- |
set -euo pipefail
ver="{{.FRONTEND_VERSION}}"
tags=(-t "{{.FRONTEND_IMAGE}}:$ver")
if [ "{{.FRONTEND_PROMOTE_MAJOR}}" = "1" ]; then
major=$(printf '%s' "$ver" | cut -d. -f1)
tags+=(-t "{{.FRONTEND_IMAGE}}:$major")
fi
# The tag the image publishes under is the version inside the
# binary: one fact, the same rule kit tags follow, so a frontend
# can never report a release it was not published as.
docker buildx b "${tags[@]}" . {{.PUBLISH_FLAGS}} {{.STAMP_FLAGS}} \
--build-arg VERSION="$ver" --push
versions:check:
desc: >-
Compare the example kits' version-arg defaults against upstream
latest releases, changing nothing. Which kits track what lives in
scripts/kit-version.sh, the same map build and push resolve a tag
through, so the report and the build cannot disagree about latest.
silent: true
cmds:
- scripts/kit-version.sh check
versions:update:
desc: >-
Refresh every upstream-tracked kit's version-arg default to the
vendor's latest release: task versions:update (one kit:
KIT=claude). This edits descriptors — review the diff, since the
version an example pins is what its provide advertises.
silent: true
cmds:
- |
set -euo pipefail
for kit in ${KIT:-$(scripts/kit-version.sh kits)}; do
before=$(scripts/kit-version.sh current "$kit")
after=$(scripts/kit-version.sh update "$kit")
if [ "$before" = "$after" ]; then
printf '%-16s %s\n' "$kit" "$after"
else
printf '%-16s %s -> %s\n' "$kit" "$before" "$after"
fi
done
env:
KIT: "{{.KIT}}"
kit:build:
desc: >-
Build one example kit against the frontend its descriptor names:
task kit:build KIT=claude [TAG=2.98.0]. Without TAG the kit's own
version answers, refreshed from upstream first where the kit tracks
a vendor release — the tag then names exactly what the build
installed. During frontend development use kit:dev instead — the
builder can pin a reused frontend tag like :3 to a stale image. A
kit is its .yaml descriptor when one exists, its comment-descriptor
.dockerfile otherwise.
requires:
vars: [KIT]
vars:
KIT_DIR:
sh: test -d "examples/{{.KIT}}" && echo "examples/{{.KIT}}" || echo "{{.KIT}}"
# Task creates `dir:` if it is missing, so without this a mistyped KIT
# silently leaves an empty directory at the repo root instead of failing.
preconditions:
- sh: test -f "{{.ROOT_DIR}}/{{.KIT_DIR}}/{{.KIT}}.yaml" || test -f "{{.ROOT_DIR}}/{{.KIT_DIR}}/{{.KIT}}.dockerfile"
msg: "no kit named '{{.KIT}}': expected {{.KIT_DIR}}/{{.KIT}}.yaml or .dockerfile"
dir: '{{.KIT_DIR}}'
cmds:
- |
set -euo pipefail
tag="{{.TAG}}"
# A version build arg outranks the descriptor in the frontend, so
# the tag follows it rather than the file — and a build pinned
# that way refreshes nothing. Both argument sources are read, in
# the order the build line puts them.
[ -n "$tag" ] || tag=$({{.ROOT_DIR}}/scripts/kit-version.sh override "{{.BUILD_ARGS}}" "{{.CLI_ARGS}}")
[ -n "$tag" ] || tag=$({{.ROOT_DIR}}/scripts/kit-version.sh update {{.KIT}})
docker buildx build . -f "$(test -f {{.KIT}}.yaml && echo {{.KIT}}.yaml || echo {{.KIT}}.dockerfile)" \
{{.BUILD_ARGS}} -t {{.KIT}}-kit:"$tag" {{.CLI_ARGS}}
kit:dev:
desc: >-
Build one example kit against the CURRENT frontend source:
task kit:dev KIT=gh. Builds the frontend under a unique tag and
rewrites the descriptor's syntax line to it in a temp copy, because
the builder's frontend-image resolution can serve a stale binary for
a reused tag no matter how the tag is rebuilt locally.
requires:
vars: [KIT]
vars:
KIT_DIR:
sh: test -d "examples/{{.KIT}}" && echo "examples/{{.KIT}}" || echo "{{.KIT}}"
DEV_TAG:
sh: date +dev-%s
BUILD_DIR:
sh: mktemp -d /tmp/sandbox-kit-dev-XXXXXX
cmds:
- defer: rm -rf {{.BUILD_DIR}}
- docker build -q -t {{.FRONTEND_IMAGE}}:{{.DEV_TAG}} . {{.STAMP_FLAGS}} >/dev/null
# `src/.` rather than `src/`: BSD cp copies the contents of a
# trailing-slash source, GNU cp creates <dest>/<src> instead, and the
# build below looks for the descriptor directly under BUILD_DIR. The
# `/.` spelling copies contents under both.
- cp -r {{.KIT_DIR}}/. {{.BUILD_DIR}}/
# Only line 1 is rewritten: a comment-descriptor kit's SECOND syntax
# stanza names the content's frontend and must stay untouched. The
# version is read, never refreshed: iterating on the frontend should
# not reach for the network, nor leave a bump in the tree behind a
# build.
- |
set -euo pipefail
F="$(test -f {{.BUILD_DIR}}/{{.KIT}}.yaml && echo {{.KIT}}.yaml || echo {{.KIT}}.dockerfile)"
tag="{{.TAG}}"
[ -n "$tag" ] || tag=$(scripts/kit-version.sh override "{{.BUILD_ARGS}}")
[ -n "$tag" ] || tag=$(scripts/kit-version.sh current {{.KIT}})
# `-i.bak` then remove it: BSD sed needs an argument for -i and GNU
# sed reads a bare '' as the script, then treats the expression as a
# filename and fails — which under `set -e` aborts this task on Linux.
# A suffix both accept is the portable spelling.
sed -i.bak '1s|^# syntax=.*|# syntax={{.FRONTEND_IMAGE}}:{{.DEV_TAG}}|' {{.BUILD_DIR}}/"$F"
rm -f {{.BUILD_DIR}}/"$F".bak
docker buildx build {{.BUILD_DIR}} -f {{.BUILD_DIR}}/"$F" \
{{.SECRETS}} {{.BUILD_ARGS}} -t {{.KIT}}-kit:"$tag"
- docker rmi {{.FRONTEND_IMAGE}}:{{.DEV_TAG}} >/dev/null
kits:dev:
desc: Build every example kit against the current frontend source
cmds:
- for: { var: KITS }
task: kit:dev
vars: { KIT: "{{.ITEM}}" }
kit:push:
desc: >-
Build and push one example kit:
task kit:push KIT=claude REGISTRY=docker.io/me [TAG=2.98.0]
Without TAG the kit publishes under its own version, refreshed from
upstream first where it tracks a vendor release, so the published
tag and the installed version are one fact. (The gh kit verifies
release provenance when a token is supplied: add
SECRETS='--secret id=gh_token,env=GH_TOKEN'.)
requires:
vars: [KIT]
preconditions:
- sh: test -n "{{.REGISTRY}}"
msg: "REGISTRY is required, e.g. REGISTRY=docker.io/me"
# As kit:build: `dir:` is created if absent, so a mistyped KIT would
# otherwise leave an empty directory behind rather than fail.
- sh: test -f "{{.ROOT_DIR}}/{{.KIT_DIR}}/{{.KIT}}.yaml" || test -f "{{.ROOT_DIR}}/{{.KIT_DIR}}/{{.KIT}}.dockerfile"
msg: "no kit named '{{.KIT}}': expected {{.KIT_DIR}}/{{.KIT}}.yaml or .dockerfile"
vars:
KIT_DIR:
sh: test -d "examples/{{.KIT}}" && echo "examples/{{.KIT}}" || echo "{{.KIT}}"
dir: '{{.KIT_DIR}}'
cmds:
- |
set -euo pipefail
tag="{{.TAG}}"
# A version build arg outranks the descriptor in the frontend, so
# the tag follows it rather than the file — and a build pinned
# that way refreshes nothing. Both argument sources are read, in
# the order the build line puts them.
[ -n "$tag" ] || tag=$({{.ROOT_DIR}}/scripts/kit-version.sh override "{{.BUILD_ARGS}}" "{{.CLI_ARGS}}")
[ -n "$tag" ] || tag=$({{.ROOT_DIR}}/scripts/kit-version.sh update {{.KIT}})
docker buildx build . -f "$(test -f {{.KIT}}.yaml && echo {{.KIT}}.yaml || echo {{.KIT}}.dockerfile)" \
{{.SECRETS}} {{.BUILD_ARGS}} \
--push -t {{.REGISTRY}}/sbx-kit-{{.KIT}}:"$tag" {{.PUBLISH_FLAGS}} {{.CLI_ARGS}}
kits:build:
desc: Build every example kit ({{.KITS}})
cmds:
- for: { var: KITS }
task: kit:build
vars: { KIT: "{{.ITEM}}" }
kits:push:
desc: >-
Build and push every example kit, each under its own version:
task kits:push REGISTRY=docker.io/me (TAG pins them all to one tag)
cmds:
- for: { var: KITS }
task: kit:push
vars: { KIT: "{{.ITEM}}" }