feat/script-contract-preflight/main
master
讓第一支腳本從頭到尾跑通,並把所有腳本共用的契約一次立好:具名 flag 輸入、單行 JSON 輸出、四層前置檢查、--dry-run、冪等查重,以及 Gitea 與 git 各自唯一的出口。
--dry-run
#1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組
#3 — 走通腳本契約:共用函式庫、四層前置檢查與 labels-list
scripts/lib.js
{ok, data, error:{code, message}}
giteaRequest
runGit
findIssueByTitle
import.meta.url
scripts/labels-list.js
test/helpers/
test/
permissions.push
tea login
config.yml
TEA_SDLC_API_BASE
TEA_SDLC_TOKEN
process.stdout.write
process.exit
DEDUPE_LIMIT
null
後續十幾支腳本不必各自處理參數、輸出格式、認證、錯誤碼與前置檢查;使用者踩到環境問題時,拿到的是一個明確的錯誤碼與「該去哪裡改」,而不是後續步驟東一個西一個地失敗。
新增,無既有功能受影響。唯一對外可見的行為是 labels-list 這支新指令。
labels-list
npm test:
npm test
ℹ tests 50 ℹ suites 0 ℹ pass 50 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 1029.524667
對真實 Gitea 的手動驗證(plugins/tea-sdlc,該 repo 的時間追蹤目前是關的):
plugins/tea-sdlc
$ node scripts/labels-list.js --repo plugins/tea-sdlc --dry-run {"ok":true,"data":{"dryRun":true,"repo":"plugins/tea-sdlc","requests":[{"method":"GET","path":"/repos/plugins/tea-sdlc/labels"}],"paths":{"templates":"/root/plugins/tea-sdlc/templates","references":"/root/plugins/tea-sdlc/references"}}} $ node scripts/labels-list.js --repo plugins/tea-sdlc {"ok":false,"error":{"code":"TIME_TRACKER_OFF","message":"repo 尚未開啟時間追蹤,工時碼錶無法運作;請到 Settings → Advanced Settings → Enable Time Tracker 開啟"}} $ echo $? 1
第一到第三層都對真實 Gitea 通過(環境、tea 登入、issues unit 寫入權探針回 404),第四層如實擋下。從 /tmp 執行結果完全相同,驗證路徑定位不依賴 cwd。
/tmp
tea
PLUGIN_LAYOUT_BROKEN
🤖 Generated with Claude Code
scripts/lib.js 立起所有腳本共用的地基:具名 flag 解析、單行 JSON 輸出 {ok, data, error:{code, message}}、Gitea API 與 git 各自唯一的出口、 四層前置檢查、依標題的冪等查重。 前置檢查依序為執行環境(git/tea 在 PATH 上、plugin 目錄完整)、Gitea 登入 有效、帳號對 issues unit 的寫入權、repo 已開啟時間追蹤;任一層不通過即帶著 可區分的錯誤碼中止,訊息一律指出該去哪裡改設定,前一層沒過就不再往下打。 issues unit 的寫入權採實測而非讀 permissions.push——Gitea 的 team unit 權限 可獨立於 repo 的 push 權限。探針打在不存在的議題 index 0:有寫入權會通過權限 中介層後回 404,沒有則直接 403,兩種結果都不改動任何東西。 認證預設沿用使用者既有的 tea login(讀 tea 的 config.yml),環境變數 TEA_SDLC_API_BASE / TEA_SDLC_TOKEN 只作為測試與 CI 的覆寫出口。 labels-list 為第一支腳本,刻意只讀不寫,呼應「不自動建立 Gitea 標籤」。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
測試一律以子行程執行腳本、比對 stdout 的單行 JSON 與 exit code,因為那正是 七個平台共用的實際呼叫方式,不會因內部重構而破碎。 Gitea 以本機 stub server 替身並錄下每一筆請求,藉此斷言「腳本到底發了哪些 請求」——包含 --dry-run 不得發出任何請求、前一層檢查沒過就不再往下打、 寫入權探針不得挾帶任何要寫入的欄位。git 則在 .tmp/ 下的臨時 repo 跑真實 指令,比 mock 可信且成本低。 lib-exports.test.js 是唯一直接 import lib 的例外:冪等查重與 git 執行點在 CLI 邊界上還沒有消費者,檔頭已註明等 #4 與 #10 落地後即可縮小或移除。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
process.stdout.write 之後立刻 process.exit 會截斷輸出——stdout 接到 pipe 時 寫入是非同步的。改為等 write 的 callback 回來再退出。實測舊寫法在約 83KB 處 被切斷,新增的回歸測試以 8000 筆標籤覆蓋這條路徑。 findIssueByTitle 的翻頁原本沒有上限,Gitea 若持續回滿一頁就會無限打下去。 加上 200 頁上限,超過即以 DEDUPE_LIMIT 報錯而非無聲回 null——無聲回 null 會 讓呼叫端把既有議題再建一次,正好是冪等查重要防的事。 parseFlags 取值時不再於三元運算式內遞增迴圈變數,改為獨立敘述。 測試工具的 maxBuffer 調高到 64MB:預設 1MB 會在長輸出時砍掉子行程,那是測試 工具的限制而非腳本的問題。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
No dependencies set.
The note is not visible to the blocked user.
摘要
讓第一支腳本從頭到尾跑通,並把所有腳本共用的契約一次立好:具名 flag 輸入、單行 JSON 輸出、四層前置檢查、
--dry-run、冪等查重,以及 Gitea 與 git 各自唯一的出口。需求議題
#1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組
工作包議題
#3 — 走通腳本契約:共用函式庫、四層前置檢查與 labels-list
變更內容
scripts/lib.js:flag 解析、{ok, data, error:{code, message}}單行輸出、giteaRequest(全專案唯一 HTTP 出口)、runGit(唯一子行程出口)、四層前置檢查、findIssueByTitle查重、以import.meta.url回推的路徑定位。scripts/labels-list.js:第一支腳本,只讀不寫。test/helpers/:stub Gitea server(錄下每一筆請求)、子行程執行器、臨時 git repo。test/:五支測試,共 50 個案例。設計重點
permissions.push為真不代表 issues 寫得進去。探針打在不存在的議題 index 0:有寫入權會通過權限中介層後回 404,沒有則直接 403,兩種結果都不改動任何東西。真實 repo 上已驗證回 404。tea login,讀 tea 的config.yml;TEA_SDLC_API_BASE/TEA_SDLC_TOKEN只是測試與 CI 的覆寫出口。YAML 只做針對 tea 實際輸出格式的最小剖析,不引入套件。process.stdout.write後不能立刻process.exit。stdout 接到 pipe 時寫入是非同步的,舊寫法在約 83KB 處截斷;已改為等 callback 回來再退出,並以 8000 筆標籤的回歸測試覆蓋。DEDUPE_LIMIT報錯而非無聲回null——無聲回 null 會讓呼叫端把既有議題再建一次,正好是查重要防的事。解決的問題
後續十幾支腳本不必各自處理參數、輸出格式、認證、錯誤碼與前置檢查;使用者踩到環境問題時,拿到的是一個明確的錯誤碼與「該去哪裡改」,而不是後續步驟東一個西一個地失敗。
影響的功能
新增,無既有功能受影響。唯一對外可見的行為是
labels-list這支新指令。測試結果
npm test:對真實 Gitea 的手動驗證(
plugins/tea-sdlc,該 repo 的時間追蹤目前是關的):第一到第三層都對真實 Gitea 通過(環境、tea 登入、issues unit 寫入權探針回 404),第四層如實擋下。從
/tmp執行結果完全相同,驗證路徑定位不依賴 cwd。待確認
labels-list是否該跑滿四層前置檢查。 目前照「任一層不通過即中止」實作,因此在時間追蹤關閉的 repo 上,連列標籤都會被擋。若希望讀取型腳本只跑前三層,我改成由各腳本宣告所需層級。tea原生子指令。 議題 #1 的 Further Notes 寫「議題、標籤、Milestone、留言、工時列表走 tea 原生子指令」,但 Testing Decisions 寫「lib.js 的 Gitea 呼叫集中於單一函式,測試時以環境變數指向本機 stub server」——後者只有自己擁有 HTTP 呼叫才做得到。此 PR 採後者。這個取捨會決定 #4–#16 的寫法,請確認。PLUGIN_LAYOUT_BROKEN),不在 #3 對第一層的字面定義內。加它是為了讓路徑定位在正式流程中有真正的消費者,而不是只出現在--dry-run輸出裡。🤖 Generated with Claude Code
scripts/lib.js 立起所有腳本共用的地基:具名 flag 解析、單行 JSON 輸出 {ok, data, error:{code, message}}、Gitea API 與 git 各自唯一的出口、 四層前置檢查、依標題的冪等查重。 前置檢查依序為執行環境(git/tea 在 PATH 上、plugin 目錄完整)、Gitea 登入 有效、帳號對 issues unit 的寫入權、repo 已開啟時間追蹤;任一層不通過即帶著 可區分的錯誤碼中止,訊息一律指出該去哪裡改設定,前一層沒過就不再往下打。 issues unit 的寫入權採實測而非讀 permissions.push——Gitea 的 team unit 權限 可獨立於 repo 的 push 權限。探針打在不存在的議題 index 0:有寫入權會通過權限 中介層後回 404,沒有則直接 403,兩種結果都不改動任何東西。 認證預設沿用使用者既有的 tea login(讀 tea 的 config.yml),環境變數 TEA_SDLC_API_BASE / TEA_SDLC_TOKEN 只作為測試與 CI 的覆寫出口。 labels-list 為第一支腳本,刻意只讀不寫,呼應「不自動建立 Gitea 標籤」。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>