Skip to content

Commit e5582ae

Browse files
authored
feat: add preview deployment infrastructure for demo app (#563)
# Enhanced Preview Deployment Pipeline for Demo App This PR implements a comprehensive preview deployment system for the demo app, enabling isolated preview environments for testing. The changes include: 1. **Structured Preview Deployment Process** - Split the deployment into three distinct phases: database, edge functions, and web app - Added validation script to ensure all required environment variables are present - Created specialized build command for preview environments 2. **Database Management** - Added script to reset preview databases with proper handling of pgmq queues - Ensured Supabase-managed extensions are properly handled during resets - Fixed extension initialization issues with pg_cron and pg_net 3. **Edge Functions Deployment** - Added dedicated script for deploying edge functions to preview environments - Implemented proper authentication using Supabase access tokens - Added support for tracking worker functions 4. **PGFlow Migrations and Improvements** - Added migrations for PGFlow auto-compilation and worker management - Implemented flow shape comparison and validation - Added support for graceful worker shutdown and monitoring 5. **Sample Flow Implementation** - Added a simple "GreetUser" flow as an example - Created corresponding edge function worker The PR ensures a complete, isolated preview environment can be deployed with a single command while maintaining proper dependency ordering between components.
1 parent 2e0d190 commit e5582ae

16 files changed

Lines changed: 919 additions & 33 deletions

apps/demo/project.json

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,59 @@
7777
"command": "wrangler deploy --env production"
7878
}
7979
},
80-
"deploy:preview": {
80+
"validate:preview": {
81+
"executor": "nx:run-commands",
82+
"options": {
83+
"cwd": "apps/demo",
84+
"command": "./scripts/validate-preview-env.sh"
85+
}
86+
},
87+
"build:preview": {
88+
"executor": "nx:run-commands",
89+
"dependsOn": ["validate:preview", "^build"],
90+
"inputs": ["{projectRoot}/.env.preview", "{projectRoot}/wrangler.toml"],
91+
"outputs": ["{projectRoot}/.svelte-kit"],
92+
"options": {
93+
"cwd": "apps/demo",
94+
"command": "vite build --mode preview"
95+
}
96+
},
97+
"deploy:preview:webapp": {
8198
"executor": "nx:run-commands",
8299
"cache": false,
83100
"local": true,
84-
"dependsOn": ["build"],
101+
"dependsOn": ["sync-edge-deps", "build:preview"],
85102
"inputs": ["{projectRoot}/wrangler.toml"],
86103
"options": {
87104
"cwd": "apps/demo",
88105
"command": "./scripts/deploy-preview.sh ${PREVIEW_NAME:-pr-${PR_NUMBER:-preview}}"
89106
}
107+
},
108+
"deploy:preview:db": {
109+
"executor": "nx:run-commands",
110+
"cache": false,
111+
"dependsOn": ["validate:preview"],
112+
"options": {
113+
"cwd": "apps/demo",
114+
"command": "./scripts/db-reset-preview.sh"
115+
}
116+
},
117+
"deploy:preview:functions": {
118+
"executor": "nx:run-commands",
119+
"cache": false,
120+
"dependsOn": ["deploy:preview:db", "sync-edge-deps"],
121+
"options": {
122+
"cwd": "apps/demo",
123+
"command": "./scripts/functions-deploy-preview.sh"
124+
}
125+
},
126+
"deploy:preview": {
127+
"executor": "nx:run-commands",
128+
"cache": false,
129+
"dependsOn": ["deploy:preview:functions", "deploy:preview:webapp"],
130+
"options": {
131+
"command": "echo '✅ Full preview deployment complete!'"
132+
}
90133
}
91134
}
92135
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Reset the preview Supabase database using linked mode
5+
# (--linked mode knows about Supabase-managed extensions like pg_cron)
6+
7+
cd "$(dirname "$0")/.."
8+
9+
# Source env
10+
set -a; source .env.preview; set +a
11+
12+
echo "🗑️ Resetting preview database..."
13+
echo " Project: $SUPABASE_PROJECT_REF"
14+
15+
# Clean up pgmq queues while extension is still intact
16+
# This prevents orphaned tables after db reset drops the extension
17+
echo "🧹 Cleaning up pgmq queues..."
18+
if ! psql "$SUPABASE_DB_URL" -q -c "SELECT pgmq.drop_queue(queue_name) FROM pgmq.list_queues();" 2>/dev/null; then
19+
echo " (no queues to clean up or pgmq not available - continuing)"
20+
fi
21+
22+
# Link to the project (uses SUPABASE_ACCESS_TOKEN for auth)
23+
echo "🔗 Linking to project..."
24+
SUPABASE_ACCESS_TOKEN="$SUPABASE_ACCESS_TOKEN" pnpm supabase link --project-ref "$SUPABASE_PROJECT_REF"
25+
26+
# Reset using linked mode (respects Supabase-managed extensions)
27+
pnpm supabase db reset --linked --yes
28+
29+
# Set up vault secrets and register worker function
30+
# - Secrets needed for ensure_workers cron to make HTTP calls to edge functions
31+
# - Worker registration so cron knows to keep article_flow_worker alive
32+
echo "🔐 Setting up vault secrets and registering worker..."
33+
34+
# Use psql's -v for safe variable passing (prevents SQL injection)
35+
# ON_ERROR_STOP ensures heredoc errors propagate correctly with set -e
36+
if ! psql "$SUPABASE_DB_URL" -q \
37+
-v ON_ERROR_STOP=1 \
38+
-v service_role_key="$SUPABASE_SERVICE_ROLE_KEY" \
39+
-v project_ref="$SUPABASE_PROJECT_REF" \
40+
<<'EOSQL'
41+
DELETE FROM vault.secrets WHERE name IN ('supabase_service_role_key', 'supabase_project_id');
42+
SELECT vault.create_secret(:'service_role_key', 'supabase_service_role_key');
43+
SELECT vault.create_secret(:'project_ref', 'supabase_project_id');
44+
SELECT pgflow.track_worker_function('article_flow_worker');
45+
EOSQL
46+
then
47+
echo "❌ Failed to set up vault secrets and register worker"
48+
exit 1
49+
fi
50+
51+
echo ""
52+
echo "✅ Preview database reset complete!"
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
11
#!/usr/bin/env bash
2-
set -e
2+
set -euo pipefail
33

