Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions eng/docker-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All breaking changes and new features in `eng/docker-tools` will be documented i

---

## 2026-03-18: CG build template supports skipping .NET SDK installation

- Issue: [#2029](https://github.com/dotnet/docker-tools/issues/2029)

`cg-build-projects.yml` now accepts `skipDotNetInstall` (boolean, default `false`) and
`initSteps` (stepList, default `[]`) parameters. Setting `skipDotNetInstall: true` skips
the built-in SDK installation. Setting `initSteps` will execute those custom steps
at the beginning of the job.

---

## 2026-03-12: Service connection OIDC changes

- Pull request: [#2013](https://github.com/dotnet/docker-tools/pull/2013)
Expand Down
2 changes: 1 addition & 1 deletion eng/docker-tools/Install-DotNetSdk.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

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

$IsRunningOnUnix = $PSVersionTable.contains("Platform") -and $PSVersionTable.Platform -eq "Unix"
Expand Down
18 changes: 15 additions & 3 deletions eng/docker-tools/templates/jobs/cg-build-projects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ parameters:
type: boolean
default: false
displayName: CG Dry Run
# When true, the job skips the .NET SDK installation.
- name: skipDotNetInstall
type: boolean
default: false
displayName: Skip .NET SDK Installation
# See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#options for possible Channel values
- name: dotnetVersionChannel
type: string
default: '9.0'
displayName: .NET Version
# Additional steps to run before building projects (e.g. custom SDK installation).
- name: initSteps
type: stepList
default: []

jobs:
- job: BuildProjects
Expand All @@ -21,9 +30,12 @@ jobs:
image: $(default1ESInternalPoolImage)
os: linux
steps:
- powershell: >
./eng/docker-tools/Install-DotNetSdk.ps1 -Channel ${{ parameters.dotnetVersionChannel }} -InstallPath "$(Build.SourcesDirectory)/.dotnet"
displayName: Run Dotnet Install Script
- ${{ each step in parameters.initSteps }}:
- ${{ step }}
- ${{ if eq(parameters.skipDotNetInstall, false) }}:
- powershell: >
./eng/docker-tools/Install-DotNetSdk.ps1 -Channel ${{ parameters.dotnetVersionChannel }} -InstallPath "$(Build.SourcesDirectory)/.dotnet"
displayName: Install .NET SDK
- script: >
find . -name '*.csproj' | grep $(cgBuildGrepArgs) | xargs -n 1 $(Build.SourcesDirectory)/.dotnet/dotnet build
displayName: Build Projects
Expand Down
18 changes: 10 additions & 8 deletions eng/docker-tools/templates/jobs/sign-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ jobs:
imageInfoDir: $(Build.ArtifactStagingDirectory)/image-info
steps:

# Install MicroBuild signing plugin for ESRP container image signing
- template: /eng/docker-tools/templates/steps/init-signing-linux.yml@self
parameters:
signType: ${{ parameters.publishConfig.Signing.SignType }}
envFileVariableName: signingEnvFilePath

# Setup docker and ImageBuilder
# Setup docker and ImageBuilder (checkout must happen before init-signing
# so that the Install-DotNetSdk.ps1 script is available)
- template: /eng/docker-tools/templates/steps/init-common.yml@self
parameters:
dockerClientOS: linux
setupImageBuilder: true
customInitSteps: ${{ parameters.customInitSteps }}
publishConfig: ${{ parameters.publishConfig }}
envFilePath: $(signingEnvFilePath)

# Install MicroBuild signing plugin for ESRP container image signing
- template: /eng/docker-tools/templates/steps/init-signing-linux.yml@self
parameters:
signType: ${{ parameters.publishConfig.Signing.SignType }}
dockerRunOptionsVariableName: signingDockerRunOptions

- template: /eng/docker-tools/templates/steps/reference-service-connections.yml@self
parameters:
Expand All @@ -47,6 +47,7 @@ jobs:
parameters:
displayName: 🔏 Sign Container Images
internalProjectName: ${{ parameters.internalProjectName }}
linuxOnlyExtraDockerRunArgs: $(signingDockerRunOptions)
args: >-
signImages
$(artifactsPath)/image-info/image-info.json
Expand All @@ -57,6 +58,7 @@ jobs:
parameters:
displayName: ✅ Verify Container Image Signatures
internalProjectName: ${{ parameters.internalProjectName }}
linuxOnlyExtraDockerRunArgs: $(signingDockerRunOptions)
args: >-
verifySignatures
$(artifactsPath)/image-info/image-info.json
Expand Down
3 changes: 2 additions & 1 deletion eng/docker-tools/templates/steps/clean-acr-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ parameters:
acr: null
action: null
age: null
customArgs: "--dry-run"
customArgs: ''
internalProjectName: null
steps:
- template: /eng/docker-tools/templates/steps/run-imagebuilder.yml@self
Expand All @@ -23,3 +23,4 @@ steps:
--action ${{ parameters.action }}
--age ${{ parameters.age }}
${{ parameters.customArgs }}
$(dryRunArg)
7 changes: 0 additions & 7 deletions eng/docker-tools/templates/steps/init-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ parameters:
type: string
default: "versions"

# Path to an env file for docker --env-file.
# Passed through to init-imagebuilder.yml.
- name: envFilePath
type: string
default: ""

steps:
# Repository Checkout
# Multi-repo checkout is used when a versions repository is needed for caching.
Expand Down Expand Up @@ -251,4 +245,3 @@ steps:
publishConfig: ${{ parameters.publishConfig }}
condition: ${{ parameters.condition }}
customInitSteps: ${{ parameters.customInitSteps }}
envFilePath: ${{ parameters.envFilePath }}
38 changes: 22 additions & 16 deletions eng/docker-tools/templates/steps/init-imagebuilder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ parameters:
type: stepList
default: []

# Path to an env file for docker --env-file.
# When set, --env-file is added to the docker run commands so the container
# receives the environment variables defined in the file.
- name: envFilePath
type: string
default: ""

steps:
# Custom ImageBuilder setup (e.g., bootstrap from source)
- ${{ if gt(length(parameters.customInitSteps), 0) }}:
Expand Down Expand Up @@ -106,21 +99,25 @@ steps:
"$(imageBuilderDockerRunExtraOptions)"
)

$envFilePath = "${{ parameters.envFilePath }}"
if ($envFilePath) {
$dockerRunArgs += "--env-file $envFilePath"
}

$authedDockerRunArgs = @(
"-e", 'SYSTEM_ACCESSTOKEN'
"-e", 'SYSTEM_OIDCREQUESTURI'
)

$dockerRunCmd = $dockerRunBaseCmd + $dockerRunArgs + @("$(imageNames.imageBuilder.withrepo)")
$authedDockerRunCmd = $dockerRunBaseCmd + $authedDockerRunArgs + $dockerRunArgs + @("$(imageNames.imageBuilder.withrepo)")
$dockerRunCmd = $dockerRunBaseCmd + $dockerRunArgs
$authedDockerRunCmd = $dockerRunBaseCmd + $authedDockerRunArgs + $dockerRunArgs

# Base commands without image name for templates that need to insert
# extra docker run args before the image name (e.g. signing)
$runImageBuilderBaseCmd = $($dockerRunCmd -join ' ')
$runAuthedImageBuilderBaseCmd = $($authedDockerRunCmd -join ' ')

Write-Host "##vso[task.setvariable variable=runImageBuilderBaseCmd]$runImageBuilderBaseCmd"
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderBaseCmd]$runAuthedImageBuilderBaseCmd"

