feat: automate business upgrades and sync reviewed frontend
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package swmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
scripts "kra/deploy/migrations"
|
||||
)
|
||||
|
||||
func migrateFramework(ctx context.Context, db *gorm.DB, conn *sql.Conn, logger *slog.Logger) error {
|
||||
tables, err := tableSet(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tables["sw_kra_state"] {
|
||||
var previous struct{ Version, Phase string }
|
||||
if err := db.Table("sw_kra_state").Where("id=1").Take(&previous).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if previous.Version != "20261103_framework_migration_v5" {
|
||||
return fmt.Errorf("unsupported legacy migration version %q", previous.Version)
|
||||
}
|
||||
switch previous.Phase {
|
||||
case "completed":
|
||||
return nil
|
||||
case "data_validated":
|
||||
return finishArchives(ctx, db, conn)
|
||||
case "backup":
|
||||
// All framework data changes and the data_validated marker commit
|
||||
// together. In this phase only empty target DDL may survive a crash.
|
||||
if err := resetIncompleteFramework(ctx, db, conn, tables); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported partial migration phase %q", previous.Phase)
|
||||
}
|
||||
} else if !tables["sys_user"] {
|
||||
return fmt.Errorf("legacy source users and migration evidence are missing")
|
||||
}
|
||||
return executeScript(ctx, conn, scripts.FrameworkSQL(), false, logger)
|
||||
}
|
||||
|
||||
func resetIncompleteFramework(ctx context.Context, db *gorm.DB, conn *sql.Conn, tables map[string]bool) error {
|
||||
for source, archive := range scripts.LegacyArchives() {
|
||||
if !tables[source] || tables[archive] {
|
||||
return fmt.Errorf("incomplete framework sources are not recoverable: %s", source)
|
||||
}
|
||||
}
|
||||
created := scripts.FrameworkCreatedTables()
|
||||
sort.SliceStable(created, func(i, j int) bool {
|
||||
priority := func(name string) int {
|
||||
if name == "sw_kra_state" {
|
||||
return 2
|
||||
}
|
||||
if name == "sw_kra_checks" {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return priority(created[i]) < priority(created[j])
|
||||
})
|
||||
for _, name := range created {
|
||||
if !tables[name] || strings.HasPrefix(name, "bak_sw2kra_") || name == "sw_kra_state" || name == "sw_kra_checks" {
|
||||
continue
|
||||
}
|
||||
var count int64
|
||||
if err := db.Table(name).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count != 0 {
|
||||
return fmt.Errorf("partial framework target %s contains committed data; automatic reset refused", name)
|
||||
}
|
||||
}
|
||||
for _, name := range created {
|
||||
if tables[name] {
|
||||
if _, err := conn.ExecContext(ctx, "DROP TABLE "+quoteName(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func finishArchives(ctx context.Context, db *gorm.DB, conn *sql.Conn) error {
|
||||
tables, err := tableSet(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, name := range scripts.FrameworkCreatedTables() {
|
||||
if !tables[name] {
|
||||
return fmt.Errorf("validated framework table is missing: %s", name)
|
||||
}
|
||||
}
|
||||
if err := verifyCheckRows(db); err != nil {
|
||||
return err
|
||||
}
|
||||
var moves []string
|
||||
for source, archive := range scripts.LegacyArchives() {
|
||||
if tables[source] == tables[archive] {
|
||||
return fmt.Errorf("ambiguous legacy archive state: %s", source)
|
||||
}
|
||||
if tables[source] {
|
||||
moves = append(moves, quoteName(source)+" TO "+quoteName(archive))
|
||||
}
|
||||
}
|
||||
sort.Strings(moves)
|
||||
if len(moves) > 0 {
|
||||
if _, err := conn.ExecContext(ctx, "RENAME TABLE "+strings.Join(moves, ",")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = conn.ExecContext(ctx, "UPDATE sw_kra_state SET phase='completed',completed_at=NOW(3) WHERE id=1")
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user