4-
# Script to deploy a preview version of the demo app
5-
# Uses preview alias for deterministic URLs
4+
# Deploy preview to Cloudflare Workers
65
# Assumes build is already done (via Nx dependsOn)
7-
# Usage: pnpm nx run demo:deploy:preview -- [preview-name]
6+
# Usage: ./deploy-preview.sh [preview-name]
87

98
PREVIEW_NAME="${1:-preview}"
109

11-
echo "🚀 Deploying preview: $PREVIEW_NAME"
12-
13-
# Navigate to demo directory if not already there
1410
cd "$(dirname "$0")/.."
1511

16-
echo "🌐 Deploying to Cloudflare Workers..."
12+
echo "🚀 Deploying preview: $PREVIEW_NAME"
1713
wrangler versions upload --preview-alias "${PREVIEW_NAME}"
1814

1915
echo ""
20-
echo "✅ Preview deployed successfully!"
21-
echo "🔗 Preview alias URL should be available at:"
22-
echo " https://${PREVIEW_NAME}-pgflow-demo.jumski.workers.dev"
23-
echo ""
24-
echo "💡 If the URL doesn't work, check:"
25-
echo " - Preview URLs are enabled in dashboard (Settings > Domains & Routes)"
26-
echo " - Worker name in wrangler.toml matches 'pgflow-demo'"
16+
echo "✅ Preview deployed!"
17+
echo "🔗 https://${PREVIEW_NAME}-pgflow-demo.jumski.workers.dev"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Deploy edge functions to the preview Supabase project
5+
6+
cd "$(dirname "$0")/.."
7+
8+
# Source env
9+
set -a; source .env.preview; set +a
10+
11+
echo "🚀 Deploying edge functions to preview..."
12+
echo " Project: $SUPABASE_PROJECT_REF"
13+
14+
SUPABASE_ACCESS_TOKEN="$SUPABASE_ACCESS_TOKEN" pnpm supabase functions deploy article_flow_worker --project-ref "$SUPABASE_PROJECT_REF"
15+
16+
echo ""
17+
echo "✅ Edge functions deployed to preview!"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Validate .env.preview has all required variables
5+
6+
cd "$(dirname "$0")/.."
7+
8+
if [[ ! -f .env.preview ]]; then
9+
echo "❌ .env.preview not found"
10+
exit 1
11+
fi
12+
13+
# Source the env file
14+
set -a; source .env.preview; set +a
15+
16+
ERRORS=()
17+
18+
# Website build vars
19+
if [[ -z "${VITE_SUPABASE_URL:-}" ]]; then
20+
ERRORS+=("VITE_SUPABASE_URL is missing")
21+
elif [[ ! "$VITE_SUPABASE_URL" =~ ^https:// ]]; then
22+
ERRORS+=("VITE_SUPABASE_URL must use https://")
23+
fi
24+
25+
if [[ -z "${VITE_SUPABASE_ANON_KEY:-}" ]]; then
26+
ERRORS+=("VITE_SUPABASE_ANON_KEY is missing")
27+
fi
28+
29+
# Supabase CLI vars
30+
if [[ -z "${SUPABASE_PROJECT_REF:-}" ]]; then
31+
ERRORS+=("SUPABASE_PROJECT_REF is missing")
32+
fi
33+
34+
if [[ -z "${SUPABASE_DB_URL:-}" ]]; then
35+
ERRORS+=("SUPABASE_DB_URL is missing")
36+
elif [[ ! "$SUPABASE_DB_URL" =~ ^postgresql:// ]]; then
37+
ERRORS+=("SUPABASE_DB_URL must start with postgresql://")
38+
fi
39+
40+
if [[ -z "${SUPABASE_ACCESS_TOKEN:-}" ]]; then
41+
ERRORS+=("SUPABASE_ACCESS_TOKEN is missing")
42+
fi
43+
44+
if [[ -z "${SUPABASE_SERVICE_ROLE_KEY:-}" ]]; then
45+
ERRORS+=("SUPABASE_SERVICE_ROLE_KEY is missing")
46+
fi
47+
48+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
49+
echo "❌ .env.preview validation failed:"
50+
for err in "${ERRORS[@]}"; do
51+
echo " - $err"
52+
done
53+
exit 1
54+
fi
55+
56+
echo "✅ .env.preview validated"

apps/demo/supabase/flows/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Re-export all flows from this directory
2+
// Example: export { MyFlow } from './my-flow.ts';

apps/demo/supabase/functions/article_flow_worker/deno.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"imports": {
3+
"@std/crypto/timing-safe-equal": "jsr:@std/crypto@1/timing-safe-equal",
34
"@pgflow/core": "../_vendor/@pgflow/core/index.ts",
45
"@pgflow/core/": "../_vendor/@pgflow/core/",
56
"@pgflow/dsl": "../_vendor/@pgflow/dsl/index.ts",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"imports": {
3+
"@pgflow/core": "npm:@pgflow/core@0.11.0",
4+
"@pgflow/core/": "npm:@pgflow/core@0.11.0/",
5+
"@pgflow/dsl": "npm:@pgflow/dsl@0.11.0",
6+
"@pgflow/dsl/": "npm:@pgflow/dsl@0.11.0/",
7+
"@pgflow/dsl/supabase": "npm:@pgflow/dsl@0.11.0/supabase",
8+
"@pgflow/edge-worker": "jsr:@pgflow/edge-worker@0.11.0",
9+
"@pgflow/edge-worker/": "jsr:@pgflow/edge-worker@0.11.0/",
10+
"@pgflow/edge-worker/_internal": "jsr:@pgflow/edge-worker@0.11.0/_internal"
11+
}
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { ControlPlane } from '@pgflow/edge-worker';
2+
import * as flows from '../../flows/index.ts';
3+
4+
ControlPlane.serve(flows);
Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,18 @@
11
-- Cleanup any orphaned pgmq queues from incomplete resets
22
-- This ensures a clean slate before flow migrations run
3+
-- Note: The primary cleanup now happens in db-reset-preview.sh BEFORE reset,
4+
-- while pgmq.list_queues() still works. This migration is a fallback.
35

46
DO $$
57
DECLARE
68
queue_record RECORD;
79
BEGIN
810
-- Drop any existing queues that might be orphaned
9-
-- This handles the case where DROP EXTENSION pgmq didn't cascade properly
1011
FOR queue_record IN
1112
SELECT queue_name
1213
FROM pgmq.list_queues()
1314
LOOP
14-
-- Use pgmq's built-in drop function to safely remove the queue
1515
PERFORM pgmq.drop_queue(queue_record.queue_name);
16-
1716
RAISE NOTICE 'Dropped orphaned queue: %', queue_record.queue_name;
1817
END LOOP;
19-
20-
-- Also drop any orphaned sequences that might exist without tables
21-
FOR queue_record IN
22-
SELECT sequence_name
23-
FROM information_schema.sequences
24-
WHERE sequence_schema = 'pgmq'
25-
AND sequence_name LIKE 'q_%_msg_id_seq'
26-
LOOP
27-
EXECUTE format('DROP SEQUENCE IF EXISTS pgmq.%I CASCADE', queue_record.sequence_name);
28-
29-
RAISE NOTICE 'Dropped orphaned sequence: %', queue_record.sequence_name;
30-
END LOOP;
31-
3218
END $$;

0 commit comments

Comments
 (0)