diff --git a/.github/workflows/build-cli-e2e-image.yml b/.github/workflows/build-cli-e2e-image.yml index 6d1ec2a5a6d..bfd5c5050ae 100644 --- a/.github/workflows/build-cli-e2e-image.yml +++ b/.github/workflows/build-cli-e2e-image.yml @@ -10,6 +10,10 @@ on: required: false type: boolean default: true + uploadImageArtifacts: + required: false + type: boolean + default: true env: CLI_E2E_DOTNET_IMAGE_ARTIFACT: cli-e2e-dotnet-image @@ -123,6 +127,12 @@ jobs: fi fi + - name: Save CLI E2E Docker image tarballs + if: ${{ inputs.uploadImageArtifacts }} + shell: bash + run: | + set -euo pipefail + mkdir -p artifacts/cli-e2e-image docker save "$CLI_E2E_DOTNET_IMAGE_TAG" | gzip > artifacts/cli-e2e-image/aspire-cli-e2e-dotnet.tar.gz if [[ "$CLI_E2E_INCLUDE_POLYGLOT_IMAGES" == "true" ]]; then @@ -131,6 +141,7 @@ jobs: fi - name: Upload CLI E2E .NET Docker image + if: ${{ inputs.uploadImageArtifacts }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ env.CLI_E2E_DOTNET_IMAGE_ARTIFACT }} @@ -138,7 +149,7 @@ jobs: retention-days: 1 - name: Upload CLI E2E polyglot Docker image - if: ${{ inputs.includePolyglotImages }} + if: ${{ inputs.uploadImageArtifacts && inputs.includePolyglotImages }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ env.CLI_E2E_POLYGLOT_IMAGE_ARTIFACT }} @@ -146,7 +157,7 @@ jobs: retention-days: 1 - name: Upload CLI E2E Java Docker image - if: ${{ inputs.includePolyglotImages }} + if: ${{ inputs.uploadImageArtifacts && inputs.includePolyglotImages }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ env.CLI_E2E_POLYGLOT_JAVA_IMAGE_ARTIFACT }} diff --git a/.github/workflows/warm-cli-e2e-image-cache.yml b/.github/workflows/warm-cli-e2e-image-cache.yml new file mode 100644 index 00000000000..f332d07293f --- /dev/null +++ b/.github/workflows/warm-cli-e2e-image-cache.yml @@ -0,0 +1,40 @@ +# Warms the BuildKit cache used by pull request CLI E2E image builds. +name: Warm CLI E2E Image Cache + +on: + workflow_dispatch: + + schedule: + - cron: '30 5 * * *' # Daily at 05:30 UTC + + push: + branches: + - main + paths: + - '.github/workflows/build-cli-e2e-image.yml' + - '.github/workflows/warm-cli-e2e-image-cache.yml' + - '.dockerignore' + - 'eng/scripts/get-aspire-cli.sh' + - 'eng/scripts/get-aspire-cli-pr.sh' + - 'tests/Shared/Docker/Dockerfile.e2e' + - 'tests/Shared/Docker/Dockerfile.e2e-polyglot-base' + - 'tests/Shared/Docker/Dockerfile.e2e-polyglot-java' + - 'tests/Shared/Docker/NuGet.DotnetTool.config' + - 'tests/Shared/Docker/configure-ubuntu-apt-mirror.sh' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + warm_cli_e2e_image_cache: + name: Warm CLI E2E image cache + if: ${{ github.repository_owner == 'microsoft' }} + uses: ./.github/workflows/build-cli-e2e-image.yml + with: + includePolyglotImages: true + # Cache-warmer mode: this run only needs to seed the GHA BuildKit cache + # scopes that sibling PR builds restore from. The cache export happens + # during `docker buildx build` itself, so no tarball is needed. No test + # job consumes artifacts from this workflow, so skip the upload steps. + uploadImageArtifacts: false diff --git a/docs/ci/cli-e2e-images.md b/docs/ci/cli-e2e-images.md index 2c2cc8fae61..26d7f6d93a1 100644 --- a/docs/ci/cli-e2e-images.md +++ b/docs/ci/cli-e2e-images.md @@ -26,6 +26,21 @@ The image build workflow has an `includePolyglotImages` input. It defaults to `t Consumer workflows download image artifacts into `${{ github.workspace }}/cli-e2e-image` and call `eng/scripts/load-cli-e2e-images.sh` to load the images and export the matching environment variables. Regular split CLI E2E jobs always require DotNet and Polyglot images. Java image download and loading is conditional on Java test jobs to avoid transferring the larger Java tarball to every split job. +The reusable image build workflow also has an `uploadImageArtifacts` input. It defaults to `true` for test workflows because each isolated test job needs the image tarballs. Cache-warming workflows set it to `false` so they only build the images and export BuildKit cache layers. + +## Cross-PR BuildKit cache + +The image build workflow exports BuildKit layers to the GitHub Actions cache with stable scopes: + +| Variant | Cache scope | +| --- | --- | +| DotNet | `cli-e2e-dotnet` | +| Polyglot | `cli-e2e-polyglot-base` | + +GitHub Actions does not let one pull request restore cache entries written by a sibling pull request. Pull request runs can restore cache entries from the current PR ref and from the base/default branch. The `.github/workflows/warm-cli-e2e-image-cache.yml` workflow runs on `main` by schedule, manual dispatch, and relevant `main` pushes so new PRs can restore layers seeded from `main`. + +The Java image is built from the local polyglot base image and does not use a separate shared BuildKit cache scope. + ## Adding another variant When adding a new shared CLI E2E Dockerfile variant: