#!/usr/bin/env bash # Roll an env BACK to a prior release: re-point releases/latest/ at a chosen release # and re-fire the deploy. Run by rollback.yml after aws creds are configured. # # ENV dev | prod (required) # BUCKET open-swe--assets (required) # DEPLOY_DOC open-swe--deploy (required) # TARGET_SHA release sha to restore; blank => releases/last-good/ (optional) # # releases/latest/ is moved TRANSACTIONALLY (same as the forward deploy): pointed at # the target, the box rolled, and on a failed roll latest is reverted to whatever it # was before the rollback attempt — so a failed rollback never leaves latest at a # release the box could not bring up. releases/last-good/ is left untouched; advance # it by running a forward deploy. # # Reuses the app deploy role's existing releases/* write + tag-scoped ssm:SendCommand # — no new IAM. The fire/wait/gate is the SAME roll-box.sh the forward deploy uses. set -euo pipefail : "${ENV:?}" "${BUCKET:?}" "${DEPLOY_DOC:?}" HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Copy a release prefix into releases/latest/ AND record the resolved sha, so # releases/latest/sha.txt is always a real commit a later revert can resolve. point_latest() { # $1 = source prefix (releases/|releases/last-good); $2 = sha to record local src="$1" sha="$2" f for f in app.tar.gz spa.tar.gz; do aws s3 cp "s3://${BUCKET}/${src}/${f}" "s3://${BUCKET}/releases/latest/${f}" done printf '%s\n' "${sha}" | aws s3 cp - "s3://${BUCKET}/releases/latest/sha.txt" } if [ -n "${TARGET_SHA:-}" ]; then SRC="releases/${TARGET_SHA}" LABEL="${TARGET_SHA}" RESOLVED_SHA="${TARGET_SHA}" else SRC="releases/last-good" LABEL="last-good" # resolve last-good's real sha so latest/sha.txt records a commit, not a label. RESOLVED_SHA="$(aws s3 cp "s3://${BUCKET}/releases/last-good/sha.txt" - 2>/dev/null | tr -d '[:space:]' || true)" RESOLVED_SHA="${RESOLVED_SHA:-last-good}" fi echo "==> rollback ${ENV} to ${LABEL} (s3://${BUCKET}/${SRC}/, sha=${RESOLVED_SHA})" # Refuse to roll back to a release that is not fully present. for f in app.tar.gz spa.tar.gz; do aws s3 ls "s3://${BUCKET}/${SRC}/${f}" >/dev/null 2>&1 \ || { echo "ERROR: ${SRC}/${f} not found in s3://${BUCKET} — cannot roll back to ${LABEL}" >&2; exit 1; } done # Capture what latest points at now, so a failed rollback can be reverted. PREV_SHA="$(aws s3 cp "s3://${BUCKET}/releases/latest/sha.txt" - 2>/dev/null | tr -d '[:space:]' || true)" echo "==> point releases/latest/ -> ${SRC} (was ${PREV_SHA:-})" point_latest "${SRC}" "${RESOLVED_SHA}" if ! ROLL_COMMENT="rollback ${ENV} to ${LABEL}" bash "${HERE}/roll-box.sh"; then if [ -n "${PREV_SHA}" ]; then echo "!! rollback deploy failed — reverting releases/latest/ -> ${PREV_SHA}" >&2 point_latest "releases/${PREV_SHA}" "${PREV_SHA}" fi exit 1 fi echo "==> ${ENV} rolled back to ${LABEL}" echo "NOTE: releases/last-good/ is left unchanged; re-run a forward deploy to advance it."