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

supabase / cli / 9232878727

25 May 2024 04:23AM UTC coverage: 60.309% (-0.07%) from 60.378%
9232878727

push

github

web-flow
fix: timeout waiting for input from non-interactive prompt (#2318)

57 of 110 new or added lines in 19 files covered. (51.82%)

8 existing lines in 6 files now uncovered.

6865 of 11383 relevant lines covered (60.31%)

646.74 hits per line

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

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

3
import (
4
        "context"
5
        _ "embed"
6
        "fmt"
7
        "math"
8
        "os"
9
        "strconv"
10

11
        "github.com/go-errors/errors"
12
        "github.com/jackc/pgconn"
13
        "github.com/jackc/pgx/v4"
14
        "github.com/spf13/afero"
15
        "github.com/supabase/cli/internal/db/diff"
16
        "github.com/supabase/cli/internal/db/dump"
17
        "github.com/supabase/cli/internal/db/reset"
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
)
23

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

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

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

93
func dumpRemoteSchema(p utils.Program, ctx context.Context, path string, config pgconn.Config, fsys afero.Fs) error {
1✔
94
        // Special case if this is the first migration
1✔
95
        p.Send(utils.StatusMsg("Dumping schema from remote database..."))
1✔
96
        if err := utils.MkdirIfNotExistFS(fsys, utils.MigrationsDir); err != nil {
1✔
97
                return err
×
98
        }
×
99
        f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
1✔
100
        if err != nil {
1✔
101
                return errors.Errorf("failed to open dump file: %w", err)
×
102
        }
×
103
        defer f.Close()
1✔
104
        return dump.DumpSchema(ctx, config, nil, false, false, f)
1✔
105
}
106

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

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

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