prefix migration files with epoch

Using epoch prefixed filenames, multiple developers can commit
migrations while minimizing versioning conflicts.

This commit remains backwards compatible with all previous migrations
and requires no changes to the `schema_migrations` table.

This commit also cleans up migrate-test temp directories
This commit is contained in:
David Campbell 2015-12-15 17:57:16 -08:00
parent 6028882529
commit 9694775972
2 changed files with 132 additions and 106 deletions

View File

@ -10,6 +10,7 @@ import (
"path"
"strconv"
"strings"
"time"
"github.com/mattes/migrate/driver"
"github.com/mattes/migrate/file"
@ -212,20 +213,23 @@ func Create(url, migrationsPath, name string) (*file.MigrationFile, error) {
if err != nil {
return nil, err
}
files, err := file.ReadMigrationFiles(migrationsPath, file.FilenameRegex(d.FilenameExtension()))
if err != nil {
return nil, err
}
version := uint64(0)
if len(files) > 0 {
lastFile := files[len(files)-1]
version = lastFile.Version
version := uint64(time.Now().Unix())
for _, f := range files {
if f.Version == version {
version++
}
}
version += 1
versionStr := strconv.FormatUint(version, 10)
length := 4 // TODO(mattes) check existing files and try to guess length
length := 10
if len(versionStr)%length != 0 {
versionStr = strings.Repeat("0", length-len(versionStr)%length) + versionStr
}

View File

@ -3,7 +3,9 @@ package migrate
import (
"io/ioutil"
"os"
"regexp"
"testing"
// Ensure imports for each driver we wish to test
_ "github.com/mattes/migrate/driver/postgres"
@ -15,6 +17,11 @@ var driverUrls = []string{
"postgres://postgres@" + os.Getenv("POSTGRES_PORT_5432_TCP_ADDR") + ":" + os.Getenv("POSTGRES_PORT_5432_TCP_PORT") + "/template1?sslmode=disable",
}
func tearDown(driverUrl, tmpdir string) {
DownSync(driverUrl, tmpdir)
os.RemoveAll(tmpdir)
}
func TestCreate(t *testing.T) {
for _, driverUrl := range driverUrls {
t.Logf("Test driver: %s", driverUrl)
@ -22,6 +29,7 @@ func TestCreate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
if _, err := Create(driverUrl, tmpdir, "test_migration"); err != nil {
t.Fatal(err)
@ -35,23 +43,33 @@ func TestCreate(t *testing.T) {
t.Fatal(err)
}
if len(files) != 4 {
t.Fatal("Expected 2 new files, got", len(files))
t.Fatal("Expected 4 new files, got", len(files))
}
fileNameRegexp := regexp.MustCompile(`^\d{10}_(.*.[up|down].sql)`)
expectFiles := []string{
"0001_test_migration.up.sql", "0001_test_migration.down.sql",
"0002_another_migration.up.sql", "0002_another_migration.down.sql",
"test_migration.up.sql", "test_migration.down.sql",
"another_migration.up.sql", "another_migration.down.sql",
}
foundCounter := 0
for _, expectFile := range expectFiles {
for _, file := range files {
if expectFile == file.Name() {
foundCounter += 1
break
var foundCounter int
for _, file := range files {
if x := fileNameRegexp.FindStringSubmatch(file.Name()); len(x) != 2 {
t.Errorf("expected %v to match %v", file.Name(), fileNameRegexp)
} else {
for _, expect := range expectFiles {
if expect == x[1] {
foundCounter++
break
}
}
}
}
if foundCounter != len(expectFiles) {
t.Error("not all expected files have been found")
t.Errorf("expected %v files, got %v", len(expectFiles), foundCounter)
}
}
}
@ -59,24 +77,26 @@ func TestCreate(t *testing.T) {
func TestReset(t *testing.T) {
for _, driverUrl := range driverUrls {
t.Logf("Test driver: %s", driverUrl)
tmpdir, err := ioutil.TempDir("/", "migrate-test")
tmpdir, err := ioutil.TempDir("/tmp", "migrate-test")
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
Create(driverUrl, tmpdir, "migration1")
Create(driverUrl, tmpdir, "migration2")
errs, ok := ResetSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err := Version(driverUrl, tmpdir)
f, err := Create(driverUrl, tmpdir, "migration2")
if err != nil {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != f.Version {
t.Fatalf("Expected version %v, got %v", version, f.Version)
}
}
}
@ -88,32 +108,33 @@ func TestDown(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
Create(driverUrl, tmpdir, "migration1")
Create(driverUrl, tmpdir, "migration2")
initVersion, _ := Version(driverUrl, tmpdir)
errs, ok := ResetSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err := Version(driverUrl, tmpdir)
if err != nil {
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != secondMigration.Version {
t.Fatalf("Expected version %v, got %v", version, secondMigration.Version)
}
errs, ok = DownSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err = Version(driverUrl, tmpdir)
if err != nil {
if err, ok := DownSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 0 {
t.Fatalf("Expected version 0, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != 0 {
t.Fatalf("Expected 0, got %v", version)
}
}
}
@ -125,32 +146,33 @@ func TestUp(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
Create(driverUrl, tmpdir, "migration1")
Create(driverUrl, tmpdir, "migration2")
initVersion, _ := Version(driverUrl, tmpdir)
errs, ok := DownSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err := Version(driverUrl, tmpdir)
if err != nil {
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
if err, ok := DownSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 0 {
t.Fatalf("Expected version 0, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != initVersion {
t.Fatalf("Expected initial version %v, got %v", initVersion, version)
}
errs, ok = UpSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err = Version(driverUrl, tmpdir)
if err != nil {
if err, ok := UpSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != secondMigration.Version {
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
}
}
}
@ -162,32 +184,33 @@ func TestRedo(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
Create(driverUrl, tmpdir, "migration1")
Create(driverUrl, tmpdir, "migration2")
initVersion, _ := Version(driverUrl, tmpdir)
errs, ok := ResetSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err := Version(driverUrl, tmpdir)
if err != nil {
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != secondMigration.Version {
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
}
errs, ok = RedoSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err = Version(driverUrl, tmpdir)
if err != nil {
if err, ok := RedoSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != secondMigration.Version {
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
}
}
}
@ -199,44 +222,43 @@ func TestMigrate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer tearDown(driverUrl, tmpdir)
Create(driverUrl, tmpdir, "migration1")
Create(driverUrl, tmpdir, "migration2")
initVersion, _ := Version(driverUrl, tmpdir)
errs, ok := ResetSync(driverUrl, tmpdir)
if !ok {
t.Fatal(errs)
}
version, err := Version(driverUrl, tmpdir)
if err != nil {
firstMigration, _ := Create(driverUrl, tmpdir, "migration1")
secondMigration, _ := Create(driverUrl, tmpdir, "migration2")
t.Logf("init %v first %v second %v", initVersion, firstMigration.Version, secondMigration.Version)
if err, ok := ResetSync(driverUrl, tmpdir); !ok {
t.Fatal(err)
}
if version != 2 {
t.Fatalf("Expected version 2, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != secondMigration.Version {
t.Fatalf("Expected migrated version %v, got %v", secondMigration.Version, version)
}
errs, ok = MigrateSync(driverUrl, tmpdir, -2)
if !ok {
t.Fatal(errs)
}
version, err = Version(driverUrl, tmpdir)
if err != nil {
if err, ok := MigrateSync(driverUrl, tmpdir, -2); !ok {
t.Fatal(err)
}
if version != 0 {
t.Fatalf("Expected version 0, got %v", version)
}
errs, ok = MigrateSync(driverUrl, tmpdir, +1)
if !ok {
t.Fatal(errs)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != 0 {
t.Fatalf("Expected 0, got %v", version)
}
version, err = Version(driverUrl, tmpdir)
if err != nil {
if err, ok := MigrateSync(driverUrl, tmpdir, +1); !ok {
t.Fatal(err)
}
if version != 1 {
t.Fatalf("Expected version 1, got %v", version)
if version, err := Version(driverUrl, tmpdir); err != nil {
t.Fatal(err)
} else if version != firstMigration.Version {
t.Fatalf("Expected first version %v, got %v", firstMigration.Version, version)
}
}
}