Skip to content

Version-tag release deploys; faster integration suite - #39

Merged
wadewilliams merged 5 commits into
masterfrom
release-version-tag-deploys
Oct 12, 2026
Merged

wadewilliams merged 5 commits into
masterfrom
release-version-tag-deploys

Conversation

@wadewilliams

@wadewilliams wadewilliams commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

What

Releases deploy by version tag. The docs deploy now goes by the release's version tag (v<version>, the same git tag build_release creates), so the host records v0.3.2 instead of 0f0a7d8. A force_deploy run that cut no release falls back to the full commit SHA.

  • The image is pushed under every name a deploy or a revert may use: the version tag (releases only), the short SHA (as before) and the full SHA. A revert's fallback is the previous commit's full SHA, so it now finds an image instead of rebuilding. Closes Push the docs image by full commit SHA too #36.
  • Spelling: the image tag is v${{ needs.release.outputs.current-version }}, the same expression build_release passes as the release's tag_name, so DEPLOY_TAG=v0.3.2 names both the git tag (annotated; flow-deploy fetches tags and checks it out) and an image tag pushed in the same job, before the deploy step.

force_deploy on its own works again. With no release, build_release is skipped, and build_and_deploy's plain if: implied success(), which a skipped dependency fails, so the deploy job was skipped too. The 2026-03-03 dispatch run shows this: Build release skipped, Build & Deploy skipped. The condition now accepts a skipped build_release, and still stops if anything before it failed or was cancelled.

Reverts from a version-tagged deploy restore by the previous version tag. The running tag is used when it's a git ref (annotated release tags included), still names the local image, and isn't the tag being deployed. The integration test test_failed_deploy_restores_the_version_that_was_running now runs with annotated tags as well as lightweight ones: v1 → v2 succeeds, v3 fails, and everything is back on v2, by tag. The first version-tagged deploy restores by the short SHA the current containers run (0f0a7d8), which is also a git ref with a matching local image.

Faster integration suite (closes #38). All four options from the issue:

  • Locally: pytest-xdist. poetry run pytest -m integration --no-cov -n auto runs all 33 tests in about 48s on a 48-core machine (6+ minutes serially). The local registry is shared by all workers: the first starts it under a file lock, and the last removes it.
    • Each test service now runs with network_mode: none. The stubs never talk to each other, and each stack's default network was using up one of Docker's address pools, which ran out after about 11 parallel stacks ("all predefined address pools have been fully subnetted").
    • Each stack commits under its own author, so commit SHAs pushed as image tags to the shared registry can't collide.
  • In CI: four integration shards (pytest-split, balanced by the recorded tests/integration/.test_durations). Integration wall time went from about 5m40s to about 1m40s per shard, setup included.
  • Shorter fixed waits, with the same intent and margins:
    • the signal tests' SIGTERM delay drops from 10s to 4s, and the sleep after from 12s to 6s; the signal still lands mid-drain
    • the late-healthcheck test now uses after:3 with start_period 8s
    • the CI-cancel probe runs 4s instead of 6s
  • Releases skip CI: release-builder's PR (branch release, author github-actions[bot]) and the chore(release): commit on master only bump the version and CHANGELOG. There's no branch protection, so a skipped job blocks nothing.

Decided against

  • Listing the dirty files when a deploy stops on a dirty tree was added, then reverted (7099e88). It's enough to know the tree is dirty.
  • deploy-action always logging out of GHCR: not needed. A failed deploy does skip deploy-action's docker logout, because bash -e ends the loop first, and docker login leaves the credential in the deploy user's ~/.docker/config.json after SSH disconnects. But that credential is the job's GITHUB_TOKEN, which GitHub invalidates when the job finishes. The only side effect: until the next deploy logs in again, a manual docker pull from ghcr.io on the host sends the dead token and is denied.

Testing

  • script/lint clean; poetry run pytest gives 338 passed at 100% coverage.
  • Integration: all 33 pass in parallel locally (three runs, about 48s each) and serially (one run); CI's four shards pass.
  • actionlint on release.yml: no findings in the changed job. Two existing shellcheck warnings remain in build_release (the unquoted docker cp $(docker create …)), left as they were.
  • The workflow change itself is only exercised by the next release, or by a force_deploy dispatch after merge, which should now run Build & Deploy by full SHA.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv

wadewilliams and others added 4 commits October 11, 2026 16:18
The docs deploy now goes by the release's version tag (v<version>, the
git tag build_release creates) rather than the short SHA, so the host
records v0.3.2, not 0f0a7d8. A force_deploy run that cut no release
falls back to the full commit SHA.

The image is pushed under every name a deploy or a revert may use: the
version tag (releases only), plus the short and full SHA. A revert falls
back to the full SHA, so it now always finds an image instead of
rebuilding (#36).

Fixes force_deploy on its own. With no release, build_release is
skipped, and build_and_deploy's plain `if:` implied success(), so the
deploy job was skipped too (as in the 2026-03-03 dispatch run). The
condition now accepts a skipped build_release, and still stops if
anything before it failed or was cancelled.

The integration test for restoring the running version now runs with
annotated tags as well as lightweight ones, since the release workflow's
tags are annotated. Both restore by the version tag.

Closes #36.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv
A remote deploy that stopped with "working tree is dirty" said nothing
about why. The 0.3.1 docs deploy hit this on a leftover legacy
.deploy-tag, and nobody could tell from the Actions log. The error now
lists the `git status --porcelain` lines, one per line (at most 10, then
"... and N more"), so the cause is in the CI log. The check itself is
unchanged: it runs first, and nothing is fetched, checked out or
touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv
Applies all four options from #38. The suite took about 5.5 minutes in
CI and 6+ locally, running serially.

- Locally: pytest-xdist. `pytest -m integration --no-cov -n auto` runs
  the 33 tests in about 48s on a 48-core machine. The session registry
  is shared by every worker: the first starts it under a file lock, and
  the last to finish removes it.
- Each test service runs with `network_mode: none`. The stubs never talk
  to each other, and every stack's default network was using up one of
  Docker's address pools, which ran out after about 11 parallel stacks
  here ("all predefined address pools have been fully subnetted").
- Each stack commits under its own author, so commit SHAs (pushed as
  image tags to the shared registry) are unique per stack.
- In CI: four integration shards (pytest-split, balanced by the recorded
  tests/integration/.test_durations).
- Shorter fixed waits, with the same intent and margins:
  - the signal tests' SIGTERM delay drops from 10s to 4s, and the sleep
    after from 12s to 6s; the signal still lands mid-drain
  - the late-healthcheck test now uses after:3 with start_period 8s
  - the CI-cancel probe runs 4s instead of 6s
- Release PRs (the `release` branch) and `chore(release):` commits skip
  CI. They only bump the version and CHANGELOG.

Closes #38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv
A PR from any branch named release skipped lint, unit and integration.
The skip now also requires the PR's author to be github-actions[bot],
which opens release-builder's release PRs. A human branch named release
is tested like any other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv
@wadewilliams wadewilliams changed the title Deploy releases by version tag; let force_deploy run alone Version-tag release deploys; faster integration suite Oct 11, 2026
Reverts 09d8024. It's enough to know the tree is dirty; the deploy stops
either way, and the host shows what's dirty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv
@wadewilliams
wadewilliams merged commit e51df6e into master Oct 12, 2026
6 checks passed
@wadewilliams
wadewilliams deleted the release-version-tag-deploys branch October 12, 2026 00:39
@github-actions github-actions Bot mentioned this pull request Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed up the integration suite (5.5 min in CI) Push the docs image by full commit SHA too

1 participant