• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / cli / 22164831089

19 Feb 2026 01:28AM UTC coverage: 61.651% (-0.1%) from 61.773%
22164831089

Pull #4871

github

web-flow
Merge 217ff8192 into 54c41b847
Pull Request #4871: feat(db): add --use-shadow-db flag to test db command

5 of 32 new or added lines in 3 files covered. (15.63%)

6 existing lines in 2 files now uncovered.

7707 of 12501 relevant lines covered (61.65%)

7.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/cmd/db.go
1
package cmd
2

3
import (
4
        "fmt"
5
        "os"
6
        "path/filepath"
7

8
        "github.com/spf13/afero"
9
        "github.com/spf13/cobra"
10
        "github.com/spf13/viper"
11
        "github.com/supabase/cli/internal/db/diff"
12
        "github.com/supabase/cli/internal/db/dump"
13
        "github.com/supabase/cli/internal/db/lint"
14
        "github.com/supabase/cli/internal/db/pull"
15
        "github.com/supabase/cli/internal/db/push"
16
        "github.com/supabase/cli/internal/db/reset"
17
        "github.com/supabase/cli/internal/db/start"
18
        "github.com/supabase/cli/internal/db/test"
19
        "github.com/supabase/cli/internal/utils"
20
        "github.com/supabase/cli/internal/utils/flags"
21
        "github.com/supabase/cli/legacy/branch/create"
22
        "github.com/supabase/cli/legacy/branch/delete"
23
        "github.com/supabase/cli/legacy/branch/list"
24
        "github.com/supabase/cli/legacy/branch/switch_"
25
        legacy "github.com/supabase/cli/legacy/diff"
26
        "github.com/supabase/cli/pkg/migration"
27
)
28

29
var (
30
        dbCmd = &cobra.Command{
31
                GroupID: groupLocalDev,
32
                Use:     "db",
33
                Short:   "Manage Postgres databases",
34
        }
35

36
        dbBranchCmd = &cobra.Command{
37
                Hidden: true,
38
                Use:    "branch",
39
                Short:  "Manage local database branches",
40
                Long:   "Manage local database branches. Each branch is associated with a separate local database. Forking remote databases is NOT supported.",
41
        }
42

43
        dbBranchCreateCmd = &cobra.Command{
44
                Deprecated: "use \"branches create <name>\" instead.\n",
45
                Use:        "create <branch name>",
46
                Short:      "Create a branch",
47
                Args:       cobra.ExactArgs(1),
48
                RunE: func(cmd *cobra.Command, args []string) error {
×
49
                        return create.Run(args[0], afero.NewOsFs())
×
50
                },
×
51
        }
52

53
        dbBranchDeleteCmd = &cobra.Command{
54
                Deprecated: "use \"branches delete <branch-id>\" instead.\n",
55
                Use:        "delete <branch name>",
56
                Short:      "Delete a branch",
57
                Args:       cobra.ExactArgs(1),
58
                RunE: func(cmd *cobra.Command, args []string) error {
×
59
                        return delete.Run(args[0], afero.NewOsFs())
×
60
                },
×
61
        }
62

63
        dbBranchListCmd = &cobra.Command{
64
                Deprecated: "use \"branches list\" instead.\n",
65
                Use:        "list",
66
                Short:      "List branches",
67
                RunE: func(cmd *cobra.Command, args []string) error {
×
68
                        return list.Run(afero.NewOsFs(), os.Stdout)
×
69
                },
×
70
        }
71

72
        dbSwitchCmd = &cobra.Command{
73
                Deprecated: "use \"branches create <name>\" instead.\n",
74
                Use:        "switch <branch name>",
75
                Short:      "Switch the active branch",
76
                Args:       cobra.ExactArgs(1),
77
                RunE: func(cmd *cobra.Command, args []string) error {
×
78
                        return switch_.Run(cmd.Context(), args[0], afero.NewOsFs())
×
79
                },
×
80
        }
81

82
        useMigra    bool
83
        usePgAdmin  bool
84
        usePgSchema bool
85
        usePgDelta  bool
86
        schema      []string
87
        file        string
88

89
        dbDiffCmd = &cobra.Command{
90
                Use:   "diff",
91
                Short: "Diffs the local database for schema changes",
92
                RunE: func(cmd *cobra.Command, args []string) error {
×
93
                        if usePgAdmin {
×
94
                                return legacy.RunPgAdmin(cmd.Context(), schema, file, flags.DbConfig, afero.NewOsFs())
×
95
                        }
×
96
                        differ := diff.DiffSchemaMigra
×
97
                        if usePgSchema {
×
98
                                differ = diff.DiffPgSchema
×
99
                                fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "--use-pg-schema flag is experimental and may not include all entities, such as views and grants.")
×
100
                        } else if usePgDelta {
×
101
                                differ = diff.DiffPgDelta
×
102
                        }
×
103
                        return diff.Run(cmd.Context(), schema, file, flags.DbConfig, differ, afero.NewOsFs())
×
104
                },
105
        }
