diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f2c915..b282e677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Changed file field to not use `dataTransfer.effectAllowed` when dropping files since it is not reliable and consistent across different OS and browsers ([#2541](https://github.com/pocketbase/pocketbase/issues/2541)). +- Auto register the initial generated snapshot migration to prevent incorrectly reapplying the snapshot on container restart ([#2551](https://github.com/pocketbase/pocketbase/discussions/2551)). + ## v0.16.1 diff --git a/plugins/migratecmd/migratecmd.go b/plugins/migratecmd/migratecmd.go index 363cd3f3..614d83d7 100644 --- a/plugins/migratecmd/migratecmd.go +++ b/plugins/migratecmd/migratecmd.go @@ -23,6 +23,7 @@ import ( "time" "github.com/AlecAivazis/survey/v2" + "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/migrations" "github.com/pocketbase/pocketbase/models" @@ -102,7 +103,19 @@ func Register(app core.App, rootCmd *cobra.Command, options *Options) error { // migrations but there is already at least 1 collection created, // to ensure that the automigrate will work with up-to-date collections data if !p.hasCustomMigrations() && len(cachedCollections) > 1 { - p.migrateCollectionsHandler(nil, false) + snapshotFile, err := p.migrateCollectionsHandler(nil, false) + if err != nil { + return err + } + + // insert the snapshot migration entry + _, insertErr := p.app.Dao().NonconcurrentDB().Insert(migrate.DefaultMigrationsTable, dbx.Params{ + "file": snapshotFile, + "applied": time.Now().Unix(), + }).Execute() + if insertErr != nil { + return insertErr + } } return nil @@ -141,11 +154,11 @@ func (p *plugin) createCommand() *cobra.Command { switch cmd { case "create": - if err := p.migrateCreateHandler("", args[1:], true); err != nil { + if _, err := p.migrateCreateHandler("", args[1:], true); err != nil { return err } case "collections": - if err := p.migrateCollectionsHandler(args[1:], true); err != nil { + if _, err := p.migrateCollectionsHandler(args[1:], true); err != nil { return err } default: @@ -166,18 +179,17 @@ func (p *plugin) createCommand() *cobra.Command { return command } -func (p *plugin) migrateCreateHandler(template string, args []string, interactive bool) error { +func (p *plugin) migrateCreateHandler(template string, args []string, interactive bool) (string, error) { if len(args) < 1 { - return fmt.Errorf("Missing migration file name") + return "", fmt.Errorf("Missing migration file name") } name := args[0] dir := p.options.Dir - resultFilePath := path.Join( - dir, - fmt.Sprintf("%d_%s.%s", time.Now().Unix(), inflector.Snakecase(name), p.options.TemplateLang), - ) + filename := fmt.Sprintf("%d_%s.%s", time.Now().Unix(), inflector.Snakecase(name), p.options.TemplateLang) + + resultFilePath := path.Join(dir, filename) if interactive { confirm := false @@ -187,7 +199,7 @@ func (p *plugin) migrateCreateHandler(template string, args []string, interactiv survey.AskOne(prompt, &confirm) if !confirm { fmt.Println("The command has been cancelled") - return nil + return "", nil } } @@ -200,34 +212,34 @@ func (p *plugin) migrateCreateHandler(template string, args []string, interactiv template, templateErr = p.goBlankTemplate() } if templateErr != nil { - return fmt.Errorf("Failed to resolve create template: %v\n", templateErr) + return "", fmt.Errorf("Failed to resolve create template: %v\n", templateErr) } } // ensure that the migrations dir exist if err := os.MkdirAll(dir, os.ModePerm); err != nil { - return err + return "", err } // save the migration file if err := os.WriteFile(resultFilePath, []byte(template), 0644); err != nil { - return fmt.Errorf("Failed to save migration file %q: %v\n", resultFilePath, err) + return "", fmt.Errorf("Failed to save migration file %q: %v\n", resultFilePath, err) } if interactive { fmt.Printf("Successfully created file %q\n", resultFilePath) } - return nil + return filename, nil } -func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) error { +func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) (string, error) { createArgs := []string{"collections_snapshot"} createArgs = append(createArgs, args...) collections := []*models.Collection{} if err := p.app.Dao().CollectionQuery().OrderBy("created ASC").All(&collections); err != nil { - return fmt.Errorf("Failed to fetch migrations list: %v", err) + return "", fmt.Errorf("Failed to fetch migrations list: %v", err) } var template string @@ -238,7 +250,7 @@ func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) erro template, templateErr = p.goSnapshotTemplate(collections) } if templateErr != nil { - return fmt.Errorf("Failed to resolve template: %v", templateErr) + return "", fmt.Errorf("Failed to resolve template: %v", templateErr) } return p.migrateCreateHandler(template, createArgs, interactive) diff --git a/plugins/migratecmd/migratecmd_test.go b/plugins/migratecmd/migratecmd_test.go index afe2655c..baced7c0 100644 --- a/plugins/migratecmd/migratecmd_test.go +++ b/plugins/migratecmd/migratecmd_test.go @@ -6,11 +6,14 @@ import ( "strings" "testing" + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/daos" "github.com/pocketbase/pocketbase/models" "github.com/pocketbase/pocketbase/models/schema" "github.com/pocketbase/pocketbase/plugins/migratecmd" "github.com/pocketbase/pocketbase/tests" + "github.com/pocketbase/pocketbase/tools/migrate" "github.com/pocketbase/pocketbase/tools/types" ) @@ -776,3 +779,36 @@ func TestAutomigrateCollectionNoChanges(t *testing.T) { } } } + +func TestInitialAutoSnapshot(t *testing.T) { + app, _ := tests.NewTestApp() + defer app.Cleanup() + + migrationsDir := filepath.Join(app.DataDir(), "_test_auto_snapshot_") + + migratecmd.MustRegister(app, nil, &migratecmd.Options{ + TemplateLang: migratecmd.TemplateLangJS, + Automigrate: true, + Dir: migrationsDir, + }) + + app.Bootstrap() + + app.OnBeforeServe().Trigger(&core.ServeEvent{ + App: app, + }) + + var foundFiles []string + + err := app.Dao().NonconcurrentDB().Select("file"). + From(migrate.DefaultMigrationsTable). + Where(dbx.NewExp("file like '%collections_snapshot.js'")). + Column(&foundFiles) + if err != nil { + t.Fatal(err) + } + + if len(foundFiles) != 1 { + t.Fatalf("Expected 1 collections_snapshot migration, found %v", foundFiles) + } +} diff --git a/tests/data/data.db b/tests/data/data.db index 8e17df2f..6b55e636 100644 Binary files a/tests/data/data.db and b/tests/data/data.db differ