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

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

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

先讀「樹狀編排方法論」再讀本文。 本文是 出貨流程(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組織共用 workflowsreusable workflows + composite actions 集中地,被所有 repo 引用
.claudeAgents / 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有言待確認
animationsRemotion 動畫庫待確認
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 可合入;任何人類直推都違規
previewStaging 環境,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 workflowPhase觸發用途
ci.yml2feature/fix/release 分支 push跑 typecheck / lint / test / build,提供 ci / ci required status check
feature-pipeline.yml2同上驗證 changeset 便利貼(gate),通過後對 preview 開 / 更新 PR + 啟 auto-merge by App,提供 check-changeset / gate required status check
deploy-preview.yml3preview 分支 push部署 staging 到 Cloudflare Pages preview environment,同 job 內 inline 開 preview → main PR + 啟 auto-merge by App
release.yml4main 分支 pushchangesets/action 偵測 .changeset/*.md → 開 Version PR(版號 bump + CHANGELOG)+ 啟 auto-merge by App
deploy-production.yml5workflow_dispatch(人工)App only:手動觸發 production deploy(CF Pages main + 可選 custom domain / DNS / redirect)
publish.yml5workflow_dispatch(人工)Lib only:手動觸發 pnpm changeset publish → npm publish + git tag
apply-access-config.ymlmain 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 情境)

情境cicheck-changesetopen-prauto-merge
1. code only(首次)🔴 Check 1 失敗⏭️ skippedN/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動作
Appdeploy-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 寫轉址
Libpublish.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 自動鏈(合併版優先):

檔名用途主要 inputsToken
ci-node.ymltypecheck + lint + test + (optional) buildnode-version, pnpm-version, run-typecheck, run-lint, run-tests, run-buildGITHUB_TOKEN
feature-pipeline-combined.ymlPhase 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.ymlPhase 4:偵測 changesets → 開 Version PR + auto-merge by App(不 publish)node-version, pnpm-versionApp
release-app.ymlPhase 4:偵測 changesets → 開 Version PR + auto-merge by App(不 deploy)node-version, pnpm-versionApp

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

檔名用途主要 inputsToken
deploy-staging-cf.ymlPhase 3 合併版:preview push → composite cf-pages-deploy 部署 preview environment;若 open-pr: true 則同 job 內 inline 開 preview→main PR + auto-merge by Appnode-version, pnpm-version, build-script, cf-pages-project (req), cf-pages-dist-dir, wrangler-version, open-prGITHUB_TOKEN + App(open-pr 時)+ CF tokens
deploy-production-cf.ymlPhase 5 (workflow_dispatch):build → cf-pages-deploy 部署 main + 可選 cf-pages-custom-domain 綁網域 + cf-dns-record 寫 DNS + cf-redirect-rule 寫轉址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-versionGITHUB_TOKEN + CF tokens
publish-lib.ymlPhase 5 (workflow_dispatch):pnpm changeset publish → npm publish + git tagnode-version, pnpm-version, build-scriptGITHUB_TOKEN

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

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

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

檔名用途何時用
check-changeset.yml純 changeset gate(不開 PR)想自行串接 open-pr 的特殊用途
open-feature-to-preview-pr.ymlpreview 開 PR + auto-merge by App不想用合併版時,搭配 check-changeset.yml 使用
open-preview-to-main-pr.ymlmain 開 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 bumppatch
依賴升級(不影響 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 類型決定原則

類型使用條件範例
patchBug 修復、效能改善、文字修正,不影響現有 API 使用方式修正 button 點擊後不觸發 onChange
minor新增功能、新 props/API,向後相容新增 size="xl" 選項
majorBreaking 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 fixPhase 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,沒有破壞分支結構的風險。

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 腳本