106

107
        dataOnly     bool
108
        useCopy      bool
109
        roleOnly     bool
110
        keepComments bool
111
        excludeTable []string
112

113
        dbDumpCmd = &cobra.Command{
114
                Use:   "dump",
115
                Short: "Dumps data or schemas from the remote database",
116
                PreRun: func(cmd *cobra.Command, args []string) {
×
117
                        if useCopy || len(excludeTable) > 0 {
×
118
                                cobra.CheckErr(cmd.MarkFlagRequired("data-only"))
×
119
                        }
×
120
                },
121
                RunE: func(cmd *cobra.Command, args []string) error {
×
122
                        opts := []migration.DumpOptionFunc{
×
123
                                migration.WithSchema(schema...),
×
124
                                migration.WithoutTable(excludeTable...),
×
125
                                migration.WithComments(keepComments),
×
126
                                migration.WithColumnInsert(!useCopy),
×
127
                        }
×
128
                        return dump.Run(cmd.Context(), file, flags.DbConfig, dataOnly, roleOnly, dryRun, afero.NewOsFs(), opts...)
×
129
                },
×
130
                PostRun: func(cmd *cobra.Command, args []string) {
×
131
                        if len(file) > 0 {
×
132
                                if absPath, err := filepath.Abs(file); err != nil {
×
133
                                        fmt.Fprintln(os.Stderr, "Dumped schema to "+utils.Bold(file)+".")
×
134
                                } else {
×
135
                                        fmt.Fprintln(os.Stderr, "Dumped schema to "+utils.Bold(absPath)+".")
×
136
                                }
×
137
                        }
138
                },
139
        }
140

141
        dryRun       bool
142
        includeAll   bool
143
        includeRoles bool
144
        includeSeed  bool
145

146
        dbPushCmd = &cobra.Command{
147
                Use:   "push",
148
                Short: "Push new migrations to the remote database",
149
                RunE: func(cmd *cobra.Command, args []string) error {
×
150
                        return push.Run(cmd.Context(), dryRun, includeAll, includeRoles, includeSeed, flags.DbConfig, afero.NewOsFs())
×
151
                },
×
152
        }
153

154
        dbPullCmd = &cobra.Command{
155
                Use:   "pull [migration name]",
156
                Short: "Pull schema from the remote database",
157
                RunE: func(cmd *cobra.Command, args []string) error {
×
158
                        name := "remote_schema"
×
159
                        if len(args) > 0 {
×
160
                                name = args[0]
×
161
                        }
×
162
                        return pull.Run(cmd.Context(), schema, flags.DbConfig, name, afero.NewOsFs())
×
163
                },
164
                PostRun: func(cmd *cobra.Command, args []string) {
×
165
                        fmt.Println("Finished " + utils.Aqua("supabase db pull") + ".")
×
166
                },
×
167
        }
168

