如何在单个矩阵元素上运行GitHub Actions工作流?
背景
GitHub Actions中,跨多操作系统运行工作流的标准方式是使用matrix策略:
jobs: package: strategy: matrix: os: [ubuntu, macos, windows] runs-on: ${{ matrix.os }}-latest
但实际场景中,我们经常需要只在单个操作系统上运行工作流(比如调试阶段,或者仅针对某一OS的代码变更)。直接注释matrix里的元素容易出错,拆分多个工作流又会增加维护复杂度——尤其是存在build->package->deploy这类链式工作流时,每个环节都要做多个变体。
尝试过的方案及问题
1. 下拉选择输入(choice)
尝试通过workflow_dispatch的选择输入指定OS,但多选时会被识别为单个字符串,无法正常解析为matrix数组:
on: workflow_dispatch: inputs: os: type: choice options: - windows - macos - ubuntu - ubuntu, macos, windows required: true jobs: package: strategy: matrix: os: "${{ github.event.inputs.os }}" runs-on: ${{ matrix.os }}-latest
单个OS选择时正常,但选多个时会把"ubuntu, macos, windows"当作一个OS名称,导致运行失败。
2. 布尔值选择输入
用布尔值勾选要运行的OS,但只能拆分成独立Job,无法复用matrix的优势:
on: workflow_dispatch: inputs: ubuntu: required: true type: boolean macos: required: true type: boolean windows: required: true type: boolean jobs: ubuntu: if: ${{ inputs.ubuntu }} runs-on: ubuntu-latest steps: ... macos: if: ${{ inputs.macos}} runs-on: macos-latest steps: ... windows: if: ${{ inputs.windows}} runs-on: windows-latest steps: ...
标准实现方案
方案1:利用fromJSON解析字符串数组
通过选择输入传递数组格式的字符串,再用fromJSON转换为matrix可识别的数组,同时支持单个和多个OS选择:
on: workflow_dispatch: inputs: os: type: choice options: - '["windows"]' - '["macos"]' - '["ubuntu"]' - '["ubuntu", "macos", "windows"]' required: true default: '["ubuntu", "macos", "windows"]' jobs: package: strategy: matrix: os: ${{ fromJSON(github.event.inputs.os) }} runs-on: ${{ matrix.os }}-latest steps: - name: 示例步骤 run: echo "运行在 ${{ matrix.os }}"
这种方式既保留了选择输入的便捷性,又能让matrix正确解析OS列表,单个和多选场景都能正常工作。
方案2:结合布尔输入和前置Job生成matrix数组
如果需要更灵活的任意组合选择(而非固定选项),可以先通过一个前置Job根据布尔输入生成OS数组,再传递给后续Job的matrix:
on: workflow_dispatch: inputs: ubuntu: type: boolean default: true macos: type: boolean default: true windows: type: boolean default: true jobs: generate-matrix: runs-on: ubuntu-latest outputs: os-list: ${{ steps.set-matrix.outputs.os-list }} steps: - id: set-matrix run: | OS_LIST=() ${{ inputs.ubuntu == 'true' }} && OS_LIST+=("ubuntu") ${{ inputs.macos == 'true' }} && OS_LIST+=("macos") ${{ inputs.windows == 'true' }} && OS_LIST+=("windows") echo "os-list=$(jq -c . <<< "${OS_LIST[@]}")" >> "$GITHUB_OUTPUT" package: needs: generate-matrix strategy: matrix: os: ${{ fromJSON(needs.generate-matrix.outputs.os-list) }} runs-on: ${{ matrix.os }}-latest steps: - name: 示例步骤 run: echo "运行在 ${{ matrix.os }}"
这种方式支持任意OS组合选择,同时复用了matrix的优势,避免重复编写Job逻辑。
方案3:支持调用方工作流传递参数
如果需要从其他工作流调用并传递OS参数,可以通过workflow_call的输入定义,结合fromJSON实现:
on: workflow_dispatch: inputs: os: type: string default: '["ubuntu", "macos", "windows"]' workflow_call: inputs: os: type: string default: '["ubuntu", "macos", "windows"]' jobs: package: strategy: matrix: os: ${{ fromJSON(inputs.os) }} runs-on: ${{ matrix.os }}-latest steps: - name: 示例步骤 run: echo "运行在 ${{ matrix.os }}"
调用方工作流可以传递单个OS的字符串数组(如'["ubuntu"]')或多个OS的数组,实现灵活控制。
工作流可重用性优化
对于build->package->deploy这类链式工作流,可以将每个环节封装为可重用的工作流(使用workflow_call),然后在主工作流中通过上述方式生成matrix,再传递给各个子工作流,无需为每个OS组合创建重复的工作流变体。
主工作流示例:
on: workflow_dispatch: inputs: os: type: choice options: - '["windows"]' - '["macos"]' - '["ubuntu"]' - '["ubuntu", "macos", "windows"]' required: true jobs: build: uses: ./.github/workflows/build.yml with: os: ${{ inputs.os }} package: needs: build uses: ./.github/workflows/package.yml with: os: ${{ inputs.os }} deploy: needs: package uses: ./.github/workflows/deploy.yml with: os: ${{ inputs.os }}
子工作流(如build.yml)示例:
on: workflow_call: inputs: os: type: string jobs: build: strategy: matrix: os: ${{ fromJSON(inputs.os) }} runs-on: ${{ matrix.os }}-latest steps: - name: 构建步骤 run: echo "在 ${{ matrix.os }} 上构建"
这种结构既保证了灵活性,又极大提升了工作流的可维护性和可重用性。
内容的提问来源于stack exchange,提问作者user1803551

