Skip to content

Commit 5368b3f

Browse files
authored
feat(DEVWF-935): add deno script for running pg-delta (#4605)
* feat: implement pg-delta flag for schema diff * chore: update pgdelta library * chore: reduce duplication * fix: step down from login role after connect * chore: always set postgres role * chore: rename script
1 parent 17fa858 commit 5368b3f

4 files changed

Lines changed: 104 additions & 29 deletions

File tree

cmd/db.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ var (
8787
useMigra bool
8888
usePgAdmin bool
8989
usePgSchema bool
90+
usePgDelta bool
9091
schema []string
9192
file string
9293

@@ -101,6 +102,8 @@ var (
101102
if usePgSchema {
102103
differ = diff.DiffPgSchema
103104
fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "--use-pg-schema flag is experimental and may not include all entities, such as views and grants.")
105+
} else if usePgDelta {
106+
differ = diff.DiffPgDelta
104107
}
105108
return diff.Run(cmd.Context(), schema, file, flags.DbConfig, differ, afero.NewOsFs())
106109
},
@@ -257,7 +260,8 @@ func init() {
257260
diffFlags.BoolVar(&useMigra, "use-migra", true, "Use migra to generate schema diff.")
258261
diffFlags.BoolVar(&usePgAdmin, "use-pgadmin", false, "Use pgAdmin to generate schema diff.")
259262
diffFlags.BoolVar(&usePgSchema, "use-pg-schema", false, "Use pg-schema-diff to generate schema diff.")
260-
dbDiffCmd.MarkFlagsMutuallyExclusive("use-migra", "use-pgadmin")
263+
diffFlags.BoolVar(&usePgDelta, "use-pg-delta", false, "Use pg-delta to generate schema diff.")
264+
dbDiffCmd.MarkFlagsMutuallyExclusive("use-migra", "use-pgadmin", "use-pg-schema", "use-pg-delta")
261265
diffFlags.String("db-url", "", "Diffs against the database specified by the connection string (must be percent-encoded).")
262266
diffFlags.Bool("linked", false, "Diffs local migration files against the linked project.")
263267
diffFlags.Bool("local", true, "Diffs local migration files against the local database.")

internal/db/diff/migra.go

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/go-errors/errors"
1212
"github.com/jackc/pgconn"
1313
"github.com/jackc/pgx/v4"
14-
"github.com/spf13/viper"
1514
"github.com/supabase/cli/internal/gen/types"
1615
"github.com/supabase/cli/internal/utils"
1716
"github.com/supabase/cli/pkg/config"
@@ -119,33 +118,9 @@ func DiffSchemaMigra(ctx context.Context, source, target pgconn.Config, schema [
119118
} else {
120119
env = append(env, "EXCLUDED_SCHEMAS="+strings.Join(managedSchemas, ","))
121120
}
122-
cmd := []string{"edge-runtime", "start", "--main-service=."}
123-
if viper.GetBool("DEBUG") {
124-
cmd = append(cmd, "--verbose")
125-
}
126-
cmdString := strings.Join(cmd, " ")
127-
entrypoint := []string{"sh", "-c", `cat <<'EOF' > index.ts && ` + cmdString + `
128-
` + diffSchemaTypeScript + `
129-
EOF
130-
`}
131-
var out, stderr bytes.Buffer
132-
if err := utils.DockerRunOnceWithConfig(
133-
ctx,
134-
container.Config{
135-
Image: utils.Config.EdgeRuntime.Image,
136-
Env: env,
137-
Entrypoint: entrypoint,
138-
},
139-
container.HostConfig{
140-
Binds: []string{utils.EdgeRuntimeId + ":/root/.cache/deno:rw"},
141-
NetworkMode: network.NetworkHost,
142-
},
143-
network.NetworkingConfig{},
144-
"",
145-
&out,
146-
&stderr,
147-
); err != nil && !strings.HasPrefix(stderr.String(), "main worker has been destroyed") {
148-
return "", errors.Errorf("error diffing schema: %w:\n%s", err, stderr.String())
121+
var out bytes.Buffer
122+
if err := diffWithStream(ctx, env, diffSchemaTypeScript, &out); err != nil {
123+
return "", err
149124
}
150125
return out.String(), nil
151126
}

internal/db/diff/pgdelta.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package diff
2+
3+
import (
4+
"bytes"
5+
"context"
6+
_ "embed"
7+
"io"
8+
"strings"
9+
10+
"github.com/docker/docker/api/types/container"
11+
"github.com/docker/docker/api/types/network"
12+
"github.com/go-errors/errors"
13+
"github.com/jackc/pgconn"
14+
"github.com/jackc/pgx/v4"
15+
"github.com/spf13/viper"
16+
"github.com/supabase/cli/internal/gen/types"
17+
"github.com/supabase/cli/internal/utils"
18+
)
19+
20+
//go:embed templates/pgdelta.ts
21+
var pgDeltaScript string
22+
23+
func DiffPgDelta(ctx context.Context, source, target pgconn.Config, schema []string, options ...func(*pgx.ConnConfig)) (string, error) {
24+
env := []string{
25+
"SOURCE=" + utils.ToPostgresURL(source),
26+
"TARGET=" + utils.ToPostgresURL(target),
27+
}
28+
if ca, err := types.GetRootCA(ctx, utils.ToPostgresURL(target), options...); err != nil {
29+
return "", err
30+
} else if len(ca) > 0 {
31+
env = append(env, "PGDELTA_TARGET_SSLROOTCERT="+ca)
32+
}
33+
if len(schema) > 0 {
34+
env = append(env, "INCLUDED_SCHEMAS="+strings.Join(schema, ","))
35+
}
36+
var out bytes.Buffer
37+
if err := diffWithStream(ctx, env, pgDeltaScript, &out); err != nil {
38+
return "", err
39+
}
40+
return out.String(), nil
41+
}
42+
43+
func diffWithStream(ctx context.Context, env []string, script string, stdout io.Writer) error {
44+
cmd := []string{"edge-runtime", "start", "--main-service=."}
45+
if viper.GetBool("DEBUG") {
46+
cmd = append(cmd, "--verbose")
47+
}
48+
cmdString := strings.Join(cmd, " ")
49+
entrypoint := []string{"sh", "-c", `cat <<'EOF' > index.ts && ` + cmdString + `
50+
` + script + `
51+
EOF
52+
`}
53+
var stderr bytes.Buffer
54+
if err := utils.DockerRunOnceWithConfig(
55+
ctx,
56+
container.Config{
57+
Image: utils.Config.EdgeRuntime.Image,
58+
Env: env,
59+
Entrypoint: entrypoint,
60+
},
61+
container.HostConfig{
62+
Binds: []string{utils.EdgeRuntimeId + ":/root/.cache/deno:rw"},
63+
NetworkMode: network.NetworkHost,
64+
},
65+
network.NetworkingConfig{},
66+
"",
67+
stdout,
68+
&stderr,
69+
); err != nil && !strings.HasPrefix(stderr.String(), "main worker has been destroyed") {
70+
return errors.Errorf("error diffing schema: %w:\n%s", err, stderr.String())
71+
}
72+
return nil
73+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createPlan } from "npm:@supabase/pg-delta@1.0.0-alpha.1";
2+
import { supabase } from "npm:@supabase/pg-delta@1.0.0-alpha.1/integrations/supabase";
3+
4+
const source = Deno.env.get("SOURCE");
5+
const target = Deno.env.get("TARGET");
6+
7+
const includedSchemas = Deno.env.get("INCLUDED_SCHEMAS");
8+
if (includedSchemas) {
9+
supabase.filter = { schema: includedSchemas.split(",") };
10+
}
11+
supabase.role = "postgres";
12+
13+
try {
14+
const result = await createPlan(source, target, supabase);
15+
const statements = result?.plan.statements ?? [];
16+
for (const sql of statements) {
17+
console.log(`${sql};`);
18+
}
19+
} catch (e) {
20+
console.error(e);
21+
// Force close event loop
22+
throw new Error("");
23+
}

0 commit comments

Comments
 (0)