mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
Mobile candidates currently require the current `main` commit, so a stable release branch cannot publish through the existing protected tag workflow. Add optional `--source-branch mobile-vMAJOR.MINOR` selection while preserving `main` as the default. Both the command and App-backed publisher reject a stable branch whose major/minor differs from the requested version. Candidates retain exact-tip checks and immutable annotated tags; the private build pipeline is unchanged. Document the temporary stable-only distribution policy and explicit per-fix backport decisions. No candidate, build, or upload is triggered by this change. No duplicate implementation was found among open release PRs. Stable publication now checks active review and status-check protection in both the operator and publisher. The process permits approved backport PRs and stable-only corrections for regressions caused by earlier backports. Administrator activation and verification of the no-bypass ruleset remain outstanding; non-admin API responses hide the bypass actor list. ### Validation - Full local `just ci` passed during preparation; the final changes affect shell release tooling, its workflow inputs, CI path detection, and documentation. - Publisher contract passed, including version-matched stable sources and mismatched branch rejection. - Operator contract covers default main, a divergent stable commit, missing or mismatched sources, immutable sequencing, and publication races. The final operator contract run passed. - Updated publisher and operator contracts pass, including 17 weakened-policy cases and rejection of unavailable protection. The live, currently unprotected stable branch is rejected. --------- Signed-off-by: Tom Brow <tomb@block.xyz>
86 lines
3.7 KiB
Bash
Executable File
86 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
fail() {
|
|
echo "Error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
# shellcheck source=scripts/release-rulesets.sh
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/release-rulesets.sh"
|
|
# shellcheck source=scripts/mobile-release-source.sh
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/mobile-release-source.sh"
|
|
|
|
[[ "$#" -eq 4 ]] || fail "usage: $0 X.Y.Z N COMMIT_SHA SOURCE_BRANCH"
|
|
version="$1"
|
|
candidate_number="$2"
|
|
target_sha="$3"
|
|
MOBILE_RELEASE_SOURCE_BRANCH="$4"
|
|
repo="${GITHUB_REPOSITORY:-}"
|
|
|
|
[[ "$repo" == "block/buzz" ]] || fail "candidate publishing is restricted to block/buzz"
|
|
[[ "$version" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] || \
|
|
fail "'$version' is not a mobile release version (expected X.Y.Z)"
|
|
[[ "$candidate_number" =~ ^[1-9][0-9]*$ ]] || \
|
|
fail "'$candidate_number' is not a candidate number (expected N >= 1)"
|
|
[[ "$target_sha" =~ ^[0-9a-f]{40}$ ]] || fail "'$target_sha' is not a full commit SHA"
|
|
require_mobile_release_source "$version" "$MOBILE_RELEASE_SOURCE_BRANCH" || exit 1
|
|
command -v gh >/dev/null 2>&1 || fail "gh is required"
|
|
require_release_tag_ruleset || exit 1
|
|
require_mobile_release_branch_ruleset "$MOBILE_RELEASE_SOURCE_BRANCH" || exit 1
|
|
|
|
source_sha="$(gh api "repos/$repo/git/ref/heads/$MOBILE_RELEASE_SOURCE_BRANCH" --jq .object.sha)" || \
|
|
fail "could not resolve $repo $MOBILE_RELEASE_SOURCE_BRANCH"
|
|
[[ "$source_sha" == "$target_sha" ]] || \
|
|
fail "$repo $MOBILE_RELEASE_SOURCE_BRANCH moved from requested commit $target_sha to $source_sha"
|
|
commit_sha="$(gh api "repos/$repo/commits/$target_sha" --jq .sha)" || \
|
|
fail "$target_sha is not a commit in $repo"
|
|
[[ "$commit_sha" == "$target_sha" ]] || \
|
|
fail "GitHub resolved requested commit $target_sha as $commit_sha"
|
|
|
|
refs="$(
|
|
gh api --paginate "repos/$repo/git/matching-refs/tags/mobile-v${version}-rc." --jq '.[].ref'
|
|
)" || fail "could not list existing candidates for $version"
|
|
next=1
|
|
while IFS= read -r ref; do
|
|
[[ -n "$ref" ]] || continue
|
|
[[ "$ref" =~ ^refs/tags/mobile-v${version//./\.}-rc\.([1-9][0-9]*)$ ]] || continue
|
|
number="${BASH_REMATCH[1]}"
|
|
(( number >= next )) && next=$((number + 1))
|
|
done <<< "$refs"
|
|
[[ "$candidate_number" -eq "$next" ]] || \
|
|
fail "candidate sequence changed; expected rc.$candidate_number but next is rc.$next"
|
|
|
|
tag="mobile-v${version}-rc.${candidate_number}"
|
|
message="Buzz Mobile $version release candidate $candidate_number"
|
|
tag_object_sha="$(
|
|
gh api --method POST "repos/$repo/git/tags" \
|
|
-f tag="$tag" \
|
|
-f message="$message" \
|
|
-f object="$target_sha" \
|
|
-f type=commit \
|
|
--jq .sha
|
|
)" || fail "could not create annotated tag object for $tag"
|
|
[[ "$tag_object_sha" =~ ^[0-9a-f]{40}$ ]] || fail "GitHub returned an invalid tag object SHA"
|
|
|
|
gh api --method POST "repos/$repo/git/refs" \
|
|
-f ref="refs/tags/$tag" \
|
|
-f sha="$tag_object_sha" \
|
|
--silent || fail "could not publish $tag"
|
|
|
|
published_type="$(gh api "repos/$repo/git/ref/tags/$tag" --jq .object.type)" || \
|
|
fail "could not verify published tag $tag"
|
|
published_object="$(gh api "repos/$repo/git/ref/tags/$tag" --jq .object.sha)" || \
|
|
fail "could not verify published tag $tag"
|
|
[[ "$published_type" == "tag" && "$published_object" == "$tag_object_sha" ]] || \
|
|
fail "$tag does not reference the expected annotated tag object"
|
|
|
|
direct_type="$(gh api "repos/$repo/git/tags/$tag_object_sha" --jq .object.type)" || \
|
|
fail "could not verify annotated tag object $tag_object_sha"
|
|
direct_sha="$(gh api "repos/$repo/git/tags/$tag_object_sha" --jq .object.sha)" || \
|
|
fail "could not verify annotated tag object $tag_object_sha"
|
|
[[ "$direct_type" == "commit" && "$direct_sha" == "$target_sha" ]] || \
|
|
fail "$tag does not point directly to requested commit $target_sha"
|
|
|
|
printf 'Published %s at %s through buzz-release-bot.\n' "$tag" "$target_sha"
|