Skip to content

Commit f721c83

Browse files
[main] Update common Docker engineering infrastructure with latest (#7098)
1 parent 7cdb9e0 commit f721c83

15 files changed

Lines changed: 299 additions & 173 deletions

eng/docker-tools/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All breaking changes and new features in `eng/docker-tools` will be documented i
44

55
---
66

7+
## 2026-03-18: CG build template supports skipping .NET SDK installation
8+
9+
- Issue: [#2029](https://github.com/dotnet/docker-tools/issues/2029)
10+
11+
`cg-build-projects.yml` now accepts `skipDotNetInstall` (boolean, default `false`) and
12+
`initSteps` (stepList, default `[]`) parameters. Setting `skipDotNetInstall: true` skips
13+
the built-in SDK installation. Setting `initSteps` will execute those custom steps
14+
at the beginning of the job.
15+
16+
---
17+
718
## 2026-03-12: Service connection OIDC changes
819

920
- Pull request: [#2013](https://github.com/dotnet/docker-tools/pull/2013)

eng/docker-tools/DEV-GUIDE.md

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ The `generateBuildMatrix` command is key to understanding how builds are paralle
171171
1. **Reads the manifest.json** - Understands which images exist
172172
2. **Builds a dependency graph** - Knows that `runtime-deps` must build before `runtime`
173173
3. **Groups by platform** - Creates jobs for each OS/Architecture combo
174-
4. **Optimizes with caching** - Can detect and exclude unchanged images
174+
4. **Optimizes with caching** - Can detect and exclude unchanged images (see [Image Caching](#image-caching) below)
175175

176176
### Controlling Which Build Stages Run
177177

@@ -338,6 +338,34 @@ The `autobuilder` label is how the infrastructure tracks that the failure cycle
338338

339339
---
340340

341+
### Image Caching
342+
343+
The infrastructure includes caching to avoid rebuilding images that haven't changed. Caching operates at two levels:
344+
345+
**1. Matrix Trimming (job-level caching)**
346+
347+
When `trimCachedImagesForMatrix` is enabled, the `generateBuildMatrix` command excludes platforms from the build matrix if they would result in cache hits. This means no build job is even created for those platforms—they're completely skipped.
348+
349+
**2. Build-time Caching**
350+
351+
Even if a platform isn't trimmed from the matrix, the `build` command checks each image against the cache before building. If the image is cached, it outputs `CACHE HIT`, pulls the previously-built image from the registry, and skips the actual Docker build.
352+
353+
#### Cache Conditions
354+
355+
An image is considered cached when **both** of the following conditions are true:
356+
357+
1. **Base image digest is unchanged** — The digest of the base image (FROM image) matches the digest recorded in the image info file from the last successful publish. If the upstream base image has been updated, this condition fails and the image will be rebuilt.
358+
359+
2. **Dockerfile commit is unchanged** — The git commit URL for the Dockerfile matches the commit URL recorded in the image info file. If you've modified the Dockerfile, this condition fails and the image will be rebuilt.
360+
361+
Caching compares against the published image info stored in the [versions repo](https://github.com/dotnet/versions). This means caching compares against what's been officially published, not what's in your current branch.
362+
363+
#### Disabling Caching
364+
365+
To force a rebuild regardless of cache state, set the `noCache` parameter to `true` when queuing the build. This disables both matrix trimming and build-time caching.
366+
367+
---
368+
341369
## Common Customization Patterns
342370

343371
### Pattern: Adding Build Arguments
@@ -389,3 +417,59 @@ When you queue a new run, you can override these as runtime parameters:
389417
2. Set `sourceBuildPipelineRunId` to the run ID containing the artifacts you need (find the build ID in the URL when viewing a pipeline run, e.g., `buildId=123456`)
390418

391419
This avoids the multi-hour rebuild cycle when you just need to retry a failed operation.
420+
421+
---
422+
423+
## Troubleshooting
424+
425+
### Why isn't my Dockerfile being built?
426+
427+
When you trigger a pipeline run, you might find that your Dockerfile isn't being built.
428+
429+
#### Symptom 1: The Dockerfile isn't included in any build job
430+
431+
If your Dockerfile doesn't appear in any build job, first verify the Dockerfile is included in the manifest file.
432+
433+
**How to verify:** Check `manifest.json` to ensure your Dockerfile path is defined under the appropriate repo and image. You can also run `generateBuildMatrix` locally to see which Dockerfiles are included:
434+
435+
```powershell
436+
./eng/docker-tools/Invoke-ImageBuilder.ps1 "generateBuildMatrix --manifest manifest.json --type platformDependencyGraph"
437+
```
438+
439+
**How to fix:** Add the Dockerfile to `manifest.json` under the correct repo, image, and platform configuration.
440+
441+
#### Symptom 2: The pipeline job isn't running at all
442+
443+
If the Dockerfile is in the manifest but you don't see a build job for it, the build matrix was likely trimmed due to [matrix trimming](#image-caching).
444+
445+
**How to verify:** Look at the "Generate platformDependencyGraph Matrix" step output in the `GenerateBuildMatrix` job. This is an example of what the output in that step looks like:
446+
447+
```yaml
448+
windowsLtsc2025Amd64:
449+
src-windowsservercore-ltsc2025-helix-graph:
450+
imageBuilderPaths: --path src/windowsservercore/ltsc2025/helix/amd64 --path src/windowsservercore/ltsc2025/helix/webassembly-net8/amd64 --path src/windowsservercore/ltsc2025/helix/webassembly/amd64
451+
legName: windows-ltsc2025amd64src-windowsservercore-ltsc2025-helix-graph
452+
osType: windows
453+
architecture: amd64
454+
osVersions: --os-version windowsservercore-ltsc2025
455+
```
456+
457+
If your Dockerfile path doesn't appear in any of the matrix legs, it was trimmed.
458+
459+
**How to fix:** Set the `noCache` parameter to `true` when queuing the build.
460+
461+
#### Symptom 3: The build output shows `CACHE HIT`
462+
463+
If your build job runs but you see `CACHE HIT` in the output of the `Build Images` step and the Dockerfile isn't actually built, the [build-time caching](#image-caching) determined that the image doesn't need to be rebuilt. This is an example of what the output in that step looks like:
464+
465+
```
466+
Image info's Dockerfile commit: https://github.com/dotnet/dotnet-buildtools-prereqs-docker/blob/aa85f0dcc3b3d6757c80dc8c2a6f38c290b372cc/src/windowsservercore/ltsc2025/helix/amd64/Dockerfile
467+
Latest Dockerfile commit: https://github.com/dotnet/dotnet-buildtools-prereqs-docker/blob/aa85f0dcc3b3d6757c80dc8c2a6f38c290b372cc/src/windowsservercore/ltsc2025/helix/amd64/Dockerfile
468+
Dockerfile commits match: True
469+
470+
CACHE HIT
471+
472+
-- EXECUTING: docker pull mcr.microsoft.com/dotnet-buildtools/prereqs@sha256:40d36a0aab610f4d513ed7c7300a5d962968a547ffe8a859a0e599691b74b77f
473+
```
474+
475+
**How to fix:** Set the `noCache` parameter to `true` when queuing the build.

eng/docker-tools/Install-DotNetSdk.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Set-StrictMode -Version Latest
2727
$ErrorActionPreference = 'Stop'
2828

2929
if (!(Test-Path "$InstallPath")) {
30-
mkdir "$InstallPath" | Out-Null
30+
New-Item -ItemType Directory -Path "$InstallPath" -Force | Out-Null
3131
}
3232

3333
$IsRunningOnUnix = $PSVersionTable.contains("Platform") -and $PSVersionTable.Platform -eq "Unix"

eng/docker-tools/templates/jobs/cg-build-projects.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@ parameters:
77
type: boolean
88
default: false
99
displayName: CG Dry Run
10+
# When true, the job skips the .NET SDK installation.
11+
- name: skipDotNetInstall
12+
type: boolean
13+
default: false
14+
displayName: Skip .NET SDK Installation
1015
# See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#options for possible Channel values
1116
- name: dotnetVersionChannel
1217
type: string
1318
default: '9.0'
1419
displayName: .NET Version
20+
# Additional steps to run before building projects (e.g. custom SDK installation).
21+
- name: initSteps
22+
type: stepList
23+
default: []
1524

1625
jobs:
1726
- job: BuildProjects
@@ -21,9 +30,12 @@ jobs:
2130
image: $(default1ESInternalPoolImage)
2231
os: linux
2332
steps:
24-
- powershell: >
25-
./eng/docker-tools/Install-DotNetSdk.ps1 -Channel ${{ parameters.dotnetVersionChannel }} -InstallPath "$(Build.SourcesDirectory)/.dotnet"
26-
displayName: Run Dotnet Install Script
33+
- ${{ each step in parameters.initSteps }}:
34+
- ${{ step }}
35+
- ${{ if eq(parameters.skipDotNetInstall, false) }}:
36+
- powershell: >
37+
./eng/docker-tools/Install-DotNetSdk.ps1 -Channel ${{ parameters.dotnetVersionChannel }} -InstallPath "$(Build.SourcesDirectory)/.dotnet"
38+
displayName: Install .NET SDK
2739
- script: >
2840
find . -name '*.csproj' | grep $(cgBuildGrepArgs) | xargs -n 1 $(Build.SourcesDirectory)/.dotnet/dotnet build
2941
displayName: Build Projects

eng/docker-tools/templates/jobs/sign-images.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ jobs:
1515
imageInfoDir: $(Build.ArtifactStagingDirectory)/image-info
1616
steps:
1717

18-
# Install MicroBuild signing plugin for ESRP container image signing
19-
- template: /eng/docker-tools/templates/steps/init-signing-linux.yml@self
20-
parameters:
21-
signType: ${{ parameters.publishConfig.Signing.SignType }}
22-
envFileVariableName: signingEnvFilePath
23-
24-
# Setup docker and ImageBuilder
18+
# Setup docker and ImageBuilder (checkout must happen before init-signing
19+
# so that the Install-DotNetSdk.ps1 script is available)
2520
- template: /eng/docker-tools/templates/steps/init-common.yml@self
2621
parameters:
2722
dockerClientOS: linux
2823
setupImageBuilder: true
2924
customInitSteps: ${{ parameters.customInitSteps }}
3025
publishConfig: ${{ parameters.publishConfig }}
31-
envFilePath: $(signingEnvFilePath)
26+
27+
# Install MicroBuild signing plugin for ESRP container image signing
28+
- template: /eng/docker-tools/templates/steps/init-signing-linux.yml@self
29+
parameters:
30+
signType: ${{ parameters.publishConfig.Signing.SignType }}
31+
dockerRunOptionsVariableName: signingDockerRunOptions
3232

3333
- template: /eng/docker-tools/templates/steps/reference-service-connections.yml@self
3434
parameters:
@@ -47,6 +47,7 @@ jobs:
4747
parameters:
4848
displayName: 🔏 Sign Container Images
4949
internalProjectName: ${{ parameters.internalProjectName }}
50+
linuxOnlyExtraDockerRunArgs: $(signingDockerRunOptions)
5051
args: >-
5152
signImages
5253
$(artifactsPath)/image-info/image-info.json
@@ -57,6 +58,7 @@ jobs:
5758
parameters:
5859
displayName: ✅ Verify Container Image Signatures
5960
internalProjectName: ${{ parameters.internalProjectName }}
61+
linuxOnlyExtraDockerRunArgs: $(signingDockerRunOptions)
6062
args: >-
6163
verifySignatures
6264
$(artifactsPath)/image-info/image-info.json

eng/docker-tools/templates/steps/clean-acr-images.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ parameters:
33
acr: null
44
action: null
55
age: null
6-
customArgs: "--dry-run"
6+
customArgs: ''
77
internalProjectName: null
88
steps:
99
- template: /eng/docker-tools/templates/steps/run-imagebuilder.yml@self
@@ -23,3 +23,4 @@ steps:
2323
--action ${{ parameters.action }}
2424
--age ${{ parameters.age }}
2525
${{ parameters.customArgs }}
26+
$(dryRunArg)

eng/docker-tools/templates/steps/init-common.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,6 @@ parameters:
5353
type: string
5454
default: "versions"
5555

56-
# Path to an env file for docker --env-file.
57-
# Passed through to init-imagebuilder.yml.
58-
- name: envFilePath
59-
type: string
60-
default: ""
61-
6256
steps:
6357
# Repository Checkout
6458
# Multi-repo checkout is used when a versions repository is needed for caching.
@@ -251,4 +245,3 @@ steps:
251245
publishConfig: ${{ parameters.publishConfig }}
252246
condition: ${{ parameters.condition }}
253247
customInitSteps: ${{ parameters.customInitSteps }}
254-
envFilePath: ${{ parameters.envFilePath }}

eng/docker-tools/templates/steps/init-imagebuilder.yml

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@ parameters:
2121
type: stepList
2222
default: []
2323

24-
# Path to an env file for docker --env-file.
25-
# When set, --env-file is added to the docker run commands so the container
26-
# receives the environment variables defined in the file.
27-
- name: envFilePath
28-
type: string
29-
default: ""
30-
3124
steps:
3225
# Custom ImageBuilder setup (e.g., bootstrap from source)
3326
- ${{ if gt(length(parameters.customInitSteps), 0) }}:
@@ -106,21 +99,25 @@ steps:
10699
"$(imageBuilderDockerRunExtraOptions)"
107100
)
108101
109-
$envFilePath = "${{ parameters.envFilePath }}"
110-
if ($envFilePath) {
111-
$dockerRunArgs += "--env-file $envFilePath"
112-
}
113-
114102
$authedDockerRunArgs = @(
115103
"-e", 'SYSTEM_ACCESSTOKEN'
116104
"-e", 'SYSTEM_OIDCREQUESTURI'
117105
)
118106
119-
$dockerRunCmd = $dockerRunBaseCmd + $dockerRunArgs + @("$(imageNames.imageBuilder.withrepo)")
120-
$authedDockerRunCmd = $dockerRunBaseCmd + $authedDockerRunArgs + $dockerRunArgs + @("$(imageNames.imageBuilder.withrepo)")
107+
$dockerRunCmd = $dockerRunBaseCmd + $dockerRunArgs
108+
$authedDockerRunCmd = $dockerRunBaseCmd + $authedDockerRunArgs + $dockerRunArgs
109+
110+
# Base commands without image name for templates that need to insert
111+
# extra docker run args before the image name (e.g. signing)
112+
$runImageBuilderBaseCmd = $($dockerRunCmd -join ' ')
113+
$runAuthedImageBuilderBaseCmd = $($authedDockerRunCmd -join ' ')
114+
115+
Write-Host "##vso[task.setvariable variable=runImageBuilderBaseCmd]$runImageBuilderBaseCmd"
116+
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderBaseCmd]$runAuthedImageBuilderBaseCmd"
121117
122-
$runImageBuilderCmd = $($dockerRunCmd -join ' ')
123-
$runAuthedImageBuilderCmd = $($authedDockerRunCmd -join ' ')
118+
# Full commands with image name for direct invocation by other templates
119+
$runImageBuilderCmd = "$runImageBuilderBaseCmd $imageBuilderImageName"
120+
$runAuthedImageBuilderCmd = "$runAuthedImageBuilderBaseCmd $imageBuilderImageName"
124121
125122
Write-Host "##vso[task.setvariable variable=runImageBuilderCmd]$runImageBuilderCmd"
126123
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderCmd]$runAuthedImageBuilderCmd"
@@ -138,3 +135,12 @@ steps:
138135
$runImageBuilderCmd = "$(Build.BinariesDirectory)\.Microsoft.DotNet.ImageBuilder\Microsoft.DotNet.ImageBuilder.exe"
139136
Write-Host "##vso[task.setvariable variable=runImageBuilderCmd]$runImageBuilderCmd"
140137
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderCmd]$runImageBuilderCmd"
138+
# On Windows the base commands are the same as the full commands since
139+
# there is no container image name to append
140+
Write-Host "##vso[task.setvariable variable=runImageBuilderBaseCmd]$runImageBuilderCmd"
141+
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderBaseCmd]$runImageBuilderCmd"
142+
# Set imageBuilderImageName to empty - on Windows there is no container image
143+
# since ImageBuilder runs as a native exe. run-imagebuilder.yml appends this
144+
# variable to the command line, so it must be defined (but empty) to avoid
145+
# leaving an unexpanded $(imageBuilderImageName) literal in the script.
146+
Write-Host "##vso[task.setvariable variable=imageBuilderImageName]"

0 commit comments

Comments
 (0)