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

supabase / cli / 16976524820

14 Aug 2025 08:57PM UTC coverage: 54.808% (-0.5%) from 55.271%
16976524820

Pull #3969

github

web-flow
Merge 419dd46ad into b3d509dd5
Pull Request #3969: feat: generate jwt tokens from signing key

11 of 13 new or added lines in 1 file covered. (84.62%)

391 existing lines in 14 files now uncovered.

6196 of 11305 relevant lines covered (54.81%)

6.09 hits per line

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

79.49
/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

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

25
var (
26
        errMissing       = errors.New("No migrations found")
27
        errInSync        = errors.New("No schema changes found")
28
        errConflict      = errors.Errorf("The remote database's migration history does not match local files in %s directory.", utils.MigrationsDir)
29
        suggestExtraPull = fmt.Sprintf(
30
                "The %s and %s schemas are excluded. Run %s again to diff them.",
31
                utils.Bold("auth"),
32
                utils.Bold("storage"),
33
                utils.Aqua("supabase db pull --schema auth,storage"),
34
        )
35
)
36

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

60
func run(ctx context.Context, schema []string, path string, conn *pgx.Conn, fsys afero.Fs) error {
4✔
61
        config := conn.Config().Config
4✔
62
        // 1. Assert `supabase/migrations` and `schema_migrations` are in sync.
4✔
63
        if err := assertRemoteInSync(ctx, conn, fsys); errors.Is(err, errMissing) {
5✔
64
                // Not passing down schemas to avoid pulling in managed schemas
1✔
65
                if err = dumpRemoteSchema(ctx, path, config, fsys); err == nil {
2✔
66
                        utils.CmdSuggestion = suggestExtraPull
1✔
67
                }
1✔
68
                return err
1✔
69
        } else if err != nil {
4✔
70
                return err
1✔
71
        }
1✔
72
        // 2. Fetch remote schema changes
73
        defaultSchema := len(schema) == 0
2✔
74
        if defaultSchema {
3✔
75
                var err error
1✔
76
                schema, err = migration.ListUserSchemas(ctx, conn)
1✔
77
                if err != nil {
2✔
78
                        return err
1✔
79
                }
1✔
80
        }
81
        err := diffRemoteSchema(ctx, schema, path, config, fsys)
1✔
82
        if defaultSchema && (err == nil || errors.Is(err, errInSync)) {
1✔
UNCOV
83
                utils.CmdSuggestion = suggestExtraPull
×
UNCOV
84
        }
×
85
        return err
1✔
86
}
87

88
func dumpRemoteSchema(ctx context.Context, path string, config pgconn.Config, fsys afero.Fs) error {
1✔
89
        // Special case if this is the first migration
1✔
90
        fmt.Fprintln(os.Stderr, "Dumping schema from remote database...")
1✔
91
        if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(path)); err != nil {
1✔
UNCOV
92
                return err
×
UNCOV
93
        }
×
94
        f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
1✔
95
        if err != nil {
1✔
UNCOV
96
                return errors.Errorf("failed to open dump file: %w", err)
×
UNCOV
97
        }
×
98
        defer f.Close()
1✔
99
        return migration.DumpSchema(ctx, config, f, dump.DockerExec)
1✔
100
}
101

102
func diffRemoteSchema(ctx context.Context, schema []string, path string, config pgconn.Config, fsys afero.Fs) error {
1✔
103
        // Diff remote db (source) & shadow db (target) and write it as a new migration.
1✔
104
        output, err := diff.DiffDatabase(ctx, schema, config, os.Stderr, fsys, diff.DiffSchemaMigra)
1✔
105
        if err != nil {
2✔
106
                return err
1✔
107
        }
1✔
UNCOV
108
        if len(output) == 0 {
×
UNCOV
109
                return errors.New(errInSync)
×
UNCOV
110
        }
×
111
        if err := utils.WriteFile(path, []byte(output), fsys); err != nil {
×
112
                return errors.Errorf("failed to write dump file: %w", err)
×
113
        }
×
114
        return nil
×
115
}
116

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

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