diff --git a/README.md b/README.md index b83d3b9..e89e4f4 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ $ touch bolt.toml 2. Add the following contents to the file: ```toml -[connection] +[bolt.db.connection] host = "localhost" port = 5432 user = "bolt_user" @@ -230,16 +230,17 @@ Bolt attempts to find a `bolt.toml` file in your current working directory or in any parent directory. ```toml +[bolt.migrations] # The directory where your migration scripts are located. # Defaults to "migrations". This directory is relative to # the current working directory. You may also use an absolute # path. -migrations_dir = "migrations" +directory_path = "migrations" # Connection parameters for the database Bolt will be # applying migrations to. All connection parameters are # required. -[connection] +[bolt.db.connection] # The host to use to connect to your database. host = # The port to use to connect to your database. @@ -260,13 +261,13 @@ driver = All configuration file settings have corresponding environment variables. -- `BOLT_MIGRATIONS_DIR` -- `BOLT_CONNECTION_HOST` -- `BOLT_CONNECTION_PORT` -- `BOLT_CONNECTION_USER` -- `BOLT_CONNECTION_PASSWORD` -- `BOLT_CONNECTION_DBNAME` -- `BOLT_CONNECTION_DRIVER` +- `BOLT_MIGRATIONS_DIR_PATH` +- `BOLT_DB_CONN_HOST` +- `BOLT_DB_CONN_PORT` +- `BOLT_DB_CONN_USER` +- `BOLT_DB_CONN_PASSWORD` +- `BOLT_DB_CONN_DBNAME` +- `BOLT_DB_CONN_DRIVER` ### Commands @@ -299,7 +300,7 @@ up [-version|-v]: ```bash $ bolt help up down [-version|-v]: - Downgrade migrations against the database. + Downgrade migrations against the database -v string alias for -version -version string diff --git a/internal/commands/down.go b/internal/commands/down.go index d9965b5..262e229 100644 --- a/internal/commands/down.go +++ b/internal/commands/down.go @@ -69,7 +69,7 @@ func (cmd *DownCmd) Execute( return subcommands.ExitFailure } - migrationFsRepo, err := repositories.NewMigrationFsRepo(cfg.MigrationsDir) + migrationFsRepo, err := repositories.NewMigrationFsRepo(&cfg.Migrations) if err != nil { consoleOutputter.Error(err.Error()) return subcommands.ExitFailure diff --git a/internal/commands/new.go b/internal/commands/new.go index 864de4e..a4eeff4 100644 --- a/internal/commands/new.go +++ b/internal/commands/new.go @@ -54,7 +54,7 @@ func (cmd *NewCmd) Execute( return subcommands.ExitFailure } - migrationFsRepo, err := repositories.NewMigrationFsRepo(cfg.MigrationsDir) + migrationFsRepo, err := repositories.NewMigrationFsRepo(&cfg.Migrations) if err != nil { consoleOutputter.Error(err.Error()) return subcommands.ExitFailure diff --git a/internal/commands/status.go b/internal/commands/status.go index 2adafef..417e95b 100644 --- a/internal/commands/status.go +++ b/internal/commands/status.go @@ -59,7 +59,7 @@ func (m *StatusCmd) Execute( return subcommands.ExitFailure } - migrationFsRepo, err := repositories.NewMigrationFsRepo(cfg.MigrationsDir) + migrationFsRepo, err := repositories.NewMigrationFsRepo(&cfg.Migrations) if err != nil { consoleOutputter.Error(err.Error()) return subcommands.ExitFailure diff --git a/internal/commands/up.go b/internal/commands/up.go index 57f9109..ba74299 100644 --- a/internal/commands/up.go +++ b/internal/commands/up.go @@ -69,7 +69,7 @@ func (cmd *UpCmd) Execute( return subcommands.ExitFailure } - migrationFsRepo, err := repositories.NewMigrationFsRepo(cfg.MigrationsDir) + migrationFsRepo, err := repositories.NewMigrationFsRepo(&cfg.Migrations) if err != nil { consoleOutputter.Error(err.Error()) return subcommands.ExitFailure diff --git a/internal/configloader/configloader.go b/internal/configloader/configloader.go index d85548a..281ed1f 100644 --- a/internal/configloader/configloader.go +++ b/internal/configloader/configloader.go @@ -27,23 +27,24 @@ var errConfigFileNotFound = errors.New( // This can come from the TOML file or environment variables, // with environment variables taking precedence. type Config struct { - // Relative file path from the bolt configuration file. - // This is optional and will default to "migrations" if - // not specified. - MigrationsDir string `toml:"migrations_dir" envconfig:"BOLT_MIGRATIONS_DIR"` + Migrations MigrationsConfig `toml:"bolt.migrations"` // Information related to how to connect to the database // that is desired to run migrations against. - Connection ConnectionConfig `toml:"connection"` + Connection ConnectionConfig `toml:"bolt.db.connection"` +} + +type MigrationsConfig struct { + DirectoryPath string `toml:"directory_path" envconfig:"BOLT_MIGRATIONS_DIR_PATH"` } type ConnectionConfig struct { - Host string `toml:"host" envconfig:"BOLT_CONNECTION_HOST"` - Port int `toml:"port" envconfig:"BOLT_CONNECTION_PORT"` - User string `toml:"user" envconfig:"BOLT_CONNECTION_USER"` - Password string `toml:"password" envconfig:"BOLT_CONNECTION_PASSWORD"` - DBName string `toml:"dbname" envconfig:"BOLT_CONNECTION_DBNAME"` - Driver string `toml:"driver" envconfig:"BOLT_CONNECTION_DRIVER"` + Host string `toml:"host" envconfig:"BOLT_DB_CONN_HOST"` + Port int `toml:"port" envconfig:"BOLT_DB_CONN_PORT"` + User string `toml:"user" envconfig:"BOLT_DB_CONN_USER"` + Password string `toml:"password" envconfig:"BOLT_DB_CONN_PASSWORD"` + DBName string `toml:"dbname" envconfig:"BOLT_DB_CONN_DBNAME"` + Driver string `toml:"driver" envconfig:"BOLT_DB_CONN_DRIVER"` } func NewConfig() (*Config, error) { @@ -52,7 +53,7 @@ func NewConfig() (*Config, error) { return nil, err } - cfg := Config{MigrationsDir: "migrations"} + cfg := Config{Migrations: MigrationsConfig{DirectoryPath: "migrations"}} if !errors.Is(err, errConfigFileNotFound) { _, err = toml.DecodeFile(filePath, &cfg) if err != nil { diff --git a/internal/configloader/configloader_test.go b/internal/configloader/configloader_test.go index 88c7f94..a41c2be 100644 --- a/internal/configloader/configloader_test.go +++ b/internal/configloader/configloader_test.go @@ -10,18 +10,18 @@ import ( "gotest.tools/v3/assert" ) -func TestNewConfigMigrationsDirDefault(t *testing.T) { +func TestNewConfigMigrationsDirPathDefault(t *testing.T) { bolttest.ChangeCwd(t, os.TempDir()) cfg, err := configloader.NewConfig() assert.NilError(t, err) - assert.Equal(t, cfg.MigrationsDir, "migrations") + assert.Equal(t, cfg.Migrations.DirectoryPath, "migrations") } func TestNewConfigFindsFileAndPopulatesConfigStruct(t *testing.T) { expectedCfg := configloader.Config{ - MigrationsDir: "myfancymigrations", + Migrations: configloader.MigrationsConfig{DirectoryPath: "myfancymigrations"}, Connection: configloader.ConnectionConfig{ Host: "testhost", Port: 1234, @@ -40,7 +40,7 @@ func TestNewConfigFindsFileAndPopulatesConfigStruct(t *testing.T) { func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { fileCfg := configloader.Config{ - MigrationsDir: "cfgmigrations", + Migrations: configloader.MigrationsConfig{DirectoryPath: "cfgmigrations"}, Connection: configloader.ConnectionConfig{ Host: "testhost", Port: 1234, @@ -53,7 +53,7 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { bolttest.CreateConfigFile(t, &fileCfg, "bolt.toml") envCfg := configloader.Config{ - MigrationsDir: "envmigrations", + Migrations: configloader.MigrationsConfig{DirectoryPath: "envmigrations"}, Connection: configloader.ConnectionConfig{ Host: "envtesthost", Port: 4321, @@ -63,13 +63,13 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { Driver: "postgres", }, } - t.Setenv("BOLT_MIGRATIONS_DIR", envCfg.MigrationsDir) - t.Setenv("BOLT_CONNECTION_HOST", envCfg.Connection.Host) - t.Setenv("BOLT_CONNECTION_PORT", fmt.Sprintf("%d", envCfg.Connection.Port)) - t.Setenv("BOLT_CONNECTION_USER", envCfg.Connection.User) - t.Setenv("BOLT_CONNECTION_PASSWORD", envCfg.Connection.Password) - t.Setenv("BOLT_CONNECTION_DBNAME", envCfg.Connection.DBName) - t.Setenv("BOLT_CONNECTION_DRIVER", envCfg.Connection.Driver) + t.Setenv("BOLT_MIGRATIONS_DIR_PATH", envCfg.Migrations.DirectoryPath) + t.Setenv("BOLT_DB_CONN_HOST", envCfg.Connection.Host) + t.Setenv("BOLT_DB_CONN_PORT", fmt.Sprintf("%d", envCfg.Connection.Port)) + t.Setenv("BOLT_DB_CONN_USER", envCfg.Connection.User) + t.Setenv("BOLT_DB_CONN_PASSWORD", envCfg.Connection.Password) + t.Setenv("BOLT_DB_CONN_DBNAME", envCfg.Connection.DBName) + t.Setenv("BOLT_DB_CONN_DRIVER", envCfg.Connection.Driver) cfg, err := configloader.NewConfig() assert.NilError(t, err) diff --git a/internal/configloader/configloader_unix_test.go b/internal/configloader/configloader_unix_test.go index 412ef47..dcd8ebd 100644 --- a/internal/configloader/configloader_unix_test.go +++ b/internal/configloader/configloader_unix_test.go @@ -13,7 +13,7 @@ import ( func TestNewConfigUnixSearchesToRootFilePath(t *testing.T) { expectedCfg := configloader.Config{ - MigrationsDir: "differentmigrationsdir", + Migrations: configloader.MigrationsConfig{DirectoryPath: "differentmigrationsdir"}, } bolttest.CreateConfigFile(t, &expectedCfg, "/bolt.toml") diff --git a/internal/configloader/configloader_windows_test.go b/internal/configloader/configloader_windows_test.go index 5c8cee9..42f2978 100644 --- a/internal/configloader/configloader_windows_test.go +++ b/internal/configloader/configloader_windows_test.go @@ -14,7 +14,7 @@ import ( func TestNewConfigWindowsSearchesToRootFilePath(t *testing.T) { expectedCfg := configloader.Config{ - MigrationsDir: "differentmigrationsdir", + Migrations: configloader.MigrationsConfig{DirectoryPath: "differentmigrationsdir"}, } configPath := filepath.Join(os.Getenv("SystemDrive"), "bolt.toml") bolttest.CreateConfigFile(t, &expectedCfg, configPath) diff --git a/internal/repositories/migration_fs_repo.go b/internal/repositories/migration_fs_repo.go index 1bec146..5478b5c 100644 --- a/internal/repositories/migration_fs_repo.go +++ b/internal/repositories/migration_fs_repo.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/eugenetriguba/bolt/internal/configloader" "github.com/eugenetriguba/bolt/internal/models" ) @@ -25,20 +26,20 @@ type MigrationFsRepo struct { migrationsDirPath string } -func NewMigrationFsRepo(migrationsDirPath string) (*MigrationFsRepo, error) { - fileInfo, err := os.Stat(migrationsDirPath) +func NewMigrationFsRepo(migrationsConfig *configloader.MigrationsConfig) (*MigrationFsRepo, error) { + fileInfo, err := os.Stat(migrationsConfig.DirectoryPath) if errors.Is(err, os.ErrNotExist) { - err = os.MkdirAll(migrationsDirPath, 0755) + err = os.MkdirAll(migrationsConfig.DirectoryPath, 0755) if err != nil { return nil, err } } else if err != nil { return nil, err } else if err == nil && !fileInfo.IsDir() { - return nil, &ErrIsNotDir{path: migrationsDirPath} + return nil, &ErrIsNotDir{path: migrationsConfig.DirectoryPath} } - return &MigrationFsRepo{migrationsDirPath: migrationsDirPath}, nil + return &MigrationFsRepo{migrationsDirPath: migrationsConfig.DirectoryPath}, nil } func (mr *MigrationFsRepo) Create(migration *models.Migration) error { diff --git a/internal/repositories/migration_fs_repo_test.go b/internal/repositories/migration_fs_repo_test.go index bb12097..8fdcd8f 100644 --- a/internal/repositories/migration_fs_repo_test.go +++ b/internal/repositories/migration_fs_repo_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/eugenetriguba/bolt/internal/configloader" "github.com/eugenetriguba/bolt/internal/models" "github.com/eugenetriguba/bolt/internal/repositories" "gotest.tools/v3/assert" @@ -20,8 +21,9 @@ func assertFileExists(t *testing.T, path string) { func TestNewMigrationFsRepoCreatesDirIfNotExists(t *testing.T) { tempDir := t.TempDir() migrationsDir := filepath.Join(tempDir, "migrations") + migrationsConfig := configloader.MigrationsConfig{DirectoryPath: migrationsDir} - _, err := repositories.NewMigrationFsRepo(migrationsDir) + _, err := repositories.NewMigrationFsRepo(&migrationsConfig) assert.NilError(t, err) assertFileExists(t, migrationsDir) @@ -30,16 +32,18 @@ func TestNewMigrationFsRepoCreatesDirIfNotExists(t *testing.T) { func TestNewMigrationFsRepoMigrationDirIsFile(t *testing.T) { tempDir := t.TempDir() migrationsDir := filepath.Join(tempDir, "migrations") + migrationsConfig := configloader.MigrationsConfig{DirectoryPath: migrationsDir} _, err := os.Create(migrationsDir) assert.NilError(t, err) - _, err = repositories.NewMigrationFsRepo(migrationsDir) + _, err = repositories.NewMigrationFsRepo(&migrationsConfig) assert.ErrorContains(t, err, "is not a directory") } func TestMigrationFsRepoCreate(t *testing.T) { tempDir := t.TempDir() - repo, err := repositories.NewMigrationFsRepo(tempDir) + migrationsConfig := configloader.MigrationsConfig{DirectoryPath: tempDir} + repo, err := repositories.NewMigrationFsRepo(&migrationsConfig) assert.NilError(t, err) migration := models.NewMigration(time.Now(), "add users table") @@ -54,7 +58,8 @@ func TestMigrationFsRepoCreate(t *testing.T) { func TestMigrationFsRepoReadUpgradeAndDowngradeScript(t *testing.T) { tempDir := t.TempDir() - repo, err := repositories.NewMigrationFsRepo(tempDir) + migrationsConfig := configloader.MigrationsConfig{DirectoryPath: tempDir} + repo, err := repositories.NewMigrationFsRepo(&migrationsConfig) assert.NilError(t, err) migration := models.NewMigration(time.Now(), "add users table") @@ -85,7 +90,8 @@ func TestMigrationFsRepoReadUpgradeAndDowngradeScript(t *testing.T) { func TestMigrationFsRepo_List(t *testing.T) { tempDir := t.TempDir() - repo, err := repositories.NewMigrationFsRepo(tempDir) + migrationsConfig := configloader.MigrationsConfig{DirectoryPath: tempDir} + repo, err := repositories.NewMigrationFsRepo(&migrationsConfig) assert.NilError(t, err) migration1 := models.NewMigration(