FR-063 Nuitka 落地版打包 — Arc SUMMARY

2026-08-15 收案。本文由 FR-063-LOG.md 全 16 blocks 濃縮而成(事實以 LOG/STATE 為準;LOG Block 14 作廢之 http2 誤診相關敘述已排除)。 母案 Notion CM-1187(Done);設計文件 design.md(D1–D11+§5);部署環境變數 deployment-env.md

1. 一段話總結

產品進入落地版(on-premise)交付階段,客戶主機上不可再出現一行可讀業務原始碼。本案把 BE 整包用 Nuitka standalone 編成機器碼 binary(業務 .py=0),統一入口單 binary 雙模式(RUN_MODE=api/socketio),建立可重複的三段 build 管線(rebuild wheel → 編譯 → 打 image +七項探針),最終產出並推上 Harbor:guidant-ai/guidant-ai-be:1.14.0latest(digest sha256:bba0449d…,壓縮 0.55GB),產物 commit bake cc7714d2。與 FR-062 license 執法構成雙保險;為三階段戰線第一棒(後續 FR-064 防竄改 → FR-065 Installer)。全程 13 工作卡+2 子需求卡+母案全 Done,場外衍生 CM-1206(socket 共編存檔 bug)一併修復收 Done。

2. Commits 清單(逐 block 收齊)

BE(compliance-manager-be)

Commit 內容(對應卡)
b053c765 design.md 定稿(設計棒)
18ef3142 .1a DI 靜態模組清單(CM-1194)
78937428 .1b 資源路徑 resource_path(CM-1195)
5acedd9b .1g config 三層重整+runtime_config seed(CM-1200)
b40a5710 .1c+.1d 統一入口 main.py+.env 平鋪(CM-1196/1197)
cb830d09 config-migration-mapping.md §6 STG/POC 上版 checklist
aa760c508a83fa98 .1e version bake+fail-fast(CM-1198)
d510ed79 .2a+.2b build 機環境+私房 wheel rebuild(CM-1201/1202)
8a697d85 .2c Nuitka build script(CM-1203)
02146e9d jedi 發版收官後 BE push 齊點(pin 還原、poetry.lock 入版控、TURNSTILE 平鋪、ENV 四值新慣例)
381acf76f449f939(共 5 筆) .2d 編譯加速:公開套件排除+dev group 搬遷+三道自動把關(CM-1205)
78debb6ce8104ef6 .4 socketio namespace 集中註冊+Flask-SocketIO 5.6.1 升版(CM-1193)
6de7ffd8 CM-1206 socket auth context 修復(BE 側)
29c4013fcc7714d2(共 3 筆) .3 production image:Dockerfile.production/entrypoint/探針(CM-1192);cc7714d2 即最終產物 bake
0dcabc8d build_image 產出(合流輪)
f921ed39 build script i18n 加 --statistics+.mo 現場重編語意註明
2834cf4c i18n 死條目 non_existent_key 清除,兩 locale 回 100%

FE(compliance-manager-fe)

Commit 內容
967e67b CM-1206 socket 修復 FE 側配合

jedi-*(jedi-python-package)

Commit / 版本 內容
57e8a41 .1f LibreOffice 路徑解析共用 helper(LIBREOFFICE_CMD env override+PATH fallback,CM-1199)
file-upload 0.0.21、captcha 0.0.14 發版推 Nexus(收官日,pin 還原一併完成)

3. 各子需求摘要

