还在因为构建太久太耗时而担忧吗,试试这个~ (迪拉熊音)
https://github.blog/changelog/2026-06-25-actions-steps-can-now-be-run-in-parallel/
TL;DR : 在你需要并行的前面加 - parallel: 关键词,加缩进即可
以下原文:
GitHub Actions now supports running steps concurrently using background.
GitHub Actions 现在支持使用 background 并行运行步骤。
Previously, all steps in a workflow ran in sequence, with each step starting only after the previous step completed. Previously, you could run steps in a non-blocking way using shell backgrounding (&), but that often interleaved logs from multiple steps. This new capability enables steps to run in parallel while retaining separate logs and execution.
以前,工作流中的所有步骤都是按顺序运行的,每个步骤只有在前一个步骤完成后才会开始。以前,你可以使用 shell 背景运行来以非阻塞方式运行步骤( & ),但这通常会交错多个步骤的日志。这项新功能允许步骤并行运行,同时保留单独的日志和执行。
Four new keywords are being introduced:
正在引入四个新关键字:
background: true runs a step asynchronously and immediately continues to the next step.
background: true 异步运行一个步骤,并立即继续到下一个步骤。
wait/wait-all pauses execution until one or more named background steps complete. wait can target one or more specific background steps, while wait-all pauses until all prior background steps have completed.
wait / wait-all 暂停执行,直到一个或多个命名的后台步骤完成。 wait 可以针对一个或多个特定的后台步骤,而 wait-all 暂停,直到所有先前的后台步骤都已完成。
cancel gracefully terminates a background step when you no longer need it, enabling you to start long-running services with a background step.
cancel 在您不再需要它时优雅地终止一个后台步骤,使您能够使用后台步骤启动长时间运行的服务。
parallel takes a group of steps and converts them to background steps with a wait after, enabling you to easily run multiple steps in parallel. This is syntactic sugar for the “run these steps concurrently, then continue” pattern.
parallel 将一组步骤转换为带有 wait 后缀的 background 步骤,使您能够轻松地并行运行多个步骤。这是“运行这些步骤并发,然后继续”模式的语法糖。
This update helps you handle common patterns in a single job:
此更新帮助您在一个作业中处理常见模式:
Running independent work in true parallel, such as multiple builds at once
并行执行独立工作,例如同时进行多个构建
Starting a background service, running dependent work, then stopping that service cleanly
启动后台服务,运行依赖工作,然后干净地停止该服务
Kicking off non-blocking work that can continue while later steps run, like uploading telemetry while packaging continues
启动非阻塞工作,该工作可以在后续步骤运行时继续进行,例如在打包过程中上传遥测数据
Read the GitHub Actions workflow syntax documentation for current syntax and usage details.
请查阅 GitHub Actions 工作流语法文档以获取当前语法和使用详情。
以下技术文档:
jobs.<job_id>.steps[*].background
不贴原文了:
示例:
steps:
- name: Start server
id: server
run: npm start
background: true
- name: Run tests against the server
run: npm test
- name: Wait for the server step to finish
wait: server
jobs.<job_id>.steps[*].wait
很像needs啊
steps:
- name: Build frontend
id: build-frontend
run: npm run build:frontend
background: true
- name: Build backend
id: build-backend
run: npm run build:backend
background: true
- name: Run linter while builds run
run: npm run lint
- name: Wait for both builds to finish
wait: [build-frontend, build-backend]
- name: Run tests
run: npm test
jobs.<job_id>.steps[*].wait-all
阻塞锁(?
steps:
- name: Start database
id: db
run: docker run -d postgres:15
background: true
- name: Start cache
id: cache
run: docker run -d redis:7
background: true
- name: Run integration tests
run: npm run test:integration
- name: Wait for all services to stop
wait-all:
jobs.<job_id>.steps[*].cancel
用于取消并行工作流
steps:
- name: Start long-running monitor
id: monitor
run: ./scripts/monitor.sh
background: true
- name: Run the main task
run: npm test
- name: Stop the monitor
cancel: monitor
jobs.<job_id>.steps[*].parallel
最简单的写法,也容易看懂
steps:
- uses: actions/checkout@v6
- parallel:
- name: Build frontend
run: npm run build:frontend
- name: Build backend
run: npm run build:backend
- name: Build docs
run: npm run build:docs
- name: Run tests after all builds complete
run: npm test
The group above is equivalent to declaring each step with background: true followed by a wait step.
上面的组相当于声明每个步骤以 background: true 开头,后跟一个 wait 步骤。
限制:最多并发十个steps
下附ce 462工作流供参考:
name: .NET Build & Package
on:
push:
branches: [net462]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-${{ github.head_ref || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build-vsto:
name: Build VSTO
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Cache VSTO build output
id: cache-vsto
uses: actions/cache@v5
with:
path: |
InkCanvas.PowerPointAddIn/bin/Release
InkCanvas.PPTAgent.Contracts/bin/Release
key: vsto-${{ hashFiles('InkCanvas.PowerPointAddIn/**') }}
- parallel:
- name: Setup dotnet
if: steps.cache-vsto.outputs.cache-hit != 'true'
uses: actions/setup-dotnet@v5
- name: Setup MSBuild
if: steps.cache-vsto.outputs.cache-hit != 'true'
uses: microsoft/setup-msbuild@v3
- name: Install VSTO Runtime
if: steps.cache-vsto.outputs.cache-hit != 'true'
shell: pwsh
run: |
$url = "https://go.microsoft.com/fwlink/?LinkId=158918"
$out = "$env:TEMP\VSTORuntime.exe"
Invoke-WebRequest -Uri $url -OutFile $out -MaximumRedirection 10
Start-Process -FilePath $out -ArgumentList "/quiet /norestart" -Wait -NoNewWindow
Write-Host "VSTO Runtime installed"
- parallel:
- name: Restore Package
if: steps.cache-vsto.outputs.cache-hit != 'true'
run: dotnet restore "Ink Canvas.sln"
- name: Restore signing certificate
if: steps.cache-vsto.outputs.cache-hit != 'true'
shell: pwsh
run: |
$ok = $false
if ("${{ secrets.CERT_PFX_BASE64 }}" -ne "") {
try {
$bytes = [Convert]::FromBase64String("${{ secrets.CERT_PFX_BASE64 }}")
[IO.File]::WriteAllBytes("clickonce.pfx", $bytes)
$pwd = ConvertTo-SecureString "${{ secrets.CERT_PFX_PASSWORD }}" -AsPlainText -Force
Import-PfxCertificate -FilePath "clickonce.pfx" -CertStoreLocation Cert:\CurrentUser\My -Password $pwd | Out-Null
$ok = $true
Write-Host "Certificate imported from secrets"
} catch {
Write-Host "Import PFX failed, falling back to self-signed: $($_.Exception.Message)"
}
}
if (-not $ok) {
New-SelfSignedCertificate `
-Type CodeSigningCert `
-Subject "CN=ICC PowerPoint Agent CI" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-NotAfter (Get-Date).AddYears(2) | Out-Null
Write-Host "Generated self-signed certificate"
}
$found = Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.HasPrivateKey -and $_.Subject -like "*ICC*" } | Select-Object -First 1
if (-not $found) { throw "No signing certificate found" }
echo "SIGN_THUMBPRINT=$($found.Thumbprint)" >> $env:GITHUB_ENV
Write-Host "Signing certificate ready: $($found.Thumbprint)"
- name: Build VSTO Add-in
if: steps.cache-vsto.outputs.cache-hit != 'true'
shell: pwsh
run: |
msbuild "InkCanvas.PowerPointAddIn/InkCanvas.PowerPointAddIn.csproj" `
/p:Configuration=Release `
/p:SignManifests=true `
/p:ManifestCertificateThumbprint=$env:SIGN_THUMBPRINT `
/verbosity:minimal -maxcpucount
if (Test-Path "InkCanvas.PowerPointAddIn\bin\Release\InkCanvas.PowerPointAddIn.dll") {
Write-Host "VSTO DLL OK"
} else {
Write-Error "VSTO DLL not found"
exit 1
}
build-and-package:
name: Build & Package
runs-on: windows-latest
needs: [build-vsto]
strategy:
fail-fast: false
matrix:
architecture: [AnyCPU, x86, arm64]
minimize: [true, false]
steps:
- parallel:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Setup dotnet
uses: actions/setup-dotnet@v5
- parallel:
- name: Restore Package
run: dotnet restore "Ink Canvas.sln"
- name: Cache VSTO build output
id: cache-vsto-restore
if: matrix.minimize != 'true'
uses: actions/cache@v5
with:
path: |
InkCanvas.PowerPointAddIn/bin/Release
InkCanvas.PPTAgent.Contracts/bin/Release
key: vsto-${{ hashFiles('InkCanvas.PowerPointAddIn/**')}}
- name: Build the Solution
env:
DLASS_TELEMETRY_TOKEN: ${{ secrets.DLASS_TELEMETRY_TOKEN }}
run: |
$platform = "${{ matrix.architecture }}"
if ($platform -eq "AnyCPU") { $platform = "Any CPU" }
dotnet build -c Release -p:platform="$platform" -p:GitFlow="$GITFLOW" -p:Minimize="${{ matrix.minimize }}" --no-restore -m
- name: Build & Upload
id: build-result
shell: pwsh
run: |
$arch = "${{ matrix.architecture }}".Replace(" ", "")
$exePath = "Ink Canvas\bin\Release\$arch\net462\InkCanvasForClass.exe"
if (Test-Path $exePath) {
echo "build_success=true" >> $env:GITHUB_OUTPUT
} else {
echo "build_success=false" >> $env:GITHUB_OUTPUT
if ("${{ github.event_name }}" -eq "workflow_dispatch") {
exit 1
}
}
- parallel:
- name: Upload Artifact
if: steps.build-result.outputs.build_success == 'true'
uses: actions/upload-artifact@v7
with:
name: InkCanvasForClass.CE.${{ matrix.architecture }}${{ case(matrix.minimize == true, '.minimize', '') }}
path: "Ink Canvas/bin/Release/${{ matrix.architecture }}/net462/*"
compression-level: 9
- name: Create Summary
if: always()
env:
MINIMIZE: ${{ matrix.minimize }}
shell: bash
run: |
SHORT_SHA=${GITHUB_SHA::7}
VERSION="debug-${SHORT_SHA}-${GITHUB_RUN_NUMBER}"
ARCH="${{ matrix.architecture }}"
[ "$MINIMIZE" = "true" ] && SUFFIX=".minimize" || SUFFIX=""
ARTIFACT="InkCanvasForClass.CE.${ARCH}${SUFFIX}"
echo "# Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.build-result.outputs.build_success }}" = "true" ]; then
echo "## ✅ Build Successful" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.create-archive.outputs.archive_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Architecture:** ${{ matrix.architecture }}" >> $GITHUB_STEP_SUMMARY
echo "**Minimize:** $MINIMIZE" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[Download Artifact](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$MINIMIZE" = "true" ]; then
suffix=".minimize"
else
suffix=""
fi
echo "[Nightly.link Download](https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.${{ matrix.architecture }}$suffix.zip) \([GhProxy Fastly Mirror](https://cdn.gh-proxy.com/nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.${{ matrix.architecture }}$suffix.zip) / [GhProxy Mirror](https://gh-proxy.com/nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/InkCanvasForClass.CE.${{ matrix.architecture }}$suffix.zip)\)" >> $GITHUB_STEP_SUMMARY
else
echo "## ❌ Build Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Event:** ${{ github.event_name }} (${{ github.event.action || 'N/A' }})" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Architecture:** ${{ matrix.architecture }}" >> $GITHUB_STEP_SUMMARY
echo "**Minimize:** $MINIMIZE" >> $GITHUB_STEP_SUMMARY
echo "**Run:** #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Check build logs for details." >> $GITHUB_STEP_SUMMARY
fi