Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
<add key="dotnet11" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet11/nuget/v3/index.json" />
<add key="dotnet12" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet12/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
Expand Down
10 changes: 5 additions & 5 deletions eng/Version.Details.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ This file should be imported by eng/Versions.props
<Project>
<PropertyGroup>
<!-- dotnet-dotnet dependencies -->
<MicrosoftDotNetArcadeSdkPackageVersion>11.0.0-beta.26411.119</MicrosoftDotNetArcadeSdkPackageVersion>
<SystemCommandLinePackageVersion>3.0.0-rc.1.26411.119</SystemCommandLinePackageVersion>
<SystemIOHashingPackageVersion>11.0.0-rc.1.26411.119</SystemIOHashingPackageVersion>
<SystemSecurityCryptographyXmlPackageVersion>11.0.0-rc.1.26411.119</SystemSecurityCryptographyXmlPackageVersion>
<SystemTextJsonPackageVersion>11.0.0-rc.1.26411.119</SystemTextJsonPackageVersion>
<MicrosoftDotNetArcadeSdkPackageVersion>12.0.0-beta.26528.120</MicrosoftDotNetArcadeSdkPackageVersion>
<SystemCommandLinePackageVersion>4.0.0-alpha.1.26528.120</SystemCommandLinePackageVersion>
<SystemIOHashingPackageVersion>12.0.0-alpha.1.26528.120</SystemIOHashingPackageVersion>
<SystemSecurityCryptographyXmlPackageVersion>12.0.0-alpha.1.26528.120</SystemSecurityCryptographyXmlPackageVersion>
<SystemTextJsonPackageVersion>12.0.0-alpha.1.26528.120</SystemTextJsonPackageVersion>
</PropertyGroup>
<!--Property group for alternate package version names-->
<PropertyGroup>
Expand Down
22 changes: 11 additions & 11 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<Source Uri="https://github.com/dotnet/dotnet" Mapping="sourcelink" Sha="7cdb217445905f3342bbb0266a4497b9a014389a" BarId="326717" />
<Source Uri="https://github.com/dotnet/dotnet" Mapping="sourcelink" Sha="bc397b207bfe2f5dd6d6643e46003e77cd32465c" BarId="338848" />
<ProductDependencies>
<Dependency Name="System.CommandLine" Version="3.0.0-rc.1.26411.119">
<Dependency Name="System.CommandLine" Version="4.0.0-alpha.1.26528.120">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>7cdb217445905f3342bbb0266a4497b9a014389a</Sha>
<Sha>bc397b207bfe2f5dd6d6643e46003e77cd32465c</Sha>
</Dependency>
<Dependency Name="System.IO.Hashing" Version="11.0.0-rc.1.26411.119">
<Dependency Name="System.IO.Hashing" Version="12.0.0-alpha.1.26528.120">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>7cdb217445905f3342bbb0266a4497b9a014389a</Sha>
<Sha>bc397b207bfe2f5dd6d6643e46003e77cd32465c</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Xml" Version="11.0.0-rc.1.26411.119">
<Dependency Name="System.Security.Cryptography.Xml" Version="12.0.0-alpha.1.26528.120">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>7cdb217445905f3342bbb0266a4497b9a014389a</Sha>
<Sha>bc397b207bfe2f5dd6d6643e46003e77cd32465c</Sha>
</Dependency>
<Dependency Name="System.Text.Json" Version="11.0.0-rc.1.26411.119">
<Dependency Name="System.Text.Json" Version="12.0.0-alpha.1.26528.120">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>7cdb217445905f3342bbb0266a4497b9a014389a</Sha>
<Sha>bc397b207bfe2f5dd6d6643e46003e77cd32465c</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="11.0.0-beta.26411.119">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="12.0.0-beta.26528.120">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>7cdb217445905f3342bbb0266a4497b9a014389a</Sha>
<Sha>bc397b207bfe2f5dd6d6643e46003e77cd32465c</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
4 changes: 2 additions & 2 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<Import Project="Version.Details.props" />

