Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
153 lines
No EOL
6.2 KiB
Bash
Executable file
153 lines
No EOL
6.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Pilot check: go-loose consuming the shared contract.
|
|
#
|
|
# This is the test that decides whether the consolidation is real. It loads go-loose's
|
|
# *actual* index.html and its *actual* hand-written style.css, with only the theme
|
|
# origin substituted exactly as go-loose's server does it, and then asks the question
|
|
# that matters:
|
|
#
|
|
# Do the components shipped by go-bananas change the attributes that go-loose's own
|
|
# CSS already keys on, so go-loose is themed without a single edit to its stylesheet?
|
|
#
|
|
# If that is true, adoption is one <link> and one <script> per page, which is what
|
|
# ADR 0005 claims. If it is false, the contract is decoration.
|
|
#
|
|
# It does not boot go-loose: that needs a database and a first-run installer, and
|
|
# neither is what is being tested. What is being tested is the page and the stylesheet.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
LOOSE="${GO_LOOSE_DIR:-/home/dnoulet/go/go-loose}"
|
|
WORK="$(mktemp -d)"
|
|
PORT="${PORT:-8398}"
|
|
THEME_PORT="${THEME_PORT:-8397}"
|
|
CHROME="${CHROME:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || true)}"
|
|
|
|
cleanup() {
|
|
for pid in "${SERVER_PID:-}" "${THEME_PID:-}"; do
|
|
[[ -n "$pid" ]] && kill "$pid" 2>/dev/null || true
|
|
done
|
|
echo "WORK=$WORK" >/tmp/pilot-work-dir; rm -rf "$WORK"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
[[ -d "$LOOSE/web" ]] || { echo "go-loose not found at $LOOSE; set GO_LOOSE_DIR" >&2; exit 1; }
|
|
[[ -n "$CHROME" ]] || { echo "no chromium found; set CHROME" >&2; exit 1; }
|
|
|
|
echo "==> building go-bananas and the SDK"
|
|
# The SDK is built first, then the server that embeds it. The other order compiles a
|
|
# binary containing whatever sdk/dist happened to contain when the pilot last ran,
|
|
# which 404s /rt/v1/components.js — and the check used not to notice, because it loaded
|
|
# the module from its own copy instead of from the service.
|
|
(cd "$ROOT/sdk" && npm run build >/dev/null)
|
|
(cd "$ROOT" && go build -o "$WORK/go-bananas" ./cmd/go-bananas)
|
|
|
|
mkdir -p "$WORK/site" "$WORK/site/assets"
|
|
|
|
# go-loose's own files with the theme origin substituted the way its server does it.
|
|
# This is the only transformation: nothing in its stylesheet or markup is edited.
|
|
echo "==> assembling go-loose's page"
|
|
THEME_ORIGIN="http://localhost:$THEME_PORT"
|
|
export LOOSE_WEB="$LOOSE/web" SITE="$WORK/site" THEME_ORIGIN
|
|
python3 - <<'PY'
|
|
import os, re
|
|
|
|
src = os.environ['LOOSE_WEB']
|
|
out = os.environ['SITE']
|
|
origin = os.environ['THEME_ORIGIN']
|
|
|
|
html = open(os.path.join(src, 'index.html')).read().replace('__GO_LOOSE_THEME_ORIGIN__', origin)
|
|
|
|
# Nothing is rewritten to a local copy any more. The module used to be pointed at a
|
|
# copy under /assets/ so that the check would not depend on CORS being configured, and
|
|
# that is exactly what hid the bug this now covers: the service sent no CORS headers,
|
|
# so in a real deployment the ES module could not be fetched and the control rendered
|
|
# empty, while the pilot passed against its own copy. A module <script> is always
|
|
# fetched under CORS rules, so the only way to test the deployed path is to load it
|
|
# from the service.
|
|
|
|
# The reporter is appended to the *copy*. go-loose's source is never edited.
|
|
if 'id="pilot"' not in html:
|
|
html = html.replace('</body>', ' <script type="module" src="/pilot.js"></script>\n</body>')
|
|
|
|
open(os.path.join(out, 'index.html'), 'w').write(html)
|
|
|
|
# go-loose serves its own files under /assets/, so they belong in site/assets/ rather
|
|
# than at the site root. Getting this wrong 404s every one of them while leaving the
|
|
# page looking merely unstyled.
|
|
assets = os.path.join(out, 'assets')
|
|
os.makedirs(assets, exist_ok=True)
|
|
for name in ('style.css', 'app.js', 'account.js', 'roles.js', 'profile.js'):
|
|
p = os.path.join(src, name)
|
|
if os.path.exists(p):
|
|
open(os.path.join(assets, name), 'w').write(open(p).read())
|
|
PY
|
|
sed "s#from '/rt/v1/components.js'#from '$THEME_ORIGIN/rt/v1/components.js'#" \
|
|
"$ROOT/hack/pilot-go-loose.js" > "$WORK/site/pilot.js"
|
|
|
|
echo "==> starting the theme service on :$THEME_PORT"
|
|
# The selector fetches contract.json, and this page is on a different origin from the
|
|
# service. A cross-origin module fetch and a JSON fetch both need the consuming
|
|
# origin in CORS_ALLOWED_ORIGINS; a same-origin deployment needs nothing here. That is
|
|
# the one setting adopting the contract requires, and setting it here is what proves it
|
|
# is sufficient.
|
|
DATABASE_PATH="$WORK/pilot.db" \
|
|
BANANAS_ENVIRONMENT=development \
|
|
BANANAS_AUTH_MODE=dev \
|
|
BANANAS_PUBLIC_BASE_URL="$THEME_ORIGIN" \
|
|
BANANAS_HTTP_ADDR=":$THEME_PORT" \
|
|
BANANAS_SDK_DIR="$WORK/site/assets" \
|
|
BANANAS_CORS_ALLOWED_ORIGINS="http://localhost:$PORT" \
|
|
"$WORK/go-bananas" >"$WORK/theme.log" 2>&1 &
|
|
THEME_PID=$!
|
|
|
|
for _ in $(seq 1 40); do
|
|
curl -fsS -m 2 "http://localhost:$THEME_PORT/healthz" >/dev/null 2>&1 && break
|
|
sleep 0.25
|
|
done
|
|
|
|
echo "==> serving go-loose's page on :$PORT"
|
|
(cd "$WORK/site" && exec python3 -m http.server "$PORT") >"$WORK/site.log" 2>&1 &
|
|
SERVER_PID=$!
|
|
for _ in $(seq 1 40); do
|
|
curl -fsS -m 2 "http://localhost:$PORT/index.html" >/dev/null 2>&1 && break
|
|
sleep 0.25
|
|
done
|
|
|
|
echo "==> driving the page"
|
|
"$CHROME" \
|
|
--headless --no-sandbox --disable-gpu \
|
|
--virtual-time-budget=12000 \
|
|
--dump-dom "http://localhost:$PORT/index.html" \
|
|
>"$WORK/dom.html" 2>"$WORK/chrome.log"
|
|
|
|
result="$(python3 - "$WORK/dom.html" <<'PY'
|
|
import re, sys
|
|
dom = open(sys.argv[1]).read()
|
|
m = re.search(r'id="pilot"[^>]*>(.*?)</pre>', dom, re.S)
|
|
print(m.group(1).strip() if m else "")
|
|
PY
|
|
)"
|
|
|
|
if [[ -z "$result" ]]; then
|
|
echo "PILOT FAIL: the page recorded nothing, so the check did not run." >&2
|
|
echo "the reporter most likely threw; diagnostics follow." >&2
|
|
echo "--- browser console ---" >&2
|
|
grep -iE 'CONSOLE.*(error|Uncaught|violates|refused)' "$WORK/chrome.log" | head -5 >&2 || true
|
|
echo "--- dom size / page head ---" >&2
|
|
wc -c <"$WORK/dom.html" >&2 || true
|
|
head -c 300 "$WORK/dom.html" >&2 || true
|
|
echo >&2
|
|
echo "--- static server 404s ---" >&2
|
|
grep -oE 'GET /[^"]*" 404' "$WORK/site.log" | head -5 >&2 || true
|
|
echo "--- pilot element ---" >&2
|
|
grep -o 'id="pilot"[^<]*' "$WORK/dom.html" | head -2 >&2 || true
|
|
echo "--- theme service log (last 5) ---" >&2
|
|
tail -5 "$WORK/theme.log" >&2 || true
|
|
fi
|
|
|
|
echo
|
|
echo "$result"
|
|
[[ "$result" == PILOT\ PASS* ]] |