mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
## 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>
63 lines
2.9 KiB
Bash
Executable File
63 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
: "${PR_HEAD_SHA:?}"
|
|
: "${MERGE_SHA:?}"
|
|
: "${VERSION:?}"
|
|
: "${PR_NUMBER:?}"
|
|
: "${GH_TOKEN:?}"
|
|
|
|
required_checks=(
|
|
"Desktop E2E Integration"
|
|
"Desktop"
|
|
"Rust Lint"
|
|
"Security"
|
|
"Unit Tests"
|
|
"Windows Rust (x86_64-pc-windows-msvc)"
|
|
"Mobile"
|
|
"Web"
|
|
"Backend Integration (relay e2e)"
|
|
"Desktop E2E Relay"
|
|
"Relay E2E"
|
|
"Desktop Build (macOS)"
|
|
"DCO Check"
|
|
)
|
|
|
|
expected_branch="version-bump/$VERSION"
|
|
[[ "${PR_HEAD_REF:-}" == "$expected_branch" ]] || { echo "unexpected release branch" >&2; exit 1; }
|
|
[[ "${PR_BASE_REF:-}" == main ]] || { echo "desktop release must target main" >&2; exit 1; }
|
|
[[ "${PR_HEAD_REPO:-}" == "$GITHUB_REPOSITORY" ]] || { echo "desktop release must be internal" >&2; exit 1; }
|
|
|
|
git fetch origin "$MERGE_SHA" "$PR_HEAD_SHA" refs/heads/main:refs/remotes/origin/main --no-tags
|
|
mapfile -t parents < <(git show -s --format='%P' "$MERGE_SHA" | tr ' ' '\n')
|
|
[[ "${#parents[@]}" -eq 2 ]] || { echo "desktop release was not merged with a true merge commit" >&2; exit 1; }
|
|
[[ "${parents[1]}" == "$PR_HEAD_SHA" ]] || { echo "merge parent 2 is not the reviewed candidate" >&2; exit 1; }
|
|
git merge-base --is-ancestor "$PR_HEAD_SHA" origin/main || { echo "candidate is not reachable from current main" >&2; exit 1; }
|
|
|
|
git checkout --detach "$PR_HEAD_SHA"
|
|
scripts/desktop_release.py validate --candidate "$PR_HEAD_SHA" --version "$VERSION" --repo "$GITHUB_REPOSITORY"
|
|
|
|
review=$(gh api graphql -f query='query($owner:String!,$repo:String!,$number:Int!){repository(owner:$owner,name:$repo){pullRequest(number:$number){reviewDecision}}}' -F owner="${GITHUB_REPOSITORY%/*}" -F repo="${GITHUB_REPOSITORY#*/}" -F number="$PR_NUMBER" --jq '.data.repository.pullRequest')
|
|
jq -e -f scripts/review-decision-approved.jq <<<"$review" >/dev/null || {
|
|
echo "pull request effective review decision is not APPROVED" >&2
|
|
exit 1
|
|
}
|
|
reviews="$(gh api --paginate "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews?per_page=100")"
|
|
valid_approvals="$(jq --arg sha "$PR_HEAD_SHA" '[.[] | select(.state == "APPROVED" and .commit_id == $sha and (.author_association == "MEMBER" or .author_association == "OWNER" or .author_association == "COLLABORATOR"))] | length' <<<"$reviews")"
|
|
[[ "$valid_approvals" -gt 0 ]] || { echo "candidate lacks an exact-head approval from a repository member or collaborator" >&2; exit 1; }
|
|
|
|
checks="$(gh api --paginate --slurp "repos/$GITHUB_REPOSITORY/commits/$PR_HEAD_SHA/check-runs?per_page=100")"
|
|
for required in "${required_checks[@]}"; do
|
|
jq -e --arg name "$required" -f scripts/required-check-succeeded.jq <<<"$checks" >/dev/null || {
|
|
echo "required check is missing or unsuccessful: $required" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
status="$(gh api "repos/$GITHUB_REPOSITORY/commits/$PR_HEAD_SHA/status")"
|
|
jq -e '(.total_count == 0) or (.state == "success")' <<<"$status" >/dev/null || {
|
|
echo "candidate has a failing or pending combined commit status" >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "verified reviewed desktop candidate $PR_HEAD_SHA at merge $MERGE_SHA"
|