.1「程式整理」(7 卡 CM-1194~1200)——讓 codebase 具備可編譯性:

  • .1a DI 靜態模組清單(CM-1194):gen_di_modules_static.py 產 138 模組靜態清單取代 runtime 目錄掃描(Nuitka 下無檔案系統可掃);--check 走 AST parse 防清單漂移,static 模式缺檔 fail-fast 不靜默退回動態。
  • .1b 資源路徑(CM-1195):四類資源讀取點(翻譯目錄/SSP docx 範本/canon/下載範本)統一走 resource_path 解析,打包後可定位。
  • .1c+.1d 統一入口+.env 平鋪(CM-1196/1197):三支舊入口退役 → 單一 main.pyRUN_MODE 切模式;scheduler 只在 api 模式起(解排程雙跑老問題);.env 由 JSON 包裝改平鋪(向後相容),逐 key 審計 49 項清 6 死項。
  • .1e version bake+fail-fast(CM-1198):build 期 bake version+commit 進產物,version 端點成為「哪個版本在跑」的唯一身分證;REGISTERED_APPS 缺模組改 RuntimeError fail-fast(上線即抓到 resource 死模組 19 次靜默 warning 的陳年漏拔)。
  • .1f LibreOffice 路徑(CM-1199,jedi 側):清除兩 adapter 的平台硬編路徑,共用 helper+env override。
  • .1g config 三層重整(CM-1200,源自 D11):六套環境 class 塌一套;Secret 拆 JSON 包裝+config_loader 集中驗證(缺項一次列全);營運參數遷 DB system_config(缺列 fail-open 回內建預設);清 SQLALCHEMY_/APISPEC_ 等死定義。

.2 Build 管線(4 卡 CM-1201/1202/1203/1205)

  • .2a+.2b build 機+私房 wheel:24core build 機環境建置(Nuitka 4.1.3/ccache);D2 炸點親驗排除——dependency-injector 官方 abi3 wheel 在 Nuitka 下 import 炸 SystemError,私房 cp311 重編 wheel PASS,全案最底層風險解除;rebuild script 帶版本感知快取(命中 0.047s)。
  • .2c Nuitka build script:整包編譯一次過(首編 56m42s/二次 18m36s),產物 832MB、.py=0、資源在位、binary 親起驗證 .1 全部機制在真產物內活著;修兩個「開發環境看不出、正式承載才死」的陰險 bug(gunicorn 動態載入模組顯式 include+升健檢項)。
  • .2d 編譯加速(CM-1205):user 拍板「公開套件不需編譯保護」——pymupdf(31 分編譯地板)等公開大套件 --nofollow 排除原樣附帶、測試依賴搬 dev group;冷編 56m42s → 穩態 12m11s(省 78%);三個靜默複製 bug 全修+三道自動把關(完整性斷言/功能探針 9 項/dry-run 預檢)。jedi-*+業務碼照舊全編譯。

.3 Production image(CM-1192):Dockerfile.production(glibc 對齊、非 root uid1000、entrypoint chown-then-setpriv、apt 清單+fc-match 斷言確保 NotoSansCJK)+volume 佈局(design §5.5)+probe_container.sh 七項探針(§5.6,末輪 PASS=11 FAIL=0);合流輪 rebuild bake cc7714d2(含 CM-1206 修復)→ build_image → push Harbor 並 API 端複驗。

.4 eventlet × Nuitka 驗證(CM-1193):本卡目的=驗證 eventlet monkey_patch 在編譯產物下是否存活。過程中揪出 .1c 模式裁剪誤刪 socketio namespace 註冊的真回歸(healthz 級探針測不到的層)→ 修法=namespace 抽離至集中清單 config/socketio_namespaces.py(比照 REGISTERED_APPS)統一註冊;Flask-SocketIO 5.3.6→5.6.1 升版根治 session 問題(留「不可退版」警語)。產物探針 4/4 PASS——eventlet × Nuitka 命題閉環,D1 保底方案(換 gevent)不需啟用

場外 CM-1206:user 實測 .4 時現形的既存獨立 bug(2026-07-07 起,先前被「連不上」蓋住)——socket 路徑無 auth context 導致共編自動存檔 AttributeError。修法:connect 驗 JWT 建 auth context、FR-048 守門不降級、錯誤不再靜默(handler emit 錯誤事件+log)、fail-closed 全路徑。BE 6de7ffd8+FE 967e67b

4. 行為差異對照(Before → After)

