158 lines
3.8 KiB
Go
158 lines
3.8 KiB
Go
package main
|
||
|
||
import (
|
||
"flag"
|
||
"fmt"
|
||
"os"
|
||
|
||
"git.toowon.com/jimmy/go-common/factory"
|
||
"git.toowon.com/jimmy/go-common/migration"
|
||
)
|
||
|
||
// 数据库迁移 CLI 脚手架。
|
||
//
|
||
// mkdir -p cmd/migrate
|
||
// cp templates/migrate/main.go cmd/migrate/main.go
|
||
// go build -o bin/migrate cmd/migrate/main.go
|
||
// ./bin/migrate up|status|down
|
||
//
|
||
// 默认已接好:Factory 初始化、日志、Database Warmup、Migrator、Close 收口。
|
||
// 配置优先级:-config/-dir > 环境变量 CONFIG_FILE/MIGRATIONS_DIR > 默认值。
|
||
|
||
func main() {
|
||
var (
|
||
configFile string
|
||
migrationsDir string
|
||
showHelp bool
|
||
)
|
||
|
||
flag.StringVar(&configFile, "config", "", "配置文件路径(默认 config.json)")
|
||
flag.StringVar(&configFile, "c", "", "配置文件路径(简写)")
|
||
flag.StringVar(&migrationsDir, "dir", "", "迁移目录(默认 migrations)")
|
||
flag.StringVar(&migrationsDir, "d", "", "迁移目录(简写)")
|
||
flag.BoolVar(&showHelp, "help", false, "显示帮助")
|
||
flag.BoolVar(&showHelp, "h", false, "显示帮助(简写)")
|
||
flag.Parse()
|
||
|
||
if showHelp {
|
||
printHelp()
|
||
return
|
||
}
|
||
|
||
command := "up"
|
||
if args := flag.Args(); len(args) > 0 {
|
||
command = args[0]
|
||
}
|
||
if command != "up" && command != "down" && command != "status" {
|
||
fmt.Fprintf(os.Stderr, "未知命令: %s\n\n", command)
|
||
printHelp()
|
||
os.Exit(1)
|
||
}
|
||
|
||
if configFile == "" {
|
||
configFile = envOr("CONFIG_FILE", "config.json")
|
||
}
|
||
if migrationsDir == "" {
|
||
migrationsDir = envOr("MIGRATIONS_DIR", "migrations")
|
||
}
|
||
|
||
app := factory.MustInit(configFile, buildOptions()...)
|
||
defer app.Close()
|
||
|
||
log := app.MustLogger()
|
||
|
||
if err := app.Warmup(factory.ModuleLogger, factory.ModuleDatabase); err != nil {
|
||
log.Error("warmup failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
|
||
m, err := app.Migrator(migrationsDir)
|
||
if err != nil {
|
||
log.Error("create migrator failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
|
||
switch command {
|
||
case "up":
|
||
if err := m.Up(); err != nil {
|
||
log.Error("migrate up failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
log.Info("migrate up ok", map[string]any{"dir": migrationsDir})
|
||
fmt.Println("✓ 迁移执行成功")
|
||
|
||
case "down":
|
||
if err := m.Down(); err != nil {
|
||
log.Error("migrate down failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
log.Info("migrate down ok", map[string]any{"dir": migrationsDir})
|
||
fmt.Println("✓ 迁移回滚成功")
|
||
|
||
case "status":
|
||
status, err := m.Status()
|
||
if err != nil {
|
||
log.Error("migrate status failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
printStatus(status)
|
||
}
|
||
}
|
||
|
||
// buildOptions 组装 Factory Option(自定义 DB / Logger 等在此注入)
|
||
func buildOptions() []factory.Option {
|
||
// 默认走 config.json。需要替换时例如:
|
||
//
|
||
// return []factory.Option{
|
||
// factory.WithLogger(customLogger),
|
||
// factory.WithDatabase(db),
|
||
// }
|
||
return nil
|
||
}
|
||
|
||
func printStatus(status []migration.MigrationStatus) {
|
||
if len(status) == 0 {
|
||
fmt.Println("没有找到迁移")
|
||
return
|
||
}
|
||
fmt.Println("\n迁移状态:")
|
||
fmt.Printf("%-20s %-40s %-10s\n", "版本", "描述", "状态")
|
||
for _, s := range status {
|
||
st := "待执行"
|
||
if s.Applied {
|
||
st = "已应用"
|
||
}
|
||
fmt.Printf("%-20s %-40s %-10s\n", s.Version, s.Description, st)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
func envOr(key, fallback string) string {
|
||
if v := os.Getenv(key); v != "" {
|
||
return v
|
||
}
|
||
return fallback
|
||
}
|
||
|
||
func printHelp() {
|
||
fmt.Println(`数据库迁移工具
|
||
|
||
用法:
|
||
migrate [命令] [选项]
|
||
|
||
命令:
|
||
up 执行待执行迁移(默认)
|
||
down 回滚最后一个迁移
|
||
status 查看状态
|
||
|
||
选项:
|
||
-config, -c 配置文件(默认: config.json,可用 CONFIG_FILE)
|
||
-dir, -d 迁移目录(默认: migrations,可用 MIGRATIONS_DIR)
|
||
-help, -h 帮助
|
||
|
||
示例:
|
||
migrate up
|
||
migrate up -config /etc/app/config.json -dir db/migrations
|
||
CONFIG_FILE=config.json migrate status`)
|
||
}
|