Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164# Sourced by scripts/publish-site.sh and scripts/publish-policy-site.sh: the# steps both take to put a static build online. Each sets these before# sourcing, then calls the steps in the order they appear here:## name prefix for every message# build the script that builds the tree# dist the tree it builds# bucket the S3 bucket the tree is uploaded to# stack the OpenTofu root that owns the bucket and the distribution# url the address the distribution serves## A publish is two steps and one pointer move:## 1. The build is uploaded as an immutable tree under# s3://<bucket>/releases/<git sha>/. Nothing serves it yet, and the# bucket is private behind the distribution's origin access control, so# an uploaded tree is visible to nobody until step 2.# 2. `tofu apply -var release_sha=<sha>` flips CloudFront's origin_path to# that tree, waits for the distribution to reach every edge, and# invalidates. A reader sees the old tree or the new one, never a# half-written mixture of the two.## Roll back by applying the sha of a tree already uploaded:## tofu -chdir=<stack> apply -var release_sha=<an earlier sha>## State is remote (<stack>/backend.tf), so what is live is recorded there:# `tofu -chdir=<stack> output release_sha` answers it.
publish_usage() { echo "usage: $0 [--dry-run] [--build-only] [--skip-build] [tofu arguments...]" >&2}
# Sets dry_run, build_only, skip_build and tofu_args. Anything not a flag is# passed straight to `tofu apply` (and to the dry run's `tofu plan`), which is# how an unattended run gets `-auto-approve`.publish_parse_args() { dry_run=0 build_only=0 skip_build=0 tofu_args=() local arg for arg in "$@"; do case "$arg" in --dry-run) dry_run=1 ;; # The old spelling of the default. Kept as a no-op rather than an error # so a habit or a saved command line does not fail; it asks for exactly # what happens anyway. --publish) ;; --build-only) build_only=1 ;; --skip-build) skip_build=1 ;; --*) publish_usage exit 2 ;; *) tofu_args+=("$arg") ;; esac done
if [ "$dry_run" -eq 1 ] && [ "$build_only" -eq 1 ]; then echo "${name}: --dry-run and --build-only are contradictory" >&2 exit 2 fi}
# Builds the tree, or reuses it under --skip-build. Under --build-only, stops# here.publish_build() { if [ "$skip_build" -eq 1 ]; then echo "${name}: --skip-build passed; reusing ${dist}/ as-is" >&2 if [ ! -d "$dist" ]; then echo "${name}: ${dist}/ does not exist -- run without --skip-build first" >&2 exit 1 fi else "$build" fi
if [ "$build_only" -eq 1 ]; then echo "${name}: --build-only passed; not touching AWS" >&2 exit 0 fi}
# Fails before uploading anything, not halfway through: a dry run checks the# same credentials the real one needs, so a rehearsal is a real rehearsal.publish_require_credentials() { local tool for tool in aws tofu; do if ! command -v "$tool" >/dev/null 2>&1; then echo "${name}: $tool is required and was not found on PATH" >&2 exit 1 fi done
local identity if ! identity="$(aws sts get-caller-identity --output text --query Arn 2>&1)"; then echo "${name}: AWS credential check failed:" >&2 echo " ${identity}" >&2 echo "${name}: log in to the account this site deploys to (AWS_PROFILE=<your profile>), then re-run." >&2 exit 1 fi echo "${name}: publishing as ${identity}" >&2}
# Sets release and prefix. The release is the commit, so a tree can be traced# back to what built it and rolling back is naming an earlier one. `status`,# not `diff-index`: diff-index trusts the index's cached stat info, so a file# rewritten with identical content reads as modified. --untracked-files=no# keeps the comparison to tracked files.publish_name_release() { release="$(git rev-parse --short=12 HEAD)" if [ -n "$(git status --porcelain --untracked-files=no)" ]; then release="${release}-dirty" echo "${name}: uncommitted changes; publishing as ${release}" >&2 fi prefix="releases/${release}" echo "${name}: release ${release} -> s3://${bucket}/${prefix}/" >&2}
# The bucket is the stack's, so the first publish into a fresh account has to# apply the stack before it has anywhere to upload to. That apply names a# release tree nobody has uploaded yet, which is the ordinary state for a few# minutes, until this script runs again and fills the tree in.publish_require_bucket() { if ! aws s3api head-bucket --bucket "${bucket}" >/dev/null 2>&1; then echo "${name}: s3://${bucket} does not exist yet (first publish?)." >&2 echo "${name}: run \`tofu -chdir=${stack} init -backend-config=backend.hcl\` and" >&2 echo "${name}: \`tofu -chdir=${stack} apply -var release_sha=${release}\`" >&2 echo "${name}: to create the infrastructure, then re-run this script." >&2 exit 1 fi}
# Uploads with `sync_tree`, which the calling script defines and which passes# its arguments on to every `aws s3 sync` it runs. Then flips the origin,# waits, and invalidates. Under --dry-run, rehearses the upload and plans.publish_release() { if [ "$dry_run" -eq 1 ]; then echo "=== dry run: nothing is uploaded and nothing is applied ===" >&2 sync_tree --dryrun tofu -chdir="${stack}" plan -var "release_sha=${release}" ${tofu_args+"${tofu_args[@]}"} echo "${name}: dry run complete; run without --dry-run to upload and apply" >&2 exit 0 fi
sync_tree echo "${name}: uploaded release to s3://${bucket}/${prefix}/" >&2
tofu -chdir="${stack}" apply -var "release_sha=${release}" ${tofu_args+"${tofu_args[@]}"}
# The apply only rewrites the distribution's config. Edge caches still hold # the previous tree until the new config has reached them, so wait, then # invalidate. An invalidation issued first would re-cache the old tree. local dist_id invalidation dist_id="$(tofu -chdir="${stack}" output -raw distribution_id)" echo "${name}: waiting for CloudFront distribution ${dist_id} to deploy" >&2 aws cloudfront wait distribution-deployed --id "${dist_id}" invalidation="$(aws cloudfront create-invalidation --distribution-id "${dist_id}" \ --paths "/*" --output text --query Invalidation.Id)" echo "${name}: created CloudFront invalidation ${invalidation}" >&2 echo "${name}: ${url} is serving ${release}" >&2}