| 1 | #!/usr/bin/env bash |
| 2 | # Verify a release commit is reachable from the canonical main branch. |
| 3 | # |
| 4 | # GitHub only processes "Closes #N" keywords when the closing commit lands on |
| 5 | # the repository default branch. Refuse branch-only release tags so release |
| 6 | # assets cannot ship from commits that main does not contain. |
| 7 | set -euo pipefail |
| 8 | |
| 9 | usage() { |
| 10 | cat >&2 <<'EOF' |
| 11 | usage: ensure-release-on-main.sh [--remote <name>] [--main <branch>] <commit-ish> |
| 12 | |
| 13 | Defaults: |
| 14 | --remote origin |
| 15 | --main main |
| 16 | EOF |
| 17 | } |
| 18 | |
| 19 | remote="origin" |
| 20 | main_branch="main" |
| 21 | |
| 22 | while [[ $# -gt 0 ]]; do |
| 23 | case "$1" in |
| 24 | --remote) |
| 25 | remote="${2:?missing value for --remote}" |
| 26 | shift 2 |
| 27 | ;; |
| 28 | --main) |
| 29 | main_branch="${2:?missing value for --main}" |
| 30 | shift 2 |
| 31 | ;; |
| 32 | -h|--help) |
| 33 | usage |
| 34 | exit 0 |
| 35 | ;; |
| 36 | -*) |
| 37 | echo "error: unknown option '$1'" >&2 |
| 38 | usage |
| 39 | exit 2 |
| 40 | ;; |
| 41 | *) |
| 42 | break |
| 43 | ;; |
| 44 | esac |
| 45 | done |
| 46 | |
| 47 | if [[ $# -ne 1 ]]; then |
| 48 | usage |
| 49 | exit 2 |
| 50 | fi |
| 51 | |
| 52 | repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" |
| 53 | cd "${repo}" |
| 54 | |
| 55 | commit="$1" |
| 56 | sha="$(git rev-parse --verify "${commit}^{commit}" 2>/dev/null)" || { |
| 57 | echo "error: '${commit}' is not a commit" >&2 |
| 58 | exit 2 |
| 59 | } |
| 60 | |
| 61 | main_ref="refs/remotes/${remote}/${main_branch}" |
| 62 | git fetch --no-tags "${remote}" "+refs/heads/${main_branch}:${main_ref}" >/dev/null |
| 63 | |
| 64 | main_sha="$(git rev-parse --verify "${main_ref}^{commit}" 2>/dev/null)" || { |
| 65 | echo "error: could not resolve ${main_ref}" >&2 |
| 66 | exit 2 |
| 67 | } |
| 68 | |
| 69 | if git merge-base --is-ancestor "${sha}" "${main_sha}"; then |
| 70 | echo "Release source ${sha} is reachable from ${remote}/${main_branch} (${main_sha})." |
| 71 | exit 0 |
| 72 | fi |
| 73 | |
| 74 | cat >&2 <<EOF |
| 75 | ::error::Release source ${sha} is not reachable from ${remote}/${main_branch} (${main_sha}). |
| 76 | Merge the release PR into ${main_branch} before tagging so GitHub processes |
| 77 | closing keywords and the release PR shows as merged. |
| 78 | EOF |
| 79 | exit 1 |
| 80 |