面向
啟動方式 main_app.py(8000)/main_socketio.py(8002)兩支入口 單一 python main.py=API(8000,日常唯一指令);RUN_MODE=socketio python main.py=通知服務(8002)。api 模式 DEBUG=true 走 Flask dev server、否則內嵌 gunicorn
Scheduler 兩入口各起一份(雙跑) 只有 api 模式起(4 jobs);socketio 模式 0 份,log 明示 disabled
Config 六套環境 class(Development/Staging/…×2 雲別) 一套 class+ENV 降級為顯示標籤(新慣例四值 DEV/STG/POC/PRD;變數本身不退,保護既有部署腳本);config_loader 啟動期集中驗證,缺項一次列全
Secret env DB_SECRET/JWT_SECRET/REDIS_SECRET JSON 包裝 平鋪 key(向後相容舊 JSON 填法);TURNSTILE 同步平鋪化
營運參數 散在 config class 常數 遷 DB system_config(migration seed 7 列、冪等);JWT 效期改 DB 後需重啟、登入政策/MFA 即時生效
socketio namespace 藏在各 module create_module() 內註冊 集中清單 config/socketio_namespaces.py(封閉集合 2 個:notification/fill-survey),app_factory 統一註冊
交付形態 原始碼部署 Nuitka standalone binary in Docker image(業務 .py=0;公開第三方套件原樣附帶)
退役物 main_app.pymain_socketio.py 刪除;六套環境 class 刪除;死 env 項清 6 支(REDIS_DB、通知四項、DRIVE_WEBHOOK_PUBLIC_BASE_URL)+SQLALCHEMY_/APISPEC_/TOKEN_TTL/WEBSITE_URL 死定義
依賴管理 poetry.lock 不入版控(歷史誤排除) poetry.lock 入版控(build 機 lock 不同步事故後拍板)

5. 關鍵決策軌跡

  1. Nuitka 取代 .pyc:Python 3.11 反編譯工具鏈已斷、純 .pyc 已有基礎保護,但 user 拍板最高保護等級 → standalone 機器碼,對齊 Go binary 交付形態。
  2. D2 私房 wheel:dependency-injector 官方 wheel 走 Limited API(abi3)強制 PEP 489 多相初始化,與 Nuitka 靜態嵌入不相容(import 期 SystemError)→ 私房 cp311 rebuild 納管線前置(版本感知快取+sdist 留檔);abi3 觀察名單(cryptography/gevent/nacl)整包編譯未觸發,續觀察。
  3. D9 統一入口(user 提議):兩支入口各編一次 → 單 binary RUN_MODE 切模式,build ×2 變 ×1,順帶解 scheduler 雙跑。
  4. D11 config 三層重整(user 提問觸發):binary 化後「改 config=重編譯交付」,設定放錯層代價放大 → class 常數/env/DB system_config 三層各歸其位。
  5. .2d 公開套件不需編譯保護(user 拍板):保護目標是業務碼與 jedi-*,pypi 公開套件編了只換到編譯時間 → 排除後冷 build 省 78%,且 Nuitka 常數表連坐圈縮小。
  6. .4 eventlet 命題閉環:編譯產物下 monkey_patch+socketio.run 正常,D1 保底(換 gevent)封存不啟用。
  7. Flask-SocketIO 5.6.1 不可退版:5.3.6 session 問題以升版根治(取代 manage_session workaround),app_factory 留警語+症狀特徵防回退。
  8. CM-1206 socket auth:socket 路徑補 JWT auth context、FR-048 守門不降級、fail-closed——即時協作路徑首次納入正規授權體系。

