Merge pull request 'dev → stage: wire Supabase migrations into CI/CD' (#94) from dev into stage

Merge PR #94 from dev into stage
This commit was merged in pull request #94.
This commit is contained in:
2026-05-11 18:22:17 +00:00
2 changed files with 122 additions and 0 deletions
+61
View File
@@ -10,6 +10,7 @@ on:
- 'src/**'
- 'dist/**'
- 'runtime/**'
- 'migrations/**'
- 'Dockerfile.stage'
- 'build-stage.sh'
- '.gitea/workflows/deploy.yaml'
@@ -80,6 +81,66 @@ jobs:
with:
project_id: neuron-785695
- name: Run database migrations
# Applies any pending migrations in migrations/*.sql to the Supabase DB.
# Runs unconditionally (asset-only or full build) so the schema is always
# current before the new code is deployed.
run: |
set -euo pipefail
python3 - << 'PYEOF'
import json, glob, subprocess, sys, os
def gcloud_secret(name):
return subprocess.check_output([
'gcloud', 'secrets', 'versions', 'access', 'latest',
f'--secret={name}', '--project=neuron-785695'
], text=True).strip()
access_token = gcloud_secret('supabase-access-token')
project_id = 'ocojsghaonltunidkzpw'
api_url = f'https://api.supabase.com/v1/projects/{project_id}/database/query'
def query(sql):
r = subprocess.run([
'curl', '-sf', '-X', 'POST', api_url,
'-H', f'Authorization: Bearer {access_token}',
'-H', 'Content-Type: application/json',
'-d', json.dumps({'query': sql})
], capture_output=True, text=True)
if r.returncode != 0:
raise RuntimeError(f'curl failed: {r.stderr}')
resp = json.loads(r.stdout)
if isinstance(resp, dict) and resp.get('message') and not isinstance(resp.get('message'), list):
raise RuntimeError(f'DB error: {resp}')
return resp
query("""
CREATE TABLE IF NOT EXISTS schema_migrations (
id text PRIMARY KEY,
applied_at timestamptz DEFAULT now()
)
""")
applied = {row['id'] for row in query('SELECT id FROM schema_migrations')}
print(f'Already applied: {sorted(applied)}')
pending = [p for p in sorted(glob.glob('migrations/*.sql'))
if os.path.basename(p) not in applied]
if not pending:
print('No pending migrations.')
sys.exit(0)
for path in pending:
name = os.path.basename(path)
print(f'Applying {name}...')
query(open(path).read())
query(f"INSERT INTO schema_migrations (id) VALUES ('{name}')")
print(f'Applied {name}')
print(f'Done. Applied {len(pending)} migration(s).')
PYEOF
- name: Configure docker auth for Artifact Registry
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
+61
View File
@@ -12,6 +12,7 @@ on:
- 'dist/**'
- 'runtime/**'
- 'tests/**'
- 'migrations/**'
- 'playwright.config.ts'
- 'package.json'
- 'Dockerfile.stage'
@@ -97,6 +98,66 @@ jobs:
with:
project_id: neuron-785695
- name: Run database migrations
# Applies any pending migrations in migrations/*.sql to the Supabase DB.
# Runs unconditionally (asset-only or full build) so the schema is always
# current before the new code is deployed.
run: |
set -euo pipefail
python3 - << 'PYEOF'
import json, glob, subprocess, sys, os
def gcloud_secret(name):
return subprocess.check_output([
'gcloud', 'secrets', 'versions', 'access', 'latest',
f'--secret={name}', '--project=neuron-785695'
], text=True).strip()
access_token = gcloud_secret('supabase-access-token')
project_id = 'ocojsghaonltunidkzpw'
api_url = f'https://api.supabase.com/v1/projects/{project_id}/database/query'
def query(sql):
r = subprocess.run([
'curl', '-sf', '-X', 'POST', api_url,
'-H', f'Authorization: Bearer {access_token}',
'-H', 'Content-Type: application/json',
'-d', json.dumps({'query': sql})
], capture_output=True, text=True)
if r.returncode != 0:
raise RuntimeError(f'curl failed: {r.stderr}')
resp = json.loads(r.stdout)
if isinstance(resp, dict) and resp.get('message') and not isinstance(resp.get('message'), list):
raise RuntimeError(f'DB error: {resp}')
return resp
query("""
CREATE TABLE IF NOT EXISTS schema_migrations (
id text PRIMARY KEY,
applied_at timestamptz DEFAULT now()
)
""")
applied = {row['id'] for row in query('SELECT id FROM schema_migrations')}
print(f'Already applied: {sorted(applied)}')
pending = [p for p in sorted(glob.glob('migrations/*.sql'))
if os.path.basename(p) not in applied]
if not pending:
print('No pending migrations.')
sys.exit(0)
for path in pending:
name = os.path.basename(path)
print(f'Applying {name}...')
query(open(path).read())
query(f"INSERT INTO schema_migrations (id) VALUES ('{name}')")
print(f'Applied {name}')
print(f'Done. Applied {len(pending)} migration(s).')
PYEOF
- name: Configure docker auth for Artifact Registry
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet