From 4b405143f4c7be800dd37ac7f6461ae7b9c355ab Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 10:20:43 -0700 Subject: [PATCH 01/10] Optimize Dockerfile layer caching and add .dockerignore - Reorder build stage: install OS tools first, then copy only manifest files (*.csproj, package.json/lock, NuGet.config, Directory.*.props, eng/), run dotnet restore, then COPY all source. Routine code edits no longer bust the tool-install and restore layers. - Add --mount=type=cache for /root/.nuget/packages (both restore and publish) and /root/.npm (build-js.sh) to speed up local builds. - Combine separate tdnf install commands into one RUN in both stages, avoiding unnecessary layer overhead. - Add .dockerignore to exclude .git, **/obj, **/bin, **/dist, **/node_modules, TestResults, artifacts, and *.tar. The **/obj exclusion is important: local dotnet restore writes project.assets.json there; without ignoring it those files could overwrite the container's restore output and cause silent mismatches. - Add cache-from: type=gha to the PR Docker build step so pull requests can read from the cache written by main-branch builds, avoiding full rebuilds on every PR. PRs do not write cache to avoid consuming the 10 GB quota with per-branch entries. --- .dockerignore | 9 ++++ .github/workflows/Build-Test-And-Deploy.yaml | 1 + Dockerfile | 54 ++++++++++++++------ 3 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..31008ebbf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +**/node_modules +**/obj +**/bin +**/dist +TestResults +artifacts +*.tar +.copilot diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index 07255243e..c3330d733 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -58,6 +58,7 @@ jobs: push: false tags: temp-pr-validation file: ./Dockerfile + cache-from: type=gha # Only build for dev registry — prod gets the image via az acr import in deploy-production - name: Build Container Image diff --git a/Dockerfile b/Dockerfile index a4721c59b..191e918a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,48 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 AS build-env WORKDIR /App -# Copy everything -COPY . ./ - # Make sure we run bash CMD ["bash"] -# Make sure we get all the updates and tools we need to build -RUN tdnf install gawk -y -# This is Node v16. For 18, use nodejs18. -RUN tdnf install nodejs -y -RUN tdnf install npm -y -RUN tdnf clean all +# Install all required build tools in a single layer (rarely changes — stays cached) +# This is Node v16. For 18, use nodejs18. +RUN tdnf install -y gawk nodejs npm && tdnf clean all + +# Copy only the files needed to restore dependencies. +# These layers are cached until a manifest file changes, so routine source edits +# don't re-run the expensive restore steps below. +COPY NuGet.config global.json Directory.Build.props Directory.Build.targets Directory.Packages.props TryDotNet.sln ./ +COPY eng/ ./eng/ + +# .csproj files — one COPY per project to preserve directory structure +COPY src/Microsoft.TryDotNet/Microsoft.TryDotNet.csproj src/Microsoft.TryDotNet/ +COPY src/Microsoft.TryDotNet.FileIntegration.Tests/Microsoft.TryDotNet.FileIntegration.Tests.csproj src/Microsoft.TryDotNet.FileIntegration.Tests/ +COPY src/Microsoft.TryDotNet.IntegrationTests/Microsoft.TryDotNet.IntegrationTests.csproj src/Microsoft.TryDotNet.IntegrationTests/ +COPY src/Microsoft.TryDotNet.SimulatorGenerator/Microsoft.TryDotNet.SimulatorGenerator.csproj src/Microsoft.TryDotNet.SimulatorGenerator/ +COPY src/Microsoft.TryDotNet.Tests/Microsoft.TryDotNet.Tests.csproj src/Microsoft.TryDotNet.Tests/ +COPY src/Microsoft.TryDotNet.WasmRunner/Microsoft.TryDotNet.WasmRunner.csproj src/Microsoft.TryDotNet.WasmRunner/ -# Build javascript library -RUN /App/build-js.sh +# npm manifests +COPY src/microsoft-trydotnet/package.json src/microsoft-trydotnet/package-lock.json src/microsoft-trydotnet/ +COPY src/microsoft-trydotnet-editor/package.json src/microsoft-trydotnet-editor/package-lock.json src/microsoft-trydotnet-editor/ +COPY src/microsoft-trydotnet-styles/package.json src/microsoft-trydotnet-styles/package-lock.json src/microsoft-trydotnet-styles/ +COPY src/microsoft-learn-mock/package.json src/microsoft-learn-mock/package-lock.json src/microsoft-learn-mock/ + +# Restore NuGet packages. The cache mount persists the package cache across local +# builds; in CI the layer itself is cached by the GHA cache backend. +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet restore --configfile /App/NuGet.config /App/TryDotNet.sln + +# Copy all remaining source (changes frequently — only layers below rebuild on edits) +COPY . ./ -# Restore -RUN dotnet restore --configfile /App/NuGet.config /App/TryDotNet.sln +# Build javascript library. The npm cache mount speeds up repeated local builds. +RUN --mount=type=cache,target=/root/.npm \ + /App/build-js.sh # Build and publish a release -RUN dotnet publish -c Release -o out /App/src/Microsoft.TryDotNet +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet publish -c Release -o out /App/src/Microsoft.TryDotNet # Build runtime image FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 @@ -31,9 +52,8 @@ WORKDIR /App # Make sure we run bash CMD ["bash"] -# Make sure we get all the tools we need -RUN tdnf install procps -y -RUN tdnf clean all +# Install runtime tools in a single layer +RUN tdnf install -y procps && tdnf clean all # Copy from build image COPY --from=build-env /App/out . From 08724397558b9a21cd07f6d679398c26bf2c56f1 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 11:09:51 -0700 Subject: [PATCH 02/10] Add buildkit-cache-dance to persist RUN --mount=type=cache in GHA Use reproducible-containers/buildkit-cache-dance@v3 to serialize the BuildKit cache mounts (/root/.nuget/packages and /root/.npm) into actions/cache between runs. On ephemeral GHA runners these mounts were previously empty at the start of every build; now they are warm on every run where dependencies haven't changed. Changes: - Add id: setup-buildx to the setup-buildx action step (required for the builder name output used by cache-dance). - Add actions/cache@v4 step with a key that includes hashes of Dockerfile, Directory.Packages.props, NuGet.config, and all package-lock.json files. restore-keys provides a warm fallback when only a subset of those files changes. - Add reproducible-containers/buildkit-cache-dance@v3 step before the Docker builds. skip-extraction is set from the cache-hit output so extraction is skipped when the key is an exact hit (actions/cache will not overwrite an existing entry with the same key anyway). - Add .buildkit-cache and scratch to .dockerignore. The cache-dance action populates .buildkit-cache with serialized NuGet and npm package data (can exceed 1 GB) and uses scratch as a temp dir; both must be excluded from the Docker build context. --- .dockerignore | 2 ++ .github/workflows/Build-Test-And-Deploy.yaml | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.dockerignore b/.dockerignore index 31008ebbf..753cb3b3b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,5 @@ TestResults artifacts *.tar .copilot +.buildkit-cache +scratch diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index c3330d733..4309ae21a 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -49,6 +49,24 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 + id: setup-buildx + + - name: Cache Docker build mounts + uses: actions/cache@v4 + id: cache-mounts + with: + path: .buildkit-cache + key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} + restore-keys: | + buildkit-cache- + + - name: Inject Docker cache mounts + uses: reproducible-containers/buildkit-cache-dance@v3 + with: + builder: ${{ steps.setup-buildx.outputs.name }} + cache-dir: .buildkit-cache + dockerfile: Dockerfile + skip-extraction: ${{ steps.cache-mounts.outputs.cache-hit }} # Build but no push with a PR - name: Docker build (no push) From 8f9b48509e312e4dd99db8f6ada323e2780b80dd Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 11:49:23 -0700 Subject: [PATCH 03/10] Fix gaps found in cross-repo comparison - Add '# syntax=docker/dockerfile:1' header to Dockerfile to explicitly pin the BuildKit frontend (required recommendation when using --mount=type=cache). - Add --mount=type=cache,target=/var/cache/tdnf,sharing=locked to both tdnf install RUN instructions. The cache mount keeps downloaded RPMs out of the image layer without needing tdnf clean all; remove those clean calls so the persistent cache is not wiped after each install. - Add permissions: actions: write to the build-and-test job. docker/build-push-action requires this permission to write to the GitHub Actions cache (type=gha). Without it cache writes fail silently. --- .github/workflows/Build-Test-And-Deploy.yaml | 3 +++ Dockerfile | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index 4309ae21a..c1c949f1e 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -15,6 +15,9 @@ jobs: build-and-test: runs-on: ubuntu-latest environment: "BuildAndUploadImage" + permissions: + actions: write # required for docker/build-push-action GHA cache writes + contents: read steps: - uses: actions/checkout@v6 diff --git a/Dockerfile b/Dockerfile index 191e918a5..3ffd0ccf0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 AS build-env WORKDIR /App @@ -6,7 +7,8 @@ CMD ["bash"] # Install all required build tools in a single layer (rarely changes — stays cached) # This is Node v16. For 18, use nodejs18. -RUN tdnf install -y gawk nodejs npm && tdnf clean all +RUN --mount=type=cache,target=/var/cache/tdnf,sharing=locked \ + tdnf install -y gawk nodejs npm # Copy only the files needed to restore dependencies. # These layers are cached until a manifest file changes, so routine source edits @@ -53,7 +55,8 @@ WORKDIR /App CMD ["bash"] # Install runtime tools in a single layer -RUN tdnf install -y procps && tdnf clean all +RUN --mount=type=cache,target=/var/cache/tdnf,sharing=locked \ + tdnf install -y procps # Copy from build image COPY --from=build-env /App/out . From 54c0b22408a012069ba51619806c1e65e14a5d2a Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 12:05:57 -0700 Subject: [PATCH 04/10] Apply learnings from EssentialCSharp.Web PR #1139 - Add scope=try-main to GHA Docker layer cache (cache-from/cache-to) so PR builds can't evict main branch cache entries - Switch to actions/cache/restore@v5 (restore-only) + conditional actions/cache/save@v5 (main only) so PRs never write buildkit-cache entries to the shared GHA cache - Set skip-extraction based on event type: false on push (always refresh cache after build), true on pull_request (no save, no need to extract) - Add explicit cache mount IDs (try-tdnf, try-nuget, try-npm) to all --mount=type=cache directives in Dockerfile to prevent collisions on shared runners --- .github/workflows/Build-Test-And-Deploy.yaml | 19 +++++++++++++------ Dockerfile | 10 +++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index c1c949f1e..a89376c09 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -55,7 +55,7 @@ jobs: id: setup-buildx - name: Cache Docker build mounts - uses: actions/cache@v4 + uses: actions/cache/restore@v5 id: cache-mounts with: path: .buildkit-cache @@ -69,7 +69,7 @@ jobs: builder: ${{ steps.setup-buildx.outputs.name }} cache-dir: .buildkit-cache dockerfile: Dockerfile - skip-extraction: ${{ steps.cache-mounts.outputs.cache-hit }} + skip-extraction: ${{ github.event_name == 'pull_request' }} # Build but no push with a PR - name: Docker build (no push) @@ -79,9 +79,9 @@ jobs: push: false tags: temp-pr-validation file: ./Dockerfile - cache-from: type=gha + cache-from: type=gha,scope=try-main - # Only build for dev registry — prod gets the image via az acr import in deploy-production + # Only build for dev registry — prod gets the image via az acr import in deploy-production - name: Build Container Image if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' uses: docker/build-push-action@v7 @@ -90,8 +90,15 @@ jobs: file: ./Dockerfile context: . outputs: type=docker,dest=${{ github.workspace }}/tryimage.tar - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=try-main + cache-to: type=gha,mode=max,scope=try-main + + - name: Save Docker build mounts cache + if: github.event_name != 'pull_request' + uses: actions/cache/save@v5 + with: + path: .buildkit-cache + key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} - name: Upload artifact if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' diff --git a/Dockerfile b/Dockerfile index 3ffd0ccf0..5365522c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ CMD ["bash"] # Install all required build tools in a single layer (rarely changes — stays cached) # This is Node v16. For 18, use nodejs18. -RUN --mount=type=cache,target=/var/cache/tdnf,sharing=locked \ +RUN --mount=type=cache,id=try-tdnf,target=/var/cache/tdnf,sharing=locked \ tdnf install -y gawk nodejs npm # Copy only the files needed to restore dependencies. @@ -32,18 +32,18 @@ COPY src/microsoft-learn-mock/package.json src/microsoft-learn-mock/package-lock # Restore NuGet packages. The cache mount persists the package cache across local # builds; in CI the layer itself is cached by the GHA cache backend. -RUN --mount=type=cache,target=/root/.nuget/packages \ +RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ dotnet restore --configfile /App/NuGet.config /App/TryDotNet.sln # Copy all remaining source (changes frequently — only layers below rebuild on edits) COPY . ./ # Build javascript library. The npm cache mount speeds up repeated local builds. -RUN --mount=type=cache,target=/root/.npm \ +RUN --mount=type=cache,id=try-npm,target=/root/.npm \ /App/build-js.sh # Build and publish a release -RUN --mount=type=cache,target=/root/.nuget/packages \ +RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ dotnet publish -c Release -o out /App/src/Microsoft.TryDotNet # Build runtime image @@ -55,7 +55,7 @@ WORKDIR /App CMD ["bash"] # Install runtime tools in a single layer -RUN --mount=type=cache,target=/var/cache/tdnf,sharing=locked \ +RUN --mount=type=cache,id=try-tdnf,target=/var/cache/tdnf,sharing=locked \ tdnf install -y procps # Copy from build image From 8b9dd595441855866e75ee373fbd3e1a119c3711 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 12:10:06 -0700 Subject: [PATCH 05/10] Split dotnet build and dotnet publish --no-build into separate layers Caches the compiled output as a distinct layer. If publish fails (e.g. publish config issue), the build layer is already cached so the next run skips the full recompile. --- Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5365522c5..2bd91a686 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,9 +42,13 @@ COPY . ./ RUN --mount=type=cache,id=try-npm,target=/root/.npm \ /App/build-js.sh -# Build and publish a release +# Build the solution RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ - dotnet publish -c Release -o out /App/src/Microsoft.TryDotNet + dotnet build -c Release --no-restore /App/TryDotNet.sln + +# Publish (skips recompile since the build layer above is cached) +RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ + dotnet publish -c Release -o out --no-build /App/src/Microsoft.TryDotNet # Build runtime image FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 From faa5bdd23baff986dbbd21439cf61c20580d7370 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 12:12:24 -0700 Subject: [PATCH 06/10] Build only Microsoft.TryDotNet instead of entire solution Avoids compiling test projects, SimulatorGenerator, and WasmRunner that are not needed in the runtime image. dotnet build resolves project references automatically so all required dependencies still build. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2bd91a686..4c3e64085 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,7 +44,7 @@ RUN --mount=type=cache,id=try-npm,target=/root/.npm \ # Build the solution RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ - dotnet build -c Release --no-restore /App/TryDotNet.sln + dotnet build -c Release --no-restore /App/src/Microsoft.TryDotNet # Publish (skips recompile since the build layer above is cached) RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ From 4eecb4403af6bf7093e99fbe9a2bb62f26479baf Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 12:49:24 -0700 Subject: [PATCH 07/10] Guard main cache writes to main branch only - Split non-PR container build into main/non-main paths - Keep cache-to scope=try-main only on refs/heads/main - Restrict actions/cache/save for buildkit mounts to main branch - Keep non-main dispatch builds read-only against main cache --- .github/workflows/Build-Test-And-Deploy.yaml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index a89376c09..2750fc60f 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -82,8 +82,8 @@ jobs: cache-from: type=gha,scope=try-main # Only build for dev registry — prod gets the image via az acr import in deploy-production - - name: Build Container Image - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' + - name: Build Container Image (main cache write) + if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' && github.ref == 'refs/heads/main' uses: docker/build-push-action@v7 with: tags: ${{ vars.DEVCONTAINER_REGISTRY }}/try:${{ github.sha }},${{ vars.DEVCONTAINER_REGISTRY }}/try:latest @@ -93,8 +93,18 @@ jobs: cache-from: type=gha,scope=try-main cache-to: type=gha,mode=max,scope=try-main + - name: Build Container Image (no cache write) + if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' && github.ref != 'refs/heads/main' + uses: docker/build-push-action@v7 + with: + tags: ${{ vars.DEVCONTAINER_REGISTRY }}/try:${{ github.sha }},${{ vars.DEVCONTAINER_REGISTRY }}/try:latest + file: ./Dockerfile + context: . + outputs: type=docker,dest=${{ github.workspace }}/tryimage.tar + cache-from: type=gha,scope=try-main + - name: Save Docker build mounts cache - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' uses: actions/cache/save@v5 with: path: .buildkit-cache From 107905f416595be7ec02e9410aa445cf7659c2fd Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 12:54:51 -0700 Subject: [PATCH 08/10] Use single publish step in Dockerfile Revert split build/publish layering and keep a single publish command for the deployment target project: - dotnet publish --no-restore /App/src/Microsoft.TryDotNet This keeps the Dockerfile simpler while still avoiding a second restore. --- Dockerfile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4c3e64085..dc6f3bbd0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,13 +42,9 @@ COPY . ./ RUN --mount=type=cache,id=try-npm,target=/root/.npm \ /App/build-js.sh -# Build the solution +# Publish only what we deploy RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ - dotnet build -c Release --no-restore /App/src/Microsoft.TryDotNet - -# Publish (skips recompile since the build layer above is cached) -RUN --mount=type=cache,id=try-nuget,target=/root/.nuget/packages \ - dotnet publish -c Release -o out --no-build /App/src/Microsoft.TryDotNet + dotnet publish -c Release --no-restore -o out /App/src/Microsoft.TryDotNet # Build runtime image FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 From a6e05cd6f2a7616847d8abf02b3b7ac15e95f9d3 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 13:59:30 -0700 Subject: [PATCH 09/10] Simplify Docker workflow to two execution paths - Keep PR validation path as build-only/no-push - Keep main branch path for image build, cache writes, and artifact upload - Remove non-main non-PR image build path - Gate deploy jobs to main branch to match artifact-producing path --- .github/workflows/Build-Test-And-Deploy.yaml | 22 ++++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index 2750fc60f..e982b5269 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -82,8 +82,8 @@ jobs: cache-from: type=gha,scope=try-main # Only build for dev registry — prod gets the image via az acr import in deploy-production - - name: Build Container Image (main cache write) - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + - name: Build Container Image + if: github.ref == 'refs/heads/main' uses: docker/build-push-action@v7 with: tags: ${{ vars.DEVCONTAINER_REGISTRY }}/try:${{ github.sha }},${{ vars.DEVCONTAINER_REGISTRY }}/try:latest @@ -93,25 +93,15 @@ jobs: cache-from: type=gha,scope=try-main cache-to: type=gha,mode=max,scope=try-main - - name: Build Container Image (no cache write) - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' && github.ref != 'refs/heads/main' - uses: docker/build-push-action@v7 - with: - tags: ${{ vars.DEVCONTAINER_REGISTRY }}/try:${{ github.sha }},${{ vars.DEVCONTAINER_REGISTRY }}/try:latest - file: ./Dockerfile - context: . - outputs: type=docker,dest=${{ github.workspace }}/tryimage.tar - cache-from: type=gha,scope=try-main - - name: Save Docker build mounts cache - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main' uses: actions/cache/save@v5 with: path: .buildkit-cache key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} - name: Upload artifact - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' + if: github.ref == 'refs/heads/main' uses: actions/upload-artifact@v7.0.1 with: name: tryimage @@ -177,7 +167,7 @@ jobs: artifact-name: 'integration-test-playlists' deploy-development: - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' + if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: [build-and-test, build-and-test-windows, integration-tests] concurrency: @@ -228,7 +218,7 @@ jobs: --image "${{ vars.DEVCONTAINER_REGISTRY }}/try:${{ github.sha }}" deploy-production: - if: github.event_name != 'pull_request_target' && github.event_name != 'pull_request' + if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: deploy-development concurrency: From 8a504920f4ab69a87aa08fa43d95db4c81878e6c Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Tue, 19 May 2026 14:05:41 -0700 Subject: [PATCH 10/10] Simplify cache-dance persistence wiring - Use actions/cache@v5 on main (restore + post-job save) - Keep PR path restore-only with actions/cache/restore@v5 - Remove explicit actions/cache/save step - Keep extraction disabled on PRs and on main cache-hit runs --- .github/workflows/Build-Test-And-Deploy.yaml | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yaml b/.github/workflows/Build-Test-And-Deploy.yaml index e982b5269..6f767733d 100644 --- a/.github/workflows/Build-Test-And-Deploy.yaml +++ b/.github/workflows/Build-Test-And-Deploy.yaml @@ -54,9 +54,20 @@ jobs: uses: docker/setup-buildx-action@v4 id: setup-buildx - - name: Cache Docker build mounts + - name: Cache Docker build mounts (main) + if: github.ref == 'refs/heads/main' + uses: actions/cache@v5 + id: cache-mounts-main + with: + path: .buildkit-cache + key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} + restore-keys: | + buildkit-cache- + + - name: Restore Docker build mounts (PR) + if: github.event_name == 'pull_request' || github.event_name == 'merge_group' uses: actions/cache/restore@v5 - id: cache-mounts + id: cache-mounts-pr with: path: .buildkit-cache key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} @@ -69,7 +80,7 @@ jobs: builder: ${{ steps.setup-buildx.outputs.name }} cache-dir: .buildkit-cache dockerfile: Dockerfile - skip-extraction: ${{ github.event_name == 'pull_request' }} + skip-extraction: ${{ github.ref != 'refs/heads/main' || steps.cache-mounts-main.outputs.cache-hit == 'true' }} # Build but no push with a PR - name: Docker build (no push) @@ -93,13 +104,6 @@ jobs: cache-from: type=gha,scope=try-main cache-to: type=gha,mode=max,scope=try-main - - name: Save Docker build mounts cache - if: github.ref == 'refs/heads/main' - uses: actions/cache/save@v5 - with: - path: .buildkit-cache - key: buildkit-cache-${{ hashFiles('Dockerfile', 'Directory.Packages.props', 'NuGet.config', 'src/**/package-lock.json') }} - - name: Upload artifact if: github.ref == 'refs/heads/main' uses: actions/upload-artifact@v7.0.1