Skip to content

Commit 8689b15

Browse files
Update dependencies from https://github.com/dotnet/arcade build 20260807.8
On relative base path root Microsoft.DotNet.Arcade.Sdk From Version 11.0.0-beta.26372.6 -> To Version 11.0.0-beta.26407.8
1 parent bef54f2 commit 8689b15

16 files changed

Lines changed: 317 additions & 35 deletions

eng/Version.Details.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props
66
<Project>
77
<PropertyGroup>
88
<!-- dotnet-arcade dependencies -->
9-
<MicrosoftDotNetArcadeSdkPackageVersion>11.0.0-beta.26381.1</MicrosoftDotNetArcadeSdkPackageVersion>
9+
<MicrosoftDotNetArcadeSdkPackageVersion>11.0.0-beta.26407.8</MicrosoftDotNetArcadeSdkPackageVersion>
1010
</PropertyGroup>
1111
<!--Property group for alternate package version names-->
1212
<PropertyGroup>

eng/Version.Details.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<Dependencies>
33
<ProductDependencies />
44
<ToolsetDependencies>
5-
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="11.0.0-beta.26381.1">
5+
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="11.0.0-beta.26407.8">
66
<Uri>https://github.com/dotnet/arcade</Uri>
7-
<Sha>93eebf1a31a5eaafd44326f1a81ca107913e098c</Sha>
7+
<Sha>212960245c74330fbfb71776563638061e35446c</Sha>
88
</Dependency>
99
</ToolsetDependencies>
1010
</Dependencies>

eng/common/Get-GitHubAppToken.ps1

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Mints a short-lived GitHub App installation access token by signing a JWT
2+
# with a private key stored in Azure Key Vault (RSA, RS256). The signed JWT is
3+
# exchanged with the GitHub API for a token scoped to a single installation.
4+
#
5+
# Requirements:
6+
# - A GitHub App whose private key has been uploaded into Key Vault as an RSA
7+
# key (the PEM converted to a Key Vault *key*, NOT stored as a secret).
8+
# - The caller (the federated Azure service connection used to run this script)
9+
# must have the `Key Vault Crypto User` role (or at minimum the `Sign`
10+
# action) on that key.
11+
# - The App must be installed on the target organization/account
12+
# (`InstallationOwner`) with the permissions/repositories it needs.
13+
#
14+
# Installation tokens (ghs_*) are exempt from the enterprise classic-PAT
15+
# lifetime policy, which is why this replaces the long-lived PAT.
16+
17+
[CmdletBinding()]
18+
param(
19+
# Name of the Key Vault that holds the GitHub App's RSA signing key.
20+
[Parameter(Mandatory = $true)]
21+
[string] $KeyVaultName,
22+
23+
# Name of the RSA key inside the Key Vault (the App's private key).
24+
[Parameter(Mandatory = $true)]
25+
[string] $KeyName,
26+
27+
# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
28+
[Parameter(Mandatory = $true)]
29+
[string] $AppClientId,
30+
31+
# Login of the organization or user account whose installation we should
32+
# mint the token for (e.g. `dotnet`, `microsoft`).
33+
[Parameter(Mandatory = $true)]
34+
[string] $InstallationOwner,
35+
36+
# Optional Azure DevOps pipeline variable name to set with the installation
37+
# token (marked as a secret). When not specified, the token is written to
38+
# stdout instead.
39+
[Parameter(Mandatory = $false)]
40+
[string] $OutputVariableName
41+
)
42+
43+
$ErrorActionPreference = 'Stop'
44+
$PSNativeCommandUseErrorActionPreference = $true
45+
46+
. $PSScriptRoot\pipeline-logging-functions.ps1
47+
48+
function ConvertTo-Base64Url([byte[]] $bytes) {
49+
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
50+
}
51+
52+
# Build JWT header and payload. Use [ordered] hashtables so JSON
53+
# serialization is deterministic.
54+
$jwtHeader = [ordered]@{
55+
alg = 'RS256'
56+
typ = 'JWT'
57+
}
58+
$now = [System.DateTimeOffset]::UtcNow
59+
$jwtPayload = [ordered]@{
60+
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
61+
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
62+
iss = $AppClientId
63+
}
64+
65+
$headerEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtHeader | ConvertTo-Json -Compress)))
66+
$payloadEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtPayload | ConvertTo-Json -Compress)))
67+
$signingInput = "$headerEncoded.$payloadEncoded"
68+
69+
# Key Vault `sign` expects the *digest* (base64), not the raw bytes.
70+
$sha256 = [System.Security.Cryptography.SHA256]::Create()
71+
$digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
72+
$digestBase64 = [Convert]::ToBase64String($digestBytes)
73+
74+
Write-Host "Signing JWT with key '$KeyName' in vault '$KeyVaultName'..."
75+
$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
76+
try {
77+
# Azure CLI can emit non-fatal Python warnings to stderr even when signing succeeds.
78+
# Use the exit code to determine success for this invocation.
79+
$PSNativeCommandUseErrorActionPreference = $false
80+
$signatureBase64 = az keyvault key sign `
81+
--vault-name $KeyVaultName `
82+
--name $KeyName `
83+
--algorithm RS256 `
84+
--digest $digestBase64 `
85+
--query signature `
86+
--output tsv `
87+
--only-show-errors
88+
$signExitCode = $LASTEXITCODE
89+
}
90+
catch {
91+
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."
92+
exit 1
93+
}
94+
finally {
95+
$PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
96+
}
97+
if ($signExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($signatureBase64)) {
98+
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."
99+
exit 1
100+
}
101+
$signatureUrl = $signatureBase64.Trim().TrimEnd('=').Replace('+', '-').Replace('/', '_')
102+
$jwt = "$signingInput.$signatureUrl"
103+
104+
$headers = @{
105+
Authorization = "Bearer $jwt"
106+
'X-GitHub-Api-Version' = '2022-11-28'
107+
Accept = 'application/vnd.github+json'
108+
'User-Agent' = 'dotnet-arcade-onelocbuild'
109+
}
110+
111+
Write-Host "Looking up installation for '$InstallationOwner'..."
112+
try {
113+
$installations = @()
114+
$page = 1
115+
do {
116+
$pageInstallations = @(Invoke-RestMethod `
117+
-Uri "https://api.github.com/app/installations?per_page=100&page=$page" `
118+
-Headers $headers `
119+
-Method Get)
120+
$installations += $pageInstallations
121+
$page++
122+
} while ($pageInstallations.Count -eq 100)
123+
}
124+
catch {
125+
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."
126+
exit 1
127+
}
128+
$installation = $installations | Where-Object { $_.account.login -ieq $InstallationOwner } | Select-Object -First 1
129+
if (-not $installation) {
130+
$found = ($installations | ForEach-Object { $_.account.login }) -join ', '
131+
Write-PipelineTelemetryError -Category 'Build' -Message "No installation found for '$InstallationOwner'. App is installed on: $found"
132+
exit 1
133+
}
134+
135+
try {
136+
$tokenResponse = Invoke-RestMethod `
137+
-Uri "https://api.github.com/app/installations/$($installation.id)/access_tokens" `
138+
-Headers $headers `
139+
-Method Post `
140+
-ContentType 'application/json'
141+
}
142+
catch {
143+
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to mint an installation access token for '$InstallationOwner' (installation $($installation.id)): $_"
144+
exit 1
145+
}
146+
147+
Write-Host "Got installation token for '$InstallationOwner' (expires $($tokenResponse.expires_at))."
148+
if ($OutputVariableName) {
149+
Write-Host "Setting pipeline variable '$OutputVariableName'."
150+
Write-Host "##vso[task.setvariable variable=$OutputVariableName;issecret=true]$($tokenResponse.token)"
151+
}
152+
else {
153+
Write-Host $tokenResponse.token -ForegroundColor Green
154+
}

