diff --git a/dialect/sql/schema/atlas.go b/dialect/sql/schema/atlas.go index ab75c5e4e..e300dac94 100644 --- a/dialect/sql/schema/atlas.go +++ b/dialect/sql/schema/atlas.go @@ -14,6 +14,7 @@ import ( "ariga.io/atlas/sql/migrate" "ariga.io/atlas/sql/schema" + "ariga.io/atlas/sql/sqltool" "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" @@ -315,7 +316,7 @@ func (m *Migrate) setupAtlas() error { m.atlas.diff = append(m.atlas.diff, withoutForeignKeys) } if m.atlas.dir != nil && m.atlas.fmt == nil { - m.atlas.fmt = migrate.DefaultFormatter + m.atlas.fmt = sqltool.GolangMigrateFormatter } return nil } diff --git a/dialect/sql/schema/migrate_test.go b/dialect/sql/schema/migrate_test.go index 4c95e8b72..59b2522f4 100644 --- a/dialect/sql/schema/migrate_test.go +++ b/dialect/sql/schema/migrate_test.go @@ -8,7 +8,6 @@ import ( "context" "os" "path/filepath" - "strconv" "testing" "time" @@ -81,9 +80,9 @@ func TestMigrate_Diff(t *testing.T) { m, err := NewMigrate(db, WithDir(d)) require.NoError(t, err) require.NoError(t, m.Diff(context.Background(), &Table{Name: "users"})) - v := strconv.FormatInt(time.Now().Unix(), 10) - requireFileEqual(t, filepath.Join(p, v+"_changes.up.sql"), "CREATE TABLE `users` (, PRIMARY KEY ());\n") - requireFileEqual(t, filepath.Join(p, v+"_changes.down.sql"), "DROP TABLE `users`;\n") + v := time.Now().Format("20060102150405") + requireFileEqual(t, filepath.Join(p, v+"_changes.up.sql"), "-- create \"users\" table\nCREATE TABLE `users` (, PRIMARY KEY ());\n") + requireFileEqual(t, filepath.Join(p, v+"_changes.down.sql"), "-- reverse: create \"users\" table\nDROP TABLE `users`;\n") require.NoFileExists(t, filepath.Join(p, "atlas.sum")) } diff --git a/doc/md/versioned-migrations.md b/doc/md/versioned-migrations.md index 5c1f08e64..92f1b7ba5 100644 --- a/doc/md/versioned-migrations.md +++ b/doc/md/versioned-migrations.md @@ -176,100 +176,68 @@ versioned migration, you need to take some extra steps. ## Use a Custom Formatter -Atlas' migration engine comes with great customizability. By the use of a custom `Formatter` you can generate the migration files in a format compatible with another tool for migration management: [pressly/goose](https://github.com/pressly/goose). +Atlas' migration engine comes with great customizability. By the use of a custom `Formatter` you can generate the +migration files in a format compatible with other migration management tools and Atlas has built-in support for the +following four: + +1. [golang-migrate/migrate](https://github.com/golang-migrate/migrate) +2. [pressly/goose](https://github.com/pressly/goose) +3. [Flyway](https://flywaydb.org/) +4. [Liquibase](https://www.liquibase.org/) ```go package main import ( - "context" - "log" - "strings" - "text/template" - "time" + "context" + "log" + "strings" + "text/template" + "time" - "ariga.io/atlas/sql/migrate" - "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/schema" - "entgo.io/ent/entc" - "entgo.io/ent/entc/gen" + "ariga.io/atlas/sql/migrate" + "ariga.io/atlas/sql/sqltool" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/schema" + "entgo.io/ent/entc" + "entgo.io/ent/entc/gen" ) -var ( - templateFuncs = template.FuncMap{ - "now": time.Now, - "sem": ensureSemicolonSuffix, - "rev": reverse, - } - // highlight-start - // gooseFormatter is an implementation for compatible formatter with goose. - gooseFormatter, _ = migrate.NewTemplateFormatter( - template.Must( - template.New(""). - Funcs(templateFuncs). - Parse(`{{now.Format "20060102150405"}}_{{.Name}}.sql`), - ), - template.Must(template.New("").Funcs(templateFuncs).Parse(`-- +goose Up --- +goose StatementBegin -{{ range .Changes }}{{ println (sem .Cmd) }}{{ end -}} --- +goose StatementEnd - --- +goose Down --- +goose StatementBegin -{{ range rev .Changes }}{{ with .Reverse }}{{ println (sem .) }}{{ end }}{{ end -}} --- +goose StatementEnd`)), - ) - // highlight-end -) - -func reverse(changes []*migrate.Change) []*migrate.Change { - n := len(changes) - rev := make([]*migrate.Change, n) - if n%2 == 1 { - rev[n/2] = changes[n/2] - } - for i, j := 0, n-1; i < j; i, j = i+1, j-1 { - rev[i], rev[j] = changes[j], changes[i] - } - return rev -} - -func ensureSemicolonSuffix(s string) string { - if !strings.HasSuffix(s, ";") { - return s + ";" - } - return s -} - func main() { - // Load the graph. - graph, err := entc.LoadGraph("/.schema", &gen.Config{}) - if err != nil { - log.Fatalln(err) - } - tbls, err := graph.Tables() - if err != nil { - log.Fatalln(err) - } - // Create a local migration directory. - d, err := migrate.NewLocalDir("migrations") - if err != nil { - log.Fatalln(err) - } - // Open connection to the database. - dlct, err := sql.Open("mysql", "root:pass@tcp(localhost:3306)/test") - if err != nil { - log.Fatalln(err) - } - // Inspect it and compare it with the graph. - // highlight-start - m, err := schema.NewMigrate(dlct, schema.WithDir(d), schema.WithFormatter(gooseFormatter)) - // highlight-end - if err != nil { - log.Fatalln(err) - } - if err := m.Diff(context.Background(), tbls...); err != nil { - log.Fatalln(err) - } + // Load the graph. + graph, err := entc.LoadGraph("/.schema", &gen.Config{}) + if err != nil { + log.Fatalln(err) + } + tbls, err := graph.Tables() + if err != nil { + log.Fatalln(err) + } + // Create a local migration directory. + d, err := migrate.NewLocalDir("migrations") + if err != nil { + log.Fatalln(err) + } + // Open connection to the database. + dlct, err := sql.Open("mysql", "root:pass@tcp(localhost:3306)/test") + if err != nil { + log.Fatalln(err) + } + // Inspect it and compare it with the graph. + m, err := schema.NewMigrate(dlct, schema.WithDir(d), + // highlight-start + // Chose one of the below. + schema.WithFormatter(sqltool.GolangMigrateFormatter), + schema.WithFormatter(sqltool.GooseFormatter), + schema.WithFormatter(sqltool.FlywayFormatter), + schema.WithFormatter(sqltool.LiquibaseFormatter), + // highlight-end + ) + if err != nil { + log.Fatalln(err) + } + if err := m.Diff(context.Background(), tbls...); err != nil { + log.Fatalln(err) + } } ``` diff --git a/go.mod b/go.mod index dc355f72d..f7d4c34b7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module entgo.io/ent go 1.17 require ( - ariga.io/atlas v0.3.8-0.20220408073907-ec24677b8189 + ariga.io/atlas v0.3.8-0.20220412124059-62aea3b5b2b3 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/go-openapi/inflect v0.19.0 github.com/go-sql-driver/mysql v1.6.0 diff --git a/go.sum b/go.sum index 786148e23..45b313c8f 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,7 @@ -ariga.io/atlas v0.3.8-0.20220324144249-d5d77d7dadfa h1:v+ZNqhCtR0uylXshIfghFGHTvpJrxB+9iNjWQV2UXak= -ariga.io/atlas v0.3.8-0.20220324144249-d5d77d7dadfa/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= -ariga.io/atlas v0.3.8-0.20220407071148-7ea9876895d3 h1:2EhkjHhv8ICTFSRUaPdoKqfz4tPe21z875xjsaAKdBI= -ariga.io/atlas v0.3.8-0.20220407071148-7ea9876895d3/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= -ariga.io/atlas v0.3.8-0.20220407124003-f50cf4c884f2 h1:TpqxB+KZnOFbY/1qSLBNvT9/FmNhar6RhbkOUliJ6s4= -ariga.io/atlas v0.3.8-0.20220407124003-f50cf4c884f2/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= -ariga.io/atlas v0.3.8-0.20220408073907-ec24677b8189 h1:R8iwOEzgGDZtX9V96jtHId1fZL8sOyDtF3pdKt5uAQg= -ariga.io/atlas v0.3.8-0.20220408073907-ec24677b8189/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= +ariga.io/atlas v0.3.8-0.20220411180353-6934b0d5dad2 h1:9qe4hjt+HaWQn8fJ2xUWXGSDIvakDva4bqdjhuMVFHY= +ariga.io/atlas v0.3.8-0.20220411180353-6934b0d5dad2/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= +ariga.io/atlas v0.3.8-0.20220412124059-62aea3b5b2b3 h1:KG7fS5TnLwEOcFZ1jA6AFBVLdAXQrLfylocOIvhZD5w= +ariga.io/atlas v0.3.8-0.20220412124059-62aea3b5b2b3/go.mod h1:2aAhlHuY8tr7rPNz2MgOBTJB/cVjK0sv1VvTIqeYSws= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=