自動化 CI/CD 管線設定。在設定或修改建置與部署管線時使用。當你需要自動化品質關卡、在 CI 中設定測試執行器,或建立部署策略時使用。
CI/CD 與自動化
概述
自動化品質關卡,確保任何變更在未通過測試、lint、型別檢查和建置之前,都不會進入正式環境。CI/CD 是所有其他技能的執行機制——它能捕捉人類和代理遺漏的問題,並且在每次變更時一致地執行。
Shift Left(左移測試): 盡早在管線中發現問題。在 lint 階段發現的錯誤只需幾分鐘;同樣的錯誤在正式環境中發現則需要數小時。將檢查向上游移動——靜態分析在測試之前,測試在暫存環境之前,暫存環境在正式環境之前。
越快越安全: 較小的批次和更頻繁的發布能降低風險,而非增加風險。包含 3 個變更的部署比包含 30 個變更的部署更容易除錯。頻繁發布能建立對發布流程本身的信心。
使用時機
- 設定新專案的 CI 管線
- 新增或修改自動化檢查
- 設定部署管線
- 當變更應觸發自動化驗證時
- 除錯 CI 失敗
品質關卡管線
每次變更在合併前都必須通過以下關卡:
Pull Request 開啟
│
▼
┌─────────────────┐
│ LINT 檢查 │ eslint, prettier
│ ↓ 通過 │
│ 型別檢查 │ tsc --noEmit
│ ↓ 通過 │
│ 單元測試 │ jest/vitest
│ ↓ 通過 │
│ 建置 │ npm run build
│ ↓ 通過 │
│ 整合測試 │ API/DB 測試
│ ↓ 通過 │
│ E2E(選擇性) │ Playwright/Cypress
│ ↓ 通過 │
│ 安全稽核 │ npm audit
│ ↓ 通過 │
│ 套件大小檢查 │ bundlesize check
└─────────────────┘
│
▼
準備好進行審查
沒有任何關卡可以跳過。 如果 lint 失敗,請修正 lint——不要停用規則。如果測試失敗,請修正程式碼——不要跳過測試。
GitHub Actions 設定
基本 CI 管線
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: 安裝依賴
run: npm ci
- name: Lint
run: npm run lint
- name: 型別檢查
run: npx tsc --noEmit
- name: 測試
run: npm test -- --coverage
- name: 建置
run: npm run build
- name: 安全稽核
run: npm audit --audit-level=high
含資料庫整合測試
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: ci_user
POSTGRES_PASSWORD: ${{ secrets.CI_DB_PASSWORD }}
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 執行遷移
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://ci_user:${{ secrets.CI_DB_PASSWORD }}@localhost:5432/testdb
- name: 整合測試
run: npm run test:integration
env:
DATABASE_URL: postgresql://ci_user:${{ secrets.CI_DB_PASSWORD }}@localhost:5432/testdb
注意: 即使是僅供 CI 使用的測試資料庫,也應使用 GitHub Secrets 來儲存憑證,而非直接寫死在程式碼中。這能養成良好習慣,並防止測試憑證在其他情境中被意外使用。
E2E 測試
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 安裝 Playwright
run: npx playwright install --with-deps chromium
- name: 建置
run: npm run build
- name: 執行 E2E 測試
run: npx playwright test
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
將 CI 失敗回饋給代理
CI 與 AI 代理結合的威力在於回饋迴圈。當 CI 失敗時:
CI 失敗
│
▼
複製失敗輸出
│
▼
提供給代理:
「CI 管線因以下錯誤而失敗:
[貼上具體錯誤]
修正問題並在本地驗證後再推送。」
│
▼
代理修正 → 推送 → CI 再次執行
關鍵模式:
Lint 失敗 → 代理執行 `npm run lint --fix` 並提交
型別錯誤 → 代理讀取錯誤位置並修正型別
測試失敗 → 代理遵循除錯與錯誤復原技能
建置錯誤 → 代理檢查設定與依賴
部署策略
預覽部署
每個 PR 都會獲得一個預覽部署以供手動測試:
# 在 PR 上部署預覽(Vercel/Netlify 等)
deploy-preview:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: 部署預覽
run: npx vercel --token=${{ secrets.VERCEL_TOKEN }}
功能開關
功能開關將部署與發布脫鉤。將未完成或有風險的功能部署在開關之後,以便:
- 在不啟用功能的情況下發布程式碼。 及早合併到 main,準備好時再啟用。
- 無需重新部署即可回滾。 停用開關,而非還原程式碼。
- 金絲雀發布新功能。 先對 1% 的使用者啟用,然後 10%,最後 100%。
- 執行 A/B 測試。 比較啟用與未啟用功能時的行為。
// 簡單的功能開關模式
if (featureFlags.isEnabled('new-checkout-flow', { userId })) {
return renderNewCheckout();
}
return renderLegacyCheckout();
開關生命週期: 建立 → 啟用測試 → 金絲雀發布 → 全面上線 → 移除開關與死程式碼。永遠存在的開關會變成技術債——建立時就設定清理日期。
分階段發布
PR 合併到 main
│
▼
暫存環境部署(自動)
│ 手動驗證
▼
正式環境部署(手動觸發或暫存環境驗證後自動)
│
▼
監控錯誤(15 分鐘視窗)
│
├── 偵測到錯誤 → 回滾
└── 無錯誤 → 完成
回滾計畫
每次部署都應可逆:
# 手動回滾工作流程
name: Rollback
on:
workflow_dispatch:
inputs:
version:
description: '要回滾到的版本'
required: true
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- name: 回滾部署
run: |
# 部署指定的先前版本
npx vercel rollback ${{ inputs.version }}
環境管理
.env.example → 提交(開發者範本)
.env → 不提交(本地開發)
.env.test → 提交(測試環境,不含真實機密)
CI 機密 → 儲存在 GitHub Secrets / vault
正式環境機密 → 儲存在部署平台 / vault
CI 絕不應擁有正式環境機密。使用獨立的機密進行 CI 測試。
超越 CI 的自動化
Dependabot / Renovate
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
Build Cop 角色
指定某人負責保持 CI 綠色。當建置中斷時,Build Cop 的職責是修復或還原——而不是造成中斷的人。這可以防止建置中斷持續累積,而每個人都認為別人會去修復。
PR 檢查
- 必要審查: 合併前至少 1 個核准
- 必要狀態檢查: CI 必須通過才能合併
- 分支保護: 禁止強制推送至 main
- 自動合併: 如果所有檢查通過且已核准,自動合併
CI 最佳化
當管線超過 10 分鐘時,依影響程度依序套用以下策略:
CI 管線太慢?
├── 快取依賴
│ └── 使用 actions/cache 或 setup-node 的快取選項來快取 node_modules
├── 平行執行工作
│ └── 將 lint、型別檢查、測試、建置拆分為獨立的平行工作
├── 只執行有變更的部分
│ └── 使用路徑過濾器跳過不相關的工作(例如,僅文件變更的 PR 跳過 e2e)
├── 使用矩陣建置
│ └── 將測試套件分散到多個執行器
├── 最佳化測試套件
│ └── 將慢速測試移出關鍵路徑,改為排程執行
└── 使用更大的執行器
└── GitHub 託管的更大執行器或自託管執行器,適用於 CPU 密集型建置
範例:快取與平行化
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npm run lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npx tsc --noEmit
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npm test -- --coverage
常見合理化藉口
| 合理化藉口 | 現實 |
|---|---|
| 「CI 太慢了」 | 最佳化管線(見下方 CI 最佳化),不要跳過。5 分鐘的管線能避免數小時的除錯。 |
| 「這個變更很簡單,跳過 CI」 | 簡單的變更也會破壞建置。而且對簡單變更來說,CI 很快。 |
| 「測試不穩定,重新執行就好」 | 不穩定的測試會掩蓋真正的錯誤,並浪費大家的時間。請修正不穩定性。 |
| 「我們之後再加 CI」 | 沒有 CI 的專案會累積損壞狀態。請在第一天就設定好。 |
| 「手動測試就夠了」 | 手動測試無法擴展,也不可重複。請盡可能自動化。 |
紅旗
- 專案中沒有 CI 管線
- CI 失敗被忽略或靜默處理
- 為了讓管線通過而在 CI 中停用測試
- 未經暫存環境驗證就部署到正式環境
- 沒有回滾機制
- 機密儲存在程式碼或 CI 設定檔中(而非機密管理工具)
- CI 時間過長且未嘗試最佳化
驗證
設定或修改 CI 後:
- [ ] 所有品質關卡都已存在(lint、型別、測試、建置、稽核)
- [ ] 管線在每個 PR 和推送至 main 時執行
- [ ] 失敗會阻止合併(已設定分支保護)
- [ ] CI 結果回饋到開發循環中
- [ ] 機密儲存在機密管理工具中,而非程式碼中
- [ ] 部署具有回滾機制
- [ ] 管線在 10 分鐘內執行完測試套件






