mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Signed-off-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1re830n24qhgstulznk5dxdluccspxkxxdq643lm4q3zwwwjgsuqqmcmdrm <1e4f17cd5505d105f3e29da8d337fcc6201358c6683558ff750444e73a488700@buzz.block.builderlab.xyz> Signed-off-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz> Co-authored-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1re830n24qhgstulznk5dxdluccspxkxxdq643lm4q3zwwwjgsuqqmcmdrm <1e4f17cd5505d105f3e29da8d337fcc6201358c6683558ff750444e73a488700@buzz.block.builderlab.xyz> Co-authored-by: 7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>
170 lines
6.0 KiB
Bash
Executable File
170 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat >&2 <<'USAGE'
|
|
usage:
|
|
scripts/mobile-release.sh candidate X.Y.Z
|
|
|
|
candidate Publish the next immutable mobile-vX.Y.Z-rc.N candidate tag at the
|
|
exact current commit of block/buzz's remote main branch.
|
|
USAGE
|
|
exit 2
|
|
}
|
|
|
|
fail() {
|
|
echo "Error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
# shellcheck source=scripts/release-rulesets.sh
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/release-rulesets.sh"
|
|
|
|
require_gh_minimum_version() {
|
|
local minimum="2.87.0" version
|
|
|
|
command -v gh >/dev/null 2>&1 || fail "gh >= $minimum is required"
|
|
version="$(gh --version 2>/dev/null | awk 'NR == 1 { print $3 }')" || \
|
|
fail "could not determine gh version (gh >= $minimum is required)"
|
|
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || \
|
|
fail "gh returned invalid version '$version'"
|
|
if ! awk -v current="$version" -v minimum="$minimum" '
|
|
BEGIN {
|
|
split(current, c, ".")
|
|
split(minimum, m, ".")
|
|
for (i = 1; i <= 3; i++) {
|
|
if ((c[i] + 0) > (m[i] + 0)) exit 0
|
|
if ((c[i] + 0) < (m[i] + 0)) exit 1
|
|
}
|
|
exit 0
|
|
}
|
|
'; then
|
|
fail "gh $version is too old; gh >= $minimum is required"
|
|
fi
|
|
}
|
|
|
|
require_clean_semver() {
|
|
[[ "$1" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] || \
|
|
fail "'$1' is not a mobile release version (expected X.Y.Z)"
|
|
}
|
|
|
|
require_clean_tree() {
|
|
git diff --quiet && git diff --cached --quiet && \
|
|
[[ -z "$(git status --short --untracked-files=normal)" ]] || \
|
|
fail "working tree is dirty; commit or stash changes first"
|
|
}
|
|
|
|
require_annotated_tag() {
|
|
local git_dir="$1" object="$2" label="$3"
|
|
if [[ "$(git -C "$git_dir" cat-file -t "$object" 2>/dev/null || true)" != "tag" ]]; then
|
|
echo "$label must be an annotated tag" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
remote_tag_commit_sha() {
|
|
local ref="$1" line advertised_oid tmp fetched_oid commit
|
|
line="$(git ls-remote --refs origin "$ref")" || return 1
|
|
[[ -n "$line" && "$line" != *$'\n'* ]] || return 1
|
|
advertised_oid="${line%%$'\t'*}"
|
|
tmp="$(mktemp -d)"
|
|
git -C "$tmp" init -q
|
|
git -C "$tmp" remote add origin "$(git remote get-url origin)"
|
|
if ! git -C "$tmp" fetch -q --depth 1 origin "$ref"; then
|
|
rm -rf "$tmp"
|
|
return 1
|
|
fi
|
|
fetched_oid="$(git -C "$tmp" rev-parse --verify FETCH_HEAD)"
|
|
if [[ "$fetched_oid" != "$advertised_oid" ]]; then
|
|
rm -rf "$tmp"
|
|
fail "$ref moved while it was being resolved"
|
|
fi
|
|
if ! require_annotated_tag "$tmp" FETCH_HEAD "$ref"; then
|
|
rm -rf "$tmp"
|
|
return 1
|
|
fi
|
|
if ! commit="$(git -C "$tmp" rev-parse --verify 'FETCH_HEAD^{commit}')"; then
|
|
rm -rf "$tmp"
|
|
return 1
|
|
fi
|
|
rm -rf "$tmp"
|
|
printf '%s' "$commit"
|
|
}
|
|
|
|
remote_main_commit_sha() {
|
|
local ref="refs/heads/main" line advertised_oid fetched_oid commit
|
|
line="$(git ls-remote --refs origin "$ref")" || return 1
|
|
[[ -n "$line" && "$line" != *$'\n'* ]] || return 1
|
|
advertised_oid="${line%%$'\t'*}"
|
|
git fetch -q --no-tags origin "$ref"
|
|
fetched_oid="$(git rev-parse --verify FETCH_HEAD)"
|
|
[[ "$fetched_oid" == "$advertised_oid" ]] || \
|
|
fail "origin/main moved while it was being resolved"
|
|
commit="$(git rev-parse --verify 'FETCH_HEAD^{commit}')" || return 1
|
|
[[ "$commit" == "$advertised_oid" ]] || \
|
|
fail "origin/main did not resolve directly to a commit"
|
|
printf '%s' "$commit"
|
|
}
|
|
|
|
command="${1:-}"
|
|
case "$command" in
|
|
candidate)
|
|
[[ "$#" -eq 2 ]] || usage
|
|
version="$2"
|
|
require_clean_semver "$version"
|
|
require_clean_tree
|
|
require_canonical_repository || exit 1
|
|
require_gh_minimum_version
|
|
|
|
local_head_sha="$(git rev-parse --verify 'HEAD^{commit}')" || fail "HEAD is not a commit"
|
|
main_sha="$(remote_main_commit_sha)" || fail "origin/main does not exist"
|
|
next=1
|
|
if ! remote_tags="$(git ls-remote --refs --tags origin "refs/tags/mobile-v${version}-rc.*")"; then
|
|
fail "could not list existing candidates for $version"
|
|
fi
|
|
while IFS=$'\t' read -r _ ref; do
|
|
[[ "$ref" =~ ^refs/tags/mobile-v${version//./\.}-rc\.([1-9][0-9]*)$ ]] || continue
|
|
number="${BASH_REMATCH[1]}"
|
|
(( number >= next )) && next=$((number + 1))
|
|
done <<< "$remote_tags"
|
|
|
|
tag="mobile-v${version}-rc.${next}"
|
|
workflow="mobile-release-candidate.yml"
|
|
if dispatch_output="$(gh workflow run "$workflow" \
|
|
--repo block/buzz \
|
|
--ref main \
|
|
-f "version=$version" \
|
|
-f "candidate_number=$next" \
|
|
-f "target_sha=$main_sha" 2>&1)"; then
|
|
:
|
|
else
|
|
if [[ "$dispatch_output" == *"does not have 'workflow_dispatch' trigger"* ]]; then
|
|
fail "$workflow is not available on main yet; merge the release-process change before publishing a candidate"
|
|
fi
|
|
fail "could not dispatch App-backed publication for $tag: $dispatch_output"
|
|
fi
|
|
run_url="$(printf '%s\n' "$dispatch_output" | awk '/^https:\/\/github\.com\/block\/buzz\/actions\/runs\/[0-9]+$/ { if (found) exit 2; found = $0 } END { if (found) print found }')" || \
|
|
fail "GitHub returned multiple workflow run URLs for one candidate dispatch"
|
|
[[ -n "$run_url" ]] || \
|
|
fail "GitHub accepted the candidate dispatch but returned no workflow run URL"
|
|
run_id="${run_url##*/}"
|
|
gh run watch "$run_id" --repo block/buzz --exit-status --compact || \
|
|
fail "App-backed publication failed: $run_url"
|
|
|
|
current_main_sha="$(remote_main_commit_sha)" || fail "origin/main does not exist after publication"
|
|
[[ "$current_main_sha" == "$main_sha" ]] || \
|
|
fail "origin/main moved from requested commit $main_sha to $current_main_sha during publication"
|
|
published_sha="$(remote_tag_commit_sha "refs/tags/$tag")" || \
|
|
fail "publication completed without exact annotated candidate tag $tag"
|
|
[[ "$published_sha" == "$main_sha" ]] || \
|
|
fail "$tag resolved to $published_sha instead of requested commit $main_sha"
|
|
if [[ "$local_head_sha" != "$main_sha" ]]; then
|
|
echo "Note: local HEAD is $local_head_sha; candidate source is current origin/main $main_sha." >&2
|
|
fi
|
|
printf 'Published %s at origin/main commit %s through buzz-release-bot. Use this exact tag in Release Mobile.\n' \
|
|
"$tag" "$main_sha"
|
|
;;
|
|
|
|
*) usage ;;
|
|
esac
|