$runImageBuilderCmd = $($dockerRunCmd -join ' ')
$runAuthedImageBuilderCmd = $($authedDockerRunCmd -join ' ')
# Full commands with image name for direct invocation by other templates
$runImageBuilderCmd = "$runImageBuilderBaseCmd $imageBuilderImageName"
$runAuthedImageBuilderCmd = "$runAuthedImageBuilderBaseCmd $imageBuilderImageName"

Write-Host "##vso[task.setvariable variable=runImageBuilderCmd]$runImageBuilderCmd"
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderCmd]$runAuthedImageBuilderCmd"
Expand All @@ -138,3 +135,12 @@ steps:
$runImageBuilderCmd = "$(Build.BinariesDirectory)\.Microsoft.DotNet.ImageBuilder\Microsoft.DotNet.ImageBuilder.exe"
Write-Host "##vso[task.setvariable variable=runImageBuilderCmd]$runImageBuilderCmd"
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderCmd]$runImageBuilderCmd"
# On Windows the base commands are the same as the full commands since
# there is no container image name to append
Write-Host "##vso[task.setvariable variable=runImageBuilderBaseCmd]$runImageBuilderCmd"
Write-Host "##vso[task.setvariable variable=runAuthedImageBuilderBaseCmd]$runImageBuilderCmd"
# Set imageBuilderImageName to empty - on Windows there is no container image
# since ImageBuilder runs as a native exe. run-imagebuilder.yml appends this
# variable to the command line, so it must be defined (but empty) to avoid
# leaving an unexpanded $(imageBuilderImageName) literal in the script.
Write-Host "##vso[task.setvariable variable=imageBuilderImageName]"
124 changes: 62 additions & 62 deletions eng/docker-tools/templates/steps/init-signing-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ parameters:
type: string
default: $(Agent.TempDirectory)/MicroBuild

