Skip to content

Commit

Permalink
feat: add -env flag and allow disabling with "none"
Browse files Browse the repository at this point in the history
  • Loading branch information
mfridman committed Dec 14, 2024
1 parent a47577b commit 483081e
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 77 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

- Add support for loading environment variables from `.env` files, enabled by default.
- The default file name is `.env`, but can be changed with the `-env=<filename>` flag.
- To disable this feature, set `-env=none`.

## [v3.23.1]

- Store implementations can **optionally** implement the `TableExists` method to provide optimized
Expand Down
45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,6 @@ brew install goose

See [installation documentation](https://pressly.github.io/goose/installation/) for more details.

# Setting Up Environment Keys
To start, configure the environment keys using one of the following methods:

**1. Via environment variables:**
```shell
export GOOSE_DRIVER=DRIVER
export GOOSE_DBSTRING=DBSTRING
export GOOSE_MIGRATION_DIR=MIGRATION_DIR
```

**2. Via `.env` files with corresponding variables. `.env` file example**:
```env
GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/admin_db
GOOSE_MIGRATION_DIR=./migrations
```
For more details about environment variables, see the [official documentation on environment variables]( https://pressly.github.io/goose/documentation/environment-variables/).

# Usage

<details>
Expand Down Expand Up @@ -254,6 +236,33 @@ Print the current version of the database:
$ goose version
$ goose: version 002

# Environment Variables

If you prefer to use environment variables, instead of passing the driver and database string as
arguments, you can set the following environment variables:

**1. Via environment variables:**

```shell
export GOOSE_DRIVER=DRIVER
export GOOSE_DBSTRING=DBSTRING
export GOOSE_MIGRATION_DIR=MIGRATION_DIR
```

**2. Via `.env` files with corresponding variables. `.env` file example**:

```env
GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/admin_db
GOOSE_MIGRATION_DIR=./migrations
```

Loading from `.env` files is enabled by default. To disable this feature, set the `-env=none` flag.
If you want to load from a specific file, set the `-env` flag to the file path.

For more details about environment variables, see the [official documentation on environment
variables](https://pressly.github.io/goose/documentation/environment-variables/).

# Migrations

goose supports migrations written in SQL or in Go.
Expand Down
85 changes: 69 additions & 16 deletions cmd/goose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ import (
"text/tabwriter"
"text/template"

"github.com/joho/godotenv"
"github.com/mfridman/xflag"
"github.com/pressly/goose/v3"
"github.com/pressly/goose/v3/internal/cfg"
"github.com/pressly/goose/v3/internal/migrationstats"
)

var (
DefaultMigrationDir = "."

flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", cfg.DefaultMigrationDir, "directory with migration files, (GOOSE_MIGRATION_DIR env variable supported)")
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")
verbose = flags.Bool("v", false, "enable verbose mode")
help = flags.Bool("h", false, "print help")
Expand All @@ -38,6 +40,7 @@ var (
noVersioning = flags.Bool("no-versioning", false, "apply migration commands with no versioning, in file order, from directory pointed to")
noColor = flags.Bool("no-color", false, "disable color output (NO_COLOR env variable supported)")
timeout = flags.Duration("timeout", 0, "maximum allowed duration for queries to run; e.g., 1h13m")
envFile = flags.String("env", "", "load environment variables from file (default .env)")
)

var version string
Expand All @@ -60,6 +63,20 @@ func main() {
fmt.Printf("goose version: %s\n", strings.TrimSpace(version))
return
}

switch *envFile {
case "":
// Best effort to load default .env file
_ = godotenv.Load()
case "none":
// Do not load any env file
default:
if err := godotenv.Load(*envFile); err != nil {
log.Fatalf("failed to load env file: %v", err)
}
}
envConfig := loadEnvConfig()

if *verbose {
goose.SetVerbose(true)
}
Expand All @@ -82,8 +99,8 @@ func main() {

// The -dir option has not been set, check whether the env variable is set
// before defaulting to ".".
if *dir == cfg.DefaultMigrationDir && cfg.GOOSEMIGRATIONDIR != "" {
*dir = cfg.GOOSEMIGRATIONDIR
if *dir == DefaultMigrationDir && envConfig.dir != "" {
*dir = envConfig.dir
}

switch args[0] {
Expand All @@ -103,7 +120,7 @@ func main() {
}
return
case "env":
for _, env := range cfg.List() {
for _, env := range envConfig.listEnvs() {
fmt.Printf("%s=%q\n", env.Name, env.Value)
}
return
Expand All @@ -125,7 +142,7 @@ func main() {
return
}

args = mergeArgs(args)
args = mergeArgs(envConfig, args)
if len(args) < 3 {
flags.Usage()
os.Exit(1)
Expand All @@ -147,7 +164,7 @@ func main() {
arguments = append(arguments, args[3:]...)
}
options := []goose.OptionsFunc{}
if *noColor || checkNoColorFromEnv() {
if *noColor || envConfig.noColor {
options = append(options, goose.WithNoColor(true))
}
if *allowMissing {
Expand Down Expand Up @@ -209,19 +226,14 @@ func mergeDrivers(drivers []string) []string {
return merged
}

func checkNoColorFromEnv() bool {
ok, _ := strconv.ParseBool(cfg.GOOSENOCOLOR)
return ok
}

func mergeArgs(args []string) []string {
func mergeArgs(config *envConfig, args []string) []string {
if len(args) < 1 {
return args
}
if s := cfg.GOOSEDRIVER; s != "" {
if s := config.driver; s != "" {
args = append([]string{s}, args...)
}
if s := cfg.GOOSEDBSTRING; s != "" {
if s := config.dbstring; s != "" {
args = append([]string{args[0], s}, args[1:]...)
}
return args
Expand Down Expand Up @@ -330,7 +342,7 @@ SELECT 'down SQL query';

// initDir will create a directory with an empty SQL migration file.
func gooseInit(dir string) error {
if dir == "" || dir == cfg.DefaultMigrationDir {
if dir == "" || dir == DefaultMigrationDir {
dir = "migrations"
}
_, err := os.Stat(dir)
Expand Down Expand Up @@ -404,3 +416,44 @@ func printValidate(filename string, verbose bool) error {
}
return w.Flush()
}

type envConfig struct {
driver string
dbstring string
dir string
noColor bool
}

func loadEnvConfig() *envConfig {
noColorBool, _ := strconv.ParseBool(envOr("NO_COLOR", "false"))
return &envConfig{
driver: envOr("GOOSE_DRIVER", ""),
dbstring: envOr("GOOSE_DBSTRING", ""),
dir: envOr("GOOSE_MIGRATION_DIR", DefaultMigrationDir),
// https://no-color.org/
noColor: noColorBool,
}
}

func (c *envConfig) listEnvs() []envVar {
return []envVar{
{Name: "GOOSE_DRIVER", Value: c.driver},
{Name: "GOOSE_DBSTRING", Value: c.dbstring},
{Name: "GOOSE_MIGRATION_DIR", Value: c.dir},
{Name: "NO_COLOR", Value: strconv.FormatBool(c.noColor)},
}
}

type envVar struct {
Name string
Value string
}

// envOr returns os.Getenv(key) if set, or else default.
func envOr(key, def string) string {
val := os.Getenv(key)
if val == "" {
val = def
}
return val
}
43 changes: 0 additions & 43 deletions internal/cfg/cfg.go

This file was deleted.

0 comments on commit 483081e

Please sign in to comment.