Skip to content

Commit 55723ef

Browse files
committed
[ADD] migration up and tests
1 parent 5d236f4 commit 55723ef

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

internal/migration/up/up.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func Run(ctx context.Context, includeAll bool, config pgconn.Config, fsys afero.
2323
if err != nil {
2424
return err
2525
}
26+
2627
return migration.ApplyMigrations(ctx, pending, conn, afero.NewIOFS(fsys))
2728
}
2829

@@ -45,6 +46,11 @@ func GetPendingMigrations(ctx context.Context, includeAll bool, conn *pgx.Conn,
4546
}
4647
utils.CmdSuggestion = suggestIgnoreFlag(diff)
4748
}
49+
repeatableMigrations, err := migration.ListRepeatableMigrations(utils.MigrationsDir, afero.NewIOFS(fsys))
50+
if err != nil {
51+
return nil, err
52+
}
53+
diff = append(diff, repeatableMigrations...)
4854
return diff, err
4955
}
5056

pkg/migration/list.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"regexp"
1010
"strconv"
11+
"strings"
1112

1213
"github.com/go-errors/errors"
1314
"github.com/jackc/pgconn"
@@ -45,6 +46,10 @@ func ListLocalMigrations(migrationsDir string, fsys fs.FS, filter ...func(string
4546
fmt.Fprintf(os.Stderr, "Skipping migration %s... (replace \"init\" with a different file name to apply this migration)\n", filename)
4647
continue
4748
}
49+
if strings.HasPrefix(filename, "r_") {
50+
// silently skip repeatable migrations
51+
continue
52+
}
4853
matches := migrateFilePattern.FindStringSubmatch(filename)
4954
if len(matches) == 0 {
5055
fmt.Fprintf(os.Stderr, "Skipping migration %s... (file name must match pattern \"<timestamp>_name.sql\")\n", filename)
@@ -60,6 +65,24 @@ func ListLocalMigrations(migrationsDir string, fsys fs.FS, filter ...func(string
6065
return clean, nil
6166
}
6267

68+
func ListRepeatableMigrations(migrationsDir string, fsys fs.FS) ([]string, error) {
69+
localMigrations, err := fs.ReadDir(fsys, migrationsDir)
70+
if err != nil && !errors.Is(err, os.ErrNotExist) {
71+
return nil, errors.Errorf("failed to read directory: %w", err)
72+
}
73+
var repeatable []string
74+
75+
for _, migration := range localMigrations {
76+
filename := migration.Name()
77+
if strings.HasPrefix(filename, "r_") && strings.HasSuffix(filename, ".sql") {
78+
path := filepath.Join(migrationsDir, filename)
79+
repeatable = append(repeatable, path)
80+
}
81+
}
82+
83+
return repeatable, nil
84+
}
85+
6386
var initSchemaPattern = regexp.MustCompile(`([0-9]{14})_init\.sql`)
6487

6588
func shouldSkip(name string) bool {

pkg/migration/list_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,44 @@ func TestLocalMigrations(t *testing.T) {
9090
assert.ErrorContains(t, err, "failed to read directory:")
9191
})
9292
}
93+
94+
func TestRepeatableMigrations(t *testing.T) {
95+
t.Run("loads repeatable migrations", func(t *testing.T) {
96+
// Setup in-memory fs
97+
files := []string{
98+
"r_test_view.sql",
99+
"r_test_function.sql",
100+
}
101+
fsys := fs.MapFS{}
102+
for _, name := range files {
103+
fsys[name] = &fs.MapFile{}
104+
}
105+
// Run test
106+
versions, err := ListRepeatableMigrations(".", fsys)
107+
// Check error
108+
assert.NoError(t, err)
109+
assert.ElementsMatch(t, files, versions)
110+
})
111+
112+
t.Run("ignores files without 'r_' prefix", func(t *testing.T) {
113+
// Setup in-memory fs
114+
fsys := fs.MapFS{
115+
"20211208000000_init.sql": &fs.MapFile{},
116+
"r_invalid.ts": &fs.MapFile{},
117+
}
118+
// Run test
119+
versions, err := ListRepeatableMigrations(".", fsys)
120+
// Check error
121+
assert.NoError(t, err)
122+
assert.Empty(t, versions)
123+
})
124+
125+
t.Run("throws error on open failure", func(t *testing.T) {
126+
// Setup in-memory fs
127+
fsys := fs.MapFS{"migrations": &fs.MapFile{}}
128+
// Run test
129+
_, err := ListRepeatableMigrations("migrations", fsys)
130+
// Check error
131+
assert.ErrorContains(t, err, "failed to read directory:")
132+
})
133+
}

0 commit comments

Comments
 (0)