The startup output was dominated by two upstream behaviours. Migrate printed a bare count for every already-applied migration — a lone "1" with no version, no explanation — through the standard logger, hence stderr, which the launcher's `2>&1 |` renders as a red PowerShell error block that looks like a failed migration. And the version-existence check echoed one SELECT count(*) per version. Both replaced: a Warn-level session for the check, and one summary line on stdout. Console encoding needed more than [Console]::OutputEncoding: PowerShell 5.1 decodes child-process output by the console code page, so chcp 65001 goes with it. The regression test capture was wrong twice and mutation testing caught both. It captured only stdout while the bug wrote to stderr, and it compared lines to "1" when the logger prefixes a timestamp, so the assertion could never fail. It now captures the standard logger too and matches with a pattern that allows the prefix — verified by reinstating the bug and watching the test fail. Not verified: the PowerShell edits, which need a Windows run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
2.0 KiB
Go
80 lines
2.0 KiB
Go
package migration
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
"sort"
|
|
"sync"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
var Migrate = &Migration{
|
|
version: make(map[string]func(db *gorm.DB, version string) error),
|
|
}
|
|
|
|
type Migration struct {
|
|
db *gorm.DB
|
|
version map[string]func(db *gorm.DB, version string) error
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
func (e *Migration) GetDb() *gorm.DB {
|
|
return e.db
|
|
}
|
|
|
|
func (e *Migration) SetDb(db *gorm.DB) {
|
|
e.db = db
|
|
}
|
|
|
|
func (e *Migration) SetVersion(k string, f func(db *gorm.DB, version string) error) {
|
|
e.mutex.Lock()
|
|
defer e.mutex.Unlock()
|
|
e.version[k] = f
|
|
}
|
|
|
|
func (e *Migration) Migrate() {
|
|
versions := make([]string, 0)
|
|
for k := range e.version {
|
|
versions = append(versions, k)
|
|
}
|
|
if !sort.StringsAreSorted(versions) {
|
|
sort.Strings(versions)
|
|
}
|
|
var err error
|
|
var count int64
|
|
// GoAuto 改动(#48):版本存在性检查用 Warn 级会话,不回显 SQL。
|
|
// 上游每个版本都会打一条 SELECT count(*),几十个迁移就是几十行——真正
|
|
// 值得看的是下面 e.db.Debug() 执行的那些「确实要跑」的迁移。
|
|
checkDB := e.db.Session(&gorm.Session{Logger: e.db.Logger.LogMode(logger.Warn)})
|
|
applied, skipped := 0, 0
|
|
for _, v := range versions {
|
|
err = checkDB.Table("sys_migration").Where("version = ?", v).Count(&count).Error
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
if count > 0 {
|
|
// GoAuto 改动(#48):上游这里是 log.Println(count),每个已应用的
|
|
// 迁移打印一个裸 "1"——没有说明是什么、也没说是哪个版本。而且 log
|
|
// 写的是 stderr,启动脚本的 `2>&1 |` 会把它变成一整块红色错误,
|
|
// 看起来像迁移失败了。汇总成一行,走 stdout。
|
|
skipped++
|
|
count = 0
|
|
continue
|
|
}
|
|
err = (e.version[v])(e.db.Debug(), v)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
applied++
|
|
}
|
|
fmt.Printf("迁移完成:新执行 %d 个,跳过 %d 个(已应用)\n", applied, skipped)
|
|
}
|
|
|
|
func GetFilename(s string) string {
|
|
s = filepath.Base(s)
|
|
return s[:13]
|
|
}
|