Skip to content

Commit 0dc6a47

Browse files
authored
feat(pg-delta): enhance pg-delta version handling (#5154)
feat(pg-delta): enhance pg-delta version handling and script interpolation - Updated the configuration to read the pg-delta npm version from the `.temp/pgdelta-version` file, defaulting to "1.0.0-alpha.22" if the file is missing or empty. - Implemented the `InterpolatePgDeltaScript` function to replace version placeholders in embedded TypeScript scripts with the effective pg-delta npm version. - Modified various functions to utilize the new script interpolation method for executing pg-delta related scripts, ensuring consistent version usage across the application. - Added tests to verify the correct behavior of the new version handling and interpolation logic. This change improves the management of pg-delta versions and enhances the flexibility of script execution.
1 parent 3f01508 commit 0dc6a47

9 files changed

Lines changed: 113 additions & 6 deletions

File tree

cmd/db_schema_declarative.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ var (
5252
// If the user has passed the --experimental flag and pg-delta is not enabled, enable it
5353
// so in the rest of the code we can know that we're running pg-delta logic.
5454
if viper.GetBool("EXPERIMENTAL") && !utils.IsPgDeltaEnabled() {
55-
utils.Config.Experimental.PgDelta = &config.PgDeltaConfig{Enabled: true}
55+
if utils.Config.Experimental.PgDelta == nil {
56+
utils.Config.Experimental.PgDelta = &config.PgDeltaConfig{Enabled: true}
57+
} else {
58+
// We preserve the version set into `.temp/pgdelta-version` by just enabling pg-delta.
59+
utils.Config.Experimental.PgDelta.Enabled = true
60+
}
5661
}
5762
if !utils.IsPgDeltaEnabled() {
5863
utils.CmdSuggestion = fmt.Sprintf("Either pass %s or add %s with %s to %s",

internal/db/diff/pgdelta.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/jackc/pgx/v4"
1515
"github.com/supabase/cli/internal/gen/types"
1616
"github.com/supabase/cli/internal/utils"
17+
"github.com/supabase/cli/pkg/config"
1718
)
1819

1920
//go:embed templates/pgdelta.ts
@@ -104,7 +105,8 @@ func DiffPgDeltaRef(ctx context.Context, sourceRef, targetRef string, schema []s
104105
binds = append(binds, cwd+":/workspace")
105106
}
106107
var stdout, stderr bytes.Buffer
107-
if err := utils.RunEdgeRuntimeScript(ctx, env, pgDeltaScript, binds, "error diffing schema", &stdout, &stderr); err != nil {
108+
script := config.InterpolatePgDeltaScript(config.Config(&utils.Config), pgDeltaScript)
109+
if err := utils.RunEdgeRuntimeScript(ctx, env, script, binds, "error diffing schema", &stdout, &stderr); err != nil {
108110
return "", err
109111
}
110112
return stdout.String(), nil
@@ -143,7 +145,8 @@ func DeclarativeExportPgDeltaRef(ctx context.Context, sourceRef, targetRef strin
143145
binds = append(binds, cwd+":/workspace")
144146
}
145147
var stdout, stderr bytes.Buffer
146-
if err := utils.RunEdgeRuntimeScript(ctx, env, pgDeltaDeclarativeExportScript, binds, "error exporting declarative schema", &stdout, &stderr); err != nil {
148+
script := config.InterpolatePgDeltaScript(config.Config(&utils.Config), pgDeltaDeclarativeExportScript)
149+
if err := utils.RunEdgeRuntimeScript(ctx, env, script, binds, "error exporting declarative schema", &stdout, &stderr); err != nil {
147150
return DeclarativeOutput{}, err
148151
}
149152
if stdout.Len() == 0 {
@@ -179,7 +182,8 @@ func ExportCatalogPgDelta(ctx context.Context, targetRef, role string, options .
179182
binds = append(binds, cwd+":/workspace")
180183
}
181184
var stdout, stderr bytes.Buffer
182-
if err := utils.RunEdgeRuntimeScript(ctx, env, pgDeltaCatalogExportScript, binds, "error exporting pg-delta catalog", &stdout, &stderr); err != nil {
185+
script := config.InterpolatePgDeltaScript(config.Config(&utils.Config), pgDeltaCatalogExportScript)
186+
if err := utils.RunEdgeRuntimeScript(ctx, env, script, binds, "error exporting pg-delta catalog", &stdout, &stderr); err != nil {
183187
return "", err
184188
}
185189
snapshot := strings.TrimSpace(stdout.String())

internal/db/pgcache/cache.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/spf13/viper"
2121
"github.com/supabase/cli/internal/gen/types"
2222
"github.com/supabase/cli/internal/utils"
23+
"github.com/supabase/cli/pkg/config"
2324
"github.com/supabase/cli/pkg/migration"
2425
)
2526

@@ -253,7 +254,8 @@ func exportCatalog(ctx context.Context, targetRef string, options ...func(*pgx.C
253254
}
254255
binds := []string{utils.EdgeRuntimeId + ":/root/.cache/deno:rw"}
255256
var stdout, stderr bytes.Buffer
256-
if err := utils.RunEdgeRuntimeScript(ctx, env, pgDeltaCatalogExportTS, binds, "error exporting pg-delta catalog", &stdout, &stderr); err != nil {
257+
script := config.InterpolatePgDeltaScript(config.Config(&utils.Config), pgDeltaCatalogExportTS)
258+
if err := utils.RunEdgeRuntimeScript(ctx, env, script, binds, "error exporting pg-delta catalog", &stdout, &stderr); err != nil {
257259
return "", err
258260
}
259261
snapshot := strings.TrimSpace(stdout.String())

internal/pgdelta/apply.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/spf13/afero"
1616
"github.com/spf13/viper"
1717
"github.com/supabase/cli/internal/utils"
18+
pkgconfig "github.com/supabase/cli/pkg/config"
1819
)
1920

2021
//go:embed templates/pgdelta_declarative_apply.ts
@@ -321,7 +322,8 @@ func ApplyDeclarative(ctx context.Context, config pgconn.Config, fsys afero.Fs)
321322

322323
fmt.Fprintln(os.Stderr, "Applying declarative schemas via pg-delta...")
323324
var stdout, stderr bytes.Buffer
324-
if err := utils.RunEdgeRuntimeScript(ctx, env, pgDeltaDeclarativeApplyScript, binds, "error running pg-delta script", &stdout, &stderr); err != nil {
325+
script := pkgconfig.InterpolatePgDeltaScript(pkgconfig.Config(&utils.Config), pgDeltaDeclarativeApplyScript)
326+
if err := utils.RunEdgeRuntimeScript(ctx, env, script, binds, "error running pg-delta script", &stdout, &stderr); err != nil {
325327
return err
326328
}
327329

internal/utils/misc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ var (
8686
PgmetaVersionPath = filepath.Join(TempDir, "pgmeta-version")
8787
PoolerVersionPath = filepath.Join(TempDir, "pooler-version")
8888
RealtimeVersionPath = filepath.Join(TempDir, "realtime-version")
89+
PgDeltaVersionPath = filepath.Join(TempDir, "pgdelta-version")
8990
CliVersionPath = filepath.Join(TempDir, "cli-latest")
9091
CurrBranchPath = filepath.Join(SupabaseDirPath, ".branches", "_current_branch")
9192
// DeclarativeDir is the canonical location for pg-delta declarative schema

pkg/config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ type (
229229
Enabled bool `toml:"enabled" json:"enabled"`
230230
DeclarativeSchemaPath string `toml:"declarative_schema_path" json:"declarative_schema_path"`
231231
FormatOptions string `toml:"format_options" json:"format_options"`
232+
// NpmVersion is set from .temp/pgdelta-version during Load (not from TOML).
233+
NpmVersion string `toml:"-" json:"-"`
232234
}
233235

234236
inspect struct {
@@ -690,6 +692,16 @@ func (c *config) Load(path string, fsys fs.FS, overrides ...ConfigEditor) error
690692
if version, err := fs.ReadFile(fsys, builder.LogflareVersionPath); err == nil && len(version) > 0 {
691693
c.Analytics.Image = replaceImageTag(Images.Logflare, string(version))
692694
}
695+
v := DefaultPgDeltaNpmVersion
696+
if version, err := fs.ReadFile(fsys, builder.PgDeltaVersionPath); err == nil {
697+
if trimmed := strings.TrimSpace(string(version)); len(trimmed) > 0 {
698+
v = trimmed
699+
}
700+
}
701+
if c.Experimental.PgDelta == nil {
702+
c.Experimental.PgDelta = &PgDeltaConfig{}
703+
}
704+
c.Experimental.PgDelta.NpmVersion = v
693705
// TODO: replace derived config resolution with viper decode hooks
694706
if err := c.resolve(builder, fsys); err != nil {
695707
return err

pkg/config/config_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,57 @@ format_options = "not-json"
243243
})
244244
}
245245

246+
func TestPgDeltaNpmVersionPinning(t *testing.T) {
247+
t.Run("defaults when pgdelta-version file missing", func(t *testing.T) {
248+
c := NewConfig()
249+
require.NoError(t, c.Load("", fs.MapFS{}))
250+
require.NotNil(t, c.Experimental.PgDelta)
251+
assert.Equal(t, DefaultPgDeltaNpmVersion, c.Experimental.PgDelta.NpmVersion)
252+
assert.Equal(t, DefaultPgDeltaNpmVersion, EffectivePgDeltaNpmVersion(Config(&c)))
253+
})
254+
255+
t.Run("EffectivePgDeltaNpmVersion nil config uses default", func(t *testing.T) {
256+
assert.Equal(t, DefaultPgDeltaNpmVersion, EffectivePgDeltaNpmVersion(nil))
257+
})
258+
259+
t.Run("reads trimmed version from supabase/.temp/pgdelta-version", func(t *testing.T) {
260+
c := NewConfig()
261+
fsys := fs.MapFS{
262+
"supabase/config.toml": &fs.MapFile{Data: []byte(`
263+
[experimental.pgdelta]
264+
enabled = true
265+
`)},
266+
"supabase/.temp/pgdelta-version": &fs.MapFile{Data: []byte(" 9.9.9-test \n")},
267+
}
268+
require.NoError(t, c.Load("", fsys))
269+
require.NotNil(t, c.Experimental.PgDelta)
270+
assert.Equal(t, "9.9.9-test", c.Experimental.PgDelta.NpmVersion)
271+
assert.Equal(t, "9.9.9-test", EffectivePgDeltaNpmVersion(Config(&c)))
272+
})
273+
274+
t.Run("whitespace-only pgdelta-version keeps default", func(t *testing.T) {
275+
c := NewConfig()
276+
fsys := fs.MapFS{
277+
"supabase/config.toml": &fs.MapFile{Data: []byte(`
278+
[experimental.pgdelta]
279+
enabled = true
280+
`)},
281+
"supabase/.temp/pgdelta-version": &fs.MapFile{Data: []byte(" \n")},
282+
}
283+
require.NoError(t, c.Load("", fsys))
284+
require.NotNil(t, c.Experimental.PgDelta)
285+
assert.Equal(t, DefaultPgDeltaNpmVersion, c.Experimental.PgDelta.NpmVersion)
286+
})
287+
288+
t.Run("InterpolatePgDeltaScript substitutes placeholder", func(t *testing.T) {
289+
c := NewConfig()
290+
require.NoError(t, c.Load("", fs.MapFS{}))
291+
// Embedded TS pins use this semver literal before InterpolatePgDeltaScript runs.
292+
got := InterpolatePgDeltaScript(Config(&c), `from "npm:@supabase/pg-delta@1.0.0-alpha.20";`)
293+
assert.Equal(t, `from "npm:@supabase/pg-delta@`+DefaultPgDeltaNpmVersion+`";`, got)
294+
})
295+
}
296+
246297
func TestRemoteOverride(t *testing.T) {
247298
t.Run("load staging override", func(t *testing.T) {
248299
config := NewConfig()

pkg/config/pgdelta_version.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package config
2+
3+
import "strings"
4+
5+
// DefaultPgDeltaNpmVersion is the npm dist-tag/version used for @supabase/pg-delta
6+
// when supabase/.temp/pgdelta-version is absent or empty.
7+
const DefaultPgDeltaNpmVersion = "1.0.0-alpha.22"
8+
9+
const pgDeltaNpmVersionPlaceholder = "1.0.0-alpha.20"
10+
11+
// EffectivePgDeltaNpmVersion returns the pg-delta npm version from loaded config,
12+
// or DefaultPgDeltaNpmVersion when unset (e.g. before Load or empty field).
13+
func EffectivePgDeltaNpmVersion(c Config) string {
14+
if c == nil {
15+
return DefaultPgDeltaNpmVersion
16+
}
17+
if c.Experimental.PgDelta != nil {
18+
if v := strings.TrimSpace(c.Experimental.PgDelta.NpmVersion); v != "" {
19+
return v
20+
}
21+
}
22+
return DefaultPgDeltaNpmVersion
23+
}
24+
25+
// InterpolatePgDeltaScript substitutes pg delta npm version placeholders in embedded TS.
26+
func InterpolatePgDeltaScript(c Config, script string) string {
27+
return strings.ReplaceAll(script, pgDeltaNpmVersionPlaceholder, EffectivePgDeltaNpmVersion(c))
28+
}

pkg/config/utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type pathBuilder struct {
2828
RealtimeVersionPath string
2929
EdgeRuntimeVersionPath string
3030
LogflareVersionPath string
31+
PgDeltaVersionPath string
3132
CliVersionPath string
3233
CurrBranchPath string
3334
SchemasDir string
@@ -64,6 +65,7 @@ func NewPathBuilder(configPath string) pathBuilder {
6465
PoolerVersionPath: filepath.Join(base, ".temp", "pooler-version"),
6566
RealtimeVersionPath: filepath.Join(base, ".temp", "realtime-version"),
6667
LogflareVersionPath: filepath.Join(base, ".temp", "logflare-version"),
68+
PgDeltaVersionPath: filepath.Join(base, ".temp", "pgdelta-version"),
6769
CliVersionPath: filepath.Join(base, ".temp", "cli-latest"),
6870
CurrBranchPath: filepath.Join(base, ".branches", "_current_branch"),
6971
SchemasDir: filepath.Join(base, "schemas"),

0 commit comments

Comments
 (0)