From 1e5961e910f7b640fd8d5513f68b35ac7581e725 Mon Sep 17 00:00:00 2001 From: Nicolas Payart Date: Thu, 21 Aug 2025 18:17:30 +0200 Subject: [PATCH] feat(status_command): add json output option Signed-off-by: Nicolas Payart --- sql-migrate/command_status.go | 91 ++++++++++++++++++++++++++--------- sql-migrate/config.go | 2 + 2 files changed, 69 insertions(+), 24 deletions(-) diff --git a/sql-migrate/command_status.go b/sql-migrate/command_status.go index 9ac2f278..590bcc75 100644 --- a/sql-migrate/command_status.go +++ b/sql-migrate/command_status.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "flag" "fmt" "os" @@ -14,6 +15,12 @@ import ( type StatusCommand struct{} +type statusRow struct { + Id string + Migrated bool + AppliedAt time.Time +} + func (*StatusCommand) Help() string { helpText := ` Usage: sql-migrate status [options] ... @@ -24,6 +31,7 @@ Options: -config=dbconfig.yml Configuration file to use. -env="development" Environment. + -output="json" Print output in JSON format (default is table format). ` return strings.TrimSpace(helpText) @@ -70,10 +78,21 @@ func (c *StatusCommand) Run(args []string) int { return 1 } - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Migration", "Applied"}) - table.SetColWidth(60) + statusRows := buildStatusRows(migrations, records) + + if OutputFormat == "json" { + if err := printStatusJSON(statusRows); err != nil { + ui.Error(fmt.Sprintf("Could not encode JSON: %s", err)) + return 1 + } + } else { + printStatusTable(statusRows) + } + + return 0 +} +func buildStatusRows(migrations []*migrate.Migration, records []*migrate.MigrationRecord) []*statusRow { rows := make(map[string]*statusRow) for _, m := range migrations { @@ -84,36 +103,60 @@ func (c *StatusCommand) Run(args []string) int { } for _, r := range records { - if rows[r.Id] == nil { + if row, ok := rows[r.Id]; ok { + row.Migrated = true + row.AppliedAt = r.AppliedAt + } else { ui.Warn(fmt.Sprintf("Could not find migration file: %v", r.Id)) - continue } - - rows[r.Id].Migrated = true - rows[r.Id].AppliedAt = r.AppliedAt } + var result []*statusRow for _, m := range migrations { - if rows[m.Id] != nil && rows[m.Id].Migrated { - table.Append([]string{ - m.Id, - rows[m.Id].AppliedAt.String(), - }) - } else { - table.Append([]string{ - m.Id, - "no", - }) + result = append(result, rows[m.Id]) + } + return result +} + +func printStatusJSON(rows []*statusRow) error { + type jsonRow struct { + Migration string `json:"migration"` + Applied string `json:"applied"` + } + + var output []jsonRow + for _, r := range rows { + applied := "no" + if r.Migrated { + applied = r.AppliedAt.Format(time.RFC3339) } + output = append(output, jsonRow{ + Migration: r.Id, + Applied: applied, + }) } - table.Render() + data, err := json.MarshalIndent(output, "", " ") + if err != nil { + return err + } - return 0 + fmt.Println(string(data)) + return nil } -type statusRow struct { - Id string - Migrated bool - AppliedAt time.Time +func printStatusTable(rows []*statusRow) { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Migration", "Applied"}) + table.SetColWidth(60) + + for _, r := range rows { + applied := "no" + if r.Migrated { + applied = r.AppliedAt.Format("2006-01-02 15:04:05") + } + table.Append([]string{r.Id, applied}) + } + + table.Render() } diff --git a/sql-migrate/config.go b/sql-migrate/config.go index 6947a534..59f17f85 100644 --- a/sql-migrate/config.go +++ b/sql-migrate/config.go @@ -27,11 +27,13 @@ var dialects = map[string]gorp.Dialect{ var ( ConfigFile string ConfigEnvironment string + OutputFormat string ) func ConfigFlags(f *flag.FlagSet) { f.StringVar(&ConfigFile, "config", "dbconfig.yml", "Configuration file to use.") f.StringVar(&ConfigEnvironment, "env", "development", "Environment to use.") + f.StringVar(&OutputFormat, "output", "table", "Output format.") } type Environment struct {