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

supabase / cli / 19140348754

06 Nov 2025 03:13PM UTC coverage: 54.622% (+0.02%) from 54.604%
19140348754

Pull #4320

github

web-flow
Merge 1aa4e5564 into d736cfd4d
Pull Request #4320: show api keys with deprecated flag

1 of 9 new or added lines in 3 files covered. (11.11%)

100 existing lines in 8 files now uncovered.

6376 of 11673 relevant lines covered (54.62%)

6.11 hits per line

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

72.81
/internal/db/pull/pull.go
1
package pull
2

3
import (
4
        "context"
5
        _ "embed"
6
        "fmt"
7
        "math"
8
        "os"
9
        "path/filepath"
10
        "strconv"
11
        "strings"
12

13
        "github.com/go-errors/errors"
14
        "github.com/jackc/pgconn"
15
        "github.com/jackc/pgx/v4"
16
        "github.com/spf13/afero"
17
        "github.com/supabase/cli/internal/db/diff"
18
        "github.com/supabase/cli/internal/db/dump"
19
        "github.com/supabase/cli/internal/migration/list"
20
        "github.com/supabase/cli/internal/migration/new"
21
        "github.com/supabase/cli/internal/migration/repair"
22
        "github.com/supabase/cli/internal/utils"
23
        "github.com/supabase/cli/pkg/migration"
24
)
25

26
var (
27
        errMissing  = errors.New("No migrations found")
28
        errInSync   = errors.New("No schema changes found")
29
        errConflict = errors.Errorf("The remote database's migration history does not match local files in %s directory.", utils.MigrationsDir)
30
)
31

32
func Run(ctx context.Context, schema []string, config pgconn.Config, name string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
2✔
33
        // 1. Check postgres connection
2✔
34
        conn, err := utils.ConnectByConfig(ctx, config, options...)
2✔
35
        if err != nil {
3✔
36
                return err
1✔
37
        }
1✔
38
        defer conn.Close(context.Background())
1✔
39
        // 2. Pull schema
1✔
40
        timestamp := utils.GetCurrentTimestamp()
1✔
41
        path := new.GetMigrationPath(timestamp, name)
1✔
42
        if err := run(ctx, schema, path, conn, fsys); err != nil {
2✔
43
                return err
1✔
44
        }
1✔
45
        // 3. Insert a row to `schema_migrations`
UNCOV
46
        fmt.Fprintln(os.Stderr, "Schema written to "+utils.Bold(path))
×
UNCOV
47
        if shouldUpdate, err := utils.NewConsole().PromptYesNo(ctx, "Update remote migration history table?", true); err != nil {
×
UNCOV
48
                return err
×
49
        } else if shouldUpdate {
×
50
                return repair.UpdateMigrationTable(ctx, conn, []string{timestamp}, repair.Applied, false, fsys)
×
51
        }
×
52
        return nil
×
53
}
54

55
func run(ctx context.Context, schema []string, path string, conn *pgx.Conn, fsys afero.Fs) error {
3✔
56
        config := conn.Config().Config
3✔
57
        // 1. Assert `supabase/migrations` and `schema_migrations` are in sync.
3✔
58
        if err := assertRemoteInSync(ctx, conn, fsys); errors.Is(err, errMissing) {
4✔
59
                // Ignore schemas flag when working on the initial pull
1✔
60
                if err = dumpRemoteSchema(ctx, path, config, fsys); err != nil {
1✔
UNCOV
61
                        return err
×
UNCOV
62
                }
×
63
                // Run a second pass to pull in changes from default privileges and managed schemas
64
                if err = diffRemoteSchema(ctx, nil, path, config, fsys); errors.Is(err, errInSync) {
1✔
65
                        err = nil
×
UNCOV
66
                }
×
67
                return err
1✔
68
        } else if err != nil {
3✔
69
                return err
1✔
70
        }
1✔
71
        // 2. Fetch remote schema changes
72
        return diffRemoteSchema(ctx, schema, path, config, fsys)
1✔
73
}
74

75
func dumpRemoteSchema(ctx context.Context, path string, config pgconn.Config, fsys afero.Fs) error {
1✔
76
        // Special case if this is the first migration
1✔
77
        fmt.Fprintln(os.Stderr, "Dumping schema from remote database...")
1✔
78
        if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(path)); err != nil {
1✔
UNCOV
79
                return err
×
80
        }