169
        dbRemoteCmd = &cobra.Command{
170
                Hidden: true,
171
                Use:    "remote",
172
                Short:  "Manage remote databases",
173
        }
174

175
        dbRemoteChangesCmd = &cobra.Command{
176
                Deprecated: "use \"db diff --use-migra --linked\" instead.\n",
177
                Use:        "changes",
178
                Short:      "Show changes on the remote database",
179
                Long:       "Show changes on the remote database since last migration.",
180
                RunE: func(cmd *cobra.Command, args []string) error {
×
181
                        return diff.Run(cmd.Context(), schema, file, flags.DbConfig, diff.DiffSchemaMigra, afero.NewOsFs())
×
182
                },
×
183
        }
184

185
        dbRemoteCommitCmd = &cobra.Command{
186
                Deprecated: "use \"db pull\" instead.\n",
187
                Use:        "commit",
188
                Short:      "Commit remote changes as a new migration",
189
                RunE: func(cmd *cobra.Command, args []string) error {
×
190
                        return pull.Run(cmd.Context(), schema, flags.DbConfig, "remote_commit", afero.NewOsFs())
×
191
                },
×
192
        }
193

194
        noSeed      bool
195
        lastVersion uint
196

197
        dbResetCmd = &cobra.Command{
198
                Use:   "reset",
199
                Short: "Resets the local database to current migrations",
200
                RunE: func(cmd *cobra.Command, args []string) error {
×
201
                        if noSeed {
×
202
                                utils.Config.Db.Seed.Enabled = false
×
203
                        }
×
204
                        return reset.Run(cmd.Context(), migrationVersion, lastVersion, flags.DbConfig, afero.NewOsFs())
×
205
                },
206
        }
207

208
        level = utils.EnumFlag{
209
                Allowed: lint.AllowedLevels,
210
                Value:   lint.AllowedLevels[0],
211
        }
212

213
        lintFailOn = utils.EnumFlag{
214
                Allowed: append([]string{"none"}, lint.AllowedLevels...),
215
                Value:   "none",
216
        }
217

218
        dbLintCmd = &cobra.Command{
219
                Use:   "lint",
220
                Short: "Checks local database for typing error",
221
                RunE: func(cmd *cobra.Command, args []string) error {
×
222
                        return lint.Run(cmd.Context(), schema, level.Value, lintFailOn.Value, flags.DbConfig, afero.NewOsFs())
×
223
                },
×
224
        }
225

226
        fromBackup string
227

228
        dbStartCmd = &cobra.Command{
229
                Use:   "start",
230
                Short: "Starts local Postgres database",
231
                RunE: func(cmd *cobra.Command, args []string) error {
×
232
                        return start.Run(cmd.Context(), fromBackup, afero.NewOsFs())
×
233
                },
×
234
        }
235

236
        dbTestCmd = &cobra.Command{
237
                Hidden: true,
238
                Use:    "test [path] ...",
239
                Short:  "Tests local database with pgTAP",
240
                RunE: func(cmd *cobra.Command, args []string) error {
×
NEW
241
                        useShadow, _ := cmd.Flags().GetBool("use-shadow-db")
×
NEW
242
                        return test.Run(cmd.Context(), args, flags.DbConfig, useShadow, afero.NewOsFs())
×
UNCOV
243
                },
×
244
        }
245
)
246

