From 6e0b65a2bb666a1e41a98af0b0b92bb8e3a4cba7 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 6 Aug 2026 20:48:01 +0800 Subject: [PATCH] =?UTF-8?q?mistakes=EF=BC=9AWorkers=20AI=20=E9=A1=8D?= =?UTF-8?q?=E5=BA=A6=E7=94=A8=E5=AE=8C=EF=BC=8F=E6=8E=83=E6=8F=8F=E7=89=88?= =?UTF-8?q?=20PDF=E2=80=94=E2=80=94=E5=85=A9=E5=80=8B=E3=80=8C=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=20bug=20=E4=BD=86=E8=A6=81=E8=AA=AA=E6=B8=85=E6=A5=9A?= =?UTF-8?q?=E3=80=8D=E7=9A=84=E5=B7=B2=E7=9F=A5=E7=8F=BE=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/arcrun-app/.version-source.json | 11 ++------ cmd/arcrun-app/app.go | 28 +++++++++++++++++- cmd/arcrun-app/daemon-version.py | 44 ++++++++++++++++++++--------- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/cmd/arcrun-app/.version-source.json b/cmd/arcrun-app/.version-source.json index f4fd0bb..3cc4549 100644 --- a/cmd/arcrun-app/.version-source.json +++ b/cmd/arcrun-app/.version-source.json @@ -1,11 +1,4 @@ { - "v0.18.9": "8be9260eb542f527", - "v0.18.10": "a440f044a849b7f2", - "v0.18.11": "1538689e181444f6", - "v0.18.12": "ce37982f05d8cb5b", - "v0.18.13": "fbf7342d0d0e27b4", - "v0.18.14": "bfcb4f2701fc23b4", - "v0.18.15": "447b59ea3ade430e", - "v0.18.16": "12de01a21cd6bf38", - "v0.18.17": "c13b08dec6a0fbef" + "_algo": 2, + "v0.18.17": "cb6e53a052b87482" } diff --git a/cmd/arcrun-app/app.go b/cmd/arcrun-app/app.go index 5ad0564..941337a 100644 --- a/cmd/arcrun-app/app.go +++ b/cmd/arcrun-app/app.go @@ -278,6 +278,32 @@ type UIFailItem struct { // maxFailShown=最多列幾份(多了會洗版;總數仍照實講)。 const maxFailShown = 8 +// humanizeFailure 把 collector 的技術錯誤翻成使用者看得懂、而且**知道要不要行動**的一句話。 +// +// 🔴 leo 2026-08-06 實測,兩個真實案例(從他 Windows 的 collector.log 挖出來的): +// +// ① `4006: you have used up your daily free allocation of 10,000 neurons` +// = Cloudflare Workers AI **當日免費額度用完**。不是檔案的問題、也不是壞掉, +// 明天會自動恢復。使用者若不知道,只會以為程式壞了、反覆重丟。 +// ② `轉檔失敗(…pdf):檔案裡沒有可抽取的文字` +// = 那份 PDF 是**掃描的圖片**,沒有文字層。要嘛做 OCR,要嘛換一份。 +// +// 兩件事的處置完全相反(一個等就好、一個要動手),所以**不能都叫「失敗」了事**。 +func humanizeFailure(raw string) string { + switch { + case strings.Contains(raw, "neurons"), strings.Contains(raw, "4006"): + return "今天的免費 AI 額度用完了 ⇒ 明天會自動恢復,這些檔案會自己補上,你不用做什麼。" + + "(想馬上處理可以到 Cloudflare 升級 Workers Paid 方案)" + case strings.Contains(raw, "沒有可抽取的文字"): + return "這份 PDF 看起來是掃描的圖片,沒有文字可以讀 ⇒ 需要先做文字辨識(OCR),或換一份有文字的版本。" + case strings.Contains(raw, "尚未支援的檔案格式"): + return "這種檔案格式還讀不了 ⇒ 先另存成 PDF 或 Word(.docx)再放回同一個資料夾。" + case strings.Contains(raw, "後重試"): + return raw + "(上面是自動重試的排程,真正的原因是前一次失敗)" + } + return raw +} + func buildFailures(s syncStatus) *UIFailures { if len(s.Failures) == 0 { return nil @@ -293,7 +319,7 @@ func buildFailures(s syncStatus) *UIFailures { } u.Items = append(u.Items, UIFailItem{ Name: filepath.Base(f.Path), - Reason: f.Error, + Reason: humanizeFailure(f.Error), }) } return u diff --git a/cmd/arcrun-app/daemon-version.py b/cmd/arcrun-app/daemon-version.py index 59d923f..c13e84e 100755 --- a/cmd/arcrun-app/daemon-version.py +++ b/cmd/arcrun-app/daemon-version.py @@ -49,6 +49,8 @@ LINE_FILE = ROOT / "DAEMON_LINE" UNRELEASED = "## 下一版(未發佈)" # 版本 → 當時原始碼指紋。用來擋「同一個版號、不同的執行檔」。 SOURCE_LOCK = HERE / ".version-source.json" +# 指紋演算法版本:改算法時 +1,帳本會自動作廢重記(見 check_or_record)。 +FINGERPRINT_ALGO = 2 RELEASED_RE = re.compile(r"^## v(\d+)\.(\d+)\.(\d+)(", re.M) @@ -58,24 +60,33 @@ def die(msg): def source_fingerprint(): - """會被編進執行檔的原始碼指紋(已提交的樹 + 未提交的差異)。 + """會被編進執行檔的原始碼指紋——直接對**工作區檔案內容**取值。 - 🔴 2026-08-06 補這道閘的由來: - 戳完版號後我又改了 code,再打一次包,腳本判定「沒有未發佈段=重打同一版」 - ⇒ **兩個內容不同的執行檔都叫 v0.18.9**(實測 sha256 前 20 碼 - 63ec1077a29383b31379 vs 1ca73a5aef5bf4f90bd3)。 - 這正是 leo 08-05 點名的「版本號手寫=說謊溫床」換個形式復發—— - 機械產生版本號還不夠,還要保證**同一個版號只對應一份原始碼**。 + 為什麼要這道閘(2026-08-06): + 戳完版號後又改 code、再打一次包,腳本判定「沒有未發佈段=重打同一版」 + ⇒ 兩個內容不同的執行檔都叫 v0.18.9。 + 機械產生版本號還不夠,還要保證同一個版號只對應一份原始碼。 + + 二修(同日,閘擋錯自己人): + 原本算「HEAD:collector 樹雜湊 + 未提交 diff」,但那在 commit 前後 + 會給出不同指紋、即使內容一模一樣——戳版號時檔案還沒提交(在 diff 裡), + 提交後樹變了、diff 空了。結果只改 wiki 也被判「版號已對應另一份原始碼」。 + 改成對工作區檔案內容取值,與有沒有提交無關。 """ - def sh(*args): - return subprocess.check_output(args, cwd=ROOT, text=True).strip() try: - tree = sh("git", "rev-parse", "HEAD:collector") + files = subprocess.check_output( + ["git", "ls-files", "-co", "--exclude-standard", "collector"], + cwd=ROOT, text=True).split("\n") except subprocess.CalledProcessError: return "" # 不在 git 裡就不擋(例如從 tarball 解出來 build) - diff = subprocess.run(["git", "diff", "HEAD", "--", "collector"], - cwd=ROOT, text=True, capture_output=True).stdout - return hashlib.sha256((tree + diff).encode()).hexdigest()[:16] + h = hashlib.sha256() + for rel in sorted(f for f in files if f.strip()): + fp = ROOT / rel + if not fp.is_file(): + continue # 已刪除的檔案 + h.update(rel.encode()) + h.update(fp.read_bytes()) + return h.hexdigest()[:16] def artifacts_for(version): @@ -93,6 +104,13 @@ def check_or_record(version, stamp): if not fp: return lock = json.loads(SOURCE_LOCK.read_text()) if SOURCE_LOCK.exists() else {} + # 指紋演算法一改,舊紀錄就是「用不同單位量出來的數字」,比對沒有意義。 + # 帳本自己記演算法版本,不合就整本作廢重記——比留著誤擋自己人好。 + if lock.get("_algo") != FINGERPRINT_ALGO: + if lock: + print("i 指紋演算法已更新(v%s),舊紀錄作廢重記" % FINGERPRINT_ALGO, + file=sys.stderr) + lock = {"_algo": FINGERPRINT_ALGO} known = lock.get(version) # 🔴 2026-08-06:打包**失敗**時版號已被戳、產物卻沒生出來 ⇒ 那個版號被白白燒掉, # 修完再打就被自己的閘擋下,只能去手改 JSON(爛示範,遲早有人順手改成「都放行」)。