6. 教訓彙整

  • 「服務有回應 ≠ 你要的版本在跑」:commit bake 的 version 端點是唯一身分證(同日兩例:runner 撞 root 舊進程佔 8002;首腦在 188 跑未 push 的 --dry-run 被舊版無視、誤觸真編譯)。跑新功能前先驗目標機 code 版本。
  • 靜默失效是本案主敵:三個複製 bug 全部「回報成功但成品是壞的」;防線=把人工清點做進腳本斷言+功能級探針(不只 import 級/healthz 級——.4 namespace 回歸正是 healthz 測不到的層)。
  • 驗證順序金字塔bash -n 0.01s → dry-run 0.3s → 小探針 10s → 全量 19m,先快後慢(runner 名言:「拿最慢的迴圈去測最快能測的東西」)。
  • Nuitka 常數表連坐:模組集合一變全量重編;排除清單越大連坐圈越小(12m11s 的由來)。
  • env 管理三坑同日爆:sample 歷史欠帳 18 支/188 設定藏 .bashrc 隱形/LICENSE_ACTIVATION 隱式預設剛好能動——解法=單一真相來源+顯式化。審 env 死活必須連 jedi 套件一起 grep(曾差點誤殺 4 支 jedi 在讀的 key)。
  • http2 誤診(Block 14 更正):runner 曾記「nginx http2 擋 WebSocket」,實為 namespace 未註冊(.1c 回歸)與 http2 假設兩問題疊置的誤診;修好前者後 user 實證 h2 200 正常。教訓:runner 的「獨立問題發現」也要驗因果——修好主因後回頭重測再下結論;nginx 不需任何 http2 特殊處置。
  • 起遠端長任務前先 pgrep 查同類進程:兩份 build 互撞的 linker crash(undefined reference)像 code 問題,實為共用 nuitka-out 汙染。
  • runner 的「環境不可行」結論也要親驗:一條 ssh -T 戳破「188 無 gitlab 金鑰」誤判,誤信就多養一條 rsync 歧路(user 同日立 memory:部署機程式碼一律 git pull)。
  • 覆蓋率統計是便宜守門:i18n --statistics 一行參數,首輪即抓到潛伏死條目。
  • stash 驗既存性禁整樹還原:.1g runner checkout-index -f -a 抹掉跨棒未 commit 檔案(STATE/LOG+pyproject override)——恢復只准 stash pop;交接檔寫完盡快 commit。

7. 已知 follow-up

項目 說明
FR-064 防竄改(CM-1188) 決策者親做中,非派工項
CM-1207(FR-065.0 DB 初始化基線) 卡已開、盤點棒 prompt 已交 user(唯讀可先行);實作棒等 FR-064 完成後派
FR-065 Installer(CM-1189) 前置=FR-064+CM-1207;FE image/產品 compose 明確劃入此案(compose 形狀由 installer 決定,不提早做)
CM-1204(手冊下載死碼端點) 場外待排程,不掛任何 FR
STG 188 舊產物去留 8000/8002 兩服務跑的是舊產物 36708b35(非最終版),去留/換新 image 由決策者定
Swagger 待補 docs.init_app 從未啟用(本案盤查發現,APISPEC_* 死項已清);復原時掛 DEBUG 開關
analysis 文件 「公開套件不需編譯保護」推理(已允諾 user)+常數表連坐與驗證金字塔可併寫

8. 部署 Handover

  • Image 拉法:Harbor 192.168.50.171(=harbor.jedicogy.com.tw:8081,188 /etc/hosts 有映射),專案 guidant-aidocker pull …/guidant-ai/guidant-ai-be:1.14.0(=latest,digest sha256:bba0449d…)。起服務:docker run … guidant-ai-be:1.14.0RUN_MODE=api→8000/RUN_MODE=socketio→8002,單 image 雙模式。
  • 環境變數清單docs/features/FR-063-2608-nuitka-packaging/deployment-env.md(必填/一般/功能模組+jedi-* 16 項+volume 掛載表+交付前 checklist)。STG/POC 上版另見 config-migration-mapping.md §6 的 13 項 checklist(含 CORS 補實際站台網址)。
  • Build 管線三支腳本(build 機 188 /opt/guidant_ai,程式碼同步一律 git pull):
    1. build_release.sh — wheel rebuild(含 dependency-injector 私房 wheel)+Nuitka 編譯,穩態 ~12m;dry-run 預檢+完整性斷言+i18n --statistics
    2. build_image.sh — 產 production image(Dockerfile.production)
    3. probe_container.sh — 七項探針健檢(design §5.6)
  • 主機層依賴:image 內建 fc-match 斷言確保 Noto Sans CJK TC;非 root uid1000 執行。