Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing migrations handling. BMP-596 #72

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@
cmd/goose/goose*
*.swp
*.test

# IDE
.vscode/
*.iml
.idea

# Mac
.DS_Store

*.db

40 changes: 13 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,12 @@ Goose is a database migration tool. Manage your database schema by creating incr

### Goals of this fork

`github.com/pressly/goose` is a fork of `bitbucket.org/liamstask/goose` with the following changes:
- No config files
- [Default goose binary](./cmd/goose/main.go) can migrate SQL files only
- Go migrations:
- We don't `go build` Go migrations functions on-the-fly
from within the goose binary
- Instead, we let you
[create your own custom goose binary](examples/go-migrations),
register your Go migration functions explicitly and run complex
migrations with your own `*sql.DB` connection
- Go migration functions let you run your code within
an SQL transaction, if you use the `*sql.Tx` argument
- The goose pkg is decoupled from the binary:
- goose pkg doesn't register any SQL drivers anymore,
thus no driver `panic()` conflict within your codebase!
- goose pkg doesn't have any vendor dependencies anymore
- We encourage using sequential versioning of migration files
(rather than timestamps-based versioning) to prevent version
mismatch and migration colissions
`github.com/mc2soft/goose` is a fork of `github.com/pressly/goose` with the following changes:
- Applying all missing migrations, regardless of latest migration version (see [example](./examples/go-migrations-package)).

# Install

$ go get -u github.com/pressly/goose/cmd/goose
$ go get -u github.com/mc2soft/goose/cmd/goose
Copy link
Collaborator

@VojtechVitek VojtechVitek Oct 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't expect us to merge this change github.com/mc2soft/goose, do you? :)

Can you iron this PR out and provide a high-level overview of what is this PR supposed to solve? Thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR resolves #73. Ok, i will iron it to avoid "mc2soft" mentions)) But pls give me feedback, do you consider such issue?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in the high-level, this would find all the missing migrations -- ie. all migrations that are available but were not applied in the DB, right?

You can

  1. list them
  2. apply them

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, with new commands:

  1. status-missing
  2. up-missing

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, do we need 1. at all? Can't goose status show these migrations as well by default?

Imho, 2. is explicit enough. I like it. It might even help people who insist on using Timestamp based migrations #63.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status show all migrations (could be a lot of them!), including missing ("pending"). But the new status-missing shows only missing migrations.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like someone from #63 to test this feature. I don't really have the same use case. Can you ping these guys, once you have the PR ready?


This will install the `goose` binary to your `$GOPATH/bin` directory.

Expand All @@ -45,17 +28,20 @@ Drivers:

Commands:
up Migrate the DB to the most recent version available
up-by-one Migrate up by a single version
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
status Dump the migration status for the current DB
status Dump the migration status for the current DB. Use [-missing-only] option to find out only migrations, missing from the current DB
version Print the current version of the database
create NAME [sql|go] Creates new migration file with next version

Options:
-dir string
directory with migration files (default ".")
-missing-only
for status command - find out only migrations, missing from the current DB

Examples:
goose sqlite3 ./foo.db status
Expand Down Expand Up @@ -204,7 +190,7 @@ language plpgsql;
## Go Migrations

1. Create your own goose binary, see [example](./examples/go-migrations)
2. Import `github.com/pressly/goose`
2. Import `github.com/mc2soft/goose`
3. Register your migration functions
4. Run goose command, ie. `goose.Up(db *sql.DB, dir string)`

Expand All @@ -216,7 +202,7 @@ package migrations
import (
"database/sql"

"github.com/pressly/goose"
"github.com/mc2soft/goose"
)

