fix(entrypoint): pending 判定不走 Laravel(坏/外来的配置缓存会打死 artisan)
CI 第一次跑这个特性时红了,根因是一个真实存在的坑:
站点自己的 bootstrap/cache/config.php 一旦是坏的或外来的(CI 真升级用例里那个
**故意写坏**的哨兵文件就是),任何 artisan 命令都会报
`Target class [files] does not exist` —— 于是 migrate:status 读不到任何东西,
pending 被误判成 0,自动迁移静默不发生。
改法:SQLite 的 pending 判定改成**纯只读 PDO** 比对「盘上迁移文件」vs「记账表里的
行」,完全不经过 Laravel(连配置缓存都不读),因此对坏缓存免疫;其它数据库(MySQL /
Postgres)仍走 migrate:status(那类站点不会有本地 config 缓存文件这回事)。
顺带在 CI 用例的 4b 段里把那个哨兵配置缓存挪开:真站点的配置缓存要么不存在、
要么是有效的;它在前面那段是用来验证「同步不许覆盖站点自己的配置缓存」的,
两种用途不能混在一个卷上。
本机实测(拿生产库只读副本造的真 v2.1 卷,409 张图):
· 干净卷 → 自动补 2 个迁移文件 + 检测到 2 条 pending + 备份 + 两张表 DONE,
数据行指纹与升级前**逐字节一致**,容器退出码 0
· 坏配置缓存仍在时 → 迁移如实报错、只打警告,容器照常启动(退出码 0)
· 表在但记账行被删的半迁移状态 → 同样只警告不崩,下次启动自动重试
This commit is contained in:
1 parent
4d75333320
commit
3b74c8aae8
2 files changed
+43
-8
No files matched your search
+38
-8
@@ -434,14 +434,44 @@ auto_migrate() {
|
||||
fi
|
||||
|
||||
# ③ 真的有 pending 才备份 + 迁移(否则每次重启都白备份一份库,纯属糟蹋磁盘)
|
||||
status=$(runuser -u www-data -- sh -c 'cd /var/www/html && php artisan migrate:status' 2>&1) || true
|
||||
case "$status" in
|
||||
*Pending*|*pending*) pending=1 ;;
|
||||
*) pending=0 ;;
|
||||
esac
|
||||
if [ "$pending" -eq 0 ]; then
|
||||
echo "[lsky] 自动迁移检查完成:没有待执行的迁移"
|
||||
return 0
|
||||
# · SQLite:直接**只读**比对「盘上的迁移文件」vs「记账表里的行」——
|
||||
# 刻意不走 Laravel:站点自己的 bootstrap/cache/config.php 一旦是坏的/外来的,
|
||||
# 任何 artisan 命令都会报 "Target class [files] does not exist"(CI 的真升级用例
|
||||
# 里那个哨兵配置缓存就是这么把 migrate:status 打死的,实测踩到)。
|
||||
# · 其它库:走 migrate:status(纯只读查询)。
|
||||
if [ "$conn" = "sqlite" ]; then
|
||||
pending=$(runuser -u www-data -- php -r '
|
||||
$dir = "/var/www/html/database/migrations";
|
||||
$p = $argv[1];
|
||||
$ran = [];
|
||||
try {
|
||||
$db = new PDO("sqlite:file:" . $p . "?mode=ro", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
||||
foreach ($db->query("SELECT migration FROM migrations") as $r) { $ran[$r[0]] = true; }
|
||||
} catch (Throwable $e) { echo "-1"; exit; }
|
||||
$n = 0;
|
||||
foreach (glob($dir . "/*.php") as $f) {
|
||||
if (!isset($ran[basename($f, ".php")])) { $n++; }
|
||||
}
|
||||
echo (string) $n;
|
||||
' "$dbpath" 2>/dev/null) || pending=-1
|
||||
case "$pending" in ''|*[!0-9]*) pending=-1 ;; esac
|
||||
if [ "$pending" -lt 0 ]; then
|
||||
echo "[lsky] 警告:读不出 SQLite 的迁移记账表,跳过自动迁移(站点不受影响)"
|
||||
return 0
|
||||
fi
|
||||
[ "$pending" -eq 0 ] && { echo "[lsky] 自动迁移检查完成:没有待执行的迁移"; return 0; }
|
||||
echo "[lsky] 检测到 $pending 条待执行迁移"
|
||||
else
|
||||
status=$(runuser -u www-data -- sh -c 'cd /var/www/html && php artisan migrate:status' 2>&1) || true
|
||||
case "$status" in
|
||||
*Pending*|*pending*) pending=1 ;;
|
||||
*) pending=0 ;;
|
||||
esac
|
||||
if [ "$pending" -eq 0 ]; then
|
||||
echo "[lsky] 自动迁移检查完成:没有待执行的迁移"
|
||||
return 0
|
||||
fi
|
||||
echo "[lsky] 检测到待执行的迁移"
|
||||
fi
|
||||
|
||||
# 迁移前备份(仅 SQLite 三件套;保留最近 3 份。MySQL/Postgres 请自行确保有备份)
|
||||
|
||||
@@ -557,6 +557,9 @@ echo " ✓ 数据面快照(含整份 md5 清单)逐字节相同"
|
||||
# 所以那段证明不了「真老站点会自动补表」。这一段专门构造真站点形状:
|
||||
# ① 建 migrations 记账表并写入上游那 10 条(= 2022-08 的 v2.1 站点就是这么记账的)
|
||||
# ② 把卷里那两个 fork 迁移文件删掉(模拟「镜像里的新迁移还没进卷」这个原始坑)
|
||||
# ③ 把上面用过的哨兵 bootstrap/cache/config.php 挪开:它是**故意写坏**的,任何 artisan
|
||||
# 命令都会被它打成 "Target class [files] does not exist";而真站点的配置缓存要么
|
||||
# 不存在、要么是有效的。不挪开就测不出「真站点能自动迁移」这件事。
|
||||
# 然后重启新镜像,断言:迁移文件被自动补进卷、两张新表被自动建好、**哨兵数据一字未变**。
|
||||
echo "== 4b) 自动迁移用例:v2.1 形状老站点 → 新镜像(无需人工干预)=="
|
||||
docker run --rm -i -v "$VOL":/var/www/html --entrypoint sh "$NEW_REF" > "$LOGDIR/phase4b-seed.out" 2>&1 <<'EOSH'
|
||||
@@ -564,6 +567,8 @@ set -eu
|
||||
cd /var/www/html
|
||||
# ② 删掉卷里那两个 fork 迁移文件(模拟「新迁移还没进卷」;镜像里那份干净树仍在)
|
||||
rm -f database/migrations/2026_10_01_*.php
|
||||
# ③ 挪开故意写坏的哨兵配置缓存(见上面第 ③ 条说明)
|
||||
rm -f bootstrap/cache/config.php
|
||||
# ① 建记账表并写入上游 10 条:v2.1 站点就是这样被 Laravel 记账的
|
||||
php -r '
|
||||
$p = "/var/www/html/database/database.sqlite";
|
||||
|
||||
Reference in new issue
Block a user