Build and deploy / build-and-deploy (push) Failing after 5s
Step 5 ships /practice/ and the six practice-area pages (construction,
technology, energy, insurance, shareholder, cross-border) from one route, and
adds the mechanical §4 gate Pouya ruled for.
check:claims — §4 Forbidden becomes a build error
scripts/check-claims.mjs greps dist/**/*.html for 10 patterns, each carrying
the incident that put it there. It strips <style> and non-JSON-LD <script>
first (a bare sweep for "leading" returned 26 hits, 25 of them
var(--leading-body)), self-tests every pattern against fixtures before
sweeping, and refuses a missing, empty or stale dist/. Wired into /build
Phase 5 and both deploy paths.
Q54 — six conduct undertakings publish, and §4 gains a third class
Conduct undertakings sit apart from credentials and offerings: the gate is
that Pouya said it in terms. The strings live in CONDUCT_UNDERTAKINGS so a
softening is one visible diff. (e) and (f) replace the third-person sentences
already on /arbitration/ rather than joining them.
Q49, Q50 recorded as rulings. §7 records the SES us-east-1 stray identity's
deletion. R11 holds typescript at its current major, with the peer-range
reason recorded.
Three facts corrected, two of them already shipped
- The LAT gloss said mediation "before filing and continuing after filing";
the Tribunal names mediation for "Before you apply" only and its second
sentence is about negotiation. An ellipsis in docs/01 had deleted it.
- "Connection allocation" is not an Ontario term.
- "The 2026 privacy statute" does not exist — Bill C-27 died without royal
assent. Struck from docs/03 rather than corrected in place.
ADR Chambers struck from /arbitration/ and from docs/01 item 3 (Pouya,
2026-08-30): the source establishes what the firm publishes, not that an
outside neutral can be appointed under its rules.
claims-auditor gains a second lens — for every quoted source, whether the
sentence beneath stays inside what the quotation establishes. Four shipped
defects had that shape and none of them is greppable.
CLAUDE.md gains a convention: never truncate the output of a check you intend
to believe. `npm run check | tail -3` returns warnings, hints and a blank line
and drops the errors line; it was reported as passing four times while
astro check was exiting 1 with 10 type errors.
Gates, exit status read directly, not through a pipe:
npm run check exit=0
npm run lint exit=0
npm run build exit=0
npm run check:claims exit=0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
105 lines
4.0 KiB
Bash
Executable File
105 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Local deploy — the fallback while Gitea Actions is unavailable.
|
|
#
|
|
# Gitea Actions needs `[actions] ENABLED = true` in app.ini and a registered
|
|
# act_runner. The instance is jointly administered, so both depend on a second
|
|
# administrator (AGENTS.md Q23). Until that lands, this script is how the site
|
|
# ships.
|
|
#
|
|
# It matches .gitea/workflows/deploy.yml on everything that determines what gets
|
|
# published: the same guard coverage, `npm run check` before the build,
|
|
# `npm run check:claims` after it, the same three sync passes in the same order
|
|
# with the same cache headers, and the same invalidation. Any change to one must
|
|
# be made to the other.
|
|
#
|
|
# Two deliberate differences: it does not run `npm ci` (your node_modules is
|
|
# already installed, and CI starts empty), and it refuses to run as user/pouya,
|
|
# which CI cannot do because CI has no such credential.
|
|
#
|
|
# Required environment (values are in AGENTS.md §7 — deliberately not restated
|
|
# here; §7 is the single source of truth for operational facts):
|
|
#
|
|
# AWS_REGION S3_BUCKET CLOUDFRONT_DISTRIBUTION_ID INTAKE_ENDPOINT
|
|
#
|
|
# Credentials: use the scoped deploy user. AGENTS.md Q22 records that it does
|
|
# NOT yet exist. NEVER run this as user/pouya — see AGENTS.md §10.
|
|
|
|
set -euo pipefail
|
|
|
|
# Same six values the workflow guards. Emptiness only — no value is echoed.
|
|
missing=''
|
|
[ -n "${AWS_REGION:-}" ] || missing="$missing AWS_REGION"
|
|
[ -n "${S3_BUCKET:-}" ] || missing="$missing S3_BUCKET"
|
|
[ -n "${CLOUDFRONT_DISTRIBUTION_ID:-}" ] || missing="$missing CLOUDFRONT_DISTRIBUTION_ID"
|
|
[ -n "${INTAKE_ENDPOINT:-}" ] || missing="$missing INTAKE_ENDPOINT"
|
|
[ -n "${AWS_ACCESS_KEY_ID:-}" ] || missing="$missing AWS_ACCESS_KEY_ID"
|
|
[ -n "${AWS_SECRET_ACCESS_KEY:-}" ] || missing="$missing AWS_SECRET_ACCESS_KEY"
|
|
if [ -n "$missing" ]; then
|
|
echo "Not set:$missing" >&2
|
|
echo >&2
|
|
echo "Values are in AGENTS.md §7. An empty INTAKE_ENDPOINT does not fail the" >&2
|
|
echo "build — it ships a live contact form posting to nothing." >&2
|
|
exit 1
|
|
fi
|
|
|
|
export AWS_DEFAULT_REGION="$AWS_REGION"
|
|
|
|
echo "==> Identity check"
|
|
caller=$(aws sts get-caller-identity --query Arn --output text)
|
|
echo " $caller"
|
|
case "$caller" in
|
|
*:user/pouya)
|
|
echo >&2
|
|
echo "REFUSING: that is the broadly-permissioned personal user." >&2
|
|
echo "AGENTS.md §10 — never use user/pouya to deploy. Use the scoped" >&2
|
|
echo "deploy user (Q22: not yet created)." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "==> Type and template check"
|
|
npm run check
|
|
|
|
echo "==> Build"
|
|
PUBLIC_SITE_URL="https://adr.smlcompany.ca" \
|
|
PUBLIC_INTAKE_ENDPOINT="$INTAKE_ENDPOINT" \
|
|
PUBLIC_BOOKING_URL="${BOOKING_URL:-}" \
|
|
npm run build
|
|
|
|
# AFTER the build and BEFORE anything is uploaded. AGENTS.md §4 Forbidden,
|
|
# enforced mechanically on the output rather than by a reviewer reading it.
|
|
# Pouya's ruling 2026-08-29: "prose in a comment does not govern the writing
|
|
# that follows it." It also refuses to run against a stale or empty dist, so a
|
|
# pass here is a pass on the bytes about to be published.
|
|
echo "==> Claim check"
|
|
npm run check:claims
|
|
|
|
echo "==> Pass 1/3 — hashed assets and fonts (immutable)"
|
|
aws s3 sync ./dist "s3://${S3_BUCKET}" \
|
|
--exclude "*" \
|
|
--include "_astro/*" --include "fonts/*" \
|
|
--cache-control "public, max-age=31536000, immutable" \
|
|
--no-progress
|
|
|
|
echo "==> Pass 2/3 — images"
|
|
aws s3 sync ./dist "s3://${S3_BUCKET}" \
|
|
--exclude "*" \
|
|
--include "*.avif" --include "*.webp" --include "*.jpg" \
|
|
--include "*.png" --include "*.svg" \
|
|
--cache-control "public, max-age=604800" \
|
|
--no-progress
|
|
|
|
echo "==> Pass 3/3 — HTML and the rest (must-revalidate, --delete)"
|
|
aws s3 sync ./dist "s3://${S3_BUCKET}" \
|
|
--exclude "_astro/*" --exclude "fonts/*" \
|
|
--cache-control "public, max-age=0, must-revalidate" \
|
|
--delete --no-progress
|
|
|
|
echo "==> Invalidate CloudFront"
|
|
aws cloudfront create-invalidation \
|
|
--distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" \
|
|
--paths "/*" >/dev/null
|
|
|
|
echo "==> Deployed to https://adr.smlcompany.ca ($(git rev-parse --short HEAD))"
|