diff --git a/entrypoint.sh b/entrypoint.sh index 7635c0f..ab89a75 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -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 请自行确保有备份) diff --git a/tools/upgrade-test.sh b/tools/upgrade-test.sh index 8a6ae3b..b17880c 100755 --- a/tools/upgrade-test.sh +++ b/tools/upgrade-test.sh @@ -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";