mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
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>
This commit is contained in:
83
scripts/prepare-desktop-release.sh
Executable file
83
scripts/prepare-desktop-release.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
version="${1:-}"
|
||||
mode="${2:-publish}"
|
||||
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || {
|
||||
echo "usage: $0 <semver> [publish|validate-only]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
remote="${RELEASE_REMOTE:-origin}"
|
||||
git fetch "$remote" refs/heads/main:refs/remotes/origin/main --no-tags
|
||||
git fetch "$remote" '+refs/tags/v*:refs/tags/v*' '+refs/tags/desktop-v*:refs/tags/desktop-v*'
|
||||
base_sha="$(git rev-parse refs/remotes/origin/main)"
|
||||
branch="version-bump/$version"
|
||||
|
||||
remote_branch="refs/heads/$branch"
|
||||
remote_oid=""
|
||||
if remote_oid="$(git ls-remote "$remote" "$remote_branch" | awk '{print $1}')" && [[ -n "$remote_oid" ]]; then
|
||||
git fetch "$remote" "$remote_branch:refs/remotes/origin/$branch"
|
||||
fi
|
||||
|
||||
git checkout -B "$branch" "$base_sha"
|
||||
just bump-desktop-version "$version"
|
||||
scripts/desktop_release.py generate "$version" --base "$base_sha" --repo block/buzz
|
||||
|
||||
git add \
|
||||
.release/desktop-candidate.json \
|
||||
CHANGELOG.md \
|
||||
desktop/package.json \
|
||||
desktop/src-tauri/tauri.conf.json \
|
||||
desktop/src-tauri/Cargo.toml \
|
||||
desktop/src-tauri/Cargo.lock \
|
||||
pnpm-lock.yaml
|
||||
|
||||
agent_name="${RELEASE_AUTOMATION_NAME:-${AGENT_NAME:-Release Automation}}"
|
||||
agent_email="${RELEASE_AUTOMATION_EMAIL:-${AGENT_EMAIL:-release-automation@users.noreply.github.com}}"
|
||||
msg="$(mktemp)"
|
||||
trap 'rm -f "$msg"' EXIT
|
||||
cat >"$msg" <<EOF
|
||||
chore(release): release Buzz Desktop version $version
|
||||
|
||||
Co-authored-by: $agent_name <$agent_email>
|
||||
EOF
|
||||
git -c user.name='Wes' -c user.email='wesbillman@users.noreply.github.com' \
|
||||
commit -s -F "$msg"
|
||||
scripts/desktop_release.py validate --candidate HEAD --version "$version" --repo block/buzz
|
||||
|
||||
candidate_sha="$(git rev-parse HEAD)"
|
||||
previous_tag="$(python3 -c 'import json; print(json.load(open(".release/desktop-candidate.json"))["previous_tag"] or "initial")')"
|
||||
printf 'base_sha=%s\ncandidate_sha=%s\nprevious_tag=%s\ntag=desktop-v%s\n' \
|
||||
"$base_sha" "$candidate_sha" "$previous_tag" "$version"
|
||||
|
||||
if [[ "$mode" == validate-only ]]; then
|
||||
exit 0
|
||||
fi
|
||||
[[ "$mode" == publish ]] || { echo "unknown mode: $mode" >&2; exit 1; }
|
||||
if [[ -n "$remote_oid" ]]; then
|
||||
git push --force-with-lease="$remote_branch:$remote_oid" "$remote" "HEAD:$remote_branch"
|
||||
else
|
||||
git push --force-with-lease="$remote_branch:" "$remote" "HEAD:$remote_branch"
|
||||
fi
|
||||
|
||||
body="$(mktemp)"
|
||||
trap 'rm -f "$msg" "$body"' EXIT
|
||||
cat >"$body" <<EOF
|
||||
## Buzz Desktop release v$version
|
||||
|
||||
- **Frozen main:** \`$base_sha\`
|
||||
- **Reviewed candidate:** \`$candidate_sha\`
|
||||
- **Previous desktop release:** \`$previous_tag\`
|
||||
- **Proposed immutable tag:** \`desktop-v$version\`
|
||||
|
||||
This PR must be merged with **Create a merge commit**. Squash/rebase, stale-head approval, incomplete notes, or a candidate mismatch produce no tag.
|
||||
|
||||
The checked-in changelog accounts for every non-merge commit in the release range. Publication remains bound to the immutable candidate tag.
|
||||
EOF
|
||||
if existing="$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')" && [[ -n "$existing" ]]; then
|
||||
gh pr edit "$existing" --title "chore(release): release Buzz Desktop version $version" --body-file "$body"
|
||||
else
|
||||
gh pr create --base main --head "$branch" \
|
||||
--title "chore(release): release Buzz Desktop version $version" --body-file "$body"
|
||||
fi
|
||||
Reference in New Issue
Block a user