Agent2API 協作與發版規範 是做什麼的?
涵蓋兩類工作:日常協作(分支落腳點、PR 署名、Issue 落款,第 1–3 節)與發版(版本號、更新日誌、tag、Release、驗收,第 4–7 節),排錯見第 8 節。
0. 兩條人工紅線(必須停下等確認)
下列兩件事必須由人先審過、明確點頭,代理才能繼續;可以準備草稿、可以列依據,不能自己拍板往下走:
- 發版:進入發版流程(改版本號、建立發布提交、推 main / beta、打 tag、掛 Release)之前,先把版本號與「這次要發什麼」講清楚,等用戶確認。未經確認不得推任何分支或 tag —— tag 一旦推出去就會觸發構建、產出公開的安裝包與映像檔,收不回來。
- 更新日誌:寫好的日誌(發布提交的提交資訊,同時也是 GitHub Release 正文)必須先給用戶逐條過目。用戶要求改措辭、增刪條目、調整分類前綴時,按回饋改完要再確認一次,不能因為「改得不多」就直接提交。
一次發版裡兩條同時適用:既要有版本號的確認,也要有日誌的確認;用戶說「繼續」之後才動手。
1. 分支落腳點:日常只碰 beta,發版才動 main
| 分支 | 角色 | 什麼時候動它 |
|---|---|---|
beta | 集成分支(預設落腳點) | 日常修改、合併 PR、解衝突 —— 一律先到這裡 |
main | 發版分支 | 只在構建發版時:切 main、把 beta 合進來、打 tag |
- 合併 PR 時 base 選
beta,不要選main。合進main等於把還沒發布的改動提前塞進發版分支;之後還得反過來把 main 合回 beta,平白多一次合併提交、多一次衝突機會。已經把 PR 開成 base=main 的,先在 PR 頁面把 base 改成beta再合。 - 本地改動也提交到
beta(寫程式碼、改文件、解衝突都算),推origin/beta;合併結果的核對、構建自檢都在 beta 上做完。 - 只有正式發版才切
main:git switch main→ 把 beta 合進來 → 寫發布提交、推 main、打 tag;tag 必須落在main上。發完版 beta 再把 main 合回來,兩邊保持一致。 - 不要自作主張選落腳分支:用戶沒說要發版,就不要往
main提交 / 推送,也不要把 PR 的 base 改成main。 - beta 版(預發布)是唯一例外:不切
main,tag 直接落在beta分支自己身上(見第 6 節)。
2. 合併 PR:保留貢獻者署名
合併貢獻者的 PR 時,提交作者(author)必須還是貢獻者本人 —— 這直接決定對方能不能進倉庫首頁 Contributors 名單、貢獻圖裡有沒有這一筆。署名是貢獻者應得的東西,不該因為合併方式丟掉。
GitHub 的歸屬規則只有一條:提交頭裡的 author 郵箱出現在某個帳號已驗證的郵箱列表裡,這次提交就算那個帳號的;否則記成匿名條目,不進 Contributors。另有兩條口徑:統計只認預設分支(本倉庫 main),合併提交與空提交不計入。
- 預設用 GitHub 網頁上的合併按鈕(
Merge pull request/Squash and merge/Rebase and merge):三種都會保留原作者為 author;squash 時 PR 裡的多位作者會一併進 co-author 名單。 - 禁止「本地摘取重提」:把對方的改動拷過來自己提交(本地
git merge --squash後自己 commit、重抄一遍改動、或改了 author 再提交)都不行 —— 這樣 author 是合併者,貢獻者的署名在 GitHub 側完全不可見。本倉庫歷史上出現過這種情況,別再來一次。 - 確實要在本地處理時(有衝突要解、要順帶調整),必須把署名帶上:單一原作者用
git commit --author="原作者 <原作者郵箱>";一個提交裡有多個人的工作,在提交資訊末尾加 trailerCo-authored-by: 名字 <郵箱>。 - 摘除 AI 的 co-author trailer:貢獻者提交資訊末尾的
Co-Authored-By: Claude Code <…>這類 AI trailer,合併時一律刪除 —— GitHub 會把它渲染成共同作者,進了main就出現在 Contributors 名單裡,而 AI 不是貢獻者。只刪 trailer 行本身,author 與其餘提交資訊逐字保留;正文裡對 AI 產品的技術性提及(「支援 Claude Code 用戶端」「Claude 相容端點」之類)與此無關,不動。摘除動的是已合入分支的歷史,趁合併後、發版前盡早做(git filter-branch --msg-filter摘行後 force-push),拖到發版後就得重寫main。 - 郵箱必須能對上帳號:用對方 GitHub 帳號裡已驗證的地址;對方想保密就用 GitHub 給的
ID+使用者名稱@users.noreply.github.com(在對方帳號的 Emails 設定裡查)。內網地址、user@機器名這類郵箱永遠關聯不上帳號,提交會被記成匿名。 - 合併後順手核對一次:
git log --format='%an <%ae>' -1 <合併提交>,或在 PR 頁面看 commit 旁的頭像是否指向原作者。
3. 回覆 Issue / PR:標明 AI 來源
由 AI 代理發出(含代筆)的每一條 Issue / PR 回覆,末尾都要單獨一行寫明來源。 收件人有權知道自己在跟誰說話 —— 與第 2 節的署名是同一條道理:誰說的寫誰。
固定落款,逐字照用(別改寫成「機器人」「助手」這類含糊說法):
🤖 本條回覆由 AI Agent 生成
- 位置:整條回覆的最後一行,單獨成段(前後各留一個空行);不要塞進正文段落、列表項、引用區塊或程式碼區塊裡 —— 引用別人的話時,落款落在自己的話之後。
- 英文場合(外部貢獻者的 PR / Issue)在中文那行下面再補一行
Generated by an AI agent.,說的是同一件事,兩邊都不省。 - 代理自己新建的 Issue / PR,正文末尾同樣加。
- 代筆也算:人在對話裡讓代理轉達(哪怕逐字)一段話,落款照加 —— 它描述的是「這條回覆是誰發出去的」,不是「誰構思的」。
- 只約束 Issue / PR 回覆(評論、review 意見、關閉 / 合併說明)。提交資訊不追加這一行,署名口徑見第 2 節。
4. 版本號
- 版本號形如
X.Y.Z(如2.7.2),日常小版本「遞增 0.01」= 末位 +1(如2.7.8→2.7.9)。 - 遞增幅度以用戶要求為準:預設用上面的「遞增 0.01」;用戶另有要求時(如「這次遞增 0.1」)一律聽用戶的,按用戶指定的幅度算出新版本號,不要自作主張套用預設幅度。
- 版本號要同步改 5 處(缺一處會導致安裝包與顯示版本對不上):
package.json(根)desktop-tauri/package.jsondesktop-tauri/src-tauri/Cargo.tomldesktop-tauri/src-tauri/tauri.conf.jsondesktop-tauri/src-tauri/server/Cargo.toml
- 應用內「關於」與更新檢查讀的是
env!("CARGO_PKG_VERSION")(第 5 處),改完跑一次cargo check讓Cargo.lock跟著刷新。 - 順手過一遍安裝文件:安裝說明已經從 README 搬進
docs/install.md(README 只留速查表 + 一個連結),另有 5 份譯文docs/install.{en,zh-Hant,ja,ko,pt-BR}.md,六份內容一一對應。凡是這次改動碰到了安裝 / 升級 / 卸載步驟、產物體名、預設連接埠、環境變數、安裝腳本參數,六份都要同步改;README 速查表受影響時也一起改。
5. 正式版發版流程
按順序執行;第 0 節的兩條紅線貫穿全程。
# 1. 收乾淨工作區(tag 裡的程式碼就是發出去的程式碼)
git status --short # 必須為空;有改動就先提交或清理
cargo check # 或前端有改動時做一次構建自檢
# 2. 切到 main 並把 beta 合進來
git switch main && git pull
git merge beta
# 3. 五處版本號一起改(預設末位 +1,幅度聽用戶);改完刷新 Cargo.lock
cargo check
# 4. 寫發布提交:提交資訊 = 更新日誌
git commit -am "…更新日誌…"
# 5. 推 main、打 tag、推 tag(一條 v* tag 觸發全部構建)
git push origin main
git tag vX.Y.Z
git push origin vX.Y.Z
# 6. 兩個工作流跑完後掛 GitHub Release
gh run list --limit 4 # 手動跑 gh 前先設代理,見第 8 節
bash scripts/release.sh vX.Y.Z
發布提交的提交資訊 = 更新日誌,因為發版收尾腳本建立 / 更新 Release 時,正文取「tag 所指提交的提交資訊」(git log -1 --format=%B)。
- 格式沿用歷史版本的編號清單,每行一條,帶分類前綴:
新增:/最佳化:/修復:/更新:/移除:,最後一條固定版本:X.Y.Z → X.Y.Z+1; - 內容取「上個 tag 以來的全部提交」綜合整理(
git log vX.Y.Z..HEAD --oneline),純 CI / 臨時的偵錯提交歸併成一條流程性描述即可; - 本次沒有程式碼改動時用空提交承載:
git commit --allow-empty -F 更新日誌.txt; - 更新日誌草稿與歷史發布說明統一寫在
docs/release-notes/<版本號>.md(如docs/release-notes/2.9.2.md),不要在專案根目錄新建.release-notes-*.md——docs/release-notes/已在忽略清單裡(只忽略這個目錄,docs/下其它文件正常入庫),新增檔案不必再加忽略規則。
推 v* tag 會同時觸發兩個工作流(.github/workflows/):
| 工作流 | 產出 | 說明 |
|---|---|---|
build.yml(build) | 桌面端三平台安裝包 + Linux 服務端包 | 四個 job:macos(dmg)/ windows(NSIS exe)/ linux-desktop(deb + AppImage)/ linux-server(容器內更新用的 tar.gz)。macOS 與 Linux 桌面端的產物只能在各自平台上構建,無法從 Windows 交叉編譯;所有產物只掛 artifact,GitHub Release 由本地腳本掛載 |
docker.yml(docker) | Docker Hub aimodcc/agent2api:<版本> + :latest(amd64 / arm64 雙架構) | 手動 workflow_dispatch 觸發時只出 :dev 測試 tag,不碰正式 tag;beta tag(版本號帶 -)只出 <版本>,不移動 :latest |
跟蹤進度:
gh run list --limit 4
gh run watch <run-id> --exit-status
發版收尾:GitHub Release 不由 CI 發布(Release 本來就不會自動建立),由本地腳本一步掛載:
bash scripts/release.sh vX.Y.Z # 自動找該 tag 的成功 build run
bash scripts/release.sh vX.Y.Z <run-id> # 或明確指定 run
腳本做三件事:下載 build 的安裝包 artifact 到 dist/ → 按 tag 提交資訊(= 更新日誌)建立 / 更新 GitHub Release 並掛附件 → 列印驗收提示。冪等可重跑(--clobber 覆蓋附件)。
6. beta 版(預發布)
應用裡有「設定 → 軟體更新 → 更新設定 → 加入 beta 版」開關:打開的人才收到 beta 更新,沒打開的人只看正式發布。
beta 版不是另一條流水線,而是第 5 節流程的一個變體 —— 唯一結構差別是「從 beta 分支打帶預發布後綴的 tag」,提交、tag、Release、驗收逐字照舊。第 0 節的兩條紅線同樣適用。
# 1. 在 beta 分支上動手(beta 版不碰 main)
git switch beta && git pull
git status --short # 必須為空
# 2. 五處版本號一起改成「與 tag 逐字一致」的型態:
# tag 打 v3.1.0-beta.1 → 五處都寫 3.1.0-beta.1(清單見第 4 節)
cargo check # 順帶重新整理 Cargo.lock
# 3. 提交並推 beta 分支(提交資訊 = 這次的 beta 更新日誌,格式同第 5 節)
git commit -am "…beta 版日誌…"
git push origin beta
# 4. 打 tag 並推:tag 落在 beta 分支的提交上
git tag v3.1.0-beta.1
git push origin v3.1.0-beta.1
# 5. 兩個工作流跑完後掛 Release(腳本按 tag 裡的 `-` 自動標 pre-release)
gh run list --limit 4
bash scripts/release.sh v3.1.0-beta.1
驗收就三條:Release 顯示 Prerelease、exe / dmg / deb / AppImage 附件齊全、Docker Hub 上 :latest 的 Pushed 時間沒有跟著變(完整清單見第 7 節)。
三處不能錯,每處錯了都有明確症狀:
- 五處版本號漏了後綴(寫成
3.1.0):比較是按 semver 做的,執行版本3.1.0對線上 tag3.1.0-beta.1會判成「目前比線上新」,beta 用戶永遠收不到更新。五個檔案都接受帶後綴的版本號(Cargo / npm / tauri.conf.json 都認),照 tag 抄即可。 - Release 漏標 pre-release:標記就是開關的分界線 —— 正式通道走 GitHub 的
/releases/latest,它排除預發布,只有開了開關的人從/releases列表裡看到 beta。漏標 = beta 被所有正式版用戶當成正式更新收到。scripts/release.sh已按「tag 裡帶-」自動標記,重跑收尾時也會把標記糾正回來,走腳本就不用操心。 - tag 打在
main上:beta 是集成分支,beta 版要的是它的快照。打在main上要麼不含 beta 分支的新改動(白測一場),要麼等於把還沒發布的改動提前推進main。
docker 的 :latest 不會被 beta tag 移動(.github/workflows/docker.yml),映像檔仍出 3.1.0-beta.1 那個版本 tag,想嚐鮮的按版本號拉。
後續常見三件事:
- 再發一個 beta:核心號不變、遞增序號(
3.1.0-beta.2,五處一起改,上面那套命令照走)。預發布序號能分出先後(beta.2>beta.1),已經在 beta 上的人照樣收到更新 —— 所以 beta 之間也要保持「tag 與版本號逐字一致」。 - 轉正(發
3.1.0正式版):按第 5 節在main上走,五處版本號去掉後綴。beta 用戶不會自動接回正式版:開關開著的人只看預發布版(pick_listed_release按「目前跑的是不是 beta 版」篩),轉正的3.1.0對他們不算更新 —— 要留在 beta 頻道的人等下一個 beta(3.2.0-beta.1一出現就收得到);想回正式版由用戶自己關開關,關掉後只在版本號高於他手上那個 beta 的正式版發布時才收到更新(3.1.0>3.1.0-beta.1,但更早的3.0.9不會把他降級)。 - 「打開了卻沒有反應」:倉庫裡一個預發布 Release 都沒有時,打開這個開關也只會看到正式版 —— 第一次 beta 發出去之後才有效果。
7. 驗收清單
- GitHub Release:
gh release view vX.Y.Z(手動跑 gh 前先設代理,見第 8 節)—— 正文日誌齊全,四個桌面端附件都在:WindowsAgent2API_<版本>_x64-setup.exe、macOSAgent2API_<版本>_universal.dmg、LinuxAgent2API_<版本>_amd64.deb與Agent2API_<版本>_amd64.AppImage(另有兩個agent2api-server-linux-*.tar.gz+.sha256,那是容器內更新包,見第 5 節); - Linux 桌面端:裝 deb(或
chmod +x後跑 AppImage)能啟動,且面板「設定 → 軟體更新」能檢查到版本、給出對應包型的安裝入口(deb → 系統安裝器,AppImage → 換檔案); - Docker Hub:
aimodcc/agent2api的 Tags 頁出現<版本>與latest,Pushed 時間一致; - 安裝包「關於」頁版本號與 tag 一致;
- 安裝文件對得上實際產物:
docs/install.md與 5 份譯文的產物名、連接埠、命令、環境變數跟這次 Release 一致(README 裡那幾張速查表同理); - beta 版多一條:Release 顯示 Prerelease,且 Docker Hub 上
latest沒有跟著動。
8. 已知坑與排查
-
GHCR 新包預設私有:若以後映像檔改推 GHCR,首次推送後需到包設定手動改 Public(目前推的是 Docker Hub,無此問題)。
-
gh 不讀 git 的代理設定,跑 gh 前要先設代理環境變數:
gh是 Go 程式,不讀~/.gitconfig裡的http(s).proxy,也不讀 Windows 系統代理(WinINET),只認HTTPS_PROXY/HTTP_PROXY環境變數。本機 GitHub 直連時通時不通,手動執行 gh 命令前先設:export HTTPS_PROXY=$(git config --get-urlmatch http.proxy https://github.com || git config --get https.proxy)只設
HTTPS_PROXY就夠(gh 的請求全是 HTTPS,實測不讀HTTP_PROXY)。scripts/release.sh已內建這一步(從 git 設定讀取後透傳給 gh),用腳本發版無需手動設。代理沒開時 gh 會立刻報proxyconnect tcp: ... connection refused而不回退直連 —— 與git push的表現一致。