跟上新版
導入是一天的事,跟上是每個月的事。 這一頁回答導入之後的三個問題: 怎麼知道上游動了、怎麼判斷要不要跟、決定要跟之後每一層各怎麼跟。 (導入本身見導入三階段;版號的定義見版本策略。)
訊號從哪來
上游進版時會發出的訊號,依層整理如下——挑一條訂閱,不要靠想起來:
| 通道 | 涵蓋 | 怎麼訂 |
|---|---|---|
GitHub Release(vX.Y.Z) | 規範版:元件與 token 有取用端可感知的變更;notes 末尾列出這一版動到的 registry item | repo 頁 Watch → Custom → Releases,有新版就收到通知 |
| CHANGELOG | 每次進版一則,含「我需要做什麼」 | 判斷時讀;不適合當提醒機制 |
npm outdated @dooping/tokens | token 層 | 放進宿主 CI 例行跑 |
dooping-check | 元件層:你抄過的 item(含相依)上游動了沒、本地改了沒 | 下面的「例行檢查」,放進宿主 CI |
/r/index.json | 機器可讀正本:規範 version、配對的 tokensVersion、每個 item 的指紋 meta.closureHash | 自己寫檢查時讀 |
四個刻意設計要知道:
- 純文件進版不打 tag、不發 Release。 版號沒動=取用端不需要任何動作, 所以你不會收到通知——安靜本身就是訊號。
- 規範版與 token 版是配對的:每版規範恰好宣告一個 tokens 版,
正本在
/r/index.json的tokensVersion(見版本策略的配對模型)。 - Release 的 item 清單分四類:內容有變、只因相依變了而受影響、新增、移除。
最容易漏的是「只因相依受影響」——修正落在
utils時,抄了data-table的人一樣要重抄。 - 訊號只來自核准版(
main)。 上游每一版先進/staging/做套用驗收,核准後才進正式站、才發 Release。 想提前試下一版,可以在自己的分支從/staging/r/裝來回報問題;正式導入與例行檢查一律對/r/(流程見版本策略「三段式發布」)。
例行檢查:dooping-check
提醒用,不擋建置——升級是決定,不是 CI 事件。一次性準備:
npx shadcn@latest add https://kielchang.github.io/dooping-design-book/r/dooping-check.json # 裝進 scripts/dooping-check.mjs
node scripts/dooping-check.mjs init data-table page-header # 列出你「主動」裝過的 item,相依會自動算進去
之後每週在宿主 CI 跑一次:
node scripts/dooping-check.mjs
每個 item 回報三種狀態之一:
| 狀態 | 意思 | 下一步 |
|---|---|---|
| 已是最新 | 上游的指紋與 lock 相同,本地檔案也沒改 | 不必動 |
| 上游有更新 | 這個 item 或它的相依在上游變了 | 照輸出的指令先 --dry-run --diff 看差異;決定跟進就重抄,再 update <item> |
| 本地改過 | 本地檔案的內容與 lock 記的不同 | 刻意偏離就記進符合性台帳;不是刻意的,重抄即可 |
dooping-check 本身也是 registry item:它有更新時,檢查會把它列成「上游有更新」。
想讓提醒擋 PR 就加 --strict(不建議當預設)。
指紋怎麼算
/r/index.json 每個 item 的 meta 有兩個指紋,dooping-check 與 Release 的異動清單都靠它們:
hash:取用端抄走的東西。依序串接下列各行(以換行分隔),取 SHA-256 的前 16 個十六進位字元:name <名稱>、type <型別>、deps <npm 相依,排序後以空白串接>、registryDeps <registry 相依的 item 名稱,排序後以空白串接>, 然後每個檔依target排序各一段file <target> <type> <內容長度>接內容。排序照字元碼位。 不含版號、標題、說明與 URL——預覽站與正式站的同一份內容必須同一個指紋,只改說明文字也不該叫你重抄。closureHash:自己加上所有遞移 registry 相依的名稱,排序後每行<名稱>:<hash>,再取 SHA-256 前 16 字元。 任何一個相依變了它就變:utils修了,data-table的closureHash跟著變、hash不變。這就是「只因相依受影響」那一類的來源。
這套串接規則是對外契約:改了等於讓所有取用端的 lock 一次失效。實作在 scripts/lib/fingerprint.mjs,
tests/registry-fingerprint.test.ts 用獨立寫的第二份實作逐 item 對過;dooping-check 內另有第三份,tests/dooping-check.test.ts 確認一致。
裝不了腳本的專案,退而求其次比對規範版號——粒度只到「規範動了」,看不出動到哪些 item:
UPSTREAM=$(curl -s https://kielchang.github.io/dooping-design-book/r/index.json | jq -r .version)
RECORDED="0.10.0" # 你的台帳記錄的上游版本,跟著台帳一起改
if [ "$UPSTREAM" != "$RECORDED" ]; then
echo "::warning::上游規範已到 v${UPSTREAM}(台帳記 v${RECORDED})——讀該版 CHANGELOG 決定要不要跟。"
fi
判斷要不要跟
收到訊號之後的流程只有一條,而它的每一步都在縮小要評估的範圍:
- 讀該版 CHANGELOG 的「我需要做什麼」。 每一則都會明說,不需要就寫不需要—— 寫不需要就結束,這是最常見的出口。
- 對照符合性台帳。 只有標「遵循」的列需要評估; 「自製」不受影響,「刻意偏離」只需重讀一次原因(上游這版說不定正好解掉了偏離的理由)。
- 衡量差距與影響再決定。 版號差距是線索:大版差=有會壞的變更、中版差=有新能力、 小版差=修正微調。但差距只回答「上游改了多大」,不回答「你要不要跟」——判準是影響:
| 情況 | 決定 |
|---|---|
| 上游修了你也踩到的問題 | 跟進,這是唯一「必須同步」的情況 |
| 有用的新能力,但這期排不進 | 記台帳、延後——在台帳的上游版本欄記下差距 |
| 與本地的刻意做法衝突 | 偏離,在台帳寫下原因(沒寫原因的偏離是待修項目) |
把「上游 bump」當成一個要排程的決定:逐列評估、更新台帳、 在自己的 CHANGELOG 記一則。
不要 npm update 之後看 CI 綠就當作升級完成。
設計系統的變更改的是已上線畫面的外觀與行為,CI 不看外觀。
決定跟進之後:每層的程序
| 層 | 程序 |
|---|---|
| token | npm update @dooping/tokens(^ 範圍內)。跨大版先讀 CHANGELOG;0.x 期間任何版本都可能 breaking,語意色名稱已穩定、值仍可能調 |
| 元件 | 重跑同名 npx shadcn@latest add <URL>(覆寫 components/dooping/ 同名檔,相依會一起覆寫;先加 --dry-run --diff 看會動到哪些檔)→ 用 git diff 逐檔審查——落點固定就是為了這一刻 diff 是乾淨的 → node scripts/dooping-check.mjs update <item> → 台帳該列更新上游版本 |
| 模式/頁面 | 重讀該則文件,對照本地實作;有差就記台帳(遵循/刻意偏離) |
元件更新走「重抄+diff 審查」,把上游的改動看過一遍再合進去; 本地改過的部分會在 diff 裡現形,逐塊決定保留或讓位。
不要手動把上游的改動「挑幾行抄過來」。半套的同步讓台帳那一列 既不是遵循也不是偏離,下一次升級就沒有人敢動它了。
開發中的節奏
跟上游的關係不是升級當天才存在。日常開發的四個習慣,讓每次升級都是小事:
- 新畫面動工前先查上游:到頁面章選頁型、查元件章有沒有現成件。 最貴的漂移是「上游有,但我們自己又做了一個」。
- 缺件不長私有版:先用缺件表的替代方案,同時把缺口回饋上游—— 每一則都是三次法則的一次證據,由守門人在上游串起來,過了就是大家的元件。 入口與格式(取用端 agent 送 PMIS、人走 GitHub 表單)見回饋與 RFC 流程。
- 節奏掛在里程碑上:sprint 或版本邊界檢查一次訊號就夠,不必每天。 衝刺中收到新版訊號,記台帳延後即可——衝刺中途不升大版。
- 台帳與離線摘要跟著改: 台帳記「跟上游的關係」,摘要記「最常用的判斷依據」,兩者過期比沒有更糟。
把「檢查上游」寫進里程碑的定型工作(跟「更新相依」同一個時段做)。
不要等到「畫面看起來怪怪的」才發現落後了四個版。中間每一版的 CHANGELOG 都在講你要不要動作,一次補讀四版的成本遠高於四次各讀一版。