如何用嵌套矩阵在GitHub Actions中批量触发任务?求替代方案
问题出在哪?
你当前的read-file任务用了矩阵,会同时跑两个实例分别处理两个JSON文件,但GitHub Actions有个坑——带矩阵的job输出变量时,只会保留最后一个实例的输出。所以check任务最后只拿到第二个文件的2个对象,自然只执行2次。
解决方案一:一次性生成完整对象矩阵(最简洁)
不需要嵌套矩阵,直接在第一个任务里读取所有JSON文件,把所有对象合并成一个数组,生成完整矩阵传给后续任务:
jobs: generate-object-matrix: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - id: set-matrix run: | # 读取所有products下的JSON文件,合并所有数组元素为一个大数组 ALL_OBJECTS=$(jq -s 'add' products/*.json) # 输出符合矩阵要求的格式(外层需要是键值对) echo "::set-output name=matrix::{\"objects\": $ALL_OBJECTS}" check: needs: generate-object-matrix runs-on: ubuntu-latest strategy: fail-fast: false matrix: object: ${{ fromJson(needs.generate-object-matrix.outputs.matrix).objects }} steps: - run: | echo "处理对象:" echo "名称: ${{ matrix.object.name }}" echo "版本: ${{ matrix.object.version }}" # 这里替换成你的实际任务逻辑
这个方案会让check任务自动执行4次,每个实例对应一个JSON对象。
解决方案二:嵌套矩阵(通过中间任务收集所有输出)
如果一定要保留“先列文件→再读文件→最后处理对象”的嵌套逻辑,需要加一个任务收集read-file所有实例的输出,再合并成完整矩阵:
jobs: list-products: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - id: set-matrix run: | FILES=$(ls products/*.json | jq -R -s -c 'split("\n")[:-1]') echo "::set-output name=matrix::$FILES" read-file: needs: list-products runs-on: ubuntu-latest strategy: matrix: manifest: ${{ fromJson(needs.list-products.outputs.matrix) }} outputs: objects: ${{ steps.read_file.outputs.contents }} steps: - uses: actions/checkout@v2 - id: read_file uses: andstor/file-reader-action@v1 with: path: ${{ matrix.manifest }} collect-objects: needs: read-file runs-on: ubuntu-latest outputs: matrix: ${{ steps.collect.outputs.matrix }} steps: - uses: actions/github-script@v6 id: collect with: script: | # 获取当前工作流的所有任务实例 const jobs = await github.rest.actions.listJobsForWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.runId }); # 筛选出read-file的任务实例,提取每个实例的objects输出 const readFileJobs = jobs.data.jobs.filter(job => job.name === 'read-file'); let allObjects = []; for (const job of readFileJobs) { if (job.outputs.objects) { const objects = JSON.parse(job.outputs.objects); allObjects = allObjects.concat(objects); } } # 返回合并后的矩阵格式 return {"objects": allObjects}; result-encoding: json check: needs: collect-objects runs-on: ubuntu-latest strategy: fail-fast: false matrix: object: ${{ fromJson(needs.collect-objects.outputs.matrix).objects }} steps: - run: | echo "处理对象:" echo "名称: ${{ matrix.object.name }}" echo "版本: ${{ matrix.object.version }}"
这个方案通过collect-objects任务把两个read-file实例的输出合并,最终传给check任务执行4次。
替代方案:单任务循环处理(无需矩阵)
如果不需要并行处理对象,直接在一个任务里循环处理所有对象即可:
jobs: process-all-objects: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: | # 合并所有JSON文件的对象到一个数组 ALL_OBJECTS=$(jq -s 'add' products/*.json) # 循环遍历每个对象执行任务 echo "$ALL_OBJECTS" | jq -c '.[]' | while read -r obj; do echo "===== 处理当前对象 =====" NAME=$(echo "$obj" | jq -r '.name') VERSION=$(echo "$obj" | jq -r '.version') echo "名称: $NAME" echo "版本: $VERSION" # 这里添加你的任务逻辑 done
这个方案不需要矩阵,适合不需要并行执行的场景,一次任务完成所有对象的处理。
内容的提问来源于stack exchange,提问作者Hari Prasath
相关产品推荐
相关产品推荐

