Files
buzz/scripts/prepare-desktop-release.sh
Will Pfleger 820a858997 fix(release): attribute desktop candidates to the operator (#6831)
The desktop release tooling hardcoded one contributor's personal
identity into every release candidate commit.
`scripts/prepare-desktop-release.sh` committed the candidate with a `git
-c user.name='Wes' -c user.email='wesbillman@users.noreply.github.com'`
override, and `scripts/desktop_release.py` `validate` required the
candidate author to be exactly `Wes
<wesbillman@users.noreply.github.com>` plus a matching `Signed-off-by`
trailer. That leaked from Wes's working setup into the validation
contract in #3568, so a release cut by any other operator was falsely
attributed to and signed off by Wes (as happened on #6828).

## Change

- `prepare-desktop-release.sh`: drop the `-c` identity overrides so `git
commit -s` uses the operator's own configured identity to author and
sign off the candidate. The automation `Co-authored-by` trailer is
unchanged.
- `desktop_release.py` `validate`: replace the exact-Wes checks with
structural ones — the commit author must be non-empty, the body must
contain a `Signed-off-by` trailer whose name and email match the commit
author (honest DCO), and the existing automation `Co-authored-by` regex
check stays. Failure messages remain specific.
- `test-desktop-release-candidate.sh`: the fixture candidate now commits
under the harness's own identity, and a new negative case rewrites the
author to a mismatched identity and asserts the validator rejects it.

Release authorization is bound to the merged PR via the GitHub API in
`scripts/verify-desktop-release-merge.sh`, never the commit author
field, so this does not weaken the trust model. `RELEASING.md` and
`.github/workflows/desktop-release-candidate.yml` reference no author
identity and need no change.

Verified locally: `scripts/test-desktop-release-candidate.sh` passes,
including the new sign-off/author-mismatch rejection.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-08-25 18:50:58 -04:00

83 lines
3.3 KiB
Bash
Executable File

#!/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 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 may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on \`main\` cannot alter it.
The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag.
EOF
if existing="$(gh pr list --repo block/buzz --head "$branch" --state open --json number --jq '.[0].number')" && [[ -n "$existing" ]]; then
gh pr edit --repo block/buzz "$existing" --title "chore(release): release Buzz Desktop version $version" --body-file "$body"
else
gh pr create --repo block/buzz --base main --head "$branch" \
--title "chore(release): release Buzz Desktop version $version" --body-file "$body"
fi