Repository navigation
Expand file tree
/
Copy pathTaskfile.yml
More file actions
269 lines (242 loc) · 13.6 KB
/
Copy pathTaskfile.yml
File metadata and controls
269 lines (242 loc) · 13.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
version: '3'
# The VS Code extension spin-stack.spinbox: TypeScript in src/, bundled by esbuild into dist/,
# packaged by vsce. Every install goes through Socket Firewall (sfw), and every workflow
# step is one of these tasks (taskgate), so a developer runs what CI runs.
vars:
# The release a publish is for: the tag a workflow runs on, or TAG=v1.2.3 on the command line.
TAG: '{{.GITHUB_REF_NAME}}'
# A tag with -pre in it is a pre-release: vsce publishes a VSIX as one only when it was packaged
# as one, so package:all has to give this to vsce package too.
PRE_RELEASE: '{{if contains "-pre" .TAG}}--pre-release{{end}}'
tasks:
install:
desc: Install the dependencies from the lockfile, through Socket Firewall (sfw).
cmds:
- sfw pnpm install --frozen-lockfile
build:
desc: Bundle the extension into dist/extension.js.
cmds:
- pnpm build
lint:
desc: Lint and check the formatting with Biome.
cmds:
- pnpm lint
typecheck:
desc: Typecheck src/ and test/ with tsc.
cmds:
- pnpm typecheck
test:
desc: The unit tests - Vitest, against the fake spin in test/fake-spin/, with coverage held to the floor in vitest.config.mts - and hack/'s Go.
cmds:
- pnpm test
- go test ./hack/...
coverage:summary:
desc: The coverage task test left in coverage/, as the job's summary (hack/coverage-summary.mjs); the HTML is coverage/index.html.
cmds:
- node hack/coverage-summary.mjs
test:cli:
desc: The extension's reader against the real spin CLI of the pinned release (test/cli/), pulled and checked by package:spin - minutes, so it is not in task test.
cmds:
- task: package:spin
- pnpm test:cli
test:integration:
desc: test:cli, then the extension activated in a real VS Code, downloaded into .vscode-test/. Under xvfb-run when it is installed, which CI's runner has; otherwise on the display there is.
vars:
XVFB:
sh: command -v xvfb-run || true
cmds:
- task: test:cli
- task: build
- '{{if .XVFB}}xvfb-run -a {{end}}pnpm test:integration'
test:remote-ssh:
desc: >-
The packaged VSIX and Microsoft's Remote-SSH in a real VS Code stable, opening the workspaces
of a spin e2e stack as a reconnect does (test/remote-ssh/run.mjs): Spinbox readies each before
Remote-SSH resolves it, and a refused key closes the remote with Spinbox's message. Needs the
stack - SPIN_STACK=<spin checkout> after its `task e2e:up` and `task e2e:workspaces` - and
bwrap, so no workflow runs it. Under xvfb-run when it is installed.
vars:
XVFB:
sh: command -v xvfb-run || true
cmds:
- task: package
- '{{if .XVFB}}xvfb-run -a {{end}}node test/remote-ssh/run.mjs'
test:spin-remote:
desc: >-
The packaged VSIX and Spinbox Remote in a real VS Code stable, its proposed API enabled by
argv.json, opening the workspaces of a spin e2e stack as vscode-remote://spin+<workspace>
(test/spin-remote/run.mjs): running, stopped, a connection cut under the window and back
with the same terminal, a refused key said once, and the dashboard's link bringing the window
already open. Needs the stack - SPIN_STACK=<spin
checkout> after its `task e2e:up` and `task e2e:workspaces` - bwrap, and
SPINBOX_REMOTE_VSIX=<open-remote-ssh's spinbox-remote.vsix>, so no workflow runs it. CASES=moved
adds a move to another host, after spin's `task e2e:hosts:up`; it fails until spin#1225.
Under xvfb-run when it is installed.
vars:
XVFB:
sh: command -v xvfb-run || true
cmds:
- task: package
- '{{if .XVFB}}xvfb-run -a {{end}}node test/spin-remote/run.mjs'
package:
desc: Package the extension into spinbox.vsix with vsce, without the CLI - what CI's gate checks packages. package:all makes the ones that are published.
cmds:
- task: build
- pnpm package
package:spin:
desc: Pull the spin CLI versions.yaml pins for every platform its release has, into build/spin/<target>/spin with its VERSION, each checked with cosign against what spin's release workflow signed and attested of that release (hack/spinbin). A platform the release lacks is skipped, and said.
vars:
SPIN:
sh: versions version spin
COSIGN:
sh: echo "go run github.com/sigstore/cosign/v3/cmd/cosign@$(versions version cosign)"
cmds:
- rm -rf build/spin
- go run ./hack/spinbin -version {{.SPIN}} -out build/spin -cosign '{{.COSIGN}}'
package:all:
desc: One VSIX per platform the pinned spin release has a CLI for, spinbox-<target>.vsix, each carrying only its own checked CLI at bin/<target>/spin and the release it is in bin/<target>/VERSION, where the extension looks for them (src/cli/binary.ts). A CLI cosign refuses fails the task before any VSIX is made.
cmds:
- rm -f spinbox-*.vsix
- task: package:spin
- task: build
- |
set -eu
for dir in build/spin/*/; do
target=$(basename "$dir")
rm -rf bin && mkdir -p "bin/$target" && cp "$dir/spin" "$dir/VERSION" "bin/$target/"
pnpm exec vsce package --no-dependencies {{.PRE_RELEASE}} --target "$target" --out "spinbox-$target.vsix"
done
rm -rf bin
publish:
desc: Package every platform, attach each VSIX to the tag's GitHub release, and publish each to the Visual Studio Marketplace - what the Publish workflow runs on a tag v*, step by step. It authenticates by the workflow's OIDC token (docs/publishing.md), so it runs only in that workflow.
cmds:
- task: publish:check
- task: package:all
- task: publish:github
- task: publish:ghcr
- task: publish:marketplace
publish:github:
desc: >-
The tag's GitHub release, with every VSIX package:all made, spin-release.txt (the one spin
release they all carry), SHA256SUMS of those files, and SHA256SUMS.sigstore.json - cosign's
keyless signature of SHA256SUMS as this workflow at the tag, verified before anything is
uploaded. `spin vscode install` finds the release by spin-release.txt and holds the VSIX to
both. A pre-release when the tag holds -pre. A release the tag already has gets these files
again, over the ones it had: a run is run again whole. Needs GH_TOKEN and the job's OIDC token.
vars:
COSIGN:
sh: echo "go run github.com/sigstore/cosign/v3/cmd/cosign@$(versions version cosign)"
OUT: build/release
NOTES: build/release-notes.md
# Who signs: this workflow at the tag, as spin's internal/vsix demands it.
IDENTITY: '{{.GITHUB_SERVER_URL}}/{{.GITHUB_REPOSITORY}}/.github/workflows/publish.yml@{{.GITHUB_REF}}'
PRE: '{{if .PRE_RELEASE}}true{{else}}false{{end}}'
cmds:
- go run ./hack/ghrelease -spin build/spin -vsix . -out {{.OUT}} -notes {{.NOTES}} -tag {{.TAG}} -identity {{.IDENTITY}}
- '{{.COSIGN}} sign-blob --yes --bundle {{.OUT}}/SHA256SUMS.sigstore.json {{.OUT}}/SHA256SUMS'
# A signature nothing verifies is a step that can quietly stop working: one that does not
# check fails here, before anything is published.
- '{{.COSIGN}} verify-blob --bundle {{.OUT}}/SHA256SUMS.sigstore.json --certificate-identity {{.IDENTITY}} --certificate-oidc-issuer https://token.actions.githubusercontent.com {{.OUT}}/SHA256SUMS'
# Created with its files at once, so no release is ever listed without them; a run made
# again whole puts this run's files and notes over the last one's.
- gh release view {{.TAG}} >/dev/null 2>&1 || gh release create {{.TAG}} --verify-tag --title {{.TAG}} --notes-file {{.NOTES}} --prerelease={{.PRE}} {{.OUT}}/*
- gh release upload {{.TAG}} --clobber {{.OUT}}/*
- gh release edit {{.TAG}} --notes-file {{.NOTES}} --prerelease={{.PRE}}
# The GitHub release again, as a public package: this repository is private, so its releases
# are a 404 to `spin vscode install`, which reads this package instead. The package is
# transport; what the CLI trusts is the signature publish:github made and verified.
publish:ghcr:
desc: >-
Push what publish:github laid out and signed (every VSIX, spin-release.txt, SHA256SUMS and
SHA256SUMS.sigstore.json) to ghcr.io's vscode-spinbox-release with oras, as one artifact
tagged with the extension's tag and spin-<release> - spin-<release>-pre for a pre-release -
the tag `spin vscode install` of that spin release asks for (hack/ghcrpush). Run again, it
pushes the same files and moves both tags to them. Needs GH_TOKEN with packages write and
oras on the PATH.
vars:
PACKAGE: ghcr.io/spin-stack/vscode-spinbox-release
SOURCE: '{{.GITHUB_SERVER_URL}}/{{.GITHUB_REPOSITORY}}'
cmds:
- printf '%s' "${GH_TOKEN}" | oras login ghcr.io --username "${GITHUB_ACTOR}" --password-stdin
- go run ./hack/ghcrpush -release build/release -tag {{.TAG}} -package {{.PACKAGE}} -source {{.SOURCE}}
publish:check:
desc: Fail before anything is built when there is no OIDC token to publish with, or when the tag is not v and package.json's version - a VSIX carries the version it was packaged with, never the tag's.
env:
TAG: '{{.TAG}}'
silent: true
cmds:
# Without id-token: write, or outside Actions, the Marketplace would only say it refused.
- cmd: |
if [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
echo "publish: no OIDC token: it runs in the Publish workflow, whose job has id-token: write (docs/publishing.md)" >&2
exit 1
fi
# The Marketplace refuses a semver pre-release version, so v0.3.0-pre ships package.json's 0.3.0.
- cmd: |
if [ "${TAG%%-pre*}" != "v$(node -p 'require("./package.json").version')" ]; then
echo "publish: the tag '$TAG' is not v and package.json's version, with -pre after it for a pre-release" >&2
exit 1
fi
publish:marketplace:
desc: Publish every VSIX in the repository's root to the Visual Studio Marketplace, as the publisher's trusted publishing policy for this workflow.
cmds:
# --skip-duplicate: a run that failed after it is run again whole, and what the Marketplace already has is no error.
- pnpm exec vsce publish --oidc --skip-duplicate {{.PRE_RELEASE}} --packagePath *.vsix
spin:newer:
desc: Whether GIVEN, a spin release, is newer than versions.yaml's spin, as release=<it or nothing> - the Bump spin workflow's first step (versions newer).
cmds:
- |
set -euo pipefail
newer=$(versions newer spin "${GIVEN}")
echo "release=${newer}" >> "${GITHUB_OUTPUT:-/dev/stdout}"
spin:bump:
desc: Pin versions.yaml's spin at RELEASE (versions bump), which asks spin's repository for the tag's commit over git - with GH_TOKEN as git's credentials when it is set, since spin is private.
requires:
vars: [RELEASE]
cmds:
# In the workflow, the only credentials git has are the app's read token; a developer's own
# git already reaches spin.
- '[ -z "${GH_TOKEN:-}" ] || gh auth setup-git'
- versions bump spin "{{.RELEASE}}"
contract:spin:
desc: Hold src/cli/contract.ts's error codes and actions to errorActions in spin's cmd/cli/cmd/errcode.go at the release versions.yaml pins (hack/errcodes), read from spin's repository over git - with GH_TOKEN as git's credentials when it is set, since spin is private. The Bump spin workflow runs it after the bump, so a release that adds or drops a code is a red bump, not an envelope read as internal.
vars:
SPIN:
sh: versions version spin
cmds:
- '[ -z "${GH_TOKEN:-}" ] || gh auth setup-git'
- |
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
git clone --quiet --depth 1 --branch {{.SPIN}} --filter=blob:none --no-checkout https://github.com/spin-stack/spin "$dir/spin"
git -C "$dir/spin" show HEAD:cmd/cli/cmd/errcode.go > "$dir/errcode.go"
go run ./hack/errcodes -spin "$dir/errcode.go" -contract src/cli/contract.ts
spin:propose:
desc: Commit versions.yaml's spin at RELEASE, with package.json at the next patch when its version is tagged already, push it to spin/next and open or rewrite the pull request there, as the app GH_TOKEN and APP_SLUG name (hack/spinpropose) - the Bump spin workflow's last step, after task ci and task package:all.
requires:
vars: [RELEASE, APP_SLUG]
cmds:
- go run ./hack/spinpropose -release "{{.RELEASE}}" -app "{{.APP_SLUG}}"
release:tag:
desc: Tag the commit GITHUB_SHA names with the tag package.json's version is released as - v and the version, with -pre after it for an odd minor - as the app whose token GH_TOKEN is, when no tag has that version yet (hack/releasetag); the Tag workflow's one step, after a push to main. A tag the app makes starts Publish; one made with a workflow's own token would not.
cmds:
- go run ./hack/releasetag
gate:
desc: What holds the repository's shape - every install through the firewall, every workflow step a task, and no commit since origin/main by a test's identity.
cmds:
- sfw check
# The Task running this one, which taskgate asks for the task names.
- taskgate -task {{.TASK_EXE}}
# A test that writes git's author into the checkout makes every commit after it the
# test's; this refuses those commits before they reach main.
- testidentity -base origin/main
ci:
desc: What CI's Gate job runs - the gate, the install, lint, typecheck, the unit tests, and the package. test:integration is its own job.
cmds:
- task: gate
- task: install
- task: lint
- task: typecheck
- task: test
- task: package