Skip to content

Commit 69a97f6

Browse files
authored
Merge pull request #3 from DirectDuck/skip_migrations
Add: skip migrations
2 parents 7dd7b79 + ad22471 commit 69a97f6

5 files changed

Lines changed: 159 additions & 24 deletions

File tree

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Run
8484
plan Shows migration files which can be applied
8585
redo Rerun last applied migration from db
8686
run Applies all new migrations
87+
skip Marks migrations done without actually running them.
8788
verify Checks and shows invalid migrations
8889

8990
Flags:
@@ -155,17 +156,21 @@ That is, you can call `pgmigrator --config docs/patches/pgmigrator.toml plan` an
155156
* If there is a NONTR - do not let dryrun run (only up to a certain filename)
156157
* `StatementTimeout` setting is ignored
157158

159+
### Skip
160+
161+
Like `Run`, but without actually running sql migration, only adding migration success record
162+
158163
### Last
159164

160165
Shows the latest database migrations from a table.
161166

162167
**Output**
163168

164-
Showing last migrations in public.pgMigrations:
165-
34 - 2022-08-30 22:25:03 (ERR) > 2022-07-30-compilations-NONTR.sql
166-
33 - 2022-08-30 22:25:03 (3s) > 2022-07-30-compilations-fix.sql
167-
32 - 2022-08-30 22:25:34 (1s) > 2022-07-28-jwlinks.sql
168-
31 - 2022-08-30 22:23:12 (5m 4s) > 2022-07-18-movieComments.sql
169+
Showing last migrations in public.pgMigrations:
170+
34 - 2022-08-30 22:25:03 (ERR) > 2022-07-30-compilations-NONTR.sql
171+
33 - 2022-08-30 22:25:03 (3s) > 2022-07-30-compilations-fix.sql
172+
32 - 2022-08-30 22:25:34 (1s) > 2022-07-28-jwlinks.sql
173+
31 - 2022-08-30 22:23:12 (5m 4s) > 2022-07-18-movieComments.sql
169174

170175
### Verify
171176

README.ru.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ A: Цель - простая утилита, которая работает с
8686
plan Shows migration files which can be applied
8787
redo Rerun last applied migration from db
8888
run Applies all new migrations
89+
skip Marks migrations done without actually running them.
8990
verify Checks and shows invalid migrations
9091

9192
Flags:
@@ -157,6 +158,10 @@ A: Цель - простая утилита, которая работает с
157158

158159
Как в `Run`, только в конце выводим сообщение о ROLLBACK.
159160

161+
### Skip
162+
163+
Как и `Run`, но без выполнения sql миграции. Только добавление записи о том, что миграция применена
164+
160165
### Last
161166

162167
Показываем последние транзакции.

pkg/app/app.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func New(rootCmd *cobra.Command, mg *migrator.Migrator, cfg Config) App {
4646
}
4747

