Skip to main content

max / makenotwork

4.8 KB · 109 lines History Blame Raw
1 #!/usr/bin/env bash
2 # Reset testnot.work to the fabricated example catalog.
3 #
4 # Reseeds from the in-repo `--seed-examples` flow: a self-contained catalog of
5 # fabricated `@example.test` creators and public-domain items. testnot holds no
6 # prod-derived data at all. This replaced a prod-restore refresh that reloaded
7 # the database from a production backup, putting real user data on the staging
8 # box; that path was deleted on 2026-07-22.
9 #
10 # Runs on fw13 (the Sando host, which has tailnet root on testnot via Tailscale
11 # SSH). It stops the app, resets the schema as the postgres superuser (streamed
12 # over Tailscale SSH), then runs the app binary once with `--seed-examples`,
13 # which applies migrations to the empty schema and runs the guarded seed before
14 # exiting. The seed's own guards (ALLOW_EXAMPLE_SEED, testnot-host allowlist,
15 # no-real-users) make it refuse anywhere but a testnot/localhost box.
16 #
17 # Idempotent and safe to re-run: the seed is fixed, and every run rebuilds the
18 # same catalog. Unlike the refresh, there is no pause-flag — the catalog is
19 # stable by construction, so a soak needs no protection from a daily wipe.
20 #
21 # Media (previews/downloads) attaches in-process only when object storage is
22 # configured in the env file (S3_*). Until MinIO is stood up on testnot, items
23 # seed hidden (scan_status stays pending) and the catalog shows creators,
24 # projects, blog posts, and follow counts without media.
25 #
26 # The seed's harness phase (login-capable accounts plus the OAuth client for the
27 # write-enabled mt on astra) runs only when MT_HARNESS_PASSWORD and
28 # MT_HARNESS_REDIRECT_URI are both set. They are read from $ENV_FILE, which the
29 # systemd-run below already passes through, so nothing here needs to know them.
30 # A box without them reseeds the catalog alone and the mt browser harness loses
31 # its login — see multithreaded/deploy/README.md.
32 set -euo pipefail
33
34 SSH_TARGET="${TESTNOT_SSH:-root@testnot}"
35 DB="${TESTNOT_DB:-makenotwork}"
36 SERVICE="makenotwork.service"
37 BIN="${TESTNOT_BIN:-/opt/mnw/current/makenotwork}"
38 BIN_DIR="$(dirname "$BIN")"
39 BIN_NAME="$(basename "$BIN")"
40 ENV_FILE="${TESTNOT_ENV:-/etc/mnw/makenotwork.env}"
41 SEED_USER="${TESTNOT_SEED_USER:-deploy}"
42
43 log() { echo "[$(date -u +%H:%M:%S)] $*"; }
44 ts_ssh() { tailscale ssh "$SSH_TARGET" "$@"; }
45
46 log "stopping $SERVICE on testnot"
47 ts_ssh "systemctl stop $SERVICE"
48
49 # Drop every non-system schema (migrations create custom schemas like
50 # tower_sessions that survive DROP SCHEMA public CASCADE). Recreate public OWNED
51 # BY the app role: on PG15+ a postgres-owned public grants no CREATE to other
52 # roles, so boot migrations would fail with "no schema has been selected to
53 # create in". This is the same reset the prod-refresh used, minus the restore.
54 log "resetting schema"
55 ts_ssh "sudo -u postgres psql -v ON_ERROR_STOP=1 -d $DB" <<SQL
56 DO \$\$
57 DECLARE s text;
58 BEGIN
59 FOR s IN
60 SELECT nspname FROM pg_namespace
61 WHERE nspname NOT LIKE 'pg_%' AND nspname <> 'information_schema'
62 LOOP
63 EXECUTE format('DROP SCHEMA IF EXISTS %I CASCADE', s);
64 END LOOP;
65 EXECUTE 'CREATE SCHEMA public AUTHORIZATION $DB';
66 END \$\$;
67 SQL
68
69 # Run the binary as the app user with the seed opt-in flag set.
70 # `--seed-examples` applies migrations to the empty schema, runs the guarded
71 # seed, and exits(0) before the server binds a port — so this is safe to run
72 # while the service is stopped. Use systemd-run with the service's own
73 # EnvironmentFile rather than shell-sourcing it: systemd parses `KEY=value`
74 # literally, so secrets containing shell metacharacters (e.g. the DB password)
75 # load correctly, whereas `. env` would choke on them.
76 log "migrating + seeding the example catalog"
77 ts_ssh "systemd-run --pipe --wait --collect --service-type=exec \
78 -p EnvironmentFile=$ENV_FILE -p WorkingDirectory=$BIN_DIR -p User=$SEED_USER \
79 -E ALLOW_EXAMPLE_SEED=1 $BIN --seed-examples"
80
81 log "starting $SERVICE"
82 ts_ssh "systemctl start $SERVICE"
83
84 # Boot smoke: the app must come back healthy after the reseed.
85 healthy=0
86 for _ in $(seq 1 20); do
87 code=$(ts_ssh "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/health" || echo 000)
88 [ "$code" = "200" ] && { log "health OK"; healthy=1; break; }
89 sleep 3
90 done
91 if [ "$healthy" -ne 1 ]; then
92 echo "testnot did not return healthy after reseed" >&2
93 exit 1
94 fi
95
96 # Content smoke: healthy is not the same as worth showing. A reseed that leaves
97 # the catalog empty returns 200 on every page, which is exactly how nine of
98 # eleven items stayed invisible for weeks. Run the thinness check here so the
99 # reseed and its verification cannot drift apart.
100 #
101 # Set SKIP_CONTENT_SMOKE=1 to reseed without it, e.g. when the catalog is
102 # deliberately mid-change and the baselines have not been re-sealed yet.
103 if [ "${SKIP_CONTENT_SMOKE:-0}" = "1" ]; then
104 log "content smoke skipped (SKIP_CONTENT_SMOKE=1)"
105 exit 0
106 fi
107 log "running content smoke"
108 exec "$(dirname "$0")/mnw-testnot-smoke.sh"
109