更新依賴
目標是讓專案的依賴保持最新且無已知的安全性漏洞,並以使用者可以信賴的變更形式交付:每個步驟都經過專案自身檢查的驗證,每個有風險的步驟都被隔離,以便可以獨立審查或還原。
依賴更新工作通常以兩種方式失敗。一種是「大爆炸」升級,所有套件一次提升,然後出現問題,沒有人能找出是哪個提升導致的。另一種是「隱藏問題的修復」,例如使用 --force、--legacy-peer-deps、放寬版本範圍或隱藏建議。以下工作流程旨在避免這兩種情況。
0. 決定要求
- 僅檢查(「有任何漏洞嗎?」、「哪些套件過時了?」):執行步驟 1–3,回報,然後停止。不要修改檔案。
- 更新(對於「更新我的依賴」、「修復稽核警告」、「保持套件最新」等要求的預設行為):執行完整的工作流程。
- 單一套件(「將 vite 升級到 v8」):執行步驟 1–2,然後對該套件執行步驟 6(以及任何必須隨之更新的套件),然後執行步驟 7–8。
1. 前置檢查
-
閱讀儲存庫的貢獻者文件(
AGENTS.md、CLAUDE.md、CONTRIBUTING.md)和 CI 配置(.github/workflows/*),以了解驗證命令(lint、typecheck、test、build)和提交訊息慣例。使用儲存庫在 CI 中實際執行的命令。如果文件中沒有說明,則使用 manifest 中定義的 scripts。 -
確保工作目錄是乾淨的。如果不是,請停止並詢問。將使用者未提交的工作混入依賴提交中會造成真正的損害。
-
透過鎖定檔案和 manifest 檢測生態系統(一個儲存庫可以有多個生態系統):
檔案 參考資料 package-lock.json、pnpm-lock.yaml、yarn.lock、bun.lockreferences/node.mdrequirements*.txt、pyproject.toml、uv.lock、poetry.lock、Pipfile.lockreferences/python.mdCargo.toml/Cargo.lockreferences/rust.mdgo.mod/go.sumreferences/go.md其他任何檔案 (Gemfile.lock、composer.lock, …) 使用該生態系統自身的工具,再加上 scripts/osv_check.py進行稽核閱讀對應的參考檔案。它列出了確切的命令和該生態系統的陷阱。
-
找出專案目標的執行環境版本:
.nvmrc、engines、.python-version、requires-python、rust-version、go指令,或是 CI 中的版本。某些套件必須跟著執行環境版本,而不是最新發行版。例如,@types/node應該與專案執行的 Node 主版本相符,將其升級到更新的 Node 主版本會讓程式碼使用在生產環境中會崩潰的 API。將「最新」視為候選版本,而不是目標版本。 -
在更改任何內容之前執行驗證命令,並記下結果。如果某些東西已經失敗,你需要現在就知道。否則,你會將一個原本就存在的失敗歸咎於升級,或者送出一个紅色的分支。如果基準狀態是紅色的,請告知使用者並詢問是否要繼續。
2. 清單盤點
使用生態系統的工具(請參閱參考資料)收集兩個清單:
- 漏洞:原生的稽核工具。如果它沒有安裝且無法短期執行,請使用內建的掃描器:
python3 <skill-dir>/scripts/osv_check.py <lockfile>...(查詢 osv.dev;僅使用標準函式庫)。 同時執行 OSV 掃描和原生工具是便宜的,而且通常值得,因為原生工具有時會遺漏嵌套或重複複製的套件中的建議。 - 過時的套件:目前版本 → 期望版本(在宣告範圍內) → 最新版本。
對於每個漏洞,找出哪個直接依賴引入了有漏洞的套件,以及該直接依賴的哪個版本修復了它。使用者對直接依賴採取行動。僅憑傳遞性套件名稱是不夠的。
3. 計畫與回報
將所有內容分為三個類別,並在進行更改之前以簡短表格顯示計畫:
- 安全性修復:任何消除建議的項目。最緊急。如果修復需要主要版本更新,則屬於主要升級(類別 3),但請在那裡首先列出它,並說明它是安全性驅動的。
- 安全更新:在 semver 範圍內的補丁/次要版本(對於 0.x 套件,次要版本視為破壞性更新)。
- 主要升級:其他所有內容,每個一行。將必須一起移動的套件分組(例如框架及其外掛、工具及其型別定義、共享同儕依賴的套件),並將每個組視為一個單元。
嚴重性對於排序很重要,而可達性對於解釋很重要。在僅用於開發的測試執行器中出現的嚴重建議仍然值得修復,但請直接說明它僅用於開發,而不是嚇到使用者。對於僅檢查的要求,此報告即為可交付成果。包括嚴重性、建議 ID、依賴路徑和修復版本。
4. 分支
從最新的預設分支建立 chore/deps-YYYY-MM-DD(今天的日期),除非使用者已經在為此工作而建立的分支上。
5. 安全性修復與安全更新
- 套用範圍內的安全性修復,重新執行驗證命令,然後提交。
- 一起套用安全的補丁/次要更新,重新執行驗證命令,然後提交。 如果驗證失敗,進行二分法:還原一半的批次並重試,直到找到問題所在,然後將其拉出並像主要升級一樣處理。
讓套件管理器寫入鎖定檔案。永遠不要手動編輯鎖定檔案。
6. 主要升級,一次一個單元
對於每個主要升級單元,按順序(安全性驅動的優先,然後是基礎工具,然後是依賴於它們的工具,例如編譯器先於依賴於它的 linter 外掛):
- 閱讀發生了什麼變化。 為你正在跨越的每個主要版本尋找遷移指南或發行說明(v2→v5 意味著閱讀 v3、v4 和 v5)。好的來源是專案的
CHANGELOG.md、GitHub releases 和文件網站上的「遷移」頁面。提取適用於這個程式碼庫的破壞性變更:搜尋已移除或重新命名的 API 和配置鍵。 - 檢查同儕。 確保新版本滿足其周圍所有套件的同儕和引擎要求。有時最新的主版本還無法採用,因為某個外掛還沒有跟上。然後採用可以運作的最新版本,並記錄阻擋其餘更新的原因。
- 升級並修復。 更新單元,套用遷移(配置重新命名、API 變更、如果專案有提供則使用 codemod),並重新執行驗證命令直到通過。正確修復程式碼。不要削弱測試、新增
skip,或使用any或@ts-ignore風格的跳脫來讓測試變綠。 - 自行提交該單元,並在正文中附上遷移說明。
- 知道何時停止。 如果一個單元需要大量的重構或產品決策(功能已移除、無法驗證的執行行為變更),或者你無法以合理的努力讓它變綠,則乾淨地還原它(
git checkout -- .回到最後一次提交)並記錄它為延遲,附上具體的阻礙因素。一個有明確理由的延遲主要升級是好結果。一個半完成的則不是。
7. 重新驗證與重新稽核
在最終狀態下執行完整的驗證命令並再次進行稽核。每個建議現在都應該被修復或解釋。對於你無法修復的建議:
- 如果它是傳遞性的,且上層套件尚未發布修復,僅在該修復版本與上層套件期望的版本語義版本相容的情況下,使用強制覆蓋/解析來鎖定已修補的傳遞版本是可以接受的。驗證它,並在 PR 中註明,以便日後有人移除它。
- 如果任何地方都不存在已修補的版本,請說明情況,並評估有漏洞的程式碼路徑在此專案中是否可達。
- 永遠不要使用忽略/允許清單機制來讓稽核無聲無息地通過。
8. 交付
提交遵循儲存庫的慣例。如果儲存庫使用 Conventional Commits 且沒有更具體的說明,請使用:
fix(deps): ...用於執行時期依賴的安全性修復build(deps): ...用於升級,以及僅用於開發的工具的安全性修復- 摘要命名套件和目標,例如
build(deps): bump vitest to v5。正文列出已修復的建議和任何已進行的遷移更改。
推送並建立一個 PR(gh pr create),除非使用者另有要求或沒有遠端。使用此正文:
## 總結
<一到兩句話:哪些套件移動了,解決了多少個建議>
## 安全性
| 建議 | 嚴重性 | 套件 (路徑) | 由誰修復 |
|---|---|---|---|
## 更新
| 套件 | 從 | 到 | 類型 | 備註 |
|---|---|---|---|---|
<類型 = security / patch / minor / major;備註 = 所做的遷移更改>
## 延遲
| 套件 | 最新版本 | 為何現在不更新 |
|---|---|---|
<阻擋的同儕、需要重構、執行環境鎖定...;如果沒有則寫「無」>
## 剩餘建議
<無,或每個建議附上原因和可達性評估>
## 驗證
<在最終提交上執行的確切命令及其結果>
最後提供簡短的聊天總結:PR 連結、已修復與剩餘的建議數量,以及延遲的主要升級及其單行原因。