diff --git a/.changeset/docker-layer-caching.md b/.changeset/docker-layer-caching.md new file mode 100644 index 00000000..3b4d84b1 --- /dev/null +++ b/.changeset/docker-layer-caching.md @@ -0,0 +1,7 @@ +--- +'@cloudflare/sandbox': patch +--- + +Add cache mounts to Dockerfile for faster builds + +Adds cache mounts for npm, apt, and pip package managers in the Dockerfile. This speeds up Docker image builds when dependencies change, particularly beneficial for users building from source. diff --git a/.github/workflows/pkg-pr-new.yml b/.github/workflows/pkg-pr-new.yml index da272cb8..2b1b6807 100644 --- a/.github/workflows/pkg-pr-new.yml +++ b/.github/workflows/pkg-pr-new.yml @@ -15,7 +15,7 @@ on: jobs: publish-preview: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 steps: - name: Checkout code @@ -73,7 +73,20 @@ jobs: password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Build and push Docker image (preview) - run: npm run docker:publish --workspace=@cloudflare/sandbox + uses: docker/build-push-action@v6 + with: + context: . + file: packages/sandbox/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: cloudflare/sandbox:${{ steps.package-version.outputs.version }} + cache-from: | + type=gha,scope=preview-pr-${{ github.event.pull_request.number }} + type=gha,scope=pr-${{ github.event.pull_request.number }} + type=gha,scope=release + cache-to: type=gha,mode=max,scope=preview-pr-${{ github.event.pull_request.number }} + build-args: | + SANDBOX_VERSION=${{ steps.package-version.outputs.version }} - name: Publish to pkg.pr.new run: npx pkg-pr-new publish './packages/sandbox' diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index a6d520a0..1a024bc1 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -15,6 +15,8 @@ jobs: unit-tests: timeout-minutes: 10 runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-version.outputs.version }} steps: - uses: actions/checkout@v4 @@ -33,6 +35,12 @@ jobs: - name: Build packages run: npm run build + - name: Get package version + id: get-version + run: | + VERSION=$(node -p "require('./packages/sandbox/package.json').version") + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Run sandbox unit tests run: | set +e @@ -64,6 +72,7 @@ jobs: # E2E tests against deployed worker e2e-tests: + needs: unit-tests timeout-minutes: 30 runs-on: ubuntu-latest steps: @@ -102,9 +111,23 @@ jobs: cd tests/e2e/test-worker ./generate-config.sh ${{ steps.env-name.outputs.worker_name }} - # Build Docker image for test worker (needed before deployment) + # Build Docker image for test worker with caching + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build test worker Docker image - run: npm run docker:local -w @cloudflare/sandbox + uses: docker/build-push-action@v6 + with: + context: . + file: packages/sandbox/Dockerfile + load: true # Load into Docker daemon for local testing + tags: cloudflare/sandbox-test:${{ needs.unit-tests.outputs.version || '0.0.0' }} + cache-from: | + type=gha,scope=pr-${{ github.event.pull_request.number }} + type=gha,scope=release + cache-to: type=gha,mode=max,scope=pr-${{ github.event.pull_request.number }} + build-args: | + SANDBOX_VERSION=${{ needs.unit-tests.outputs.version || '0.0.0' }} # Deploy test worker using official Cloudflare action - name: Deploy test worker diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 82a791d6..ec123771 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,8 @@ jobs: if: ${{ github.repository_owner == 'cloudflare' }} runs-on: ubuntu-latest timeout-minutes: 10 + outputs: + version: ${{ steps.get-version.outputs.version }} steps: - uses: actions/checkout@v4 @@ -34,6 +36,12 @@ jobs: - name: Build packages run: npm run build + - name: Get package version + id: get-version + run: | + VERSION=$(node -p "require('./packages/sandbox/package.json').version") + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Run sandbox unit tests run: | set +e @@ -106,7 +114,17 @@ jobs: password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Build and push Docker image (beta) - run: npm run docker:publish:beta --workspace=@cloudflare/sandbox + uses: docker/build-push-action@v6 + with: + context: . + file: packages/sandbox/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: cloudflare/sandbox:${{ needs.unit-tests.outputs.version }}-beta + cache-from: type=gha,scope=release + cache-to: type=gha,mode=max,scope=release + build-args: | + SANDBOX_VERSION=${{ needs.unit-tests.outputs.version }} - name: Publish to npm with beta tag run: npm publish --tag beta --access public @@ -153,7 +171,17 @@ jobs: password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Build and push Docker image - run: npm run docker:publish --workspace=@cloudflare/sandbox + uses: docker/build-push-action@v6 + with: + context: . + file: packages/sandbox/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: cloudflare/sandbox:${{ needs.unit-tests.outputs.version }} + cache-from: type=gha,scope=release + cache-to: type=gha,mode=max,scope=release + build-args: | + SANDBOX_VERSION=${{ needs.unit-tests.outputs.version }} - id: changesets uses: changesets/action@v1 diff --git a/packages/sandbox/Dockerfile b/packages/sandbox/Dockerfile index 67a1de8d..d445cfb3 100644 --- a/packages/sandbox/Dockerfile +++ b/packages/sandbox/Dockerfile @@ -29,8 +29,9 @@ WORKDIR /app COPY --from=pruner /app/out/json/ . COPY --from=pruner /app/out/package-lock.json ./package-lock.json -# Install ALL dependencies (including devDependencies for build) -RUN npm ci +# Install ALL dependencies with cache mount for npm packages +RUN --mount=type=cache,target=/root/.npm \ + npm ci # Copy pruned source code COPY --from=pruner /app/out/full/ . @@ -53,7 +54,8 @@ COPY --from=builder /app/packages ./packages COPY --from=builder /app/tooling ./tooling # Install ONLY production dependencies (excludes typescript, @types/*, etc.) -RUN npm ci --production +RUN --mount=type=cache,target=/root/.npm \ + npm ci --production # ============================================================================ # Stage 4: Runtime - Ubuntu 22.04 with only runtime dependencies @@ -69,8 +71,12 @@ ENV DEBIAN_FRONTEND=noninteractive # Set the sandbox version as an environment variable for version checking ENV SANDBOX_VERSION=${SANDBOX_VERSION} -# Install essential runtime packages -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Install essential runtime packages with cache mounts +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + rm -f /etc/apt/apt.conf.d/docker-clean && \ + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache && \ + apt-get update && apt-get install -y --no-install-recommends \ curl \ wget \ ca-certificates \ @@ -82,8 +88,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ unzip \ zip \ jq \ - file \ - && rm -rf /var/lib/apt/lists/* + file # Set Python 3.11 as default python3 RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 @@ -96,8 +101,9 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ # Install Bun runtime from official image COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun -# Install essential Python packages for code execution -RUN pip3 install --no-cache-dir \ +# Install essential Python packages with cache mount +RUN --mount=type=cache,target=/root/.cache/pip \ + pip3 install \ matplotlib \ numpy \ pandas \