twitch-bot/pkg/database/migration.go

95 lines
2.1 KiB
Go

package database
import (
"path"
"strconv"
"strings"
"github.com/pkg/errors"
)
func (c connector) Migrate(module string, migrations MigrationStorage) error {
m, err := collectMigrations(migrations, "/")
if err != nil {
return errors.Wrap(err, "collecting migrations")
}
migrationKey := strings.Join([]string{"migration_state", module}, "-")
var lastMigration int
if err = c.ReadCoreMeta(migrationKey, &lastMigration); err != nil && !errors.Is(err, ErrCoreMetaNotFound) {
return errors.Wrap(err, "getting last migration")
}
nextMigration := lastMigration
for {
nextMigration++
filename := m[nextMigration]
if filename == "" {
break
}
if err = c.applyMigration(migrations, filename); err != nil {
return errors.Wrapf(err, "applying migration %d", nextMigration)
}
if err = c.StoreCoreMeta(migrationKey, nextMigration); err != nil {
return errors.Wrap(err, "updating migration number")
}
}
return nil
}
func (c connector) applyMigration(migrations MigrationStorage, filename string) error {
rawMigration, err := migrations.ReadFile(filename)
if err != nil {
return errors.Wrap(err, "reading migration file")
}
_, err = c.db.Exec(string(rawMigration))
return errors.Wrap(err, "executing migration statement(s)")
}
func collectMigrations(migrations MigrationStorage, dir string) (map[int]string, error) {
out := map[int]string{}
entries, err := migrations.ReadDir(dir)
if err != nil {
return nil, errors.Wrapf(err, "reading dir %q", dir)
}
for _, e := range entries {
if e.IsDir() {
sout, err := collectMigrations(migrations, path.Join(dir, e.Name()))
if err != nil {
return nil, errors.Wrapf(err, "scanning subdir %q", e.Name())
}
for n, p := range sout {
if out[n] != "" {
return nil, errors.Errorf("migration %d found more than once", n)
}
out[n] = p
}
continue
}
if !migrationFilename.MatchString(e.Name()) {
continue
}
matches := migrationFilename.FindStringSubmatch(e.Name())
n, err := strconv.Atoi(matches[1])
if err != nil {
return nil, errors.Wrap(err, "parsing migration number")
}
out[n] = path.Join(dir, e.Name())
}
return out, nil
}