Skip to content

Commit 01f958c

Browse files
committed
release: a partial build could publish a release missing platforms
Each matrix leg independently created-or-updated a PUBLISHED release through softprops/action-gh-release. If one leg failed after a sibling had already uploaded, the result was a published release, marked latest, carrying fewer assets than it should. A user on the missing platform gets a 404 from a release that looks complete, and nothing in CI could tell: every gate this workflow had was about whether the code compiled, not about what actually reached users. Same class of defect as the phantom 0-asset releases the plugin repos just fixed, so it gets the same shape. The Release is created once as a DRAFT, the matrix uploads into it, and a final job promotes it to published-and-latest only after asserting the full asset set is attached. A draft is invisible to users and to releases/latest, so an incomplete build now fails as "no release appeared" rather than "a wrong release appeared". The count is asserted against an expected total rather than against "more than zero", so a silent drop from three platforms to one is caught as well. fail-fast is off so every leg reports; there is nothing to protect by cancelling siblings now that the draft gate is what keeps a partial result away from users. Not exercised by a tag push here. The next release cut on this repo is its own proof; if the gate is wrong it fails closed, leaving a draft, which is the safe direction.
1 parent 2b89630 commit 01f958c

1 file changed

Lines changed: 71 additions & 5 deletions

File tree

.github/workflows/release.yml

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
name: Release
22

3-
# Builds release binaries for macOS + Linux on a v* tag and uploads them to the
4-
# GitHub release. NOTE: untested without a real tag push — treat as a starting point.
3+
# Builds release binaries for macOS + Linux on a v* tag and uploads them to the GitHub release.
4+
#
5+
# The Release is created as a DRAFT and is promoted to published-and-latest only after the build
6+
# matrix has finished AND the expected number of assets is provably attached. This is the same
7+
# shape every first-party plugin repo uses, and it is here for the same reason: this workflow used
8+
# to have each matrix leg independently create-or-update a PUBLISHED release via
9+
# softprops/action-gh-release, so a leg that failed after a sibling had already uploaded left a
10+
# published release carrying fewer assets than it should, with `latest` pointing at it. A user
11+
# on the missing platform gets a 404 from a release that looks complete. Nothing in the old
12+
# workflow could detect that: every gate it had was about whether the build compiled.
13+
#
14+
# A draft is invisible to users and to `releases/latest`, so the failure mode is now "the release
15+
# does not appear" rather than "the release appears and is wrong".
516

617
on:
718
push:
@@ -10,9 +21,38 @@ on:
1021
permissions:
1122
contents: write
1223

24+
env:
25+
# Every target in the build matrix must land an asset before the release is promoted. Keep this
26+
# in step with the matrix below; the gate compares against it rather than against "more than
27+
# zero", so a silent drop from three platforms to one fails too.
28+
EXPECTED_ASSETS: "3"
29+
1330
jobs:
31+
# Created first so the parallel matrix legs have something to attach to (uploading from a matrix
32+
# with no pre-existing release races and fails with "release not found"). Draft from the start:
33+
# the promotion below is the only thing that ever publishes it.
34+
create-release:
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v4
38+
- name: Create draft Release
39+
env:
40+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41+
run: |
42+
gh release create "${GITHUB_REF_NAME}" \
43+
--repo "${GITHUB_REPOSITORY}" \
44+
--title "${GITHUB_REF_NAME}" \
45+
--draft \
46+
--verify-tag --generate-notes \
47+
|| gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}"
48+
1449
build:
50+
needs: create-release
1551
strategy:
52+
# Let every leg run and report. With fail-fast the first failure cancels its siblings, which
53+
# hides how many platforms are actually broken; the draft gate below is what keeps a partial
54+
# result from reaching users, so there is nothing to protect by stopping early.
55+
fail-fast: false
1656
matrix:
1757
include:
1858
- target: x86_64-unknown-linux-gnu
@@ -34,6 +74,32 @@ jobs:
3474
tar -C target/${{ matrix.target }}/release -czf \
3575
busbar-admin-${{ github.ref_name }}-${{ matrix.target }}.tar.gz busbar-admin
3676
- name: upload
37-
uses: softprops/action-gh-release@v2
38-
with:
39-
files: busbar-admin-${{ github.ref_name }}-${{ matrix.target }}.tar.gz
77+
env:
78+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79+
run: |
80+
gh release upload "${GITHUB_REF_NAME}" \
81+
"busbar-admin-${GITHUB_REF_NAME}-${{ matrix.target }}.tar.gz" \
82+
--repo "${GITHUB_REPOSITORY}" --clobber
83+
84+
# The only step that ever publishes. `needs: build` means a total build failure never reaches it
85+
# at all and the release stays an invisible draft; a partial failure reaches it and is refused
86+
# here on the count.
87+
verify-assets:
88+
needs: build
89+
runs-on: ubuntu-latest
90+
steps:
91+
- name: Require the full asset set, then promote
92+
env:
93+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
94+
run: |
95+
set -euo pipefail
96+
count="$(gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" \
97+
--json assets --jq '.assets | length')"
98+
echo "attached assets: ${count} (expected ${EXPECTED_ASSETS})"
99+
gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" \
100+
--json assets --jq '.assets[].name'
101+
if [ "${count}" -ne "${EXPECTED_ASSETS}" ]; then
102+
echo "::error::PARTIAL RELEASE PREVENTED: ${GITHUB_REF_NAME} has ${count} assets, expected ${EXPECTED_ASSETS}. Leaving it as a DRAFT so no user can download an incomplete release." >&2
103+
exit 1
104+
fi
105+
gh release edit "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" --draft=false --latest

0 commit comments

Comments
 (0)