Gh-Release 是做什麼的?
Bump、changelog、tag、push、watch。
以下命令中,~/.claude/skills/gh-release/scripts/ 使用完整路徑。請嚴格按照原文執行——保留開頭的 ~,不要將其展開。 權限規則會比對確切的命令字串,因此 /home/<you>/.claude/...(波浪號展開後)、python3 ~/...、相對路徑以及先執行 cd 都會被視為不同的字串,並會重新觸發權限提示。波浪號路徑並非「以非正式方式書寫的絕對路徑」——對權限規則而言,它就是路徑本身。
有兩件事會讓發行失敗,而且都發生在建立標籤之前:
- 重複打造倉庫已自動化的版本提升。 手動編輯只會修改倉庫已知的檔案,遺漏它本來就會處理的檔案,並且跳過其檢查關卡。
- 標籤無法觸發任何動作。 看起來就像 CI 佇列速度緩慢,結果二十分鐘後你才會發現。
步驟 1 和步驟 3 的目的就是要捕捉這些情況。
0. 決定版本
讀取標籤(git tag --sort=-v:refname | head -5)以了解倉庫的格式(v1.2.3 或 1.2.3)和當前版本。如果使用者指定了版本,請直接使用。否則,根據自上次標籤以來的提交記錄來提議一個版本——重大變更為 major,feat: 為 minor,其他情況為 patch——並說明原因。
在以下情況下拒絕繼續:工作目錄不乾淨、HEAD 處於分離狀態、或分支落後於遠端倉庫。發布未提交的工作會產生一個任何人都無法重現的標籤。
1. 使用倉庫本身的發行機制
在親自手動操作之前,永遠先檢查:
~/.claude/skills/gh-release/scripts/versiontool.py discover
它會回報專案技能、mise tasks、npm scripts、Make targets、just recipes、scripts/-bin/ 目錄下的項目,以及工具設定檔(cargo-release、bumpversion、release-please、git-cliff)。優先順序如下:
kind=project-skill - 停止閱讀本技能並遵循該技能。它是倉庫自身定義的流程,知道許多這個通用技能無法得知的細節。
一個 task 或 script(kind=mise-task|npm-script|make-target|just-recipe|script)
- 執行前先閱讀它,然後再執行。你需要了解它涵蓋了哪些步驟,以免重複執行或遺漏步驟。典型的 task 會執行以下部分或全部操作:bump、changelog 升級、檢查、commit、tag、push。記錄下來,然後只執行其餘未涵蓋的步驟。
許多 task 只接受裸版本號,並自行添加前綴(例如 mise run release 0.13.0 會標記 v0.13.0),因此如果 task 預期的是版本號,就不要傳入標籤。如果 task 有 --dry-run 參數,請先使用它,並向使用者顯示計畫。
僅有工具設定檔但無相對應的 task - 使用該工具自身的命令(cargo release <version>、bump-my-version bump ...),而不是手動編輯。
result=none - 只有在這種情況下,才回退到步驟 2。
2. 備用方案:尋找每個版本號所在的位置
僅在步驟 1 找不到任何結果時才執行此步驟。
~/.claude/skills/gh-release/scripts/versiontool.py locations <current-version>
它會搜尋確切的版本號字串,而不是「version」這個單詞。在一個真實的倉庫中測試:搜尋 version 鍵找到了 5 個檔案,而搜尋確切字串則找到了 8 個。遺漏的檔案使用了 "version-name"(GNOME 元資料)和 "Version"(Plasma)。遺漏任何一個都會導致發布的二進位檔報告錯誤的版本號。
輸出已分類:edit(需要修改)、regenerate(鎖定檔案——執行提示中的命令,切勿手動編輯,否則 --locked 會在 CI 中失敗)、changelog(步驟 4)。
然後也要檢查上一個版本號——如果在其中找到匹配項,表示該檔案在上次發行中被遺漏,且已過時。請在此次發行中一併修復。
3. 預先檢查標籤
~/.claude/skills/gh-release/scripts/relwatch.py preflight <tag>
它會讀取 .github/workflows/*.yml,提取所有 on.push.tags 模式,並告訴你你的標籤是否符合某個模式,以及該工作流程是否看起來會建立一個 Release。result=NO_MATCH 表示推送這個標籤完全不會觸發任何動作。
請在提交之前執行此操作,此時修改標籤的成本為零。
此外,請目視確認工作流程的 release 步驟使用了你即將推送的標籤。許多工作流程不會驗證標籤是否與程式碼中的版本號相符,因此不一致會導致發布的二進位檔報告錯誤的版本號,且 CI 會毫無異狀地顯示綠色。
4. Changelog
如果步驟 1 的 task 負責升級 changelog,就讓它處理,然後跳到步驟 5。
遵循 Keep a Changelog 格式(存在 ## [Unreleased] 標題):升級它。
## [Unreleased]變成## [X.Y.Z] - YYYY-MM-DD- 在其上方新增一個空的
## [Unreleased] - 更新底部的連結定義,包括
[Unreleased]比較連結的新基礎版本
沒有 changelog,或沒有 Unreleased 章節:根據自上次標籤以來的提交記錄撰寫條目,並按照檔案現有的分類方式進行分組。嚴格比對現有的語氣——如果條目都是一行文,就寫一行文;如果是帶有粗體主題的散文,就寫成那樣。
無論哪種情況:描述的是對專案使用者來說什麼改變了,而不是你對倉庫做了什麼。
對每個條目套用 unslop 技能。 Changelog 會被閱讀數年,人們會根據它決定是否升級。「各種改進和錯誤修復」比沒有條目更糟;同樣地,一個只是用更長的詞彙複述提交主旨的條目也是如此。特定於文件的提示載於 docs-update/SKILL.md 的步驟 5,並在此處原封不動地適用:不要鋪陳、不要使用空洞的加強詞、不要在答案確定時含糊其詞。
5. 在打標籤前驗證
~/.claude/skills/gh-release/scripts/versiontool.py verify <old-version> # 必須輸出 result=clean
即使倉庫的 task 已經執行了版本提升,也要執行此驗證。 六個月前寫的 bumper 不會知道上週有人新增的檔案,而這正是版本號在某個角落過時的原因。Changelog 的歷史會被忽略;只會計算可編輯的檔案。
然後執行專案的檢查關卡(建置、測試、lint)。一個指向紅色提交的標籤必須被刪除並重新建立,而刪除已推送的標籤對於任何已經擷取它的人來說都很麻煩。
6. 提交、打標籤、推送
跳過步驟 1 的 task 已經完成的部分。如果它已經提交、打標籤並推送了,就直接跳到步驟 7。
否則:為發行建立一個提交,主旨需依照 ~/.claude/CLAUDE.md 以分支名稱作為前綴。版本提升和 changelog 應屬於同一個提交;它們是同一項變更。
git commit -m "[<branch>] chore(release): <version>"
git tag -a <tag> -m "<tag>"
git push && git push origin <tag>
先推送提交,再推送標籤。先到達的標籤可能會針對遠端尚未擁有的提交啟動建置。
記錄推送時間;它會界定監控的範圍。
7. 監控直到 Release 建立
~/.claude/skills/gh-release/scripts/relwatch.py watch <tag> --timeout 1800
| 結果 | 意義 |
|---|---|
released url=... | 完成 |
ci_failed run=... conclusion=... | 步驟 8 |
no_run | 未觸發任何動作。標籤不符合任何模式——請執行 preflight |
still_running | 並非失敗。再次監控 |
綠色 CI 不代表已經發布:發布步驟可能比建置步驟落後,因此監控器會持續等待,直到 Release 物件實際存在。
與 babysit-pr 相同的子代理規則:在單一 Bash 呼叫內部的單次監控(--timeout 540)成本更低,可直接內嵌;只有在等待需要跨越多個呼叫時才委派給子代理。在 Release 建立或你遇到阻止因素之前,不要停止。
8. 當 CI 在打標籤後失敗時
標籤是錯誤的,因此請撤回它,而不是繞過它進行修補:
git push --delete origin <tag>
git tag -d <tag>
修復,然後從步驟 5 開始使用相同的版本號重新切割。不要為了跳過失敗而提升版本號:一個從未發布過的版本不值得浪費,而且版本號的間隙會引導人去尋找那個遺失的 Release。
如果標籤已公開一段時間,或存在部分 Release,請告知使用者並讓他們選擇刪除它,還是基於它發布一個修補版本。
9. 報告
PR/比較連結、標籤、Release URL、變更內容以及每個被提升的檔案。如果有任何事項留給使用者處理,請明確指出。