shoc-pr-review-runner/scripts/run-frontend-gates.sh
Adam Moussa c3cd8f7765
feat: SHOC PR review runner, phase 1
Manually-dispatched GitHub Actions workflow that reviews SHOC pull requests in
a clean environment: exact-head checkout of shoc-frontend-new and shoc-backend,
clean build/test gates, a truthful evidence report, a single-shot Fireworks
review, deterministic output validation, and published artifacts. The runner
never writes to the product repositories or their pull requests.

The review checklists move here from the reviewers' local Cursor commands so
the instructions live outside both product repos.

Phase 1 does not provision a database, start either application, or run live
browser flows; the evidence report records those as NOT_RUN so a review cannot
claim them.

Security architecture: building a PR executes its author's code, so the
workflow is split. The gates job runs that code holding no Fireworks key and
revokes its App token first; the review job holds the key, executes no product
code, and re-checks out this repo fresh. Product checkouts live outside the
workspace, the App token is downscoped at mint time, gate results fail closed
on any duplicate key, changed files are read from git objects rather than the
filesystem, and the validator re-checks every claim against the gate table.
2026-07-29 12:05:38 -04:00

70 lines
2.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# Frontend clean install + static gates + mocked Playwright (spec §14, Phase 1).
#
# Gates: npm ci (HUSKY=0) -> lint, unit tests, production build (tsc -b inside),
# mocked Playwright e2e. lint/test are independent of build; e2e needs build
# tooling installed but drives its own dev server (playwright.config webServer).
#
# The repo's `verify`/governance script is deliberately NOT run here: it fails
# closed without full git history, and the PR's own CI already runs it — that
# result is ingested into evidence via resolve-pr-head.sh.
#
# Reads: FRONTEND_DIR (default $WORKSPACE_DIR/frontend),
# RUN_MOCKED_E2E (default true)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
source "$SCRIPT_DIR/lib.sh"
FRONTEND_DIR="${FRONTEND_DIR:-$WORKSPACE_DIR/frontend}"
RUN_MOCKED_E2E="${RUN_MOCKED_E2E:-true}"
cd "$FRONTEND_DIR"
[ -f package.json ] || die "package.json not found in $FRONTEND_DIR"
export HUSKY=0
overall=0
if ! run_gate frontend.install frontend-install.log npm ci; then
record_gate frontend.lint BLOCKED "install failed"
record_gate frontend.unit_tests BLOCKED "install failed"
record_gate frontend.build BLOCKED "install failed"
record_gate frontend.e2e_mocked BLOCKED "install failed"
exit 1
fi
run_gate frontend.lint frontend-lint.log npm run lint || overall=1
run_gate frontend.unit_tests frontend-unit.log npm test || overall=1
# Production build includes TypeScript compilation (tsc -b). VITE_API_URL must
# end in /api or the build's contract guard throws by design; use the relative
# default so the committed .env.production absolute URL is not baked in.
if run_gate frontend.build frontend-build.log env VITE_API_URL="/api" npm run build; then
build_ok=1
else
build_ok=0
overall=1
fi
if [ "$RUN_MOCKED_E2E" != "true" ]; then
record_gate frontend.e2e_mocked NOT_RUN "disabled by run_mocked_e2e input"
elif ! jq -e '.scripts["test:e2e"]' package.json >/dev/null 2>&1; then
record_gate frontend.e2e_mocked NOT_RUN "no test:e2e script at this head"
elif [ "$build_ok" -ne 1 ]; then
record_gate frontend.e2e_mocked BLOCKED "production build failed"
overall=1
else
if run_gate frontend.e2e_browsers frontend-e2e-install.log \
npx playwright install --with-deps chromium; then
# Mocked suite: Playwright's webServer starts the dev server itself; all
# backend calls in the suite are page.route-fulfilled. This is NOT live
# integration coverage and evidence records it as mocked only.
run_gate frontend.e2e_mocked frontend-e2e.log npm run test:e2e || overall=1
else
record_gate frontend.e2e_mocked BLOCKED "playwright browser install failed"
overall=1
fi
fi
exit "$overall"