From dc9d4e2b9ca7063da465cae7672322ad4d3a15a0 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 6 May 2026 00:14:05 +0800 Subject: [PATCH] perf(docker): parallelize multi-arch build and improve layer caching Split the single docker job into a matrix (amd64/arm64) that builds in parallel, then merge via docker-manifest job. Also restructure the Dockerfile to pre-compile dependencies with dummy sources so the dep layer is cached by GHA when only application code changes. --- .github/workflows/release.yml | 68 ++++++++++++++++++++++++++++------- Dockerfile | 26 ++++++++++---- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d909e25257..8c7eb22006 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,7 +108,7 @@ jobs: args: --target ${{ matrix.target }} publish: - needs: [build, docker] + needs: [build, docker-manifest] runs-on: ubuntu-latest steps: - name: Publish draft release @@ -318,6 +318,10 @@ jobs: docker: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: [linux/amd64, linux/arm64] steps: - uses: actions/checkout@v4 @@ -333,18 +337,58 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + platforms: ${{ matrix.platform }} + outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/dbx,push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ matrix.platform }} + cache-to: type=gha,scope=${{ matrix.platform }},mode=max + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + docker-manifest: + needs: docker + runs-on: ubuntu-latest + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Extract version from tag id: version run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/dbx:${{ steps.version.outputs.version }} - ${{ secrets.DOCKERHUB_USERNAME }}/dbx:latest - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + -t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:${{ steps.version.outputs.version }} \ + -t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:latest \ + $(printf '${{ secrets.DOCKERHUB_USERNAME }}/dbx@sha256:%s ' *) diff --git a/Dockerfile b/Dockerfile index d7e7faf123..5ffc000fd1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,17 +23,31 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu \ && rm -rf /var/lib/apt/lists/* +# Copy dependency manifests only and create dummy sources to pre-compile dependencies. +# This layer is cached by GHA as long as Cargo.toml/Cargo.lock don't change. COPY Cargo.toml Cargo.lock ./ -COPY crates/ crates/ -COPY src-web/ src-web/ -RUN mkdir -p src-tauri/src && echo 'fn main() {}' > src-tauri/src/main.rs && echo 'pub fn run() {}' > src-tauri/src/lib.rs +COPY crates/dbx-core/Cargo.toml crates/dbx-core/ +COPY src-web/Cargo.toml src-web/ COPY src-tauri/Cargo.toml src-tauri/ +RUN mkdir -p crates/dbx-core/src && echo '' > crates/dbx-core/src/lib.rs \ + && mkdir -p src-web/src && echo 'fn main() {}' > src-web/src/main.rs \ + && mkdir -p src-tauri/src && echo 'fn main() {}' > src-tauri/src/main.rs && echo 'pub fn run() {}' > src-tauri/src/lib.rs + COPY src-tauri/build.rs src-tauri/ COPY src-tauri/tauri.conf.json src-tauri/ -RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-$TARGETARCH \ - --mount=type=cache,target=/app/target,id=cargo-target-$TARGETARCH \ - case "$TARGETARCH" in \ +RUN case "$TARGETARCH" in \ + amd64) rust_target=x86_64-unknown-linux-gnu ;; \ + arm64) rust_target=aarch64-unknown-linux-gnu ;; \ + *) echo "Unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \ + esac && \ + cargo zigbuild --release -p dbx-web --target "$rust_target" || true + +# Copy real sources and rebuild (only application code recompiles) +COPY crates/ crates/ +COPY src-web/ src-web/ + +RUN case "$TARGETARCH" in \ amd64) rust_target=x86_64-unknown-linux-gnu ;; \ arm64) rust_target=aarch64-unknown-linux-gnu ;; \ *) echo "Unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \