diff --git a/README.md b/README.md index b09fe3a..16320bd 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,7 @@ arguments, you can set the following environment variables: export GOOSE_DRIVER=DRIVER export GOOSE_DBSTRING=DBSTRING export GOOSE_MIGRATION_DIR=MIGRATION_DIR +export GOOSE_TABLE=TABLENAME ``` **2. Via `.env` files with corresponding variables. `.env` file example**: @@ -255,6 +256,7 @@ export GOOSE_MIGRATION_DIR=MIGRATION_DIR GOOSE_DRIVER=postgres GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/admin_db GOOSE_MIGRATION_DIR=./migrations +GOOSE_TABLE=custom.goose_migrations ``` Loading from `.env` files is enabled by default. To disable this feature, set the `-env=none` flag. diff --git a/cmd/goose/main.go b/cmd/goose/main.go index bd5964b..01b1e46 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -28,7 +28,7 @@ var ( flags = flag.NewFlagSet("goose", flag.ExitOnError) dir = flags.String("dir", DefaultMigrationDir, "directory with migration files, (GOOSE_MIGRATION_DIR env variable supported)") - table = flags.String("table", "goose_db_version", "migrations table name") + table = flags.String("table", "", "migrations table name") verbose = flags.Bool("v", false, "enable verbose mode") help = flags.Bool("h", false, "print help") versionFlag = flags.Bool("version", false, "print version") @@ -83,7 +83,9 @@ func main() { if *sequential { goose.SetSequential(true) } - goose.SetTableName(*table) + + // The order of precedence should be: flag > env variable > default value. + goose.SetTableName(firstNonEmpty(*table, envConfig.table, goose.DefaultTablename)) args := flags.Args() @@ -421,6 +423,7 @@ type envConfig struct { driver string dbstring string dir string + table string noColor bool } @@ -429,6 +432,7 @@ func loadEnvConfig() *envConfig { return &envConfig{ driver: envOr("GOOSE_DRIVER", ""), dbstring: envOr("GOOSE_DBSTRING", ""), + table: envOr("GOOSE_TABLE", ""), dir: envOr("GOOSE_MIGRATION_DIR", DefaultMigrationDir), // https://no-color.org/ noColor: noColorBool, @@ -440,6 +444,7 @@ func (c *envConfig) listEnvs() []envVar { {Name: "GOOSE_DRIVER", Value: c.driver}, {Name: "GOOSE_DBSTRING", Value: c.dbstring}, {Name: "GOOSE_MIGRATION_DIR", Value: c.dir}, + {Name: "GOOSE_TABLE", Value: c.table}, {Name: "NO_COLOR", Value: strconv.FormatBool(c.noColor)}, } } @@ -457,3 +462,13 @@ func envOr(key, def string) string { } return val } + +// firstNonEmpty returns the first non-empty string from the provided input or an empty string if all are empty. +func firstNonEmpty(values ...string) string { + for _, v := range values { + if v != "" { + return v + } + } + return "" +} diff --git a/cmd/goose/main_test.go b/cmd/goose/main_test.go new file mode 100644 index 0000000..a8c7714 --- /dev/null +++ b/cmd/goose/main_test.go @@ -0,0 +1,68 @@ +package main + +import ( + "testing" +) + +func TestFirstNonEmpty(t *testing.T) { + tests := []struct { + name string + input []string + expected string + }{ + { + name: "no values", + input: []string{}, + expected: "", + }, + { + name: "all empty values", + input: []string{"", "", ""}, + expected: "", + }, + { + name: "single non-empty value at start", + input: []string{"value", "", ""}, + expected: "value", + }, + { + name: "single non-empty value in middle", + input: []string{"", "value", ""}, + expected: "value", + }, + { + name: "single non-empty value at end", + input: []string{"", "", "value"}, + expected: "value", + }, + { + name: "multiple non-empty values", + input: []string{"first", "second", "third"}, + expected: "first", + }, + { + name: "mixed empty and non-empty values", + input: []string{"", "value1", "", "value2"}, + expected: "value1", + }, + { + name: "only one value, empty", + input: []string{""}, + expected: "", + }, + { + name: "only one value, non-empty", + input: []string{"value"}, + expected: "value", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := firstNonEmpty(tt.input...) + if result != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result) + } + }) + } +}