From 5678339af0867ee7a627d62de10a2cdeed7f4d2a Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Sat, 25 Mar 2023 21:48:19 +0200 Subject: [PATCH] added migrate history-sync command --- CHANGELOG.md | 2 ++ plugins/migratecmd/migratecmd.go | 1 + tools/migrate/runner.go | 23 +++++++++++++++ tools/migrate/runner_test.go | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a6ffcf..55f57aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ - Added option to explicitly set the record id from the Admin UI ([#2118](https://github.com/pocketbase/pocketbase/issues/2118)). +- Added `migrate history-sync` command to clean `_migrations` history table from deleted migration files references. + - Added `core.RecordAuthWithOAuth2Event.IsNewRecord` bool field to indicate whether the OAuth2 action created a new auth record. - **!** Renamed `daos.GetTableColumns()` to `daos.TableColumns()` for consistency with the other Dao table related helpers. diff --git a/plugins/migratecmd/migratecmd.go b/plugins/migratecmd/migratecmd.go index 33df0b9e..41e7ad0a 100644 --- a/plugins/migratecmd/migratecmd.go +++ b/plugins/migratecmd/migratecmd.go @@ -123,6 +123,7 @@ func (p *plugin) createCommand() *cobra.Command { - down [number] - reverts the last [number] applied migrations - create name - creates new blank migration template file - collections - creates new migration file with snapshot of the local collections configuration +- history-sync - ensures that the _migrations history table doesn't have references to deleted migration files ` command := &cobra.Command{ diff --git a/tools/migrate/runner.go b/tools/migrate/runner.go index 7b23ce38..ee504f68 100644 --- a/tools/migrate/runner.go +++ b/tools/migrate/runner.go @@ -107,6 +107,14 @@ func (r *Runner) Run(args ...string) error { } } + return nil + case "history-sync": + if err := r.removeMissingAppliedMigrations(); err != nil { + color.Red(err.Error()) + return err + } + + color.Green("The %s table was synced with the available migrations.", r.tableName) return nil default: return fmt.Errorf("Unsupported command: %q\n", cmd) @@ -252,3 +260,18 @@ func (r *Runner) lastAppliedMigrations(limit int) ([]string, error) { return files, nil } + +func (r *Runner) removeMissingAppliedMigrations() error { + loadedMigrations := r.migrationsList.Items() + + names := make([]any, len(loadedMigrations)) + for i, migration := range loadedMigrations { + names[i] = migration.File + } + + _, err := r.db.Delete(r.tableName, dbx.Not(dbx.HashExp{ + "file": names, + })).Execute() + + return err +} diff --git a/tools/migrate/runner_test.go b/tools/migrate/runner_test.go index 03bb1c13..935199f6 100644 --- a/tools/migrate/runner_test.go +++ b/tools/migrate/runner_test.go @@ -138,6 +138,56 @@ func TestRunnerUpAndDown(t *testing.T) { } } +func TestHistorySync(t *testing.T) { + testDB, err := createTestDB() + if err != nil { + t.Fatal(err) + } + defer testDB.Close() + + // mock migrations history + l := MigrationsList{} + l.Register(func(db dbx.Builder) error { + return nil + }, func(db dbx.Builder) error { + return nil + }, "1_test") + l.Register(func(db dbx.Builder) error { + return nil + }, func(db dbx.Builder) error { + return nil + }, "2_test") + l.Register(func(db dbx.Builder) error { + return nil + }, func(db dbx.Builder) error { + return nil + }, "3_test") + + r, err := NewRunner(testDB.DB, l) + if err != nil { + t.Fatalf("Failed to initialize the runner: %v", err) + } + + if _, err := r.Up(); err != nil { + t.Fatalf("Failed to apply the mock migrations: %v", err) + } + + if !r.isMigrationApplied(testDB.DB, "2_test") { + t.Fatalf("Expected 2_test migration to be applied") + } + + // mock deleted migrations + r.migrationsList.list = []*Migration{r.migrationsList.list[0], r.migrationsList.list[2]} + + if err := r.removeMissingAppliedMigrations(); err != nil { + t.Fatalf("Failed to remove missing applied migrations: %v", err) + } + + if r.isMigrationApplied(testDB.DB, "2_test") { + t.Fatalf("Expected 2_test migration to NOT be applied") + } +} + // ------------------------------------------------------------------- // Helpers // -------------------------------------------------------------------