Files
pangolin/server/cmd/migrate/main.go
T
wangjia 52857d1d55 feat(1e): config PANGOLIN_ prefix + store.Open UTC DSN + go:embed migrations (tsk_zRA6fGU1JuHj)
- internal/config: rewrite Config with PANGOLIN_ prefix fields
  (HTTPAddr, AdminAddr, GRPCAddr, MySQL DSN/fields, RedisAddr,
  AutoMigrate, JWTSecret placeholder, WebhookSecret); Load() replaces
  FromEnv(); missing required MySQL vars return named-field error.

- internal/store/mysql.go: single Open(cfg) entry point; uses
  mysql.ParseDSN to structurally override ParseTime=true, Loc=UTC,
  Collation=utf8mb4_unicode_ci, Params[time_zone]='+00:00'; asserts
  SELECT @@session.time_zone=+00:00 after Ping (startup fatal).

- migrations/embed.go: //go:embed *.sql exposes var FS embed.FS.

- internal/store/migrate.go: MigrateUp/MigrateDown/MigrateVersion
  backed by golang-migrate iofs source + mysql driver; ErrNoChange
  treated as success.

- cmd/migrate/main.go: filled — up/down/version subcommands, reads
  config.Load() + store.Open.

- cmd/server/main.go: startup sequence Load → store.Open (UTC assert)
  → MigrateUp (if PANGOLIN_AUTO_MIGRATE=true) → HTTP listen;
  structured slog output at each step.

- internal/store/mysql_test.go: pure-function unit tests for buildDSN
  (empty-fields case + conflicting params overridden case); both pass.
- internal/store/mysql_integration_test.go: //go:build integration;
  testcontainers mysql:8 — UTC assertion + MigrateUp idempotency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:52:27 +08:00

80 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Command migrate applies or reverts database schema migrations for the
// Pangolin server.
//
// Usage:
//
// migrate up apply all pending migrations
// migrate down revert all applied migrations
// migrate version print the current migration version
//
// Configuration is read from environment variables (PANGOLIN_ prefix).
// Set PANGOLIN_MYSQL_DSN or the individual PANGOLIN_MYSQL_HOST / USER /
// PASSWORD / DBNAME vars before running.
package main
import (
"fmt"
"log"
"os"
"github.com/wangjia/pangolin/server/internal/config"
"github.com/wangjia/pangolin/server/internal/store"
)
func main() {
if len(os.Args) < 2 {
usage()
os.Exit(1)
}
cmd := os.Args[1]
cfg, err := config.Load()
if err != nil {
log.Fatalf("migrate: config: %v", err)
}
db, err := store.Open(cfg)
if err != nil {
log.Fatalf("migrate: db: %v", err)
}
defer db.Close()
switch cmd {
case "up":
if err := store.MigrateUp(db); err != nil {
log.Fatalf("migrate up: %v", err)
}
log.Println("migrate: up — done")
case "down":
if err := store.MigrateDown(db); err != nil {
log.Fatalf("migrate down: %v", err)
}
log.Println("migrate: down — done")
case "version":
version, dirty, err := store.MigrateVersion(db)
if err != nil {
log.Fatalf("migrate version: %v", err)
}
fmt.Printf("migrate: version=%d dirty=%v\n", version, dirty)
default:
fmt.Fprintf(os.Stderr, "migrate: unknown command %q\n", cmd)
usage()
os.Exit(1)
}
}
func usage() {
fmt.Fprintln(os.Stderr, "Usage: migrate <up|down|version>")
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "Environment variables:")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_DSN full DSN (takes precedence)")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_HOST MySQL host (required if no DSN)")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_PORT MySQL port (default 3306)")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_USER MySQL user (required if no DSN)")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_PASSWORD MySQL password")
fmt.Fprintln(os.Stderr, " PANGOLIN_MYSQL_DBNAME MySQL database (required if no DSN)")
}