eng/common/SetupNugetSources.ps1

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# condition: eq(variables['Agent.OS'], 'Windows_NT')
1212
# inputs:
1313
# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1
14-
# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token
14+
# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config
1515
# env:
1616
# Token: $(InternalFeedToken)
1717
#
@@ -29,12 +29,14 @@
2929
[CmdletBinding()]
3030
param (
3131
[Parameter(Mandatory = $true)][string]$ConfigFile,
32-
$Password
32+
# Keep the legacy name as an alias while callers migrate secrets to the Token environment variable.
33+
[Alias("Password")]$Credential
3334
)
3435

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

3941
# This script only consumes helper functions from tools.ps1 to configure NuGet feeds.
4042
# Skip importing configure-toolset.ps1 so that repo-specific toolset setup (e.g. acquiring
@@ -44,14 +46,14 @@ $disableConfigureToolsetImport = $true
4446
. $PSScriptRoot\tools.ps1
4547

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

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

5759
if ($packageSource -eq $null)
@@ -67,13 +69,13 @@ function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Usern
6769
Write-Host "Package source $SourceName already present and enabled."
6870
}
6971

70-
AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd
72+
AddCredential -Creds $creds -Source $SourceName -Username $Username -credential $credential
7173
}
7274

7375
# Add a credential node for the specified source
74-
function AddCredential($creds, $source, $username, $pwd) {
76+
function AddCredential($creds, $source, $username, $credential) {
7577
# If no cred supplied, don't do anything.
76-
if (!$pwd) {
78+
if (!$credential) {
7779
return;
7880
}
7981

@@ -108,27 +110,27 @@ function AddCredential($creds, $source, $username, $pwd) {
108110
$sourceElement.AppendChild($passwordElement) | Out-Null
109111
}
110112

111-
$passwordElement.SetAttribute("value", $pwd)
113+
$passwordElement.SetAttribute("value", $credential)
112114
}
113115

114116
# Enable all darc-int package sources.
115-
function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds) {
117+
function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds, $Credential) {
116118
$maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]")
117119
ForEach ($DisabledPackageSource in $maestroInternalSources) {
118-
EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key
120+
EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key -Credential $Credential
119121
}
120122
}
121123

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

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