247
func init() {
×
248
        // Build branch command
×
249
        dbBranchCmd.AddCommand(dbBranchCreateCmd)
×
250
        dbBranchCmd.AddCommand(dbBranchDeleteCmd)
×
251
        dbBranchCmd.AddCommand(dbBranchListCmd)
×
252
        dbBranchCmd.AddCommand(dbSwitchCmd)
×
253
        dbCmd.AddCommand(dbBranchCmd)
×
254
        // Build diff command
×
255
        diffFlags := dbDiffCmd.Flags()
×
256
        diffFlags.BoolVar(&useMigra, "use-migra", true, "Use migra to generate schema diff.")
×
257
        diffFlags.BoolVar(&usePgAdmin, "use-pgadmin", false, "Use pgAdmin to generate schema diff.")
×
258
        diffFlags.BoolVar(&usePgSchema, "use-pg-schema", false, "Use pg-schema-diff to generate schema diff.")
×
259
        diffFlags.BoolVar(&usePgDelta, "use-pg-delta", false, "Use pg-delta to generate schema diff.")
×
260
        dbDiffCmd.MarkFlagsMutuallyExclusive("use-migra", "use-pgadmin", "use-pg-schema", "use-pg-delta")
×
261
        diffFlags.String("db-url", "", "Diffs against the database specified by the connection string (must be percent-encoded).")
×
262
        diffFlags.Bool("linked", false, "Diffs local migration files against the linked project.")
×
263
        diffFlags.Bool("local", true, "Diffs local migration files against the local database.")
×
264
        dbDiffCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
265
        diffFlags.StringVarP(&file, "file", "f", "", "Saves schema diff to a new migration file.")
×
266
        diffFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
×
267
        dbCmd.AddCommand(dbDiffCmd)
×
268
        // Build dump command
×
269
        dumpFlags := dbDumpCmd.Flags()
×
270
        dumpFlags.BoolVar(&dryRun, "dry-run", false, "Prints the pg_dump script that would be executed.")
×
271
        dumpFlags.BoolVar(&dataOnly, "data-only", false, "Dumps only data records.")
×
272
        dumpFlags.BoolVar(&useCopy, "use-copy", false, "Use copy statements in place of inserts.")
×
273
        dumpFlags.StringSliceVarP(&excludeTable, "exclude", "x", []string{}, "List of schema.tables to exclude from data-only dump.")
×
274
        dumpFlags.BoolVar(&roleOnly, "role-only", false, "Dumps only cluster roles.")
×
275
        dbDumpCmd.MarkFlagsMutuallyExclusive("role-only", "data-only")
×
276
        dumpFlags.BoolVar(&keepComments, "keep-comments", false, "Keeps commented lines from pg_dump output.")
×
277
        dbDumpCmd.MarkFlagsMutuallyExclusive("keep-comments", "data-only")
×
278
        dumpFlags.StringVarP(&file, "file", "f", "", "File path to save the dumped contents.")
×
279
        dumpFlags.String("db-url", "", "Dumps from the database specified by the connection string (must be percent-encoded).")
×
280
        dumpFlags.Bool("linked", true, "Dumps from the linked project.")
×
281
        dumpFlags.Bool("local", false, "Dumps from the local database.")
×
282
        dbDumpCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
283
        dumpFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.")
×
284
        cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", dumpFlags.Lookup("password")))
×
285
        dumpFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
×
286
        dbDumpCmd.MarkFlagsMutuallyExclusive("schema", "role-only")
×
287
        dbCmd.AddCommand(dbDumpCmd)
×
288
        // Build push command
×
289
        pushFlags := dbPushCmd.Flags()
×
290
        pushFlags.BoolVar(&includeAll, "include-all", false, "Include all migrations not found on remote history table.")
×
291
        pushFlags.BoolVar(&includeRoles, "include-roles", false, "Include custom roles from "+utils.CustomRolesPath+".")
×
292
        pushFlags.BoolVar(&includeSeed, "include-seed", false, "Include seed data from your config.")
×
293
        pushFlags.BoolVar(&dryRun, "dry-run", false, "Print the migrations that would be applied, but don't actually apply them.")
×
294
        pushFlags.String("db-url", "", "Pushes to the database specified by the connection string (must be percent-encoded).")
×
295
        pushFlags.Bool("linked", true, "Pushes to the linked project.")
×
296
        pushFlags.Bool("local", false, "Pushes to the local database.")
×
297
        dbPushCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
298
        pushFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.")
×
299
        cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", pushFlags.Lookup("password")))
×
300
        dbCmd.AddCommand(dbPushCmd)
×
301
        // Build pull command
×
302
        pullFlags := dbPullCmd.Flags()
×
303
        pullFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
×
304
        pullFlags.String("db-url", "", "Pulls from the database specified by the connection string (must be percent-encoded).")
×
305
        pullFlags.Bool("linked", true, "Pulls from the linked project.")
×
306
        pullFlags.Bool("local", false, "Pulls from the local database.")
×
307
        dbPullCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
308
        pullFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.")
×
309
        cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", pullFlags.Lookup("password")))
×
310
        dbCmd.AddCommand(dbPullCmd)
×
311
        // Build remote command
×
312
        remoteFlags := dbRemoteCmd.PersistentFlags()
×
313
        remoteFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
×
314
        remoteFlags.String("db-url", "", "Connect using the specified Postgres URL (must be percent-encoded).")
×
315
        remoteFlags.Bool("linked", true, "Connect to the linked project.")
×
316
        dbRemoteCmd.MarkFlagsMutuallyExclusive("db-url", "linked")
×
317
        remoteFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.")
×
318
        cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", remoteFlags.Lookup("password")))
×
319
        dbRemoteCmd.AddCommand(dbRemoteChangesCmd)
×
320
        dbRemoteCmd.AddCommand(dbRemoteCommitCmd)
×
321
        dbCmd.AddCommand(dbRemoteCmd)
×
322
        // Build reset command
×
323
        resetFlags := dbResetCmd.Flags()
×
324
        resetFlags.String("db-url", "", "Resets the database specified by the connection string (must be percent-encoded).")
×
325
        resetFlags.Bool("linked", false, "Resets the linked project with local migrations.")
×
326
        resetFlags.Bool("local", true, "Resets the local database with local migrations.")
×
327
        resetFlags.BoolVar(&noSeed, "no-seed", false, "Skip running the seed script after reset.")
×
328
        dbResetCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
329
        resetFlags.StringVar(&migrationVersion, "version", "", "Reset up to the specified version.")
×
330
        resetFlags.UintVar(&lastVersion, "last", 0, "Reset up to the last n migration versions.")
×
331
        dbResetCmd.MarkFlagsMutuallyExclusive("version", "last")
×
332
        dbCmd.AddCommand(dbResetCmd)
×
333
        // Build lint command
×
334
        lintFlags := dbLintCmd.Flags()
×
335
        lintFlags.String("db-url", "", "Lints the database specified by the connection string (must be percent-encoded).")
×
336
        lintFlags.Bool("linked", false, "Lints the linked project for schema errors.")
×
337
        lintFlags.Bool("local", true, "Lints the local database for schema errors.")
×
338
        dbLintCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
339
        lintFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
×
340
        lintFlags.Var(&level, "level", "Error level to emit.")
×
341
        lintFlags.Var(&lintFailOn, "fail-on", "Error level to exit with non-zero status.")
×
342
        dbCmd.AddCommand(dbLintCmd)
×
343
        // Build start command
×
344
        startFlags := dbStartCmd.Flags()
×
345
        startFlags.StringVar(&fromBackup, "from-backup", "", "Path to a logical backup file.")
×
346
        dbCmd.AddCommand(dbStartCmd)
×
347
        // Build test command
×
348
        dbCmd.AddCommand(dbTestCmd)
×
349
        testFlags := dbTestCmd.Flags()
×
350
        testFlags.String("db-url", "", "Tests the database specified by the connection string (must be percent-encoded).")
×
351
        testFlags.Bool("linked", false, "Runs pgTAP tests on the linked project.")
×
352
        testFlags.Bool("local", true, "Runs pgTAP tests on the local database.")
×
NEW
353
        testFlags.Bool("use-shadow-db", false, "Creates a temporary database from migrations for running tests in isolation.")
×
354
        dbTestCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
×
355
        rootCmd.AddCommand(dbCmd)
×
356
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc