Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
1f3d41a
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Apr 27, 2026
ccfecf6
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] May 4, 2026
21eb334
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] May 11, 2026
f91a859
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] May 18, 2026
9e91d78
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] May 25, 2026
117b168
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jun 1, 2026
8bac32e
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jun 8, 2026
18d4460
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jun 15, 2026
0111030
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jun 29, 2026
af0e69f
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jul 6, 2026
363808d
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jul 13, 2026
6ba4faf
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jul 20, 2026
1eb870f
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Jul 27, 2026
31190df
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 3, 2026
9e2816a
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 10, 2026
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
28 changes: 14 additions & 14 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,38 @@
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.SignTool" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.SignTool" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<!-- Stay on package 8.0 until we stop testing for net6.0
<Dependency Name="Microsoft.DotNet.RemoteExecutor" Version="9.0.0-beta.24212.4">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>812d978c303174dc1aa305d7359e79053d7d4971</Sha>
</Dependency> -->
<Dependency Name="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="11.0.0-beta.26211.1">
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="11.0.0-beta.26407.8">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a08169b890573cfd7f949ea9062c86a4db1aab1b</Sha>
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
<MicrosoftDotNetBuildTasksTemplatingVersion>9.0.0-beta.24212.4</MicrosoftDotNetBuildTasksTemplatingVersion>
<MicrosoftDotNetPlatformAbstractionsVersion>5.0.0-preview.5.20278.1</MicrosoftDotNetPlatformAbstractionsVersion>
<MicrosoftDotNetRemoteExecutorVersion>8.0.0-beta.24525.2</MicrosoftDotNetRemoteExecutorVersion>
<MicrosoftDotNetXUnitExtensionsVersion>11.0.0-beta.26211.1</MicrosoftDotNetXUnitExtensionsVersion>
<MicrosoftDotNetXUnitExtensionsVersion>11.0.0-beta.26407.8</MicrosoftDotNetXUnitExtensionsVersion>
<MicrosoftExtensionsDependencyModelVersion>9.0.4</MicrosoftExtensionsDependencyModelVersion>
<MicrosoftMLOnnxTestModelsVersion>0.0.6-test</MicrosoftMLOnnxTestModelsVersion>
<MicrosoftMLTensorFlowTestModelsVersion>0.0.13-test</MicrosoftMLTensorFlowTestModelsVersion>
Expand Down
5 changes: 5 additions & 0 deletions eng/common/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `eng/common`

Files under `eng/common` come from [Arcade](https://github.com/dotnet/arcade).
Edits in `eng/common` will be overwritten by automation unless the changes are made directly in the Arcade repository.
For more information, see the [Arcade documentation](https://github.com/dotnet/arcade/tree/main/Documentation).
154 changes: 154 additions & 0 deletions eng/common/Get-GitHubAppToken.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Mints a short-lived GitHub App installation access token by signing a JWT
# with a private key stored in Azure Key Vault (RSA, RS256). The signed JWT is
# exchanged with the GitHub API for a token scoped to a single installation.
#
# Requirements:
# - A GitHub App whose private key has been uploaded into Key Vault as an RSA
# key (the PEM converted to a Key Vault *key*, NOT stored as a secret).
# - The caller (the federated Azure service connection used to run this script)
# must have the `Key Vault Crypto User` role (or at minimum the `Sign`
# action) on that key.
# - The App must be installed on the target organization/account
# (`InstallationOwner`) with the permissions/repositories it needs.
#
# Installation tokens (ghs_*) are exempt from the enterprise classic-PAT
# lifetime policy, which is why this replaces the long-lived PAT.

[CmdletBinding()]
param(
# Name of the Key Vault that holds the GitHub App's RSA signing key.
[Parameter(Mandatory = $true)]
[string] $KeyVaultName,

# Name of the RSA key inside the Key Vault (the App's private key).
[Parameter(Mandatory = $true)]
[string] $KeyName,

# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
[Parameter(Mandatory = $true)]
[string] $AppClientId,

# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
[Parameter(Mandatory = $true)]
[string] $InstallationOwner,

# Optional Azure DevOps pipeline variable name to set with the installation
# token (marked as a secret). When not specified, the token is written to
# stdout instead.
[Parameter(Mandatory = $false)]
[string] $OutputVariableName
)

$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true

. $PSScriptRoot\pipeline-logging-functions.ps1

function ConvertTo-Base64Url([byte[]] $bytes) {
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}

# Build JWT header and payload. Use [ordered] hashtables so JSON
# serialization is deterministic.
$jwtHeader = [ordered]@{
alg = 'RS256'
typ = 'JWT'
}
$now = [System.DateTimeOffset]::UtcNow
$jwtPayload = [ordered]@{
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
iss = $AppClientId
}

$headerEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtHeader | ConvertTo-Json -Compress)))
$payloadEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtPayload | ConvertTo-Json -Compress)))
$signingInput = "$headerEncoded.$payloadEncoded"

