U10a 首切片:goose 迁移内嵌回退(单文件部署去 cwd 依赖第一步) #9
@@ -0,0 +1,14 @@
|
|||||||
|
// Package dbassets 内嵌数据库迁移链(U10a:单文件应用无源码目录时回退使用)。
|
||||||
|
// 磁盘目录存在时仍优先磁盘(开发态行为不变);磁盘与嵌入的一致性由
|
||||||
|
// 迁移门禁测试(ExpectedMigrationCount)约束。
|
||||||
|
package dbassets
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
// MigrationsFS 内嵌 deploy/postgres/goose 迁移链。
|
||||||
|
//
|
||||||
|
//go:embed all:goose
|
||||||
|
var MigrationsFS embed.FS
|
||||||
|
|
||||||
|
// MigrationsDir 是嵌入 FS 内的迁移子目录名(goose SetBaseFS 的根)。
|
||||||
|
const MigrationsDir = "goose"
|
||||||
+16
-3
@@ -9,6 +9,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"chat-agent-system/deploy/postgres"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
"github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/pressly/goose/v3"
|
"github.com/pressly/goose/v3"
|
||||||
@@ -25,8 +26,9 @@ func RunMigrations(ctx context.Context, pool *pgxpool.Pool, migrationsDir string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
useDisk := true
|
||||||
if _, err := os.Stat(absDir); err != nil {
|
if _, err := os.Stat(absDir); err != nil {
|
||||||
return fmt.Errorf("db migrate: directory %s: %w", absDir, err)
|
useDisk = false
|
||||||
}
|
}
|
||||||
|
|
||||||
db := stdlib.OpenDB(*pool.Config().ConnConfig)
|
db := stdlib.OpenDB(*pool.Config().ConnConfig)
|
||||||
@@ -37,8 +39,19 @@ func RunMigrations(ctx context.Context, pool *pgxpool.Pool, migrationsDir string
|
|||||||
}
|
}
|
||||||
goose.SetTableName(schemaTable)
|
goose.SetTableName(schemaTable)
|
||||||
|
|
||||||
if err := goose.UpContext(ctx, db, absDir); err != nil {
|
if useDisk {
|
||||||
return fmt.Errorf("db migrate up: %w", err)
|
if err := goose.UpContext(ctx, db, absDir); err != nil {
|
||||||
|
return fmt.Errorf("db migrate up: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// U10a:源码目录不存在(单文件部署)时回退内嵌迁移链。
|
||||||
|
// goose SetBaseFS 是全局状态;用后复位,避免污染后续磁盘调用。
|
||||||
|
goose.SetBaseFS(dbassets.MigrationsFS)
|
||||||
|
defer goose.SetBaseFS(nil)
|
||||||
|
if err := goose.UpContext(ctx, db, dbassets.MigrationsDir); err != nil {
|
||||||
|
return fmt.Errorf("db migrate up (embedded): %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,3 +278,21 @@ func rewriteDatabaseName(dsn, name string) string {
|
|||||||
}
|
}
|
||||||
return dsn
|
return dsn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunMigrationsEmbeddedFallback(t *testing.T) {
|
||||||
|
dsn := os.Getenv("TEST_DATABASE_URL")
|
||||||
|
if dsn == "" {
|
||||||
|
t.Skip("TEST_DATABASE_URL not set; embedded fallback needs PG")
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
pool, err := pgxpool.New(ctx, dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("connect: %v", err)
|
||||||
|
}
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
// 不存在的磁盘目录必须触发内嵌回退并成功走完迁移链(幂等)。
|
||||||
|
if err := RunMigrations(ctx, pool, filepath.Join(t.TempDir(), "no-such-dir")); err != nil {
|
||||||
|
t.Fatalf("embedded fallback migrations: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user