Skip to content

Commit 84f7075

Browse files
committed
feat: warn user about overlapping supabase schemas
1 parent ec24fe2 commit 84f7075

File tree

7 files changed

+105
-26
lines changed

7 files changed

+105
-26
lines changed

apps/deploy-worker/src/deploy.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
import { exec as execSync } from 'node:child_process'
2-
import { promisify } from 'node:util'
31
import { DeployError, IntegrationRevokedError } from '@database.build/deploy'
42
import {
5-
getAccessToken,
6-
createManagementApiClient,
73
createDeployedDatabase,
4+
createManagementApiClient,
85
generatePassword,
6+
getAccessToken,
97
getDatabaseUrl,
108
getPoolerUrl,
9+
SUPABASE_SCHEMAS,
1110
type SupabaseClient,
1211
type SupabaseDeploymentConfig,
1312
type SupabasePlatformConfig,
1413
type SupabaseProviderMetadata,
1514
} from '@database.build/deploy/supabase'
15+
import { exec as execSync } from 'node:child_process'
16+
import { promisify } from 'node:util'
1617
const exec = promisify(execSync)
1718

1819
/**
@@ -136,24 +137,7 @@ export async function deploy(
136137
databasePassword: remoteDatabasePassword,
137138
})
138139

139-
const excludedSchemas = [
140-
'auth',
141-
'cron',
142-
'extensions',
143-
'graphql',
144-
'graphql_public',
145-
'net',
146-
'pgbouncer',
147-
'pgsodium',
148-
'pgsodium_masks',
149-
'realtime',
150-
'storage',
151-
'supabase_functions',
152-
'supabase_migrations',
153-
'vault',
154-
]
155-
.map((schema) => `--exclude-schema=${schema}`)
156-
.join(' ')
140+
const excludedSchemas = SUPABASE_SCHEMAS.map((schema) => `--exclude-schema=${schema}`).join(' ')
157141

158142
// use pg_dump and pg_restore to transfer the data from the local database to the remote database
159143
const command = `pg_dump "${params.localDatabaseUrl}" -Fc ${excludedSchemas} -Z 0 | pg_restore -d "${remoteDatabaseUrl}" --clean --if-exists`

apps/postgres-new/components/deploy/deploy-dialog.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { Button } from '~/components/ui/button'
77
import {
88
Dialog,
99
DialogContent,
10+
DialogDescription,
1011
DialogFooter,
1112
DialogHeader,
1213
DialogTitle,
1314
} from '~/components/ui/dialog'
1415
import { useIntegrationQuery } from '~/data/integrations/integration-query'
1516
import { SupabaseIcon } from '../supabase-icon'
17+
import { SchemaOverlapWarning } from './schema-overlap-warning'
1618

1719
export type DeployDialogProps = {
1820
databaseId: string
@@ -34,25 +36,26 @@ export function DeployDialog({ databaseId, open, onOpenChange, onConfirm }: Depl
3436
</DialogTitle>
3537
<div className="py-2 border-b" />
3638
</DialogHeader>
37-
<div className="flex flex-col gap-6">
39+
<DialogDescription className="flex flex-col gap-4">
3840
{!integration ? (
3941
<Loader
4042
className="animate-spin self-center justify-self-center"
4143
size={36}
4244
strokeWidth={0.75}
4345
/>
4446
) : (
45-
<div className="prose flex flex-col gap-2">
47+
<div className="flex flex-col gap-2">
4648
You are about to deploy your in-browser database to Supabase. This will create a new
4749
Supabase project under your linked organization.
4850
<DeployCard
4951
organization={integration.organization}
5052
projectName={generateProjectName(databaseId)}
5153
/>
54+
<SchemaOverlapWarning databaseId={databaseId} />
5255
Would you like to deploy this database?
5356
</div>
5457
)}
55-
</div>
58+
</DialogDescription>
5659
<DialogFooter className="mt-1">
5760
<Button
5861
variant="secondary"

apps/postgres-new/components/deploy/redeploy-dialog.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Input } from '~/components/ui/input'
1414
import { MergedDatabase } from '~/data/merged-databases/merged-databases'
1515
import { Button } from '../ui/button'
1616
import { SupabaseIcon } from '../supabase-icon'
17+
import { SchemaOverlapWarning } from './schema-overlap-warning'
1718

1819
export type RedeployDialogProps = {
1920
database: MergedDatabase
@@ -43,6 +44,7 @@ export function RedeployDialog({ database, open, onOpenChange, onConfirm }: Rede
4344
version of your browser database. Existing schema and data in the deployed database
4445
will be lost.
4546
</p>
47+
<SchemaOverlapWarning databaseId={database.id} />
4648
<div className="my-1 border-b" />
4749
<div className="flex flex-col gap-3">
4850
<p>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { SUPABASE_SCHEMAS } from '@database.build/deploy/supabase'
2+
import { Results } from '@electric-sql/pglite'
3+
import { sql } from '@electric-sql/pglite/template'
4+
import { join } from '~/lib/db'
5+
import { useAsyncMemo } from '~/lib/hooks'
6+
import { useApp } from '../app-provider'
7+
import { Loader } from 'lucide-react'
8+
9+
export type SchemaOverlapWarningProps = {
10+
databaseId: string
11+
}
12+
13+
export function SchemaOverlapWarning({ databaseId }: SchemaOverlapWarningProps) {
14+
const { dbManager } = useApp()
15+
16+
const { value: overlappingSchemas, loading: isLoadingSchemas } = useAsyncMemo(async () => {
17+
if (!dbManager) {
18+
throw new Error('dbManager is not available')
19+
}
20+
21+
const db = await dbManager.getDbInstance(databaseId)
22+
23+
console.log(SUPABASE_SCHEMAS)
24+
25+
const { rows }: Results<{ schema_name: string }> = await db.sql`
26+
SELECT schema_name
27+
FROM information_schema.schemata
28+
WHERE schema_name IN (${join(
29+
SUPABASE_SCHEMAS.map((s) => sql`${s}`),
30+
','
31+
)})
32+
`
33+
34+
return rows.map((row) => row.schema_name)
35+
}, [databaseId])
36+
37+
if (isLoadingSchemas || !overlappingSchemas) {
38+
return (
39+
<Loader
40+
className="animate-spin self-center justify-self-center my-4"
41+
size={36}
42+
strokeWidth={0.75}
43+
/>
44+
)
45+
}
46+
47+
if (overlappingSchemas.length > 0) {
48+
return (
49+
<div className="flex flex-col gap-2 rounded-md border-destructive bg-destructive/25 p-4 mb-2">
50+
The following Supabase schemas were detected in your browser database and will be excluded
51+
from the deployment:
52+
<div className="prose text-sm">
53+
<ul className="my-0">
54+
{overlappingSchemas.map((schema) => (
55+
<li key={schema} className="my-0">
56+
<code>{schema}</code>
57+
</li>
58+
))}
59+
</ul>
60+
</div>
61+
</div>
62+
)
63+
}
64+
65+
return null
66+
}

apps/postgres-new/lib/db/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,10 @@ interface TemplateContainer {
562562
* Useful for building SQL queries with a dynamic number
563563
* of parameters.
564564
*/
565-
function join(templateContainers: TemplateContainer[], delimiter: string): TemplateContainer {
565+
export function join(
566+
templateContainers: TemplateContainer[],
567+
delimiter: string
568+
): TemplateContainer {
566569
return templateContainers.reduce(
567570
(acc, container, i) => (i === 0 ? container : sql`${acc}${raw`${delimiter}`}${container}`),
568571
sql``

packages/deploy/src/supabase/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './get-database-url.js'
55
export * from './revoke-integration.js'
66
export * from './wait-for-health.js'
77
export * from './database-types.js'
8+
export * from './schemas.js'
89
export * from './types.js'
910

1011
export * from './management-api/client.js'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Supabase built-in schemas that will be excluded from the
3+
* deployment if they exist in the source database.
4+
*/
5+
export const SUPABASE_SCHEMAS = [
6+
'auth',
7+
'cron',
8+
'extensions',
9+
'graphql',
10+
'graphql_public',
11+
'net',
12+
'pgbouncer',
13+
'pgsodium',
14+
'pgsodium_masks',
15+
'realtime',
16+
'storage',
17+
'supabase_functions',
18+
'supabase_migrations',
19+
'vault',
20+
]

0 commit comments

Comments
 (0)