Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
]
},
"microsoft.dotnet.helix.jobmonitor": {
"version": "11.0.0-beta.26381.1",
"version": "11.0.0-beta.26404.101",
"commands": [
"dotnet-helix-job-monitor"
]
Expand Down
161 changes: 79 additions & 82 deletions eng/Version.Details.props

Large diffs are not rendered by default.

314 changes: 157 additions & 157 deletions eng/Version.Details.xml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
<MicrosoftBuildVersion>17.11.48</MicrosoftBuildVersion>
<MicrosoftBuildTasksCoreVersion>17.11.48</MicrosoftBuildTasksCoreVersion>
<MicrosoftBuildFrameworkVersion>17.11.48</MicrosoftBuildFrameworkVersion>
<MicrosoftBuildFrameworkHotReloadVersion>18.7.1</MicrosoftBuildFrameworkHotReloadVersion>
<MicrosoftBuildUtilitiesCoreVersion>17.11.48</MicrosoftBuildUtilitiesCoreVersion>
<DotnetSosVersion>7.0.412701</DotnetSosVersion>
<DotnetSosTargetFrameworkVersion>6.0</DotnetSosTargetFrameworkVersion>
Expand Down
139 changes: 139 additions & 0 deletions eng/common/Get-GitHubAppToken.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# 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.
#
# 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.
# - The App must be installed on the target organization/account
# (`InstallationOwner`) with the permissions/repositories it needs.
#
# Installation tokens (ghs_*) are exempt from the enterprise classic-PAT
# lifetime policy, which is why this replaces the long-lived PAT.

[CmdletBinding()]
param(
# Name of the Key Vault that holds the GitHub App's RSA signing key.
[Parameter(Mandatory = $true)]
[string] $KeyVaultName,

# Name of the RSA key inside the Key Vault (the App's private key).
[Parameter(Mandatory = $true)]
[string] $KeyName,

# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
[Parameter(Mandatory = $true)]
[string] $AppClientId,

# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
[Parameter(Mandatory = $true)]
[string] $InstallationOwner,

# Optional Azure DevOps pipeline variable name to set with the installation
# token (marked as a secret). When not specified, the token is written to
# stdout instead.
[Parameter(Mandatory = $false)]
[string] $OutputVariableName
)

$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true

. $PSScriptRoot\pipeline-logging-functions.ps1

function ConvertTo-Base64Url([byte[]] $bytes) {
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}

# Build JWT header and payload. Use [ordered] hashtables so JSON
# serialization is deterministic.
$jwtHeader = [ordered]@{
alg = 'RS256'
typ = 'JWT'
}
$now = [System.DateTimeOffset]::UtcNow
$jwtPayload = [ordered]@{
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
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"

# 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 key '$KeyName' in vault '$KeyVaultName'..."
try {
$signResponseJson = az keyvault key sign `
--vault-name $KeyVaultName `
--name $KeyName `
--algorithm RS256 `
--digest $digestBase64
}
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."
exit 1
}
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($signResponseJson)) {
Write-PipelineTelemetryError -Category 'Build' -Message "'az keyvault key sign' exited with code $LASTEXITCODE 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
}
$signResponse = $signResponseJson | ConvertFrom-Json
if ([string]::IsNullOrEmpty($signResponse.signature)) {
Write-PipelineTelemetryError -Category 'Build' -Message "Key Vault returned an empty signature for key '$KeyName' in vault '$KeyVaultName'."
exit 1
}
$signatureUrl = $signResponse.signature.TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$signingInput.$signatureUrl"

$headers = @{
Authorization = "Bearer $jwt"
'X-GitHub-Api-Version' = '2022-11-28'
Accept = 'application/vnd.github+json'
'User-Agent' = 'dotnet-arcade-onelocbuild'
}

