|
|
@@ -76,7 +76,10 @@ re-brand of opencode-devbox's `pi-only` variant.
|
|
|
|
4. Push tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
|
|
4. Push tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
|
|
5. Watch CI: smoke job builds amd64 only and asserts size + extensions +
|
|
|
|
5. Watch CI: smoke job builds amd64 only and asserts size + extensions +
|
|
|
|
pi version + new-base-tooling presence. Variant build is multi-arch
|
|
|
|
pi version + new-base-tooling presence. Variant build is multi-arch
|
|
|
|
(amd64 + arm64) only after smoke passes.
|
|
|
|
(amd64 + arm64) only after smoke passes. **A tag push produces two runs, not
|
|
|
|
|
|
|
|
one** — `lint.yml` fires on every push (including tag refs) and
|
|
|
|
|
|
|
|
`docker-publish.yml` fires on `v*` tags. Watch the **publish** run; see
|
|
|
|
|
|
|
|
*Gitea API access* below for how to find it without picking lint by mistake.
|
|
|
|
6. Verify the Hub tags appear (latest + vX.Y.Z, the `-studio` pair, plus
|
|
|
|
6. Verify the Hub tags appear (latest + vX.Y.Z, the `-studio` pair, plus
|
|
|
|
base-latest if the base was rebuilt this run).
|
|
|
|
base-latest if the base was rebuilt this run).
|
|
|
|
7. **Revoke any short-lived Gitea PAT** used during the release at
|
|
|
|
7. **Revoke any short-lived Gitea PAT** used during the release at
|
|
|
@@ -92,13 +95,61 @@ host `.env` via `docker-compose.yml` (`${GITEA_ACCESS_TOKEN:-}` /
|
|
|
|
**not** baked into the image. When configured, they are also available for
|
|
|
|
**not** baked into the image. When configured, they are also available for
|
|
|
|
**any** direct Gitea API interaction from inside the container — inspecting
|
|
|
|
**any** direct Gitea API interaction from inside the container — inspecting
|
|
|
|
CI runs, checking published tags, listing commits — e.g.
|
|
|
|
CI runs, checking published tags, listing commits — e.g.
|
|
|
|
`curl -H "Authorization: token $GITEA_ACCESS_TOKEN" "$GITEA_HOST/api/v1/repos/joakimp/pi-devbox/actions/runs?limit=5"`.
|
|
|
|
`curl -H "Authorization: token $GITEA_ACCESS_TOKEN" "$GITEA_HOST/api/v1/repos/joakimp/pi-devbox/actions/runs?limit=20"`.
|
|
|
|
Prefer this over a short-lived PAT file when the env token is present (the
|
|
|
|
Prefer this over a short-lived PAT file when the env token is present (the
|
|
|
|
`ci-release-watcher` skill auto-detects it). Public-repo GET listings work
|
|
|
|
`ci-release-watcher` skill auto-detects it). Public-repo GET listings work
|
|
|
|
unauthenticated too, so the token matters mainly for private repos or
|
|
|
|
unauthenticated too, so the token matters mainly for private repos or
|
|
|
|
rate-limit headroom; its lifecycle is host-managed, so there is nothing to
|
|
|
|
rate-limit headroom; its lifecycle is host-managed, so there is nothing to
|
|
|
|
revoke after use. Never echo the token value (including into logs).
|
|
|
|
revoke after use. Never echo the token value (including into logs).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Gotcha — a tag push fires EVERY workflow whose triggers match the tag ref.**
|
|
|
|
|
|
|
|
`lint.yml` uses a bare `push:` trigger, so a release tag yields *both* a lint run
|
|
|
|
|
|
|
|
and the publish run. The listing is newest-first and lint sorts **above** the
|
|
|
|
|
|
|
|
publish run, so "take the first run whose `path` contains `refs/tags/<tag>`"
|
|
|
|
|
|
|
|
picks the wrong one **reliably, not occasionally**. Real listing for v1.6.4:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
id=531 #104 lint.yml@refs/tags/v1.6.4 <- wrong; sorts first
|
|
|
|
|
|
|
|
id=530 #103 docker-publish.yml@refs/tags/v1.6.4 <- the release build
|
|
|
|
|
|
|
|
id=529 #102 lint.yml@refs/heads/main <- same commit, linted on push
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Lint goes green in minutes while the image is still building, so watching it
|
|
|
|
|
|
|
|
makes a release look finished when nothing has been published yet.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Gotcha — the jobs endpoint takes the internal `id`, NOT the `run_number` the
|
|
|
|
|
|
|
|
UI shows as `#104`.** The two diverge widely, and `GET
|
|
|
|
|
|
|
|
.../actions/runs/<run_number>/jobs` does **not** error — it silently returns a
|
|
|
|
|
|
|
|
*different* run's jobs. Always read `id` from the run listing:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
|
|
# Which runs did this tag/commit trigger? Filter on head_sha; never trust
|
|
|
|
|
|
|
|
# ordering or run numbering. limit=20, not 5 — with two runs per push the
|
|
|
|
|
|
|
|
# publish run falls off a 5-item window fast.
|
|
|
|
|
|
|
|
curl -sS -H "Authorization: token $GITEA_ACCESS_TOKEN" \
|
|
|
|
|
|
|
|
"$GITEA_HOST/api/v1/repos/joakimp/pi-devbox/actions/runs?limit=20" \
|
|
|
|
|
|
|
|
| jq --arg sha "$(git rev-list -n1 vX.Y.Z)" \
|
|
|
|
|
|
|
|
'.workflow_runs[] | select(.head_sha==$sha) | {id, run_number, path, status, conclusion}'
|
|
|
|
|
|
|
|
# pick the id whose .path starts with docker-publish.yml, then:
|
|
|
|
|
|
|
|
curl -sS -H "Authorization: token $GITEA_ACCESS_TOKEN" \
|
|
|
|
|
|
|
|
"$GITEA_HOST/api/v1/repos/joakimp/pi-devbox/actions/runs/<id>/jobs" \
|
|
|
|
|
|
|
|
| jq '.jobs[] | {name, status, conclusion}'
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Watcher config for this repo** (`ci-release-watcher` skill, hub-only shape —
|
|
|
|
|
|
|
|
pi-devbox has no downstream host to deploy to):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `EXPECT_WORKFLOW=docker-publish.yml` — the skill's `preflight_run()` aborts at
|
|
|
|
|
|
|
|
startup if the run id belongs to lint instead.
|
|
|
|
|
|
|
|
- `EXPECTED_FRESH_TAGS='vX.Y.Z latest vX.Y.Z-studio latest-studio'`
|
|
|
|
|
|
|
|
- `EXPECTED_EXISTS_TAGS='base-latest'` — existence only: it is content-addressed
|
|
|
|
|
|
|
|
and legitimately keeps its old timestamp when the base is a cache hit.
|
|
|
|
|
|
|
|
- `CRITICAL_JOBS='build-variant build-variant-studio'` — job names are matched
|
|
|
|
|
|
|
|
**exactly** (`critical.issubset(succeeded)`), so the studio variant must be
|
|
|
|
|
|
|
|
listed explicitly; the skill's default omits it. Leave `promote-base-latest`
|
|
|
|
|
|
|
|
out: it legitimately skips on a base cache hit, which would misclassify a good
|
|
|
|
|
|
|
|
run. `update-description` is the cosmetic post-publish job.
|
|
|
|
|
|
|
|
|
|
|
|
## Cache-hit footgun (must-know)
|
|
|
|
## Cache-hit footgun (must-know)
|
|
|
|
|
|
|
|
|
|
|
|
`PI_VERSION` defaults to `latest` in `Dockerfile.variant` but **CI must
|
|
|
|
`PI_VERSION` defaults to `latest` in `Dockerfile.variant` but **CI must
|
|
|
|