ci(e2e): narrow post-migration service_role grants to match schema.sql

schema.sql now grants service_role its own narrowed privileges
(upstream "narrow service role schema grants"), so the broad GRANT ALL
workaround is obsolete — and worse, it widened the test database beyond
what prod gets, letting e2e pass on privileges a real deployment lacks.
Keep a post-migration re-grant (schema.sql's ON ALL statements can't
cover tables the migrations create) but with the identical narrowed set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
willchen96 2026-07-26 00:00:30 +08:00
parent c196f9b20e
commit f3173a9dba
3 changed files with 22 additions and 21 deletions

View file

@ -129,18 +129,17 @@ jobs:
psql "$DB_URL" -v ON_ERROR_STOP=1 -f "$m" >/dev/null 2>&1 \
|| echo "::warning::migration returned a non-zero status (already applied?): $m"
done
# 3) schema.sql revokes client grants (anon/authenticated) on purpose, but
# assumes a HOSTED Supabase where service_role already holds full table
# access. On a fresh CLI stack loaded via psql, service_role gets no grants
# on the new public tables, so the backend — which queries exclusively as
# service_role (lib/supabase.ts) — 500s with "permission denied for table
# user_profiles" on the first write. Grant AFTER migrations so the tables
# they add are covered too.
# 3) schema.sql now grants service_role its narrowed data privileges
# itself, but those GRANT ... ON ALL statements only cover tables that
# exist when schema.sql runs — tables created by the migrations above
# are not covered, and the backend (which queries exclusively as
# service_role, lib/supabase.ts) 500s with "permission denied" on them.
# Re-grant AFTER migrations with the SAME privilege set as schema.sql —
# deliberately not ALL, so e2e cannot pass on a privilege prod lacks.
psql "$DB_URL" -v ON_ERROR_STOP=1 <<'SQL'
GRANT USAGE ON SCHEMA public TO service_role;
GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO service_role;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO service_role;
SQL
# 4) PostgREST cached its schema when `supabase start` booted it (before
# the DDL above). Tell it to reload so the new tables/grants are visible.

View file

@ -17,10 +17,11 @@ and on manual `workflow_dispatch`, the `e2e / playwright` job:
`backend/schema.sql`, then applies every dated migration in `backend/migrations/`
on top. `schema.sql` is meant to be the latest shape but in practice lags the
migrations (e.g. it is missing `workflow_open_source_submissions`, which
`GET /workflows/:id` queries — a 500 without the migrations). It also grants
`service_role` full access to the `public` tables afterward, because
`schema.sql` revokes client grants assuming a hosted Supabase where
`service_role` is already privileged;
`GET /workflows/:id` queries — a 500 without the migrations). After the
migrations it re-grants `service_role` the same narrowed data privileges
`schema.sql` grants (`SELECT/INSERT/UPDATE/DELETE` on tables, `USAGE/SELECT`
on sequences): the schema's own `GRANT ... ON ALL` statements only cover
tables that existed when the schema loaded, not ones the migrations create;
4. writes `backend/.env` and `frontend/.env.local` from the live Supabase values;
5. **builds** the web app (`next build`) and serves it with `next start` — a
production build, not `next dev`, so there is no on-demand compilation (which

View file

@ -2,10 +2,10 @@
# Boot the full local e2e stack and run the Playwright suite.
#
# Local-machine mirror of .github/workflows/e2e.yml: starts the Supabase CLI
# stack (Docker), loads backend/schema.sql + backend/migrations/ + the
# service_role grants (schema.sql assumes a hosted project where service_role
# is already privileged — see docs/e2e-ci.md), points backend/.env and
# frontend/.env.local at the local stack, then runs `npx playwright test`.
# stack (Docker), loads backend/schema.sql + backend/migrations/ (re-granting
# service_role's narrowed privileges afterwards for migration-created tables —
# see docs/e2e-ci.md), points backend/.env and frontend/.env.local at the
# local stack, then runs `npx playwright test`.
#
# Usage, from the repo root:
# npm run test:e2e:local # whole suite
@ -62,11 +62,12 @@ for m in migrations/*.sql; do
psql "$DB_URL" -q -f "$m" >/dev/null 2>&1 ||
echo "warning: migration returned non-zero (already applied?): $m"
done
# schema.sql grants these itself, but only for tables that existed when it
# ran — re-grant after migrations, with the same narrowed set (not ALL).
psql "$DB_URL" -v ON_ERROR_STOP=1 -q <<'SQL'
GRANT USAGE ON SCHEMA public TO service_role;
GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO service_role;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO service_role;
NOTIFY pgrst, 'reload schema';
SQL