'use strict'; /** * Regenerate supabase/schema/model_snapshots.columns.json from the MIGRATION * CHAIN, by applying every committed migration to a disposable postgres and * reading information_schema. * * The contract must be DERIVED, never hand-maintained: a hand-written list is * a second opinion about the schema, and a second opinion is exactly what let * `published_side` reach production. Deriving it from the same migrations the * release verification applies means the list cannot drift from the release. * * docker run -d --name schema-gen -e POSTGRES_PASSWORD=test -e POSTGRES_DB=v postgres:15-alpine * node scripts/generate-schema-contract.js schema-gen * * Drift against a LIVE database is a separate question — see * scripts/verify-schema-contract.js. */ const { execFileSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const CONTAINER = process.argv[2]; const TABLE = process.env.SCHEMA_TABLE || 'model_snapshots'; const OUT = path.join(__dirname, '..', 'supabase', 'schema', `${TABLE}.columns.json`); function psql(sql) { return execFileSync('docker', ['exec', CONTAINER, 'psql', '-U', 'postgres', '-d', 'v', '-tAc', sql], { encoding: 'utf8' }).trim(); } function main() { if (!CONTAINER) throw new Error('usage: node scripts/generate-schema-contract.js '); const cols = psql( `select string_agg(column_name, ',' order by column_name)` + ` from information_schema.columns` + ` where table_schema='public' and table_name='${TABLE}'` + ` and is_generated='NEVER' and identity_generation is null;`, ).split(',').filter(Boolean); if (cols.length === 0) throw new Error(`no columns found for ${TABLE} — was the migration chain applied?`); const prev = fs.existsSync(OUT) ? JSON.parse(fs.readFileSync(OUT, 'utf8')) : {}; const doc = { ...prev, table: TABLE, generated_by: 'scripts/generate-schema-contract.js', derived_from: 'supabase/migrations/*.sql applied in order to a disposable postgres:15-alpine', column_count: cols.length, columns: cols, }; fs.writeFileSync(OUT, `${JSON.stringify(doc, null, 2)}\n`); console.log(`wrote ${OUT} — ${cols.length} columns`); } if (require.main === module) main();