oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1#!/usr/bin/env bash
2# Drive the real editor in a real browser: build the app, run it on a
3# scratch database, sign in for real, and put a headless Chrome through
4# fixtures/browser-drive.mjs. Exits non-zero if any check fails.
5#
6# Not in .amadan/ci.d/10-gate yet, deliberately, and the reason is
7# bigger than "the runner needs a Chrome installed".
8#
9# Chromium does not start inside the agent sandbox as it is configured
10# by default — it fails early on something it cannot write or bind.
11# The exact symptom moves: it has been "socket() failed: Operation not
12# permitted" and it has been "Failed to create a unique user data
13# directory for headless". Neither is quoted here as the diagnosis,
14# because quoting one symptom as though it were the reason is part of
15# what made the old version of this paragraph read as final.
16#
17# BUT IT RUNS FINE WITH THE SANDBOX OFF, and this header said otherwise
18# for long enough to do damage. It read "cannot start at all", full
19# stop — and a session that believed it merged a branch describing
20# itself as impossible to browser-verify, having never tried. A real
21# WCAG 2.2 SC 1.4.10 failure and four blinded checks went to main behind
22# that sentence. The sandbox is a setting, not a property of the
23# machine; a session that needs a browser turns it off for this one
24# command and gets one.
25#
26# So the open question for the gate is smaller than this file used to
27# claim. Not "can this run here" — it can — but "may a CI step run
28# unsandboxed", which is a real decision about what CI is allowed to do
29# and belongs to whoever owns the runner rather than to the branch that
30# wrote the script.
31#
32# Until that is answered it is run by hand, which is worth far more than
33# not having it: its first run caught an editor that silently discarded
34# every edit, and the run after this file's own header was corrected
35# caught five regressions a green gate had passed.
36#
37# $DOCS_CHROME overrides which browser to use.
38set -euo pipefail
39cd "$(dirname "$0")/.."
40
41chrome="${DOCS_CHROME:-}"
42if [ -z "$chrome" ]; then
43 for candidate in chromium chromium-browser google-chrome google-chrome-stable; do
44 if command -v "$candidate" >/dev/null 2>&1; then chrome="$candidate"; break; fi
45 done
46fi
47[ -n "$chrome" ] || { echo "browser-drive: no Chrome found; set \$DOCS_CHROME" >&2; exit 2; }
48command -v node >/dev/null || { echo "browser-drive: node not on PATH" >&2; exit 2; }
49
50port="${DOCS_DRIVE_PORT:-8188}"
51debug_port="${DOCS_DRIVE_DEBUG_PORT:-9288}"
52addr="127.0.0.1:${port}"
53scratch="$(mktemp -d "${TMPDIR:-/tmp}/docs-drive.XXXXXX")"
54
55server_pid=""
56chrome_pid=""
57cleanup() {
58 [ -n "$chrome_pid" ] && kill "$chrome_pid" 2>/dev/null || true
59 [ -n "$server_pid" ] && kill "$server_pid" 2>/dev/null || true
60 rm -rf "$scratch" 2>/dev/null || true
61}
62trap cleanup EXIT
63
64export CGO_ENABLED=0
65echo "browser-drive: building"
66go build -o "${scratch}/docs" ./cmd/docs
67
68# -seed so there is a document to open; the same development-only path
69# scripts/run-dev.sh uses, against a database that exists for this run
70# only.
71"${scratch}/docs" -env development -seed -addr "$addr" -db "${scratch}/docs.db" > "${scratch}/server.log" 2>&1 &
72server_pid=$!
73
74for _ in $(seq 1 40); do
75 if curl -fsS -o /dev/null "http://${addr}/signin" 2>/dev/null; then break; fi
76 sleep 0.25
77done
78curl -fsS -o /dev/null "http://${addr}/signin" || { echo "browser-drive: server did not start" >&2; cat "${scratch}/server.log" >&2; exit 1; }
79
80# Sign in the way a person does. In development the magic link is
81# logged instead of mailed (internal/docs/auth.go), so the link is read
82# back out of this run's own log — no stand-in identity, no test-only
83# door into the app.
84curl -fsS -X POST -H 'Sec-Fetch-Site: same-origin' \
85 --data-urlencode 'address=dev@example.com' -o /dev/null "http://${addr}/signin"
86link="$(grep -o "http://${addr}/auth/verify?token=[A-Za-z0-9_-]*" "${scratch}/server.log" | tail -1)"
87[ -n "$link" ] || { echo "browser-drive: no sign-in link in the log" >&2; exit 1; }
88
89# The link opens a confirm door that spends nothing; the drive presses
90# its Sign in button (browser-drive.mjs), which is what leaves the
91# session cookie in the profile it then uses.
92
93"$chrome" --headless=new --no-sandbox --disable-gpu --disable-crash-reporter \
94 --remote-debugging-port="${debug_port}" --user-data-dir="${scratch}/profile" \
95 --window-size=1280,900 \
96 about:blank > "${scratch}/chrome.log" 2>&1 &
97chrome_pid=$!
98
99target=""
100for _ in $(seq 1 40); do
101 target="$(curl -fsS "http://127.0.0.1:${debug_port}/json/list" 2>/dev/null \
102 | tr ',' '\n' | grep -o 'ws://[^"]*devtools/page/[A-Za-z0-9]*' | head -1 || true)"
103 [ -n "$target" ] && break
104 sleep 0.25
105done
106[ -n "$target" ] || { echo "browser-drive: Chrome did not expose a debugging target" >&2; exit 1; }
107
108echo "browser-drive: driving http://${addr}"
109node fixtures/browser-drive.mjs "$target" "http://${addr}" "$link"
110