#!/usr/bin/env bash # Push both artifacts and point infra at them. # # scripts/deploy.sh # # Runs the api build and push, then the site build and push, then writes the # release id both were named after into infra's releases.auto.tfvars.json. # The result is one uncommitted infra diff; applying it is the deploy, and # this script does not apply anything. # # INFRA_DIR overrides where the infra checkout is; the default is the sibling # directory, which is how the repos are laid out. set -euo pipefail cd "$(dirname "$0")/.." # Everything below fails before anything is built. A half-finished deploy is # recoverable - the artifacts are named after the commit and nothing serves # them until the apply - but a wasted build is still a wasted five minutes. : "${AWS_PROFILE:?set AWS_PROFILE; default credentials are almost never the right account}" # The variable being set says nothing about the session behind it. Ask STS # now, so an expired login fails here instead of after both builds, when the # api push is the first thing to touch AWS. if ! aws sts get-caller-identity --query Account --output text >/dev/null; then echo "deploy: no working AWS session for profile $AWS_PROFILE; log in again" >&2 exit 1 fi command -v jq >/dev/null || { echo "deploy: jq is not installed; it writes releases.auto.tfvars.json" >&2 exit 1 } if ! infra="$(cd "${INFRA_DIR:-../infra}" 2>/dev/null && pwd)"; then echo "deploy: no infra checkout at ${INFRA_DIR:-../infra}; set INFRA_DIR" >&2 exit 1 fi tfvars="$infra/envs/lance.blue/releases.auto.tfvars.json" [ -f "$tfvars" ] || { echo "deploy: $infra has no envs/lance.blue/releases.auto.tfvars.json" >&2 exit 1 } # A dirty tree gets a -dirty ref, which names a build nobody else can rebuild. if [ -n "$(git status --porcelain)" ]; then echo "deploy: headquarters has uncommitted changes; commit or stash them" >&2 exit 1 fi # Deploying off a branch is fine: the branch name is part of the release id, so # what is serving is legible from the id alone. main is the one branch with a # rule, because a main build is meant to be exactly what the remote has. Ask # the remote rather than trusting whatever the last fetch left behind. branch=$(git rev-parse --abbrev-ref HEAD) if [ "$branch" = main ]; then git fetch --quiet origin main if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then echo "deploy: on main, but HEAD is not origin/main; pull or push first" >&2 exit 1 fi fi # Nothing here cares whether infra is dirty. headquarters and arena each write # their own keys in the same file, so the second one to deploy would always # find the first one's bump sitting there uncommitted. infra's own deploy.sh is # where a clean tree is required. BUILD_REF="$(scripts/build-ref.sh)" export BUILD_REF echo "deploy: $BUILD_REF -> $AWS_PROFILE" echo # The push scripts build again from cache, so this is mostly a fail-fast: a # broken build stops here rather than after the api image is in ECR. services/api/build.sh web/scripts/build.sh # What the tfvars file gets is read back out of the pushes rather than assumed, # so a change to how either one names a build fails here instead of pointing # production at a path that does not exist. logs="$(mktemp -d)" trap 'rm -rf "$logs" "$tfvars.new"' EXIT services/api/push.sh 2>&1 | tee "$logs/api" web/scripts/push.sh 2>&1 | tee "$logs/web" # "Pushed /headquarters-api:" image="$(grep -E '^Pushed [^ ]+:' "$logs/api" | tail -n 1 || true)" api_ref="${image##*:}" # "Pushed to s3:///releases/" prefix="$(grep -E '^Pushed to s3://' "$logs/web" | tail -n 1 || true)" site_ref="${prefix##*/}" for ref in "$api_ref" "$site_ref"; do if [ "$ref" != "$BUILD_REF" ]; then echo >&2 echo "deploy: pushed $api_ref and $site_ref, expected $BUILD_REF" >&2 echo "deploy: $tfvars not touched" >&2 exit 1 fi done # Written beside the file it replaces so the move is atomic; a jq that fails # halfway leaves the current release id in place. jq --arg ref "$BUILD_REF" '.releases.api = $ref | .releases.site = $ref' \ "$tfvars" >"$tfvars.new" mv "$tfvars.new" "$tfvars" echo echo "Wrote releases.api and releases.site = $BUILD_REF" echo "To serve it:" echo " tofu -chdir=$infra/envs/lance.blue plan -out=lance.blue.tfplan" echo " tofu -chdir=$infra/envs/lance.blue apply lance.blue.tfplan"