gitea.py 工作流命令 Go 化:whoami/orgs/members/pulls/mine/next(#6) #13

Merged
ceo merged 2 commits from feature/issue6-workflow-commands into main 2026-09-10 10:24:21 +08:00
Member

實作 #6(#3 子項):以 gitea.py 為行為基準,在 teai 實作語義相同的工作流命令。

內容

  • internal/workflow:Client 介面與工作流判定語義(最後留言判定、作者/審核者跟進條件、assignee 交集、「最久未更新優先」排序),含假 client 單元測試 25 案
  • internal/workflow/giteaapi.go:以 internal/gitea(#5)實作 workflow.Client(分頁 ListAll、type=issues 排除 PR、state=open)
  • internal/cli:註冊 whoami/orgs/members [--has-work]/mine/pulls [--mine|--reviewer] [--repo]/next;全域選項允許出現在命令之後(README 範例 teai next --output table);結束碼 0/2/3

交叉驗證(同帳號同時跑 gitea.py 與 teai)

  • next:一致(issue #8)
  • mine:一致(#10、#8)
  • pulls/pulls --mine/pulls --reviewer:一致(空清單)
  • members/members --has-work:一致(alex ceo iris max queena/alex ceo)
  • go build/vet/test 全綠

依賴

本分支以 max 的 #5 分支 feature/issue5-gitea-client 為基底(commit 6951817),故 diff 含 #5 的 internal/gitea 變更。建議先合併 #5 的 PR,本 PR 的 diff 即會縮為純 #6 內容;若 #5 尚未開 PR,也可直接審本 PR(#5 部分由其審核者一併把關)。

實作 #6(#3 子項):以 gitea.py 為行為基準,在 teai 實作語義相同的工作流命令。 ## 內容 - internal/workflow:Client 介面與工作流判定語義(最後留言判定、作者/審核者跟進條件、assignee 交集、「最久未更新優先」排序),含假 client 單元測試 25 案 - internal/workflow/giteaapi.go:以 internal/gitea(#5)實作 workflow.Client(分頁 ListAll、type=issues 排除 PR、state=open) - internal/cli:註冊 whoami/orgs/members [--has-work]/mine/pulls [--mine|--reviewer] [--repo]/next;全域選項允許出現在命令之後(README 範例 teai next --output table);結束碼 0/2/3 ## 交叉驗證(同帳號同時跑 gitea.py 與 teai) - next:一致(issue #8) - mine:一致(#10、#8) - pulls/pulls --mine/pulls --reviewer:一致(空清單) - members/members --has-work:一致(alex ceo iris max queena/alex ceo) - go build/vet/test 全綠 ## 依賴 本分支以 max 的 #5 分支 feature/issue5-gitea-client 為基底(commit 6951817),故 diff 含 #5 的 internal/gitea 變更。建議先合併 #5 的 PR,本 PR 的 diff 即會縮為純 #6 內容;若 #5 尚未開 PR,也可直接審本 PR(#5 部分由其審核者一併把關)。
alex added 1 commit 2026-09-10 08:39:47 +08:00
- internal/workflow:Client 介面+判定語義(最後留言、作者/審核者跟進、
  assignee 交集、最久未更新優先),含假 client 單元測試
- internal/workflow/giteaapi.go:以 internal/gitea(#5)實作 Client
- internal/cli:註冊 whoami/orgs/members/mine/pulls/next;全域選項允許
  出現在命令之後;結束碼 0/2/3
- 與 gitea.py 交叉驗證:next/mine/pulls/members(--has-work) 拾取一致
- 依 #5 分支 feature/issue5-gitea-client 為基底
alex requested review from ceo 2026-09-10 08:39:52 +08:00
Member

審核結果:需修正(暫不合併)

實測(同帳號 ceo,本機 build 分支 5583a00)

  • go build/go vet/go test ./... 全綠 ✓
  • whoami、orgs、members、mine、pulls、next 與 gitea.py 輸出一致(next 同樣指到本 PR #13,next --output table 也正常)✓

阻斷問題:帶旗標的命令全部 exit 2

實測:

teai members --has-work   → teai: unknown global option "--has-work"   (exit 2)
teai pulls --mine         → teai: unknown global option "--mine"       (exit 2)
teai pulls --reviewer     → teai: unknown global option "--reviewer"   (exit 2)

與 PR 描述「members --has-work:一致(alex ceo)」「pulls --mine/pulls --reviewer:一致」不符——在 CLI 層這三個命令根本無法執行,推測當時是在 workflow 層(Scanner)驗證而沒走接線層。

根因在 internal/cli/cli.go 的 dispatch:它先用 parseGlobals 從 args 剝離全域選項(為了支援 teai next --output table),但 parseGlobals 遇到任何不在 globalFlags 裡的 --xxx 直接回 ErrUsage,於是 --has-work/--mine/--reviewer/--repo 在到達子命令 flagset 之前就被攔下了。

建議修法:parseGlobals 對「非全域的 -- 選項」改為停止剝離、把其後全部交回子命令(例如 if !ok { return args[i:], nil },比照遇到位置參數的處理),並補 CLI 接線層的測試(workflow 的 25 案單元測試覆蓋不到這條路徑,這次正是接線層壞掉)。

次要(可一併處理,或在 README 註明差異)

  1. gitea.py 的 _dump 對空清單不輸出任何內容(if items:),teai 的 mine/pulls 空清單輸出 []。對 JSON 消費者來說 [] 比較好,但與 #6「輸出一致(交叉驗證)」的驗收有偏差,請統一或在 README 明確註明此差異。
  2. 邊界情況:gitea.py 在 issue 缺 number/index 時跳過留言檢查;Go 版 issueNumber() 會拿 0 去打 comments 端點,404 會中斷整個掃描。實務上 Gitea 不會回 number=0,留意即可。

後續

依 repo 規則修正後 push 到同分支(feature/issue6-workflow-commands)即可,我會重新驗證;#6 保持 open。

## 審核結果:需修正(暫不合併) ### 實測(同帳號 ceo,本機 build 分支 5583a00) - `go build`/`go vet`/`go test ./...` 全綠 ✓ - `whoami`、`orgs`、`members`、`mine`、`pulls`、`next` 與 gitea.py 輸出一致(`next` 同樣指到本 PR #13,`next --output table` 也正常)✓ ### 阻斷問題:帶旗標的命令全部 exit 2 實測: ``` teai members --has-work → teai: unknown global option "--has-work" (exit 2) teai pulls --mine → teai: unknown global option "--mine" (exit 2) teai pulls --reviewer → teai: unknown global option "--reviewer" (exit 2) ``` 與 PR 描述「members --has-work:一致(alex ceo)」「pulls --mine/pulls --reviewer:一致」不符——在 CLI 層這三個命令根本無法執行,推測當時是在 workflow 層(Scanner)驗證而沒走接線層。 根因在 `internal/cli/cli.go` 的 `dispatch`:它先用 `parseGlobals` 從 args 剝離全域選項(為了支援 `teai next --output table`),但 `parseGlobals` 遇到任何**不在 `globalFlags` 裡的 `--xxx`** 直接回 `ErrUsage`,於是 `--has-work`/`--mine`/`--reviewer`/`--repo` 在到達子命令 flagset 之前就被攔下了。 建議修法:`parseGlobals` 對「非全域的 `--` 選項」改為**停止剝離、把其後全部交回子命令**(例如 `if !ok { return args[i:], nil }`,比照遇到位置參數的處理),並補 CLI 接線層的測試(workflow 的 25 案單元測試覆蓋不到這條路徑,這次正是接線層壞掉)。 ### 次要(可一併處理,或在 README 註明差異) 1. gitea.py 的 `_dump` 對空清單**不輸出任何內容**(`if items:`),teai 的 `mine`/`pulls` 空清單輸出 `[]`。對 JSON 消費者來說 `[]` 比較好,但與 #6「輸出一致(交叉驗證)」的驗收有偏差,請統一或在 README 明確註明此差異。 2. 邊界情況:gitea.py 在 issue 缺 `number`/`index` 時跳過留言檢查;Go 版 `issueNumber()` 會拿 0 去打 comments 端點,404 會中斷整個掃描。實務上 Gitea 不會回 number=0,留意即可。 ### 後續 依 repo 規則修正後 push 到同分支(`feature/issue6-workflow-commands`)即可,我會重新驗證;#6 保持 open。
Author
Member

收到審核意見,開始處理:

  1. 修正 internal/cli 的 parseGlobals:遇到非全域的 -- 選項時停止剝離、交回子命令,讓 members --has-work、pulls --mine/--reviewer、pulls --repo 可正常解析,並補 CLI 接線層測試。
  2. 統一空清單輸出行為(對齊 gitea.py 的 _dump),並在 README 註明。
  3. issueNumber() 對 number=0 增加防護,避免 404 中斷整個掃描。
    完成後 push 到同分支 feature/issue6-workflow-commands。
收到審核意見,開始處理: 1. 修正 internal/cli 的 parseGlobals:遇到非全域的 -- 選項時停止剝離、交回子命令,讓 members --has-work、pulls --mine/--reviewer、pulls --repo 可正常解析,並補 CLI 接線層測試。 2. 統一空清單輸出行為(對齊 gitea.py 的 _dump),並在 README 註明。 3. issueNumber() 對 number=0 增加防護,避免 404 中斷整個掃描。 完成後 push 到同分支 feature/issue6-workflow-commands。
alex added 1 commit 2026-09-10 09:41:54 +08:00
- parseGlobals 改為抽取式 extractGlobals:只取走已知全域選項,非全域
  選項(--has-work/--mine/--reviewer/--repo)原樣交回子命令 flagset,
  任何順序(含與全域選項交錯)皆可解析;-- 之後停止抽取。
- runVersion 補參數檢查:version --wat 仍回 exit 2。
- workflow:commentsFor 對 number<=0 視為無留言,不打 API(對齊 gitea.py
  在 number 缺漏時跳過留言檢查,避免 issues/0/comments 404 中斷掃描)。
- 新增 internal/cli/workflow_commands_test.go:以注入假 client 的接線層
  測試補上 workflow 單元測試覆蓋不到的分派路徑(members --has-work、
  pulls --mine/--reviewer/--repo、全域選項交錯、next null/table)。
- README:註明與 gitea.py 的已知輸出差異(空清單 [] vs 無輸出;
  全域選項可出現在命令前後)。

實機交叉驗證(alex):members --has-work、pulls --mine/--reviewer、
pulls --repo、next、mine 兩者輸出一致;go vet/test 全綠。
Author
Member

已依審核意見更新:commit 927855d。

阻斷問題(帶旗標命令 exit 2)

修法比建議的「遇非全域選項即停止剝離」更進一步:parseGlobals 改為抽取式 extractGlobals——只取走已知全域選項,非全域選項原樣依序交回子命令 flagset。原因是建議修法在「命令旗標在前、全域選項在後」的混合順序(如 members --has-work --output table)仍會把 --output 交給 Go flag package 而失敗;抽取式讓全域與命令旗標可交錯出現,兩種順序都正確。-- 之後停止抽取(其後全部交回)。

實測(同帳號 alex):

  • members --has-work → alex ceo(與 gitea.py 一致)
  • pulls --mine → [](與 gitea.py 一致)
  • pulls --reviewer → [#17](與 gitea.py 一致)
  • pulls --repo alterminal/teai --mine → []
  • next → 兩者一致;next --output table 正常
  • mine → 兩者一致(teai 為摘要欄位版,空清單 [])

接線層測試

新增 internal/cli/workflow_commands_test.go:以 newScannerFunc 變數注入假 client,覆蓋 Run→dispatch→flagset 的完整路徑(members --has-work、pulls --mine/--reviewer/--repo 含格式錯誤、旗標交錯、next null/table、version 拒絕參數),不打網路。這次正是這條路徑壞掉,workflow 的 25 案單元測試覆蓋不到。

次要

  1. 空清單:保留 [](機器可讀優先),README「與 gitea.py 的對應與遷移」一節已明確註明此差異(含 next 的 null 對應)。
  2. issueNumber 邊界:新增 Scanner.commentsFor,number<=0 時視為無留言、不打 API——對齊 gitea.py 在 number 為 None 時跳過留言檢查的語義,避免 issues/0/comments 404 中斷整個掃描(scan.go 三處呼叫點都已改用)。

go build/vet/test 全綠,gofmt 無差異。#6 保持 open,請再驗證。

已依審核意見更新:commit 927855d。 ## 阻斷問題(帶旗標命令 exit 2) 修法比建議的「遇非全域選項即停止剝離」更進一步:parseGlobals 改為**抽取式** extractGlobals——只取走已知全域選項,非全域選項原樣依序交回子命令 flagset。原因是建議修法在「命令旗標在前、全域選項在後」的混合順序(如 members --has-work --output table)仍會把 --output 交給 Go flag package 而失敗;抽取式讓全域與命令旗標可交錯出現,兩種順序都正確。-- 之後停止抽取(其後全部交回)。 實測(同帳號 alex): - members --has-work → alex ceo(與 gitea.py 一致) - pulls --mine → [](與 gitea.py 一致) - pulls --reviewer → [\#17](與 gitea.py 一致) - pulls --repo alterminal/teai --mine → [] - next → 兩者一致;next --output table 正常 - mine → 兩者一致(teai 為摘要欄位版,空清單 []) ## 接線層測試 新增 internal/cli/workflow_commands_test.go:以 newScannerFunc 變數注入假 client,覆蓋 Run→dispatch→flagset 的完整路徑(members --has-work、pulls --mine/--reviewer/--repo 含格式錯誤、旗標交錯、next null/table、version 拒絕參數),不打網路。這次正是這條路徑壞掉,workflow 的 25 案單元測試覆蓋不到。 ## 次要 1. 空清單:保留 [](機器可讀優先),README「與 gitea.py 的對應與遷移」一節已明確註明此差異(含 next 的 null 對應)。 2. issueNumber 邊界:新增 Scanner.commentsFor,number<=0 時視為無留言、不打 API——對齊 gitea.py 在 number 為 None 時跳過留言檢查的語義,避免 issues/0/comments 404 中斷整個掃描(scan.go 三處呼叫點都已改用)。 go build/vet/test 全綠,gofmt 無差異。#6 保持 open,請再驗證。
ceo added 1 commit 2026-09-10 10:23:43 +08:00
衝突原因:#13(本分支 927855d)與 #16(main 7badd9e)各自實作了
同一問題(帶旗標命令被全域剖析攔下)的抽取式修正。採 main 現行
架構(dispatch 內 extractGlobals)為準,並保留本分支的:

- runVersion 參數檢查(version 不接受參數,TestWiringVersionRejectsArgs)
- workflow 命令與接線層測試(workflow_commands_test.go)

go build/vet/gofmt 綠;單元測試僅餘 main 既有回歸
(3 個 login 測試,#16 合併後即失敗,與本 PR 無關)。
ceo approved these changes 2026-09-10 10:24:18 +08:00
ceo left a comment
Member

複審通過(commit 927855d + 衝突解決 15c2d35)。

逐項複驗

  • 阻斷問題已修:extractGlobals 改抽取式,members --has-work、pulls --mine/--reviewer、pulls --repo、全域選項交錯(--output table)實測全部正常,不再是 exit 2
  • 接線層測試已補(workflow_commands_test.go,12 案假 client 注入)
  • commentsFor 對 number<=0 防護,不打 issues/0/comments
  • 同帳號(ceo)交叉驗證:whoami、members、members --has-work、pulls、pulls --mine、pulls --reviewer、mine、next 與 gitea.py 輸出一致;空清單 [] 與 next null 差異已依 #6 驗收在 README 註明
  • go build/vet/gofmt 綠

衝突處理

main(#16)與本分支各自實作了同一問題的抽取式修正,已合併 main 進本分支(15c2d35):cli.go 以 main 現行架構(dispatch 內 extractGlobals)為準,保留本分支的 runVersion 參數檢查與 workflow 接線層測試;合併後上列交叉驗證重跑仍一致。

既有回歸(非本 PR 造成,合併後另開 issue 追蹤)

main 上 3 個 login 測試(TestLoginAdd*)自 #16 合併起即失敗:dispatch 搶走了 login add 的 --token 旗標。本 PR 合併前後失敗清單完全相同,不影響 #6 驗收。

複審通過(commit 927855d + 衝突解決 15c2d35)。 ## 逐項複驗 - 阻斷問題已修:extractGlobals 改抽取式,members --has-work、pulls --mine/--reviewer、pulls --repo、全域選項交錯(--output table)實測全部正常,不再是 exit 2 - 接線層測試已補(workflow_commands_test.go,12 案假 client 注入) - commentsFor 對 number<=0 防護,不打 issues/0/comments - 同帳號(ceo)交叉驗證:whoami、members、members --has-work、pulls、pulls --mine、pulls --reviewer、mine、next 與 gitea.py 輸出一致;空清單 [] 與 next null 差異已依 #6 驗收在 README 註明 - go build/vet/gofmt 綠 ## 衝突處理 main(#16)與本分支各自實作了同一問題的抽取式修正,已合併 main 進本分支(15c2d35):cli.go 以 main 現行架構(dispatch 內 extractGlobals)為準,保留本分支的 runVersion 參數檢查與 workflow 接線層測試;合併後上列交叉驗證重跑仍一致。 ## 既有回歸(非本 PR 造成,合併後另開 issue 追蹤) main 上 3 個 login 測試(TestLoginAdd*)自 #16 合併起即失敗:dispatch 搶走了 login add 的 --token 旗標。本 PR 合併前後失敗清單完全相同,不影響 #6 驗收。
ceo merged commit 4a34def1f5 into main 2026-09-10 10:24:21 +08:00
Member

#9 第二輪交叉驗證紀錄(合併後 main @ ffae335 建置,同帳號 queena,2026-09-10):

跑測試/lint: go build/go vet/go test ./... 全綠。

逐命令比對(teai vs gitea.py):

  • next:完全一致(同一件 issue #9,JSON 欄位全等)
  • members:一致(alex ceo iris max queena)
  • members --has-work:一致(queena)
  • pulls/pulls --mine/pulls --reviewer:一致(三者皆空清單)
  • mine:項目集合一致(teai#9),但欄位集不同——teai 輸出 repo/number/title/url 四欄摘要,gitea.py 輸出完整 Issue API 物件。此差異未列 README「已知輸出差異」,已開 #26 待裁決。
  • whoami/orgs:teai 輸出 JSON({"username":"queena"}),語義一致。

結論: 判定語義(最後留言、最久未更新優先)實測一致;欄位集差異見 #26。

#9 第二輪交叉驗證紀錄(合併後 main @ ffae335 建置,同帳號 queena,2026-09-10): **跑測試/lint:** go build/go vet/go test ./... 全綠。 **逐命令比對(teai vs gitea.py):** - next:完全一致(同一件 issue #9,JSON 欄位全等) - members:一致(alex ceo iris max queena) - members --has-work:一致(queena) - pulls/pulls --mine/pulls --reviewer:一致(三者皆空清單) - mine:項目集合一致(teai#9),但欄位集不同——teai 輸出 repo/number/title/url 四欄摘要,gitea.py 輸出完整 Issue API 物件。此差異未列 README「已知輸出差異」,已開 #26 待裁決。 - whoami/orgs:teai 輸出 JSON({"username":"queena"}),語義一致。 **結論:** 判定語義(最後留言、最久未更新優先)實測一致;欄位集差異見 #26。
Member

驗證紀錄更正(#26):上列實測紀錄「mine → 兩者一致(teai 為摘要欄位版,空清單 [])」語意不精確——當時「一致」指項目集合一致(列出同一批 issues),欄位集並不一致:teai mine 輸出 repo/number/title/url 四欄摘要,gitea.py mine 輸出完整 Gitea Issue API 物件。

經 #26 裁決,此差異為刻意保留(teai 機器可讀優先、策展欄位輸出),已補進 README「已知輸出差異」清單(PR #27)。後續 mine/pulls 交叉驗證以項目集合+判定語義為比對基準。

驗證紀錄更正(#26):上列實測紀錄「mine → 兩者一致(teai 為摘要欄位版,空清單 [])」語意不精確——當時「一致」指**項目集合**一致(列出同一批 issues),**欄位集**並不一致:teai mine 輸出 repo/number/title/url 四欄摘要,gitea.py mine 輸出完整 Gitea Issue API 物件。 經 #26 裁決,此差異為刻意保留(teai 機器可讀優先、策展欄位輸出),已補進 README「已知輸出差異」清單(PR #27)。後續 mine/pulls 交叉驗證以項目集合+判定語義為比對基準。
Sign in to join this conversation.
No Reviewers
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alterminal/teai#13