fix(cli): acr update 假綠與沉默修復——部署部分失敗必須印出、deploy 迴圈逐 worker 進度、migrate 錯誤印 server 回應

- update.ts: result.message 含失敗清單時不再被「✓ 部署完成」蓋掉(404 重跑 3 次找不到根因的元兇)
- update.ts: migrate-cron-index 非 2xx 印 response body 前 200 字 + 404/500 含義提示
- deploy.ts: downloadAndDeploy 逐 worker 印 [i/N] 進度(原本 20+ worker 靜默部署像當機)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-06-11 08:04:22 +08:00
parent 1af7655ac6
commit 35cdda7061
2 changed files with 31 additions and 6 deletions
+20 -4
View File
@@ -84,7 +84,15 @@ export async function cmdUpdate(): Promise<void> {
const result = await downloadAndDeploy(ctx);
if (result.implemented) {
// message 含部分失敗清單(「部署 X/Y 成功,N 失敗:✗ ...」)——必須印出來,
// 否則 worker 失敗被綠勾蓋掉(假綠):cypher 沒部上 → 後面 migrate 打舊 worker 404
// 用戶重跑 N 次都不知道根因(壓測 2026-06-11 實證)。
if (result.message?.includes('失敗')) {
console.log(chalk.yellow(`\n ⚠ 部署部分失敗:`));
console.log(chalk.yellow(' ' + result.message.split('\n').join('\n ')));
} else {
console.log(chalk.green('\n ✓ 部署完成'));
}
// 重跑 seed(薄殼:呼叫 API /init/seed;冪等,覆寫既有)。
// 修壓測 §4.1.3「update 不做 seed,但 init 提示說 update 會重試 seed」的矛盾。
const cypherUrl = config.cypher_executor_url
@@ -108,10 +116,18 @@ export async function cmdUpdate(): Promise<void> {
process.stdout.write(chalk.gray(' → 遷移 cron index(舊 per-key → 集中 key,冪等)...'));
try {
const res = await fetch(`${cypherUrl}/webhooks/named/migrate-cron-index`, { method: 'POST' });
const body = await res.json().catch(() => null) as { success?: boolean; migrated?: number; skipped?: number } | null;
console.log(res.ok && body?.success
? chalk.green(` ✓ migrated ${body.migrated ?? 0}, skipped ${body.skipped ?? 0}`)
: chalk.yellow(` ⚠ HTTP ${res.status}(可重跑 acr update`));
const rawText = await res.text();
let body: { success?: boolean; migrated?: number; skipped?: number; errors?: string[] } | null = null;
try { body = JSON.parse(rawText); } catch { /* 非 JSON(如 CF 錯誤頁)→ 用原文 */ }
if (res.ok && body?.success) {
console.log(chalk.green(` ✓ migrated ${body.migrated ?? 0}, skipped ${body.skipped ?? 0}`));
} else {
// 印 server 回的錯誤內容(截前 200 字)——只回 HTTP status 沒人能診斷
// (壓測 2026-06-11404→500 重跑 3 次都看不到根因)。
const detail = (body?.errors?.join('; ') ?? rawText).slice(0, 200);
console.log(chalk.yellow(` ⚠ HTTP ${res.status}${detail ? `${detail}` : ''}`));
console.log(chalk.yellow(' 404 = cypher worker 還是舊版(看上方部署是否有失敗);500 = server 端錯誤(看上行錯誤內容)'));
}
} catch (e) {
console.log(chalk.yellow(` ⚠ cron index 遷移失敗(${e instanceof Error ? e.message : e}`));
}
+10 -1
View File
@@ -101,16 +101,25 @@ export async function downloadAndDeploy(ctx: DeployContext, ref = 'main'): Promi
}
// 3. 對每個 worker:注入 KV id+ cypher WORKER_SUBDOMAIN)→ wrangler deploy。tier1 先 tier2 後。
// 逐 worker 串流進度(每個含 pnpm install + wrangler deploy,沉默會讓人以為卡住——
// 壓測 2026-06-11 richblack 觀察:「D1 ✓」後停很久其實在這個迴圈靜默部署 20+ worker)。
const allDirs = [...tier1, ...tier2];
const failures: string[] = [];
let deployed = 0;
for (const dir of [...tier1, ...tier2]) {
console.log(chalk.gray(` → 部署 ${allDirs.length} 個 worker(每個含 install + deploy,依序進行)...`));
for (let i = 0; i < allDirs.length; i++) {
const dir = allDirs[i];
const tomlPath = join(dir, 'wrangler.toml');
const label = dir.replace(/^.*\.component-builds\//, '').replace(/^.*\//, '');
process.stdout.write(chalk.gray(` [${i + 1}/${allDirs.length}] ${label} ...`));
try {
injectWranglerConfig(tomlPath, ctx);
runWranglerDeploy(dir, ctx);
deployed++;
console.log(chalk.green(' ✓'));
} catch (e) {
failures.push(`${dir}: ${e instanceof Error ? e.message : String(e)}`);
console.log(chalk.yellow(' ⚠'));
}
}