# Key Vault `sign` expects the *digest* (base64), not the raw bytes.
$sha256 = [System.Security.Cryptography.SHA256]::Create()
$digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
$digestBase64 = [Convert]::ToBase64String($digestBytes)

Write-Host "Signing JWT with key '$KeyName' in vault '$KeyVaultName'..."
$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
try {
# Azure CLI can emit non-fatal Python warnings to stderr even when signing succeeds.
# Use the exit code to determine success for this invocation.
$PSNativeCommandUseErrorActionPreference = $false
$signatureBase64 = az keyvault key sign `
--vault-name $KeyVaultName `
--name $KeyName `
--algorithm RS256 `
--digest $digestBase64 `
--query signature `
--output tsv `
--only-show-errors
$signExitCode = $LASTEXITCODE
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to sign the JWT via Key Vault (key '$KeyName', vault '$KeyVaultName'): $_. Verify the service connection identity has the 'Key Vault Crypto User' role (Sign action) on the key."
exit 1
}
finally {
$PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
}
if ($signExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($signatureBase64)) {
Write-PipelineTelemetryError -Category 'Build' -Message "'az keyvault key sign' exited with code $signExitCode for key '$KeyName' in vault '$KeyVaultName'. Verify the service connection identity has the 'Key Vault Crypto User' role (Sign action) on the key."
exit 1
}
$signatureUrl = $signatureBase64.Trim().TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$signingInput.$signatureUrl"

$headers = @{
Authorization = "Bearer $jwt"
'X-GitHub-Api-Version' = '2022-11-28'
Accept = 'application/vnd.github+json'
'User-Agent' = 'dotnet-arcade-onelocbuild'
}

Write-Host "Looking up installation for '$InstallationOwner'..."
try {
$installations = @()
$page = 1
do {
$pageInstallations = @(Invoke-RestMethod `
-Uri "https://api.github.com/app/installations?per_page=100&page=$page" `
-Headers $headers `
-Method Get)
$installations += $pageInstallations
$page++
} while ($pageInstallations.Count -eq 100)
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App's Client ID ('$AppClientId') may be incorrect."
exit 1
}
$installation = $installations | Where-Object { $_.account.login -ieq $InstallationOwner } | Select-Object -First 1
if (-not $installation) {
$found = ($installations | ForEach-Object { $_.account.login }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "No installation found for '$InstallationOwner'. App is installed on: $found"
exit 1
}

try {
$tokenResponse = Invoke-RestMethod `
-Uri "https://api.github.com/app/installations/$($installation.id)/access_tokens" `
-Headers $headers `
-Method Post `
-ContentType 'application/json'
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to mint an installation access token for '$InstallationOwner' (installation $($installation.id)): $_"
exit 1
}

Write-Host "Got installation token for '$InstallationOwner' (expires $($tokenResponse.expires_at))."
if ($OutputVariableName) {
Write-Host "Setting pipeline variable '$OutputVariableName'."
Write-Host "##vso[task.setvariable variable=$OutputVariableName;issecret=true]$($tokenResponse.token)"
}
else {
Write-Host $tokenResponse.token -ForegroundColor Green
}
49 changes: 30 additions & 19 deletions eng/common/SetupNugetSources.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
# condition: eq(variables['Agent.OS'], 'Windows_NT')
# inputs:
# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1
# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token
# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config
# env:
# Token: $(dn-bot-dnceng-artifact-feeds-rw)
# Token: $(InternalFeedToken)
#
# Note: This logic is abstracted into enable-internal-sources.yml, which uses
# NuGetAuthenticate or a WIF-backed service connection. Prefer that template
# over calling this script directly.
#
# Note that the NuGetAuthenticate task should be called after SetupNugetSources.
# This ensures that:
Expand All @@ -25,24 +29,31 @@
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)][string]$ConfigFile,
$Password
# Keep the legacy name as an alias while callers migrate secrets to the Token environment variable.
[Alias("Password")]$Credential
)

$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$feedCredential = if ($env:Token) { $env:Token } else { $Credential }

# This script only consumes helper functions from tools.ps1 to configure NuGet feeds.
# Skip importing configure-toolset.ps1 so that repo-specific toolset setup (e.g. acquiring
# a bootstrap SDK) is not triggered as a side effect of feed configuration.
$disableConfigureToolsetImport = $true

