From 51fa74b4112bf634550b7c69ef98b2e673b0c79c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 10 Aug 2026 05:04:25 +0000 Subject: [PATCH 01/12] Update dependencies from https://github.com/dotnet/dotnet build 20260809.2 On relative base path root Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Feed From Version 11.0.0-beta.26401.101 -> To Version 11.0.0-beta.26409.102 Microsoft.DotNet.Cecil From Version 0.11.5-preview.26401.101 -> To Version 0.11.5-preview.26409.102 Microsoft.NET.ILLink , Microsoft.NETCore.App.Ref From Version 11.0.0-rc.1.26401.101 -> To Version 11.0.0-rc.1.26409.102 Microsoft.NET.Sdk , Microsoft.TemplateEngine.Authoring.Tasks From Version 11.0.100-rc.1.26401.101 -> To Version 11.0.100-rc.1.26409.102 --- eng/Version.Details.xml | 28 ++-- eng/Versions.props | 12 +- eng/common/Get-GitHubAppToken.ps1 | 154 ++++++++++++++++++ .../core-templates/job/helix-job-monitor.yml | 13 ++ eng/common/core-templates/job/onelocbuild.yml | 28 +++- .../job/publish-build-assets.yml | 2 - .../post-build/common-variables.yml | 2 - eng/common/core-templates/stages/renovate.yml | 2 + .../steps/get-github-app-token.yml | 79 +++++++++ .../core-templates/steps/publish-logs.yml | 3 - .../steps/get-github-app-token.yml | 7 + .../templates/steps/get-github-app-token.yml | 7 + global.json | 2 +- 13 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 eng/common/Get-GitHubAppToken.ps1 create mode 100644 eng/common/core-templates/steps/get-github-app-token.yml create mode 100644 eng/common/templates-official/steps/get-github-app-token.yml create mode 100644 eng/common/templates/steps/get-github-app-token.yml diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index edd1abeaae2..548a4d9f6ef 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,20 +1,20 @@ - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 https://github.com/dotnet/dotnet @@ -36,17 +36,17 @@ - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/dotnet/dotnet - 5da2de22f7c6e2ebe7ed4c16c614b00183145d3c + 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 https://github.com/microsoft/testfx diff --git a/eng/Versions.props b/eng/Versions.props index f266a7321ef..a4f0b9749a0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,20 +1,20 @@ - 11.0.100-rc.1.26401.101 + 11.0.100-rc.1.26409.102 $(MicrosoftNETSdkPackageVersion) - 11.0.0-rc.1.26401.101 - 11.0.0-rc.1.26401.101 + 11.0.0-rc.1.26409.102 + 11.0.0-rc.1.26409.102 7.0.0-beta.22103.1 11.0.0-beta.26060.102 - 11.0.0-beta.26401.101 + 11.0.0-beta.26409.102 11.0.100-preview.4.26215.121 11.0.100-preview.4.26215.121 $(MicrosoftNETWorkloadMonoToolChainCurrentManifest110100preview4PackageVersion) $(MicrosoftNETWorkloadEmscriptenCurrentManifest110100preview4PackageVersion) - 11.0.100-rc.1.26401.101 - 0.11.5-preview.26401.101 + 11.0.100-rc.1.26409.102 + 0.11.5-preview.26409.102 4.4.0-preview.26402.3 10.0.10 11.0.0-preview.1.26104.118 diff --git a/eng/common/Get-GitHubAppToken.ps1 b/eng/common/Get-GitHubAppToken.ps1 new file mode 100644 index 00000000000..6b5899d7a29 --- /dev/null +++ b/eng/common/Get-GitHubAppToken.ps1 @@ -0,0 +1,154 @@ +# 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'..." +$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference +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 +} +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 +} +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 +} +$signatureUrl = $signatureBase64.Trim().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 = @() + $page = 1 + do { + $pageInstallations = @(Invoke-RestMethod ` + -Uri "https://api.github.com/app/installations?per_page=100&page=$page" ` + -Headers $headers ` + -Method Get) + $installations += $pageInstallations + $page++ + } while ($pageInstallations.Count -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." + exit 1 +} +$installation = $installations | Where-Object { $_.account.login -ieq $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 +} diff --git a/eng/common/core-templates/job/helix-job-monitor.yml b/eng/common/core-templates/job/helix-job-monitor.yml index 62fa1997117..a65b50d0a78 100644 --- a/eng/common/core-templates/job/helix-job-monitor.yml +++ b/eng/common/core-templates/job/helix-job-monitor.yml @@ -26,6 +26,11 @@ parameters: type: string default: '' +# Whether failures in the monitor job should allow the pipeline to continue. +- name: continueOnError + type: boolean + default: false + # NuGet package id of the Helix job monitor tool. - name: toolPackageId type: string @@ -65,6 +70,12 @@ parameters: type: boolean default: true +# When true, allow the monitor to succeed when this stage produces no Helix jobs in any attempt. +# Forwarded as --allow-no-helix-jobs. +- name: allowNoHelixJobs + 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; @@ -97,6 +108,7 @@ jobs: - job: HelixJobMonitor displayName: Monitor Helix Jobs timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + continueOnError: ${{ parameters.continueOnError }} ${{ if ne(length(parameters.dependsOn), 0) }}: dependsOn: ${{ parameters.dependsOn }} ${{ if ne(parameters.condition, '') }}: @@ -189,6 +201,7 @@ jobs: --helix-base-uri '${{ parameters.helixBaseUri }}' --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)' diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml index 2816d2905a0..4f5653d73ac 100644 --- a/eng/common/core-templates/job/onelocbuild.yml +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -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 and the App path is enabled by default. + # DevDiv requires its own project-scoped service connection before this path can be enabled there. + UseGitHubAppAuthentication: true + GitHubAppServiceConnection: 'dnceng-oneloc-githubapp' + GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9' + GitHubAppKeyVaultName: 'EngKeyVault' + GitHubAppKeyName: 'oneloc-localization-app-key' + SourcesDirectory: $(System.DefaultWorkingDirectory) CreatePr: true AutoCompletePr: false @@ -89,6 +98,20 @@ 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/core-templates/steps/get-github-app-token.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + 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: @@ -110,7 +133,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 }} diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 4229288d3d3..330225ae093 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -58,8 +58,6 @@ jobs: parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: Publish-Build-Assets - - group: AzureDevOps-Artifact-Feeds-Pats - name: runCodesignValidationInjection value: false # unconditional - needed for logs publishing (redactor tool version) diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml index db298ae16ba..a3a8480e254 100644 --- a/eng/common/core-templates/post-build/common-variables.yml +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -1,6 +1,4 @@ variables: - - group: Publish-Build-Assets - # Whether the build is internal or not - name: IsInternalBuild value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} diff --git a/eng/common/core-templates/stages/renovate.yml b/eng/common/core-templates/stages/renovate.yml index edab2818258..cfa9683794a 100644 --- a/eng/common/core-templates/stages/renovate.yml +++ b/eng/common/core-templates/stages/renovate.yml @@ -81,6 +81,8 @@ resources: extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: + settings: + networkIsolationPolicy: Permissive pool: ${{ parameters.pool }} sdl: sourceAnalysisPool: ${{ parameters.sdlPool }} diff --git a/eng/common/core-templates/steps/get-github-app-token.yml b/eng/common/core-templates/steps/get-github-app-token.yml new file mode 100644 index 00000000000..6d42a48d3c3 --- /dev/null +++ b/eng/common/core-templates/steps/get-github-app-token.yml @@ -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 }}' diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index c496f3d0dc7..2c1e0ab1162 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -30,8 +30,6 @@ steps: -TokensFilePath '$(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' -runtimeSourceFeed https://ci.dot.net/internal -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' - '$(publishing-dnceng-devdiv-code-r-build-re)' - '$(akams-client-id)' '$(System.AccessToken)' ${{parameters.CustomSensitiveDataList}} continueOnError: true @@ -56,4 +54,3 @@ steps: condition: always() retryCountOnTaskFailure: 10 # for any files being locked isProduction: false # logs are non-production artifacts - diff --git a/eng/common/templates-official/steps/get-github-app-token.yml b/eng/common/templates-official/steps/get-github-app-token.yml new file mode 100644 index 00000000000..c89f3641a4d --- /dev/null +++ b/eng/common/templates-official/steps/get-github-app-token.yml @@ -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 }} diff --git a/eng/common/templates/steps/get-github-app-token.yml b/eng/common/templates/steps/get-github-app-token.yml new file mode 100644 index 00000000000..79e182c6416 --- /dev/null +++ b/eng/common/templates/steps/get-github-app-token.yml @@ -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 }} diff --git a/global.json b/global.json index 2eb55a24266..88c3acc74e0 100644 --- a/global.json +++ b/global.json @@ -4,6 +4,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.134", - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26401.101" + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26409.102" } } From dbb2ec8575fd2a8b75e880b781d6e28c67915cae Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 10 Aug 2026 05:05:35 +0000 Subject: [PATCH 02/12] Update dependencies from https://github.com/microsoft/testfx build 20260809.6 On relative base path root MSTest From Version 4.4.0-preview.26402.3 -> To Version 4.4.0-preview.26409.6 --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 548a4d9f6ef..d8e044c755f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -48,9 +48,9 @@ https://github.com/dotnet/dotnet 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 - + https://github.com/microsoft/testfx - e492730d840fc5864eff04230dd993041cf6189a + 26dda6f3c287c9f8a012c2727870d33a0d88e0cb diff --git a/eng/Versions.props b/eng/Versions.props index a4f0b9749a0..37af25895dc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -15,7 +15,7 @@ $(MicrosoftNETWorkloadEmscriptenCurrentManifest110100preview4PackageVersion) 11.0.100-rc.1.26409.102 0.11.5-preview.26409.102 - 4.4.0-preview.26402.3 + 4.4.0-preview.26409.6 10.0.10 11.0.0-preview.1.26104.118 From 07e488c0d4f0136ffb6e5c140ed0bd167f89dcb2 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Aug 2026 12:11:06 -0500 Subject: [PATCH 03/12] [tests] Account for generated Java source JAR Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../Tests/Xamarin.Android.Build.Tests/XASdkTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index 0569f21a1f4..ef78cdcbcbf 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -185,12 +185,12 @@ public String Say (String quote) { aarEntry.Extract (aarStream); aarStream.Seek (0, SeekOrigin.Begin); - // Look for 2 .jar files under libs/ + // Look for the Maven dependency, foo.jar, and the compiled AndroidJavaSource output under libs/ using var aar = ZipArchive.Open (aarStream); int count = aar.Count (e => e.FullName.StartsWith ("libs/", StringComparison.OrdinalIgnoreCase) && e.FullName.EndsWith (".jar", StringComparison.OrdinalIgnoreCase)); - Assert.AreEqual (2, count, $"There should be 2 .jar files in the {aarPath} archive, but found {count}."); + Assert.AreEqual (3, count, $"There should be 3 .jar files in the {aarPath} archive, but found {count}."); } static IEnumerable Get_DotNetTargetFrameworks_Data () From d1790590be35127bf45e684de7664a6099e2401d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 11 Aug 2026 20:19:40 +0000 Subject: [PATCH 04/12] Update dependencies from https://github.com/dotnet/dotnet build 20260811.1 On relative base path root Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Feed From Version 11.0.0-beta.26401.101 -> To Version 11.0.0-beta.26411.101 Microsoft.DotNet.Cecil From Version 0.11.5-preview.26401.101 -> To Version 0.11.5-preview.26411.101 Microsoft.NET.ILLink , Microsoft.NETCore.App.Ref From Version 11.0.0-rc.1.26401.101 -> To Version 11.0.0-rc.1.26411.101 Microsoft.NET.Sdk , Microsoft.TemplateEngine.Authoring.Tasks From Version 11.0.100-rc.1.26401.101 -> To Version 11.0.100-rc.1.26411.101 --- eng/Version.Details.xml | 28 +++++++------- eng/Versions.props | 12 +++--- eng/common/SetupNugetSources.ps1 | 38 ++++++++++--------- eng/common/SetupNugetSources.sh | 4 +- eng/common/build.ps1 | 7 ++-- eng/common/build.sh | 14 ++++--- .../steps/enable-internal-sources.yml | 12 ++++-- eng/common/msbuild.ps1 | 10 ++--- eng/common/msbuild.sh | 16 ++++---- eng/common/tools.ps1 | 8 +++- eng/common/tools.sh | 21 +++++++++- global.json | 2 +- 12 files changed, 100 insertions(+), 72 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d8e044c755f..940570dcb11 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,20 +1,20 @@ - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a https://github.com/dotnet/dotnet @@ -36,17 +36,17 @@ - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a - + https://github.com/dotnet/dotnet - 7fb8cef14d9ae6bd729b04638f88d00f1ba7eb99 + f5782bfe7cbe62e23058d578aa2216269b0ad34a https://github.com/microsoft/testfx diff --git a/eng/Versions.props b/eng/Versions.props index 37af25895dc..48837b6ac60 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,20 +1,20 @@ - 11.0.100-rc.1.26409.102 + 11.0.100-rc.1.26411.101 $(MicrosoftNETSdkPackageVersion) - 11.0.0-rc.1.26409.102 - 11.0.0-rc.1.26409.102 + 11.0.0-rc.1.26411.101 + 11.0.0-rc.1.26411.101 7.0.0-beta.22103.1 11.0.0-beta.26060.102 - 11.0.0-beta.26409.102 + 11.0.0-beta.26411.101 11.0.100-preview.4.26215.121 11.0.100-preview.4.26215.121 $(MicrosoftNETWorkloadMonoToolChainCurrentManifest110100preview4PackageVersion) $(MicrosoftNETWorkloadEmscriptenCurrentManifest110100preview4PackageVersion) - 11.0.100-rc.1.26409.102 - 0.11.5-preview.26409.102 + 11.0.100-rc.1.26411.101 + 0.11.5-preview.26411.101 4.4.0-preview.26409.6 10.0.10 11.0.0-preview.1.26104.118 diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index b3bddff355e..b7a3769364d 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -11,7 +11,7 @@ # condition: eq(variables['Agent.OS'], 'Windows_NT') # inputs: # filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token +# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config # env: # Token: $(InternalFeedToken) # @@ -29,12 +29,14 @@ [CmdletBinding()] param ( [Parameter(Mandatory = $true)][string]$ConfigFile, - $Password + # Keep the legacy name as an alias while callers migrate secrets to the Token environment variable. + [Alias("Password")]$Credential ) $ErrorActionPreference = "Stop" Set-StrictMode -Version 2.0 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$feedCredential = if ($env:Token) { $env:Token } else { $Credential } # This script only consumes helper functions from tools.ps1 to configure NuGet feeds. # Skip importing configure-toolset.ps1 so that repo-specific toolset setup (e.g. acquiring @@ -44,14 +46,14 @@ $disableConfigureToolsetImport = $true . $PSScriptRoot\tools.ps1 # Adds or enables the package source with the given name -function AddOrEnablePackageSource($sources, $disabledPackageSources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { - if ($disabledPackageSources -eq $null -or -not (EnableInternalPackageSource -DisabledPackageSources $disabledPackageSources -Creds $creds -PackageSourceName $SourceName)) { - AddPackageSource -Sources $sources -SourceName $SourceName -SourceEndPoint $SourceEndPoint -Creds $creds -Username $userName -pwd $Password +function AddOrEnablePackageSource($sources, $disabledPackageSources, $SourceName, $SourceEndPoint, $creds, $Username, $credential) { + if ($disabledPackageSources -eq $null -or -not (EnableInternalPackageSource -DisabledPackageSources $disabledPackageSources -Creds $creds -PackageSourceName $SourceName -Credential $credential)) { + AddPackageSource -Sources $sources -SourceName $SourceName -SourceEndPoint $SourceEndPoint -Creds $creds -Username $Username -credential $credential } } # Add source entry to PackageSources -function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { +function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $credential) { $packageSource = $sources.SelectSingleNode("add[@key='$SourceName']") if ($packageSource -eq $null) @@ -67,13 +69,13 @@ function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Usern Write-Host "Package source $SourceName already present and enabled." } - AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd + AddCredential -Creds $creds -Source $SourceName -Username $Username -credential $credential } # Add a credential node for the specified source -function AddCredential($creds, $source, $username, $pwd) { +function AddCredential($creds, $source, $username, $credential) { # If no cred supplied, don't do anything. - if (!$pwd) { + if (!$credential) { return; } @@ -108,19 +110,19 @@ function AddCredential($creds, $source, $username, $pwd) { $sourceElement.AppendChild($passwordElement) | Out-Null } - $passwordElement.SetAttribute("value", $pwd) + $passwordElement.SetAttribute("value", $credential) } # Enable all darc-int package sources. -function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds) { +function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds, $Credential) { $maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") ForEach ($DisabledPackageSource in $maestroInternalSources) { - EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key + EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key -Credential $Credential } } # Enables an internal package source by name, if found. Returns true if the package source was found and enabled, false otherwise. -function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSourceName) { +function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSourceName, $Credential) { $DisabledPackageSource = $DisabledPackageSources.SelectSingleNode("add[@key='$PackageSourceName']") if ($DisabledPackageSource) { Write-Host "Enabling internal source '$($DisabledPackageSource.key)'." @@ -128,7 +130,7 @@ function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSo # Due to https://github.com/NuGet/Home/issues/10291, we must actually remove the disabled entries $DisabledPackageSources.RemoveChild($DisabledPackageSource) - AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -pwd $Password + AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -credential $credential return $true } return $false @@ -153,7 +155,7 @@ if ($sources -eq $null) { $creds = $null $feedSuffix = "v3/index.json" -if ($Password) { +if ($feedCredential) { $feedSuffix = "v2" # Looks for a node. Create it if none is found. $creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials") @@ -169,7 +171,7 @@ $userName = "dn-bot" $disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources") if ($disabledSources -ne $null) { Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node" - EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds + EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds -Credential $feedCredential } $dotnetVersions = @('5','6','7','8','9','10') @@ -177,8 +179,8 @@ foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; $dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']") if ($dotnetSource -ne $null) { - AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password - AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -credential $feedCredential + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/$feedSuffix" -Creds $creds -Username $userName -credential $feedCredential } } diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index 67e7e0942ca..c3ae8ac054f 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -24,7 +24,9 @@ # This logic is also abstracted into enable-internal-sources.yml. ConfigFile=$1 -CredToken=$2 +# Prefer the environment variable so credentials do not appear in process arguments. +# Retain the positional argument as a compatibility fallback for existing callers. +CredToken=${Token:-$2} NL='\n' TB=' ' diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index dd84699f500..fee2f839919 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -8,6 +8,7 @@ Param( [bool] $warnAsError = $true, [string] $warnNotAsError = '', [bool] $nodeReuse = $true, + [bool][Alias('mt')]$msbuildMultiThreaded = $false, [switch] $buildCheck = $false, [switch][Alias('r')]$restore, [switch] $deployDeps, @@ -79,6 +80,7 @@ function Print-Usage() { Write-Host " -excludePrereleaseVS Set to exclude build engines in prerelease versions of Visual Studio" Write-Host " -nativeToolsOnMachine Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)" Write-Host " -nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" + Write-Host " -msbuildMultiThreaded Sets MSBuild's multi-threaded mode, i.e. the -mt switch ('1' or '0') (short: -mt)" Write-Host " -buildCheck Sets /check msbuild parameter" Write-Host " -fromVMR Set when building from within the VMR" Write-Host " -disablePipelineSetResult Set to disable masking the actual exit code in the pipeline when the build fails" @@ -175,9 +177,8 @@ try { if (-not $excludeCIBinarylog) { $binaryLog = $true } - # Disable node reuse on CI unless explicitly opted in via MSBUILD_NODEREUSE_ENABLED. - # Internal testing only; this env var will be replaced with a switch (https://github.com/dotnet/arcade/issues/17013) and must not be depended on. - if ($env:MSBUILD_NODEREUSE_ENABLED -ne "1") { + # Node reuse isn't used on CI unless it was explicitly requested via -nodeReuse. + if (-not $PSBoundParameters.ContainsKey('nodeReuse')) { $nodeReuse = $false } } diff --git a/eng/common/build.sh b/eng/common/build.sh index e37edd6cff3..109d83ff73f 100644 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -43,6 +43,7 @@ usage() echo " --pipelinesLog Promote msbuild errors/warnings to Azure Pipelines timeline issues; defaults to on in CI (short: -pl)" echo " --prepareMachine Prepare machine for CI run, clean up processes after build" echo " --nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" + echo " --msbuildMultiThreaded Sets MSBuild's multi-threaded mode, i.e. the -mt switch ('true' or 'false') (short: --mt)" echo " --warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" echo " --warnNotAsError Sets a semi-colon delimited list of warning codes that should not be treated as errors" echo " --buildCheck Sets /check msbuild parameter" @@ -84,7 +85,9 @@ clean=false warn_as_error=true warn_not_as_error='' -node_reuse=true +# Empty means "not specified"; tools.sh defaults these to on for local builds and off on CI. +node_reuse='' +msbuild_multi_threaded='' build_check=false binary_log=false binary_log_name='' @@ -199,6 +202,10 @@ while [[ $# -gt 0 ]]; do node_reuse=$2 shift ;; + -msbuildmultithreaded|-mt) + msbuild_multi_threaded=$2 + shift + ;; -buildcheck) build_check=true ;; @@ -224,11 +231,6 @@ fi if [[ "$ci" == true ]]; then pipelines_log=true - # Disable node reuse on CI unless explicitly opted in via MSBUILD_NODEREUSE_ENABLED. - # Internal testing only; this env var will be replaced with a switch (https://github.com/dotnet/arcade/issues/17013) and must not be depended on. - if [[ "${MSBUILD_NODEREUSE_ENABLED:-}" != "1" ]]; then - node_reuse=false - fi if [[ "$exclude_ci_binary_log" == false ]]; then binary_log=true fi diff --git a/eng/common/core-templates/steps/enable-internal-sources.yml b/eng/common/core-templates/steps/enable-internal-sources.yml index 51af9a01709..843cdff7821 100644 --- a/eng/common/core-templates/steps/enable-internal-sources.yml +++ b/eng/common/core-templates/steps/enable-internal-sources.yml @@ -19,7 +19,7 @@ steps: displayName: Setup Internal Feeds inputs: filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config env: Token: ${{ parameters.legacyCredential }} - task: Bash@3 @@ -28,7 +28,7 @@ steps: inputs: targetType: inline script: | - "$(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh" "$(System.DefaultWorkingDirectory)/NuGet.config" "$Token" + "$(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh" "$(System.DefaultWorkingDirectory)/NuGet.config" env: Token: ${{ parameters.legacyCredential }} # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. @@ -58,13 +58,17 @@ steps: displayName: Setup Internal Feeds inputs: filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config + env: + Token: $(dnceng-artifacts-feeds-read-access-token) - task: Bash@3 condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) displayName: Setup Internal Feeds inputs: filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh - arguments: $(System.DefaultWorkingDirectory)/NuGet.config $(dnceng-artifacts-feeds-read-access-token) + arguments: $(System.DefaultWorkingDirectory)/NuGet.config + env: + Token: $(dnceng-artifacts-feeds-read-access-token) # This is required in certain scenarios to install the ADO credential provider. # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others # (e.g. dotnet msbuild). diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1 index 495d533a909..b6dfb570ea5 100644 --- a/eng/common/msbuild.ps1 +++ b/eng/common/msbuild.ps1 @@ -3,6 +3,7 @@ Param( [string] $verbosity = 'minimal', [bool] $warnAsError = $true, [bool] $nodeReuse = $true, + [bool][Alias('mt')]$msbuildMultiThreaded = $false, [switch] $ci, [switch] $prepareMachine, [switch] $excludePrereleaseVS, @@ -13,12 +14,9 @@ Param( . $PSScriptRoot\tools.ps1 try { - if ($ci) { - # Disable node reuse on CI unless explicitly opted in via MSBUILD_NODEREUSE_ENABLED. - # Internal testing only; this env var will be replaced with a switch (https://github.com/dotnet/arcade/issues/17013) and must not be depended on. - if ($env:MSBUILD_NODEREUSE_ENABLED -ne "1") { - $nodeReuse = $false - } + # Node reuse isn't used on CI unless it was explicitly requested via -nodeReuse. + if ($ci -and -not $PSBoundParameters.ContainsKey('nodeReuse')) { + $nodeReuse = $false } MSBuild @extraArgs diff --git a/eng/common/msbuild.sh b/eng/common/msbuild.sh index 333be3232fc..a40c101237b 100644 --- a/eng/common/msbuild.sh +++ b/eng/common/msbuild.sh @@ -14,7 +14,9 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" verbosity='minimal' warn_as_error=true -node_reuse=true +# Empty means "not specified"; tools.sh defaults these to on for local builds and off on CI. +node_reuse='' +msbuild_multi_threaded='' prepare_machine=false extra_args='' @@ -33,6 +35,10 @@ while (($# > 0)); do node_reuse=$2 shift 2 ;; + --msbuildmultithreaded|--mt) + msbuild_multi_threaded=$2 + shift 2 + ;; --ci) ci=true shift 1 @@ -50,13 +56,5 @@ done . "$scriptroot/tools.sh" -if [[ "$ci" == true ]]; then - # Disable node reuse on CI unless explicitly opted in via MSBUILD_NODEREUSE_ENABLED. - # Internal testing only; this env var will be replaced with a switch (https://github.com/dotnet/arcade/issues/17013) and must not be depended on. - if [[ "${MSBUILD_NODEREUSE_ENABLED:-}" != "1" ]]; then - node_reuse=false - fi -fi - MSBuild $extra_args ExitWithExitCode 0 diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index da07386ff1f..d4ee05cb63b 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -31,6 +31,10 @@ # Set to true to reuse msbuild nodes. Recommended to not reuse on CI. [bool]$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { !$ci } +# Set to true to build with MSBuild's multi-threaded mode (-mt). Opt-in for now, so off unless it was +# explicitly requested. It's intended to become the default for local builds once it has proven out. +[bool]$msbuildMultiThreaded = if (Test-Path variable:msbuildMultiThreaded) { $msbuildMultiThreaded } else { $false } + # Configures warning treatment in msbuild. [bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } @@ -808,8 +812,8 @@ function MSBuild() { $cmdArgs = "$($buildTool.Command) /m /nologo /clp:Summary /v:$verbosity /nr:$nodeReuse /p:ContinuousIntegrationBuild=$ci" - # Add -mt flag for MSBuild multithreaded mode if enabled via environment variable - if ($env:MSBUILD_MT_ENABLED -eq "1") { + # Build with MSBuild's multi-threaded mode. + if ($msbuildMultiThreaded) { $cmdArgs += ' -mt' } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 20f791c11de..a2c96576047 100644 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -1,5 +1,15 @@ #!/usr/bin/env bash +# Normalizes the value of a boolean build argument. Accepts 1/0 in addition to true/false so that +# the same value works with the PowerShell scripts, whose [bool] parameters only bind 1/0. +function NormalizeBoolArg { + case "${1:-}" in + 1) echo true ;; + 0) echo false ;; + *) echo "${1:-}" ;; + esac +} + # Initialize variables if they aren't already defined. # CI mode - set to true on CI server for PR validation build or official build. @@ -43,13 +53,20 @@ restore=${restore:-true} verbosity=${verbosity:-'minimal'} # Set to true to reuse msbuild nodes. Recommended to not reuse on CI. +node_reuse=$(NormalizeBoolArg "${node_reuse:-}") if [[ "$ci" == true ]]; then node_reuse=${node_reuse:-false} else node_reuse=${node_reuse:-true} fi +# Set to true to build with MSBuild's multi-threaded mode (-mt). Opt-in for now, so off unless it was +# explicitly requested. It's intended to become the default for local builds once it has proven out. +msbuild_multi_threaded=$(NormalizeBoolArg "${msbuild_multi_threaded:-}") +msbuild_multi_threaded=${msbuild_multi_threaded:-false} + # Configures warning treatment in msbuild. +warn_as_error=$(NormalizeBoolArg "${warn_as_error:-}") warn_as_error=${warn_as_error:-true} # Specifies semi-colon delimited list of warning codes that should not be treated as errors. @@ -587,9 +604,9 @@ function MSBuild { } } - # Add -mt flag for MSBuild multithreaded mode if enabled via environment variable + # Build with MSBuild's multi-threaded mode. local mt_switch="" - if [[ "${MSBUILD_MT_ENABLED:-}" == "1" ]]; then + if [[ "$msbuild_multi_threaded" == true ]]; then mt_switch="-mt" fi diff --git a/global.json b/global.json index 88c3acc74e0..1a9a564c1bb 100644 --- a/global.json +++ b/global.json @@ -4,6 +4,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.134", - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26409.102" + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26411.101" } } From 5b35c1d12f9d9816f3aef4004bd782457ce1b19a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Aug 2026 13:24:39 +0000 Subject: [PATCH 05/12] Update dependencies from https://github.com/dotnet/dotnet build 20260811.19 On relative base path root Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Feed From Version 11.0.0-beta.26401.101 -> To Version 11.0.0-beta.26411.119 Microsoft.DotNet.Cecil From Version 0.11.5-preview.26401.101 -> To Version 0.11.5-preview.26411.119 Microsoft.NET.ILLink , Microsoft.NETCore.App.Ref From Version 11.0.0-rc.1.26401.101 -> To Version 11.0.0-rc.1.26411.119 Microsoft.NET.Sdk , Microsoft.TemplateEngine.Authoring.Tasks From Version 11.0.100-rc.1.26401.101 -> To Version 11.0.100-rc.1.26411.119 --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 12 ++++++------ eng/common/tools.ps1 | 3 ++- eng/common/tools.sh | 3 ++- global.json | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 940570dcb11..03194327f93 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,20 +1,20 @@ - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a https://github.com/dotnet/dotnet @@ -36,17 +36,17 @@ - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a - + https://github.com/dotnet/dotnet - f5782bfe7cbe62e23058d578aa2216269b0ad34a + 7cdb217445905f3342bbb0266a4497b9a014389a https://github.com/microsoft/testfx diff --git a/eng/Versions.props b/eng/Versions.props index 48837b6ac60..28aa2a199a6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,20 +1,20 @@ - 11.0.100-rc.1.26411.101 + 11.0.100-rc.1.26411.119 $(MicrosoftNETSdkPackageVersion) - 11.0.0-rc.1.26411.101 - 11.0.0-rc.1.26411.101 + 11.0.0-rc.1.26411.119 + 11.0.0-rc.1.26411.119 7.0.0-beta.22103.1 11.0.0-beta.26060.102 - 11.0.0-beta.26411.101 + 11.0.0-beta.26411.119 11.0.100-preview.4.26215.121 11.0.100-preview.4.26215.121 $(MicrosoftNETWorkloadMonoToolChainCurrentManifest110100preview4PackageVersion) $(MicrosoftNETWorkloadEmscriptenCurrentManifest110100preview4PackageVersion) - 11.0.100-rc.1.26411.101 - 0.11.5-preview.26411.101 + 11.0.100-rc.1.26411.119 + 0.11.5-preview.26411.119 4.4.0-preview.26409.6 10.0.10 11.0.0-preview.1.26104.118 diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index d4ee05cb63b..e84033dad90 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -39,7 +39,8 @@ [bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } # Specifies semi-colon delimited list of warning codes that should not be treated as errors. -[string]$warnNotAsError = if (Test-Path variable:warnNotAsError) { $warnNotAsError } else { '' } +# Defaults to NuGet Audit warning codes NU1901-NU1904. +[string]$warnNotAsError = if ((Test-Path variable:warnNotAsError) -and $warnNotAsError) { $warnNotAsError } else { 'NU1901;NU1902;NU1903;NU1904' } # Specifies which msbuild engine to use for build: 'vs', 'dotnet' or unspecified (determined based on presence of tools.vs in global.json). [string]$msbuildEngine = if (Test-Path variable:msbuildEngine) { $msbuildEngine } else { $null } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index a2c96576047..4d14b100b06 100644 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -70,7 +70,8 @@ warn_as_error=$(NormalizeBoolArg "${warn_as_error:-}") warn_as_error=${warn_as_error:-true} # Specifies semi-colon delimited list of warning codes that should not be treated as errors. -warn_not_as_error=${warn_not_as_error:-''} +# Defaults to NuGet Audit warning codes NU1901-NU1904. +warn_not_as_error="${warn_not_as_error:-NU1901;NU1902;NU1903;NU1904}" # True to attempt using .NET Core already that meets requirements specified in global.json # installed on the machine instead of downloading one. diff --git a/global.json b/global.json index 1a9a564c1bb..44205817173 100644 --- a/global.json +++ b/global.json @@ -4,6 +4,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.134", - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26411.101" + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26411.119" } } From 5b3baf07ee1752abb939507717cd6cc7b90270ad Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 12 Aug 2026 10:05:35 -0500 Subject: [PATCH 06/12] [tests] Remove duplicate NativeAOT lane Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../automation/yaml-templates/stage-package-tests.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build-tools/automation/yaml-templates/stage-package-tests.yaml b/build-tools/automation/yaml-templates/stage-package-tests.yaml index 632529d2883..923d6f038c2 100644 --- a/build-tools/automation/yaml-templates/stage-package-tests.yaml +++ b/build-tools/automation/yaml-templates/stage-package-tests.yaml @@ -180,15 +180,6 @@ stages: artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab artifactFolder: $(DotNetTargetFramework)-NativeAOT - - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml - parameters: - configuration: $(XA.Build.Configuration) - testName: Mono.Android.NET_Tests-NativeAOTTrimmable - project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj - extraBuildArgs: -p:TestsFlavor=NativeAOTTrimmable -p:PublishAot=true -p:_AndroidTypeMapImplementation=trimmable - artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab - artifactFolder: $(DotNetTargetFramework)-NativeAOTTrimmable - - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml parameters: configuration: $(XA.Build.Configuration) From a857079c741883e1a75cee42881b89d6e87fc7e3 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 12 Aug 2026 13:29:41 -0500 Subject: [PATCH 07/12] [ci] Capture package intermediates on failure Preserve packaged_resources, base.zip, and the unsigned app bundle before the APK test cleanup so archive omissions can be localized. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../yaml-templates/apk-instrumentation.yaml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/build-tools/automation/yaml-templates/apk-instrumentation.yaml b/build-tools/automation/yaml-templates/apk-instrumentation.yaml index 0cb374a7b1e..05b8f4b27a3 100644 --- a/build-tools/automation/yaml-templates/apk-instrumentation.yaml +++ b/build-tools/automation/yaml-templates/apk-instrumentation.yaml @@ -59,12 +59,19 @@ steps: continueOnError: true timeoutInMinutes: 3 -- script: > - DEST="$(Build.StagingDirectory)/Test${{ parameters.configuration }}/${{ parameters.artifactFolder }}/" && - mkdir -p "$DEST" && - cp "${{ parameters.artifactSource }}" "$DEST" || - echo "copy apk/aab failed" # We don't need to report the error - displayName: copy apk/aab +- script: | + DEST="$(Build.StagingDirectory)/Test${{ parameters.configuration }}/${{ parameters.artifactFolder }}/" + INTERMEDIATE_DIR="$(dirname "${{ parameters.project }}")/obj/${{ parameters.configuration }}/$(DotNetTargetFramework)-android/android/bin" + mkdir -p "$DEST" + cp "${{ parameters.artifactSource }}" "$DEST" || echo "copy apk/aab failed" + cp "$INTERMEDIATE_DIR/packaged_resources" "$DEST/packaged_resources.zip" || echo "copy packaged_resources failed" + cp "$INTERMEDIATE_DIR/base.zip" "$DEST" || echo "copy base.zip failed" + for AAB in "$INTERMEDIATE_DIR"/*.aab; do + if [ -f "$AAB" ]; then + cp "$AAB" "$DEST" + fi + done + displayName: copy package intermediates condition: ne(variables['Agent.JobStatus'], 'Succeeded') continueOnError: true From e4c8f2480c098b1613c3577365da8f7f48614239 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 12 Aug 2026 13:30:09 -0500 Subject: [PATCH 08/12] [ci] Stage every package test archive Keep each enabled invocation's intermediates so a failed job artifact contains passing and failing archives for comparison. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- build-tools/automation/yaml-templates/apk-instrumentation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/automation/yaml-templates/apk-instrumentation.yaml b/build-tools/automation/yaml-templates/apk-instrumentation.yaml index 05b8f4b27a3..4949c9057d8 100644 --- a/build-tools/automation/yaml-templates/apk-instrumentation.yaml +++ b/build-tools/automation/yaml-templates/apk-instrumentation.yaml @@ -72,7 +72,7 @@ steps: fi done displayName: copy package intermediates - condition: ne(variables['Agent.JobStatus'], 'Succeeded') + condition: ${{ parameters.condition }} continueOnError: true # Always capture full device logcat so we can diagnose native library load From 8d552dd32e3d54018f424b3628225924609de79c Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 12 Aug 2026 16:08:54 -0500 Subject: [PATCH 09/12] [tests] Remove fixed dotnet test workarounds The current SDK contains the fixes for duplicate framework-reference state and DOTNET_HOST_PATH propagation during device-aware dotnet test builds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../automation/yaml-templates/apk-instrumentation.yaml | 2 -- tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/build-tools/automation/yaml-templates/apk-instrumentation.yaml b/build-tools/automation/yaml-templates/apk-instrumentation.yaml index 4949c9057d8..e90c8f52b49 100644 --- a/build-tools/automation/yaml-templates/apk-instrumentation.yaml +++ b/build-tools/automation/yaml-templates/apk-instrumentation.yaml @@ -29,8 +29,6 @@ steps: $env:PATH = "${DOTNET_ROOT};$env:PATH" $dotnetPath = "${DOTNET_ROOT}\dotnet.exe" } - # Workaround for https://github.com/dotnet/sdk/pull/55581 - $env:DOTNET_HOST_PATH = $dotnetPath & $dotnetPath test $projectFile -bl:${{ parameters.xaSourcePath }}/bin/Test${{ parameters.configuration }}/run-${{ parameters.testName }}.binlog -c ${{ parameters.configuration }} --results-directory $resultsDir --report-trx --report-trx-filename ${{ parameters.testName }}.trx --output Detailed ${{ parameters.extraBuildArgs }} Pop-Location displayName: run ${{ parameters.testName }} diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 0f4a333f069..1f4e1b8f922 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -2837,11 +2837,6 @@ public void DotNetNewAndroidTest (string mode, MSTestPackageChannel msTestPackag Assert.IsTrue (dotnet.Build (parameters: buildParameters.ToArray ()), "`dotnet build` should succeed"); dotnet.AssertHasNoWarnings (); - // `dotnet test` doesn't go through the MSBuild Run target, so Install - // must be invoked explicitly to deploy the APK to the device. - if (mode == "test") - Assert.IsTrue (dotnet.Build (target: "Install", parameters: buildParameters.ToArray ()), "`dotnet build -t:Install` should succeed"); - // Run based on mode var runParameters = buildParameters.Select (p => $"/p:{p}").ToList (); if (mode == "test") From 0bd772e72c37574cf97139e83546c726985f1d04 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 12 Aug 2026 22:24:25 -0500 Subject: [PATCH 10/12] [NativeAOT] Preserve shared library during deployment Register an existing NativeAOT shared library from the ComputeFilesToPublish hook so incremental deployment builds retain it even when the native linker target is not scheduled. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../Microsoft.Android.Sdk.NativeAOT.targets | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 3db85d3f68c..1574c59f081 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -371,13 +371,6 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. /> - - - - $(NativeBinaryPrefix)$(TargetName).so - PreserveNewest - - @@ -399,7 +392,16 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. DependsOnTargets="$(_AndroidRunNativeCompileDependsOn)" /> + + <_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so + + + + + $(NativeBinaryPrefix)$(TargetName).so + PreserveNewest + From 5dff6b74237c0fd4faf73ae1510c3db6bed93c7d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 13 Aug 2026 08:50:52 -0500 Subject: [PATCH 11/12] [NativeAOT] Resolve incremental shared library path Reconstruct the RID-specific NativeAOT output path when NativeCompile is not scheduled, and cover publish registration without the late-bound NativeAOT properties. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../Microsoft.Android.Sdk.NativeAOT.targets | 6 ++- .../NativeAotBuildTests.cs | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 1574c59f081..cfc00a72881 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -393,13 +393,15 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. - <_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so + <_AndroidNativeAotSharedLibraryName>lib$(TargetName).so + <_AndroidNativeAotSharedLibrary Condition=" '$(NativeOutputPath)' != '' ">$(NativeOutputPath)$(_AndroidNativeAotSharedLibraryName) + <_AndroidNativeAotSharedLibrary Condition=" '$(NativeOutputPath)' == '' ">$(OutputPath)$(RuntimeIdentifier)/native/$(_AndroidNativeAotSharedLibraryName) - $(NativeBinaryPrefix)$(TargetName).so + $(_AndroidNativeAotSharedLibraryName) PreserveNewest diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs index 9f1cfab9198..cd08cb6b7b6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs @@ -57,6 +57,46 @@ public void BuildNativeAot_WithoutNdk () ); } + [Test] + public void NativeAotSharedLibraryIsPublishedWithoutNativeCompile () + { + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; + proj.SetRuntime (AndroidRuntime.NativeAOT); + proj.Imports.Add (new Import ("nativeaot-publish.targets") { + TextContent = () => + """ + + + + + + <_ExpectedNativeAotSharedLibrary>$(OutputPath)$(RuntimeIdentifier)/native/lib$(TargetName).so + + + + + + + <_NativeAotAppLibrary Include="@(ResolvedFileToPublish)" Condition=" '%(Filename)%(Extension)' == 'lib$(TargetName).so' " /> + + + + + + """, + }); + + using var builder = CreateApkBuilder (); + Assert.IsTrue ( + builder.RunTarget (proj, "_AndroidFixNativeLibraryFileName", parameters: [ + "RuntimeIdentifier=android-arm64", + ]), + "Existing NativeAOT shared library should be registered for publishing." + ); + } + [Test] public void BuildNativeAot_WithNdkLinker () { From b6f38b49fc70f09cdb8d2e61f4e1b81087aaba78 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 13 Aug 2026 08:53:00 -0500 Subject: [PATCH 12/12] [tests] Remove synthetic NativeAOT publish test Rely on the existing end-to-end NativeAOT APK lane instead of manufacturing internal target state. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e6b01b86-3a5c-497d-b24e-086bb8d73d5b --- .../NativeAotBuildTests.cs | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs index cd08cb6b7b6..9f1cfab9198 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs @@ -57,46 +57,6 @@ public void BuildNativeAot_WithoutNdk () ); } - [Test] - public void NativeAotSharedLibraryIsPublishedWithoutNativeCompile () - { - var proj = new XamarinAndroidApplicationProject { - IsRelease = true, - }; - proj.SetRuntime (AndroidRuntime.NativeAOT); - proj.Imports.Add (new Import ("nativeaot-publish.targets") { - TextContent = () => - """ - - - - - - <_ExpectedNativeAotSharedLibrary>$(OutputPath)$(RuntimeIdentifier)/native/lib$(TargetName).so - - - - - - - <_NativeAotAppLibrary Include="@(ResolvedFileToPublish)" Condition=" '%(Filename)%(Extension)' == 'lib$(TargetName).so' " /> - - - - - - """, - }); - - using var builder = CreateApkBuilder (); - Assert.IsTrue ( - builder.RunTarget (proj, "_AndroidFixNativeLibraryFileName", parameters: [ - "RuntimeIdentifier=android-arm64", - ]), - "Existing NativeAOT shared library should be registered for publishing." - ); - } - [Test] public void BuildNativeAot_WithNdkLinker () {