×
81
        f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
1✔
82
        if err != nil {
1✔
UNCOV
83
                return errors.Errorf("failed to open dump file: %w", err)
×
UNCOV
84
        }
×
85
        defer f.Close()
1✔
86
        return migration.DumpSchema(ctx, config, f, dump.DockerExec)
1✔
87
}
88

89
func diffRemoteSchema(ctx context.Context, schema []string, path string, config pgconn.Config, fsys afero.Fs) error {
2✔
90
        // Diff remote db (source) & shadow db (target) and write it as a new migration.
2✔
91
        output, err := diff.DiffDatabase(ctx, schema, config, os.Stderr, fsys, diff.DiffSchemaMigra)
2✔
92
        if err != nil {
4✔
93
                return err
2✔
94
        }
2✔
95
        if trimmed := strings.TrimSpace(output); len(trimmed) == 0 {
×
UNCOV
96
                return errors.New(errInSync)
×
UNCOV
97
        }
×
98
        // Append to existing migration file since we run this after dump
UNCOV
99
        f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
×
UNCOV
100
        if err != nil {
×
UNCOV
101
                return errors.Errorf("failed to open migration file: %w", err)
×
UNCOV
102
        }
×
UNCOV
103
        defer f.Close()
×
UNCOV
104
        if _, err := f.WriteString(output); err != nil {
×
UNCOV
105
                return errors.Errorf("failed to write migration file: %w", err)
×
106
        }
×
107
        return nil
×
108
}
109

110
func assertRemoteInSync(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error {
7✔
111
        remoteMigrations, err := migration.ListRemoteMigrations(ctx, conn)
7✔
112
        if err != nil {
8✔
113
                return err
1✔
114
        }
1✔
115
        localMigrations, err := list.LoadLocalVersions(fsys)
6✔
116
        if err != nil {
7✔
117
                return err
1✔
118
        }
1✔
119
        // Find any mismatch between local and remote migrations
120
        var extraRemote, extraLocal []string
5✔
121
        for i, j := 0, 0; i < len(remoteMigrations) || j < len(localMigrations); {
9✔
122
                remoteTimestamp := math.MaxInt
4✔
123
                if i < len(remoteMigrations) {
7✔
124
                        if remoteTimestamp, err = strconv.Atoi(remoteMigrations[i]); err != nil {
3✔
125
                                i++
×
UNCOV
126
                                continue
×
127
                        }
128
                }
129
                localTimestamp := math.MaxInt
4✔
130
                if j < len(localMigrations) {
7✔
131
                        if localTimestamp, err = strconv.Atoi(localMigrations[j]); err != nil {
3✔
UNCOV
132
                                j++
×
UNCOV
133
                                continue
×
134
                        }
135
                }
136
                // Top to bottom chronological order
137
                if localTimestamp < remoteTimestamp {
6✔
138
                        extraLocal = append(extraLocal, localMigrations[j])
2✔
139
                        j++
2✔
140
                } else if remoteTimestamp < localTimestamp {
5✔
141
                        extraRemote = append(extraRemote, remoteMigrations[i])
1✔
142
                        i++
1✔
143
                } else {
2✔
144
                        i++
1✔
145
                        j++
1✔
146
                }
1✔
147
        }
148
        // Suggest delete local migrations / reset migration history
149
        if len(extraRemote)+len(extraLocal) > 0 {
7✔
150
                utils.CmdSuggestion = suggestMigrationRepair(extraRemote, extraLocal)
2✔
151
                return errors.New(errConflict)
2✔
152
        }
2✔
153
        if len(localMigrations) == 0 {
5✔
154
                return errors.New(errMissing)
2✔
155
        }
2✔
156
        return nil
1✔
157
}
158

159
func suggestMigrationRepair(extraRemote, extraLocal []string) string {
2✔
160
        result := fmt.Sprintln("\nMake sure your local git repo is up-to-date. If the error persists, try repairing the migration history table:")
2✔
161
        for _, version := range extraRemote {
3✔
162
                result += fmt.Sprintln(utils.Bold("supabase migration repair --status reverted " + version))
1✔
163
        }
1✔
164
        for _, version := range extraLocal {
4✔
165
                result += fmt.Sprintln(utils.Bold("supabase migration repair --status applied " + version))
2✔
166
        }
2✔
167
        return result
2✔
168
}
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

© 2025 Coveralls, Inc