. $PSScriptRoot\tools.ps1

# Adds or enables the package source with the given name
function AddOrEnablePackageSource($sources, $disabledPackageSources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) {
if ($disabledPackageSources -eq $null -or -not (EnableInternalPackageSource -DisabledPackageSources $disabledPackageSources -Creds $creds -PackageSourceName $SourceName)) {
AddPackageSource -Sources $sources -SourceName $SourceName -SourceEndPoint $SourceEndPoint -Creds $creds -Username $userName -pwd $Password
function AddOrEnablePackageSource($sources, $disabledPackageSources, $SourceName, $SourceEndPoint, $creds, $Username, $credential) {
if ($disabledPackageSources -eq $null -or -not (EnableInternalPackageSource -DisabledPackageSources $disabledPackageSources -Creds $creds -PackageSourceName $SourceName -Credential $credential)) {
AddPackageSource -Sources $sources -SourceName $SourceName -SourceEndPoint $SourceEndPoint -Creds $creds -Username $Username -credential $credential
}
}

# Add source entry to PackageSources
function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) {
function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $credential) {
$packageSource = $sources.SelectSingleNode("add[@key='$SourceName']")

if ($packageSource -eq $null)
Expand All @@ -58,13 +69,13 @@ function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Usern
Write-Host "Package source $SourceName already present and enabled."
}

AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd
AddCredential -Creds $creds -Source $SourceName -Username $Username -credential $credential
}

# Add a credential node for the specified source
function AddCredential($creds, $source, $username, $pwd) {
function AddCredential($creds, $source, $username, $credential) {
# If no cred supplied, don't do anything.
if (!$pwd) {
if (!$credential) {
return;
}

Expand Down Expand Up @@ -99,27 +110,27 @@ function AddCredential($creds, $source, $username, $pwd) {
$sourceElement.AppendChild($passwordElement) | Out-Null
}

$passwordElement.SetAttribute("value", $pwd)
$passwordElement.SetAttribute("value", $credential)
}

# Enable all darc-int package sources.
function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds) {
function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds, $Credential) {
$maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]")
ForEach ($DisabledPackageSource in $maestroInternalSources) {
EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key
EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key -Credential $Credential
}
}

# Enables an internal package source by name, if found. Returns true if the package source was found and enabled, false otherwise.
function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSourceName) {
function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSourceName, $Credential) {
$DisabledPackageSource = $DisabledPackageSources.SelectSingleNode("add[@key='$PackageSourceName']")
if ($DisabledPackageSource) {
Write-Host "Enabling internal source '$($DisabledPackageSource.key)'."

# Due to https://github.com/NuGet/Home/issues/10291, we must actually remove the disabled entries
$DisabledPackageSources.RemoveChild($DisabledPackageSource)

AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -pwd $Password
AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -credential $credential
return $true
}
return $false
Expand All @@ -144,7 +155,7 @@ if ($sources -eq $null) {

$creds = $null
$feedSuffix = "v3/index.json"
if ($Password) {
if ($feedCredential) {
$feedSuffix = "v2"
# Looks for a <PackageSourceCredentials> node. Create it if none is found.
$creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials")
Expand All @@ -160,16 +171,16 @@ $userName = "dn-bot"
$disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources")
if ($disabledSources -ne $null) {
Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node"
EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds
EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds -Credential $feedCredential
}
$dotnetVersions = @('5','6','7','8','9','10')

foreach ($dotnetVersion in $dotnetVersions) {
$feedPrefix = "dotnet" + $dotnetVersion;
$dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']")
if ($dotnetSource -ne $null) {
AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password
AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password
AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -credential $feedCredential
AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/$feedSuffix" -Creds $creds -Username $userName -credential $feedCredential
}
}

Expand Down
9 changes: 8 additions & 1 deletion eng/common/SetupNugetSources.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
# This logic is also abstracted into enable-internal-sources.yml.

ConfigFile=$1
CredToken=$2
# Prefer the environment variable so credentials do not appear in process arguments.
# Retain the positional argument as a compatibility fallback for existing callers.
CredToken=${Token:-$2}
NL='\n'
TB=' '

Expand All @@ -40,6 +42,11 @@ while [[ -h "$source" ]]; do
done
scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"

# This script only consumes helper functions from tools.sh to configure NuGet feeds.
# Skip importing configure-toolset.sh so that repo-specific toolset setup (e.g. acquiring
# a bootstrap SDK) is not triggered as a side effect of feed configuration.
disable_configure_toolset_import=1

. "$scriptroot/tools.sh"

if [ ! -f "$ConfigFile" ]; then
Expand Down
Loading
Loading