func init() {
Expand Down Expand Up @@ -244,7 +230,7 @@ func Down(tx *sql.Tx) error {

Licensed under [MIT License](./LICENSE)

[GoDoc]: https://godoc.org/github.com/pressly/goose
[GoDoc Widget]: https://godoc.org/github.com/pressly/goose?status.svg
[Travis]: https://travis-ci.org/pressly/goose
[Travis Widget]: https://travis-ci.org/pressly/goose.svg?branch=master
[GoDoc]: https://godoc.org/github.com/mc2soft/goose
[GoDoc Widget]: https://godoc.org/github.com/mc2soft/goose?status.svg
[Travis]: https://travis-ci.org/mc2soft/goose
[Travis Widget]: https://travis-ci.org/mc2soft/goose.svg?branch=master
18 changes: 12 additions & 6 deletions cmd/goose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"log"
"os"

"github.com/pressly/goose"
"github.com/mc2soft/goose"

// Init DB drivers.
_ "github.com/go-sql-driver/mysql"
Expand All @@ -17,8 +17,9 @@ import (
)

var (
flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", ".", "directory with migration files")
flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", ".", "directory with migration files")
missingOnly = flags.Bool("missing-only", false, "for status command - find out only migrations, missing from the current DB")
)

func main() {
Expand All @@ -28,7 +29,7 @@ func main() {
args := flags.Args()

if len(args) > 1 && args[0] == "create" {
if err := goose.Run("create", nil, *dir, args[1:]...); err != nil {
if err := goose.Run("create", nil, *dir, *missingOnly, args[1:]...); err != nil {
log.Fatalf("goose run: %v", err)
}
return
Expand Down Expand Up @@ -75,7 +76,7 @@ func main() {
arguments = append(arguments, args[3:]...)
}

if err := goose.Run(command, db, *dir, arguments...); err != nil {
if err := goose.Run(command, db, *dir, *missingOnly, arguments...); err != nil {
log.Fatalf("goose run: %v", err)
}
}
Expand Down Expand Up @@ -107,17 +108,22 @@ Examples:
goose redshift "postgres://user:[email protected]:5439/db" status

Options:
-dir string
directory with migration files (default ".")
-missing-only
for status command - find out only migrations, missing from the current DB
`

usageCommands = `
Commands:
up Migrate the DB to the most recent version available
up-by-one Migrate up by a single version
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
reset Roll back all migrations
status Dump the migration status for the current DB
status Dump the migration status for the current DB. Use [-missing-only] option to find out only migrations, missing from the current DB
version Print the current version of the database
create NAME [sql|go] Creates new migration file with next version
`
Expand Down
2 changes: 1 addition & 1 deletion create.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ var goSQLMigrationTemplate = template.Must(template.New("goose.go-migration").Pa

import (
"database/sql"
"github.com/pressly/goose"
"github.com/mc2soft/goose"
)

func init() {
Expand Down
74 changes: 74 additions & 0 deletions examples/go-migrations-package/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SQL + Go migrations

## This example: Best practice: Split migrations into a standalone package

Also take a look at "missing" migrations handling.

```bash
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db status
Applied At Migration
=======================================
Pending -- 00001_create_users_table.sql
Pending -- 00002_rename_root.go
Pending -- 00004_rename_admin.go
$ go run main.go -dir ./db/migrations -missing-only sqlite3 ./db/foo.db status
Missing migrations
===========
00001_create_users_table.sql
00002_rename_root.go
00004_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db up
OK 00001_create_users_table.sql
OK 00002_rename_root.go
OK 00004_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db status
Applied At Migration
=======================================
Fri Oct 6 13:03:09 2017 -- 00001_create_users_table.sql
Fri Oct 6 13:03:09 2017 -- 00002_rename_root.go
Fri Oct 6 13:03:09 2017 -- 00004_rename_admin.go
$ go run main.go -dir ./db/migrations -missing-only sqlite3 ./db/foo.db status
goose: no missing migrations

```
Get "missing" migrations: remove "_" at _00003 and _00005 migrations, then do:
```bash
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db status
Applied At Migration
=======================================
Fri Oct 6 13:03:09 2017 -- 00001_create_users_table.sql
Fri Oct 6 13:03:09 2017 -- 00002_rename_root.go
Pending -- 00003_rename_admin.go
Fri Oct 6 13:03:09 2017 -- 00004_rename_admin.go
Pending -- 00005_rename_admin.go
$ go run main.go -dir ./db/migrations -missing-only sqlite3 ./db/foo.db status
Missing migrations
===========
00003_rename_admin.go
00005_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db up-by-one
OK 00003_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db up-by-one
OK 00005_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db status
Applied At Migration
=======================================
Fri Oct 6 13:03:09 2017 -- 00001_create_users_table.sql
Fri Oct 6 13:03:09 2017 -- 00002_rename_root.go
Fri Oct 6 13:20:52 2017 -- 00003_rename_admin.go
Fri Oct 6 13:03:09 2017 -- 00004_rename_admin.go
Fri Oct 6 13:20:56 2017 -- 00005_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
OK 00005_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
OK 00003_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
OK 00004_rename_admin.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
OK 00002_rename_root.go
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
OK 00001_create_users_table.sql
$ go run main.go -dir ./db/migrations sqlite3 ./db/foo.db down
2017/10/06 16:21:27 goose run: no migration 0
exit status 1
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- +goose NO TRANSACTION
-- +goose Up
CREATE TABLE users (
id int NOT NULL PRIMARY KEY,
username text,
name text,
surname text
);

INSERT INTO users VALUES
(0, 'root', '', ''),
(1, 'stepych', 'Stepan', 'Bayburtyan');

-- +goose Down
DROP TABLE users;
27 changes: 27 additions & 0 deletions examples/go-migrations-package/db/migrations/00002_rename_root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package migrations

import (
"database/sql"

"github.com/mc2soft/goose"
)

func init() {
goose.AddMigration(Up00002, Down00002)
}

func Up00002(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
if err != nil {
return err
}
return nil
}

func Down00002(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
if err != nil {
return err
}
return nil
}
27 changes: 27 additions & 0 deletions examples/go-migrations-package/db/migrations/00004_rename_admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package migrations

import (
"database/sql"

"github.com/mc2soft/goose"
)

func init() {
goose.AddMigration(Up00004, Down00004)
}

func Up00004(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin4' WHERE username='admin';")
if err != nil {
return err
}
return nil
}

func Down00004(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='admin4';")
if err != nil {
return err
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package migrations

import (
"database/sql"

"github.com/mc2soft/goose"
)

func init() {
goose.AddMigration(Up00003, Down00003)
}

func Up00003(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin3' WHERE username='admin4';")
if err != nil {
return err
}
return nil
}

func Down00003(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin4' WHERE username='admin3';")
if err != nil {
return err
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package migrations

import (
"database/sql"

"github.com/mc2soft/goose"
)

func init() {
goose.AddMigration(Up00005, Down00005)
}

func Up00005(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin5' WHERE username='admin3';")
if err != nil {
return err
}
return nil
}

func Down00005(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin3' WHERE username='admin5';")
if err != nil {
return err
}
return nil
}
Loading