feat(ci): PR-run sources for the tested-tree fast path; dev reuses and retags the PR image #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/pr-image-dev-fast-path"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Extends the tested-tree fast path (#13) so the
devpush after a PR merge also skips tests and the Docker build, reusing the image the PR run built. John approved this on 2026-09-29. All new inputs are optional. With the old inputs, behaviour is unchanged except for one relaxation, listed under Behaviour changes.tested-tree.ymlanddetect-changes.ymlget the same new inputs. The step stays byte-identical in both, and the test still enforces that.fast_path_rules: one line per deploy branch,<deploy branch>: <source> .... A source is eitherpush:<branch>(that branch's push run) orpull_request(a PR run of a same-tree commit). The consumers use:fast_path_branch/fast_path_deploy_branchremain as the one-rule shorthand<deploy>: push:<branch>.push:<branch>: same-tree commits among that branch's last 30 first-parent commits (as before).pull_request: same-tree commits among HEAD's last 30 ancestors. A merge's PR head is its second parent; a fast-forward's is HEAD itself. The step fetches by sha to deepen the shallow checkout, asactions/checkoutdoes.event == pull_request. The task API reports theirhead_branchas#<n>, so branch is not matched.fast_path_jobsjob must have its latest attempt atsuccessin that source's run of that commit. A pair with a skipped, failed, running or missing job proves nothing, and the step moves on to the next pair.mainafter a fast-patheddev. dev's own run has Frontend and Docker Build skipped, so that pair is passed over and a same-tree PR run proves the tree instead.fast_path_imagesplus the secretfast_path_registry_token(andfast_path_registry_prefix/_username):verified_shabecomes the first proven commit whose image(s) exist. This is adocker manifest inspectcheck, like the one indokku-image-deploy.yml.DOCKER_CONFIG, are never put on a command line or printed, and are deleted on exit.pull_requestsources requirefast_path_images, because a fork PR can pass its tests but never publishes.fast_path_retag: dev: on that branch, a verified commit other than HEAD is copied registry-side to:<HEAD sha>withdocker buildx imagetools create(no pull, no build) and then read back. If the copy fails, the step reportstested_tree=falseand the caller builds as today. Result: every dev sha still has its own tag, and nothing downstream changes.README.mddocuments the rules, the per-run flow and the expected timings.Behaviour changes (legacy inputs)
false, even if an older same-tree commit had passed. Now each same-tree commit stands alone, and any one that passed every job proves the tree. That is the same tree, so the failure was flakiness, the same as a retried job, which was already accepted. It is also what letsmainpass over a fast-pathed dev run whose jobs are skipped.workflow_dispatchreads asworkflow_call. Forward-deploy-web run #110 showed this: the dispatch passed the event check and was stopped only by the ref check. So the step cannot refuse a manual dispatch onmainordev.mainwould skip Frontend but not deploy, because Deploy Gate checkspushin a normal job.tested_treeonly when their owngithub.event_name == 'push', and the README tells callers to do the same.Validation
uv run --with pyyaml==6.0.3 python -m unittest discover -s tests: 44 pass. That is 11 new, and the 33 existing ones pass unmodified. They also pass under-o pipefail.--no-ffmerge into dev, a promotion to main, shallow checkouts), withcurl,dockerandsleepmocked.imagetools createarguments and the read-back. It also checks that the auth in the private config decodes to publisher:token, that the token is never in the output, and that the config file is gone after the step.false: a failed retag, a missing PR image (the fork case), and a merge onto a moved dev (tree differs).false: a skipped job, a failed job, and a push run standing in for a PR run.refs/pullref, and dispatch orpull_requestevents.actionlint1.7.12 with shellcheck: no new findings. The finding set is identical toorigin/main's (14, all pre-existing: thecilabel and SC2086 in the change filter). The two consumerci.ymlfiles are likewise unchanged in findings.ruff checkandruff formatpass ontests/.Expected timings
devpush (clean merge)mainpromotiongit:from-imagephase (~3.3 min) is the floor.Spec Drift Callouts
tested-tree.yml, not in a consumer job. The brief suggested the dev run retag withimagetools create. Doing it inside the Fast Path step:if:(the runner mis-evaluates those onworkflow_calljobs);mainalso accepts PR runs (main: push:dev pull_request). This is not optional. After a fast-pathed dev, dev's own run has the jobs skipped, so without a PR source every promotion would take the slow path.:<dev sha>from the retag.main, so there the feature PR run proves it and main deploys:<PR head sha>. That is the same bytes the dev retag points at.fast_path_retagis a branch list, not a boolean, so one Fast Path job can serve bothdev(retag) andmain(no retag).Rollout order
feat/ci-pr-image→dev), in either order. Both passfast_path_rules,fast_path_imagesandfast_path_retag, which only exist once this is onmain. After this merges, push an empty re-trigger commit to each PR branch (as last time), so their PR runs use the newtested-tree.yml.retagged ... as ...andfast path: YES. Then promote to main and check the Deploy Gate saysfast path:.Not verifiable without a live run
pull_requestruns: Forgejo documents that same-repo PR runs get repo secrets and fork PR runs do not. I could not observe it. IfREGISTRY_PUBLISH_TOKENis absent, the consumer publish step warns and skips, and dev takes the slow path.github.event.pull_request.head.repo.full_nameand.head.sha: not confirmed as populated in Forgejo's payload. If empty, the step does not publish, which is again the slow path.GITHUB_SHA: assumed to be the PR head. The task API'shead_shafor PR runs is the head (e.g. forward-deploy-web #28,522110a, the merge's second parent). The publish step refuses to tag if the checkout's HEAD is notpull_request.head.sha.docker buildx imagetools createagainstregistry-directwith publisher credentials. For a single-platform source it writes an index that wraps the same manifest, so the tag's digest differs but the image bytes are the same. Dokku pulls it like any tag. The deploy'salternate-tagscheck is by tag, so it is unaffected.actions/checkoutrelies on the same thing, so I expect it to work.Caveat
PR runs now publish one tag per PR head push. That is more tags in
haskytech/<repo>/<image>, each sharing layers with the rest. A registry cleanup rule (keep N / older than X days, excluding deployed shas) would be worth adding. It is not part of this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01CS99mH2YQv9t6iPuAbr21S