Write-Host "Looking up installation for '$InstallationOwner'..."
try {
$installations = Invoke-RestMethod -Uri 'https://api.github.com/app/installations' -Headers $headers -Method Get
}
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."
exit 1
}
$installation = $installations | Where-Object { $_.account.login -eq $InstallationOwner } | Select-Object -First 1
if (-not $installation) {
$found = ($installations | ForEach-Object { $_.account.login }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "No installation found for '$InstallationOwner'. App is installed on: $found"
exit 1
}

try {
$tokenResponse = Invoke-RestMethod `
-Uri "https://api.github.com/app/installations/$($installation.id)/access_tokens" `
-Headers $headers `
-Method Post `
-ContentType 'application/json'
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to mint an installation access token for '$InstallationOwner' (installation $($installation.id)): $_"
exit 1
}

Write-Host "Got installation token for '$InstallationOwner' (expires $($tokenResponse.expires_at))."
if ($OutputVariableName) {
Write-Host "Setting pipeline variable '$OutputVariableName'."
Write-Host "##vso[task.setvariable variable=$OutputVariableName;issecret=true]$($tokenResponse.token)"
}
else {
Write-Host $tokenResponse.token -ForegroundColor Green
}
27 changes: 26 additions & 1 deletion eng/common/core-templates/job/onelocbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ parameters:
# exist, and any pipeline that sets this to '' fall back to PAT-based auth via the CeapexPat parameter.
CeapexServiceConnection: 'dnceng-onelocbuild-ceapex'

# GitHub App authentication for the OneLoc check-in PR (dnceng/internal only).
# The infrastructure identifiers are centralized here so consumers only need to opt in.
# DevDiv requires its own project-scoped service connection before this path can be enabled there.
UseGitHubAppAuthentication: false
GitHubAppServiceConnection: 'dnceng-oneloc-githubapp'
GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9'
GitHubAppKeyVaultName: 'EngKeyVault'
GitHubAppKeyName: 'oneloc-localization-app-key'

SourcesDirectory: $(System.DefaultWorkingDirectory)
CreatePr: true
AutoCompletePr: false
Expand Down Expand Up @@ -89,6 +98,19 @@ jobs:
outputVariableName: 'CeapexEntraToken'
condition: ${{ parameters.condition }}

# Mint a short-lived GitHub App installation token for the loc check-in PR (dnceng/internal only).
# All other projects fall back to PAT-based auth, since the app service connection is scoped to dnceng/internal.
- ${{ if and(eq(parameters.RepoType, 'gitHub'), eq(parameters.UseGitHubAppAuthentication, true), eq(variables['System.TeamProject'], 'internal')) }}:
- template: /eng/common/templates/steps/get-github-app-token.yml
parameters:
azureSubscription: ${{ parameters.GitHubAppServiceConnection }}
keyVaultName: ${{ parameters.GitHubAppKeyVaultName }}
keyName: ${{ parameters.GitHubAppKeyName }}
appClientId: ${{ parameters.GitHubAppClientId }}
installationOwner: ${{ parameters.GitHubOrg }}
outputVariableName: 'GitHubAppInstallationToken'
condition: ${{ parameters.condition }}

- task: OneLocBuild@2
displayName: OneLocBuild
env:
Expand All @@ -110,7 +132,10 @@ jobs:
patVariable: ${{ parameters.CeapexPat }}
${{ if eq(parameters.RepoType, 'gitHub') }}:
repoType: ${{ parameters.RepoType }}
gitHubPatVariable: "${{ parameters.GithubPat }}"
${{ if and(eq(parameters.UseGitHubAppAuthentication, true), eq(variables['System.TeamProject'], 'internal')) }}:
gitHubPatVariable: "$(GitHubAppInstallationToken)"
${{ if or(eq(parameters.UseGitHubAppAuthentication, false), ne(variables['System.TeamProject'], 'internal')) }}:
gitHubPatVariable: "${{ parameters.GithubPat }}"
${{ if ne(parameters.MirrorRepo, '') }}:
isMirrorRepoSelected: true
gitHubOrganization: ${{ parameters.GitHubOrg }}
Expand Down
79 changes: 79 additions & 0 deletions eng/common/core-templates/steps/get-github-app-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 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.
#
# 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.
# - The App must be installed on the target organization/account
# (`installationOwner`) with the permissions/repositories you need.
#
# Output: a secret pipeline variable named ${{ parameters.outputVariableName }}
# containing the installation access token. Token lifetime is ~1 hour and is
# automatically scrubbed from logs. Installation tokens are exempt from the
# enterprise classic-PAT lifetime policy.

parameters:
# 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 that holds the GitHub App's RSA signing key.
- name: keyVaultName
type: string

# Name of the RSA key inside the Key Vault (the App's private key).
- name: keyName
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
type: string

# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
- name: installationOwner
type: string

# Name of the pipeline variable that will receive the installation token.
- name: outputVariableName
type: string

- name: is1ESPipeline
type: boolean

- name: stepName
type: string
default: getGitHubAppInstallationToken

- name: condition
type: string
default: ''

- name: displayName
type: string
default: Get GitHub App installation token

steps:
- task: AzureCLI@2
displayName: ${{ parameters.displayName }}
name: ${{ parameters.stepName }}
${{ if ne(parameters.condition, '') }}:
condition: ${{ parameters.condition }}
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
& "$(System.DefaultWorkingDirectory)/eng/common/Get-GitHubAppToken.ps1" `
-KeyVaultName '${{ parameters.keyVaultName }}' `
-KeyName '${{ parameters.keyName }}' `
-AppClientId '${{ parameters.appClientId }}' `
-InstallationOwner '${{ parameters.installationOwner }}' `
-OutputVariableName '${{ parameters.outputVariableName }}'
7 changes: 7 additions & 0 deletions eng/common/templates-official/steps/get-github-app-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
steps:
- template: /eng/common/core-templates/steps/get-github-app-token.yml
parameters:
is1ESPipeline: true

${{ each parameter in parameters }}:
${{ parameter.key }}: ${{ parameter.value }}
7 changes: 7 additions & 0 deletions eng/common/templates/steps/get-github-app-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
steps:
- template: /eng/common/core-templates/steps/get-github-app-token.yml
parameters:
is1ESPipeline: false

${{ each parameter in parameters }}:
${{ parameter.key }}: ${{ parameter.value }}
2 changes: 1 addition & 1 deletion eng/native/version/_version.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if defined(__GNUC__) && !defined(__clang__) && defined(TARGET_SUNOS) && defined(TARGET_AMD64)
#if defined(__GNUC__) && !defined(__clang__) && defined(__sun) && defined(__x86_64__)
char sccsid[] __attribute__((used, weak)) = "@(#)No version information produced";
__asm__(".pushsection .init_array; .reloc ., R_X86_64_NONE, sccsid; .popsection");
#else
Expand Down
8 changes: 4 additions & 4 deletions global.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"dotnet": "11.0.100-preview.6.26359.118"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26379.102",
"Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26381.1",
"Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26379.102",
"Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26404.101",
"Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26404.101",
"Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26404.101",
"Microsoft.Build.NoTargets": "3.7.0",
"Microsoft.Build.Traversal": "3.4.0",
"Microsoft.NET.Sdk.IL": "11.0.0-rc.1.26379.102"
"Microsoft.NET.Sdk.IL": "11.0.0-rc.1.26404.101"
}
}
2 changes: 1 addition & 1 deletion src/mono/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
else()
if(NOT EXISTS "${VERSION_FILE_PATH}")
file(WRITE "${VERSION_FILE_PATH}"
"#if defined(__GNUC__) && !defined(__clang__) && defined(TARGET_SUNOS) && defined(TARGET_AMD64)\n"
"#if defined(__GNUC__) && !defined(__clang__) && defined(__sun) && defined(__x86_64__)\n"
"char sccsid[] __attribute__((used, weak)) = \"@(#)Version 42.42.42.42424 @Commit: AAA\";\n"
"__asm__(\".pushsection .init_array; .reloc ., R_X86_64_NONE, sccsid; .popsection\");\n"
"#else\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="$(MicrosoftCodeAnalysisVersion)" />
<!-- to support MSBuildWorkspace -->
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildVersion)" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkVersion)" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkHotReloadVersion)" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="$(MicrosoftCodeAnalysisVersion)" />
</ItemGroup>

Expand Down
Loading