From 1749be99d52a78355a1c5af1177b380e5c019163 Mon Sep 17 00:00:00 2001 From: "Jiaxiao (mossaka) Zhou" Date: Mon, 16 Feb 2026 18:35:12 +0000 Subject: [PATCH] perf: parallelize container image builds in release workflow Split the monolithic build-and-release job into 6 parallel jobs: - setup: Extract version from tag/package.json - build-squid: Build + push + sign + SBOM squid image (amd64+arm64) - build-agent: Build + push + sign + SBOM agent image (amd64+arm64) - build-api-proxy: Build + push + sign + SBOM api-proxy image (amd64+arm64) - build-agent-act: Build + push + sign + SBOM agent-act image (amd64 only) - release: Create binaries, release notes, and GitHub Release All 4 image builds run in parallel after setup, and the release job waits for all builds to complete. This significantly reduces total release time since each multi-arch build takes several minutes, especially with ARM64 QEMU emulation. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/release.yml | 147 ++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35b9ad649..43e67fe99 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,28 +12,24 @@ permissions: id-token: write # Required for cosign keyless signing jobs: - build-and-release: - name: Build and Release + setup: + name: Extract Version runs-on: ubuntu-latest - + outputs: + version: ${{ steps.version.outputs.version }} + version_number: ${{ steps.version.outputs.version_number }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 - name: Setup Node.js + if: github.event_name == 'workflow_dispatch' uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: '22' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build TypeScript - run: npm run build - name: Extract version from tag - id: version_early + id: version run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then VERSION=$(node -p "require('./package.json').version") @@ -46,6 +42,14 @@ jobs: echo "version_number=$VERSION_NUMBER" >> $GITHUB_OUTPUT fi + build-squid: + name: Build Squid Image + runs-on: ubuntu-latest + needs: setup + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + - name: Log in to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 with: @@ -72,7 +76,7 @@ jobs: push: true platforms: linux/amd64,linux/arm64 tags: | - ghcr.io/${{ github.repository }}/squid:${{ steps.version_early.outputs.version_number }} + ghcr.io/${{ github.repository }}/squid:${{ needs.setup.outputs.version_number }} ghcr.io/${{ github.repository }}/squid:latest cache-from: type=gha,scope=squid cache-to: type=gha,mode=max,scope=squid @@ -96,6 +100,32 @@ jobs: --type spdxjson \ ghcr.io/${{ github.repository }}/squid@${{ steps.build_squid.outputs.digest }} + build-agent: + name: Build Agent Image + runs-on: ubuntu-latest + needs: setup + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + with: + platforms: arm64 + + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + - name: Build and push Agent image id: build_agent uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 @@ -104,7 +134,7 @@ jobs: push: true platforms: linux/amd64,linux/arm64 tags: | - ghcr.io/${{ github.repository }}/agent:${{ steps.version_early.outputs.version_number }} + ghcr.io/${{ github.repository }}/agent:${{ needs.setup.outputs.version_number }} ghcr.io/${{ github.repository }}/agent:latest # Disable cache for agent image to ensure security-critical packages # (like libcap2-bin for capability dropping) are always freshly installed @@ -129,6 +159,32 @@ jobs: --type spdxjson \ ghcr.io/${{ github.repository }}/agent@${{ steps.build_agent.outputs.digest }} + build-api-proxy: + name: Build API Proxy Image + runs-on: ubuntu-latest + needs: setup + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + with: + platforms: arm64 + + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + - name: Build and push API Proxy image id: build_api_proxy uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 @@ -137,7 +193,7 @@ jobs: push: true platforms: linux/amd64,linux/arm64 tags: | - ghcr.io/${{ github.repository }}/api-proxy:${{ steps.version_early.outputs.version_number }} + ghcr.io/${{ github.repository }}/api-proxy:${{ needs.setup.outputs.version_number }} ghcr.io/${{ github.repository }}/api-proxy:latest cache-from: type=gha,scope=api-proxy cache-to: type=gha,mode=max,scope=api-proxy @@ -161,8 +217,29 @@ jobs: --type spdxjson \ ghcr.io/${{ github.repository }}/api-proxy@${{ steps.build_api_proxy.outputs.digest }} - # Build agent-act image with catthehacker/ubuntu:act-24.04 base for GitHub Actions parity - # amd64-only: catthehacker/ubuntu:act-24.04 does not publish arm64 manifests + # Build agent-act image with catthehacker/ubuntu:act-24.04 base for GitHub Actions parity + # amd64-only: catthehacker/ubuntu:act-24.04 does not publish arm64 manifests + build-agent-act: + name: Build Agent-Act Image + runs-on: ubuntu-latest + needs: setup + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + - name: Build and push Agent-Act image id: build_agent_act uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 @@ -171,7 +248,7 @@ jobs: push: true platforms: linux/amd64 tags: | - ghcr.io/${{ github.repository }}/agent-act:${{ steps.version_early.outputs.version_number }} + ghcr.io/${{ github.repository }}/agent-act:${{ needs.setup.outputs.version_number }} ghcr.io/${{ github.repository }}/agent-act:latest build-args: | BASE_IMAGE=ghcr.io/catthehacker/ubuntu:act-24.04 @@ -196,6 +273,26 @@ jobs: --type spdxjson \ ghcr.io/${{ github.repository }}/agent-act@${{ steps.build_agent_act.outputs.digest }} + release: + name: Create Release + runs-on: ubuntu-latest + needs: [setup, build-squid, build-agent, build-api-proxy, build-agent-act] + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + + - name: Setup Node.js + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build TypeScript + run: npm run build + - name: Install pkg for binary creation run: npm install -g pkg @@ -219,7 +316,7 @@ jobs: run: | npx tsx scripts/ci/smoke-test-binary.ts \ release/awf-linux-x64 \ - ${{ steps.version_early.outputs.version_number }} + ${{ needs.setup.outputs.version_number }} - name: Create tarball for npm package run: | @@ -235,7 +332,7 @@ jobs: id: previous_tag run: | set -euo pipefail - CURRENT_TAG="${{ steps.version_early.outputs.version }}" + CURRENT_TAG="${{ needs.setup.outputs.version }}" # Use git tags directly (more reliable than gh release list) # Get the most recent tag that is not the current tag @@ -248,7 +345,7 @@ jobs: id: changelog run: | set -euo pipefail - CURRENT_TAG="${{ steps.version_early.outputs.version }}" + CURRENT_TAG="${{ needs.setup.outputs.version }}" PREVIOUS_TAG="${{ steps.previous_tag.outputs.previous_tag }}" echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" @@ -310,8 +407,8 @@ jobs: - name: Create Release Notes id: release_notes env: - VERSION: ${{ steps.version_early.outputs.version }} - VERSION_NUMBER: ${{ steps.version_early.outputs.version_number }} + VERSION: ${{ needs.setup.outputs.version }} + VERSION_NUMBER: ${{ needs.setup.outputs.version_number }} REPOSITORY: ${{ github.repository }} run: | set -euo pipefail @@ -338,11 +435,11 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 with: - tag_name: ${{ steps.version_early.outputs.version }} - name: Release ${{ steps.version_early.outputs.version }} + tag_name: ${{ needs.setup.outputs.version }} + name: Release ${{ needs.setup.outputs.version }} body_path: release_notes.md draft: false - prerelease: ${{ contains(steps.version_early.outputs.version, 'alpha') || contains(steps.version_early.outputs.version, 'beta') || contains(steps.version_early.outputs.version, 'rc') }} + prerelease: ${{ contains(needs.setup.outputs.version, 'alpha') || contains(needs.setup.outputs.version, 'beta') || contains(needs.setup.outputs.version, 'rc') }} files: | release/awf-linux-x64 release/awf.tgz