From ddd477d012ce9b19ac5280cf0fd9584bd0003c78 Mon Sep 17 00:00:00 2001 From: SmileQWQ Date: Tue, 5 May 2026 19:57:58 +0800 Subject: [PATCH] ci: build multi-arch images on native runners - split release metadata, web asset packaging and Docker image publishing into separate jobs - build amd64 images on ubuntu-latest and arm64 images on ubuntu-24.04-arm instead of QEMU emulation - push per-platform image digests and merge them into latest, release and sha manifests - enable GitHub Actions cache scopes for each image and platform build --- .github/workflows/build-and-release.yml | 192 ++++++++++++++++++------ 1 file changed, 142 insertions(+), 50 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index f3235c7..2dc1240 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -42,12 +42,11 @@ jobs: VITE_APP_VERSION: ${{ github.head_ref || github.ref_name }} run: npm run build - release: + release-metadata: if: github.event_name == 'release' runs-on: ubuntu-latest - permissions: - contents: write - packages: write + outputs: + release_tag: ${{ steps.release_tag.outputs.value }} steps: - name: Validate release tag @@ -61,10 +60,18 @@ jobs: fi echo "value=$TAG" >> "$GITHUB_OUTPUT" + release-assets: + if: github.event_name == 'release' + needs: release-metadata + runs-on: ubuntu-latest + permissions: + contents: write + + steps: - name: Checkout release tag uses: actions/checkout@v4 with: - ref: ${{ github.event.release.tag_name }} + ref: ${{ needs.release-metadata.outputs.release_tag }} fetch-depth: 0 - name: Setup Node.js @@ -87,7 +94,7 @@ jobs: - name: Build env: - VITE_APP_VERSION: ${{ steps.release_tag.outputs.value }} + VITE_APP_VERSION: ${{ needs.release-metadata.outputs.release_tag }} run: npm run build - name: Archive web dist @@ -95,6 +102,56 @@ jobs: cd apps/web/dist zip -r ../../../subtracker-web-dist.zip . + - name: Upload assets to GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.release-metadata.outputs.release_tag }} + files: | + subtracker-web-dist.zip + + docker-build: + if: github.event_name == 'release' + needs: release-metadata + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - image: api + image_name: ghcr.io/smile-qwq/subtracker-api + dockerfile: ./Dockerfile + platform: linux/amd64 + platform_pair: linux-amd64 + runner: ubuntu-latest + - image: api + image_name: ghcr.io/smile-qwq/subtracker-api + dockerfile: ./Dockerfile + platform: linux/arm64 + platform_pair: linux-arm64 + runner: ubuntu-24.04-arm + - image: web + image_name: ghcr.io/smile-qwq/subtracker-web + dockerfile: ./docker/web.Dockerfile + platform: linux/amd64 + platform_pair: linux-amd64 + runner: ubuntu-latest + - image: web + image_name: ghcr.io/smile-qwq/subtracker-web + dockerfile: ./docker/web.Dockerfile + platform: linux/arm64 + platform_pair: linux-arm64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + + steps: + - name: Checkout release tag + uses: actions/checkout@v4 + with: + ref: ${{ needs.release-metadata.outputs.release_tag }} + fetch-depth: 0 + - name: Login to GHCR uses: docker/login-action@v3 with: @@ -102,57 +159,92 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup QEMU - uses: docker/setup-qemu-action@v3 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push image by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.dockerfile }} + platforms: ${{ matrix.platform }} + build-args: | + VITE_APP_VERSION=${{ needs.release-metadata.outputs.release_tag }} + labels: | + org.opencontainers.image.source=https://github.com/Smile-QWQ/SubTracker + org.opencontainers.image.version=${{ needs.release-metadata.outputs.release_tag }} + org.opencontainers.image.revision=${{ github.sha }} + cache-from: type=gha,scope=${{ matrix.image }}-${{ matrix.platform_pair }} + cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.platform_pair }} + provenance: false + sbom: false + outputs: type=image,name=${{ matrix.image_name }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + shell: bash + run: | + mkdir -p "/tmp/digests/${{ matrix.image }}" + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${{ matrix.image }}/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.image }}-${{ matrix.platform_pair }} + path: /tmp/digests/${{ matrix.image }}/* + if-no-files-found: error + retention-days: 1 + + docker-merge: + if: github.event_name == 'release' + needs: + - release-metadata + - docker-build + runs-on: ubuntu-latest + permissions: + packages: write + strategy: + fail-fast: false + matrix: + include: + - image: api + image_name: ghcr.io/smile-qwq/subtracker-api + - image: web + image_name: ghcr.io/smile-qwq/subtracker-web + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-${{ matrix.image }}-* + merge-multiple: true + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Extract API Docker metadata - id: api_meta + - name: Extract Docker metadata + id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.API_IMAGE_NAME }} + images: ${{ matrix.image_name }} tags: | type=raw,value=latest - type=raw,value=${{ steps.release_tag.outputs.value }} + type=raw,value=${{ needs.release-metadata.outputs.release_tag }} type=sha - - name: Extract Web Docker metadata - id: web_meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.WEB_IMAGE_NAME }} - tags: | - type=raw,value=latest - type=raw,value=${{ steps.release_tag.outputs.value }} - type=sha - - - name: Build and push API Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.api_meta.outputs.tags }} - labels: ${{ steps.api_meta.outputs.labels }} - - - name: Build and push Web Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./docker/web.Dockerfile - platforms: linux/amd64,linux/arm64 - push: true - build-args: | - VITE_APP_VERSION=${{ steps.release_tag.outputs.value }} - tags: ${{ steps.web_meta.outputs.tags }} - labels: ${{ steps.web_meta.outputs.labels }} - - - name: Upload assets to GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ steps.release_tag.outputs.value }} - files: | - subtracker-web-dist.zip + - name: Create multi-architecture manifest + shell: bash + run: | + cd /tmp/digests + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ matrix.image_name }}@sha256:%s ' *)