GitHub Actions: Reusable Workflow 與 Composite Action 深度

深入比較 Reusable Workflow 與 Composite Action 兩種複用機制,涵蓋設計、輸入輸出、Secrets、版本管理與實戰範例

GitHub Actions: Reusable Workflow 與 Composite Action 深度

標籤:#DevOps #GitHub Actions #Reusable Workflow #Composite Action #CI/CD

深入比較 GitHub Actions 兩種複用機制,搞清楚哪一種該用、怎麼用、為什麼


目錄


為什麼需要複用?

當 organization 裡有 10 個以上的 repo,每個都需要相同的「lint → test → build → publish image」流程,用複製貼上的方式維護會變成災難:

  • 修一個 bug 要改 10 個 repo:升級 Node 版本、修改 cache key、改 Slack 通知 webhook
  • 流程容易飄移:有的 repo 用 Node 18、有的還在用 16,標準難以強制
  • 安全更新成本高:Action 版本要全部追蹤,寫死 SHA 後升級工作量倍增

GitHub Actions 提供兩種主要複用機制:

  1. Reusable Workflow:整個 workflow / job 複用
  2. Composite Action:多個 step 包成一個自訂 action

選錯機制會讓設計變得彆扭,所以先搞清楚兩者定位再開始寫。


三種複用機制總覽

機制 複用範圍 用途 典型場景
Reusable Workflow 整個 workflow 或 job 標準化整套 CI/CD pipeline 公司統一的 build/deploy 流程
Composite Action 多個 step 抽出可被嵌入既有 job 的步驟組 重複的 setup + cache + login 步驟
JavaScript/Docker Action 程式邏輯 寫真正的程式邏輯(可發到 Marketplace) 內部工具、外掛

本筆記聚焦在前兩種,JavaScript/Docker Action 留給單獨筆記。

簡單心法

整個 job 都一樣 → Reusable Workflow 幾個 step 重複 → Composite Action


Reusable Workflow 詳解

定義 Reusable Workflow

關鍵:on: workflow_call

# .github/workflows/reusable-go-build.yml
name: Reusable Go Build

on:
  workflow_call:
    inputs:
      go-version:
        description: "Go 版本"
        required: false
        type: string
        default: "1.22"
      os:
        description: "執行 runner OS"
        required: false
        type: string
        default: "ubuntu-latest"
    secrets:
      GORELEASER_TOKEN:
        required: false
    outputs:
      build-sha:
        description: "建置出的 commit SHA"
        value: ${{ jobs.build.outputs.sha }}

jobs:
  build:
    runs-on: ${{ inputs.os }}
    outputs:
      sha: ${{ steps.meta.outputs.sha }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ inputs.go-version }}
      - id: meta
        run: echo "sha=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
      - run: go build ./...
      - run: go test ./...

呼叫 Reusable Workflow

呼叫端使用 uses:,注意要寫在 jobs.<id> 層級,不是 steps 層級

# .github/workflows/ci.yml
name: CI

on:
  pull_request:
  push:
    branches: [main]

jobs:
  build:
    uses: my-org/ci-templates/.github/workflows/reusable-go-build.yml@v1
    with:
      go-version: "1.22"
      os: ubuntu-latest
    secrets:
      GORELEASER_TOKEN: ${{ secrets.GORELEASER_TOKEN }}

跨 Repo 呼叫的關鍵限制

來源 路徑語法
同 repo ./.github/workflows/file.yml(不指定 ref)
跨 repo owner/repo/.github/workflows/file.yml@ref
同 organization 內 與跨 repo 同,但 organization 設定可放寬權限

跨 repo 呼叫必須指定 ref(branch、tag、SHA),不能省略

多層巢狀的限制

Reusable Workflow 可以呼叫另一個 Reusable Workflow,但有層數限制:

caller-workflow.yml
└─> reusable-A.yml         # 第 1 層
    └─> reusable-B.yml     # 第 2 層
        └─> reusable-C.yml # 第 3 層
            └─> reusable-D.yml # 第 4 層(極限)

GitHub 官方目前允許最多 4 層巢狀,超過會直接報錯。建議實務上不要超過 2 層,debug 會非常痛苦。


Composite Action 詳解

定義 Composite Action

Composite Action 用 action.yml 定義,不是 workflow,所以放在獨立 repo 或 repo 內的子目錄都可以。

# .github/actions/setup-and-cache-go/action.yml
name: "Setup Go and Cache"
description: "Setup Go with module + build cache configured"

inputs:
  go-version:
    description: "Go 版本"
    required: false
    default: "1.22"
  cache-key-suffix:
    description: "Cache key 附加識別符"
    required: false
    default: "default"

outputs:
  cache-hit:
    description: "是否命中快取"
    value: ${{ steps.cache.outputs.cache-hit }}

runs:
  using: "composite"
  steps:
    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: ${{ inputs.go-version }}
    - name: Cache Go modules
      id: cache
      uses: actions/cache@v4
      with:
        path: |
          ~/.cache/go-build
          ~/go/pkg/mod
        key: ${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.cache-key-suffix }}-${{ hashFiles('**/go.sum') }}
    - name: Show Go version
      shell: bash
      run: go version

重點細節

  1. runs.using: "composite" 是 Composite Action 的識別
  2. 每個自訂 run: step 都必須指定 shell:(bashpwshshpython...)
  3. 不需要寫 shell: 的場景:當 step 用 uses: 引用其他 action 時
  4. 沒有 runs-on::Composite Action 跑在呼叫它的 job 的 runner 上

呼叫 Composite Action

# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup
        id: setup
        uses: ./.github/actions/setup-and-cache-go
        with:
          go-version: "1.22"
          cache-key-suffix: "test"
      - name: Show cache hit
        run: echo "cache hit = ${{ steps.setup.outputs.cache-hit }}"
      - run: go test ./...

跨 repo 引用:

      - uses: my-org/shared-actions/setup-go@v2

Composite Action 沒有的東西

Composite Action 不能:

  • 宣告 secrets:(只能透過 inputs: 傳入)
  • 指定獨立的 runs-on:
  • 定義 if: 條件來控制整個 action 是否執行(只能在呼叫端做)
  • 使用 matrix(matrix 是 job 層級)

這些限制都是因為 Composite Action 本質上是「被嵌入 job 的 step 群」。


Reusable Workflow vs Composite Action

並列比較表