4848
func (a App) Run(ctx context.Context) error {
49-
a.rootCmd.AddCommand(a.initCmd(), a.dryRunCmd(ctx), a.lastCmd(ctx), a.planCmd(ctx), a.redoCmd(ctx), a.runCmd(ctx), a.verifyCmd(ctx))
49+
a.rootCmd.AddCommand(a.initCmd(), a.dryRunCmd(ctx), a.lastCmd(ctx), a.planCmd(ctx), a.redoCmd(ctx), a.runCmd(ctx), a.verifyCmd(ctx), a.skipCmd(ctx))
5050
a.rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
5151
if cmd.Name() == "init" || cmd.Name() == "help" {
5252
return
@@ -262,6 +262,46 @@ If <count> applied, runs only <count> migrations. By default: 5`,
262262
}
263263
}
264264

265+
// skipCmd marks migrations done without actually running them
266+
func (a App) skipCmd(ctx context.Context) *cobra.Command {
267+
return &cobra.Command{
268+
Use: "skip [<count>]",
269+
Short: "Marks migrations done without actually running them",
270+
Long: `Marks migrations done without actually running them.
271+
If <count> applied, marks only first <count> migrations displayed in plan. Default <count> = 5.`,
272+
Run: func(cmd *cobra.Command, args []string) {
273+
// get list of migrations
274+
mm, err := a.mg.Plan(ctx)
275+
if err != nil {
276+
log.Fatalf("Execute command failed: %v\n", err)
277+
} else if len(mm) == 0 {
278+
fmt.Println("No new migrations were found.")
279+
return
280+
}
281+
282+
// calculate count
283+
cnt, err := count(args)
284+
if err != nil {
285+
log.Fatal("invalid argument")
286+
} else if cnt > len(mm) {
287+
cnt = len(mm)
288+
}
289+
290+
// skip migrations
291+
ch := make(chan string)
292+
wg := &sync.WaitGroup{}
293+
go readCh(ch, wg)
294+
fmt.Println("Skipping migrations...")
295+
if err = a.mg.Skip(ctx, mm[:cnt], ch); err != nil {
296+
log.Fatalf("Skip migration error: %v", err)
297+
return
298+
}
299+
wg.Wait()
300+
fmt.Println("Done")
301+
},
302+
}
303+
}
304+
265305
// redoCmd rerun last migration
266306
func (a App) redoCmd(ctx context.Context) *cobra.Command {
267307
return &cobra.Command{

pkg/migrator/migrator.go

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ func NewMigrator(db *pg.DB, cfg Config, rootDir string) *Migrator {
3434
return m
3535
}
3636

37+
// writeMigrationToDB inserts log that migration was completed in postgres
38+
func writeMigrationToDB(ctx context.Context, mg Migration, tx *pg.Tx, start time.Time) error {
39+
finish := time.Now()
40+
pm := mg.ToDB()
41+
pm.StartedAt = start
42+
pm.FinishedAt = &finish
43+
44+
if _, err := tx.ModelContext(ctx, pm).Insert(); err != nil {
45+
return fmt.Errorf(`add new migration "%s" failed: %w`, mg.Filename, err)
46+
}
47+
return nil
48+
}
49+
3750
// readAllFiles read files from migrator root dir and return its filenames
3851
func (m *Migrator) readAllFiles() ([]string, error) {
3952
dir, err := os.Open(m.rootDir)
@@ -191,17 +204,7 @@ func (m *Migrator) applyMigration(ctx context.Context, mg Migration) (err error)
191204
return fmt.Errorf(`apply migration failed: %w`, err)
192205
}
193206

194-
// insert into pgMigrations
195-
finish := time.Now()
196-
pm := mg.ToDB()
197-
pm.StartedAt = start
198-
pm.FinishedAt = &finish
199-
200-
if _, err = tx.ModelContext(ctx, pm).Insert(); err != nil {
201-
return fmt.Errorf(`add new migration failed: %w`, err)
202-
}
203-
204-
return nil
207+
return writeMigrationToDB(ctx, mg, tx, start)
205208
}
206209

207210
// setStatementTimeout set statement timeout to transaction connection
@@ -293,14 +296,52 @@ func (m *Migrator) dryRunMigrations(ctx context.Context, mm Migrations, chCurren
293296
return fmt.Errorf(`apply migration "%s" failed: %w`, mg.Filename, err)
294297
}
295298

296-
// insert into pgMigrations
297-
finish := time.Now()
298-
pm := mg.ToDB()
299-
pm.StartedAt = start
300-
pm.FinishedAt = &finish
299+
if err = writeMigrationToDB(ctx, mg, tx, start); err != nil {
300+
return err
301+
}
302+
}
301303

302-
if _, err = tx.ModelContext(ctx, pm).Insert(); err != nil {
303-
return fmt.Errorf(`add new migration "%s" failed: %w`, mg.Filename, err)
304+
return nil
305+
}
306+
307+
// Skip marks migrations as completed
308+
func (m *Migrator) Skip(ctx context.Context, filenames []string, chCurrentFile chan string) error {
309+
defer close(chCurrentFile)
310+
311+
// create migration table if not exists
312+
if err := m.createMigratorTable(ctx); err != nil {
313+
return err
314+
}
315+
316+
// prepare migrations
317+
mm, err := m.newMigrations(filenames)
318+
if err != nil {
319+
return fmt.Errorf("prepare migrations failed: %w", err)
320+
}
321+
322+
// skip migrations
323+
if err := m.skipMigrations(ctx, mm, chCurrentFile); err != nil {
324+
return fmt.Errorf("skip migrations failed: %w", err)
325+
}
326+
return nil
327+
}
328+
329+
func (m *Migrator) skipMigrations(ctx context.Context, mm Migrations, chCurrentFile chan string) (err error) {
330+
var tx *pg.Tx
331+
tx, err = m.db.Begin()
332+
if err != nil {
333+
return fmt.Errorf(`begin transaction failed: %w`, err)
334+
}
335+
336+
defer func() {
337+
err = finishTxOnErr(tx, err)
338+
}()
339+
340+
// write migrations to pgMigrations table
341+
for _, mg := range mm {
342+
chCurrentFile <- mg.Filename
343+
if err = writeMigrationToDB(ctx, mg, tx, time.Now()); err != nil {
344+
return err
304345
}
305346
}
306347

pkg/migrator/migrator_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,50 @@ func TestMigrator_DryRun(t *testing.T) {
356356
})
357357
}
358358

359+
func TestMigrator_skipMigrations(t *testing.T) {
360+
ctx := context.Background()
361+
Convey("TestMigrator_skipMigrations", t, func() {
362+
err := recreateSchema()
363+
So(err, ShouldBeNil)
364+
err = testMigrator.createMigratorTable(ctx)
365+
So(err, ShouldBeNil)
366+
367+
dirFiles := []string{
368+
"2022-12-12-01-create-table-statuses.sql",
369+
"2022-12-12-02-create-table-news.sql",
370+
}
371+
mm, err := testMigrator.newMigrations(dirFiles)
372+
So(err, ShouldBeNil)
373+
374+
ch := make(chan string)
375+
go readFromCh(ch, t)
376+
err = testMigrator.skipMigrations(ctx, mm, ch)
377+
So(err, ShouldBeNil)
378+
379+
for _, mg := range mm {
380+
var pm PgMigration
381+
err = testMigrator.db.ModelContext(ctx, &pm).Where(`"filename" = ?`, mg.Filename).Select()
382+
So(err, ShouldBeNil)
383+
So(pm, ShouldNotBeNil)
384+
So(pm.FinishedAt, ShouldNotBeEmpty)
385+
}
386+
})
387+
}
388+
389+
func TestMigrator_Skip(t *testing.T) {
390+
ctx := context.Background()
391+
Convey("TestMigrator_Skip", t, func() {
392+
err := recreateSchema()
393+
So(err, ShouldBeNil)
394+
filenames, err := testMigrator.Plan(ctx)
395+
So(err, ShouldBeNil)
396+
ch := make(chan string)
397+
go readFromCh(ch, t)
398+
err = testMigrator.Skip(ctx, filenames, ch)
399+
So(err, ShouldBeNil)
400+
})
401+
}
402+
359403
func TestMigrator_compareMD5Sum(t *testing.T) {
360404
Convey("TestMigrator_compareMD5Sum", t, func() {
361405
Convey("check correct", func() {

0 commit comments

Comments
 (0)