本文件定義 Prinsur 三個 repo(prinsur-web、prinsur-api、prinsur-ai)如何強制 commit 規範,以及相關自動化工具的設定政策。
規範定義見:
- commit-tags.md — 11 個 commit type
- commit-scopes.md — 各 repo scope 清單
三個 repo 統一採用 GitHub Squash Merge 策略。這有兩個重要意涵:
- 主幹每個 commit = PR 的 squash commit,其 message 來自 PR title(body 為 GitHub 自動匯總)。
- Feature branch 上的本地 commits 不會進入 main,因此對本地 commit 做嚴格檢查(commitlint husky hook)ROI 很低,反而常被 contributor 抱怨影響開發節奏。
結論:強制點放在 PR title,不放在本地 commit。
參考資料:
使用 amannn/action-semantic-pull-request@v6 在每個 PR 層檢查 title。
本 repo .github/workflows/pr-title-lint.yml 提供 workflow_call 介面,三個 repo 呼叫此 workflow 即可,不需各自維護完整設定。
各 repo 的 caller workflow(放在 <repo>/.github/workflows/lint-pr-title.yml):
name: Lint PR Title
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
pull-requests: read
jobs:
lint:
uses: prinsur/.github/.github/workflows/pr-title-lint.yml@main
with:
scopes: |
# 替換為當前 repo 的 scope 清單prinsur-web:
app
ui
chat
insurance
auth
landing
i18n
api
common
layouts
infra
prinsur-api:
admin
agent
conversation
customer
insurance
auth
storage
verification
db
http
infra
prinsur-ai:
chat
agent
api
core
models
infra
type∈ 11 個允許的 type(固定於 reusable workflow,無需 caller 傳入)scope∈ caller 傳入的 scope 清單(可為空,因為規範允許 scope 省略)subject不得以大寫字母開頭(^(?![A-Z]).+$)
使用 pull_request(而非 pull_request_target)的理由:
- prinsur 三個 repo 皆為 organization 內部 private repo,不預期接收 fork PR,
pull_request_target提供的 fork 支援價值低。 pull_request會從 PR 的 HEAD(feature branch)讀取 workflow 定義,首次加入本 workflow 時可自我驗證;若改用pull_request_target,GitHub 會從 base branch(main)讀取 workflow,首次 PR 因檔案尚未在 main 而無法觸發(chicken-and-egg)。- 本 workflow 只讀取 PR metadata,不 checkout 原始碼、不需要 secret,
pull_request的 read-onlyGITHUB_TOKEN已足夠。
若未來需接收 fork PR,可依 amannn/action-semantic-pull-request 文件改用 pull_request_target。
Dependabot 是 commit 來源之一。為避免 changelog 混亂,三個 repo 採統一前綴:
| 依賴類型 | 前綴 | 原因 |
|---|---|---|
| 語言層依賴(pnpm、uv、gomod) | chore(deps): |
與歷史 commit 一致,changelog 不特別突出 |
| GitHub Actions workflow 依賴 | ci(deps): |
本質上是 CI 設定變更 |
在 <repo>/.github/dependabot.yml 確認:
# Dependabot 使用 npm / pip / gomod 作為 ecosystem 值,
# 會自動偵測專案實際使用的 pnpm / uv / poetry 等 sub-tool
- package-ecosystem: "npm" # prinsur-web (pnpm)
commit-message:
prefix: "chore(deps)"
- package-ecosystem: "pip" # prinsur-ai (uv)
commit-message:
prefix: "chore(deps)"
- package-ecosystem: "gomod" # prinsur-api
commit-message:
prefix: "chore(deps)"
# GitHub Actions(若有)
- package-ecosystem: "github-actions"
commit-message:
prefix: "ci(deps)"手動新增或升級 runtime 依賴時,建議使用 build(deps):(更精確表達該變更會影響產出的 artifact)。但若只是小量升級、與歷史一致優先,使用 chore(deps): 亦可。開發依賴(dev-only)統一用 chore(deps):。
目前三個 repo 都用 Dependabot。若未來改用 Renovate,需在 renovate.json 明確指定 semanticCommitType 與 semanticCommitScope,與上述政策一致:
{
"semanticCommits": "enabled",
"semanticCommitType": "chore",
"semanticCommitScope": "deps"
}考慮過但刻意不做。原因:
- Squash Merge 下,本地 commit 不上 main。強制本地 commit 格式價值低。
- 社群回報本地 hook 常被抱怨。開發中常有 WIP、fixup、實驗性 commit,被 hook 擋下反而阻礙迭代。
- PR title lint 已覆蓋主要價值。主幹 commit 一致性 + 未來 changelog 基礎皆已滿足。
若團隊成長到需要嚴格化(例如導入 semantic-release 自動發版、或需要本地 commit 做 fine-grained changelog),再評估加入 commitlint CI check(非 husky)。
| 時機 | 考慮導入 |
|---|---|
| 需要自動 changelog | conventional-changelog + release-please / semantic-release |
| 需要自動 semver bump | semantic-release(僅 node 生態友善,Go/Python 需替代品)或 release-please |
| 需要更嚴格檢查 | commitlint CI check(不用 husky 本地 hook) |
| 新 repo 加入 org | 直接套用本 repo 的 reusable workflow,無額外設定 |
- Type 拼錯 — 例如
refractor、feature(應為refactor、feat) - Scope 不在清單 — 例如 prinsur-api 用了
middleware(應為http) - Subject 以大寫開頭 — 例如
feat(ui): Add dark mode(應為add dark mode) - 缺少冒號空格 — 例如
feat(ui):add dark mode(冒號後需空格)
修正 PR title 後 check 會自動重跑,無需重推 commit。
本 repo @main 為 caller 參考的版本。若需凍結特定版本,caller 改用 commit SHA 或 tag(例如 @v1.0.0)。目前保持 @main 以利統一演進。