diff --git a/.agignore b/.agignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/.agignore @@ -0,0 +1 @@ +/vendor diff --git a/.gitignore b/.gitignore index 4dbecbe..0e83ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ +/dist .DS_Store -dbmate -dist diff --git a/Dockerfile b/Dockerfile index 34763cb..7b2b2f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.8.0 +FROM golang:1.8 # required to force cgo (for sqlite driver) with cross compile ENV CGO_ENABLED 1 @@ -19,6 +19,6 @@ COPY . $GOPATH/src/github.com/amacneil/dbmate WORKDIR $GOPATH/src/github.com/amacneil/dbmate # build -RUN go install -v +RUN go install -v ./cmd/dbmate CMD dbmate diff --git a/Makefile b/Makefile index ad39585..40bb818 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ DC := docker-compose BUILD_FLAGS := -ldflags '-s' +PACKAGES := . ./cmd/dbmate -all: clean container lint test build +all: clean container test lint build clean: rm -rf dist @@ -12,15 +13,15 @@ container: $(DC) up -d lint: - $(DC) run dbmate golint - $(DC) run dbmate go vet - $(DC) run dbmate errcheck + $(DC) run dbmate golint -set_exit_status $(PACKAGES) + $(DC) run dbmate go vet $(PACKAGES) + $(DC) run dbmate errcheck $(PACKAGES) test: - $(DC) run dbmate go test -v + $(DC) run dbmate go test -v $(PACKAGES) build: clean - $(DC) run -e GOARCH=386 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-i386 - $(DC) run -e GOARCH=amd64 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-amd64 + $(DC) run -e GOARCH=386 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-i386 ./cmd/dbmate + $(DC) run -e GOARCH=amd64 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-amd64 ./cmd/dbmate # musl target does not support sqlite - $(DC) run -e GOARCH=amd64 -e CGO_ENABLED=0 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-musl-amd64 + $(DC) run -e GOARCH=amd64 -e CGO_ENABLED=0 dbmate go build $(BUILD_FLAGS) -o dist/dbmate-linux-musl-amd64 ./cmd/dbmate diff --git a/cmd/dbmate/main.go b/cmd/dbmate/main.go new file mode 100644 index 0000000..8efeb41 --- /dev/null +++ b/cmd/dbmate/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "fmt" + "log" + "net/url" + "os" + + "github.com/amacneil/dbmate" + "github.com/joho/godotenv" + "github.com/urfave/cli" +) + +func main() { + loadDotEnv() + + app := NewApp() + err := app.Run(os.Args) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + os.Exit(1) + } +} + +// NewApp creates a new command line app +func NewApp() *cli.App { + app := cli.NewApp() + app.Name = "dbmate" + app.Usage = "A lightweight, framework-independent database migration tool." + app.Version = dbmate.Version + + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "migrations-dir, d", + Value: dbmate.DefaultMigrationsDir, + Usage: "specify the directory containing migration files", + }, + cli.StringFlag{ + Name: "env, e", + Value: "DATABASE_URL", + Usage: "specify an environment variable containing the database URL", + }, + } + + app.Commands = []cli.Command{ + { + Name: "new", + Aliases: []string{"n"}, + Usage: "Generate a new migration file", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + name := c.Args().First() + return db.New(name) + }), + }, + { + Name: "up", + Usage: "Create database (if necessary) and migrate to the latest version", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + return db.Up() + }), + }, + { + Name: "create", + Usage: "Create database", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + return db.Create() + }), + }, + { + Name: "drop", + Usage: "Drop database (if it exists)", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + return db.Drop() + }), + }, + { + Name: "migrate", + Usage: "Migrate to the latest version", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + return db.Migrate() + }), + }, + { + Name: "rollback", + Aliases: []string{"down"}, + Usage: "Rollback the most recent migration", + Action: action(func(db *dbmate.DB, c *cli.Context) error { + return db.Rollback() + }), + }, + } + + return app +} + +// load environment variables from .env file +func loadDotEnv() { + if _, err := os.Stat(".env"); err != nil { + return + } + + if err := godotenv.Load(); err != nil { + log.Fatal("Error loading .env file") + } +} + +// action wraps a cli.ActionFunc with dbmate initialization logic +func action(f func(*dbmate.DB, *cli.Context) error) cli.ActionFunc { + return func(c *cli.Context) error { + u, err := getDatabaseURL(c) + if err != nil { + return err + } + db := dbmate.NewDB(u) + db.MigrationsDir = c.GlobalString("migrations-dir") + + return f(db, c) + } +} + +// getDatabaseURL returns the current environment database url +func getDatabaseURL(c *cli.Context) (u *url.URL, err error) { + env := c.GlobalString("env") + value := os.Getenv(env) + + return url.Parse(value) +} diff --git a/cmd/dbmate/main_test.go b/cmd/dbmate/main_test.go new file mode 100644 index 0000000..73d5227 --- /dev/null +++ b/cmd/dbmate/main_test.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + "net/url" + "os" + "testing" + + "github.com/stretchr/testify/require" + "github.com/urfave/cli" +) + +func testContext(t *testing.T, u *url.URL) *cli.Context { + var err error + + err = os.Setenv("DATABASE_URL", u.String()) + require.Nil(t, err) + + app := NewApp() + flagset := flag.NewFlagSet(app.Name, flag.ContinueOnError) + for _, f := range app.Flags { + f.Apply(flagset) + } + + return cli.NewContext(app, flagset, nil) +} + +func TestGetDatabaseUrl(t *testing.T) { + envURL, err := url.Parse("foo://example.org/db") + require.Nil(t, err) + ctx := testContext(t, envURL) + + u, err := getDatabaseURL(ctx) + require.Nil(t, err) + + require.Equal(t, "foo", u.Scheme) + require.Equal(t, "example.org", u.Host) + require.Equal(t, "/db", u.Path) +} diff --git a/commands_test.go b/commands_test.go deleted file mode 100644 index 8bcc6eb..0000000 --- a/commands_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package main - -import ( - "flag" - "net/url" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "github.com/urfave/cli" -) - -var testdataDir string - -func testContext(t *testing.T, u *url.URL) *cli.Context { - var err error - - // only chdir once, because testdata is relative to current directory - if testdataDir == "" { - testdataDir, err = filepath.Abs("./testdata") - require.Nil(t, err) - - err = os.Chdir(testdataDir) - require.Nil(t, err) - } - - err = os.Setenv("DATABASE_URL", u.String()) - require.Nil(t, err) - - app := NewApp() - flagset := flag.NewFlagSet(app.Name, flag.ContinueOnError) - for _, f := range app.Flags { - f.Apply(flagset) - } - - return cli.NewContext(app, flagset, nil) -} - -func testURLs(t *testing.T) []*url.URL { - return []*url.URL{ - postgresTestURL(t), - mySQLTestURL(t), - sqliteTestURL(t), - } -} - -func TestGetDatabaseUrl(t *testing.T) { - envURL, err := url.Parse("foo://example.org/db") - require.Nil(t, err) - ctx := testContext(t, envURL) - - u, err := GetDatabaseURL(ctx) - require.Nil(t, err) - - require.Equal(t, "foo", u.Scheme) - require.Equal(t, "example.org", u.Host) - require.Equal(t, "/db", u.Path) -} - -func testMigrateCommandURL(t *testing.T, u *url.URL) { - ctx := testContext(t, u) - - // drop and recreate database - err := DropCommand(ctx) - require.Nil(t, err) - err = CreateCommand(ctx) - require.Nil(t, err) - - // migrate - err = MigrateCommand(ctx) - require.Nil(t, err) - - // verify results - db, err := GetDriverOpen(u) - require.Nil(t, err) - defer mustClose(db) - - count := 0 - err = db.QueryRow(`select count(*) from schema_migrations - where version = '20151129054053'`).Scan(&count) - require.Nil(t, err) - require.Equal(t, 1, count) - - err = db.QueryRow("select count(*) from users").Scan(&count) - require.Nil(t, err) - require.Equal(t, 1, count) -} - -func TestMigrateCommand(t *testing.T) { - for _, u := range testURLs(t) { - testMigrateCommandURL(t, u) - } -} - -func testUpCommandURL(t *testing.T, u *url.URL) { - ctx := testContext(t, u) - - // drop database - err := DropCommand(ctx) - require.Nil(t, err) - - // create and migrate - err = UpCommand(ctx) - require.Nil(t, err) - - // verify results - db, err := GetDriverOpen(u) - require.Nil(t, err) - defer mustClose(db) - - count := 0 - err = db.QueryRow(`select count(*) from schema_migrations - where version = '20151129054053'`).Scan(&count) - require.Nil(t, err) - require.Equal(t, 1, count) - - err = db.QueryRow("select count(*) from users").Scan(&count) - require.Nil(t, err) - require.Equal(t, 1, count) -} - -func TestUpCommand(t *testing.T) { - for _, u := range testURLs(t) { - testUpCommandURL(t, u) - } -} - -func testRollbackCommandURL(t *testing.T, u *url.URL) { - ctx := testContext(t, u) - - // drop, recreate, and migrate database - err := DropCommand(ctx) - require.Nil(t, err) - err = CreateCommand(ctx) - require.Nil(t, err) - err = MigrateCommand(ctx) - require.Nil(t, err) - - // verify migration - db, err := GetDriverOpen(u) - require.Nil(t, err) - defer mustClose(db) - - count := 0 - err = db.QueryRow(`select count(*) from schema_migrations - where version = '20151129054053'`).Scan(&count) - require.Nil(t, err) - require.Equal(t, 1, count) - - // rollback - err = RollbackCommand(ctx) - require.Nil(t, err) - - // verify rollback - err = db.QueryRow("select count(*) from schema_migrations").Scan(&count) - require.Nil(t, err) - require.Equal(t, 0, count) - - err = db.QueryRow("select count(*) from users").Scan(&count) - require.NotNil(t, err) - require.Regexp(t, "(does not exist|doesn't exist|no such table)", err.Error()) -} - -func TestRollbackCommand(t *testing.T) { - for _, u := range testURLs(t) { - testRollbackCommandURL(t, u) - } -} diff --git a/commands.go b/db.go similarity index 60% rename from commands.go rename to db.go index 60c2d3b..283671c 100644 --- a/commands.go +++ b/db.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" @@ -10,18 +10,33 @@ import ( "regexp" "sort" "time" - - "github.com/urfave/cli" ) -// UpCommand creates the database (if necessary) and runs migrations -func UpCommand(ctx *cli.Context) error { - u, err := GetDatabaseURL(ctx) - if err != nil { - return err - } +// DefaultMigrationsDir specifies default directory to find migration files +var DefaultMigrationsDir = "./db/migrations" - drv, err := GetDriver(u.Scheme) +// DB allows dbmate actions to be performed on a specified database +type DB struct { + DatabaseURL *url.URL + MigrationsDir string +} + +// NewDB initializes a new dbmate database +func NewDB(databaseURL *url.URL) *DB { + return &DB{ + DatabaseURL: databaseURL, + MigrationsDir: DefaultMigrationsDir, + } +} + +// GetDriver loads the required database driver +func (db *DB) GetDriver() (Driver, error) { + return GetDriver(db.DatabaseURL.Scheme) +} + +// Up creates the database (if necessary) and runs migrations +func (db *DB) Up() error { + drv, err := db.GetDriver() if err != nil { return err } @@ -29,71 +44,59 @@ func UpCommand(ctx *cli.Context) error { // create database if it does not already exist // skip this step if we cannot determine status // (e.g. user does not have list database permission) - exists, err := drv.DatabaseExists(u) + exists, err := drv.DatabaseExists(db.DatabaseURL) if err == nil && !exists { - if err := drv.CreateDatabase(u); err != nil { + if err := drv.CreateDatabase(db.DatabaseURL); err != nil { return err } } // migrate - return MigrateCommand(ctx) + return db.Migrate() } -// CreateCommand creates the current database -func CreateCommand(ctx *cli.Context) error { - u, err := GetDatabaseURL(ctx) +// Create creates the current database +func (db *DB) Create() error { + drv, err := db.GetDriver() if err != nil { return err } - drv, err := GetDriver(u.Scheme) - if err != nil { - return err - } - - return drv.CreateDatabase(u) + return drv.CreateDatabase(db.DatabaseURL) } -// DropCommand drops the current database (if it exists) -func DropCommand(ctx *cli.Context) error { - u, err := GetDatabaseURL(ctx) +// Drop drops the current database (if it exists) +func (db *DB) Drop() error { + drv, err := db.GetDriver() if err != nil { return err } - drv, err := GetDriver(u.Scheme) - if err != nil { - return err - } - - return drv.DropDatabase(u) + return drv.DropDatabase(db.DatabaseURL) } const migrationTemplate = "-- migrate:up\n\n\n-- migrate:down\n\n" -// NewCommand creates a new migration file -func NewCommand(ctx *cli.Context) error { +// New creates a new migration file +func (db *DB) New(name string) error { // new migration name timestamp := time.Now().UTC().Format("20060102150405") - name := ctx.Args().First() if name == "" { - return fmt.Errorf("Please specify a name for the new migration.") + return fmt.Errorf("please specify a name for the new migration") } name = fmt.Sprintf("%s_%s.sql", timestamp, name) // create migrations dir if missing - migrationsDir := ctx.GlobalString("migrations-dir") - if err := os.MkdirAll(migrationsDir, 0755); err != nil { - return fmt.Errorf("Unable to create directory `%s`.", migrationsDir) + if err := os.MkdirAll(db.MigrationsDir, 0755); err != nil { + return fmt.Errorf("unable to create directory `%s`", db.MigrationsDir) } // check file does not already exist - path := filepath.Join(migrationsDir, name) + path := filepath.Join(db.MigrationsDir, name) fmt.Printf("Creating migration: %s\n", path) if _, err := os.Stat(path); !os.IsNotExist(err) { - return fmt.Errorf("File already exists") + return fmt.Errorf("file already exists") } // write new migration @@ -111,14 +114,6 @@ func NewCommand(ctx *cli.Context) error { return nil } -// GetDatabaseURL returns the current environment database url -func GetDatabaseURL(ctx *cli.Context) (u *url.URL, err error) { - env := ctx.GlobalString("env") - value := os.Getenv(env) - - return url.Parse(value) -} - func doTransaction(db *sql.DB, txFunc func(Transaction) error) error { tx, err := db.Begin() if err != nil { @@ -136,50 +131,44 @@ func doTransaction(db *sql.DB, txFunc func(Transaction) error) error { return tx.Commit() } -func openDatabaseForMigration(ctx *cli.Context) (Driver, *sql.DB, error) { - u, err := GetDatabaseURL(ctx) +func (db *DB) openDatabaseForMigration() (Driver, *sql.DB, error) { + drv, err := db.GetDriver() if err != nil { return nil, nil, err } - drv, err := GetDriver(u.Scheme) + sqlDB, err := drv.Open(db.DatabaseURL) if err != nil { return nil, nil, err } - db, err := drv.Open(u) - if err != nil { + if err := drv.CreateMigrationsTable(sqlDB); err != nil { + mustClose(sqlDB) return nil, nil, err } - if err := drv.CreateMigrationsTable(db); err != nil { - mustClose(db) - return nil, nil, err - } - - return drv, db, nil + return drv, sqlDB, nil } -// MigrateCommand migrates database to the latest version -func MigrateCommand(ctx *cli.Context) error { - migrationsDir := ctx.GlobalString("migrations-dir") +// Migrate migrates database to the latest version +func (db *DB) Migrate() error { re := regexp.MustCompile(`^\d.*\.sql$`) - files, err := findMigrationFiles(migrationsDir, re) + files, err := findMigrationFiles(db.MigrationsDir, re) if err != nil { return err } if len(files) == 0 { - return fmt.Errorf("No migration files found.") + return fmt.Errorf("no migration files found") } - drv, db, err := openDatabaseForMigration(ctx) + drv, sqlDB, err := db.openDatabaseForMigration() if err != nil { return err } - defer mustClose(db) + defer mustClose(sqlDB) - applied, err := drv.SelectMigrations(db, -1) + applied, err := drv.SelectMigrations(sqlDB, -1) if err != nil { return err } @@ -193,13 +182,13 @@ func MigrateCommand(ctx *cli.Context) error { fmt.Printf("Applying: %s\n", filename) - migration, err := parseMigration(filepath.Join(migrationsDir, filename)) + migration, err := parseMigration(filepath.Join(db.MigrationsDir, filename)) if err != nil { return err } // begin transaction - err = doTransaction(db, func(tx Transaction) error { + err = doTransaction(sqlDB, func(tx Transaction) error { // run actual migration if _, err := tx.Exec(migration["up"]); err != nil { return err @@ -224,7 +213,7 @@ func MigrateCommand(ctx *cli.Context) error { func findMigrationFiles(dir string, re *regexp.Regexp) ([]string, error) { files, err := ioutil.ReadDir(dir) if err != nil { - return nil, fmt.Errorf("Could not find migrations directory `%s`.", dir) + return nil, fmt.Errorf("could not find migrations directory `%s`", dir) } matches := []string{} @@ -260,7 +249,7 @@ func findMigrationFile(dir string, ver string) (string, error) { } if len(files) == 0 { - return "", fmt.Errorf("Can't find migration file: %s*.sql", ver) + return "", fmt.Errorf("can't find migration file: %s*.sql", ver) } return files[0], nil @@ -307,15 +296,15 @@ func parseMigration(path string) (map[string]string, error) { return migrations, nil } -// RollbackCommand rolls back the most recent migration -func RollbackCommand(ctx *cli.Context) error { - drv, db, err := openDatabaseForMigration(ctx) +// Rollback rolls back the most recent migration +func (db *DB) Rollback() error { + drv, sqlDB, err := db.openDatabaseForMigration() if err != nil { return err } - defer mustClose(db) + defer mustClose(sqlDB) - applied, err := drv.SelectMigrations(db, 1) + applied, err := drv.SelectMigrations(sqlDB, 1) if err != nil { return err } @@ -326,24 +315,23 @@ func RollbackCommand(ctx *cli.Context) error { latest = ver } if latest == "" { - return fmt.Errorf("Can't rollback: no migrations have been applied.") + return fmt.Errorf("can't rollback: no migrations have been applied") } - migrationsDir := ctx.GlobalString("migrations-dir") - filename, err := findMigrationFile(migrationsDir, latest) + filename, err := findMigrationFile(db.MigrationsDir, latest) if err != nil { return err } fmt.Printf("Rolling back: %s\n", filename) - migration, err := parseMigration(filepath.Join(migrationsDir, filename)) + migration, err := parseMigration(filepath.Join(db.MigrationsDir, filename)) if err != nil { return err } // begin transaction - err = doTransaction(db, func(tx Transaction) error { + err = doTransaction(sqlDB, func(tx Transaction) error { // rollback migration if _, err := tx.Exec(migration["down"]); err != nil { return err diff --git a/db_test.go b/db_test.go new file mode 100644 index 0000000..d4c4dd9 --- /dev/null +++ b/db_test.go @@ -0,0 +1,145 @@ +package dbmate + +import ( + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +var testdataDir string + +func newTestDB(t *testing.T, u *url.URL) *DB { + var err error + + // only chdir once, because testdata is relative to current directory + if testdataDir == "" { + testdataDir, err = filepath.Abs("./testdata") + require.Nil(t, err) + + err = os.Chdir(testdataDir) + require.Nil(t, err) + } + + return NewDB(u) +} + +func testURLs(t *testing.T) []*url.URL { + return []*url.URL{ + postgresTestURL(t), + mySQLTestURL(t), + sqliteTestURL(t), + } +} + +func testMigrateURL(t *testing.T, u *url.URL) { + db := newTestDB(t, u) + + // drop and recreate database + err := db.Drop() + require.Nil(t, err) + err = db.Create() + require.Nil(t, err) + + // migrate + err = db.Migrate() + require.Nil(t, err) + + // verify results + sqlDB, err := GetDriverOpen(u) + require.Nil(t, err) + defer mustClose(sqlDB) + + count := 0 + err = sqlDB.QueryRow(`select count(*) from schema_migrations + where version = '20151129054053'`).Scan(&count) + require.Nil(t, err) + require.Equal(t, 1, count) + + err = sqlDB.QueryRow("select count(*) from users").Scan(&count) + require.Nil(t, err) + require.Equal(t, 1, count) +} + +func TestMigrate(t *testing.T) { + for _, u := range testURLs(t) { + testMigrateURL(t, u) + } +} + +func testUpURL(t *testing.T, u *url.URL) { + db := newTestDB(t, u) + + // drop database + err := db.Drop() + require.Nil(t, err) + + // create and migrate + err = db.Up() + require.Nil(t, err) + + // verify results + sqlDB, err := GetDriverOpen(u) + require.Nil(t, err) + defer mustClose(sqlDB) + + count := 0 + err = sqlDB.QueryRow(`select count(*) from schema_migrations + where version = '20151129054053'`).Scan(&count) + require.Nil(t, err) + require.Equal(t, 1, count) + + err = sqlDB.QueryRow("select count(*) from users").Scan(&count) + require.Nil(t, err) + require.Equal(t, 1, count) +} + +func TestUp(t *testing.T) { + for _, u := range testURLs(t) { + testUpURL(t, u) + } +} + +func testRollbackURL(t *testing.T, u *url.URL) { + db := newTestDB(t, u) + + // drop, recreate, and migrate database + err := db.Drop() + require.Nil(t, err) + err = db.Create() + require.Nil(t, err) + err = db.Migrate() + require.Nil(t, err) + + // verify migration + sqlDB, err := GetDriverOpen(u) + require.Nil(t, err) + defer mustClose(sqlDB) + + count := 0 + err = sqlDB.QueryRow(`select count(*) from schema_migrations + where version = '20151129054053'`).Scan(&count) + require.Nil(t, err) + require.Equal(t, 1, count) + + // rollback + err = db.Rollback() + require.Nil(t, err) + + // verify rollback + err = sqlDB.QueryRow("select count(*) from schema_migrations").Scan(&count) + require.Nil(t, err) + require.Equal(t, 0, count) + + err = sqlDB.QueryRow("select count(*) from users").Scan(&count) + require.NotNil(t, err) + require.Regexp(t, "(does not exist|doesn't exist|no such table)", err.Error()) +} + +func TestRollback(t *testing.T) { + for _, u := range testURLs(t) { + testRollbackURL(t, u) + } +} diff --git a/driver.go b/driver.go index 3d0fc6b..f9bec1d 100644 --- a/driver.go +++ b/driver.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" @@ -33,7 +33,7 @@ func GetDriver(name string) (Driver, error) { case "sqlite", "sqlite3": return SQLiteDriver{}, nil default: - return nil, fmt.Errorf("Unknown driver: %s", name) + return nil, fmt.Errorf("unknown driver: %s", name) } } diff --git a/driver_test.go b/driver_test.go index e1b2490..7e6e9e1 100644 --- a/driver_test.go +++ b/driver_test.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "testing" @@ -22,6 +22,6 @@ func TestGetDriver_MySQL(t *testing.T) { func TestGetDriver_Error(t *testing.T) { drv, err := GetDriver("foo") - require.Equal(t, "Unknown driver: foo", err.Error()) + require.Equal(t, "unknown driver: foo", err.Error()) require.Nil(t, drv) } diff --git a/main.go b/main.go deleted file mode 100644 index fb13d5e..0000000 --- a/main.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - - "github.com/joho/godotenv" - "github.com/urfave/cli" -) - -func main() { - loadDotEnv() - - app := NewApp() - err := app.Run(os.Args) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %s\n", err) - os.Exit(1) - } -} - -// NewApp creates a new command line app -func NewApp() *cli.App { - app := cli.NewApp() - app.Name = "dbmate" - app.Usage = "A lightweight, framework-independent database migration tool." - app.Version = Version - - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "migrations-dir, d", - Value: "./db/migrations", - Usage: "specify the directory containing migration files", - }, - cli.StringFlag{ - Name: "env, e", - Value: "DATABASE_URL", - Usage: "specify an environment variable containing the database URL", - }, - } - - app.Commands = []cli.Command{ - { - Name: "new", - Aliases: []string{"n"}, - Usage: "Generate a new migration file", - Action: NewCommand, - }, - { - Name: "up", - Usage: "Create database (if necessary) and migrate to the latest version", - Action: UpCommand, - }, - { - Name: "create", - Usage: "Create database", - Action: CreateCommand, - }, - { - Name: "drop", - Usage: "Drop database (if it exists)", - Action: DropCommand, - }, - { - Name: "migrate", - Usage: "Migrate to the latest version", - Action: MigrateCommand, - }, - { - Name: "rollback", - Aliases: []string{"down"}, - Usage: "Rollback the most recent migration", - Action: RollbackCommand, - }, - } - - return app -} - -type command func(*cli.Context) error - -func loadDotEnv() { - if _, err := os.Stat(".env"); err != nil { - return - } - - if err := godotenv.Load(); err != nil { - log.Fatal("Error loading .env file") - } -} diff --git a/mysql.go b/mysql.go index 05c8d81..56f230d 100644 --- a/mysql.go +++ b/mysql.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" @@ -6,7 +6,7 @@ import ( "net/url" "strings" - _ "github.com/go-sql-driver/mysql" + _ "github.com/go-sql-driver/mysql" // mysql driver for database/sql ) // MySQLDriver provides top level database functions diff --git a/mysql_test.go b/mysql_test.go index dcd693d..38cd632 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" diff --git a/postgres.go b/postgres.go index fc3258a..5b20e67 100644 --- a/postgres.go +++ b/postgres.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" diff --git a/postgres_test.go b/postgres_test.go index 9d46696..83a6d31 100644 --- a/postgres_test.go +++ b/postgres_test.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" diff --git a/sqlite.go b/sqlite.go index a93880c..27694d3 100644 --- a/sqlite.go +++ b/sqlite.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" @@ -7,7 +7,7 @@ import ( "os" "regexp" - _ "github.com/mattn/go-sqlite3" + _ "github.com/mattn/go-sqlite3" // sqlite driver for database/sql ) // SQLiteDriver provides top level database functions diff --git a/sqlite_test.go b/sqlite_test.go index e2a95db..6e59283 100644 --- a/sqlite_test.go +++ b/sqlite_test.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "database/sql" diff --git a/utils.go b/utils.go index a5cbe20..204cf2b 100644 --- a/utils.go +++ b/utils.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "io" diff --git a/utils_test.go b/utils_test.go index de811ad..b53de0b 100644 --- a/utils_test.go +++ b/utils_test.go @@ -1,4 +1,4 @@ -package main +package dbmate import ( "net/url" diff --git a/version.go b/version.go index d0c6572..bc0e9fd 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ -package main +package dbmate // Version of dbmate const Version = "1.2.1"