mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
Mobile candidate publication currently fails if the source branch advances after selection, and the operator can report failure even after the correct immutable tag was created. It also prevents releasing an explicitly selected historical baseline. Add `--commit FULL_SHA` and independently verify in the operator and publisher that the selected commit belongs to the selected source branch's history. Compare immutable SHAs, keep the candidate pinned through publication, and verify the resulting annotated tag instead of requiring the branch to remain stationary. Stable-branch review/check protection and immutable-tag permissions remain enforced. Without `--commit`, selection still defaults to the current source tip. ### Validation - Updated operator and publisher contracts pass: historical main/stable selection, branch advancement during publication, unrelated or missing commits, ancestry lookup failure, and unchanged tag/sequence checks. - Removing ancestry enforcement causes the unrelated-history regression to fail. - Live read-only ancestry verification accepts the original 0.24 baseline. Local publication stops at the existing App-permission check; no tag or build was created. - Full local `just ci` passed. Signed-off-by: Tom Brow <tomb@block.xyz>
86 lines
3.8 KiB
Bash
Executable File
86 lines
3.8 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" =~ ^[0-9a-f]{40}$ ]] || fail "GitHub returned an invalid source commit 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"
|
|
require_mobile_release_commit_ancestor "$target_sha" "$source_sha" || exit 1
|
|
|
|
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"
|