# Name of the pipeline variable to set with the signing env file path.
# When set, a signing env file is written after plugin installation and
# the specified pipeline variable is set to its path via logging directive,
# allowing downstream steps to reference it as $(variableName).
# When empty, no env file is created (non-signing jobs).
- name: envFileVariableName
# Name of the pipeline variable to set with the signing docker run options.
# The variable will contain both the MicroBuild plugin volume mount and
# the --env-file flag, ready to pass as extraDockerRunOptions to run-imagebuilder.
- name: dockerRunOptionsVariableName
type: string
default: ""

steps:
# Install .NET SDK on Linux - needed to download the MicroBuild plugin nupkgs when nuget.exe is unavailable.
# Install to an isolated location so the repo's global.json doesn't interfere.
- task: UseDotNet@2
# Install .NET 8.0 SDK for MicroBuild plugin installation using dotnet-install.sh.
# We avoid UseDotNet@2 because it sets DOTNET_ROOT globally, which breaks PowerShell
# (pwsh) on Azure Linux 3 where pwsh requires the .NET 9.0 runtime from the system
# .NET installation. Instead, we install to an isolated directory and only expose it
# to the MicroBuild task via its env block.
- powershell: >
$(engDockerToolsPath)/Install-DotNetSdk.ps1
-InstallPath "${{ parameters.microBuildOutputFolder }}/.dotnet"
-Channel "8.0"
displayName: Install .NET SDK for MicroBuild Plugin
condition: and(succeeded(), ${{ parameters.condition }})
inputs:
packageType: sdk
version: 8.0.x
installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet

