Skip to content

Commit c5a1ea2

Browse files
committed
ci: treat the native release assets as one set, not a bag of files
manifest.json pins a sha256 for every zip and the installers verify against it, so uploading only the missing members of a partial set would pair zips from one build with checksums from another — a rebuild is not guaranteed to be byte-identical even at the same commit. Upload all of them or none, and stop the job on a partial set so a person decides between keeping what is published and cutting the next patch. Also label the example fence in CONTRIBUTING.md.
1 parent 7b1914e commit c5a1ea2

2 files changed

Lines changed: 24 additions & 16 deletions

File tree

.github/workflows/release.yml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,15 @@ jobs:
650650
# source change would put different bytes behind a version number users
651651
# already have. A version has to identify one exact build forever.
652652
#
653-
# Uploading only what is missing keeps both properties: a run that failed
654-
# part way through still completes on a re-run, and a rebuild of an
655-
# already-published version is left on the floor instead of shipped under
656-
# its old number. To correct a bad build, cut the next patch.
653+
# These assets are one set, not a bag of files: manifest.json pins a
654+
# sha256 for every zip, and install.sh / install.ps1 verify against it.
655+
# So the rule is all or nothing. A release that already carries the whole
656+
# set is finished and is left alone; an empty one gets everything. A
657+
# partial set stops the job, because filling in the gaps would pair zips
658+
# from one build with checksums from another, and a rebuild is not
659+
# guaranteed to be byte-identical even at the same commit. Sorting that
660+
# out is a decision for a person: either what is published is the
661+
# release, or it needs a new patch version.
657662
- name: Upload assets to GitHub Release
658663
shell: bash
659664
env:
@@ -662,21 +667,24 @@ jobs:
662667
run: |
663668
set -euo pipefail
664669
existing="$(gh release view "$RELEASE_TAG" --json assets --jq '.assets[].name')"
665-
missing=()
666-
skipped=()
670+
present=()
671+
absent=()
667672
for path in dist-native-release/*; do
668673
name="$(basename "$path")"
669674
if printf '%s\n' "$existing" | grep -qxF "$name"; then
670-
skipped+=("$name")
675+
present+=("$name")
671676
else
672-
missing+=("$path")
677+
absent+=("$path")
673678
fi
674679
done
675-
if [ "${#skipped[@]}" -gt 0 ]; then
676-
echo "::notice::Already on ${RELEASE_TAG} and left untouched: ${skipped[*]}"
677-
fi
678-
if [ "${#missing[@]}" -eq 0 ]; then
679-
echo "::notice::Every asset is already on ${RELEASE_TAG}; nothing to upload."
680+
if [ "${#absent[@]}" -eq 0 ]; then
681+
echo "::notice::All ${#present[@]} assets are already on ${RELEASE_TAG}; nothing to upload."
680682
exit 0
681683
fi
682-
gh release upload "$RELEASE_TAG" "${missing[@]}"
684+
if [ "${#present[@]}" -gt 0 ]; then
685+
echo "::error::${RELEASE_TAG} already carries ${#present[@]} of these assets: ${present[*]}"
686+
echo "::error::Adding the rest would mix two builds behind one version - manifest.json pins a sha256 per zip."
687+
echo "::error::Resolve by hand: keep what is published, or cut the next patch and release that instead."
688+
exit 1
689+
fi
690+
gh release upload "$RELEASE_TAG" "${absent[@]}"

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Pythinker Code follows semantic versioning. The commit type you would have writt
9292

9393
Each number counts on its own and none of them roll over at nine. Ten fixes on top of `1.2.0` land on `1.2.10`, not `1.3.0`; the minor moves only when a feature ships, and the major only when something breaks:
9494

95-
```
95+
```text
9696
1.2.9 + fix → 1.2.10
9797
1.9.9 + fix → 1.9.10
9898
1.9.9 + feature → 1.10.0
@@ -116,7 +116,7 @@ Never do any of these:
116116

117117
To correct a bad build, cut the next patch instead: fix the problem, bump, build from that exact commit, sign and notarize, and publish fresh artifacts under the new number. The updater then advertises the new version, and anyone who already installed the old one keeps a build that still matches what it claims to be.
118118

119-
CI enforces this rather than trusting the rule. The desktop release uploads into a draft and refuses to touch a release that is already published; the native CLI job uploads only assets that are missing, so a run that failed part way through still completes while a rebuild of a shipped version is left on the floor.
119+
CI enforces this rather than trusting the rule. The desktop release uploads into a draft and refuses to touch a release that is already published. The native CLI job treats its assets as one set — `manifest.json` pins a sha256 for every zip — so it uploads all of them or none: a release that already has the full set is left alone, and a partial set stops the job rather than pairing zips from one build with checksums from another.
120120

121121
### Release cadence
122122

0 commit comments

Comments
 (0)