比較項目 Reusable Workflow Composite Action
檔案位置 .github/workflows/*.yml action.yml(任意路徑)
觸發方式 on: workflow_call runs.using: composite
複用單位 整個 workflow / job 一組 steps
執行 runner 自己指定 runs-on 跑在呼叫它的 job 上
能用 matrix? ✅ 可以,呼叫端傳 matrix ❌ 不能(matrix 在 job 層)
能用 secrets? ✅ 宣告 secrets: 區塊 ❌ 只能透過 inputs:
能多個 job? ✅ 可以(整個 workflow) ❌ 只是 steps
權限控制 ✅ 獨立 permissions: 繼承呼叫端
Marketplace? ❌ 不能發佈 ✅ 可以發佈
顯示在 Actions 頁面? ✅ 獨立顯示 ❌ 被嵌入呼叫端的 step log

怎麼選?決策樹

要複用什麼?
├─ 整套 CI/CD pipeline(含 runner / 環境 / 多個 job)
│  └─ Reusable Workflow
├─ 一個 job 的完整內容(在指定 runner 跑一系列事)
│  └─ Reusable Workflow(更乾淨)
├─ 幾個 step 序列(setup + cache + login 三步)
│  └─ Composite Action
└─ 想做成可發佈的工具(別人也能用)
   └─ Composite Action(可發 Marketplace)

容易踩坑的選擇

錯誤示範:把「checkout + setup-node + npm install」做成 Reusable Workflow

# ❌ 這只是 3 個 step,不該做成 Reusable Workflow
jobs:
  setup:
    uses: ./.github/workflows/just-setup.yml
  test:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - run: npm test  # 但 setup 在另一個 job 跑,這裡又要重新 checkout...

問題:Reusable Workflow 被當作獨立 job 執行,跑完後檔案系統不會傳到下一個 job。要傳 artifacts 又得用 upload-artifact / download-artifact,反而更累贅。這種情境應該用 Composite Action。

正確做法:

# .github/actions/setup-node-deps/action.yml
runs:
  using: "composite"
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: "20"
    - run: npm ci
      shell: bash
# 使用
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-node-deps
      - run: npm test

輸入輸出與 Secrets

Inputs 型別差異

Reusable Workflow 的 inputs 必須指定 type(string / boolean / number):

on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true
      enable-feature-x:
        type: boolean
        default: false
      replicas:
        type: number
        default: 3

Composite Action 的 inputs 沒有 type(全部都是 string):

inputs:
  environment:
    required: true
    description: "deploy 環境"
  enable-feature-x:
    default: "false"  # 字串

要在 Composite Action 處理 boolean 輸入時要小心:

# Composite Action 內
- name: Maybe do something
  if: inputs.enable-feature-x == 'true'  # 字串比較!不是 boolean
  shell: bash
  run: echo "feature enabled"

Outputs 傳遞

Reusable Workflow Outputs

需要兩層宣告:job 層 outputworkflow 層 output

# Reusable workflow 內
on:
  workflow_call:
    outputs:
      image-tag:
        description: "built image tag"
        value: ${{ jobs.build.outputs.tag }}  # 從 job output 取

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.meta.outputs.tag }}  # 從 step 取
    steps:
      - id: meta
        run: echo "tag=v1.2.3" >> "$GITHUB_OUTPUT"
# 呼叫端取用
jobs:
  build:
    uses: ./.github/workflows/build.yml
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "deploying ${{ needs.build.outputs.image-tag }}"

Composite Action Outputs

直接從 step 取值:

# action.yml
outputs:
  result:
    value: ${{ steps.calc.outputs.value }}

runs:
  using: "composite"
  steps:
    - id: calc
      shell: bash
      run: echo "value=42" >> "$GITHUB_OUTPUT"
# 呼叫端
- id: my-action
  uses: ./.github/actions/calc
- run: echo "got ${{ steps.my-action.outputs.result }}"

Secrets 傳遞

Reusable Workflow 三種傳遞方式

1. 明確列出

jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    secrets:
      AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
      AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}

2. secrets: inherit(全部繼承)

jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    secrets: inherit

inherit 只在同 repo / 同 organization 呼叫時可用,且 organization 設定要允許

3. 跨 repo 必須明確傳遞

跨 repo 的 reusable workflow 不能用 secrets: inherit,必須一個一個列出。

Composite Action 沒有 secrets

Composite Action 只能用 inputs 接收,呼叫端要明確傳:

- uses: ./.github/actions/deploy
  with:
    aws-key: ${{ secrets.AWS_ACCESS_KEY }}  # 把 secret 當 input 傳

⚠️ 注意:傳給 Composite Action 的 secret 在 log 還是會被自動 mask,但是只要 secret 出現在 action.yml 的 inputs.default 或 hardcoded 字串中,就會洩漏。


版本管理策略

三種引用方式

方式 範例 安全性 維護性
Branch @main ❌ 低 ⚠️ 變動快
Tag @v1@v1.2.3 ⚠️ 中(tag 可被移動) ✅ 好
Full SHA @a1b2c3d... ✅ 高 ❌ 升級麻煩

對外部 Action 的建議

# ❌ 危險:branch 隨時會變
- uses: some-org/some-action@main

# ⚠️ 一般:可接受,信任維護者
- uses: some-org/some-action@v3

# ✅ 安全:鎖死 SHA,但要記得用 Dependabot 升級
- uses: some-org/some-action@a1b2c3d4e5f6789...  # v3.1.0

GitHub 推薦的折衷做法:寫 full SHA + 註解寫 tag

- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

這樣 Dependabot 可以同時識別 tag 和升 SHA。

對內部 Reusable Workflow 的策略

公司內部 reusable workflow 建議:

  1. 主要 caller 使用 major tag(@v1)
  2. 發佈 minor / patch tag(@v1.2.0)
  3. 同時維護 floating tag(讓 v1 指向最新的 v1.x)
# 發佈新版本
git tag v1.2.0
git tag -f v1  # 強制更新 floating tag
git push origin v1.2.0
git push origin v1 --force

呼叫端:

# 自動拿到 v1 系列最新版
uses: my-org/ci-templates/.github/workflows/build.yml@v1

權限與安全

Reusable Workflow 的 permissions

# 在 reusable workflow 內宣告
on: workflow_call
permissions:
  contents: read
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    # ...

呼叫端的 permissions 必須大於等於 reusable workflow 需要的權限,否則會失敗:

# 呼叫端
permissions:
  contents: read
  packages: write  # 必須給,否則 reusable workflow 拿不到
jobs:
  build:
    uses: ./.github/workflows/publish.yml

Composite Action 沒有獨立 permissions

Composite Action 繼承呼叫端 job 的 permissions。要做敏感操作時,需要呼叫端的 workflow 明確開啟對應權限。

Organization 級別的限制

公司可以限制哪些 reusable workflow / action 可以被使用:

Organization Settings → Actions → General → Allowed actions

✅ Allow {org} actions and reusable workflows
✅ Allow actions created by GitHub
✅ Allow specified actions:
   - actions/checkout@*
   - docker/login-action@v3.*.*
   - aws-actions/configure-aws-credentials@v4

這個設定能避免有人偷偷用未稽核的 action。


實戰範例

範例 1:統一的多語言 CI 模板(Reusable Workflow)

# my-org/ci-templates/.github/workflows/standard-ci.yml
name: Standard CI

on:
  workflow_call:
    inputs:
      language:
        type: string
        required: true  # go | node | python
      version:
        type: string
        required: true
      run-tests:
        type: boolean
        default: true
      coverage-threshold:
        type: number
        default: 80
    secrets:
      CODECOV_TOKEN:
        required: false

jobs:
  setup-and-test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        if: inputs.language == 'go'
        uses: actions/setup-go@v5
        with:
          go-version: ${{ inputs.version }}

      - name: Setup Node
        if: inputs.language == 'node'
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.version }}
          cache: "npm"

      - name: Setup Python
        if: inputs.language == 'python'
        uses: actions/setup-python@v5
        with:
          python-version: ${{ inputs.version }}

      - name: Install deps
        run: |
          case "${{ inputs.language }}" in
            go) go mod download ;;
            node) npm ci ;;
            python) pip install -r requirements.txt ;;
          esac

      - name: Run tests
        if: inputs.run-tests
        run: |
          case "${{ inputs.language }}" in
            go) go test -race -coverprofile=coverage.out ./... ;;
            node) npm test -- --coverage ;;
            python) pytest --cov ;;
          esac

      - name: Upload coverage
        if: inputs.run-tests && env.CODECOV_TOKEN != ''
        uses: codecov/codecov-action@v4
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

呼叫端:

# my-org/some-go-repo/.github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  ci:
    uses: my-org/ci-templates/.github/workflows/standard-ci.yml@v1
    with:
      language: go
      version: "1.22"
      coverage-threshold: 85
    secrets:
      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

範例 2:共用 Docker 推送步驟(Composite Action)

# my-org/shared-actions/build-and-push/action.yml
name: "Build and Push Docker Image"
description: "Build multi-arch image and push to registry"

inputs:
  registry:
    required: true
    description: "registry hostname"
  image-name:
    required: true
  tag:
    required: false
    default: "latest"
  registry-username:
    required: true
  registry-password:
    required: true
  platforms:
    required: false
    default: "linux/amd64,linux/arm64"

outputs:
  image-digest:
    value: ${{ steps.push.outputs.digest }}

runs:
  using: "composite"
  steps:
    - uses: docker/setup-qemu-action@v3
    - uses: docker/setup-buildx-action@v3

    - name: Login
      uses: docker/login-action@v3
      with:
        registry: ${{ inputs.registry }}
        username: ${{ inputs.registry-username }}
        password: ${{ inputs.registry-password }}

    - name: Build and push
      id: push
      uses: docker/build-push-action@v5
      with:
        platforms: ${{ inputs.platforms }}
        push: true
        tags: ${{ inputs.registry }}/${{ inputs.image-name }}:${{ inputs.tag }}

呼叫端:

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - id: img
        uses: my-org/shared-actions/build-and-push@v2
        with:
          registry: ghcr.io
          image-name: ${{ github.repository }}
          tag: ${{ github.sha }}
          registry-username: ${{ github.actor }}
          registry-password: ${{ secrets.GITHUB_TOKEN }}
      - run: echo "digest = ${{ steps.img.outputs.image-digest }}"

範例 3:Reusable Workflow + Composite Action 組合

複雜場景常會混用:

# my-org/ci-templates/.github/workflows/full-deploy.yml
on:
  workflow_call:
    inputs:
      environment: { type: string, required: true }

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.img.outputs.image-digest }}
    steps:
      - uses: actions/checkout@v4
      - id: img
        uses: my-org/shared-actions/build-and-push@v2  # Composite Action
        with:
          registry: ghcr.io
          image-name: ${{ github.repository }}
          tag: ${{ github.sha }}
          registry-username: ${{ github.actor }}
          registry-password: ${{ secrets.GITHUB_TOKEN }}

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: my-org/shared-actions/k8s-deploy@v2  # 另一個 Composite Action
        with:
          image: ${{ needs.build.outputs.image }}
          env: ${{ inputs.environment }}

最佳實踐

1. 命名規則

.github/workflows/
├── ci.yml                       # 一般 workflow
├── deploy.yml
└── _reusable-build.yml          # 底線開頭表示是被呼叫的

.github/actions/
├── setup-go/
│   └── action.yml
└── deploy-to-k8s/
    └── action.yml

底線開頭只是慣例(GitHub 沒強制),但讓人一眼看出這是「不會被自動觸發」的 reusable workflow。

2. 輸入要有 default 與 description

inputs:
  environment:
    type: string
    required: true
    description: "Deploy 目標環境(staging | production)"
  timeout-minutes:
    type: number
    required: false
    default: 30
    description: "整個 deploy 流程的超時(分鐘)"

description 不只是文件,GitHub UI 在 workflow_dispatch 觸發時會顯示。

3. 不要在 Reusable Workflow 內寫死 runner

# ❌ 寫死,無法在 self-hosted 跑
jobs:
  build:
    runs-on: ubuntu-latest

# ✅ 把 runner 開放成 input
jobs:
  build:
    runs-on: ${{ inputs.runner-os }}

4. 限制 Reusable Workflow 的 trigger

# 純 reusable workflow 只要 workflow_call
on:
  workflow_call:
    ...

# 同時想手動觸發?加上 workflow_dispatch
on:
  workflow_call:
    inputs:
      environment: { type: string, required: true }
  workflow_dispatch:
    inputs:
      environment: { type: choice, options: [staging, production] }

注意 workflow_callworkflow_dispatch 的 inputs schema 可以共用,但要重複寫一次。

5. 避免在 Composite Action 內寫死 path

# ❌ 寫死路徑
- shell: bash
  run: ls /github/workspace/dist

# ✅ 用 GITHUB_WORKSPACE
- shell: bash
  run: ls "$GITHUB_WORKSPACE/dist"

6. 測試 Reusable Workflow

Reusable Workflow 沒辦法獨立執行(只能被呼叫),建議:

  1. 同 repo 內建立 test-reusable.yml 來測試
  2. workflow_dispatch 觸發手動驗證
  3. 改完後先發 pre-release tag(v1.2.0-rc.1),caller 試用沒問題再正式發

常見問題

Q1: Reusable Workflow 呼叫失敗,提示 "secret not provided"

原因:reusable workflow 內的 secrets: 區塊宣告了 required: true,但呼叫端沒傳。

解法:

# 呼叫端
jobs:
  build:
    uses: ./.github/workflows/build.yml
    secrets:
      MY_SECRET: ${{ secrets.MY_SECRET }}  # 補上

或在 reusable workflow 內改 required: false,內部自己判斷有沒有提供。


Q2: Composite Action 內的 run: 為什麼一定要 shell?

原因:GitHub Actions 規定 Composite Action 的每個 run: step 必須明確指定 shell,因為 Composite Action 可能在不同 OS 上跑,不能自動推測。

runs:
  using: "composite"
  steps:
    - run: echo hello
      shell: bash     # 必填!

Q3: 為什麼 secrets: inherit 不能用?

可能原因:

  1. 跨 repo 呼叫:inherit 只在同 repo / 同 org 可用
  2. Organization 設定限制:org 管理員可以禁用 inherit
  3. Public repo 呼叫 private repo 的 workflow:有額外限制

檢查:Organization Settings → Actions → General → Workflow permissions


Q4: Reusable Workflow 一直顯示「Startup failure」

常見原因:

  1. 語法錯誤:on: 沒有 workflow_call
  2. 路徑寫錯:同 repo 引用要用 ./.github/workflows/x.yml,跨 repo 要 owner/repo/.github/workflows/x.yml@ref
  3. 權限不足:呼叫端的 permissions: 不夠
  4. 巢狀過深:超過 4 層 reusable workflow

Q5: Composite Action 內可以呼叫 Reusable Workflow 嗎?

不行。Composite Action 是 step 序列,但 Reusable Workflow 是 job / workflow 層級。要把 reusable workflow 嵌入既有 job 是不可能的。

反過來 OK:Reusable Workflow 內可以用 Composite Action。


Q6: Output 取不到值,顯示空字串

原因:Reusable Workflow 的 output 必須兩層宣告:

on:
  workflow_call:
    outputs:
      result:
        value: ${{ jobs.work.outputs.r }}  # 第 1 層:workflow output

jobs:
  work:
    outputs:
      r: ${{ steps.s.outputs.x }}  # 第 2 層:job output
    steps:
      - id: s
        run: echo "x=hello" >> "$GITHUB_OUTPUT"  # step output

少了任何一層,呼叫端 needs.<job>.outputs.result 都會是空。


Q7: 怎麼在 PR 上看到 Reusable Workflow 的 step 細節?

Reusable Workflow 跑起來會在 Actions 頁面獨立顯示(雖然由 caller 觸發)。

如果是 Composite Action,step 會被攤平到 caller 的 job log 內,但會用 [action-name] 前綴標示。


總結

核心要點

  • 整套 pipeline 要複用 → Reusable Workflow(workflow_call)
  • 幾個 step 要複用 → Composite Action(runs.using: composite)
  • 不要混淆:Reusable Workflow 是獨立 job,Composite Action 是嵌入 step
  • 跨 repo 必須鎖版本:用 tag 或 SHA,不要用 branch
  • secrets 處理:Reusable Workflow 有 secrets: 區塊和 inherit,Composite 只能靠 inputs
  • 權限傳遞:Reusable Workflow 有獨立 permissions:,但 caller 要給足夠權限

快速決策

情境 用哪個
全公司統一的 build/test/deploy Reusable Workflow
需要 matrix / 多個 job Reusable Workflow
抽出「setup + cache + login」三步 Composite Action
想發到 Marketplace Composite Action
在既有 job 中插入幾個 step Composite Action
需要獨立 runner / 環境 / 權限 Reusable Workflow

速查

# Reusable Workflow 骨架
on:
  workflow_call:
    inputs:    { name: { type: string, required: true } }
    secrets:   { TOKEN: { required: true } }
    outputs:   { result: { value: ${{ jobs.x.outputs.r }} } }

jobs:
  x:
    runs-on: ubuntu-latest
    outputs: { r: ${{ steps.s.outputs.v }} }
    steps: ...

# Composite Action 骨架
name: My Action
inputs:    { name: { required: true, default: "x" } }
outputs:   { result: { value: ${{ steps.s.outputs.v }} } }
runs:
  using: composite
  steps:
    - shell: bash
      run: echo "v=hi" >> "$GITHUB_OUTPUT"
      id: s

建立日期:2026-05-25

🔗相關文章