From 1579c7c0295810a36db68f3ff9d087aec8cc59d8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 14:13:37 -0700
Subject: [PATCH 01/11] [release/8.0]: Bump
Microsoft.AspNetCore.Identity.EntityFrameworkCore from 8.0.30 to 8.0.31
(#38998)
---
.../EFCore.AspNet.Specification.Tests.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/EFCore.AspNet.Specification.Tests/EFCore.AspNet.Specification.Tests.csproj b/test/EFCore.AspNet.Specification.Tests/EFCore.AspNet.Specification.Tests.csproj
index 3cf7e13df88..e15f5414aa0 100644
--- a/test/EFCore.AspNet.Specification.Tests/EFCore.AspNet.Specification.Tests.csproj
+++ b/test/EFCore.AspNet.Specification.Tests/EFCore.AspNet.Specification.Tests.csproj
@@ -48,7 +48,7 @@
-
+
From 9b205370ad692b46c5a69969e7651dd134192def Mon Sep 17 00:00:00 2001
From: "dotnet-release-bot[bot]"
<280112824+dotnet-release-bot[bot]@users.noreply.github.com>
Date: Wed, 7 Oct 2026 11:47:19 -0700
Subject: [PATCH 02/11] Update branding on release/9.0 (#39184)
Co-authored-by: dotnet-release-bot[bot] <280112824+dotnet-release-bot[bot]@users.noreply.github.com>
---
eng/Versions.props | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/eng/Versions.props b/eng/Versions.props
index 0702caa3db6..6142047222f 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -1,9 +1,8 @@
- 9.0.21
+ 9.0.22
servicing
-
-
+
False
true
-
+
From 63ab07988ad160397f11f07655a913e3b0dbe620 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 7 Oct 2026 16:00:20 -0700
Subject: [PATCH 05/11] [release/9.0]: Bump Grpc.AspNetCore from 2.83.0 to
2.84.0 (#39132)
---
updated-dependencies:
- dependency-name: Grpc.AspNetCore
dependency-version: 2.84.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
test/Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/Directory.Packages.props b/test/Directory.Packages.props
index 6c0147cda28..2fe3e18fc38 100644
--- a/test/Directory.Packages.props
+++ b/test/Directory.Packages.props
@@ -14,7 +14,7 @@
-
+
From 7b7f63d004e8c9a3ae4d8434b7f6e2e9d5e4397b Mon Sep 17 00:00:00 2001
From: William Godbe
Date: Wed, 7 Oct 2026 16:00:56 -0700
Subject: [PATCH 06/11] [release/9.0] Avoid artifact name collisions on job
retries (#39051)
* Avoid artifact collisions on job retries
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Allow test result artifact upload failures
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Suffix custom log artifact names on retries
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Will Godbe
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
azure-pipelines-public.yml | 6 +++---
azure-pipelines.yml | 9 ++++++---
eng/common/templates-official/job/job.yml | 2 +-
eng/common/templates/job/job.yml | 2 +-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/azure-pipelines-public.yml b/azure-pipelines-public.yml
index a58d865a95a..d80a2832bd8 100644
--- a/azure-pipelines-public.yml
+++ b/azure-pipelines-public.yml
@@ -86,7 +86,7 @@ stages:
continueOnError: true
inputs:
pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
artifactType: Container
parallel: true
@@ -110,7 +110,7 @@ stages:
continueOnError: true
inputs:
pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
artifactType: Container
parallel: true
@@ -135,7 +135,7 @@ stages:
continueOnError: true
inputs:
pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
artifactType: Container
parallel: true
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 4b38f0ad63d..dfc835cfd93 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -139,8 +139,9 @@ extends:
- output: pipelineArtifact
displayName: Upload TestResults
condition: always()
+ continueOnError: true
path: artifacts/TestResults/$(_BuildConfig)/
- artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
- job: macOS
pool:
name: Azure Pipelines
@@ -166,8 +167,9 @@ extends:
- output: pipelineArtifact
displayName: Upload TestResults
condition: always()
+ continueOnError: true
path: artifacts/TestResults/$(_BuildConfig)/
- artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
- job: Linux
timeoutInMinutes: 180
pool:
@@ -191,8 +193,9 @@ extends:
- output: pipelineArtifact
displayName: Upload TestResults
condition: always()
+ continueOnError: true
path: artifacts/TestResults/$(_BuildConfig)/
- artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults_Attempt_$(System.JobAttempt)
- job: Helix
timeoutInMinutes: 180
pool:
diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml
index 81ea7a261f2..329dcc4b34a 100644
--- a/eng/common/templates-official/job/job.yml
+++ b/eng/common/templates-official/job/job.yml
@@ -46,7 +46,7 @@ jobs:
displayName: Publish Logs
PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
publishLocation: Container
- ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+ ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)') }}_Attempt_$(System.JobAttempt)
continueOnError: true
condition: always()
sbomEnabled: false # we don't need SBOM for logs
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index 5bdd3dd85fd..d5a4ae6b446 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -66,7 +66,7 @@ jobs:
displayName: Publish Logs
pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
publishLocation: Container
- artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+ artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)') }}_Attempt_$(System.JobAttempt)
continueOnError: true
condition: always()
From b36228c49620a32fbb72b30781e8063d1055fcbb Mon Sep 17 00:00:00 2001
From: "dotnet-maestro[bot]"
<42748379+dotnet-maestro[bot]@users.noreply.github.com>
Date: Thu, 8 Oct 2026 23:53:31 +0000
Subject: [PATCH 07/11] Update dependencies from build 335710 (#39204)
[release/10.0] Source code updates from dotnet/dotnet
---
NuGet.config | 2 +-
eng/Version.Details.props | 10 +-
eng/Version.Details.xml | 48 ++--
eng/common/Get-GitHubAppToken.ps1 | 123 +++++---
.../core-templates/job/helix-job-monitor.yml | 128 +++------
eng/common/core-templates/job/onelocbuild.yml | 8 +-
.../steps/get-github-app-token.yml | 29 +-
.../core-templates/steps/send-to-helix.yml | 264 +++++++++++-------
eng/common/run-helix-job-monitor.sh | 65 +++++
global.json | 4 +-
10 files changed, 400 insertions(+), 281 deletions(-)
create mode 100644 eng/common/run-helix-job-monitor.sh
diff --git a/NuGet.config b/NuGet.config
index 84597d1f8b2..db0ad66ff68 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,7 +4,7 @@
-
+
diff --git a/eng/Version.Details.props b/eng/Version.Details.props
index 61fb7d68d54..a1a16f27d70 100644
--- a/eng/Version.Details.props
+++ b/eng/Version.Details.props
@@ -6,9 +6,9 @@ This file should be imported by eng/Versions.props
- 10.0.0-beta.26505.113
- 10.0.0-beta.26505.113
- 10.0.0-beta.26505.113
+ 10.0.0-beta.26508.104
+ 10.0.0-beta.26508.104
+ 10.0.0-beta.26508.104
10.0.14
10.0.14
10.0.14
@@ -16,10 +16,10 @@ This file should be imported by eng/Versions.props
10.0.14
10.0.14
10.0.14
- 10.0.14-servicing.26505.113
+ 10.0.14-servicing.26508.104
10.0.14
10.0.14
- 10.0.14-servicing.26505.113
+ 10.0.14-servicing.26508.104
10.0.14
10.0.14
10.0.14
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 6812cd834a5..7f297dcb82b 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,80 +1,80 @@
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
-
+
https://github.com/dotnet/dotnet
- c4287c01fc8bb2c4ce9320bca4d61ec945a7493d
+ 8d12953c7e56fbc03f5b22372c0013d69c908517
diff --git a/eng/common/Get-GitHubAppToken.ps1 b/eng/common/Get-GitHubAppToken.ps1
index ea776bd6bc2..ec005e487c3 100644
--- a/eng/common/Get-GitHubAppToken.ps1
+++ b/eng/common/Get-GitHubAppToken.ps1
@@ -1,13 +1,11 @@
# 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.
+# with an RSA private key (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.
+# - A GitHub App ID and PEM private key stored as Azure Key Vault secrets.
+# - The federated Azure service connection running this script must have
+# `Get` access to those two secrets.
# - The App must be installed on the target organization/account
# (`InstallationOwner`) with the permissions/repositories it needs.
#
@@ -16,17 +14,17 @@
[CmdletBinding()]
param(
- # Name of the Key Vault that holds the GitHub App's RSA signing key.
+ # Name of the Key Vault holding the GitHub App credentials.
[Parameter(Mandatory = $true)]
[string] $KeyVaultName,
- # Name of the RSA key inside the Key Vault (the App's private key).
+ # Secret Manager projection containing the GitHub App ID.
[Parameter(Mandatory = $true)]
- [string] $KeyName,
+ [string] $AppIdSecretName,
- # The GitHub App's Client ID (the value to put in the `iss` JWT claim).
+ # Secret Manager projection containing the PEM private key.
[Parameter(Mandatory = $true)]
- [string] $AppClientId,
+ [string] $AppPrivateKeySecretName,
# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
@@ -39,16 +37,69 @@ param(
[Parameter(Mandatory = $false)]
[string] $OutputVariableName
)
-
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
. $PSScriptRoot\pipeline-logging-functions.ps1
+if ($KeyVaultName -notmatch '^[A-Za-z][A-Za-z0-9-]{1,22}[A-Za-z0-9]$' -or $KeyVaultName.Contains('--')) {
+ Write-PipelineTelemetryError -Category 'Build' -Message "KeyVaultName '$KeyVaultName' is not a valid Azure Key Vault name."
+ exit 1
+}
+
function ConvertTo-Base64Url([byte[]] $bytes) {
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}
+$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
+try {
+ # Azure CLI can emit non-fatal Python warnings to stderr.
+ $PSNativeCommandUseErrorActionPreference = $false
+ $keyVaultAccessToken = az account get-access-token `
+ --resource https://vault.azure.net `
+ --query accessToken `
+ --output tsv `
+ --only-show-errors
+ $tokenExitCode = $LASTEXITCODE
+}
+catch {
+ Write-PipelineTelemetryError -Category 'Build' -Message "Failed to acquire an Azure Key Vault access token: $_"
+ exit 1
+}
+finally {
+ $PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
+}
+if ($tokenExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($keyVaultAccessToken)) {
+ Write-PipelineTelemetryError -Category 'Build' -Message "'az account get-access-token' exited with code $tokenExitCode while acquiring an Azure Key Vault access token."
+ exit 1
+}
+
+function Get-KeyVaultSecret([string] $SecretName) {
+ # Use the data-plane REST API because `az keyvault secret show` can fail
+ # with Errno 22 on hosted Windows agents when reading these projections.
+ $escapedSecretName = [Uri]::EscapeDataString($SecretName)
+ $secretUri = "https://$KeyVaultName.vault.azure.net/secrets/$escapedSecretName`?api-version=7.4"
+ try {
+ $response = Invoke-RestMethod `
+ -Uri $secretUri `
+ -Headers @{ Authorization = "Bearer $keyVaultAccessToken" } `
+ -Method Get
+ }
+ catch {
+ Write-PipelineTelemetryError -Category 'Build' -Message "Failed to read secret '$SecretName' from vault '$KeyVaultName': $_. Verify the secret exists and the service connection has 'Key Vault Secrets User' access to it."
+ exit 1
+ }
+ if ([string]::IsNullOrWhiteSpace($response.value)) {
+ Write-PipelineTelemetryError -Category 'Build' -Message "Secret '$SecretName' in vault '$KeyVaultName' is empty."
+ exit 1
+ }
+ return [string] $response.value
+}
+
+Write-Host "Reading GitHub App credentials from vault '$KeyVaultName'..."
+$appId = Get-KeyVaultSecret $AppIdSecretName
+$privateKey = Get-KeyVaultSecret $AppPrivateKeySecretName
+
# Build JWT header and payload. Use [ordered] hashtables so JSON
# serialization is deterministic.
$jwtHeader = [ordered]@{
@@ -59,46 +110,38 @@ $now = [System.DateTimeOffset]::UtcNow
$jwtPayload = [ordered]@{
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
- iss = $AppClientId
+ iss = $appId
}
$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)
+$sha256 = [System.Security.Cryptography.SHA256]::Create()
+try {
+ $digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
+}
+finally {
+ $sha256.Dispose()
+}
-Write-Host "Signing JWT with key '$KeyName' in vault '$KeyVaultName'..."
-$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
+Write-Host 'Signing JWT with the GitHub App private key...'
+$rsa = [System.Security.Cryptography.RSA]::Create()
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
+ $rsa.ImportFromPem($privateKey)
+ $signatureBytes = $rsa.SignHash(
+ $digestBytes,
+ [System.Security.Cryptography.HashAlgorithmName]::SHA256,
+ [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
+ $signatureUrl = ConvertTo-Base64Url $signatureBytes
}
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."
+ Write-PipelineTelemetryError -Category 'Build' -Message "Failed to sign the GitHub App JWT with the supplied private 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
+ $rsa.Dispose()
}
-$signatureUrl = $signatureBase64.Trim().TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$signingInput.$signatureUrl"
$headers = @{
@@ -126,7 +169,7 @@ try {
} while ($pageInstallationCount -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."
+ Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App ID may be incorrect."
exit 1
}
$matchingInstallations = @($installations | Where-Object { $_.account.login -ieq $InstallationOwner })
diff --git a/eng/common/core-templates/job/helix-job-monitor.yml b/eng/common/core-templates/job/helix-job-monitor.yml
index c95e3a48cdd..a3dd1ab5341 100644
--- a/eng/common/core-templates/job/helix-job-monitor.yml
+++ b/eng/common/core-templates/job/helix-job-monitor.yml
@@ -69,7 +69,7 @@ parameters:
type: string
default: ''
-# Azure service connection ID authorized for Helix. Required when
+# Workload identity federation service connection name authorized for Helix. Required when
# useEntraAuthentication is true.
- name: azureSubscription
type: string
@@ -141,6 +141,21 @@ jobs:
displayName: Monitor Helix Jobs
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
continueOnError: ${{ parameters.continueOnError }}
+ variables:
+ HELIX_MONITOR_BASE_URI: ${{ parameters.helixBaseUri }}
+ HELIX_MONITOR_USE_ENTRA_AUTHENTICATION: ${{ parameters.useEntraAuthentication }}
+ HELIX_MONITOR_ENTRA_SCOPE: ${{ parameters.helixEntraScope }}
+ HELIX_MONITOR_POLLING_INTERVAL_SECONDS: ${{ parameters.pollingIntervalSeconds }}
+ HELIX_MONITOR_FAIL_ON_FAILED_TESTS: ${{ parameters.failWorkItemsWithFailedTests }}
+ HELIX_MONITOR_ALLOW_NO_HELIX_JOBS: ${{ parameters.allowNoHelixJobs }}
+ HELIX_MONITOR_USE_FULLY_QUALIFIED_TEST_NAME: ${{ parameters.useFullyQualifiedTestName }}
+ HELIX_MONITOR_TIMEOUT_IN_MINUTES: ${{ parameters.timeoutInMinutes }}
+ HELIX_MONITOR_TEST_RESULT_UPLOAD_PARALLELISM: ${{ parameters.testResultUploadParallelism }}
+ HELIX_MONITOR_ORGANIZATION: ${{ parameters.organization }}
+ HELIX_MONITOR_REPOSITORY: ${{ parameters.repository }}
+ HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE: ${{ parameters.testResultAttachmentMode }}
+ HELIX_MONITOR_TOOL_NUPKG_ARTIFACT_NAME: ${{ parameters.toolNupkgArtifactName }}
+ HELIX_MONITOR_TOOL_COMMAND: ${{ parameters.toolCommand }}
${{ if ne(length(parameters.dependsOn), 0) }}:
dependsOn: ${{ parameters.dependsOn }}
${{ if ne(parameters.condition, '') }}:
@@ -162,22 +177,6 @@ jobs:
- pwsh: throw "azureSubscription must be set when useEntraAuthentication is true."
displayName: Validate Helix Entra authentication
- - ${{ if eq(parameters.useEntraAuthentication, true) }}:
- - task: AzureCLI@2
- displayName: Initialize Helix Entra authentication
- inputs:
- azureSubscription: ${{ parameters.azureSubscription }}
- addSpnToEnvironment: true
- scriptType: pscore
- scriptLocation: inlineScript
- inlineScript: |
- if ([string]::IsNullOrWhiteSpace($env:servicePrincipalId) -or [string]::IsNullOrWhiteSpace($env:tenantId)) {
- throw "The Helix Azure service connection did not provide a service principal or tenant ID."
- }
-
- Write-Host "##vso[task.setvariable variable=HelixEntraClientId]$env:servicePrincipalId"
- Write-Host "##vso[task.setvariable variable=HelixEntraTenantId]$env:tenantId"
-
- ${{ if ne(parameters.toolNupkgArtifactName, '') }}:
- task: DownloadPipelineArtifact@2
displayName: Download Helix Job Monitor artifact
@@ -246,77 +245,26 @@ jobs:
- bash: ./eng/common/dotnet.sh tool restore
displayName: Restore Helix Job Monitor
- - bash: |
- set -euo pipefail
-
- toolArgs=(
- --helix-base-uri '${{ parameters.helixBaseUri }}'
- --use-entra-authentication '${{ parameters.useEntraAuthentication }}'
- --helix-entra-scope '${{ parameters.helixEntraScope }}'
- --polling-interval-seconds '${{ parameters.pollingIntervalSeconds }}'
- --fail-on-failed-tests '${{ parameters.failWorkItemsWithFailedTests }}'
- --allow-no-helix-jobs '${{ parameters.allowNoHelixJobs }}'
- --use-fully-qualified-test-name '${{ parameters.useFullyQualifiedTestName }}'
- --max-wait-minutes "$((${{ parameters.timeoutInMinutes }} - 5))" # Set the tool's timeout slightly lower than the Azure DevOps job timeout to allow it to exit gracefully.
- --stage-name '$(System.StageName)'
- --stage-attempt '$(System.StageAttempt)'
- --job-attempt '$(System.JobAttempt)'
- --test-result-upload-parallelism '${{ parameters.testResultUploadParallelism }}'
- )
-
- organization='${{ parameters.organization }}'
- repository='${{ parameters.repository }}'
- testResultAttachmentMode='${{ parameters.testResultAttachmentMode }}'
-
- # Fall back to Azure DevOps-provided environment variables when the caller did not
- # supply organization / repository explicitly. BUILD_REPOSITORY_NAME is typically
- # 'owner/repo' for GitHub-backed builds and 'owner-repo' for internal builds.
- if [ -z "$organization" ] || [ -z "$repository" ]; then
- buildRepoName="${BUILD_REPOSITORY_NAME:-}"
- if [ -n "$buildRepoName" ] && [[ "$buildRepoName" == */* ]]; then
- repoOwner="${buildRepoName%%/*}"
- repoName="${buildRepoName#*/}"
- elif [ -n "$buildRepoName" ] && [[ "$buildRepoName" == *-* ]]; then
- repoOwner="${buildRepoName%%-*}"
- repoName="${buildRepoName#*-}"
- fi
-
- if [ -n "${repoOwner:-}" ] && [ -n "${repoName:-}" ]; then
- if [ -z "$organization" ]; then organization="$repoOwner"; fi
- if [ -z "$repository" ]; then repository="$repoName"; fi
- fi
- fi
-
- if [ -n "$organization" ]; then toolArgs+=( --organization "$organization" ); fi
- if [ -n "$repository" ]; then toolArgs+=( --repository "$repository" ); fi
- if [ -n "$testResultAttachmentMode" ]; then
- toolArgs+=( --test-result-attachment-mode "$testResultAttachmentMode" )
- fi
-
- # Build.Reason and Build.SourceBranch are required to derive the Helix source filter
- # the same way the Helix SDK submitter does (PR -> 'pr', internal -> 'official',
- # otherwise -> 'ci'). Without these, manually-queued / scheduled / CI builds would
- # be looked up under the wrong source prefix and find zero jobs.
- toolArgs+=( --build-reason "$(Build.Reason)" )
- toolArgs+=( --source-branch "$(Build.SourceBranch)" )
-
- if [ -n '${{ parameters.toolNupkgArtifactName }}' ]; then
- # Tool was installed from a local nupkg; run the DLL via the repo-local dotnet.
- export DOTNET_ROOT="$(Build.SourcesDirectory)/.dotnet"
- ./eng/common/dotnet.sh exec "$(HelixJobMonitorDll)" "${toolArgs[@]}"
- else
- # Tool was restored from the local .config/dotnet-tools.json manifest; invoke it
- # through the manifest from the repo root.
- pushd "$BUILD_SOURCESDIRECTORY" > /dev/null
- trap 'popd > /dev/null' EXIT
- ./eng/common/dotnet.sh tool run '${{ parameters.toolCommand }}' -- "${toolArgs[@]}"
- fi
- displayName: Monitor Helix Jobs
- env:
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- ${{ if eq(parameters.useEntraAuthentication, false) }}:
+ - ${{ if eq(parameters.useEntraAuthentication, true) }}:
+ - task: AzureCLI@2
+ displayName: Monitor Helix Jobs
+ inputs:
+ azureSubscription: ${{ parameters.azureSubscription }}
+ keepAzSessionActive: true
+ scriptType: bash
+ scriptLocation: inlineScript
+ inlineScript: |
+ set -euo pipefail
+
+ # AzureCLI@2 replaces SYSTEM_ACCESSTOKEN with the service connection token.
+ export SYSTEM_ACCESSTOKEN="$HELIX_AZDO_ACCESSTOKEN"
+ bash "$BUILD_SOURCESDIRECTORY/eng/common/run-helix-job-monitor.sh"
+ env:
+ HELIX_AZDO_ACCESSTOKEN: $(System.AccessToken)
+
+ - ${{ if eq(parameters.useEntraAuthentication, false) }}:
+ - bash: bash "$BUILD_SOURCESDIRECTORY/eng/common/run-helix-job-monitor.sh"
+ displayName: Monitor Helix Jobs
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
HELIX_ACCESSTOKEN: ${{ parameters.helixAccessToken }}
- ${{ if eq(parameters.useEntraAuthentication, true) }}:
- AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId)
- AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId)
- AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.azureSubscription }}
diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml
index d7a69f1c76e..adcdfa17536 100644
--- a/eng/common/core-templates/job/onelocbuild.yml
+++ b/eng/common/core-templates/job/onelocbuild.yml
@@ -10,9 +10,9 @@ parameters:
# GitHub App authentication for the OneLoc check-in PR.
GitHubAppServiceConnection: 'dnceng-oneloc-githubapp'
- GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9'
GitHubAppKeyVaultName: 'EngKeyVault'
- GitHubAppKeyName: 'oneloc-localization-app-key'
+ GitHubAppIdSecretName: 'oneloc-localization-app-app-id'
+ GitHubAppPrivateKeySecretName: 'oneloc-localization-app-app-private-key'
SourcesDirectory: $(System.DefaultWorkingDirectory)
CreatePr: true
@@ -101,8 +101,8 @@ jobs:
${{ else }}:
azureSubscription: ${{ parameters.GitHubAppServiceConnection }}
keyVaultName: ${{ parameters.GitHubAppKeyVaultName }}
- keyName: ${{ parameters.GitHubAppKeyName }}
- appClientId: ${{ parameters.GitHubAppClientId }}
+ appIdSecretName: ${{ parameters.GitHubAppIdSecretName }}
+ appPrivateKeySecretName: ${{ parameters.GitHubAppPrivateKeySecretName }}
installationOwner: ${{ parameters.GitHubOrg }}
outputVariableName: 'GitHubAppInstallationToken'
condition: ${{ parameters.condition }}
diff --git a/eng/common/core-templates/steps/get-github-app-token.yml b/eng/common/core-templates/steps/get-github-app-token.yml
index 6d42a48d3c3..3eeb5a4c1bc 100644
--- a/eng/common/core-templates/steps/get-github-app-token.yml
+++ b/eng/common/core-templates/steps/get-github-app-token.yml
@@ -1,13 +1,11 @@
# 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 JWT is
-# exchanged with the GitHub API for a token scoped to a single installation.
+# with an RSA private key (RS256). The JWT is exchanged with the GitHub API
+# for a token scoped to a single installation.
#
# Requirements (per GitHub App you want to authenticate as):
-# - A GitHub App with its private key uploaded into Key Vault as an RSA key
-# (PEM converted to a key, NOT stored as a secret).
-# - The Azure service connection passed via `azureSubscription` must be
-# granted the `Key Vault Crypto User` role (or at minimum `Sign` action)
-# on that key.
+# - A GitHub App ID and PEM private key stored as Azure Key Vault secrets.
+# - The Azure service connection passed via `azureSubscription` must have
+# `Get` access to those two secrets.
# - The App must be installed on the target organization/account
# (`installationOwner`) with the permissions/repositories you need.
#
@@ -17,23 +15,18 @@
# enterprise classic-PAT lifetime policy.
parameters:
-# Azure DevOps service connection (federated) that can call
-# `az keyvault key sign` on the App's signing key.
+# Azure DevOps service connection (federated) that can read the App credentials.
- name: azureSubscription
type: string
-# Name of the Key Vault that holds the GitHub App's RSA signing key.
+# Name of the Key Vault holding Secret Manager's github-app-secret projections.
- name: keyVaultName
type: string
-# Name of the RSA key inside the Key Vault (the App's private key).
-- name: keyName
+- name: appIdSecretName
type: string
-# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
-# Prefer this over the numeric App ID; GitHub accepts either, but Client ID
-# is the documented form going forward.
-- name: appClientId
+- name: appPrivateKeySecretName
type: string
# Login of the organization or user account whose installation we should
@@ -73,7 +66,7 @@ steps:
inlineScript: |
& "$(System.DefaultWorkingDirectory)/eng/common/Get-GitHubAppToken.ps1" `
-KeyVaultName '${{ parameters.keyVaultName }}' `
- -KeyName '${{ parameters.keyName }}' `
- -AppClientId '${{ parameters.appClientId }}' `
+ -AppIdSecretName '${{ parameters.appIdSecretName }}' `
+ -AppPrivateKeySecretName '${{ parameters.appPrivateKeySecretName }}' `
-InstallationOwner '${{ parameters.installationOwner }}' `
-OutputVariableName '${{ parameters.outputVariableName }}'
diff --git a/eng/common/core-templates/steps/send-to-helix.yml b/eng/common/core-templates/steps/send-to-helix.yml
index 3951e47104a..41a94526ea0 100644
--- a/eng/common/core-templates/steps/send-to-helix.yml
+++ b/eng/common/core-templates/steps/send-to-helix.yml
@@ -7,7 +7,7 @@ parameters:
HelixAccessToken: '' # optional -- legacy access token; not forwarded when HelixUseEntraAuthentication is true
HelixUseEntraAuthentication: false # optional -- use refreshable Entra authentication instead of a PAT or anonymous access
HelixEntraScope: '' # optional -- explicit Entra scope required for custom HelixBaseUri hosts
- HelixAzureSubscription: '' # required when HelixUseEntraAuthentication is true -- Azure service connection ID authorized for Helix
+ HelixAzureSubscription: '' # required when HelixUseEntraAuthentication is true -- workload identity federation service connection name authorized for Helix
HelixProjectPath: 'eng/common/helixpublish.proj' # optional -- path to the project file to build relative to BUILD_SOURCESDIRECTORY
HelixProjectArguments: '' # optional -- arguments passed to the build command
HelixConfiguration: '' # optional -- additional property attached to a job
@@ -42,108 +42,178 @@ steps:
- ${{ if eq(parameters.HelixUseEntraAuthentication, true) }}:
- task: AzureCLI@2
- displayName: Initialize Helix Entra authentication
+ displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
inputs:
azureSubscription: ${{ parameters.HelixAzureSubscription }}
- addSpnToEnvironment: true
+ keepAzSessionActive: true
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
- if ([string]::IsNullOrWhiteSpace($env:servicePrincipalId) -or [string]::IsNullOrWhiteSpace($env:tenantId)) {
- throw "The Helix Azure service connection did not provide a service principal or tenant ID."
- }
+ # AzureCLI@2 replaces SYSTEM_ACCESSTOKEN with the service connection token.
+ $env:SYSTEM_ACCESSTOKEN = $env:HELIX_AZDO_ACCESSTOKEN
- Write-Host "##vso[task.setvariable variable=HelixEntraClientId]$env:servicePrincipalId"
- Write-Host "##vso[task.setvariable variable=HelixEntraTenantId]$env:tenantId"
- condition: ${{ parameters.condition }}
+ $(Build.SourcesDirectory)\eng\common\msbuild.ps1 `
+ $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }} `
+ /restore `
+ /p:TreatWarningsAsErrors=false `
+ /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }} `
+ /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }} `
+ ${{ parameters.HelixProjectArguments }} `
+ /t:Test `
+ /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ HelixEntraScope: ${{ parameters.HelixEntraScope }}
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ HELIX_AZDO_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
+ - task: AzureCLI@2
+ displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
+ inputs:
+ azureSubscription: ${{ parameters.HelixAzureSubscription }}
+ keepAzSessionActive: true
+ scriptType: bash
+ scriptLocation: inlineScript
+ inlineScript: |
+ # AzureCLI@2 replaces SYSTEM_ACCESSTOKEN with the service connection token.
+ export SYSTEM_ACCESSTOKEN="$HELIX_AZDO_ACCESSTOKEN"
+
+ $(Build.SourcesDirectory)/eng/common/msbuild.sh \
+ $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }} \
+ /restore \
+ /p:TreatWarningsAsErrors=false \
+ /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }} \
+ /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }} \
+ ${{ parameters.HelixProjectArguments }} \
+ /t:Test \
+ /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ HelixEntraScope: ${{ parameters.HelixEntraScope }}
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ HELIX_AZDO_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
- - powershell: >
- $(Build.SourcesDirectory)\eng\common\msbuild.ps1
- $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
- /restore
- /p:TreatWarningsAsErrors=false
- /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
- /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
- ${{ parameters.HelixProjectArguments }}
- /t:Test
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
- displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
- env:
- BuildConfig: $(_BuildConfig)
- HelixSource: ${{ parameters.HelixSource }}
- HelixType: ${{ parameters.HelixType }}
- HelixBuild: ${{ parameters.HelixBuild }}
- HelixConfiguration: ${{ parameters.HelixConfiguration }}
- HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
- ${{ if eq(parameters.HelixUseEntraAuthentication, false) }}:
+ - ${{ if eq(parameters.HelixUseEntraAuthentication, false) }}:
+ - powershell: >
+ $(Build.SourcesDirectory)\eng\common\msbuild.ps1
+ $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
+ /restore
+ /p:TreatWarningsAsErrors=false
+ /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
+ /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
+ ${{ parameters.HelixProjectArguments }}
+ /t:Test
+ /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
+ displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
HelixAccessToken: ${{ parameters.HelixAccessToken }}
- ${{ if eq(parameters.HelixUseEntraAuthentication, true) }}:
- AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId)
- AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId)
- AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.HelixAzureSubscription }}
- HelixEntraScope: ${{ parameters.HelixEntraScope }}
- HelixPreCommands: ${{ parameters.HelixPreCommands }}
- HelixPostCommands: ${{ parameters.HelixPostCommands }}
- WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
- WorkItemCommand: ${{ parameters.WorkItemCommand }}
- WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
- CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
- XUnitProjects: ${{ parameters.XUnitProjects }}
- XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
- XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
- XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
- XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
- IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
- DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
- DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
- WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
- HelixBaseUri: ${{ parameters.HelixBaseUri }}
- Creator: ${{ parameters.Creator }}
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
- continueOnError: ${{ parameters.continueOnError }}
- - script: >
- $(Build.SourcesDirectory)/eng/common/msbuild.sh
- $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
- /restore
- /p:TreatWarningsAsErrors=false
- /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
- /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
- ${{ parameters.HelixProjectArguments }}
- /t:Test
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
- displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
- env:
- BuildConfig: $(_BuildConfig)
- HelixSource: ${{ parameters.HelixSource }}
- HelixType: ${{ parameters.HelixType }}
- HelixBuild: ${{ parameters.HelixBuild }}
- HelixConfiguration: ${{ parameters.HelixConfiguration }}
- HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
- ${{ if eq(parameters.HelixUseEntraAuthentication, false) }}:
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
+ - script: >
+ $(Build.SourcesDirectory)/eng/common/msbuild.sh
+ $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
+ /restore
+ /p:TreatWarningsAsErrors=false
+ /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
+ /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
+ ${{ parameters.HelixProjectArguments }}
+ /t:Test
+ /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
+ displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
HelixAccessToken: ${{ parameters.HelixAccessToken }}
- ${{ if eq(parameters.HelixUseEntraAuthentication, true) }}:
- AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId)
- AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId)
- AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.HelixAzureSubscription }}
- HelixEntraScope: ${{ parameters.HelixEntraScope }}
- HelixPreCommands: ${{ parameters.HelixPreCommands }}
- HelixPostCommands: ${{ parameters.HelixPostCommands }}
- WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
- WorkItemCommand: ${{ parameters.WorkItemCommand }}
- WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
- CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
- XUnitProjects: ${{ parameters.XUnitProjects }}
- XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
- XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
- XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
- XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
- IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
- DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
- DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
- WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
- HelixBaseUri: ${{ parameters.HelixBaseUri }}
- Creator: ${{ parameters.Creator }}
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT'))
- continueOnError: ${{ parameters.continueOnError }}
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/run-helix-job-monitor.sh b/eng/common/run-helix-job-monitor.sh
new file mode 100644
index 00000000000..c79bd98e3f7
--- /dev/null
+++ b/eng/common/run-helix-job-monitor.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+scriptroot="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. "$scriptroot/pipeline-logging-functions.sh"
+
+toolArgs=(
+ --helix-base-uri "$HELIX_MONITOR_BASE_URI"
+ --use-entra-authentication "$HELIX_MONITOR_USE_ENTRA_AUTHENTICATION"
+ --helix-entra-scope "$HELIX_MONITOR_ENTRA_SCOPE"
+ --polling-interval-seconds "$HELIX_MONITOR_POLLING_INTERVAL_SECONDS"
+ --fail-on-failed-tests "$HELIX_MONITOR_FAIL_ON_FAILED_TESTS"
+ --allow-no-helix-jobs "$HELIX_MONITOR_ALLOW_NO_HELIX_JOBS"
+ --use-fully-qualified-test-name "$HELIX_MONITOR_USE_FULLY_QUALIFIED_TEST_NAME"
+ --max-wait-minutes "$((HELIX_MONITOR_TIMEOUT_IN_MINUTES - 5))"
+ --stage-name "$SYSTEM_STAGENAME"
+ --stage-attempt "$SYSTEM_STAGEATTEMPT"
+ --job-attempt "$SYSTEM_JOBATTEMPT"
+ --test-result-upload-parallelism "$HELIX_MONITOR_TEST_RESULT_UPLOAD_PARALLELISM"
+)
+
+organization="${HELIX_MONITOR_ORGANIZATION:-}"
+repository="${HELIX_MONITOR_REPOSITORY:-}"
+
+# Fall back to Azure DevOps-provided environment variables when the caller did not
+# supply organization or repository explicitly.
+if [ -z "$organization" ] || [ -z "$repository" ]; then
+ buildRepoName="${BUILD_REPOSITORY_NAME:-}"
+ if [ -n "$buildRepoName" ] && [[ "$buildRepoName" == */* ]]; then
+ repoOwner="${buildRepoName%%/*}"
+ repoName="${buildRepoName#*/}"
+ elif [ -n "$buildRepoName" ] && [[ "$buildRepoName" == *-* ]]; then
+ repoOwner="${buildRepoName%%-*}"
+ repoName="${buildRepoName#*-}"
+ fi
+
+ if [ -n "${repoOwner:-}" ] && [ -n "${repoName:-}" ]; then
+ if [ -z "$organization" ]; then organization="$repoOwner"; fi
+ if [ -z "$repository" ]; then repository="$repoName"; fi
+ fi
+fi
+
+if [ -n "$organization" ]; then toolArgs+=( --organization "$organization" ); fi
+if [ -n "$repository" ]; then toolArgs+=( --repository "$repository" ); fi
+if [ -n "${HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE:-}" ]; then
+ toolArgs+=( --test-result-attachment-mode "$HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE" )
+fi
+
+# These values let the monitor derive the same Helix source filter as the SDK submitter.
+toolArgs+=( --build-reason "$BUILD_REASON" )
+toolArgs+=( --source-branch "$BUILD_SOURCEBRANCH" )
+
+cd "$BUILD_SOURCESDIRECTORY"
+exitCode=0
+if [ -n "${HELIX_MONITOR_TOOL_NUPKG_ARTIFACT_NAME:-}" ]; then
+ export DOTNET_ROOT="$BUILD_SOURCESDIRECTORY/.dotnet"
+ ./eng/common/dotnet.sh exec "$HELIXJOBMONITORDLL" "${toolArgs[@]}" || exitCode=$?
+else
+ ./eng/common/dotnet.sh tool run "$HELIX_MONITOR_TOOL_COMMAND" -- "${toolArgs[@]}" || exitCode=$?
+fi
+
+if [ "$exitCode" -ne 0 ]; then
+ Write-PipelineTelemetryError -force -category 'Helix' "Helix job monitor failed (exit code '$exitCode')."
+ exit "$exitCode"
+fi
diff --git a/global.json b/global.json
index fb1725a1820..f56babbff7e 100644
--- a/global.json
+++ b/global.json
@@ -18,7 +18,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26505.113",
- "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26505.113"
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26508.104",
+ "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26508.104"
}
}
From 318bf8b87a4906d20df149161d26f90932f4abdf Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Oct 2026 12:19:49 -0700
Subject: [PATCH 08/11] [release/10.0] Report CommandSource.ExecuteDelete for
ExecuteDelete commands (#39197)
Fixes #39186
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
...alShapedQueryCompilingExpressionVisitor.cs | 2 +-
.../CommandInterceptionTestBase.cs | 46 +++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
index df54cef0bb0..34e84bb2504 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
@@ -89,7 +89,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
return extensionExpression switch
{
UpdateExpression updateExpression => GenerateNonQueryShaper(updateExpression.ApplyTags(_tags), CommandSource.ExecuteUpdate),
- DeleteExpression deleteExpression => GenerateNonQueryShaper(deleteExpression.ApplyTags(_tags), CommandSource.ExecuteUpdate),
+ DeleteExpression deleteExpression => GenerateNonQueryShaper(deleteExpression.ApplyTags(_tags), CommandSource.ExecuteDelete),
_ => base.VisitExtension(extensionExpression)
};
diff --git a/test/EFCore.Relational.Specification.Tests/CommandInterceptionTestBase.cs b/test/EFCore.Relational.Specification.Tests/CommandInterceptionTestBase.cs
index aa1081e8ac2..f01b98609f5 100644
--- a/test/EFCore.Relational.Specification.Tests/CommandInterceptionTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/CommandInterceptionTestBase.cs
@@ -101,6 +101,52 @@ public virtual async Task Intercept_non_query_passively(bool async, bool inject)
protected class PassiveNonQueryCommandInterceptor() : CommandInterceptorBase(DbCommandMethod.ExecuteNonQuery);
+ [ConditionalTheory, InlineData(false, false), InlineData(true, false), InlineData(false, true), InlineData(true, true)]
+ public virtual async Task Intercept_ExecuteDelete_passively(bool async, bool inject)
+ {
+ var (context, interceptor) = await CreateContextAsync(inject);
+ using (context)
+ {
+ using (context.Database.BeginTransaction())
+ {
+ using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
+ var query = context.Set().Where(e => e.Id == 77);
+ var result = async
+ ? await query.ExecuteDeleteAsync()
+ : query.ExecuteDelete();
+
+ Assert.Equal(1, result);
+
+ AssertNormalOutcome(context, interceptor, async, CommandSource.ExecuteDelete);
+
+ AssertExecutedEvents(listener);
+ }
+ }
+ }
+
+ [ConditionalTheory, InlineData(false, false), InlineData(true, false), InlineData(false, true), InlineData(true, true)]
+ public virtual async Task Intercept_ExecuteUpdate_passively(bool async, bool inject)
+ {
+ var (context, interceptor) = await CreateContextAsync(inject);
+ using (context)
+ {
+ using (context.Database.BeginTransaction())
+ {
+ using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
+ var query = context.Set().Where(e => e.Id == 77);
+ var result = async
+ ? await query.ExecuteUpdateAsync(s => s.SetProperty(e => e.Type, "White Hole"))
+ : query.ExecuteUpdate(s => s.SetProperty(e => e.Type, "White Hole"));
+
+ Assert.Equal(1, result);
+
+ AssertNormalOutcome(context, interceptor, async, CommandSource.ExecuteUpdate);
+
+ AssertExecutedEvents(listener);
+ }
+ }
+ }
+
[ConditionalTheory, InlineData(false, false), InlineData(true, false), InlineData(false, true), InlineData(true, true)]
public virtual async Task Intercept_query_to_suppress_execution(bool async, bool inject)
{
From 6133917412ecea3e1e6b2a50148fc394dd58ae48 Mon Sep 17 00:00:00 2001
From: "dotnet-maestro[bot]"
<42748379+dotnet-maestro[bot]@users.noreply.github.com>
Date: Fri, 9 Oct 2026 16:42:28 -0700
Subject: [PATCH 09/11] [release/10.0] Source code updates from dotnet/dotnet
(#39207)
Updated Dependencies:
Microsoft.DotNet.Arcade.Sdk, Microsoft.DotNet.Build.Tasks.Templating, Microsoft.DotNet.Helix.Sdk (Version 10.0.0-beta.26508.104 -> 10.0.0-beta.26508.128)
Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.DependencyModel, Microsoft.Extensions.Logging, Microsoft.NETCore.App.Ref, System.Formats.Asn1, System.Runtime.Caching, System.Text.Encodings.Web, System.Text.Json (Version 10.0.14 -> 10.0.14)
Microsoft.Extensions.HostFactoryResolver.Sources, Microsoft.NETCore.Platforms (Version 10.0.14-servicing.26508.104 -> 10.0.14-servicing.26508.128)
[[ commit created by automation ]]
---
NuGet.config | 2 +-
eng/Version.Details.props | 10 ++++----
eng/Version.Details.xml | 48 +++++++++++++++++++--------------------
global.json | 4 ++--
4 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/NuGet.config b/NuGet.config
index db0ad66ff68..41eb2056e76 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,7 +4,7 @@
-
+
diff --git a/eng/Version.Details.props b/eng/Version.Details.props
index a1a16f27d70..87558d746db 100644
--- a/eng/Version.Details.props
+++ b/eng/Version.Details.props
@@ -6,9 +6,9 @@ This file should be imported by eng/Versions.props
- 10.0.0-beta.26508.104
- 10.0.0-beta.26508.104
- 10.0.0-beta.26508.104
+ 10.0.0-beta.26508.128
+ 10.0.0-beta.26508.128
+ 10.0.0-beta.26508.128
10.0.14
10.0.14
10.0.14
@@ -16,10 +16,10 @@ This file should be imported by eng/Versions.props
10.0.14
10.0.14
10.0.14
- 10.0.14-servicing.26508.104
+ 10.0.14-servicing.26508.128
10.0.14
10.0.14
- 10.0.14-servicing.26508.104
+ 10.0.14-servicing.26508.128
10.0.14
10.0.14
10.0.14
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 7f297dcb82b..b96079265aa 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,80 +1,80 @@
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
-
+
https://github.com/dotnet/dotnet
- 8d12953c7e56fbc03f5b22372c0013d69c908517
+ 476261becdd44d137ca9c688d059788d24330975
diff --git a/global.json b/global.json
index f56babbff7e..67ced961707 100644
--- a/global.json
+++ b/global.json
@@ -18,7 +18,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26508.104",
- "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26508.104"
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26508.128",
+ "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26508.128"
}
}
From d8388e5e5008747ab8df475131981086fa2b1758 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 12 Oct 2026 18:49:52 +0000
Subject: [PATCH 10/11] Revert eng/common merge changes
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---
eng/common/Get-GitHubAppToken.ps1 | 123 ++++++----------
.../core-templates/job/helix-job-monitor.yml | 35 +++--
eng/common/core-templates/job/onelocbuild.yml | 8 +-
.../steps/get-github-app-token.yml | 29 ++--
.../core-templates/steps/send-to-helix.yml | 135 ++++--------------
eng/common/run-helix-job-monitor.sh | 65 ---------
6 files changed, 109 insertions(+), 286 deletions(-)
delete mode 100644 eng/common/run-helix-job-monitor.sh
diff --git a/eng/common/Get-GitHubAppToken.ps1 b/eng/common/Get-GitHubAppToken.ps1
index ec005e487c3..ea776bd6bc2 100644
--- a/eng/common/Get-GitHubAppToken.ps1
+++ b/eng/common/Get-GitHubAppToken.ps1
@@ -1,11 +1,13 @@
# Mints a short-lived GitHub App installation access token by signing a JWT
-# with an RSA private key (RS256). The signed JWT is exchanged with the GitHub
-# API for a token scoped to a single installation.
+# 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 ID and PEM private key stored as Azure Key Vault secrets.
-# - The federated Azure service connection running this script must have
-# `Get` access to those two secrets.
+# - 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.
#
@@ -14,17 +16,17 @@
[CmdletBinding()]
param(
- # Name of the Key Vault holding the GitHub App credentials.
+ # Name of the Key Vault that holds the GitHub App's RSA signing key.
[Parameter(Mandatory = $true)]
[string] $KeyVaultName,
- # Secret Manager projection containing the GitHub App ID.
+ # Name of the RSA key inside the Key Vault (the App's private key).
[Parameter(Mandatory = $true)]
- [string] $AppIdSecretName,
+ [string] $KeyName,
- # Secret Manager projection containing the PEM private key.
+ # The GitHub App's Client ID (the value to put in the `iss` JWT claim).
[Parameter(Mandatory = $true)]
- [string] $AppPrivateKeySecretName,
+ [string] $AppClientId,
# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
@@ -37,69 +39,16 @@ param(
[Parameter(Mandatory = $false)]
[string] $OutputVariableName
)
+
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
. $PSScriptRoot\pipeline-logging-functions.ps1
-if ($KeyVaultName -notmatch '^[A-Za-z][A-Za-z0-9-]{1,22}[A-Za-z0-9]$' -or $KeyVaultName.Contains('--')) {
- Write-PipelineTelemetryError -Category 'Build' -Message "KeyVaultName '$KeyVaultName' is not a valid Azure Key Vault name."
- exit 1
-}
-
function ConvertTo-Base64Url([byte[]] $bytes) {
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}
-$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
-try {
- # Azure CLI can emit non-fatal Python warnings to stderr.
- $PSNativeCommandUseErrorActionPreference = $false
- $keyVaultAccessToken = az account get-access-token `
- --resource https://vault.azure.net `
- --query accessToken `
- --output tsv `
- --only-show-errors
- $tokenExitCode = $LASTEXITCODE
-}
-catch {
- Write-PipelineTelemetryError -Category 'Build' -Message "Failed to acquire an Azure Key Vault access token: $_"
- exit 1
-}
-finally {
- $PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
-}
-if ($tokenExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($keyVaultAccessToken)) {
- Write-PipelineTelemetryError -Category 'Build' -Message "'az account get-access-token' exited with code $tokenExitCode while acquiring an Azure Key Vault access token."
- exit 1
-}
-
-function Get-KeyVaultSecret([string] $SecretName) {
- # Use the data-plane REST API because `az keyvault secret show` can fail
- # with Errno 22 on hosted Windows agents when reading these projections.
- $escapedSecretName = [Uri]::EscapeDataString($SecretName)
- $secretUri = "https://$KeyVaultName.vault.azure.net/secrets/$escapedSecretName`?api-version=7.4"
- try {
- $response = Invoke-RestMethod `
- -Uri $secretUri `
- -Headers @{ Authorization = "Bearer $keyVaultAccessToken" } `
- -Method Get
- }
- catch {
- Write-PipelineTelemetryError -Category 'Build' -Message "Failed to read secret '$SecretName' from vault '$KeyVaultName': $_. Verify the secret exists and the service connection has 'Key Vault Secrets User' access to it."
- exit 1
- }
- if ([string]::IsNullOrWhiteSpace($response.value)) {
- Write-PipelineTelemetryError -Category 'Build' -Message "Secret '$SecretName' in vault '$KeyVaultName' is empty."
- exit 1
- }
- return [string] $response.value
-}
-
-Write-Host "Reading GitHub App credentials from vault '$KeyVaultName'..."
-$appId = Get-KeyVaultSecret $AppIdSecretName
-$privateKey = Get-KeyVaultSecret $AppPrivateKeySecretName
-
# Build JWT header and payload. Use [ordered] hashtables so JSON
# serialization is deterministic.
$jwtHeader = [ordered]@{
@@ -110,38 +59,46 @@ $now = [System.DateTimeOffset]::UtcNow
$jwtPayload = [ordered]@{
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
- iss = $appId
+ 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"
-$sha256 = [System.Security.Cryptography.SHA256]::Create()
-try {
- $digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
-}
-finally {
- $sha256.Dispose()
-}
+# 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 the GitHub App private key...'
-$rsa = [System.Security.Cryptography.RSA]::Create()
+Write-Host "Signing JWT with key '$KeyName' in vault '$KeyVaultName'..."
+$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
try {
- $rsa.ImportFromPem($privateKey)
- $signatureBytes = $rsa.SignHash(
- $digestBytes,
- [System.Security.Cryptography.HashAlgorithmName]::SHA256,
- [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
- $signatureUrl = ConvertTo-Base64Url $signatureBytes
+ # 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 GitHub App JWT with the supplied private key: $_"
+ 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 {
- $rsa.Dispose()
+ $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 = @{
@@ -169,7 +126,7 @@ try {
} while ($pageInstallationCount -eq 100)
}
catch {
- Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App ID may be incorrect."
+ 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
}
$matchingInstallations = @($installations | Where-Object { $_.account.login -ieq $InstallationOwner })
diff --git a/eng/common/core-templates/job/helix-job-monitor.yml b/eng/common/core-templates/job/helix-job-monitor.yml
index 9182f076486..b1ad5d68a46 100644
--- a/eng/common/core-templates/job/helix-job-monitor.yml
+++ b/eng/common/core-templates/job/helix-job-monitor.yml
@@ -135,21 +135,6 @@ jobs:
displayName: Monitor Helix Jobs
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
continueOnError: ${{ parameters.continueOnError }}
- variables:
- HELIX_MONITOR_BASE_URI: ${{ parameters.helixBaseUri }}
- HELIX_MONITOR_USE_ENTRA_AUTHENTICATION: ${{ parameters.useEntraAuthentication }}
- HELIX_MONITOR_ENTRA_SCOPE: ${{ parameters.helixEntraScope }}
- HELIX_MONITOR_POLLING_INTERVAL_SECONDS: ${{ parameters.pollingIntervalSeconds }}
- HELIX_MONITOR_FAIL_ON_FAILED_TESTS: ${{ parameters.failWorkItemsWithFailedTests }}
- HELIX_MONITOR_ALLOW_NO_HELIX_JOBS: ${{ parameters.allowNoHelixJobs }}
- HELIX_MONITOR_USE_FULLY_QUALIFIED_TEST_NAME: ${{ parameters.useFullyQualifiedTestName }}
- HELIX_MONITOR_TIMEOUT_IN_MINUTES: ${{ parameters.timeoutInMinutes }}
- HELIX_MONITOR_TEST_RESULT_UPLOAD_PARALLELISM: ${{ parameters.testResultUploadParallelism }}
- HELIX_MONITOR_ORGANIZATION: ${{ parameters.organization }}
- HELIX_MONITOR_REPOSITORY: ${{ parameters.repository }}
- HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE: ${{ parameters.testResultAttachmentMode }}
- HELIX_MONITOR_TOOL_NUPKG_ARTIFACT_NAME: ${{ parameters.toolNupkgArtifactName }}
- HELIX_MONITOR_TOOL_COMMAND: ${{ parameters.toolCommand }}
${{ if ne(length(parameters.dependsOn), 0) }}:
dependsOn: ${{ parameters.dependsOn }}
${{ if ne(parameters.condition, '') }}:
@@ -171,6 +156,22 @@ jobs:
- pwsh: throw "azureSubscription must be set when useEntraAuthentication is true."
displayName: Validate Helix Entra authentication
+ - ${{ if eq(parameters.useEntraAuthentication, true) }}:
+ - task: AzureCLI@2
+ displayName: Initialize Helix Entra authentication
+ inputs:
+ azureSubscription: ${{ parameters.azureSubscription }}
+ addSpnToEnvironment: true
+ scriptType: pscore
+ scriptLocation: inlineScript
+ inlineScript: |
+ if ([string]::IsNullOrWhiteSpace($env:servicePrincipalId) -or [string]::IsNullOrWhiteSpace($env:tenantId)) {
+ throw "The Helix Azure service connection did not provide a service principal or tenant ID."
+ }
+
+ Write-Host "##vso[task.setvariable variable=HelixEntraClientId]$env:servicePrincipalId"
+ Write-Host "##vso[task.setvariable variable=HelixEntraTenantId]$env:tenantId"
+
- ${{ if ne(parameters.toolNupkgArtifactName, '') }}:
- task: DownloadPipelineArtifact@2
displayName: Download Helix Job Monitor artifact
@@ -308,3 +309,7 @@ jobs:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
${{ if eq(parameters.useEntraAuthentication, false) }}:
HELIX_ACCESSTOKEN: ${{ parameters.helixAccessToken }}
+ ${{ if eq(parameters.useEntraAuthentication, true) }}:
+ AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId)
+ AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId)
+ AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.azureSubscription }}
diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml
index c859dc2b071..25af2787900 100644
--- a/eng/common/core-templates/job/onelocbuild.yml
+++ b/eng/common/core-templates/job/onelocbuild.yml
@@ -10,9 +10,9 @@ parameters:
# GitHub App authentication for the OneLoc check-in PR.
GitHubAppServiceConnection: 'dnceng-oneloc-githubapp'
+ GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9'
GitHubAppKeyVaultName: 'EngKeyVault'
- GitHubAppIdSecretName: 'oneloc-localization-app-app-id'
- GitHubAppPrivateKeySecretName: 'oneloc-localization-app-app-private-key'
+ GitHubAppKeyName: 'oneloc-localization-app-key'
SourcesDirectory: $(System.DefaultWorkingDirectory)
CreatePr: true
@@ -102,8 +102,8 @@ jobs:
${{ else }}:
azureSubscription: ${{ parameters.GitHubAppServiceConnection }}
keyVaultName: ${{ parameters.GitHubAppKeyVaultName }}
- appIdSecretName: ${{ parameters.GitHubAppIdSecretName }}
- appPrivateKeySecretName: ${{ parameters.GitHubAppPrivateKeySecretName }}
+ keyName: ${{ parameters.GitHubAppKeyName }}
+ appClientId: ${{ parameters.GitHubAppClientId }}
installationOwner: ${{ parameters.GitHubOrg }}
outputVariableName: 'GitHubAppInstallationToken'
condition: ${{ parameters.condition }}
diff --git a/eng/common/core-templates/steps/get-github-app-token.yml b/eng/common/core-templates/steps/get-github-app-token.yml
index 3eeb5a4c1bc..6d42a48d3c3 100644
--- a/eng/common/core-templates/steps/get-github-app-token.yml
+++ b/eng/common/core-templates/steps/get-github-app-token.yml
@@ -1,11 +1,13 @@
# Mints a short-lived GitHub App installation access token by signing a JWT
-# with an RSA private key (RS256). The JWT is exchanged with the GitHub API
-# for a token scoped to a single installation.
+# with a private key stored in Azure Key Vault (RSA, RS256). The JWT is
+# exchanged with the GitHub API for a token scoped to a single installation.
#
# Requirements (per GitHub App you want to authenticate as):
-# - A GitHub App ID and PEM private key stored as Azure Key Vault secrets.
-# - The Azure service connection passed via `azureSubscription` must have
-# `Get` access to those two secrets.
+# - A GitHub App with its private key uploaded into Key Vault as an RSA key
+# (PEM converted to a key, NOT stored as a secret).
+# - The Azure service connection passed via `azureSubscription` must be
+# granted the `Key Vault Crypto User` role (or at minimum `Sign` action)
+# on that key.
# - The App must be installed on the target organization/account
# (`installationOwner`) with the permissions/repositories you need.
#
@@ -15,18 +17,23 @@
# enterprise classic-PAT lifetime policy.
parameters:
-# Azure DevOps service connection (federated) that can read the App credentials.
+# Azure DevOps service connection (federated) that can call
+# `az keyvault key sign` on the App's signing key.
- name: azureSubscription
type: string
-# Name of the Key Vault holding Secret Manager's github-app-secret projections.
+# Name of the Key Vault that holds the GitHub App's RSA signing key.
- name: keyVaultName
type: string
-- name: appIdSecretName
+# Name of the RSA key inside the Key Vault (the App's private key).
+- name: keyName
type: string
-- name: appPrivateKeySecretName
+# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
+# Prefer this over the numeric App ID; GitHub accepts either, but Client ID
+# is the documented form going forward.
+- name: appClientId
type: string
# Login of the organization or user account whose installation we should
@@ -66,7 +73,7 @@ steps:
inlineScript: |
& "$(System.DefaultWorkingDirectory)/eng/common/Get-GitHubAppToken.ps1" `
-KeyVaultName '${{ parameters.keyVaultName }}' `
- -AppIdSecretName '${{ parameters.appIdSecretName }}' `
- -AppPrivateKeySecretName '${{ parameters.appPrivateKeySecretName }}' `
+ -KeyName '${{ parameters.keyName }}' `
+ -AppClientId '${{ parameters.appClientId }}' `
-InstallationOwner '${{ parameters.installationOwner }}' `
-OutputVariableName '${{ parameters.outputVariableName }}'
diff --git a/eng/common/core-templates/steps/send-to-helix.yml b/eng/common/core-templates/steps/send-to-helix.yml
index 8c76d8c2abe..62cce4d4ea2 100644
--- a/eng/common/core-templates/steps/send-to-helix.yml
+++ b/eng/common/core-templates/steps/send-to-helix.yml
@@ -41,121 +41,40 @@ steps:
- ${{ if eq(parameters.HelixUseEntraAuthentication, true) }}:
- task: AzureCLI@2
- displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
+ displayName: Initialize Helix Entra authentication
inputs:
azureSubscription: ${{ parameters.HelixAzureSubscription }}
- keepAzSessionActive: true
+ addSpnToEnvironment: true
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
- # AzureCLI@2 replaces SYSTEM_ACCESSTOKEN with the service connection token.
- $env:SYSTEM_ACCESSTOKEN = $env:HELIX_AZDO_ACCESSTOKEN
+ if ([string]::IsNullOrWhiteSpace($env:servicePrincipalId) -or [string]::IsNullOrWhiteSpace($env:tenantId)) {
+ throw "The Helix Azure service connection did not provide a service principal or tenant ID."
+ }
- $(Build.SourcesDirectory)\eng\common\msbuild.ps1 `
- $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }} `
- /restore `
- /p:TreatWarningsAsErrors=false `
- /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }} `
- /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }} `
- ${{ parameters.HelixProjectArguments }} `
- /t:Test `
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
- env:
- BuildConfig: $(_BuildConfig)
- HelixSource: ${{ parameters.HelixSource }}
- HelixType: ${{ parameters.HelixType }}
- HelixBuild: ${{ parameters.HelixBuild }}
- HelixConfiguration: ${{ parameters.HelixConfiguration }}
- HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
- HelixEntraScope: ${{ parameters.HelixEntraScope }}
- HelixPreCommands: ${{ parameters.HelixPreCommands }}
- HelixPostCommands: ${{ parameters.HelixPostCommands }}
- WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
- WorkItemCommand: ${{ parameters.WorkItemCommand }}
- WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
- CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
- XUnitProjects: ${{ parameters.XUnitProjects }}
- XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
- XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
- XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
- XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
- IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
- DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
- DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
- WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
- HelixBaseUri: ${{ parameters.HelixBaseUri }}
- Creator: ${{ parameters.Creator }}
- HELIX_AZDO_ACCESSTOKEN: $(System.AccessToken)
- condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
- continueOnError: ${{ parameters.continueOnError }}
- - task: AzureCLI@2
- displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
- inputs:
- azureSubscription: ${{ parameters.HelixAzureSubscription }}
- keepAzSessionActive: true
- scriptType: bash
- scriptLocation: inlineScript
- inlineScript: |
- # AzureCLI@2 replaces SYSTEM_ACCESSTOKEN with the service connection token.
- export SYSTEM_ACCESSTOKEN="$HELIX_AZDO_ACCESSTOKEN"
-
- $(Build.SourcesDirectory)/eng/common/msbuild.sh \
- $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }} \
- /restore \
- /p:TreatWarningsAsErrors=false \
- /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }} \
- /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }} \
- ${{ parameters.HelixProjectArguments }} \
- /t:Test \
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
- env:
- BuildConfig: $(_BuildConfig)
- HelixSource: ${{ parameters.HelixSource }}
- HelixType: ${{ parameters.HelixType }}
- HelixBuild: ${{ parameters.HelixBuild }}
- HelixConfiguration: ${{ parameters.HelixConfiguration }}
- HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
- HelixEntraScope: ${{ parameters.HelixEntraScope }}
- HelixPreCommands: ${{ parameters.HelixPreCommands }}
- HelixPostCommands: ${{ parameters.HelixPostCommands }}
- WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
- WorkItemCommand: ${{ parameters.WorkItemCommand }}
- WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
- CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
- XUnitProjects: ${{ parameters.XUnitProjects }}
- XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
- XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
- XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
- XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
- IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
- DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
- DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
- WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
- HelixBaseUri: ${{ parameters.HelixBaseUri }}
- Creator: ${{ parameters.Creator }}
- HELIX_AZDO_ACCESSTOKEN: $(System.AccessToken)
- condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT'))
- continueOnError: ${{ parameters.continueOnError }}
+ Write-Host "##vso[task.setvariable variable=HelixEntraClientId]$env:servicePrincipalId"
+ Write-Host "##vso[task.setvariable variable=HelixEntraTenantId]$env:tenantId"
+ condition: ${{ parameters.condition }}
- - ${{ if eq(parameters.HelixUseEntraAuthentication, false) }}:
- - powershell: >
- $(Build.SourcesDirectory)\eng\common\msbuild.ps1
- $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
- /restore
- /p:TreatWarningsAsErrors=false
- /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
- /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
- ${{ parameters.HelixProjectArguments }}
- /t:Test
- /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
- displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
- env:
- BuildConfig: $(_BuildConfig)
- HelixSource: ${{ parameters.HelixSource }}
- HelixType: ${{ parameters.HelixType }}
- HelixBuild: ${{ parameters.HelixBuild }}
- HelixConfiguration: ${{ parameters.HelixConfiguration }}
- HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ - powershell: >
+ $(Build.SourcesDirectory)\eng\common\msbuild.ps1
+ $(Build.SourcesDirectory)/${{ parameters.HelixProjectPath }}
+ /restore
+ /p:TreatWarningsAsErrors=false
+ /p:EnableHelixJobMonitor=${{ parameters.UseHelixMonitor }}
+ /p:HelixUseEntraAuthentication=${{ parameters.HelixUseEntraAuthentication }}
+ ${{ parameters.HelixProjectArguments }}
+ /t:Test
+ /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog
+ displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ ${{ if eq(parameters.HelixUseEntraAuthentication, false) }}:
HelixAccessToken: ${{ parameters.HelixAccessToken }}
${{ if eq(parameters.HelixUseEntraAuthentication, true) }}:
AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId)
diff --git a/eng/common/run-helix-job-monitor.sh b/eng/common/run-helix-job-monitor.sh
deleted file mode 100644
index c79bd98e3f7..00000000000
--- a/eng/common/run-helix-job-monitor.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-scriptroot="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-. "$scriptroot/pipeline-logging-functions.sh"
-
-toolArgs=(
- --helix-base-uri "$HELIX_MONITOR_BASE_URI"
- --use-entra-authentication "$HELIX_MONITOR_USE_ENTRA_AUTHENTICATION"
- --helix-entra-scope "$HELIX_MONITOR_ENTRA_SCOPE"
- --polling-interval-seconds "$HELIX_MONITOR_POLLING_INTERVAL_SECONDS"
- --fail-on-failed-tests "$HELIX_MONITOR_FAIL_ON_FAILED_TESTS"
- --allow-no-helix-jobs "$HELIX_MONITOR_ALLOW_NO_HELIX_JOBS"
- --use-fully-qualified-test-name "$HELIX_MONITOR_USE_FULLY_QUALIFIED_TEST_NAME"
- --max-wait-minutes "$((HELIX_MONITOR_TIMEOUT_IN_MINUTES - 5))"
- --stage-name "$SYSTEM_STAGENAME"
- --stage-attempt "$SYSTEM_STAGEATTEMPT"
- --job-attempt "$SYSTEM_JOBATTEMPT"
- --test-result-upload-parallelism "$HELIX_MONITOR_TEST_RESULT_UPLOAD_PARALLELISM"
-)
-
-organization="${HELIX_MONITOR_ORGANIZATION:-}"
-repository="${HELIX_MONITOR_REPOSITORY:-}"
-
-# Fall back to Azure DevOps-provided environment variables when the caller did not
-# supply organization or repository explicitly.
-if [ -z "$organization" ] || [ -z "$repository" ]; then
- buildRepoName="${BUILD_REPOSITORY_NAME:-}"
- if [ -n "$buildRepoName" ] && [[ "$buildRepoName" == */* ]]; then
- repoOwner="${buildRepoName%%/*}"
- repoName="${buildRepoName#*/}"
- elif [ -n "$buildRepoName" ] && [[ "$buildRepoName" == *-* ]]; then
- repoOwner="${buildRepoName%%-*}"
- repoName="${buildRepoName#*-}"
- fi
-
- if [ -n "${repoOwner:-}" ] && [ -n "${repoName:-}" ]; then
- if [ -z "$organization" ]; then organization="$repoOwner"; fi
- if [ -z "$repository" ]; then repository="$repoName"; fi
- fi
-fi
-
-if [ -n "$organization" ]; then toolArgs+=( --organization "$organization" ); fi
-if [ -n "$repository" ]; then toolArgs+=( --repository "$repository" ); fi
-if [ -n "${HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE:-}" ]; then
- toolArgs+=( --test-result-attachment-mode "$HELIX_MONITOR_TEST_RESULT_ATTACHMENT_MODE" )
-fi
-
-# These values let the monitor derive the same Helix source filter as the SDK submitter.
-toolArgs+=( --build-reason "$BUILD_REASON" )
-toolArgs+=( --source-branch "$BUILD_SOURCEBRANCH" )
-
-cd "$BUILD_SOURCESDIRECTORY"
-exitCode=0
-if [ -n "${HELIX_MONITOR_TOOL_NUPKG_ARTIFACT_NAME:-}" ]; then
- export DOTNET_ROOT="$BUILD_SOURCESDIRECTORY/.dotnet"
- ./eng/common/dotnet.sh exec "$HELIXJOBMONITORDLL" "${toolArgs[@]}" || exitCode=$?
-else
- ./eng/common/dotnet.sh tool run "$HELIX_MONITOR_TOOL_COMMAND" -- "${toolArgs[@]}" || exitCode=$?
-fi
-
-if [ "$exitCode" -ne 0 ]; then
- Write-PipelineTelemetryError -force -category 'Helix' "Helix job monitor failed (exit code '$exitCode')."
- exit "$exitCode"
-fi
From 081dd52acd18d9ad991e6f2aafb2eaa761db471b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 13 Oct 2026 12:40:26 -0700
Subject: [PATCH 11/11] [release/11.0]: Bump
Microsoft.Testing.Extensions.CodeCoverage from 18.11.2 to 18.12.0 (#39213)
* [release/11.0]: Bump Microsoft.Testing.Extensions.CodeCoverage from 18.11.2 to 18.12.0
---
updated-dependencies:
- dependency-name: Microsoft.Testing.Extensions.CodeCoverage
dependency-version: 18.12.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Update MicrosoftTestingPlatformVersion to 2.5.0
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
---------
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com
---
eng/Versions.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/eng/Versions.props b/eng/Versions.props
index 105cc5c5b67..d70a459226d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -46,8 +46,8 @@
2.4.0
4.0.1
11.0.0-beta.26509.3
- 2.4.1
- 18.11.2
+ 2.5.1
+ 18.12.0