Block a partial publish when a build fails - #42
Conversation
WORKFLOW.md D4.5 requires that a build failure block every publish target. This repo's WORKFLOW.md was missing the guarantee entirely, and the pipeline did not satisfy it. build-docker needed only [get-version, validate, validate-release] and guarded solely on cancellation. The image builds from source and consumes no executable artifact, so the two builds were independent and nothing forced an ordering. On a real publish where build-executable failed, github-release skipped, so no tag and no release were cut, while build-docker was untouched and pushed the multi-arch image anyway, moving `latest`. An image shipped with no release behind it. build-docker becomes the terminal publish target: it needs build-executable, and its `if` gains `!failure()`. The distinction that matters is failed versus skipped. A failed upstream build must stop the push, and a skipped one must not, because `validate` is skipped on every smoke run and that run still has to build the image. The explicit get-version and validate-release result checks stay, so a job that somehow did not run cannot feed empty version inputs into a build. The cost is on smoke, where build-docker now waits for build-executable instead of running beside it, adding roughly the executable build's duration to PR feedback. Correctness over a subset of the wall clock. WORKFLOW.md gains D4.5 in the same commit rather than in the doc-refresh PR ahead of it, so the contract and the code that satisfies it land together instead of the file claiming a guarantee the pipeline breaks. The failure path is not reachable from a pull request, since smoke never publishes, so it is established from the needs graph rather than by observation. actionlint passes. Audit run 2026-08-03T16:52:36Z, hub 1ed0cc8, against develop@39c896b. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #42 +/- ##
========================================
Coverage 43.37% 43.37%
========================================
Files 28 28
Lines 3896 3896
Branches 306 306
========================================
Hits 1690 1690
Misses 2154 2154
Partials 52 52 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟢 Ready to approve
The workflow dependency/guard changes correctly prevent Docker publishing on upstream build failure without breaking the smoke (skipped-validate) path described in the PR.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR closes a real release/publish correctness gap by ensuring Docker publishing cannot proceed when an upstream build job fails, aligning the workflow behavior with WORKFLOW.md’s D4.5 contract.
Changes:
- Document the “no partial publish” guarantee (D4.5) in
WORKFLOW.md. - Update the release workflow so
build-dockerdepends onbuild-executableand is gated by!failure()(while still allowing skipped smoke-only validation).
File summaries
| File | Description |
|---|---|
| WORKFLOW.md | Adds D4.5 to explicitly require that any failed enabled build blocks all publish targets (including Docker). |
| .github/workflows/build-release-task.yml | Makes build-docker depend on build-executable and adds !failure() to prevent Docker pushes when any upstream dependency failed, while preserving smoke behavior when validate is skipped. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
The one behavioral defect the audit found, and it is live today.
WORKFLOW.mdD4.5 requires that a build failure block every publish target. This repo'sWORKFLOW.mdwas missing the guarantee entirely, and the pipeline did not satisfy it.The defect
The image builds from source and consumes no executable artifact, so the two builds were independent and nothing forced an ordering. On a real publish where
build-executablefails:build-executablegithub-releaseneedsit, so it skipsbuild-dockerlatestmovesAn image shipped with no release behind it. That is exactly the partial publish D4.5 exists to prevent.
The fix
build-dockerbecomes the terminal publish target: itneedsbuild-executable, and itsifgains!failure().The distinction that matters is failed versus skipped. A failed upstream build must stop the push; a skipped one must not, because
validateis skipped on every smoke run and that run still has to build the image.!failure()draws exactly that line. The explicitget-versionandvalidate-releaseresult checks stay, so a job that somehow did not run cannot feed empty version inputs into a build.Reasoning, since the failure path is not reachable from a PR
Smoke never publishes, so no pull request can exercise a failing real publish. The behavior is established from the
needsgraph rather than by observation:failure()build-dockergithub-releasevalidateskipped)push: false!inputs.smoke)build-executablefailsvalidatefailsvalidate-releasefailsThe smoke row is the one that would break if
!failure()were the wrong guard, and this PR's own CI run is that row.Cost
On smoke,
build-dockernow waits forbuild-executableinstead of running beside it, adding roughly the executable build's duration to PR feedback. Correctness over a subset of the wall clock.D4.5 lands here, not in #40
The doc-refresh PR deliberately left it out. Stating the guarantee before implementing it would have replaced a silent gap with a false claim, so the contract and the code that satisfies it land together.
Verification
actionlintclean.dotnet husky runclean,editorconfig-checkerclean, hub prose gate clean on the changed lines.Last of four in the round. Follows #39, #40 and #41. If #40 merges first this may need a trivial rebase - both touch
WORKFLOW.mdD4, two lines apart.🤖 Generated with Claude Code