安全的公開 GitHub 發佈
核心規則
將公眾信任置於速度之上。偏好一個乾淨的公開快照、一個 release-branch PR/CI 閘道、經過驗證的 GitHub Release 成品、校驗和,以及對不可逆操作需有明確的維護者批准。
切勿就地從私有歷史發佈。一個最初建立為乾淨公開快照且僅為最終打磨而保持私有的儲存庫,可在最終審查閘道通過後切換為公開。僅在實際需要分發 Python 套件時才使用 PyPI/TestPyPI。
工作流程
- 首先進行檢查:
git status -sb、遠端、目前分支、標籤與相關工作流程。- 使用
gh檢查公開儲存庫的可見性、預設分支、發佈/標籤狀態以及 GitHub 環境。 - 檢查提及安裝路徑、發佈、Homebrew、TestPyPI、PyPI、私有儲存庫、本地路徑或交接檔案的現有說明文件。
- 界定公開範圍:
- 公開說明文件與套件中繼資料可能會提及公開的擁有者/儲存庫、套件名稱、公開標籤與官方安裝指令。
- 除非改寫為公開導向的指引,否則公開說明文件不得提及私有儲存庫名稱、本地 home 路徑、
HANDOFF.md、CONTINUITY.md、.local、.codex、令牌、金鑰、原始內部記錄或僅限維護者的說明。 - 儲存庫內部的說明文件與媒體,優先使用相對 README 連結。在發佈說明中使用絕對的 GitHub 擁有者/儲存庫網址是可以的,但在 README 中使用可能會造成不必要的擁有者耦合以及公開範圍審查檢查中的誤判。
- 準備 GitHub Release 閘道:
- 在進行遠端變更之前,先在本地建置/測試。
- 預設使用
release/vX.Y.Z-> PR -> CI -> 合併的流程,之後再標記。 - 當 README 或在地化的 README 檔案有變更時,需要在合併前取得 Aki 對 GitHub 渲染後 README 的視覺批准。
- 在上傳前驗證 Release 成品:預期的檔名、版本中繼資料、校驗和、壓縮檔內容,以及適用的平台特定簽名/公證檢查。
- 對於 macOS
.appzip 成品,建置壓縮檔時應避免 AppleDouble 或 Finder 中繼資料。偏好使用COPYFILE_DISABLE=1 zip或等效的壓縮指令,並拒絕包含__MACOSX、.DS_Store或._*檔案的壓縮檔。這些額外的檔案可能會導致簽署的應用程式在解壓縮後驗證失敗。 - 始終透過將要上傳的確切壓縮檔解壓縮到臨時目錄、檢查應用程式/套件版本中繼資料,並對解壓縮後的內容執行相關的簽名/公證驗證來進行驗證。
- 按以下順序發佈 GitHub Release:
- 重新擷取
main,驗證最新的 CI 為綠色,並確認本地工作目錄是乾淨的。 - 確認目標標籤和 GitHub Release 尚不存在。
- 在驗證過的
maincommit 上建立標籤。 - 推送該標籤。
- 從最終的上傳檔案建立校驗和檔案,使用適用於發佈的基礎名稱,而非本地路徑(例如
dist/...)。 - 使用有意的說明與僅包含意圖中的成品來建立 GitHub Release。
- 當校驗和對成品類型有用時,上傳校驗和檔案,並在使用者需要驗證下載時將校驗和包含在 Release 說明中。
- 重新擷取
- 在 GitHub 發佈後進行驗證:
- 標籤指向預期的 commit。
- GitHub Release 在預期的網址是可見的。
- Release 成品與校驗和與本地驗證過的檔案相符。
- 將已發佈的 Release 成品下載到臨時目錄,並從下載的副本進行驗證。對於有校驗和支援的壓縮檔,執行
shasum -a 256 -c <asset>.sha256;對於 macOS.appzip 檔案,解壓縮下載的成品並在解壓縮後的應用程式上執行應用程式版本以及codesign --verify --strict --deep檢查。 - 公開的 README/說明文件與實際的發佈版本及成品名稱相符。
可選的 PyPI / TestPyPI 小節
僅在 GitHub release 同時發佈 Python 套件、CLI 或 uvx 安裝路徑時使用本小節。
- 確保
testpypi和pypi的 GitHub 環境存在。 - 將
pypi保持在維護者批准後才能使用。 - 使用
id-token: write與 PyPI Trusted Publishers,而非儲存的 PyPI 令牌。 - 按以下順序發佈:
- 從預期標籤執行的僅建置工作流程。
- 包含
SHA256SUMS與成品簽署證明的 GitHub Release 壓縮檔。 - 發佈到 TestPyPI,然後進行安裝冒煙測試。
- 僅在 TestPyPI 顯示版本與安裝冒煙測試通過後,才發佈到 PyPI。
- 更新說明文件,使實際安裝路徑成為主要選項。
- 發佈後進行驗證:
- PyPI/TestPyPI JSON 包含預期的版本。
uvx --from <package> <command> --help能正常運作。- 準備就緒檢查會阻止重新上傳相同版本。
- 公開的 README/說明文件與實際的安裝路徑相符。
僅限 GitHub 的公開快照核對清單
當目標是沒有 PyPI 的 GitHub 儲存庫或應用程式發佈時使用。
- 在最終閘道通過前保持儲存庫為私有。如果目標是一個已分離的、已公開的儲存庫,切勿將私有歷史複製進去;僅移動經過審查的快照內容。
- 預設使用 release branch 和 PR 來進行公開快照:
- 在公開儲存庫中建立或更新
release/vX.Y.Z。 - 在該分支上提交準備好的快照與公開說明文件。
- 開一個指向
main的 PR 並讓 CI 在那裡執行。 - 當 README 或與發佈相關的說明文件變更時,也使用
readme-release-sync,使公開副本保持可讀、雙語且與發佈的行為一致。 - 提供 Aki 該 PR/分支的 GitHub 渲染 README 連結,並在合併前等待明確的視覺批准。本地 Markdown 預覽、原始檔案檢視、螢幕截圖或自動化連結檢查不能取代此閘道。
- 僅在 CI 為綠色且公開範圍審查通過後才合併。
- 僅在 Aki 明確要求跳過 PR 閘道時,才直接推送到
main。
- 在公開儲存庫中建立或更新
- 審查被追蹤的檔案,而不僅僅是工作目錄:
- 秘密、本地路徑、維護者名稱、舊的私有儲存庫名稱、交接檔案、產生的壓縮檔與過時的產品名稱。
- 使用
git ls-files檢查是否有意外的.mov、.mp4、建置輸出、快取與大型二進位檔案。
- 將 README 媒體視為公開範圍的一部分:
- 內嵌 GIF 是 GitHub README 最可靠的示範格式;請保持其在 GitHub 實際的圖片/GIF 限制以下。
- 本地 README 的
<video>嵌入與儲存庫內部的 MP4 預覽連結可能會失敗,或開啟不穩定的 GitHub 檔案預覽頁面。 - 僅在真正需要時,將可選的 MP4 示範放在 GitHub Release 成品中,並在產品不需要時將它們以及發佈說明中的引用移除。
- 在建立標籤或 GitHub Release 之前,重新擷取:
- PR 合併狀態與合併後
main上的最新 CI 狀態 git status -sb、git log --oneline -1、git tag --list與目前的 Release 成品- 當儲存庫已為公開時,透過 GitHub contents API 檢查 README 與引用的圖片路徑
- 每個二進位上傳的壓縮檔內容與解壓縮成品驗證結果
- PR 合併狀態與合併後
- 在切換可見性之前,重新擷取:
gh repo view ... --json visibility,isPrivate,url,defaultBranchRef- 預設分支的最新 Actions 狀態
- Release 成品與發佈說明
- 透過 GitHub contents API 檢查 README 與引用的圖片路徑
- 僅在 Aki 明確指示要發佈後才切換可見性,然後驗證
visibility=PUBLIC、公開 README 網址的可訪問性、Release 成品以及乾淨的本地工作目錄。
從乾淨快照到公開儲存庫流程
當私有/內部儲存庫為一個分離的公開儲存庫提供內容時使用此流程。
- 在私有儲存庫中完成並驗證功能。
- 從允許的公開檔案列表中建立一個臨時快照資料夾。
- 在快照中應用公開命名並移除僅限私有的內容。
- 審查快照中的秘密、私有路徑、交接檔案、舊產品名稱、產生的壓縮檔與意外的大型檔案。
- 將審查過的快照複製到公開儲存庫的工作目錄中。
- 建立或切換到
release/vX.Y.Z。 - 在該分支上更新公開 README、在地化 README、變更日誌、版本中繼資料、發佈草稿說明與 README 媒體引用。
- 檢查
git diff、README 媒體連結、雙語一致性、過時的措辭以及公開範圍掃描結果。 - 執行相關的本地建置/測試/套件檢查。
- 在
release/vX.Y.Z上提交發佈準備的變更並推送該分支。 - 開一個指向
main的 PR;還不要標記或建立 GitHub Release。 - 等待 CI,在 release branch 上修復失敗,並重新執行檢查。
- 提供 Aki 該 PR/分支上
README.md與任何在地化 README 檔案的 GitHub 渲染 README 網址。等待明確的視覺批准,確認 README 在 GitHub 上看起來是正確的。 - 僅在 CI、審查閘道以及 Aki 的 GitHub 渲染 README 視覺批准全部通過後,才合併 PR。
- 重新擷取
main並驗證合併後的 commit 是乾淨且綠色的。 - 在建立
vX.Y.Z、上傳成品或建立/發佈 GitHub Release 之前,停下來等待 Aki 批准。
尚無公開儲存庫流程
當專案從未有過公開儲存庫時使用此路徑。
- 不要將私有/內部的工作儲存庫切換為公開。
- 建立一個新的乾淨發佈儲存庫,並先保持其私有。
- 僅從審查過的公開快照初始化它,而不是從私有歷史。
- 從一開始就使用面向公眾的產品名稱、套件中繼資料、README 檔案、授權、變更日誌、發佈草稿說明、螢幕截圖/GIF 與工作流程。
- 對被追蹤的檔案執行相同的秘密/私有路徑/大型檔案/公開範圍審查。
- 即使儲存庫仍然是私有的,也使用
release/vX.Y.Z-> PR -> CI -> Aki 的 GitHub 渲染 README 視覺批准 -> 合併的流程。 - 如果可能,在儲存庫仍為私有時建立預期的標籤與 GitHub Release 草稿,並在那裡驗證成品/校驗和。
- 在變更可見性之前,停下來等待 Aki 批准。
- 僅在最終審查通過、CI 綠色、有意的 Release 成品以及 Aki 明確批准後,才將可見性切換為公開。
- 在可見性切換後,重新擷取並驗證公開 README 的可訪問性、Release 成品、標籤、最新的 Actions 狀態以及本地乾淨的工作目錄。
重要經驗教訓
- GitHub Actions 工作流程定義取自於用來觸發工作流程的 ref。如果在新增工作流程輸入或防護之前就已建立標籤,從該舊標籤觸發將不會知道新的輸入。
- 未來的發佈應在建立標籤之前加入發佈防護。然後從該標籤使用受保護的輸入進行觸發。
- 如果一個一次性的歷史標籤早於防護機制,請不要猜測。執行本地準備就緒檢查,先驗證 TestPyPI,驗證成品/簽署證明,並記錄此例外情況。
- Release 成品衛生很重要:在探索性上傳之後,最終的公開 Release 應僅包含意圖中的成品。刪除不需要的成品,並在發佈前移除它們的 SHA 行或發佈說明中的提及。
詳細操作手冊
請閱讀 references/pypi-trusted-release-runbook.md 以取得逐步核對清單、指令模式以及公開說明文件稽核模式。