mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-28 17:20:58 +00:00
201 lines
6.2 KiB
Bash
Executable file
201 lines
6.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
MODE="publish"
|
|
VERSION=""
|
|
MOBILE_RELEASE_DECISION=""
|
|
MOBILE_RELEASE_EVIDENCE=""
|
|
HOTFIX_REASON=""
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/trigger-stable-patch.sh [--dry-run] [options] [version]
|
|
|
|
Dispatches exactly one governed workflow. The default release workflow builds
|
|
and validates an immutable candidate before publication. Use --dry-run only
|
|
when a no-public-release rehearsal is required.
|
|
|
|
Options:
|
|
--dry-run Dispatch Release Dry Run only.
|
|
--mobile-release-decision VALUE Override the inferred mobile decision.
|
|
--mobile-release-evidence VALUE Evidence for a mobile compatibility decision.
|
|
--emergency-hotfix-reason VALUE Bypass an RC-required risk with an explicit reason.
|
|
-h, --help Show this help.
|
|
EOF
|
|
}
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--dry-run)
|
|
MODE="dry-run"
|
|
shift
|
|
;;
|
|
--mobile-release-decision)
|
|
MOBILE_RELEASE_DECISION="${2:?--mobile-release-decision requires a value}"
|
|
shift 2
|
|
;;
|
|
--mobile-release-evidence)
|
|
MOBILE_RELEASE_EVIDENCE="${2:?--mobile-release-evidence requires a value}"
|
|
shift 2
|
|
;;
|
|
--emergency-hotfix-reason)
|
|
HOTFIX_REASON="${2:?--emergency-hotfix-reason requires a value}"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--*)
|
|
echo "Unknown option: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
if [ -n "$VERSION" ]; then
|
|
echo "Only one version may be supplied." >&2
|
|
exit 2
|
|
fi
|
|
VERSION="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
VERSION="${VERSION:-$(tr -d '\n' < VERSION)}"
|
|
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[1-9][0-9]*$ ]]; then
|
|
echo "Stable patch version required, got: ${VERSION}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
FILE_VERSION="$(tr -d '\n' < VERSION)"
|
|
if [ "$FILE_VERSION" != "$VERSION" ]; then
|
|
echo "VERSION contains ${FILE_VERSION}; requested ${VERSION}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$(git status --porcelain=v1)" ]; then
|
|
echo "The release worktree must be clean." >&2
|
|
git status --short
|
|
exit 1
|
|
fi
|
|
|
|
CURRENT_BRANCH="$(git branch --show-current)"
|
|
REQUIRED_BRANCH="$(python3 scripts/release_control/control_plane.py --branch-for-version "$VERSION")"
|
|
if [ "$CURRENT_BRANCH" != "$REQUIRED_BRANCH" ]; then
|
|
echo "Version ${VERSION} must be released from ${REQUIRED_BRANCH}, not ${CURRENT_BRANCH}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
git fetch --quiet --prune origin "$REQUIRED_BRANCH" --tags
|
|
LOCAL_SHA="$(git rev-parse HEAD)"
|
|
REMOTE_SHA="$(git rev-parse "origin/${REQUIRED_BRANCH}")"
|
|
if [ "$LOCAL_SHA" != "$REMOTE_SHA" ]; then
|
|
echo "The exact release commit must already be pushed to origin/${REQUIRED_BRANCH}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
NOTES_FILE="docs/releases/RELEASE_NOTES_v${VERSION}.md"
|
|
if [ ! -s "$NOTES_FILE" ]; then
|
|
echo "Canonical release notes are required at ${NOTES_FILE}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
RESOLVER_ARGS=(
|
|
--version "$VERSION"
|
|
--derive-rollback-latest-stable
|
|
--release-notes-file "$NOTES_FILE"
|
|
)
|
|
HOTFIX_EXCEPTION="false"
|
|
if [ -n "$HOTFIX_REASON" ]; then
|
|
HOTFIX_EXCEPTION="true"
|
|
RESOLVER_ARGS+=(--hotfix-exception --hotfix-reason "$HOTFIX_REASON")
|
|
fi
|
|
|
|
PROMOTION_METADATA="$(python3 scripts/release_control/resolve_release_promotion.py "${RESOLVER_ARGS[@]}")"
|
|
ROLLBACK_TAG="$(awk -F= '$1 == "rollback_tag" {print $2}' <<<"$PROMOTION_METADATA")"
|
|
if [ -z "$ROLLBACK_TAG" ]; then
|
|
echo "Release preflight did not resolve a rollback tag." >&2
|
|
exit 1
|
|
fi
|
|
|
|
MOBILE_IMPACT_PATHS="$({
|
|
git diff --name-only "${ROLLBACK_TAG}..HEAD" | rg '^(internal/(relay|mobile)/|pkg/relay/|internal/api/(cloud_handoff|magic_link|mobile)|tests/integration/.*(mobile|relay)|scripts/.*mobile)'
|
|
} || true)"
|
|
if [ -z "$MOBILE_RELEASE_DECISION" ]; then
|
|
if [ -n "$MOBILE_IMPACT_PATHS" ]; then
|
|
echo "Mobile-facing paths changed since ${ROLLBACK_TAG}:" >&2
|
|
printf '%s\n' "$MOBILE_IMPACT_PATHS" >&2
|
|
echo "Supply --mobile-release-decision and --mobile-release-evidence after completing the governed mobile check." >&2
|
|
exit 1
|
|
fi
|
|
MOBILE_RELEASE_DECISION="no-mobile-impact"
|
|
MOBILE_RELEASE_EVIDENCE="No mobile-facing paths changed between ${ROLLBACK_TAG} and ${LOCAL_SHA}."
|
|
fi
|
|
|
|
python3 scripts/release_control/mobile_release_gate.py \
|
|
--version "$VERSION" \
|
|
--decision "$MOBILE_RELEASE_DECISION" \
|
|
--evidence "$MOBILE_RELEASE_EVIDENCE"
|
|
|
|
if [ "$MODE" = "dry-run" ]; then
|
|
WORKFLOW="release-dry-run.yml"
|
|
python3 scripts/check-workflow-dispatch-inputs.py \
|
|
--workflow-path .github/workflows/release-dry-run.yml \
|
|
--branch "$CURRENT_BRANCH" \
|
|
--require version \
|
|
--require promoted_from_tag \
|
|
--require rollback_version \
|
|
--require ga_date \
|
|
--require v5_eos_date \
|
|
--require hotfix_exception \
|
|
--require hotfix_reason \
|
|
--require note \
|
|
--require mobile_release_decision \
|
|
--require mobile_release_evidence
|
|
|
|
gh workflow run "$WORKFLOW" \
|
|
--ref "$CURRENT_BRANCH" \
|
|
-f version="$VERSION" \
|
|
-f promoted_from_tag="" \
|
|
-f rollback_version="$ROLLBACK_TAG" \
|
|
-f ga_date="" \
|
|
-f v5_eos_date="" \
|
|
-f hotfix_exception="$HOTFIX_EXCEPTION" \
|
|
-f hotfix_reason="$HOTFIX_REASON" \
|
|
-f note="Stable patch preflight for ${VERSION} at ${LOCAL_SHA}" \
|
|
-f mobile_release_decision="$MOBILE_RELEASE_DECISION" \
|
|
-f mobile_release_evidence="$MOBILE_RELEASE_EVIDENCE"
|
|
else
|
|
python3 scripts/check-workflow-dispatch-inputs.py \
|
|
--workflow-path .github/workflows/create-release.yml \
|
|
--branch "$CURRENT_BRANCH" \
|
|
--require version \
|
|
--require release_notes \
|
|
--require promoted_from_tag \
|
|
--require rollback_version \
|
|
--require ga_date \
|
|
--require v5_eos_date \
|
|
--require hotfix_exception \
|
|
--require hotfix_reason \
|
|
--require draft_only \
|
|
--require mobile_release_decision \
|
|
--require mobile_release_evidence
|
|
|
|
gh workflow run create-release.yml \
|
|
--ref "$CURRENT_BRANCH" \
|
|
-f version="$VERSION" \
|
|
-f release_notes="$(<"$NOTES_FILE")" \
|
|
-f promoted_from_tag="" \
|
|
-f rollback_version="$ROLLBACK_TAG" \
|
|
-f ga_date="" \
|
|
-f v5_eos_date="" \
|
|
-f hotfix_exception="$HOTFIX_EXCEPTION" \
|
|
-f hotfix_reason="$HOTFIX_REASON" \
|
|
-f draft_only=false \
|
|
-f mobile_release_decision="$MOBILE_RELEASE_DECISION" \
|
|
-f mobile_release_evidence="$MOBILE_RELEASE_EVIDENCE"
|
|
|
|
fi
|
|
|
|
echo "Dispatched ${WORKFLOW:-create-release.yml} for v${VERSION} at ${LOCAL_SHA}."
|