工作流程規範Changeset-based 5-phase 工作流

shibamimi-tech 組織下所有 repo 的標準開發、審查、測試、發佈流程,無例外。每次 PR 都要附真 changeset。Phase 1 → Phase 4 全自動,Phase 5 production deploy 為唯一人工 gate。

最後更新
閱讀時間
約 39 分鐘
作者
柴米科技有限公司
版本
v1.3.3

先讀「樹狀編排方法論」再讀本文。 本文是 出貨流程(GitHub 端:把做完的成果檢驗、合併、部署)。「怎麼把一個大任務拆解、調度、執行完成」屬於 做事流程,請先讀 樹狀編排方法論。兩者在 push 這個接縫交接:樹(本地把事做好)→ push → 果園(本文的 5-phase 檢驗出貨)。一棵樹 = 一條 feature 分支 = 一個 PR = 一張 changeset。

⚠️ 注意事項(閱讀前必看)

下列 5 條是當前版本最容易踩坑、與直覺不同的鐵則,建議讀完本文件前先過一次。

  1. Phase 1 → 4 全鏈自動化,Phase 5 為唯一人工 gate dev push 含 changeset 後一路自動推進到 main + version bump,發佈負責人只在 Phase 5Run workflow 觸發 production deploy。不要在 PR 上等簽核。

  2. 品質責任前移到 feature/ 階段* preview→main 無人工 approve gate,因此每一筆 push 進 feature/* 都要當作 ready-to-ship 對待 — 本地測試完成、changeset 描述正確才推。Phase 3 的 staging deploy 主要供「上線後 sanity check + rollback 備援」,不是 ship 前把關。

  3. preview→main merge 一律讓 App 處理,不要管理員手動跑 gh pr merge 管理員權限會 bypass preview ruleset deletion 保護,連帶被 delete_branch_on_merge=true 殺掉 preview 分支。若 PR 卡住需手動推一把,用 gh api repos/{owner}/{repo}/pulls/{n}/merge -X PUT -f merge_method=merge -F delete_branch=false 顯式禁刪 head。

  4. caller workflow 必須宣告完整 per-job permissions: block reusable workflow 在 job 層宣告的所有 scope 都要在 caller 一字不漏列出,少一項即 startup_failure。詳見 Repo 建置與設定 — Caller permissions 對齊絕對不要透過把 default_workflow_permissions 翻成 write 來繞過 — 是反模式,下一次跑 init-repo 就會被打回原形。

  5. .github.claude 走簡化流程,其餘 repo 一律完整 5-phase 只有 .github.claude 兩個 repo 允許 admin 直推 main(無 required check、admin always bypass):前者是 reusable workflow 的家、自我引用會循環,後者是 agents / skills 集中地、不發版且僅供組織內使用。除此之外其他 repo 都必須走完整 5-phase(feature/* → preview → main + changeset + 自動鏈)。新案子不要再為「就改一行」「純設定」「只是 submodule bump」之類理由請求例外。

一、概覽

本文件定義 shibamimi-tech 組織所有 repo 的標準開發、審查、測試、發佈流程。除 .github.claude 兩個 repo 走簡化流程外,其餘一律走完整 5-phase,每次 PR 都附一張真 changeset。

Repo 分類:

Repo 名稱 專案 / 產品名稱 專案敘述 完整 5-phase 工作流
.github 組織共用 workflows reusable workflows + composite actions 集中地,被所有 repo 引用
.claude Agents / Skills 集中地 Claude 各種 agents、skills、rules;以 git submodule 形式被各 repo 引用
packages 共用套件 monorepo @shibamimi-tech/ui、content、config、cli 等共用套件集中地(GitHub Packages npm publish)
corp-site 公司官方網站 柴米科技公司對外網站
docs 內部文件中心 組織內部文件、規範、知識庫;部署到 Cloudflare Pages(Zero Trust 保護)
investor-relations 投資人關係網站 對投資人揭露公司資訊
susumie 速速咩 待確認
youyan 有言 待確認
animations Remotion 動畫庫 待確認
site-exporter 網站匯出工具 待確認
app-template 新 repo 派生模板 「Use this template」一鍵派生新 app repo,內含完整 caller workflows + changeset infra

簡化流程的兩個例外.github 是 reusable workflow 的家、self-host changeset gate 會自我引用循環;.claude 不發版、不部署、僅供組織內使用。兩者 main ruleset 都配置 admin always bypass、無 required check,可直接 commit + push main。除此之外不再開設其他例外,新案子若覺得「就改一行」「純設定」想跳流程,請走完整 5-phase。

派生新 repo:用 shibamimi-tech/app-template 的「Use this template」按鈕一鍵派生(含完整 caller workflows + changeset infra),然後跑 pnpm dlx @shibamimi-tech/cli init-repo 套組織標準設定 + 建 preview 分支。詳見 app-template 的 TEMPLATE_USAGE.md

核心設計目標(完整 5-phase repo):

  • 全鏈自動化:Phase 1 → Phase 4 完全無需人工干預(含 preview 合 main、Version PR merge);Phase 5 production deploy 為唯一人工 gate
  • 兩段式部署:功能先以 staging deploy 部署到 preview 環境(post-hoc 驗證 + rollback 備援),最後由人工觸發 production deploy 上 main
  • Changesets 驅動版本:每個功能貢獻者自行描述變更意圖,CI 自動彙整 CHANGELOG 並升版
  • changeset 即契約:沒有 changeset 變動就連 PR 都不會被建立/更新;要 ship 必須補便利貼
  • 一律走 feature 分支:完整 5-phase repo 的任何變更(含 hotfix、CI 設定、docs)都必須從 feature/* / fix/* / release/* 分支 → PR → preview → main;禁止直推 mainpreview,連 admin 也不行(簡化流程的 .github.claude 不在此限)
  • 品質把關前移到 feature 分支:因 preview→main 不再有人工 approve gate,請在 feature/* 階段完成本地測試與 review,push 前確認 ready-to-ship

二、鐵則:分支線性不變式(完整 5-phase repo)

適用範圍: 所有完整 5-phase repo(packages、corp-site、investor-relations、animations、site-exporter、susumie、youyan、app-template、docs 等)。.github.claude 走簡化流程、無 preview 分支,本鐵則不適用。

preview 永遠是 main 的祖先

v2.1 起這個不變式由 git 機制保證,不再只是約定:preview→main 用 merge commit,main 的 merge commit 直接把 preview HEAD 列為 parent → git merge-base --is-ancestor preview main 永遠成立。

不變式靠這三件事維護:

  1. 所有 commit 都從 feature 分支進 preview(feature → preview 用 squash,保持 preview 歷史線性乾淨)
  2. preview → main 用 merge commit(不是 squash),由 open-preview-to-main-pr.yml 強制
  3. 禁止直推 main / preview(連 admin、hotfix、revert 也不行)

違反第 3 條的後果: 直推 main 會讓 main 走在 preview 前面 → preview 不再是 main 祖先 → 下次 preview→main merge 可能撞 modify/delete 衝突 → 需要手動解,留下技術債。

實際案例(2026-05-23):shibamimi-tech/packages 曾有兩筆 fix(ci) → revert(ci) 對沖 commit 直推 main,雖然淨內容變化 = 0,但 git 看到歷史動過檔案,下次 preview→main 仍觸發 modify/delete 衝突。淨變化為零也算違規

為何不用 squash(v2.0 的設計問題): squash merge 在 main 上建立的是「跟 preview 內容相同但 SHA 不同」的 commit,preview 跟 main 立刻變成 SHA 歧異的兄弟分支(內容等價,git 看不出來)。下一輪 feature → preview 會撞 add/add conflict。v2.0 靠「砍掉重建 preview」掩蓋,v2.1 直接從機制上避開。

三、分支模型

1. 分支清單

分支 用途 保護狀態 誰可以推
main 生產版本,對外穩定 受保護:禁止 force push、必須走 PR;允許 merge commit(preview→main 專用);無 required approvals、無 required status checks(App 啟用 auto-merge 後立即 fire) 只有 open-preview-to-main-pr 產生的 PR + changesets Version PR 可合入;任何人類直推都違規
preview Staging 環境,QA 測試平台 受保護:禁止 force push、禁止 merge commit、必須走 PR 只有 feature/fix/release 的 PR 可合入;不接受直推
feature/<name> 新功能開發 不保護 開發者自由 push
fix/<name> 缺陷修復(含 hotfix、CI 設定、docs) 不保護 開發者自由 push
release/<name> 冷啟動、手動 version PR 等特殊用途 不保護 開發者自由 push

admin 也不能直推 — 完整 5-phase repo 即使 GitHub 允許 bypass,也不該用。每次直推都是技術債,未來會以衝突的形式還回來。詳見「鐵則:分支線性不變式」段落。(簡化流程的 .github.claude 是設計上的例外,admin 直推 main 是正常路徑。)

Branch protection 細部設定:preview 與 main 的 required checks、approvals、allowed merge methods 等實際 GitHub UI 對照,集中在 Repo 建置與設定 — Step 4 → 1 Branch protection。新 repo 由 owner 透過 org-level ruleset 自動套用,一般開發人員不需手動設定。

新 repo 建置:建置 SOP(GitHub App 設置、@shibamimi-tech/cli init-repo、Cloudflare 設定、冷啟動 v0.1.0)見 Repo 建置與設定,僅 owner 執行。

四、Actions / App 關係圖

1. Caller workflow 用途速查

整套流程中每個 repo 的 .github/workflows/ 通常會有以下 caller workflow(檔名為慣例、實際以各 repo 為準)。每支 caller 內部都 uses: 一支組織 reusable workflow(見 第七節)。

Caller workflow Phase 觸發 用途
ci.yml 2 feature/fix/release 分支 push 跑 typecheck / lint / test / build,提供 ci / ci required status check
feature-pipeline.yml 2 同上 驗證 changeset 便利貼(gate),通過後對 preview 開 / 更新 PR + 啟 auto-merge by App,提供 check-changeset / gate required status check
deploy-preview.yml 3 preview 分支 push 部署 staging 到 Cloudflare Pages preview environment,同 job 內 inline 開 preview → main PR + 啟 auto-merge by App
release.yml 4 main 分支 push changesets/action 偵測 .changeset/*.md → 開 Version PR(版號 bump + CHANGELOG)+ 啟 auto-merge by App
deploy-production.yml 5 workflow_dispatch(人工) App only:手動觸發 production deploy(CF Pages main + 可選 custom domain / DNS / redirect + 自動清理舊 deployments)
publish.yml 5 workflow_dispatch(人工) Lib only:手動觸發 pnpm changeset publish → npm publish + git tag
apply-access-config.yml main push 動到 .github/access-config.json 內部 repo 限定(如 docs):把 Zero Trust Access Application + Policy 同步到 Cloudflare

設計慣例:caller workflow 主要負責「觸發條件」+「傳 inputs」+「對齊 permissions」三件事,實際邏輯都在組織 reusable workflow 內。新 repo 從 app-template 派生時這 6 支 caller 預先擺好;後續想拆獨立 / 換 reusable 都只需改 caller 內的 uses:

2. Phase × Token × Push actor 關係圖

下圖把上表的「誰觸發誰」「用什麼 token」「會不會觸發下游 workflow」按 Phase 攤平成時序圖。每個 Note over 標出階段邊界、par/and 顯示並行追蹤(Phase 2 兩條 track)、自指箭頭表示 GitHub 平台內部動作(auto-merge svc fire / merge commit 寫入分支):

圖例:

  • GITHUB_TOKEN — 受 GitHub「不觸發下游 workflow」限制,所以 Phase 2 Track A 的 CI、Phase 5 的 deploy / publish 都用它(不需觸發下游)
  • App tokenshibamimi-tech-workflow installation token,突破 GITHUB_TOKEN 限制,整鏈 auto-merge / open PR 都用它(fire 後的 push 要觸發下游)
  • 🟢 — auto-merge svc fire 完,merge push 的 actor 是 App-bot → 觸發下游 workflow,整條鏈通了
  • par/and — Phase 2 內 Track A(CI)與 Track B(feature-pipeline)並行跑,兩個 required status check 都綠才放行 auto-merge

3. App 在整套流程做了什麼

shibamimi-tech-workflow App 接管「PR 自動開立 + auto-merge 啟用 + auto-merge fire」這條鏈,從 feature push 一路全自動推進到 main(含 version bump),Phase 3、Phase 4 都無人工 approve gate;Phase 5 production deploy 才回到人類觸發。

具體 3 個動作:

  1. Phase 2:透過 feature-pipeline-combined.yml(合併版,由 caller feature-pipeline.yml 引用)在單一 job 內依序跑 check-changeset → open/update feature→preview PR → enable auto-merge by App
  2. Phase 3:透過 deploy-staging-cf.ymlopen-pr: true(caller deploy-preview.yml 引用)在 staging deploy 完成後同一 job 內 inline 開 preview→main PR + enable auto-merge by App;不想合併的 repo 仍可獨立呼叫 open-preview-to-main-pr.yml
  3. Phase 4:透過 release-app.yml / release-lib.yml 由 changesets/action 開 Version PR、enable auto-merge、執行 fire merge

不做的事:CI checks(用 GITHUB_TOKEN 即可)、deploy/publish(人類觸發)。

單 job 合併版的設計理由:原本 check-changeset.yml + open-feature-to-preview-pr.yml 是兩支 reusable workflow,需要兩次 checkout 與兩次 billable minute;Phase 3 的 deploy-staging-cf.yml + open-preview-to-main-pr.yml 同理。合併後省下重複的 setup 成本,舊版的拆分 reusable 仍保留作為 fallback / 特殊用途。

五、五階段流程

1. Phase 1:Feature 開發(執行者:開發人員)

觸發: 收到新功能需求或 Bug 報告

動作:

  1. 從最新 main 切出分支:

    git checkout main && git pull
    git checkout -b feature/<>     # 或 fix/<簡述>、release/<簡述>
  2. 自由撰寫程式碼,過程中可以多次 git commit + git push

  3. 每次 push 自動發生(兩條 track 並行)

    • Track A:ci-node — 跑 typecheck / lint / test
    • Track B:feature-pipelinecheck-changeset job 驗證「本次 push 動到 .changeset/*.md「HEAD 存在有效便利貼」;通過後接著跑 open-pr job:
      • 若分支對 preview 還沒 PR → 自動建立 + 啟用 auto-merge
      • 若已存在 PR → re-enable auto-merge(idempotent)
    • changeset 沒動 → check-changeset 紅燈 → open-pr 直接 skip → 沒有 PR 被建立/更新
  4. 功能完成時,執行:

    pnpm changeset

    互動式選 package + bump 類型(patch/minor/major)+ 寫變更說明,產出 .changeset/<random>.md

  5. push changeset:

    git add . && git commit -m "add changeset"
    git push
  6. 此次 push 觸發 check-changeset gate(偵測到 push 範圍動到 .changeset/*.md 且 HEAD 存在便利貼)→ 通過後 open-pr 自動建立/更新對 preview 的 PR 並啟用 auto-merge

核心心智模型(5 個 push 情境)

情境 ci check-changeset open-pr auto-merge
1. code only(首次) 🔴 Check 1 失敗 ⏭️ skipped N/A(無 PR)
2. code + changeset(首次) 🟢 建 PR + 啟 auto-merge 兩 check 都綠 → merge
3. code only(PR 已存在)— 純備份 🔴 Check 1 失敗 ⏭️ skipped 🔴 卡住(required check 未通過)
4. code + 編輯 changeset 🟢 re-enable auto-merge 兩 check 都綠 → merge
5. 刪光 changeset 🔴 Check 2 失敗 ⏭️ skipped 🔴 卡住

設計取捨:允許 dev 純 code push 做雲端備份(不會建立無意義 PR、不會觸發審查),只是 auto-merge 卡住直到補 changeset。要 ship 必須補便利貼 — changeset 即契約

2. Phase 2:CI + App Auto-merge to preview(執行者:CI + App)

觸發: 開發者 push 到 feature/fix/release 分支(每次 push 都觸發)

自動化動作(兩條 track 並行):

feature/* push

   ├─► Track A: ci.yml (uses ci-node.yml)              ─┐
   │   (typecheck / lint / test / build)                 │
   │                                                      ├─► 兩 required status check 都綠
   └─► Track B: feature-pipeline.yml                   ─┘    → App auto-merge fire (App identity)
        ├─ check-changeset job                                     → 🟢 squash merge → preview
        │    Check 1: 本次 push diff 含 .changeset/*.md
        │    Check 2: HEAD 仍存在 .changeset/*.md
        │    AND 任一失敗 → 紅燈

        └─ open-pr job (needs: check-changeset)
             ├─ App 取得 installation token
             ├─ PR 不存在 → 建立 + 啟 auto-merge by App
             └─ PR 已存在 → re-enable auto-merge by App
  1. ci-node workflow 跑 typecheck / lint / test → 提供 ci / ci status check
  2. feature-pipeline caller 依序跑兩個 reusable job:
    • check-changeset(純 git + bash,數秒完成)
      • Check 1:本次 push 範圍(${before}..${after})內是否動到 .changeset/*.md(排除 README.md
      • Check 2:HEAD 是否實際存在有效 changeset 便利貼(防「刪光 changeset」繞過)
      • 兩個 Check AND 邏輯;任一失敗則 open-pr 整個 skip
    • open-pr(needs: check-changeset)
      • shibamimi-tech-workflow App token 取代 GITHUB_TOKEN(重點!)
      • preview 的開啟 PR 不存在 → gh pr create 建立 + gh pr merge --auto 啟 auto-merge by App
      • 已存在 → 跳過建立,re-enable auto-merge by App(idempotent)
  3. GitHub auto-merge 機制偵測:required status check 都通過 → App 自動 merge 到 preview(mergedBy 是 App-bot,不是 GITHUB_TOKEN bot);任一未通過 → 等待
  4. merge 完成後:
    • feature 分支自動刪除
    • App push 到 preview 觸發下游deploy-preview.ymlopen-preview-to-main-pr.yml 自動跑

為什麼用 App 而不用 GITHUB_TOKEN?

GitHub 規則:GITHUB_TOKEN 觸發的 push 不會觸發其他 workflow(防 infinite loop)。若用 GITHUB_TOKEN 啟用 auto-merge → fire 後 push to preview → 下游 deploy-previewopen-preview-to-main-pr 都不會跑

App installation token 不受此限。詳見「Actions / App 關係圖」。

為什麼 code-only push 不會建立無意義 PR?

open-prneeds: check-changeset。changeset gate 紅燈 → open-pr 被 GitHub Actions 自動 skip,連 gh pr create 都不會跑。

→ dev 可以盡情 push 純 code 做雲端備份,不會在 PR 列表造成噪音、不會浪費 reviewer 注意力。要 ship 才補 changeset。

3. Phase 3:Staging 部署 + 自動合 main(全自動)

觸發: preview 分支收到 App push(從 Phase 2 自動鏈接過來)

自動化動作(單一 job 內依序完成):

caller deploy-preview.yml 引用 deploy-staging-cf.yml 並傳 open-pr: true,整支 reusable workflow 在同一個 job 內依序做兩件事:

  1. Composite action cf-pages-deploy → 部署到 Cloudflare Pages preview environment
    • 完成後產出 preview URL(如 preview.<project>.pages.dev
    • 通常 30-60 秒
    • caller 端不需要 scripts/deploy.sh、不需 wrangler devDep、不需 CF 知識
  2. Inline open-preview-to-main-pr 邏輯 → 用 App token 建立 / 更新 preview → main PR + enable auto-merge by App
    • idempotent:重複 push 不會建立多個 PR
    • merge method 用 merge commit(非 squash),讓 preview HEAD 物理上是 main 的祖先
    • main ruleset approvals=0、無 required status checks → App enable auto-merge 後立即 fire merge commit 進 main
    • 不想合併的 repo(lib,不需 staging deploy)可改用獨立 reusable open-preview-to-main-pr.yml

人工動作:

  • 。Phase 3 全自動。
  • QA / PM 可在 Cloudflare preview URL 做 post-hoc sanity check + 回歸測試;若發現問題,到 Phase 5 之前都還有機會 hold 住 production deploy,或事後用 CF Pages rollback / 推 revert fix。

預期產出: 功能合入 main,main 上產生一個 merge commit,preview 的 HEAD 是這個 merge commit 的 parent → preview 物理上就是 main 的祖先(鐵則由 git 機制保證)

Lib 專案 staging 部署可省略: Library 不需 deploy preview,但 open-preview-to-main-pr 仍會跑開 PR + 自動 merge。

品質把關前移: 因 preview→main 無人工 gate,品質責任前移到 feature/* 階段(push 前完成本地測試 / 自我 review)。Phase 3 的 staging deploy 主要供 post-hoc 驗證與 Phase 5 deploy production 前的 sanity check。

⚠️ 不要管理員身分手動跑 gh pr merge X --merge --auto 觸發 preview→main PR:管理員權限會 bypass preview ruleset 16747883 的 deletion 保護,連帶被 delete_branch_on_merge=true 殺掉 preview 分支。preview→main merge 一律讓 reusable workflow 內由 App token 跑(會 respect ruleset)。若 PR 卡住需手動推一把,用 gh api repos/{owner}/{repo}/pulls/{n}/merge -X PUT -f merge_method=merge -F delete_branch=false 顯式禁止刪 head。

4. Phase 4:Version Bump(執行者:App + changesets/action)

觸發: Phase 3 結束、main 收到 App push

自動化動作:

  1. release.yml 觸發 → 跑 changesets/action(用 App token)
  2. 偵測 .changeset/*.md 存在 → 自動開啟「chore: version packages」PR (head = changeset-release/main):
    • 套用所有 pending changeset → 升版號(package.json)
    • 統整 CHANGELOG.md
    • 刪除已使用的 .changeset/*.md 便利貼
  3. 立即用 App token enable auto-merge on Version PR(--squash --auto
  4. main approvals=0、無 required status checks → Version PR 立即 squash merge 進 main

人工動作:

  • 。Phase 4 全自動。

預期產出:

  • Version PR auto-merge 後 main 上會有一個 chore: version packages squash commit
  • main 上有正確的 version + CHANGELOG,所有 .changeset/*.md 已被清空
  • 此時 main 已準備好可發版,但 production deploy 不會自動跑(Phase 5 是人工觸發點)

設計理由: Phase 4 與 Phase 5 拆開,讓你決定何時 deploy production(例如等所有 stakeholders 都看過 staging 再正式上線)。Version bump 是純自動化的記帳動作,與 production deploy 是兩件事。

5. Phase 5:Manual Production Deploy(執行者:發佈負責人)

觸發: 發佈負責人到 GitHub UI 點 Run workflow 觸發 deploy-production.yml(app)或 publish.yml(lib),或本地跑 gh workflow run deploy-production.yml --ref main

自動化動作:

專案類型 Workflow 動作
App deploy-production.yml (uses deploy-production-cf.yml) pnpm build → composite action cf-pages-deploy 部署到 Pages main + 可選 cf-pages-custom-domain 綁網域 + 可選 cf-dns-record 寫 DNS + 可選 cf-redirect-rule 寫轉址 + composite action cf-pages-deployment-cleanup(production / preview 各自保留最新 N 筆,預設各 3,其餘刪除;keep <= 0 可關閉)
Lib publish.yml (uses publish-lib.yml) pnpm build + pnpm changeset publish → npm publish 未發版的 packages + 建 git tag

預期產出:

  • App:production 環境收到新版本(如 <domain> 顯示最新 build)
  • Lib:npm registry 上有新版本可供 pnpm install,git tag 已建立

為什麼 Phase 5 是人工觸發?

  • production deploy 是「不可逆」動作(CF Pages rollback 雖然支援,但對 user 仍有影響)
  • 給發佈負責人最後一次 sanity check 機會
  • Phase 4 完成的「main 有 version bump」狀態本身已是穩定可發版的快照,等多久才 deploy 由人決定

六、Lib vs App 流程差異

階段 Lib(套件庫) App(應用程式)
Phase 1:開發 相同 相同
Phase 2:CI + App Auto-merge to preview 相同 相同
Phase 3:Staging deploy + 自動合 main 不需 staging deploy,但仍走 preview→main PR(自動 merge) wrangler deploy preview + preview→main PR(自動 merge)
Phase 4:Version Bump 相同 相同
Phase 5:手動觸發 publish.ymlpnpm changeset publish(npm publish + git tag) deploy-production.ymlpnpm release(wrangler deploy main)

七、Reusable Workflows 索引

所有 reusable workflows 位於本 repo:shibamimi-tech/.github

1. 引用方式

jobs:
  my-job:
    uses: shibamimi-tech/.github/.github/workflows/<name>.yml@main
    with:
      node-version: "22"
    secrets:
      anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}

2. Workflow 清單

Phase 2–4 自動鏈(合併版優先):

檔名 用途 主要 inputs Token
ci-node.yml typecheck + lint + test + (optional) build node-version, pnpm-version, run-typecheck, run-lint, run-tests, run-build GITHUB_TOKEN
feature-pipeline-combined.yml Phase 2 合併版(推薦):單一 job 跑 check-changeset → open/update feature→preview PR → enable auto-merge by App。job id 為 gate,對應 required status check check-changeset / gate (無) App
release-lib.yml Phase 4:偵測 changesets → 開 Version PR + auto-merge by App(不 publish) node-version, pnpm-version App
release-app.yml Phase 4:偵測 changesets → 開 Version PR + auto-merge by App(不 deploy) node-version, pnpm-version App

Phase 3 與 Phase 5(Cloudflare Pages 部署,呼叫組織 composite actions):

檔名 用途 主要 inputs Token
deploy-staging-cf.yml Phase 3 合併版:preview push → composite cf-pages-deploy 部署 preview environment;若 open-pr: true 則同 job 內 inline 開 preview→main PR + auto-merge by App node-version, pnpm-version, build-script, cf-pages-project (req), cf-pages-dist-dir, wrangler-version, open-pr GITHUB_TOKEN + App(open-pr 時)+ CF tokens
deploy-production-cf.yml Phase 5 (workflow_dispatch):build → cf-pages-deploy 部署 main + 可選 cf-pages-custom-domain 綁網域 + cf-dns-record 寫 DNS + cf-redirect-rule 寫轉址 + cf-pages-deployment-cleanup 清理舊 production / preview deployments node-version, pnpm-version, build-script, cf-pages-project (req), cf-pages-dist-dir, cf-pages-custom-domain, cf-dns-zone, cf-dns-records, cf-redirect-rules, wrangler-version, cf-pages-keep-production (預設 3), cf-pages-keep-preview (預設 3) GITHUB_TOKEN + CF tokens
publish-lib.yml Phase 5 (workflow_dispatch):pnpm changeset publish → npm publish + git tag node-version, pnpm-version, build-script GITHUB_TOKEN

Zero Trust Access 設定同步(內部 repo 限定,如 docs):

檔名 用途 主要 inputs Token
apply-cf-access.yml main push 時把 .github/access-config.json 同步到 Cloudflare Access Application + Policy (透過 composite cf-access-application / cf-access-policy CF tokens
export-cf-access.yml 從 Cloudflare 反向匯出當前 Access 設定,便於 drift 檢查 (同上) CF tokens

舊版 / fallback(仍保留,但合併版優先):

檔名 用途 何時用
check-changeset.yml 純 changeset gate(不開 PR) 想自行串接 open-pr 的特殊用途
open-feature-to-preview-pr.yml preview 開 PR + auto-merge by App 不想用合併版時,搭配 check-changeset.yml 使用
open-preview-to-main-pr.yml main 開 release PR + auto-merge by App(維護 preview 為 main 祖先的鐵則) lib repo 不需 staging deploy 時、或不想用 deploy-staging-cf.ymlopen-pr: true inline 時

Token 欄位說明: App 表示用 shibamimi-tech-workflow App installation token 取代 GITHUB_TOKEN,讓 App fire 的 PR merge → push 能觸發下游 workflow(解決 GITHUB_TOKEN 不觸發下游的限制)。

CF tokens 說明: 透過 secrets: inherit 自 repo / org secrets 注入 CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID,由組織 composite actions(cf-pages-deploy 等)使用,caller 端不需自行管理 wrangler 設定。

引用方式:uses: shibamimi-tech/.github/.github/workflows/<name>.yml@main

3. 新 repo 套用標準設定(PR merge 規則等)

repo settings(squash only、auto-merge、delete branch on merge、workflow permissions 等)無法透過 GitHub Actions 的 GITHUB_TOKEN 修改——GitHub 不開放 administration scope 給 workflow,避免 workflow 被 compromise 時把整個 repo 鎖死。

採用 @shibamimi-tech/cli 維運 CLI(互動式 UI,需 gh auth login 後執行):

cd shibamimi-tech/<repo>
pnpm dlx @shibamimi-tech/cli init-repo

子命令涵蓋:PR/merge 規則、workflow permissions、preview 分支建立(idempotent)。詳細套用內容與手動 checklist 見 Repo 建置與設定 — Step 3 / 4(owner only)。

未來若要自動化,可建 GitHub App 授予 Administration 權限,再透過 workflow 用 App Token 觸發。目前以本地腳本為主。

4. 範例:一個典型 App repo 的 workflow 設定

# .github/workflows/ci.yml — Phase 2 Track A
name: CI
on:
  push:
    branches-ignore:
      - main
      - preview

jobs:
  ci:
    uses: shibamimi-tech/.github/.github/workflows/ci-node.yml@main
    secrets: inherit
    with:
      node-version: "22"
      run-typecheck: true
      run-build: true

# .github/workflows/feature-pipeline.yml — Phase 2 Track B(合併版)
# 單一 job 內依序跑 check-changeset → open/update PR + auto-merge by App。
# changeset gate 沒過就不會建/更新 PR。job id 保留為 `check-changeset` 以對應
# required status check `check-changeset / gate`。
name: Feature Pipeline
on:
  push:
    branches-ignore:
      - main
      - preview

jobs:
  check-changeset:
    permissions:
      contents: write          # reusable workflow 宣告 contents: write,caller 給 read 會 startup_failure
      pull-requests: write
    uses: shibamimi-tech/.github/.github/workflows/feature-pipeline-combined.yml@main
    secrets: inherit

# .github/workflows/deploy-preview.yml — Phase 3 staging deploy + inline 開 preview→main PR
# 用 open-pr: true 把 preview→main PR 開立合進同 job,省一份 billable minute。
name: Deploy Preview
on:
  push:
    branches: [preview]
  workflow_dispatch:

jobs:
  deploy:
    permissions:
      contents: write          # open-pr 步驟需要(mergePullRequest mutation)
      pull-requests: write
      packages: read
    uses: shibamimi-tech/.github/.github/workflows/deploy-staging-cf.yml@main
    secrets: inherit
    with:
      node-version: "22"
      build-script: "build"
      cf-pages-project: "shibamimi-tech-<repo>"
      open-pr: true

# .github/workflows/release.yml — Phase 4 Version Bump
name: Release
on:
  push:
    branches: [main]

jobs:
  release:
    uses: shibamimi-tech/.github/.github/workflows/release-app.yml@main
    secrets: inherit

# .github/workflows/deploy-production.yml — Phase 5 Manual Production Deploy
name: Deploy Production
on:
  workflow_dispatch:

jobs:
  deploy:
    uses: shibamimi-tech/.github/.github/workflows/deploy-production-cf.yml@main
    secrets: inherit
    with:
      node-version: "22"
      build-script: "build"
      cf-pages-project: "shibamimi-tech-<repo>"
      cf-pages-custom-domain: "<repo>.shibamimi.com"
      cf-dns-zone: "shibamimi.com"
      cf-dns-records: |
        [
          {
            "name": "<repo>.shibamimi.com",
            "type": "CNAME",
            "content": "shibamimi-tech-<repo>.pages.dev",
            "proxied": true
          }
        ]

lib repo 把最後一個 deploy-production.yml 換成 publish.yml 引用 publish-lib.ymlrelease.yml 改引用 release-lib.yml;不需 staging deploy,可把 deploy-preview.yml 改為獨立呼叫 open-preview-to-main-pr.yml(不傳 open-pr / 不部署)。

不想用合併版的 app repo:把 feature-pipeline.yml 拆回 check-changeset.yml + open-feature-to-preview-pr.yml 兩個 job、deploy-preview.yml 拿掉 open-pr: true 並另設 open-preview-to-main-pr.yml caller。功能等價,多一份 billable minute。

八、Changesets 整合

1. 何時需要寫 Changeset

每次 PR 都需要。沒有「這個變更不需要」的例外。

變更類型 需要 changeset? 建議 bump
Breaking change(API 移除、預設行為大改) major
新增功能、新 API、新文章、新元件 minor
修復 Bug、改變行為 patch
純重構(外部行為不變) patch
文件、README、CHANGELOG 修改 patch
CI / 設定調整、submodule bump patch
依賴升級(不影響 API) patch
依賴升級(影響 API) minormajor

過去的「空 changeset」例外已廢除。曾允許「config-only / docs / submodule bump 等非發版變更」用兩行 --- 之間留空的便利貼通過 gate;實務上這類便利貼會永遠卡在 .changeset/ 目錄(changesets/action 看不到 bump、不開 Version PR、不消化便利貼、不寫 CHANGELOG),導致組織失去自動變更歷史。新規範下不論大小變更,都要選 patch/minor/major + 在 body 寫一段描述「對使用者的影響」。

內部 repo(如 docsinvestor-relations)不 publish 沒關係,package.json 升版只是當 CHANGELOG 的書脊用,不會被任何外部 consumer 拉到。

2. Changeset 格式

---
"@shibamimi-tech/ui": minor
"@shibamimi-tech/config": patch
---

新增 `Button` 元件的 `loading` 狀態支援,允許在非同步操作期間顯示載入動畫。

說明:

  • frontmatter 列出受影響的 package 名稱與 bump 類型
  • body 用一段話描述「對使用者的影響」,這段文字會進入 CHANGELOG

3. Bump 類型決定原則

類型 使用條件 範例
patch Bug 修復、效能改善、文字修正,不影響現有 API 使用方式 修正 button 點擊後不觸發 onChange
minor 新增功能、新 props/API,向後相容 新增 size="xl" 選項
major Breaking change:移除 API、改變預設行為、強制升級依賴 移除舊版 onClick prop,改為 onPress

建議: 疑慮時選 minor 而非 major;只有真正會讓使用者現有代碼壞掉才選 major。

4. 版本管理指令

# 新增 changeset 便利貼(互動式)
pnpm changeset

# 預覽待發佈的版本(不實際修改)
pnpm changeset status

# 手動套用 changeset(正常情況由 CI 負責,不需手動執行)
pnpm changeset version

九、角色與責任

角色 主要責任 介入階段
開發人員 實作功能、撰寫測試、補 changeset、push 前自我 review 確認 ready-to-ship(品質把關前移)、修復 CI 問題 Phase 1、Phase 2(修復)
QA / PM 在 staging URL 做 post-hoc sanity check 與回歸測試;發現問題時通知發佈負責人 hold 住 Phase 5 deploy,或推 revert fix Phase 3(事後驗證)、Phase 5 前
發佈負責人 監看 staging、決定何時手動觸發 production deploy(唯一人工 gate) Phase 5

十、常見場景 / FAQ

1. Hotfix 怎麼處理?

規則:patch 也走完整五階段流程。沒有「繞過 preview」這個選項。

理由:

  • CI + check-changeset 幾秒到數分鐘內自動完成,完整流程的時間成本很低
  • 繞過 preview 即使是「就改一行」也會打破分支線性不變式,下次 release 時觸發衝突
  • 即使內容對沖(fix + revert)淨變化 = 0,git 歷史仍會記錄「main 動過檔案」→ 衝突

正確流程:

fix/urgent-xxx → push(含 changeset)→ feature-pipeline gate → App auto-merge 進 preview
   → preview→main PR 自動產生 + 立即 App auto-merge 進 main(Phase 3 全自動,無需 approve)
   → Version PR App auto-merge → 你到 Actions 手動觸發 Deploy Production

資安等真正緊急的情境呢? 仍然走 fix/* 分支。CI + 全鏈 auto-merge 幾分鐘就完成,Phase 5 手動觸發部署只需點一下 button。比手動繞過 preview 後續還要補對齊還快。

2. 多個 feature 累積在 main(未 deploy)太久怎麼辦?

  • v2.7 起 preview→main 與 Version PR 都全自動,main 永遠跟 preview 同步,不會積壓在 preview
  • 定期(建議每週)讓發佈負責人到 Actions 手動觸發 Deploy Production,把 main 上累積的版本 ship 到 production
  • 若想拆成部分 release,需在 feature/* 階段就決定(不要 push 那批不想 ship 的 feature)
  • open-preview-to-main-pr 是 idempotent 的,不用擔心重複開 PR

3. 緊急回滾怎麼做?

Option A(推薦): Revert commit + 走正常流程

# 從 main 切出 fix 分支
git checkout main && git pull
git checkout -b fix/revert-xxx

# 在 fix 分支上做 revert(不在 main 上做!)
git revert <bad-commit-hash>

# 加 changeset 描述回滾原因
pnpm changeset

# 推送 → feature-pipeline gate → auto-merge to preview → preview→main PR → merge
git push -u origin fix/revert-xxx

⚠️ 絕對不要 git checkout main && git revert <sha> && git push origin main。即使 admin 有權限,這會破壞分支線性,未來必出衝突。

Option B: 如果是 App,可直接在 Cloudflare Dashboard 回滾到上一個 deployment — 這條路徑完全不動 git,沒有破壞分支結構的風險。

與部署清理的關係: deploy-production-cf.yml 每次部署後會透過 cf-pages-deployment-cleanup 清掉超出保留數量的舊 deployments(預設 production / preview 各留 3,見上方 workflow 索引)。只要 cf-pages-keep-production 維持 ≥ 2,Option B 依賴的「上一個 deployment」永遠會被保留;若把它調成 ≤ 1,Option B 將失效(CF Dashboard 上已無上一版可選),回滾只能走 Option A。

4. Q:我 push 了 changeset,但我發現要再小改一行 code,怎麼辦?

A:兩種做法:

  1. 小修正在原分支:push 修正 commit → 編輯 changeset .md 描述(反映本次修正)→ 再 push → check-changeset 通過 → auto-merge fire
  2. 大修正開新分支:close 原 PR → 新建 feature/* 分支補上 → 重走流程

避免「push 修正但不更新 changeset」——check-changeset 會紅燈(未偵測到 changeset 變動),auto-merge 不會觸發。

5. Q:我可以 push 純 code 做雲端備份嗎?

A:可以check-changeset 會亮紅燈,但這是預期行為:

  • 連 PR 都不會被建立(如果是首次 push 該分支)
  • 已存在的 PR auto-merge 卡住
  • 不會浪費 reviewer 注意力(沒 AI review,但也沒人類 reviewer 被打擾)

要 ship 時補 changeset 再 push 即可解鎖。

6. 第一次冷啟動 v0.1.0

由 owner 負責,詳見 Repo 建置與設定 — Step 5

十一、從舊規範遷移

本節適用於從舊版提交規範(Laziimit,已廢棄)遷移到 Changesets 的 repo。

舊規範概念 新規範對應
手動修改 package.json 版本號 changesets/action 在 CI 自動處理
四區段 commit 訊息([新增] / [修改] / [刪除] / [備註]) 自由格式 commit 訊息;版本描述移至 .changeset/*.md
本地計算 patch/minor 類型 pnpm changeset 互動式選擇 bump 類型
主動 push 觸發發佈 走 Phase 3–4 自動流程 + Phase 5 手動觸發 deploy/publish
CHANGELOG 需手動維護 changesets/action 自動統整 CHANGELOG.md

遷移步驟:

  1. 安裝 @changesets/clipnpm add -D @changesets/cli
  2. 初始化:pnpm changeset init(產生 .changeset/config.json
  3. 設定 .changeset/config.jsonchangeloglinked 等欄位
  4. 在各 repo 加入 release-lib.ymlrelease-app.yml workflow
  5. 刪除舊的手動版本 bump 腳本