<PropertyGroup>
<VersionPrefix>11.0.100</VersionPrefix>
<PreReleaseVersionLabel>rc</PreReleaseVersionLabel>
<VersionPrefix>12.0.100</VersionPrefix>
<PreReleaseVersionLabel>alpha</PreReleaseVersionLabel>
<PreReleaseVersionIteration>1</PreReleaseVersionIteration>
<!-- Allowed values: '', 'prerelease', 'release'. Set to 'release' when stabilizing. -->
<DotNetFinalVersionKind></DotNetFinalVersionKind>
Expand Down
148 changes: 101 additions & 47 deletions eng/common/Get-GitHubAppToken.ps1
Original file line number Diff line number Diff line change
@@ -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.
#
Expand All @@ -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`).
Expand All @@ -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]@{
Expand All @@ -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
$rsa.Dispose()
}
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 = @{
Expand All @@ -110,27 +153,38 @@ $headers = @{

Write-Host "Looking up installation for '$InstallationOwner'..."
try {
$installations = @()
$installations = [System.Collections.Generic.List[object]]::new()
$page = 1
do {
$pageInstallations = @(Invoke-RestMethod `
$pageResponse = Invoke-RestMethod `
-Uri "https://api.github.com/app/installations?per_page=100&page=$page" `
-Headers $headers `
-Method Get)
$installations += $pageInstallations
-Method Get
$pageInstallationCount = 0
foreach ($installation in $pageResponse) {
$installations.Add($installation)
$pageInstallationCount++
}
$page++
} while ($pageInstallations.Count -eq 100)
} 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
}
$installation = $installations | Where-Object { $_.account.login -ieq $InstallationOwner } | Select-Object -First 1
if (-not $installation) {
$matchingInstallations = @($installations | Where-Object { $_.account.login -ieq $InstallationOwner })
if ($matchingInstallations.Count -eq 0) {
$found = ($installations | ForEach-Object { $_.account.login }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "No installation found for '$InstallationOwner'. App is installed on: $found"
exit 1
}
if ($matchingInstallations.Count -ne 1) {
$matchingIds = ($matchingInstallations | ForEach-Object { $_.id }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "Found multiple installations for '$InstallationOwner': $matchingIds"
exit 1
}
$installation = $matchingInstallations[0]
Write-Host "Using installation $($installation.id) for '$($installation.account.login)'."

try {
$tokenResponse = Invoke-RestMethod `
Expand Down
2 changes: 1 addition & 1 deletion eng/common/SetupNugetSources.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ if ($disabledSources -ne $null) {
Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node"
EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds -Credential $feedCredential
}
$dotnetVersions = @('5','6','7','8','9','10')
$dotnetVersions = @('5','6','7','8','9','10','11')

foreach ($dotnetVersion in $dotnetVersions) {
$feedPrefix = "dotnet" + $dotnetVersion;
Expand Down
2 changes: 1 addition & 1 deletion eng/common/SetupNugetSources.sh
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ if [ "$?" == "0" ]; then
EnableMaestroInternalPackageSources
fi

DotNetVersions=('5' '6' '7' '8' '9' '10')
DotNetVersions=('5' '6' '7' '8' '9' '10' '11')

for DotNetVersion in ${DotNetVersions[@]} ; do
FeedPrefix="dotnet${DotNetVersion}";
Expand Down
2 changes: 1 addition & 1 deletion eng/common/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Param(
[bool] $warnAsError = $true,
[string] $warnNotAsError = '',
[bool] $nodeReuse = $true,
[bool][Alias('mt')]$msbuildMultiThreaded = $false,
[bool][Alias('mt')]$msbuildMultiThreaded = $true,
[switch] $buildCheck = $false,
[switch][Alias('r')]$restore,
[switch] $deployDeps,
Expand Down
10 changes: 5 additions & 5 deletions eng/common/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -266,16 +266,16 @@ function Build {
fi

mkdir -p "$(dirname "$binary_log_path")"
bl="/bl:\"$binary_log_path\""
bl=("/bl:$binary_log_path")
fi

local check=""
if [[ "$build_check" == true ]]; then
check="/check"
fi

MSBuild $_InitializeToolset \
$bl \
MSBuild "$_InitializeToolset" \
${bl[@]+"${bl[@]}"} \
$check \
/p:Configuration=$configuration \
/p:RepoRoot="$repo_root" \
Expand All @@ -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
Expand Down
Loading