目錄
- 什麼是 GitHub Actions?
- 核心概念
- Workflow 語法詳解
- 觸發事件
- 表達式與上下文
- Job 進階配置
- 可重用元件
- Secrets 與環境變數
- 快取與效能優化
- 實戰範例
- 安全性最佳實踐
- 除錯與疑難排解
- 常見問題
- 總結
什麼是 GitHub Actions?
GitHub Actions 是 GitHub 內建的 CI/CD 和自動化平台,讓你在 Repository 中直接定義自動化流程,回應事件(push、PR、排程等)來執行建構、測試、部署等任務。
核心特點
- 事件驅動:push、PR、issue、排程、手動觸發等都可作為啟動條件
- YAML 定義:用
.github/workflows/*.yml宣告式地描述整個流程 - 豐富的 Marketplace:超過 20,000+ 個社群 Action 可直接使用
- 矩陣建構:一份設定同時跑多個版本/平台的組合測試
- 原生整合:與 GitHub 的 PR、Issue、Package、Environment 深度整合
GitHub Actions vs 其他 CI/CD 工具
| 特性 | GitHub Actions | GitLab CI/CD | Jenkins |
|---|---|---|---|
| 設定方式 | YAML(Repository 內) | YAML(Repository 內) | Groovy / UI |
| 託管 | GitHub 託管 Runner | GitLab 託管 Runner | 自行架設 |
| 費用 | 公開 Repo 免費 | 公開 Repo 免費 | 開源免費(需自行維護) |
| 上手難度 | 低 | 低 | 中高 |
| 擴充性 | Marketplace Action | CI 模板 | Plugin 生態系 |
| Self-hosted | ✅ 支援 | ✅ 支援 | ✅ 原生 |
核心概念
架構總覽
Repository
└── .github/workflows/
├── ci.yml ← Workflow(工作流程)
├── deploy.yml
└── release.yml
每個 Workflow:
┌──────────────────────────────────────────────┐
│ Workflow(工作流程) │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Job A │──→│ Job B │ ← 可定義依賴 │
│ │ │ │ │ │
│ │ Step 1 │ │ Step 1 │ ← 每個 Step │
│ │ Step 2 │ │ Step 2 │ 是一個 Action │
│ │ Step 3 │ │ Step 3 │ 或 Shell 指令 │
│ └──────────┘ └──────────┘ │
│ Runner A Runner B ← 各自獨立的機器 │
└──────────────────────────────────────────────┘
六大元件
| 元件 | 說明 | 比喻 |
|---|---|---|
| Workflow | 自動化流程的最上層單位,一個 .yml 檔案就是一個 Workflow |
一條生產線 |
| Event | 觸發 Workflow 的事件(push、PR、排程等) | 啟動開關 |
| Job | Workflow 中的一組任務,每個 Job 在獨立 Runner 上執行 | 生產線上的工作站 |
| Step | Job 中的單一操作步驟,按順序執行 | 工作站上的單一動作 |
| Action | 可重用的最小單元,可以是社群的或自定義的 | 標準化的工具 |
| Runner | 執行 Job 的伺服器(GitHub 託管或自架) | 工作機器 |
元件關係
Event(觸發)
└→ Workflow(流程)
├→ Job 1(獨立 Runner)
│ ├→ Step 1: uses: actions/checkout@v4 ← Action
│ ├→ Step 2: run: npm install ← Shell 指令
│ └→ Step 3: run: npm test
└→ Job 2(依賴 Job 1)
├→ Step 1: uses: actions/checkout@v4
└→ Step 2: run: npm run deploy
Runner 類型
| Runner | 作業系統 | 標籤 | 適用場景 |
|---|---|---|---|
| GitHub 託管 | Ubuntu | ubuntu-latest、ubuntu-24.04 |
通用 CI/CD |
| GitHub 託管 | macOS | macos-latest、macos-15 |
iOS / macOS 建構 |
| GitHub 託管 | Windows | windows-latest、windows-2022 |
.NET / Windows 建構 |
| Self-hosted | 自訂 | 自訂標籤 | 特殊硬體、私有網路、降低費用 |
Workflow 語法詳解
基本結構
# .github/workflows/ci.yml
name: CI # Workflow 名稱(顯示在 GitHub UI)
on: # 觸發條件
push:
branches: [main]
permissions: # Workflow 層級的權限設定
contents: read
env: # Workflow 層級的環境變數
NODE_VERSION: '20'
jobs: # 定義所有 Job
build: # Job ID(自訂名稱)
name: Build Application # Job 顯示名稱
runs-on: ubuntu-latest # Runner 類型
timeout-minutes: 10 # 超時設定
env: # Job 層級的環境變數
CI: true
steps: # 定義步驟
- name: Checkout # Step 名稱
uses: actions/checkout@v4 # 使用 Action
- name: Build
run: npm run build # 執行 Shell 指令
Step 的兩種類型
steps:
# 類型 1:使用 Action(uses)
- name: Setup Node.js
uses: actions/setup-node@v4 # {owner}/{repo}@{ref}
with: # 傳入 Action 的參數
node-version: '20'
cache: 'npm'
# 類型 2:執行 Shell 指令(run)
- name: Install and Build
run: | # 多行指令用 |
npm ci
npm run build
working-directory: ./frontend # 指定工作目錄
shell: bash # 指定 Shell(預設 bash)
env: # Step 層級的環境變數
NODE_ENV: production
條件執行(if)
steps:
# 只在 main 分支執行
- name: Deploy
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
# 只在 PR 執行
- name: Preview
if: github.event_name == 'pull_request'
run: ./preview.sh
# 即使前一步失敗也執行(常用於清理)
- name: Cleanup
if: always()
run: ./cleanup.sh
# 只在失敗時執行(通知)
- name: Notify on failure
if: failure()
run: ./notify-slack.sh
# 組合條件
- name: Release
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: ./release.sh
Step 之間傳遞資料
jobs:
example:
runs-on: ubuntu-latest
steps:
# 方式 1:透過 output
- name: Set version
id: version # 設定 Step ID
run: echo "value=1.2.3" >> $GITHUB_OUTPUT
- name: Use version
run: echo "Version is ${{ steps.version.outputs.value }}"
# 方式 2:透過環境變數
- name: Set env
run: echo "MY_VAR=hello" >> $GITHUB_ENV
- name: Use env
run: echo "$MY_VAR" # 直接使用
觸發事件
常用觸發事件總覽
on:
# ── Git 事件 ──
push: # 推送到分支
branches: [main, develop]
tags: ['v*'] # 推送標籤
paths: ['src/**'] # 只在特定路徑變更時觸發
pull_request: # PR 事件
branches: [main]
types: [opened, synchronize, reopened]
# ── 排程 ──
schedule:
- cron: '0 2 * * 1' # 每週一凌晨 2 點(UTC)
# ── 手動觸發 ──
workflow_dispatch: # 在 GitHub UI 手動觸發
inputs:
environment:
description: '部署環境'
required: true
type: choice
options: [staging, production]
dry_run:
description: '僅測試不實際部署'
type: boolean
default: false
# ── 其他 Workflow 觸發 ──
workflow_call: # 被其他 Workflow 呼叫(可重用 Workflow)
inputs:
config:
type: string
required: true
secrets:
deploy_key:
required: true
路徑過濾
on:
push:
branches: [main]
paths:
- 'src/**' # src 下任何變更
- '!src/**/*.test.js' # 排除測試檔案
- 'package.json' # 特定檔案
paths-ignore: # 或用 paths-ignore 排除
- 'docs/**'
- '*.md'
活動類型過濾
on:
# PR 的特定動作
pull_request:
types: [opened, synchronize, reopened, labeled]
# Issue 的特定動作
issues:
types: [opened, labeled]
# Release 發布
release:
types: [published]
# 外部觸發(API 呼叫)
repository_dispatch:
types: [deploy-command]
Cron 語法速查
┌───────────── 分鐘(0-59)
│ ┌─────────── 小時(0-23)
│ │ ┌───────── 日期(1-31)
│ │ │ ┌─────── 月份(1-12)
│ │ │ │ ┌───── 星期幾(0-6,0 = 星期日)
│ │ │ │ │
* * * * *
| 範例 | 說明 |
|---|---|
0 0 * * * |
每天 00:00 UTC |
0 2 * * 1 |
每週一 02:00 UTC |
30 5 1 * * |
每月 1 號 05:30 UTC |
*/15 * * * * |
每 15 分鐘 |
0 9 * * 1-5 |
週一到週五 09:00 UTC |
⚠️ GitHub Actions 的 cron 使用 UTC 時區,台灣時間需要 減 8 小時。 例如想在台灣時間每天早上 9 點執行,cron 要設定為
0 1 * * *(UTC 01:00)。
表達式與上下文
表達式語法
GitHub Actions 使用 ${{ }} 語法存取上下文資料和進行運算。
# 基本存取
${{ github.ref }}
${{ secrets.API_KEY }}
${{ env.NODE_VERSION }}
# 比較運算
${{ github.ref == 'refs/heads/main' }}
${{ github.event_name != 'pull_request' }}
# 邏輯運算
${{ github.ref == 'refs/heads/main' && success() }}
${{ failure() || cancelled() }}
# 包含判斷
${{ contains(github.event.head_commit.message, '[skip ci]') }}
${{ startsWith(github.ref, 'refs/tags/v') }}
# 三元運算(用 && || 模擬)
${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
常用上下文
| 上下文 | 說明 | 常用屬性 |
|---|---|---|
github |
事件與 Repository 資訊 | ref、sha、event_name、repository、actor |
env |
環境變數 | 自訂的環境變數 |
secrets |
加密的 Secrets | GITHUB_TOKEN、自訂 Secrets |
jobs |
目前 Workflow 的 Job 資訊 | jobs.<id>.result |
steps |
目前 Job 的 Step 資訊 | steps.<id>.outputs、steps.<id>.outcome |
matrix |
矩陣參數 | 矩陣定義的變數 |
needs |
依賴 Job 的資訊 | needs.<id>.outputs、needs.<id>.result |
inputs |
手動觸發或可重用 Workflow 的輸入 | 自訂的 input |
runner |
Runner 環境資訊 | os、arch、temp |
vars |
Repository / Organization 層級變數 | 自訂的變數 |
狀態檢查函數
if: success() # 前面的步驟都成功(預設行為)
if: failure() # 前面有步驟失敗
if: always() # 無論成功或失敗都執行
if: cancelled() # Workflow 被取消時
Job 進階配置
Job 之間的依賴
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
# 依賴 lint 和 test 都成功後才執行
deploy:
needs: [lint, test] # 等待這些 Job 完成
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
┌──────┐
┌───→│ lint │───┐
│ └──────┘ │
觸發─┤ ├──→ deploy
│ ┌──────┐ │
└───→│ test │───┘
└──────┘
(lint 和 test 平行執行,都成功後才 deploy)
Job 之間傳遞資料
jobs:
build:
runs-on: ubuntu-latest
outputs: # 宣告 Job 的 output
version: ${{ steps.ver.outputs.version }}
steps:
- id: ver
run: echo "version=1.2.3" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Deploying ${{ needs.build.outputs.version }}"
Matrix 策略(矩陣建構)
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20, 22]
# 產生 3 × 3 = 9 個組合
# 排除特定組合
exclude:
- os: windows-latest
node-version: 18
# 額外加入特定組合
include:
- os: ubuntu-latest
node-version: 22
experimental: true # 自訂變數
fail-fast: false # 一個失敗不中止其他(預設 true)
max-parallel: 4 # 最大同時執行數
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
Services(服務容器)
jobs:
test:
runs-on: ubuntu-latest
services:
postgres: # 自訂服務名稱
image: postgres:16
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >- # Docker 選項
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Run tests
env:
DATABASE_URL: postgres://postgres:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
run: npm test
權限設定(Permissions)
# Workflow 層級
permissions:
contents: read # 讀取程式碼
pull-requests: write # 寫入 PR 評論
issues: write # 建立 Issue
# Job 層級(覆蓋 Workflow 層級)
jobs:
deploy:
permissions:
contents: read
id-token: write # OIDC token(用於雲端部署)
Concurrency(並行控制)
# Workflow 層級
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 新的執行會取消進行中的
# Job 層級
jobs:
deploy:
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false # 部署不應被取消
Environment(部署環境)
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com # 顯示在 PR 上的連結
steps:
- run: ./deploy.sh staging
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production # 可在 GitHub 設定必要審核
url: https://example.com
steps:
- run: ./deploy.sh production
在 GitHub Repository → Settings → Environments 中可設定:
- Required reviewers:部署前需人工審核
- Wait timer:等待一段時間後自動通過
- Branch protection:限制只有特定分支能部署
可重用元件
可重用 Workflow(Reusable Workflows)
定義(被呼叫的 Workflow):
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call: # 宣告為可重用 Workflow
inputs:
environment:
required: true
type: string
version:
required: true
type: string
secrets:
deploy_key:
required: true
outputs:
deploy_url:
description: "部署後的 URL"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.deploy.outputs.url }}
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Deploy
id: deploy
run: |
echo "Deploying ${{ inputs.version }} to ${{ inputs.environment }}"
echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUT
env:
DEPLOY_KEY: ${{ secrets.deploy_key }}
呼叫:
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml # 呼叫可重用 Workflow
with:
environment: staging
version: ${{ github.ref_name }}
secrets:
deploy_key: ${{ secrets.STAGING_KEY }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
version: ${{ github.ref_name }}
secrets:
deploy_key: ${{ secrets.PRODUCTION_KEY }}
Composite Action(組合動作)
將多個步驟封裝成一個可重用的 Action。
# .github/actions/setup-and-build/action.yml
name: 'Setup and Build'
description: '設定環境並建構專案'
inputs:
node-version:
description: 'Node.js 版本'
required: false
default: '20'
outputs:
build-path:
description: '建構輸出路徑'
value: ${{ steps.build.outputs.path }}
runs:
using: 'composite' # 宣告為 Composite Action
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- name: Install dependencies
shell: bash # composite 中 run 必須指定 shell
run: npm ci
- name: Build
id: build
shell: bash
run: |
npm run build
echo "path=dist" >> $GITHUB_OUTPUT
使用:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-and-build # 相對路徑引用
with:
node-version: '20'
Reusable Workflow vs Composite Action
| 特性 | Reusable Workflow | Composite Action |
|---|---|---|
| 粒度 | 整個 Workflow(含多個 Job) | 單一 Step(多步驟組合) |
| Runner | 各自獨立的 Runner | 使用呼叫方的 Runner |
| Secrets | 需要明確傳遞 | 自動繼承呼叫方的上下文 |
| 檔案位置 | .github/workflows/ |
.github/actions/{name}/ |
| 適用場景 | 完整的部署流程 | 重複的設定步驟 |
Secrets 與環境變數
Secrets 層級
Organization Secrets ← 所有 Repo 共用
↓
Repository Secrets ← 單一 Repo 內共用
↓
Environment Secrets ← 特定 Environment 內使用
使用方式
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
env:
# Secrets 透過 env 傳入
API_KEY: ${{ secrets.API_KEY }}
# 內建的 GITHUB_TOKEN(自動提供)
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./deploy.sh
- name: Use vars
env:
# Repository Variables(非加密,適合非敏感設定)
APP_NAME: ${{ vars.APP_NAME }}
run: echo "Deploying $APP_NAME"
Secrets vs Variables
| Secrets | Variables | |
|---|---|---|
| 加密 | ✅ 加密儲存 | ❌ 明文儲存 |
| Log 遮罩 | ✅ 自動遮罩 *** |
❌ 會顯示在 Log |
| 用途 | API Key、Token、密碼 | 環境名稱、版本號、功能開關 |
| 存取 | ${{ secrets.NAME }} |
${{ vars.NAME }} |
GITHUB_TOKEN
每次 Workflow 執行時自動產生的 Token,用來存取當前 Repository。
permissions:
contents: read
pull-requests: write
steps:
- name: Comment on PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '✅ CI 通過!'
})
快取與效能優化
依賴快取
# 方式 1:使用 setup-* Action 內建的 cache
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 自動快取 node_modules
# 方式 2:手動控制快取
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Artifact(建構產出物)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
# 上傳 Artifact
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7 # 保留天數(預設 90)
deploy:
needs: build
runs-on: ubuntu-latest
steps:
# 下載 Artifact
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- run: ./deploy.sh dist/
效能優化技巧
| 技巧 | 說明 | 節省效果 |
|---|---|---|
| 依賴快取 | 快取 node_modules、Go modules 等 | 安裝時間減少 50-80% |
| paths 過濾 | 只在相關檔案變更時觸發 | 減少不必要的執行 |
| concurrency | 取消進行中的舊 Workflow | 節省 Runner 時間 |
| 矩陣 fail-fast | 一個失敗就中止全部 | 快速回饋 |
| Job 平行化 | 無依賴的 Job 同時執行 | 總時間 = 最長的 Job |
| Docker layer cache | 快取 Docker 建構層 | 建構時間減少 60-90% |
| Artifact 傳遞 | Job 之間傳遞建構結果 | 避免重複建構 |
實戰範例
範例 1:完整的 CI Pipeline
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npx tsc --noEmit # 型別檢查
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
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: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
env:
DATABASE_URL: postgres://postgres:test@localhost:5432/testdb
- uses: codecov/codecov-action@v4
if: github.event_name == 'push'
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
範例 2:Docker 建構 + 多環境部署
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
tags: ['v*']
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-image:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha,prefix=
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-staging:
needs: build-image
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- name: Deploy to staging
run: |
echo "Deploying ${{ needs.build-image.outputs.image-tag }} to staging"
deploy-production:
needs: [build-image, deploy-staging]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- name: Deploy to production
run: |
echo "Deploying ${{ needs.build-image.outputs.image-tag }} to production"
範例 3:手動觸發 + 排程任務
# .github/workflows/maintenance.yml
name: Maintenance
on:
schedule:
- cron: '0 1 * * 1' # 每週一台灣時間 09:00
workflow_dispatch:
inputs:
task:
description: '執行任務'
required: true
type: choice
options:
- cleanup
- backup
- health-check
jobs:
maintenance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run task
run: |
TASK="${{ github.event.inputs.task || 'health-check' }}"
echo "Running: $TASK"
./scripts/maintenance.sh "$TASK"
- name: Notify
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "⚠️ 維護任務失敗:${{ github.event.inputs.task }}"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
範例 4:PR 自動化
# .github/workflows/pr-automation.yml
name: PR Automation
on:
pull_request:
types: [opened, synchronize, labeled]
permissions:
contents: read
pull-requests: write
jobs:
auto-label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
size-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check PR size
uses: actions/github-script@v7
with:
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const changes = files.reduce((sum, f) => sum + f.changes, 0);
if (changes > 500) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `⚠️ 這個 PR 有 ${changes} 行變更,建議拆分為更小的 PR。`,
});
}
安全性最佳實踐
1. Action 版本鎖定
# ✅ 推薦:使用 commit SHA(最安全)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# ✅ 可接受:使用主版本標籤
- uses: actions/checkout@v4
# ❌ 不推薦:使用 main 分支(可能被竄改)
- uses: actions/checkout@main
2. 最小權限原則
# Workflow 層級設定最小權限
permissions:
contents: read
jobs:
deploy:
permissions:
contents: read
id-token: write # 只在需要的 Job 開放
3. Secret 安全
# ❌ 不要在指令中直接使用 Secret
- run: curl -H "Authorization: ${{ secrets.API_KEY }}" https://api.example.com
# ✅ 透過環境變數傳入
- run: curl -H "Authorization: $API_KEY" https://api.example.com
env:
API_KEY: ${{ secrets.API_KEY }}
4. Fork PR 的安全考量
# 防止 Fork 的 PR 存取 Secrets
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
# 只在非 Fork PR 執行需要 Secrets 的操作
deploy-preview:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- run: ./deploy-preview.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
5. 安全檢查清單
| 項目 | 說明 |
|---|---|
| 鎖定 Action 版本 | 使用 SHA 或主版本標籤,避免 @main |
| 最小權限 | permissions 只開需要的 |
| 保護 Secrets | 不要在 run 中直接展開,用 env 傳入 |
| Fork 隔離 | 對 Fork PR 限制 Secrets 和敏感操作 |
| Branch Protection | 主分支要求 CI 通過才能合併 |
| Environment 審核 | Production 環境設定必要審核人 |
| OIDC | 雲端部署用 OIDC 取代長期 Token |
除錯與疑難排解
啟用 Debug Log
在 Repository → Settings → Secrets 新增:
| Secret | 值 | 用途 |
|---|---|---|
ACTIONS_RUNNER_DEBUG |
true |
Runner 層級的除錯日誌 |
ACTIONS_STEP_DEBUG |
true |
Step 層級的除錯日誌 |
本地除錯工具:act
# 安裝
brew install act
# 執行 Workflow
act push # 模擬 push 事件
act pull_request # 模擬 PR 事件
act -j build # 只執行特定 Job
act -l # 列出所有 Workflow 和 Job
act --secret-file .secrets # 使用本地 Secrets 檔案
常見除錯技巧
steps:
# 印出所有上下文資訊
- name: Debug context
run: |
echo "Event: ${{ github.event_name }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
# 印出完整 event payload
- name: Dump event
run: cat "$GITHUB_EVENT_PATH" | jq .
# 檢查檔案系統
- name: List files
run: |
pwd
ls -la
常見問題
問題 1:Workflow 沒有被觸發
可能原因:
- YAML 檔案不在
.github/workflows/目錄下 - YAML 語法錯誤
- 分支名稱不符合
branches過濾條件 paths過濾排除了變更的檔案- Workflow 被停用(Repository → Actions → 手動啟用)
排查步驟:
# 驗證 YAML 語法
yamllint .github/workflows/ci.yml
# 檢查分支名稱
git branch --show-current
問題 2:Permission denied 錯誤
症狀:
Error: Resource not accessible by integration
解決方案:
# 明確設定所需權限
permissions:
contents: read
pull-requests: write
issues: write
也可能需要在 Repository → Settings → Actions → General → Workflow permissions 中調整預設權限。
問題 3:快取沒有命中
可能原因:
key中使用的hashFiles()路徑不正確- 不同作業系統的快取不共用(
runner.os不同) - 快取超過 10 GB 上限被自動清除
建議:
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: | # 提供 fallback key
${{ runner.os }}-node-
問題 4:Job 之間無法共享檔案
原因:每個 Job 在獨立的 Runner 上執行,檔案系統不共享。
解決方案:使用 Artifact 傳遞:
# Job A:上傳
- uses: actions/upload-artifact@v4
with:
name: my-data
path: output/
# Job B:下載
- uses: actions/download-artifact@v4
with:
name: my-data
總結
核心概念速查
Workflow(.yml 檔案)
├── Event(觸發條件):push / PR / schedule / dispatch
├── Job(工作單元):在獨立 Runner 上執行
│ ├── Step:uses: Action 或 run: Shell
│ └── Step:支援 if 條件、output 傳遞
├── 可重用元件
│ ├── Reusable Workflow:完整流程重用
│ └── Composite Action:步驟組合重用
└── 安全
├── Secrets / Variables:加密 vs 明文
├── Permissions:最小權限原則
└── Environment:部署審核控制
語法速查
| 語法 | 用途 | 範例 |
|---|---|---|
on: |
觸發條件 | on: push: |
jobs: |
定義 Job | jobs: build: |
runs-on: |
指定 Runner | runs-on: ubuntu-latest |
uses: |
使用 Action | uses: actions/checkout@v4 |
run: |
Shell 指令 | run: npm test |
with: |
Action 參數 | with: node-version: '20' |
env: |
環境變數 | env: CI: true |
if: |
條件執行 | if: github.ref == 'refs/heads/main' |
needs: |
Job 依賴 | needs: [build, test] |
strategy.matrix: |
矩陣建構 | matrix: node: [18, 20] |
secrets: |
存取密鑰 | ${{ secrets.API_KEY }} |
permissions: |
權限控制 | permissions: contents: read |
concurrency: |
並行控制 | cancel-in-progress: true |
常用 Action 速查
| Action | 用途 |
|---|---|
actions/checkout@v4 |
檢出程式碼 |
actions/setup-node@v4 |
設定 Node.js |
actions/setup-go@v5 |
設定 Go |
actions/setup-java@v4 |
設定 Java |
actions/cache@v4 |
快取依賴 |
actions/upload-artifact@v4 |
上傳建構產出物 |
actions/download-artifact@v4 |
下載建構產出物 |
docker/build-push-action@v5 |
建構推送 Docker Image |
docker/login-action@v3 |
Docker Registry 登入 |
actions/github-script@v7 |
執行 GitHub API 腳本 |
建立日期:2026-04-10 最後更新:2026-04-10