Files
Lucas WintherandClaude Opus 5.5 ec416bdade fix(deploy): archive script defaults to old host and checks for the 2021 build
DEPLOY: set LETSENCRYPT_HOST with VIRTUAL_HOST in step 4, blog redirects
point to /work/writing/<slug>/, drop the archive refresh row, and note
that sources stay stale while a broken commit is at head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:30:47 +01:00

66 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Mirrors the 2021 site (a Create React App build) into archive/2021/ so it can be served
# frozen at old.lucaswinther.info (deploy/compose.old-site.yml). Idempotent: files already
# present are skipped; delete archive/2021 (or pass --force) to refetch. Files that 404 are
# skipped with a warning; any other failure aborts.
# It refuses to write anything unless the page at the base URL is that CRA build, so it can never
# archive the new site over the old one. Before the DNS cutover the 2021 site is still on the apex:
# OLD_SITE_URL=https://lucaswinther.info scripts/archive-old-site.sh
# Usage: [OLD_SITE_URL=…] [ARCHIVE_DEST=…] scripts/archive-old-site.sh [--force]
set -euo pipefail
base=${OLD_SITE_URL:-https://old.lucaswinther.info}
base=${base%/}
dest=${ARCHIVE_DEST:-"$(cd "$(dirname "$0")/.." && pwd)/archive/2021"}
force=0; [ "${1:-}" = "--force" ] && force=1
probe=$(mktemp)
trap 'rm -f "$probe"' EXIT
if ! curl -sS --fail --location --output "$probe" "$base/"; then
echo "error: could not fetch $base/" >&2; exit 1
fi
if ! grep -qE 'webpackJsonpprojects-page|Lucas Winther Portfolio' "$probe"; then
echo "error: $base/ is not the 2021 site (no CRA build markers); refusing to write $dest" >&2
echo " before the cutover use OLD_SITE_URL=https://lucaswinther.info" >&2
exit 1
fi
mkdir -p "$dest"
fetched=0 skipped=0 missing=0
# fetch <url-path> [local-path]: path is as in the URL (already encoded); local-path is decoded.
fetch() {
local urlpath=$1 local_path=${2:-$(bun -e 'process.stdout.write(decodeURIComponent(process.argv[1]))' "$1")}
local out="$dest/${local_path#/}" code
if [ -s "$out" ] && [ "$force" = 0 ]; then skipped=$((skipped + 1)); return 0; fi
mkdir -p "$(dirname "$out")"
code=$(curl -sS --fail --location --output "$out.part" --write-out '%{http_code}' "$base$urlpath" 2>/dev/null) || code=$?
if [ -f "$out.part" ] && [ "$code" = 200 ]; then
mv "$out.part" "$out"; fetched=$((fetched + 1)); echo "fetched $urlpath"
else
rm -f "$out.part"
# curl --fail exits 22 on HTTP >= 400; the 404 case is the expected one.
if [ "$code" = 22 ]; then echo "warning: $urlpath not found (404 or other HTTP error), skipped" >&2; missing=$((missing + 1))
else echo "error: fetching $urlpath failed (curl/http: $code)" >&2; exit 1; fi
fi
}
for f in index.html info.json favicon.ico logo192.png manifest.json; do
[ "$f" = index.html ] && fetch / index.html || fetch "/$f"
done
# Static bundles referenced by index.html, plus assets referenced from those bundles.
refs() { grep -ohE '/static/[A-Za-z0-9_./-]+\.(js|css|woff2?|ttf|svg|png|jpe?g|gif|webp|map)' "$@" 2>/dev/null | sort -u || true; }
for p in $(refs "$dest/index.html"); do fetch "$p"; done
for p in $(refs "$dest"/static/css/*.css "$dest"/static/js/*.js); do fetch "$p"; done
# Project images: /images/<title>.jpg for every entry in info.json.
while IFS= read -r title; do
fetch "/images/$(bun -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$title").jpg"
done < <(bun -e 'for (const p of Object.values(await Bun.file(process.argv[1]).json())) if (p.title) console.log(p.title)' "$dest/info.json")
# Reports linked from the academics field.
while IFS= read -r url; do
fetch "/documents/${url##*/}"
done < <(bun -e 'for (const p of Object.values(await Bun.file(process.argv[1]).json())) if (/^https:\/\/lucaswinther\.info\/documents\//.test(p.academics ?? "")) console.log(p.academics)' "$dest/info.json")
echo "archive: $fetched fetched, $skipped already present, $missing skipped (not found) -> $dest"