# Create a global.json in the MicroBuild folder that pins to the installed SDK.
# This prevents the repo's global.json from causing SDK resolution failures.
# This prevents the repo's global.json from causing SDK resolution failures
# when MicroBuild runs dotnet restore from this directory.
- script: |
mkdir -p ${{ parameters.microBuildOutputFolder }}
version=$(dotnet --version)
version=$(${{ parameters.microBuildOutputFolder }}/.dotnet/dotnet --version)
cat > ${{ parameters.microBuildOutputFolder }}/global.json << EOF
{
"sdk": {
Expand Down Expand Up @@ -69,56 +69,56 @@ steps:
TeamName: $(TeamName)
MicroBuildOutputFolderOverride: $(Agent.TempDirectory)/MicroBuild
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
PATH: ${{ parameters.microBuildOutputFolder }}/.dotnet:$(PATH)

# Configure ImageBuilder docker run options and write env file for signing.
# Sets imageBuilderDockerRunExtraOptions with the plugin volume mount and,
# when envFileVariableName is provided, writes a signing env file and sets
# the named pipeline variable to its path via logging directive.
- ${{ if ne(parameters.envFileVariableName, '') }}:
- task: PowerShell@2
displayName: Configure ImageBuilder Signing Options
condition: and(succeeded(), ${{ parameters.condition }})
inputs:
targetType: 'inline'
script: |
# Mount the MicroBuild signing plugin directory (contains DDSignFiles.dll and esrpcli.dll).
$imageBuilderDockerRunExtraOptions = "-v $env:MBSIGN_APPFOLDER`:/microbuild"
Write-Host "MicroBuild signing enabled, mounting $env:MBSIGN_APPFOLDER to /microbuild"
Write-Host "##vso[task.setvariable variable=imageBuilderDockerRunExtraOptions]$imageBuilderDockerRunExtraOptions"
# Configure docker run options for signing.
# Writes an env file with signing variables and sets $(signingDockerRunOptions)
# with both the MicroBuild plugin volume mount and the --env-file flag.
- task: PowerShell@2
displayName: Configure ImageBuilder Signing Options
condition: and(succeeded(), ${{ parameters.condition }})
inputs:
targetType: 'inline'
script: |
# Write the signing env file for docker --env-file.
# Docker reads this file on the host before creating the container,
# so no volume mount is needed for the file itself.
$envFilePath = "$(Agent.TempDirectory)/imagebuilder-signing.env"
$envFileContent = @(
# MicroBuild plugin variables for DDSignFiles.dll
"MBSIGN_APPFOLDER=/microbuild"
"VSENGESRPSSL"
"USEESRPCLI"
"MBSIGN_CONNECTEDSERVICE"

# Write the signing env file for docker --env-file.
# Docker reads this file on the host before creating the container,
# so no volume mount is needed for the file itself.
$envFilePath = "$(Agent.TempDirectory)/imagebuilder-signing.env"
$envFileContent = @(
# MicroBuild plugin variables for DDSignFiles.dll
"MBSIGN_APPFOLDER=/microbuild"
"VSENGESRPSSL"
"USEESRPCLI"
"MBSIGN_CONNECTEDSERVICE"
# Container-local temp/workspace paths (host paths aren't accessible inside the container)
"MBSIGNTEMPDIR=/tmp/MicroBuildSign"
"PIPELINE_WORKSPACE=$(Build.ArtifactStagingDirectory)"
"AGENT_TEMPDIRECTORY=/tmp"

# Container-local temp/workspace paths (host paths aren't accessible inside the container)
"MBSIGNTEMPDIR=/tmp/MicroBuildSign"
"PIPELINE_WORKSPACE=$(Build.ArtifactStagingDirectory)"
"AGENT_TEMPDIRECTORY=/tmp"
# Azure DevOps pipeline variables for ESRP bearer token auth (ESRPUtils.GetAccountInfo)
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"
"BUILD_BUILDID"
"SYSTEM_TEAMPROJECT"
"BUILD_SOURCEBRANCH"

# Azure DevOps pipeline variables for ESRP bearer token auth (ESRPUtils.GetAccountInfo)
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"
"BUILD_BUILDID"
"SYSTEM_TEAMPROJECT"
"BUILD_SOURCEBRANCH"
# Azure DevOps pipeline variables for ESRP CLI federated token (ESRPCliDll.GetFederatedTokenData)
"SYSTEM_JOBID"
"SYSTEM_PLANID"
"SYSTEM_TEAMPROJECTID"
"SYSTEM_HOSTTYPE"
"SYSTEM_COLLECTIONURI"

# Azure DevOps pipeline variables for ESRP CLI federated token (ESRPCliDll.GetFederatedTokenData)
"SYSTEM_JOBID"
"SYSTEM_PLANID"
"SYSTEM_TEAMPROJECTID"
"SYSTEM_HOSTTYPE"
"SYSTEM_COLLECTIONURI"
# Azure DevOps pipeline variables for DDSignFilesConfiguration
"BUILD_DEFINITIONNAME"
"BUILD_BUILDNUMBER"
)

# Azure DevOps pipeline variables for DDSignFilesConfiguration
"BUILD_DEFINITIONNAME"
"BUILD_BUILDNUMBER"
)
$envFileContent | Set-Content -Path $envFilePath -Encoding utf8NoBOM

$envFileContent | Set-Content -Path $envFilePath -Encoding utf8NoBOM
Write-Host "##vso[task.setvariable variable=${{ parameters.envFileVariableName }}]$envFilePath"
# Compose docker run options for signing:
# - Volume mount for MicroBuild plugin directory (DDSignFiles.dll and esrpcli.dll)
# - Env file with signing environment variables
$signingDockerRunOptions = "-v $env:MBSIGN_APPFOLDER`:/microbuild --env-file `"$envFilePath`""
Write-Host "signingDockerRunOptions: $signingDockerRunOptions"
Write-Host "##vso[task.setvariable variable=${{ parameters.dockerRunOptionsVariableName }}]$signingDockerRunOptions"
13 changes: 11 additions & 2 deletions eng/docker-tools/templates/steps/run-imagebuilder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ parameters:
type: string
default: "linux"

# Additional docker run args inserted before the image name (Linux only).
# These are flags passed to `docker run` for the containerized ImageBuilder
# and must not be set for Windows jobs where ImageBuilder runs as a native
# executable. Used for signing (volume mounts, env files) or other
# job-specific docker configuration.
- name: linuxOnlyExtraDockerRunArgs
type: string
default: ""

steps:
- ${{ if and(eq(variables['System.TeamProject'], parameters.internalProjectName), ne(variables['Build.Reason'], 'PullRequest')) }}:

Expand Down Expand Up @@ -56,7 +65,7 @@ steps:
$serviceConnectionsArgs += "$($connection.tenantId):$($connection.clientId):$($connection.id)"
}

$(runAuthedImageBuilderCmd) ${{ parameters.args }} @serviceConnectionsArgs
$(runAuthedImageBuilderBaseCmd) ${{ parameters.linuxOnlyExtraDockerRunArgs }} $(imageBuilderImageName) ${{ parameters.args }} @serviceConnectionsArgs

- ${{ else }}:

Expand All @@ -69,4 +78,4 @@ steps:
inputs:
targetType: 'inline'
script: >-
$(runImageBuilderCmd) ${{ parameters.args }}
$(runImageBuilderBaseCmd) ${{ parameters.linuxOnlyExtraDockerRunArgs }} $(imageBuilderImageName) ${{ parameters.args }}
Loading
Loading