前言
前面的系列文章完成了 Task Document、Block Runtime、互動式 HTML、跨職能流程圖、AI Agent API 與公開資源權限。功能和測試都完成後,仍然有最後一個問題:怎麼把新版外掛放進正式環境,而且在出錯時真的能回到上一版?
「測試通過」只代表目前工作目錄在目前環境可以運作,不代表即將上傳的壓縮檔就是那份程式,也不代表資料庫備份可以還原,更不代表上一版外掛能讀取目前資料。
這篇記錄 KnockersTaskBlocks 如何把 build、測試、Artifact、Migration、部署與回滾組成同一份 release contract。
直接把工作目錄 SCP 上去有什麼問題?
開發目錄通常同時包含:
- Source code 與未編譯前端。
node_modules、測試 fixture 與暫存檔。- 尚未提交的修改。
- 本機專用設定。
- 不應出現在正式站的原型與紀錄。
如果每次部署都臨時決定要複製哪些檔案,正式環境就很難回答:目前究竟是哪個 commit?前端 bundle 對應哪一份 source?回滾應該還原哪個目錄?
因此部署單位不是工作目錄,而是一份建置後不再修改的 runtime archive。
Release Artifact 應該自我描述
每次發布產生:
- 外掛 runtime archive。
release-manifest.json。- Artifact 與備份的 SHA-256 清單。
- 版本與相容 Kanboard 資訊。
- 已通過的 test gates。
- Bundle size measurement。
泛化後的 manifest 大致如下:
{
"release": "TaskBlocks-1.4.0",
"sourceCommit": "<git-commit>",
"pluginVersion": "1.4.0",
"schemaVersion": 3,
"gates": {
"phpTests": "pass",
"javascriptTests": "pass",
"browserTests": "pass",
"bundleBudget": "pass"
}
}
Manifest 不是用來取代 Git,而是把「這個檔案如何產生」和「它通過哪些條件」放進同一個可驗證交付物。
上傳前後都重新計算 checksum,能發現壓縮、傳輸或解壓流程是否改變內容。Archive 也要檢查沒有 AppleDouble、測試憑證、Source map、開發設定和不需要的 dependency。
發布 Gate 不只是 Unit Test
這個外掛同時有 PHP、React、瀏覽器與多資料庫行為,因此 release gate 分成多層:
PHP
- Schema migration。
- Task Document validation 與 projection。
- Revision conflict。
- Public scope 與 resource ownership。
- 所有 runtime PHP 檔案 syntax lint。
JavaScript
- Editor 與 Viewer Registry。
- Interactive HTML lifecycle。
- Swimlane Domain transformation。
- Share management UI。
真實瀏覽器
- 編輯、摘要、唯讀與公開模式。
- HTML sandbox、靜態預覽與全螢幕。
- 流程圖拖曳、連線與唯讀限制。
- 附件、Internal Link 與撤銷後失效。
Supply chain 與 Artifact
- Dependency vulnerability gate。
- Archive integrity。
- Manifest、version 與 source commit 一致。
- 必要 assets 都在 runtime archive 中。
只有全部通過的 candidate 才能進入 web root。
Bundle Budget 需要成為阻擋條件
加入 React Flow 後,最容易發生的退化是把重型 chunk 不小心合併進所有任務頁的初始 JavaScript。
這個專案為 release 設定 project-specific budget:
- Task Document 對初始 JavaScript 的增加量超過門檻就失敗。
- Swimlane Diagram chunk 超過獨立門檻就失敗。
- 沒有流程圖的頁面不能請求 React Flow assets。
門檻不是通用 Web 標準,而是根據目前 baseline 和可接受的使用體驗制定。重點是把數字寫進 build script,讓 regression 自動阻擋 release,而不是部署後才憑感覺判斷頁面變慢。
Runtime performance 也使用大型 fixture 測量 mount、drag、connect、cycle traversal 與 long task。圖演算法如果漏掉 visited set,即使小型測試都通過,也可能在循環或大型文件中凍結瀏覽器。
Migration 必須在支援的資料庫實際執行
外掛支援 SQLite、MySQL 與 PostgreSQL,不能只在 SQLite unit test 通過就推論其他資料庫安全。
Migration smoke 會在 disposable database 中驗證:
- 空白安裝可以建立最新 schema。
- 舊 schema 可以升級到目前版本。
- Unique constraint、foreign key 與 cascade 符合預期。
- 既有 Task Document 與 Revision 數量沒有被改寫。
- 新版程式可以讀取升級後資料。
對會刪除或重建資料的測試,預設必須 fail closed:只有資料庫名稱符合測試 prefix、環境變數明確確認,而且 target 位於 allow-list 時才允許執行。
正式回滾則盡量不做 destructive down migration。若 release 只新增資料表或索引,緊急回復舊程式時可以保留新表不用,避免事故中同時操作程式與資料毀損風險。
有備份,不代表可以還原
部署前會備份:
- Database dump。
- 現有外掛完整目錄。
- Configuration 與必要自訂資產。
- 新舊 Artifact checksum。
但備份真正有價值的條件,是可以在隔離環境還原並啟動。
回滾演練不只做 gzip -t 或列出 tar 內容,而是:
- 將 database dump 還原到 disposable database。
- 使用部署前外掛建立隔離的應用程式副本。
- 啟動 PHP 與資料庫。
- 用瀏覽器開啟登入任務與匿名公開頁。
- 驗證 Task Document、附件、連結與相容內容。
- 關閉服務並移除暫存環境。
這才能證明舊外掛真的可以讀取目前資料,而不是只證明壓縮檔沒有損壞。
正式替換要保留立即前一版
部署時先把 candidate 解壓到 web root 外,完成:
- PHP lint。
- 必要檔案與 assets 檢查。
- Plugin version 與 manifest 核對。
- Owner、group 與 mode 驗證。
通過後再做目錄層級的替換,並保留立即前一版。若新版本 origin smoke 失敗,可以把 candidate 移出並恢復上一個完整目錄,不在事故中逐檔猜測哪些檔案需要還原。
Database restore 是最後手段。若部署沒有執行破壞性 migration,也沒有寫壞內容,優先回復外掛檔案可以降低停機時間與資料回退範圍。
Production Smoke 應該使用可還原資料
正式環境仍需要驗證真正的 Web Server、PHP、Database、CSP 與瀏覽器行為,但不能留下測試資料。
Smoke fixture 的做法是:
- 保存測試任務原本的完整文件與 Revision。
- 加入可以辨識的 HTML、流程圖、附件與連結。
- 驗證登入、公開、全螢幕與撤銷流程。
- 確認所有新 assets 由 origin 正常回應。
- 還原原始文件與公開狀態。
- 再次比對原始內容與測試資料是否完全移除。
若驗收腳本需要 API Key、Session 或分享 Token,只能在執行時從環境取得,不能把 credential 或完整 response 寫入 repository 和永久 measurement。
部署後還要看什麼?
成功頁面載入一次不代表部署完成。最後還要檢查:
- PHP、Web Server 與應用程式錯誤紀錄。
- Browser console 是否出現外掛資產或 runtime error。
- Cache-busting asset 是否為新版本。
- Public response 是否維持 no-store。
- Task Document、Revision 與公開狀態是否回到驗收前基準。
監控要先建立部署前 baseline,再比較部署後新增內容,才能區分既有第三方外掛噪音與這次 release 造成的問題。
整個系列最後得到的結論
從 Markdown 任務描述開始,KnockersTaskBlocks 最後加入了結構化文件、Block Runtime、互動式 HTML、流程圖、AI Agent API 與單一任務分享。每一項功能都帶來新的資料與安全邊界,但它們有相同的開發原則:
- 不修改 Kanboard 核心。
- 新功能沿用原生身分、權限與資源 ownership。
- Task Document 保留版本、相容內容與降級閱讀。
- 重型功能按需載入,並有明確 guardrail。
- Public access 以 Scope 約束每一個子資源。
- Release 以可驗證 Artifact 和實際回滾演練收尾。
對 legacy system 進行延伸開發,真正困難的不是把新介面做出來,而是確保升級、停用、公開、並行修改與部署失敗時,既有系統仍然保持可理解、可操作和可回復。
系列起點可以從這篇開始閱讀:Kanboard 任務內容區塊外掛開發實錄:從 Markdown 描述到結構化文件。
