Bash · 3009 bytes Raw Blame History
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: AGPL-3.0-or-later
3 #
4 # S40 cutover rollback. Re-deploys a previous tag to production.
5 # Walks the operator through the data-safe path; do NOT use this
6 # blindly when migrations changed schema in non-additive ways
7 # (read docs/internal/runbooks/rollback.md before running).
8 #
9 # Usage:
10 # deploy/cutover/rollback.sh v0.999
11 #
12 # What it does:
13 # 1. Checks out the named tag.
14 # 2. Confirms the tag exists and is signed (if signing is on).
15 # 3. Runs make deploy-check (DRY-RUN) and prints the diff.
16 # 4. Asks for explicit confirmation before applying.
17 # 5. Runs make deploy.
18 # 6. Runs the smoke script post-deploy.
19 #
20 # Exit status:
21 # 0 — rollback completed and smoked
22 # 1 — operator aborted, deploy failed, or smoke failed
23 # 2 — usage error
24
25 set -euo pipefail
26
27 if [[ $# -lt 1 ]]; then
28 echo "usage: $0 <previous-tag>" >&2
29 exit 2
30 fi
31
32 TAG="$1"
33 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
34 cd "$ROOT"
35
36 confirm() {
37 local prompt="$1"
38 read -r -p "$prompt [yes/NO] " resp
39 if [[ "$resp" != "yes" ]]; then
40 echo "aborted." >&2
41 exit 1
42 fi
43 }
44
45 echo "rollback target: $TAG"
46
47 # 1. Verify the tag exists locally; fetch if needed.
48 if ! git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then
49 echo "tag $TAG not found locally; fetching..."
50 git fetch --tags
51 if ! git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then
52 echo "FAIL: tag $TAG does not exist on origin" >&2
53 exit 1
54 fi
55 fi
56
57 # 2. Check whether the rollback crosses any new migration files.
58 # Forward-only migrations mean the schema is ahead of the rolled-
59 # back code. The operator must read the matching `down` migrations
60 # before continuing; we don't auto-rollback schema here.
61 NEW_MIGRATIONS=$(git diff --name-only "$TAG"..HEAD -- 'internal/migrationsfs/migrations/*.sql' || true)
62 if [[ -n "$NEW_MIGRATIONS" ]]; then
63 echo ""
64 echo "WARNING: migrations exist between $TAG and HEAD:"
65 echo "$NEW_MIGRATIONS" | sed 's/^/ /'
66 echo ""
67 echo "Rolling back code without rolling back schema is fine ONLY if"
68 echo "every migration above is purely additive (new columns/tables"
69 echo "the old code ignores). Read docs/internal/runbooks/rollback.md"
70 echo "before continuing."
71 echo ""
72 confirm "All migrations above are additive (the old code handles them)?"
73 fi
74
75 # 3. Check out the tag.
76 echo ""
77 echo "checking out $TAG..."
78 git checkout "$TAG"
79
80 # 4. Dry-run.
81 echo ""
82 echo "running ANSIBLE deploy-check (DRY-RUN)..."
83 make deploy-check ANSIBLE_INVENTORY=production
84
85 # 5. Confirm + apply.
86 echo ""
87 confirm "Apply the rollback to production?"
88 make deploy ANSIBLE_INVENTORY=production
89
90 # 6. Smoke. Tries to read the production base URL from a deploy var
91 # file; falls back to asking.
92 BASE="${SHITHUB_PROD_URL:-}"
93 if [[ -z "$BASE" ]]; then
94 read -r -p "Smoke base URL (e.g. https://shithub.sh): " BASE
95 fi
96 echo ""
97 echo "running smoke against $BASE..."
98 deploy/cutover/smoke.sh "$BASE"
99
100 echo ""
101 echo "rollback to $TAG complete and smoked. Update the status page."