Repository navigation
Version-tag release deploys; faster integration suite - #39
Merged
Merged
Conversation
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
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
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Releases deploy by version tag. The docs deploy now goes by the release's version tag (
v<version>, the same git tagbuild_releasecreates), so the host recordsv0.3.2instead of0f0a7d8. Aforce_deployrun that cut no release falls back to the full commit SHA.v${{ needs.release.outputs.current-version }}, the same expressionbuild_releasepasses as the release'stag_name, soDEPLOY_TAG=v0.3.2names 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_deployon its own works again. With no release,build_releaseis skipped, andbuild_and_deploy's plainif:impliedsuccess(), 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 skippedbuild_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_runningnow runs with annotated tags as well as lightweight ones: v1 → v2 succeeds, v3 fails, and everything is back onv2, 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:
pytest-xdist.poetry run pytest -m integration --no-cov -n autoruns 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.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").pytest-split, balanced by the recordedtests/integration/.test_durations). Integration wall time went from about 5m40s to about 1m40s per shard, setup included.after:3withstart_period8srelease, authorgithub-actions[bot]) and thechore(release):commit on master only bump the version and CHANGELOG. There's no branch protection, so a skipped job blocks nothing.Decided against
7099e88). It's enough to know the tree is dirty.docker logout, becausebash -eends the loop first, anddocker loginleaves the credential in the deploy user's~/.docker/config.jsonafter SSH disconnects. But that credential is the job'sGITHUB_TOKEN, which GitHub invalidates when the job finishes. The only side effect: until the next deploy logs in again, a manualdocker pullfrom ghcr.io on the host sends the dead token and is denied.Testing
script/lintclean;poetry run pytestgives 338 passed at 100% coverage.actionlintonrelease.yml: no findings in the changed job. Two existing shellcheck warnings remain inbuild_release(the unquoteddocker cp $(docker create …)), left as they were.force_deploydispatch after merge, which should now run Build & Deploy by full SHA.🤖 Generated with Claude Code
https://claude.ai/code/session_01Be6ghMqRxW6R4WS3vJY1Yv