Files
buzz/scripts/test-desktop-release-candidate.sh
T
1dfd89ea67 feat(release): make desktop releases immutable (#3568)
## Summary
- add a manual desktop release preparer that regenerates one
version-only candidate from current `origin/main`
- validate deterministic complete changelog accounting, candidate
authorship, allowed files, exact-head approval, required checks, and
two-parent merge topology before tagging the reviewed candidate
- move desktop tags/releases from `v*` to `desktop-v*` while preserving
relay, chart, push-chart, and mobile behavior
- stage all four platform outputs in Actions artifacts and grant GitHub
release write access only to one final all-platform-gated publisher
- publish the versioned release only after complete artifact assembly;
update stable `latest.json` last; never promote prereleases or published
rebuild outputs

## Safety properties
- desktop tags point to the reviewed candidate SHA, not the merge commit
- release builds remain tag-bound and reverify tag == checked-out HEAD
- one final writer fails closed on artifact basename collisions
- per-tag concurrency serializes publication without cancellation
- published reruns do not replace immutable versioned assets or promote
signatures from a rebuild
- candidate branches use an explicit remote OID lease when regenerated

## Validation
- `scripts/test-desktop-release-candidate.sh`
- `scripts/test-release-ref-contract.sh`
- `scripts/test-mobile-release-contract.sh`
- changed workflow YAML parsing (Ruby Psych)
- changed shell syntax (`bash -n`)
- `git diff --check`
- push hooks: branch-skew, Rust workspace tests (1,853 passed), desktop
Tauri tests (3 passed)

## Coordinated companion
- squareup/buzz-releases#79 updates the manually entered desktop
source-tag contract to stable-only `desktop-v*`
- merge the private contract companion before the first namespaced
desktop release

## Rollout blockers (no settings changed here)
Before the first candidate/release:
1. enable merge commits in repository settings
2. allow `merge` in ruleset `13596885`
3. require approval after the last push in ruleset `13596885`
4. include `refs/tags/desktop-v*` explicitly in release ruleset
`14378754`
5. prove the non-publishing candidate/merge/tag/artifact validation path
before any production release

Do not test the old workflow with a prerelease: it can still mutate the
production rolling updater release.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-30 11:27:56 -07:00

85 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
cp "$repo_root/scripts/desktop_release.py" "$tmp/desktop_release.py"
git -C "$tmp" init -q
git -C "$tmp" config user.name test
git -C "$tmp" config user.email test@example.com
mkdir -p "$tmp/scripts" "$tmp/desktop/src-tauri" "$tmp/crates/buzz-core" "$tmp/.release"
mv "$tmp/desktop_release.py" "$tmp/scripts/desktop_release.py"
printf '{"version":"1.0.0"}\n' > "$tmp/desktop/package.json"
printf '{"version":"1.0.0"}\n' > "$tmp/desktop/src-tauri/tauri.conf.json"
printf '[package]\nversion = "1.0.0"\n' > "$tmp/desktop/src-tauri/Cargo.toml"
echo '# Changelog' > "$tmp/CHANGELOG.md"
echo first > "$tmp/desktop/feature"
git -C "$tmp" add .
git -C "$tmp" commit -qm 'feat: first desktop change'
git -C "$tmp" -c tag.gpgSign=false tag v1.0.0
echo second >> "$tmp/desktop/feature"
git -C "$tmp" commit -qam 'fix: desktop fix'
echo policy > "$tmp/POLICY.md"
git -C "$tmp" add POLICY.md
git -C "$tmp" commit -qm 'docs: repository policy'
base=$(git -C "$tmp" rev-parse HEAD)
(
cd "$tmp"
scripts/desktop_release.py generate 1.0.1 --base "$base" --repo block/buzz
python3 - <<'PY'
import json
for path in ('desktop/package.json', 'desktop/src-tauri/tauri.conf.json'):
data=json.load(open(path)); data['version']='1.0.1'; open(path,'w').write(json.dumps(data)+'\n')
p='desktop/src-tauri/Cargo.toml'; open(p,'w').write('[package]\nversion = "1.0.1"\n')
PY
rm -f msg
git add .
cat >msg <<'EOF'
chore(release): release Buzz Desktop version 1.0.1
Co-authored-by: Test Automation <test@example.com>
EOF
git -c user.name=Wes -c user.email=wesbillman@users.noreply.github.com commit -q -s -F msg
rm msg
scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz
grep -Fq '### Other repository changes' CHANGELOG.md
grep -Fq "$(git rev-parse HEAD~1)" CHANGELOG.md
grep -Fq "$(git rev-parse HEAD~2)" CHANGELOG.md
# Metadata cannot lie about the prior release boundary.
cp .release/desktop-candidate.json metadata.json
python3 - <<'PY'
import json
p='.release/desktop-candidate.json'; d=json.load(open(p)); d['previous_tag']=None; open(p,'w').write(json.dumps(d)+'\n')
PY
if scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz >/dev/null 2>&1; then
echo "validator accepted a forged previous release tag" >&2
exit 1
fi
mv metadata.json .release/desktop-candidate.json
)
# An initial release must account for the root commit, not silently omit it.
initial=$(mktemp -d)
cp "$repo_root/scripts/desktop_release.py" "$initial/desktop_release.py"
git -C "$initial" init -q
git -C "$initial" config user.name test
git -C "$initial" config user.email test@example.com
mkdir -p "$initial/scripts" "$initial/desktop/src-tauri"
mv "$initial/desktop_release.py" "$initial/scripts/desktop_release.py"
printf '{"version":"0.1.0"}\n' > "$initial/desktop/package.json"
printf '{"version":"0.1.0"}\n' > "$initial/desktop/src-tauri/tauri.conf.json"
printf '[package]\nversion = "0.1.0"\n' > "$initial/desktop/src-tauri/Cargo.toml"
printf '# Changelog\n' > "$initial/CHANGELOG.md"
echo root > "$initial/ROOT.md"
git -C "$initial" add .
git -C "$initial" commit -qm 'feat: root release content'
root_sha=$(git -C "$initial" rev-parse HEAD)
(cd "$initial" && scripts/desktop_release.py generate 0.1.0 --base "$root_sha" --repo block/buzz)
grep -Fq "$root_sha" "$initial/CHANGELOG.md"
rm -rf "$initial"
echo "desktop release candidate contract passed"