131-
AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -pwd $Password
133+
AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -credential $credential
132134
return $true
133135
}
134136
return $false
@@ -153,7 +155,7 @@ if ($sources -eq $null) {
153155

154156
$creds = $null
155157
$feedSuffix = "v3/index.json"
156-
if ($Password) {
158+
if ($feedCredential) {
157159
$feedSuffix = "v2"
158160
# Looks for a <PackageSourceCredentials> node. Create it if none is found.
159161
$creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials")
@@ -169,16 +171,16 @@ $userName = "dn-bot"
169171
$disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources")
170172
if ($disabledSources -ne $null) {
171173
Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node"
172-
EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds
174+
EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds -Credential $feedCredential
173175
}
174176
$dotnetVersions = @('5','6','7','8','9','10')
175177

176178
foreach ($dotnetVersion in $dotnetVersions) {
177179
$feedPrefix = "dotnet" + $dotnetVersion;
178180
$dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']")
179181
if ($dotnetSource -ne $null) {
180-
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
181-
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
182+
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
183+
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
182184
}
183185
}
184186

eng/common/SetupNugetSources.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
# This logic is also abstracted into enable-internal-sources.yml.
2525

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

eng/common/core-templates/job/helix-job-monitor.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ parameters:
2626
type: string
2727
default: ''
2828

29+
# Whether failures in the monitor job should allow the pipeline to continue.
30+
- name: continueOnError
31+
type: boolean
32+
default: false
33+
2934
# NuGet package id of the Helix job monitor tool.
3035
- name: toolPackageId
3136
type: string
@@ -103,6 +108,7 @@ jobs:
103108
- job: HelixJobMonitor
104109
displayName: Monitor Helix Jobs
105110
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
111+
continueOnError: ${{ parameters.continueOnError }}
106112
${{ if ne(length(parameters.dependsOn), 0) }}:
107113
dependsOn: ${{ parameters.dependsOn }}
108114
${{ if ne(parameters.condition, '') }}:

eng/common/core-templates/job/onelocbuild.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ parameters:
1414
# exist, and any pipeline that sets this to '' fall back to PAT-based auth via the CeapexPat parameter.
1515
CeapexServiceConnection: 'dnceng-onelocbuild-ceapex'
1616

17+
# GitHub App authentication for the OneLoc check-in PR (dnceng/internal only).
18+
# The infrastructure identifiers are centralized here and the App path is enabled by default.
19+
# DevDiv requires its own project-scoped service connection before this path can be enabled there.
20+
UseGitHubAppAuthentication: true
21+
GitHubAppServiceConnection: 'dnceng-oneloc-githubapp'
22+
GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9'
23+
GitHubAppKeyVaultName: 'EngKeyVault'
24+
GitHubAppKeyName: 'oneloc-localization-app-key'
25+
1726
SourcesDirectory: $(System.DefaultWorkingDirectory)
1827
CreatePr: true
1928
AutoCompletePr: false
@@ -89,6 +98,20 @@ jobs:
8998
outputVariableName: 'CeapexEntraToken'
9099
condition: ${{ parameters.condition }}
91100

101+
# Mint a short-lived GitHub App installation token for the loc check-in PR (dnceng/internal only).
102+
# All other projects fall back to PAT-based auth, since the app service connection is scoped to dnceng/internal.
103+
- ${{ if and(eq(parameters.RepoType, 'gitHub'), eq(parameters.UseGitHubAppAuthentication, true), eq(variables['System.TeamProject'], 'internal')) }}:
104+
- template: /eng/common/core-templates/steps/get-github-app-token.yml
105+
parameters:
106+
is1ESPipeline: ${{ parameters.is1ESPipeline }}
107+
azureSubscription: ${{ parameters.GitHubAppServiceConnection }}
108+
keyVaultName: ${{ parameters.GitHubAppKeyVaultName }}
109+
keyName: ${{ parameters.GitHubAppKeyName }}
110+
appClientId: ${{ parameters.GitHubAppClientId }}
111+
installationOwner: ${{ parameters.GitHubOrg }}
112+
outputVariableName: 'GitHubAppInstallationToken'
113+
condition: ${{ parameters.condition }}
114+
92115
- task: OneLocBuild@2
93116
displayName: OneLocBuild
94117
env:
@@ -110,7 +133,10 @@ jobs:
110133
patVariable: ${{ parameters.CeapexPat }}
111134
${{ if eq(parameters.RepoType, 'gitHub') }}:
112135
repoType: ${{ parameters.RepoType }}
113-
gitHubPatVariable: "${{ parameters.GithubPat }}"
136+
${{ if and(eq(parameters.UseGitHubAppAuthentication, true), eq(variables['System.TeamProject'], 'internal')) }}:
137+
gitHubPatVariable: "$(GitHubAppInstallationToken)"
138+
${{ if or(eq(parameters.UseGitHubAppAuthentication, false), ne(variables['System.TeamProject'], 'internal')) }}:
139+
gitHubPatVariable: "${{ parameters.GithubPat }}"
114140
${{ if ne(parameters.MirrorRepo, '') }}:
115141
isMirrorRepoSelected: true
116142
gitHubOrganization: ${{ parameters.GitHubOrg }}

eng/common/core-templates/job/publish-build-assets.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ jobs:
5858
parameters:
5959
is1ESPipeline: ${{ parameters.is1ESPipeline }}
6060
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
61-
- group: Publish-Build-Assets
62-
- group: AzureDevOps-Artifact-Feeds-Pats
6361
- name: runCodesignValidationInjection
6462
value: false
6563
# unconditional - needed for logs publishing (redactor tool version)

eng/common/core-templates/post-build/common-variables.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
variables:
2-
- group: Publish-Build-Assets
3-
42
# Whether the build is internal or not
53
- name: IsInternalBuild
64
value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }}

0 commit comments

Comments
 (0)