diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index d01ed609cb..4d2b8957b4 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -7,34 +7,34 @@
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
-
+
https://github.com/dotnet/arcade
- 66b75e61d883abd9e3af86a811c3809a8d9142ca
+ 1574a0ce35761b7ce5e783074cc2f9567d278396
diff --git a/eng/Versions.props b/eng/Versions.props
index 067b1503dc..4db12c5d5f 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -98,7 +98,7 @@
9.0.0-beta.24212.4
5.0.0-preview.5.20278.1
8.0.0-beta.24525.2
- 11.0.0-beta.26456.1
+ 11.0.0-beta.26461.5
9.0.4
0.0.6-test
0.0.13-test
diff --git a/eng/common/Get-GitHubAppToken.ps1 b/eng/common/Get-GitHubAppToken.ps1
index ea776bd6bc..ec005e487c 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/build.sh b/eng/common/build.sh
index 109d83ff73..f65b048aa8 100755
--- a/eng/common/build.sh
+++ b/eng/common/build.sh
@@ -254,7 +254,7 @@ function Build {
properties+=("/p:Projects=$projects")
fi
- local bl=""
+ local bl=()
if [[ "$binary_log" == true ]]; then
local binary_log_path=""
if [[ -z "$binary_log_name" ]]; then
@@ -266,7 +266,7 @@ function Build {
fi
mkdir -p "$(dirname "$binary_log_path")"
- bl="/bl:\"$binary_log_path\""
+ bl=("/bl:$binary_log_path")
fi
local check=""
@@ -274,8 +274,8 @@ function Build {
check="/check"
fi
- MSBuild $_InitializeToolset \
- $bl \
+ MSBuild "$_InitializeToolset" \
+ ${bl[@]+"${bl[@]}"} \
$check \
/p:Configuration=$configuration \
/p:RepoRoot="$repo_root" \
@@ -299,7 +299,7 @@ function Build {
if [[ "$clean" == true ]]; then
if [ -d "$artifacts_dir" ]; then
- rm -rf $artifacts_dir
+ rm -rf "$artifacts_dir"
echo "Artifacts directory deleted."
fi
exit 0
diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml
index cb60f52978..2716ecd18f 100644
--- a/eng/common/core-templates/job/job.yml
+++ b/eng/common/core-templates/job/job.yml
@@ -28,6 +28,7 @@ parameters:
enablePublishTestResults: false
enablePublishing: false
enableBuildRetry: false
+ enableAstred: false
mergeTestResults: false
testRunTitle: ''
testResultsFormat: ''
@@ -119,6 +120,12 @@ jobs:
- name: ${{ pair.key }}
value: ${{ pair.value }}
+ - ${{ if and(eq(parameters.enableAstred, true), eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: MSBUILDDEBUGENGINE
+ value: 1
+ - name: MSBUILDDEBUGPATH
+ value: $(Build.ArtifactStagingDirectory)/AstredCapture/binlogs
+
# DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds
- ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- group: DotNet-HelixApi-Access
@@ -236,3 +243,8 @@ jobs:
condition: always()
- ${{ each step in parameters.artifactPublishSteps }}:
- ${{ step }}
+
+ - ${{ if and(eq(parameters.enableAstred, true), eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - template: /eng/common/core-templates/steps/astred-artifacts.yml
+ parameters:
+ binlogDir: $(MSBUILDDEBUGPATH)
diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml
index e4e6b77fc3..b772dc5788 100644
--- a/eng/common/core-templates/job/onelocbuild.yml
+++ b/eng/common/core-templates/job/onelocbuild.yml
@@ -5,23 +5,14 @@ parameters:
# Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool
pool: ''
- CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex
- GithubPat: $(BotAccount-dotnet-bot-repo-PAT)
-
- # Service connection for WIF-based Entra authentication to ceapex feeds (replaces CeapexPat).
- # dnceng/internal and DevDiv/DevDiv have same-named, project-scoped connections. Other projects,
- # and any pipeline that sets this to '', fall back to PAT-based auth via the CeapexPat parameter.
+ # Project-scoped WIF service connection for Ceapex feed authentication.
CeapexServiceConnection: 'dnceng-onelocbuild-ceapex'
# GitHub App authentication for the OneLoc check-in PR.
- # dnceng/internal and DevDiv/DevDiv are enabled by default with their project-scoped service
- # connections. Other projects must explicitly opt in after provisioning equivalent infrastructure.
- UseGitHubAppAuthentication: true
- UseGitHubAppAuthenticationInOtherProjects: false
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
@@ -49,7 +40,6 @@ jobs:
displayName: OneLocBuild${{ parameters.JobNameSuffix }}
variables:
- - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat
- name: _GenerateLocProjectArguments
value: -SourcesDirectory ${{ parameters.SourcesDirectory }}
-LanguageSet "${{ parameters.LanguageSet }}"
@@ -80,6 +70,10 @@ jobs:
steps:
- ${{ if eq(parameters.is1ESPipeline, '') }}:
- 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error
+ - ${{ if notIn(variables['System.TeamProject'], 'internal', 'DevDiv') }}:
+ - 'OneLocBuild is supported only in dnceng/internal and DevDiv/DevDiv.': error
+ - ${{ if eq(parameters.CeapexServiceConnection, '') }}:
+ - 'CeapexServiceConnection must identify a WIF service connection.': error
- ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}:
- task: Powershell@2
@@ -89,17 +83,15 @@ jobs:
displayName: Generate LocProject.json
condition: ${{ parameters.condition }}
- # Acquire an Entra token for ceapex feed access in the supported internal and DevDiv projects.
- - ${{ if and(ne(parameters.CeapexServiceConnection, ''), or(eq(variables['System.TeamProject'], 'internal'), eq(variables['System.TeamProject'], 'DevDiv'))) }}:
- - template: /eng/common/templates/steps/get-federated-access-token.yml
- parameters:
- federatedServiceConnection: ${{ parameters.CeapexServiceConnection }}
- outputVariableName: 'CeapexEntraToken'
- condition: ${{ parameters.condition }}
+ # Acquire a short-lived Entra token for Ceapex feed access.
+ - template: /eng/common/templates/steps/get-federated-access-token.yml
+ parameters:
+ federatedServiceConnection: ${{ parameters.CeapexServiceConnection }}
+ outputVariableName: 'CeapexEntraToken'
+ condition: ${{ parameters.condition }}
- # Mint a short-lived GitHub App installation token for the loc check-in PR. Use the connection
- # provisioned in each supported project; other projects must explicitly opt in and override it.
- - ${{ if and(eq(parameters.RepoType, 'gitHub'), eq(parameters.UseGitHubAppAuthentication, true), or(eq(variables['System.TeamProject'], 'internal'), eq(variables['System.TeamProject'], 'DevDiv'), eq(parameters.UseGitHubAppAuthenticationInOtherProjects, true))) }}:
+ # Mint a short-lived GitHub App installation token for the loc check-in PR.
+ - ${{ if eq(parameters.RepoType, 'gitHub') }}:
- template: /eng/common/core-templates/steps/get-github-app-token.yml
parameters:
is1ESPipeline: ${{ parameters.is1ESPipeline }}
@@ -108,8 +100,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 }}
@@ -129,16 +121,10 @@ jobs:
isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }}
isShouldReusePrSelected: ${{ parameters.ReusePr }}
packageSourceAuth: patAuth
- ${{ if and(ne(parameters.CeapexServiceConnection, ''), or(eq(variables['System.TeamProject'], 'internal'), eq(variables['System.TeamProject'], 'DevDiv'))) }}:
- patVariable: $(CeapexEntraToken)
- ${{ if or(eq(parameters.CeapexServiceConnection, ''), and(ne(variables['System.TeamProject'], 'internal'), ne(variables['System.TeamProject'], 'DevDiv'))) }}:
- patVariable: ${{ parameters.CeapexPat }}
+ patVariable: $(CeapexEntraToken)
${{ if eq(parameters.RepoType, 'gitHub') }}:
repoType: ${{ parameters.RepoType }}
- ${{ if and(eq(parameters.UseGitHubAppAuthentication, true), or(eq(variables['System.TeamProject'], 'internal'), eq(variables['System.TeamProject'], 'DevDiv'), eq(parameters.UseGitHubAppAuthenticationInOtherProjects, true))) }}:
- gitHubPatVariable: "$(GitHubAppInstallationToken)"
- ${{ else }}:
- gitHubPatVariable: "${{ parameters.GithubPat }}"
+ gitHubPatVariable: "$(GitHubAppInstallationToken)"
${{ if ne(parameters.MirrorRepo, '') }}:
isMirrorRepoSelected: true
gitHubOrganization: ${{ parameters.GitHubOrg }}
diff --git a/eng/common/core-templates/steps/astred-artifacts.yml b/eng/common/core-templates/steps/astred-artifacts.yml
new file mode 100644
index 0000000000..b914082f58
--- /dev/null
+++ b/eng/common/core-templates/steps/astred-artifacts.yml
@@ -0,0 +1,103 @@
+# Astred footer for producing and uploading a portable digest.
+# The calling job must configure its header before any build steps run:
+# MSBUILDDEBUGENGINE=1
+# MSBUILDDEBUGPATH=
+parameters:
+- name: sourcesPath
+ type: string
+ default: $(Build.SourcesDirectory)
+- name: binlogDir
+ type: string
+ default: $(Build.ArtifactStagingDirectory)/AstredCapture/binlogs
+- name: capturePath
+ type: string
+ default: $(Build.ArtifactStagingDirectory)/AstredCapture
+
+steps:
+- task: AstredInstaller@0
+ displayName: Install Astred CLI
+ inputs:
+ Version: '2.14.1'
+ FeedUrl: 'https://pkgs.dev.azure.com/dnceng/_packaging/dotnet-internal-FoSSE/nuget/v3/index.json'
+
+- pwsh: |
+ $ErrorActionPreference = 'Continue'
+ $apjOut = Join-Path $env:ASTRED_CAPTURE_PATH 'apj'
+ New-Item -ItemType Directory -Force -Path $apjOut | Out-Null
+
+ $binlogs = Get-ChildItem -Path $env:ASTRED_BINLOG_DIR -Recurse -Force -Filter *.binlog `
+ -ErrorAction SilentlyContinue
+ if (-not $binlogs) {
+ Write-Host "##vso[task.logissue type=warning]No binlogs found in $env:ASTRED_BINLOG_DIR. Check the calling job's Astred header configuration for MSBUILDDEBUGENGINE / MSBuildDebugEngine and MSBUILDDEBUGPATH."
+ exit 0
+ }
+
+ $project = Join-Path $apjOut '.astred.project.json'
+ $binlogPaths = @($binlogs.FullName)
+ Write-Host "astproj: processing $($binlogPaths.Count) binlog(s)"
+ astred astproj -nofolders @binlogPaths "-o:$project"
+ if ($LASTEXITCODE -ne 0) {
+ Write-Host "##vso[task.logissue type=warning]astproj failed (exit $LASTEXITCODE)"
+ }
+ displayName: Generate Astred Project Files
+ workingDirectory: ${{ parameters.sourcesPath }}
+ env:
+ ASTRED_BINLOG_DIR: ${{ parameters.binlogDir }}
+ ASTRED_CAPTURE_PATH: ${{ parameters.capturePath }}
+ condition: succeededOrFailed()
+ continueOnError: true
+
+- pwsh: |
+ $ErrorActionPreference = 'Continue'
+ $apjDir = Join-Path $env:ASTRED_CAPTURE_PATH 'apj'
+ $uploadRoot = Join-Path $env:ASTRED_CAPTURE_PATH 'upload'
+ $project = Join-Path $apjDir '.astred.project.json'
+
+ if (-not (Test-Path $project)) {
+ Write-Host "##vso[task.logissue type=warning]No Astred project file was produced."
+ exit 0
+ }
+
+ $digest = Join-Path $apjDir '.astred.digest.zip'
+ Remove-Item $digest -ErrorAction SilentlyContinue
+ astred "-repo:$env:ASTRED_SOURCES_PATH" "-project:$project" -digest
+ if ($LASTEXITCODE -eq 0 -and (Test-Path $digest)) {
+ $commitTimeText = & git -C $env:ASTRED_SOURCES_PATH show -s --format=%cI $env:BUILD_SOURCEVERSION
+ if ($LASTEXITCODE -ne 0) {
+ throw "Could not read the commit timestamp for $env:BUILD_SOURCEVERSION."
+ }
+
+ $commitTime = [DateTimeOffset]::Parse(
+ $commitTimeText.Trim(),
+ [Globalization.CultureInfo]::InvariantCulture)
+ $eventFolder = '{0}_{1}' -f `
+ $commitTime.UtcDateTime.ToString('yyyy-MM-ddTHH-mm-ssZ'), `
+ $env:BUILD_SOURCEVERSION
+ $targetDir = Join-Path (Join-Path $uploadRoot 'AST') $eventFolder
+ $target = Join-Path $targetDir '.astred.digest.zip'
+ New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
+ Move-Item $digest $target -Force
+ Write-Host "Prepared Astred digest: $target"
+ Write-Host "##vso[task.setvariable variable=ASTRED_DIGEST_READY]true"
+ }
+ elseif ($LASTEXITCODE -ne 0) {
+ Write-Host "##vso[task.logissue type=warning]Digest generation failed for $project (exit $LASTEXITCODE)"
+ Remove-Item $digest -ErrorAction SilentlyContinue
+ }
+ else {
+ Write-Host "##vso[task.logissue type=warning]Digest not produced for $project"
+ }
+ displayName: Package Portable Astred Digests
+ env:
+ ASTRED_SOURCES_PATH: ${{ parameters.sourcesPath }}
+ ASTRED_CAPTURE_PATH: ${{ parameters.capturePath }}
+ condition: succeededOrFailed()
+ continueOnError: true
+
+- task: UploadAstred@0
+ displayName: Upload Digest to Astred
+ condition: and(succeededOrFailed(), eq(variables['ASTRED_DIGEST_READY'], 'true'))
+ inputs:
+ SourcePath: ${{ parameters.capturePath }}/upload
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
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 6d42a48d3c..3eeb5a4c1b 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/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh
index f58abbd2d1..3fea306bc2 100755
--- a/eng/common/cross/build-rootfs.sh
+++ b/eng/common/cross/build-rootfs.sh
@@ -532,27 +532,33 @@ ensureDownloadTool()
}
if [[ "$__CodeName" == "alpine" ]]; then
- __ApkToolsVersion=2.12.11
+ __ApkToolsVersion=2.14.4-r1
__ApkToolsDir="$(mktemp -d)"
__ApkKeysDir="$(mktemp -d)"
arch="$(uname -m)"
__AlpineRepo="${__AlpineRepoOverride:-https://dl-cdn.alpinelinux.org/alpine}"
ensureDownloadTool
+ __ApkToolsPackage="$__ApkToolsDir/apk-tools-static.apk"
+ __ApkToolsUrl="$__AlpineRepo/v3.20/main/$arch/apk-tools-static-$__ApkToolsVersion.apk"
if [[ "$__hasWget" == 1 ]]; then
- wget -P "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static"
+ wget -O "$__ApkToolsPackage" "$__ApkToolsUrl"
else
- curl -SLO --create-dirs --output-dir "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static"
+ curl -fSL -o "$__ApkToolsPackage" "$__ApkToolsUrl"
fi
+
if [[ "$arch" == "x86_64" ]]; then
- __ApkToolsSHA512SUM="53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33"
+ __ApkToolsSHA512SUM="b1b3cc382aa0ec26a2c24b742701a1f9885d0678365f9aea15d3d005926b06ecc802659cec8a7deba2717af99c19c708a17c23e1f0f07742268ee5be5400eb9e"
elif [[ "$arch" == "aarch64" ]]; then
- __ApkToolsSHA512SUM="9e2b37ecb2b56c05dad23d379be84fd494c14bd730b620d0d576bda760588e1f2f59a7fcb2f2080577e0085f23a0ca8eadd993b4e61c2ab29549fdb71969afd0"
+ __ApkToolsSHA512SUM="61f9a636c5ac4e96e7a3f69fd65e60fc57b3ec8b23619c4df86f59b89e71d1309b3e406388945bdf0dd9168dac22df376943a70ff3efa179e5687e586f825fb0"
else
- echo "WARNING: add missing hash for your host architecture. To find the value, use: 'find /tmp -name apk.static -exec sha512sum {} \;'"
+ >&2 echo "ERROR: Unsupported apk-tools-static host architecture '$arch'."
+ exit 1
fi
- echo "$__ApkToolsSHA512SUM $__ApkToolsDir/apk.static" | sha512sum -c
+ echo "$__ApkToolsSHA512SUM $__ApkToolsPackage" | sha512sum -c
+ tar -xzf "$__ApkToolsPackage" -C "$__ApkToolsDir" --strip-components=1 sbin/apk.static
+ rm "$__ApkToolsPackage"
chmod +x "$__ApkToolsDir/apk.static"
if [[ "$__AlpineVersion" == "edge" ]]; then
diff --git a/global.json b/global.json
index ab5a15ec09..8760179131 100644
--- a/global.json
+++ b/global.json
@@ -13,8 +13,8 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26456.1",
- "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26456.1",
+ "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26461.5",
+ "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26461.5",
"Microsoft.Build.NoTargets": "3.7.0",
"Microsoft.Build.Traversal": "3.2.0"
}