diff --git a/NuGet.config b/NuGet.config
index ecd238ee7aa0..0945b4f0620e 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -10,7 +10,7 @@
-
+
diff --git a/eng/Version.Details.props b/eng/Version.Details.props
index e72aada49d2b..48d8e2dfc7fe 100644
--- a/eng/Version.Details.props
+++ b/eng/Version.Details.props
@@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props
- 10.0.0-beta.26473.106
- 10.0.0-beta.26473.106
+ 10.0.0-beta.26507.119
+ 10.0.0-beta.26507.119
0.11.5-alpha.26070.104
- 10.0.0-beta.26473.106
+ 10.0.0-beta.26507.119
10.0.3-servicing.26070.104
10.0.3
10.0.3
- 10.0.402-servicing.26471.104
+ 10.0.403-servicing.26507.119
10.0.3
- 10.0.402
+ 10.0.403
26.0.11017
18.5.9227
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 76ce5ad7d106..a695503b3ea6 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,8 +1,8 @@
-
+
https://github.com/dotnet/dotnet
- 82c20eb47c0af1a101d821399e1a600a58a9d899
+ af9776efa74ccd9f306ca541913b0ae328e42472
https://github.com/dotnet/dotnet
@@ -95,25 +95,25 @@
-
+
https://github.com/dotnet/dotnet
- e26618ae227ceb29376490c71302a875161acad6
+ af9776efa74ccd9f306ca541913b0ae328e42472
-
+
https://github.com/dotnet/dotnet
- e26618ae227ceb29376490c71302a875161acad6
+ af9776efa74ccd9f306ca541913b0ae328e42472
-
+
https://github.com/dotnet/dotnet
- 82c20eb47c0af1a101d821399e1a600a58a9d899
+ af9776efa74ccd9f306ca541913b0ae328e42472
https://github.com/dotnet/xharness
f40ec1c86c47f721ced71581e8228b55c20b3eba
-
+
https://github.com/dotnet/dotnet
- e26618ae227ceb29376490c71302a875161acad6
+ af9776efa74ccd9f306ca541913b0ae328e42472
diff --git a/eng/common/Get-GitHubAppToken.ps1 b/eng/common/Get-GitHubAppToken.ps1
index ea776bd6bc28..ec005e487c38 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 1e685f593314..a3dd1ab5341c 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
@@ -100,13 +100,12 @@ parameters:
type: boolean
default: false
-# When true, test results are reported to Azure DevOps using the fully qualified test name
-# (Namespace.Type.Method) as the stable automatedTestName and the visible title is qualified as
-# well (--use-fully-qualified-test-name). Opt-in because it changes AzDO test identity and display;
-# primarily useful for frameworks like MSTest whose display name is only the method name.
+# When true (the default), test results are reported to Azure DevOps using the fully qualified test
+# name (Namespace.Type.Method) as the stable automatedTestName and the visible title is qualified as
+# well (--use-fully-qualified-test-name). Set to false to preserve framework-provided display names.
- name: useFullyQualifiedTestName
type: boolean
- default: false
+ default: true
# Controls per-test output attachments. Defaults to Failed.
- name: testResultAttachmentMode
@@ -142,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, '') }}:
@@ -163,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
@@ -247,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 d7a69f1c76e9..adcdfa175369 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 6d42a48d3c36..3eeb5a4c1bc9 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 3951e47104a8..41a94526ea09 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 000000000000..c79bd98e3f77
--- /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 4e9ffd98e649..f0e9bd7a32b0 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "10.0.402-servicing.26471.104",
+ "version": "10.0.403-servicing.26507.119",
"paths": [
"builds/downloads/dotnet",
"$host$"
@@ -8,9 +8,9 @@
"errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'."
},
"tools": {
- "dotnet": "10.0.402-servicing.26471.104"
+ "dotnet": "10.0.403-servicing.26507.119"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26473.106"
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26507.119"
}
}