Add status command (#120)

Add new command to list applied and pending migrations.

Closes #28
Closes #32
Closes #66
Closes #120
This commit is contained in:
fcarletti 2020-02-27 23:24:11 -06:00 committed by Adrian Macneil
parent f7a3390299
commit 256f92ad19
5 changed files with 143 additions and 4 deletions

View file

@ -35,6 +35,14 @@ type DB struct {
WaitTimeout time.Duration
}
// migrationFileRegexp pattern for valid migration files
var migrationFileRegexp = regexp.MustCompile(`^\d.*\.sql$`)
type statusResult struct {
filename string
applied bool
}
// New initializes a new dbmate database
func New(databaseURL *url.URL) *DB {
return &DB{
@ -253,8 +261,7 @@ func (db *DB) openDatabaseForMigration() (Driver, *sql.DB, error) {
// Migrate migrates database to the latest version
func (db *DB) Migrate() error {
re := regexp.MustCompile(`^\d.*\.sql$`)
files, err := findMigrationFiles(db.MigrationsDir, re)
files, err := findMigrationFiles(db.MigrationsDir, migrationFileRegexp)
if err != nil {
return err
}
@ -445,3 +452,66 @@ func (db *DB) Rollback() error {
return nil
}
func checkMigrationsStatus(db *DB) ([]statusResult, error) {
files, err := findMigrationFiles(db.MigrationsDir, migrationFileRegexp)
if err != nil {
return nil, err
}
if len(files) == 0 {
return nil, fmt.Errorf("no migration files found")
}
drv, sqlDB, err := db.openDatabaseForMigration()
if err != nil {
return nil, err
}
defer mustClose(sqlDB)
applied, err := drv.SelectMigrations(sqlDB, -1)
if err != nil {
return nil, err
}
var results []statusResult
for _, filename := range files {
ver := migrationVersion(filename)
res := statusResult{filename: filename}
if ok := applied[ver]; ok {
res.applied = true
} else {
res.applied = false
}
results = append(results, res)
}
return results, nil
}
// Status shows the status of all migrations
func (db *DB) Status() error {
results, err := checkMigrationsStatus(db)
if err != nil {
return err
}
var totalApplied int
for _, res := range results {
if res.applied {
fmt.Println("[X]", res.filename)
totalApplied++
} else {
fmt.Println("[ ]", res.filename)
}
}
fmt.Println()
fmt.Printf("Applied: %d\n", totalApplied)
fmt.Printf("Pending: %d\n", len(results)-totalApplied)
return nil
}

View file

@ -298,6 +298,9 @@ func testRollbackURL(t *testing.T, u *url.URL) {
require.NoError(t, err)
require.Equal(t, 1, count)
err = sqlDB.QueryRow("select count(*) from posts").Scan(&count)
require.Nil(t, err)
// rollback
err = db.Rollback()
require.NoError(t, err)
@ -305,9 +308,9 @@ func testRollbackURL(t *testing.T, u *url.URL) {
// verify rollback
err = sqlDB.QueryRow("select count(*) from schema_migrations").Scan(&count)
require.NoError(t, err)
require.Equal(t, 0, count)
require.Equal(t, 1, count)
err = sqlDB.QueryRow("select count(*) from users").Scan(&count)
err = sqlDB.QueryRow("select count(*) from posts").Scan(&count)
require.NotNil(t, err)
require.Regexp(t, "(does not exist|doesn't exist|no such table)", err.Error())
}
@ -317,3 +320,53 @@ func TestRollback(t *testing.T) {
testRollbackURL(t, u)
}
}
func testStatusUrl(t *testing.T, u *url.URL) {
db := newTestDB(t, u)
// drop, recreate, and migrate database
err := db.Drop()
require.NoError(t, err)
err = db.Create()
require.NoError(t, err)
// verify migration
sqlDB, err := GetDriverOpen(u)
require.NoError(t, err)
defer mustClose(sqlDB)
// two pending
results, err := checkMigrationsStatus(db)
require.NoError(t, err)
require.Len(t, results, 2)
require.False(t, results[0].applied)
require.False(t, results[1].applied)
// run migrations
err = db.Migrate()
require.NoError(t, err)
// two applied
results, err = checkMigrationsStatus(db)
require.NoError(t, err)
require.Len(t, results, 2)
require.True(t, results[0].applied)
require.True(t, results[1].applied)
// rollback last migration
err = db.Rollback()
require.NoError(t, err)
// one applied, one pending
results, err = checkMigrationsStatus(db)
require.NoError(t, err)
require.Len(t, results, 2)
require.True(t, results[0].applied)
require.False(t, results[1].applied)
}
func TestStatus(t *testing.T) {
for _, u := range testURLs(t) {
testStatusUrl(t, u)
}
}