diff --git a/doc.go b/doc.go index 8ff186d0..8a09c972 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,13 @@ /* SQL Schema migration tool for Go. +The package provides functions for parsing and executing database migrations, +as well as utilities for working with configuration settings: + + - Functions to set migration table name and schema + - GetEnvironmentFromConfig for loading environment configuration from YAML + - Migration runners for up/down migrations + Key features: - Usable as a CLI tool or as a library diff --git a/migrate.go b/migrate.go index c9cb4a48..343ad5b4 100644 --- a/migrate.go +++ b/migrate.go @@ -18,6 +18,7 @@ import ( "time" "github.com/go-gorp/gorp/v3" + "gopkg.in/yaml.v2" "github.com/rubenv/sql-migrate/sqlparse" ) @@ -768,6 +769,58 @@ func SkipMax(db *sql.DB, dialect string, m MigrationSource, dir MigrationDirecti return applied, nil } +// EnvironmentConfig holds the configuration for a database environment +type EnvironmentConfig struct { + Dialect string + DataSource string + Dir string + TableName string + SchemaName string + IgnoreUnknown bool +} + +// GetEnvironmentFromConfig creates an environment configuration from provided config data +// This allows users to access environment configuration functionality from the library +// without depending on the command-line tool's config.go implementation. +func GetEnvironmentFromConfig(configData []byte, environment string) (*EnvironmentConfig, error) { + config := make(map[string]*EnvironmentConfig) + + err := yaml.Unmarshal(configData, config) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal config: %w", err) + } + + env := config[environment] + if env == nil { + return nil, fmt.Errorf("no environment found: %s", environment) + } + + if env.Dialect == "" { + return nil, errors.New("no dialect specified") + } + + if env.DataSource == "" { + return nil, errors.New("no data source specified") + } + env.DataSource = os.ExpandEnv(env.DataSource) + + if env.Dir == "" { + env.Dir = "migrations" + } + + if env.TableName != "" { + SetTable(env.TableName) + } + + if env.SchemaName != "" { + SetSchema(env.SchemaName) + } + + SetIgnoreUnknown(env.IgnoreUnknown) + + return env, nil +} + // Filter a slice of migrations into ones that should be applied. func ToApply(migrations []*Migration, current string, direction MigrationDirection) []*Migration { index := -1 diff --git a/sql-migrate/command_status.go b/sql-migrate/command_status.go index 9ac2f278..a661cde7 100644 --- a/sql-migrate/command_status.go +++ b/sql-migrate/command_status.go @@ -74,30 +74,24 @@ func (c *StatusCommand) Run(args []string) int { table.SetHeader([]string{"Migration", "Applied"}) table.SetColWidth(60) - rows := make(map[string]*statusRow) - + // Build a map of source migrations for quick lookup + sourceMigrations := make(map[string]*migrate.Migration) for _, m := range migrations { - rows[m.Id] = &statusRow{ - Id: m.Id, - Migrated: false, - } + sourceMigrations[m.Id] = m } - for _, r := range records { - if rows[r.Id] == nil { - ui.Warn(fmt.Sprintf("Could not find migration file: %v", r.Id)) - continue - } - - rows[r.Id].Migrated = true - rows[r.Id].AppliedAt = r.AppliedAt + // Build a map of applied migrations for quick lookup + applied := make(map[string]*migrate.MigrationRecord) + for _, m := range records { + applied[m.Id] = m } + // Print status for all migrations in the source for _, m := range migrations { - if rows[m.Id] != nil && rows[m.Id].Migrated { + if rec, ok := applied[m.Id]; ok { table.Append([]string{ m.Id, - rows[m.Id].AppliedAt.String(), + rec.AppliedAt.Format(time.RFC3339), }) } else { table.Append([]string{ @@ -107,6 +101,16 @@ func (c *StatusCommand) Run(args []string) int { } } + // Print status for migrations in DB but not in source + for _, m := range records { + if _, ok := sourceMigrations[m.Id]; !ok { + table.Append([]string{ + m.Id, + fmt.Sprintf("yes (missing in source, applied at %s)", m.AppliedAt.Format(time.RFC3339)), + }) + } + } + table.Render() return 0 diff --git a/sql-migrate/config.go b/sql-migrate/config.go index 6947a534..1b8f36f8 100644 --- a/sql-migrate/config.go +++ b/sql-migrate/config.go @@ -58,6 +58,8 @@ func ReadConfig() (map[string]*Environment, error) { return config, nil } +// GetEnvironment returns the current environment configuration. +// This function is now exported for library use. func GetEnvironment() (*Environment, error) { config, err := ReadConfig() if err != nil {