From bef1ee914f299ef99dfaa118da53330a14acbc98 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Thu, 3 Sep 2026 15:56:07 -0400 Subject: [PATCH] feat(scripts)!: publish by default, rehearse with --dry-run A script called publish-site that did not publish unless told twice is a trap: the safe spelling was the long one and the short one silently did nothing. Running it publishes now, and --dry-run is the flag that plans instead. --publish is accepted as a no-op rather than an error, since it asks for exactly what already happens. Co-Authored-By: Claude Opus 5 (1M context) Change-Id: I71d5dfc7c18fbb98be5679f6d0c0a93fa88e7f32 --- plan/site.md | 2 +- scripts/publish-site.sh | 30 +++++++++++++++++------------- scripts/test-publish-site.sh | 22 +++++++++++----------- site/README.md | 6 +++--- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/plan/site.md b/plan/site.md index cd7966f5..956ccc12 100644 --- a/plan/site.md +++ b/plan/site.md @@ -5,7 +5,7 @@ status: open crates: [didbot-site-anim, didbot-brand] dependsOn: [] exitCriterion: > - `scripts/publish-site.sh --publish` puts a build behind did.bot that a + `scripts/publish-site.sh` puts a build behind did.bot that a stranger can navigate end to end: marketing pages, every file in docs/, and the crate API reference, with no dead link between them. --- diff --git a/scripts/publish-site.sh b/scripts/publish-site.sh index 8864bfa6..cc3db702 100755 --- a/scripts/publish-site.sh +++ b/scripts/publish-site.sh @@ -24,14 +24,14 @@ # stack's own comments for the zone, the certificate and the viewer-request # function that resolves a directory index. # -# Defaults to a dry run: without --publish nothing is uploaded and no apply is -# started, because a mistake here is a production site. +# Publishes. That is what the name says and what running it does; --dry-run is +# the flag, for the times the plan is what is wanted. # # Usage: -# scripts/publish-site.sh # build + upload plan + tofu plan +# scripts/publish-site.sh # build + upload + apply + invalidate +# scripts/publish-site.sh --dry-run # build + upload plan + tofu plan # scripts/publish-site.sh --build-only # build, skip AWS entirely -# scripts/publish-site.sh --publish # build + upload + apply + invalidate -# scripts/publish-site.sh --publish --skip-build # reuse site/dist/, publish it +# scripts/publish-site.sh --skip-build # reuse site/dist/, publish it # # Anything else is passed straight to `tofu apply` (and to the dry run's `tofu # plan`), which is how an unattended run gets `-auto-approve`. @@ -42,26 +42,30 @@ cd "$(dirname "$0")/.." bucket="did-bot-site" stack="infra/site" -publish=0 +dry_run=0 build_only=0 skip_build=0 tofu_args=() for arg in "$@"; do case "$arg" in - --publish) publish=1 ;; + --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 ;; --*) - echo "usage: $0 [--publish] [--build-only] [--skip-build] [tofu arguments...]" >&2 + echo "usage: $0 [--dry-run] [--build-only] [--skip-build] [tofu arguments...]" >&2 exit 2 ;; *) tofu_args+=("$arg") ;; esac done -if [ "$publish" -eq 1 ] && [ "$build_only" -eq 1 ]; then - echo "publish-site: --publish and --build-only are contradictory" >&2 +if [ "$dry_run" -eq 1 ] && [ "$build_only" -eq 1 ]; then + echo "publish-site: --dry-run and --build-only are contradictory" >&2 exit 2 fi @@ -151,11 +155,11 @@ if [ ! -f site/dist/404.html ]; then exit 1 fi -if [ "$publish" -eq 0 ]; then - echo "=== dry run: nothing is uploaded and nothing is applied (pass --publish) ===" >&2 +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 "publish-site: dry run complete; pass --publish to upload and apply" >&2 + echo "publish-site: dry run complete; run without --dry-run to upload and apply" >&2 exit 0 fi diff --git a/scripts/test-publish-site.sh b/scripts/test-publish-site.sh index 1bc815a4..4665ee3b 100755 --- a/scripts/test-publish-site.sh +++ b/scripts/test-publish-site.sh @@ -105,7 +105,7 @@ fail() { echo "=== a failing credential check is refused, before any upload ===" >&2 : >"$log" -if FAKE_AWS_NO_CREDS=1 scripts/publish-site.sh --skip-build --publish >"$workdir/out" 2>&1; then +if FAKE_AWS_NO_CREDS=1 scripts/publish-site.sh --skip-build >"$workdir/out" 2>&1; then fail "publish should have failed with no credentials" elif ! grep -q "credential check failed" "$workdir/out"; then fail "the refusal did not say the credential check failed" @@ -117,7 +117,7 @@ fi echo "=== a first publish, with no bucket yet, says how to create it ===" >&2 : >"$log" -if FAKE_AWS_NO_BUCKET=1 scripts/publish-site.sh --skip-build --publish >"$workdir/out" 2>&1; then +if FAKE_AWS_NO_BUCKET=1 scripts/publish-site.sh --skip-build >"$workdir/out" 2>&1; then fail "publish should have failed with no bucket to upload to" elif ! grep -q "tofu -chdir=infra/site apply" "$workdir/out"; then fail "the refusal did not say how to create the stack" @@ -130,7 +130,7 @@ fi echo "=== a tree with no 404.html is refused, before any upload ===" >&2 : >"$log" rm site/dist/404.html -if scripts/publish-site.sh --skip-build --publish >"$workdir/out" 2>&1; then +if scripts/publish-site.sh --skip-build >"$workdir/out" 2>&1; then fail "publish should have failed with no 404.html in the tree" elif ! grep -q "404.html" "$workdir/out"; then fail "the refusal did not name 404.html" @@ -141,9 +141,9 @@ if grep -q '^aws s3 sync' "$log"; then fi seed_dist -echo "=== a dry run plans and writes nothing ===" >&2 +echo "=== --dry-run plans and writes nothing ===" >&2 : >"$log" -if ! scripts/publish-site.sh --skip-build >"$workdir/out" 2>&1; then +if ! scripts/publish-site.sh --skip-build --dry-run >"$workdir/out" 2>&1; then fail "the dry run should succeed against the stubs" cat "$workdir/out" >&2 fi @@ -163,10 +163,10 @@ if grep -qE '^tofu -chdir=infra/site apply|create-invalidation' "$log"; then cat "$log" >&2 fi -echo "=== --publish uploads the tree, flips the origin, then invalidates ===" >&2 +echo "=== a plain run uploads the tree, flips the origin, then invalidates ===" >&2 : >"$log" -if ! scripts/publish-site.sh --skip-build --publish >"$workdir/out" 2>&1; then - fail "--publish should succeed against the stubs" +if ! scripts/publish-site.sh --skip-build >"$workdir/out" 2>&1; then + fail "a plain run should succeed against the stubs" cat "$workdir/out" >&2 fi @@ -176,7 +176,7 @@ if [ "$uploads" -ne 2 ]; then cat "$log" >&2 fi if grep -q -- '--dryrun' "$log"; then - fail "--publish rehearsed the upload instead of making it" + fail "a plain run rehearsed the upload instead of making it" fi # The release is the commit, and the whole tree lands under that one prefix: @@ -196,13 +196,13 @@ if ! grep -q "tofu -chdir=infra/site apply -var release_sha=${release}" "$log"; fi if ! grep -q '^aws cloudfront wait distribution-deployed' "$log"; then - fail "--publish did not wait for the distribution to deploy" + fail "the publish did not wait for the distribution to deploy" fi apply_line="$(grep -n 'tofu -chdir=infra/site apply' "$log" | head -1 | cut -d: -f1)" wait_line="$(grep -n 'cloudfront wait distribution-deployed' "$log" | head -1 | cut -d: -f1)" invalidate_line="$(grep -n 'cloudfront create-invalidation' "$log" | head -1 | cut -d: -f1)" if [ -z "$invalidate_line" ]; then - fail "--publish did not invalidate the distribution" + fail "the publish did not invalidate the distribution" elif [ "$apply_line" -gt "$wait_line" ] || [ "$wait_line" -gt "$invalidate_line" ]; then fail "expected apply, then wait, then invalidate; saw $apply_line, $wait_line, $invalidate_line" cat "$log" >&2 diff --git a/site/README.md b/site/README.md index e67dd3bc..dad458cb 100644 --- a/site/README.md +++ b/site/README.md @@ -314,8 +314,8 @@ From the repository root, not from here: ``` scripts/build-site.sh # cargo doc + astro build + mount /api/ + site/tests scripts/build-site.sh --skip-tests -scripts/publish-site.sh # build, then a dry-run publish plan -scripts/publish-site.sh --publish # build, upload a release, point did.bot at it +scripts/publish-site.sh # build, upload a release, point did.bot at it +scripts/publish-site.sh --dry-run # build, then a plan of what that would do ``` See those scripts' own comments for what each step does and assumes. The @@ -462,7 +462,7 @@ alias pair pointing at the distribution, and the `_atproto` TXT record that lets `did.bot` resolve as an ATProto handle. That stack's own comments carry the detail; three things about it shape this project: -- **A publish is a pointer move.** `scripts/publish-site.sh --publish` uploads +- **A publish is a pointer move.** `scripts/publish-site.sh` uploads the build as an immutable tree under `s3://did-bot-site/releases//` and then applies `release_sha`, which flips the distribution's `origin_path` to it, waits for that config to reach every edge, and -- 2.51.2