From 7f6e157b509dbcb0fc1fa305a9009d6a422660a2 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 11:26:39 -0500 Subject: [PATCH 01/14] [ci] Add signing to dnceng internal pipeline Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 243 +++++++++++++++++- .../automation/dnceng/arcade/Workloads.proj | 209 +++++++++++++++ .../dnceng/arcade/eng/Signing.props | 26 ++ .../dnceng/arcade/vsman/Workload.vsmanproj | 83 ++++++ .../dnceng/arcade/vsman/global.json | 6 + 5 files changed, 563 insertions(+), 4 deletions(-) create mode 100644 build-tools/automation/dnceng/arcade/Workloads.proj create mode 100644 build-tools/automation/dnceng/arcade/eng/Signing.props create mode 100644 build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj create mode 100644 build-tools/automation/dnceng/arcade/vsman/global.json diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 0a5ee0a5f7e..68468094fa4 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -10,10 +10,9 @@ # branch policies for main, release/*, and feature/*. Azure Repos does not # support YAML `pr` triggers. # -# Signing is intentionally NOT enabled yet. We will eventually release & -# sign from here; when we do, convert this to `extends` the 1ES -# `MicroBuild.1ES.Official.yml` template and flip the `use1ESTemplate` -# parameters below to `true` (see azure-pipelines.yaml for reference). +# Signing uses the in-repo Arcade templates and adapter under +# build-tools/automation/dnceng. Do not add a Xamarin.yaml-templates +# repository resource: dnceng/internal cannot access that repository. name: $(Build.SourceBranchName)-$(Build.SourceVersion)-$(Rev:r) @@ -46,6 +45,20 @@ resources: # Global variables variables: - template: /build-tools/automation/yaml-templates/variables.yaml@self +- name: _TeamName + value: Maui +- ${{ if and(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ne(variables['Build.Reason'], 'PullRequest')) }}: + - name: _SignType + value: real +- ${{ else }}: + - name: _SignType + value: test +- name: _BuildConfig + value: Release +- name: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE + value: 'true' +- name: DOTNET_SDK_VULNERABILITY_CHECK_DISABLE + value: 'true' # Override for internal pipeline - name: DefaultJavaSdkMajorVersion value: 21 @@ -270,6 +283,228 @@ stages: - template: /build-tools/automation/yaml-templates/fail-on-issue.yaml +# Signing and Visual Studio workload insertion artifacts +- stage: dotnet_prepare_release + displayName: Prepare .NET Release + dependsOn: + - mac_build + - linux_build + condition: and( + succeeded(), + ne(variables['Build.Reason'], 'PullRequest'), + ne(variables['Build.Reason'], 'Schedule') + ) + jobs: + - template: /eng/common/templates/jobs/jobs.yml@self + parameters: + runAsPublic: false + enableMicrobuild: true + microbuildUseESRP: ${{ and(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ne(variables['Build.Reason'], 'PullRequest')) }} + jobs: + - job: prepare_release + displayName: Sign NuGets and workload MSIs + timeoutInMinutes: 240 + pool: + name: $(NetCoreInternalPoolName) + demands: + - ImageOverride -equals $(WindowsPoolImageNetCoreInternal) + workspace: + clean: all + steps: + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false + path: s/android + + - checkout: android-tools + path: s/android-tools + + - task: DownloadPipelineArtifact@2 + displayName: Download unsigned macOS and Windows packages + inputs: + artifactName: $(NuGetArtifactName) + downloadPath: $(Build.StagingDirectory)\unsigned\mac-win + + - task: DownloadPipelineArtifact@2 + displayName: Download unsigned Linux packages + inputs: + artifactName: $(LinuxNuGetArtifactName) + downloadPath: $(Build.StagingDirectory)\unsigned\linux + + - task: UseDotNet@2 + displayName: Install .NET 8 SDK for SwixBuild + inputs: + version: 8.0.x + + - task: UseDotNet@2 + displayName: Install .NET SDK for Arcade signing + inputs: + version: $(DotNetSdkVersion).x + includePreviewVersions: true + + - pwsh: | + $unsigned = "$(Build.StagingDirectory)\unsigned" + $artifacts = "$(Build.SourcesDirectory)\android\artifacts" + $shipping = Join-Path $artifacts "packages\Release\Shipping" + $logs = Join-Path $artifacts "log\Release" + + Remove-Item $artifacts -Recurse -Force -ErrorAction Ignore + New-Item $shipping -ItemType Directory -Force | Out-Null + New-Item $logs -ItemType Directory -Force | Out-Null + + $packages = @(Get-ChildItem $unsigned -Filter "*.nupkg" -File -Recurse) + if ($packages.Count -eq 0) { + throw "The build did not publish any NuGet packages." + } + Copy-Item $packages.FullName $shipping + + $workloadProps = Get-ChildItem $unsigned -Filter "vs-workload.props" -File -Recurse | Select-Object -First 1 + if (-not $workloadProps) { + throw "The build did not publish vs-workload.props." + } + Copy-Item $workloadProps.FullName $shipping + + $signList = Get-ChildItem $unsigned -Filter "SignList.xml" -File -Recurse | Select-Object -First 1 + if (-not $signList) { + throw "The build did not publish SignList.xml." + } + Write-Host "##vso[task.setvariable variable=AndroidSignList]$($signList.FullName)" + + $signVerify = Get-ChildItem $unsigned -Filter "SignVerifyIgnore.txt" -File -Recurse | Select-Object -First 1 + if (-not $signVerify) { + throw "The build did not publish SignVerifyIgnore.txt." + } + Copy-Item $signVerify.FullName "$(Build.StagingDirectory)\SignVerifyIgnore.txt" + + $inputNames = $packages.Name | Sort-Object -Unique + $inputNames | ConvertTo-Json | Set-Content "$(Build.StagingDirectory)\unsigned-package-names.json" + + $shortHash = "$(Build.SourceVersion)".Substring(0, 7) + Write-Host "##vso[task.setvariable variable=BUILD_SOURCEVERSIONSHORT]$shortHash" + displayName: Normalize unsigned artifacts for Arcade + + - pwsh: | + $adapter = "$(Agent.TempDirectory)\android-arcade" + Remove-Item $adapter -Recurse -Force -ErrorAction Ignore + Copy-Item "$(Build.SourcesDirectory)\android\build-tools\automation\dnceng\arcade" $adapter -Recurse + Copy-Item "$(Build.SourcesDirectory)\android\eng\common" "$adapter\eng\common" -Recurse + + $globalJson = Get-Content "$(Build.SourcesDirectory)\android\global.json" -Raw | ConvertFrom-Json -AsHashtable + $globalJson["tools"] = [ordered]@{ + dotnet = (& dotnet --version) + } + $globalJson | ConvertTo-Json -Depth 5 | Set-Content "$adapter\global.json" + + Write-Host "##vso[task.setvariable variable=ArcadeAdapterRoot]$adapter" + displayName: Prepare isolated Arcade adapter + + - pwsh: | + $properties = @( + "/p:RepositoryEngineeringDir=$(ArcadeAdapterRoot)\eng\", + "/p:AndroidRepoRoot=$(Build.SourcesDirectory)\android", + "/p:AndroidSignList=$(AndroidSignList)", + "/p:ArtifactsDir=$(Build.SourcesDirectory)\android\artifacts\", + "/p:NuGetPackageRoot=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestorePackagesPath=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", + "/p:OfficialBuildId=$(Build.BuildNumber)", + "/p:DotNetSignType=$(_SignType)", + "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", + "/p:RepositoryName=dotnet/android" + ) + + & "$(ArcadeAdapterRoot)\eng\common\build.ps1" ` + -configuration Release ` + -msbuildEngine dotnet ` + -projects "$(ArcadeAdapterRoot)\Workloads.proj" ` + -restore ` + -sign ` + -ci ` + -warnAsError:$false ` + -excludeCIBinarylog ` + @properties + displayName: Sign prebuilt NuGet packages with Arcade + env: + BUILD_REPOSITORY_NAME: dotnet/android + BUILD_REPOSITORY_URI: https://github.com/dotnet/android + NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - pwsh: | + $properties = @( + "/p:RepositoryEngineeringDir=$(ArcadeAdapterRoot)\eng\", + "/p:AndroidRepoRoot=$(Build.SourcesDirectory)\android", + "/p:AndroidSignList=$(AndroidSignList)", + "/p:ArtifactsDir=$(Build.SourcesDirectory)\android\artifacts\", + "/p:NuGetPackageRoot=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestorePackagesPath=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", + "/p:OfficialBuildId=$(Build.BuildNumber)", + "/p:DotNetSignType=$(_SignType)", + "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", + "/p:RepositoryName=dotnet/android" + ) + + & "$(ArcadeAdapterRoot)\eng\common\build.ps1" ` + -configuration Release ` + -msbuildEngine dotnet ` + -projects "$(ArcadeAdapterRoot)\Workloads.proj" ` + -build ` + -sign ` + -ci ` + -warnAsError:$false ` + @properties + displayName: Build and sign workload MSIs with Arcade + env: + BUILD_REPOSITORY_NAME: dotnet/android + BUILD_REPOSITORY_URI: https://github.com/dotnet/android + NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - pwsh: | + $shipping = "$(Build.SourcesDirectory)\android\artifacts\packages\Release\Shipping" + $inputNames = @(Get-Content "$(Build.StagingDirectory)\unsigned-package-names.json" -Raw | ConvertFrom-Json) + foreach ($name in $inputNames) { + if (-not (Test-Path (Join-Path $shipping $name) -PathType Leaf)) { + throw "Signed output is missing '$name'." + } + } + + $msiPackages = @(Get-ChildItem $shipping -Filter "*.nupkg" -File | + Where-Object { $_.Name -notin $inputNames }) + if ($msiPackages.Count -eq 0) { + throw "The workload build did not generate any MSI NuGet packages." + } + + $msiDirectory = "$(Build.StagingDirectory)\msi-nupkgs" + New-Item $msiDirectory -ItemType Directory -Force | Out-Null + Copy-Item $msiPackages.FullName $msiDirectory + displayName: Validate signed package outputs + + - task: MicroBuildCodesignVerify@3 + displayName: Verify signed workload artifacts + inputs: + TargetFolders: | + $(Build.SourcesDirectory)\android\artifacts\packages\Release\Shipping\manifests + $(Build.SourcesDirectory)\android\artifacts\packages\Release\Shipping\manifests-packs + $(Build.StagingDirectory)\msi-nupkgs + ExcludeSNVerify: true + ApprovalListPathForCerts: $(Build.StagingDirectory)\SignVerifyIgnore.txt + + - task: PublishPipelineArtifact@1 + displayName: Publish signed NuGet packages + inputs: + artifactName: nuget-signed + targetPath: $(Build.SourcesDirectory)\android\artifacts\packages\Release\Shipping + + - task: PublishPipelineArtifact@1 + displayName: Publish signing logs + condition: succeededOrFailed() + inputs: + artifactName: signing-logs-$(System.StageAttempt)-$(System.JobAttempt) + targetPath: $(Build.SourcesDirectory)\android\artifacts\log\Release + # MSBuild Tests Stage - stage: msbuild_dotnet displayName: MSBuild Tests diff --git a/build-tools/automation/dnceng/arcade/Workloads.proj b/build-tools/automation/dnceng/arcade/Workloads.proj new file mode 100644 index 00000000000..18686403fcb --- /dev/null +++ b/build-tools/automation/dnceng/arcade/Workloads.proj @@ -0,0 +1,209 @@ + + + false + false + $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', '..', '..', '..', '..')) + + + + + + netstandard2.0 + false + false + <_ArtifactsDir>$([MSBuild]::EnsureTrailingSlash('$(ArtifactsDir)')) + $(_ArtifactsDir)packages\Release\ + $(ArtifactsPackagesDir)Shipping\ + $(_ArtifactsDir)log\Release\ + $(ArtifactsLogDir)workload-msi + $(_ArtifactsDir)workload-msi\ + $(MSBuildThisFileDirectory)vsman\Workload.vsmanproj + $(ArtifactsShippingPackagesDir)\manifests + $(ArtifactsShippingPackagesDir)\manifests-pre + $(ArtifactsShippingPackagesDir)\manifests-packs + $(ArtifactsShippingPackagesDir) + $(ArtifactsShippingPackagesDir) + $(ArtifactsShippingPackagesDir)\vs-workload.props + 11.0.0-beta.26325.102 + 1.1.922 + 6.0.3-dotnet.4 + 6.0.3-dotnet.4 + 6.0.3-dotnet.4 + 3.1.7 + net$(BundledNETCoreAppTargetFrameworkVersion) + $(BUILD_BUILDID) + $(BUILD_SOURCEVERSIONSHORT)/$(BUILD_BUILDID) + $(PkgMicrosoft_VisualStudioEng_MicroBuild_Plugins_SwixBuild)\build\Microsoft.VisualStudioEng.MicroBuild.Plugins.SwixBuild.targets + $(PkgMicrosoft_Wix)\tools\net6.0\any\wix.exe + $(PkgMicrosoft_WixToolset_Heat)\tools\net472\x64\heat.exe + wixext6 + + + + + + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_PreviewMatch>$([System.Text.RegularExpressions.Regex]::Match(%(WorkloadPackages.Identity), `\-(preview|rc|ci.+).\d+`)) + <_NuGetVersion>$([System.Version]::Parse('%(WorkloadPackages.Version)').Major).$([System.Version]::Parse('%(WorkloadPackages.Version)').Minor).$([System.Version]::Parse('%(WorkloadPackages.Version)').Build) + + + %(WorkloadPackages._NuGetVersion) + + + + + + + 5 + 1996-04-01 + + + + + + + + + + + $(_GeneratedMsiVersion) + + + + <_GeneratedMsiVersion /> + + + + + + <_VSWherePath>$([MSBuild]::NormalizePath('$(Pkgvswhere)', 'tools', 'vswhere.exe')) + + + + + + <_DesktopMSBuildPath>$(_VSInstallDirectory)\MSBuild\Current\Bin\MSBuild.exe + + + + + + + + + + + + + + + Microsoft400 + + + + + + + + <_PackProjects Include="@(_SwixProjects)" Condition="'%(PackageType)' == 'msi-pack'" + ManifestOutputPath="$(PackManifestOutputPath)" + LogGroup="packs" + ZipFile="Workload.VSDrop.$(TargetName).packs.zip" /> + <_ComponentProjects Include="@(_SwixProjects)" Condition="'%(PackageType)' == 'msi-manifest' or '%(PackageType)' == 'manifest-package-group' or ('%(PackageType)' == 'component' and '%(IsPreview)' != 'true')" + ManifestOutputPath="$(ManifestOutputPath)" + LogGroup="components" + ZipFile="Workload.VSDrop.$(TargetName).components.zip" /> + <_PreviewComponentProjects Include="@(_SwixProjects)" Condition="'%(PackageType)' == 'msi-manifest' or '%(PackageType)' == 'manifest-package-group' or ('%(PackageType)' == 'component' and '%(IsPreview)' == 'true')" + ManifestOutputPath="$(PreviewManifestOutputPath)" + LogGroup="components-pre" + ZipFile="Workload.VSDrop.$(TargetName)-pre.components.zip" /> + <_InsertionProjects Include="@(_PackProjects);@(_ComponentProjects);@(_PreviewComponentProjects)" /> + + + + <_Drop Include="%(_InsertionProjects.ZipFile)" SourceDirectory="%(ManifestOutputPath)" /> + <_DropMetadata Include="$(ManifestBuildVersion);$(BUILD_REPOSITORY_NAME);$(BUILD_SOURCEBRANCH)" /> + + + + + <_VsmanArguments>"$(_DesktopMSBuildPath)" "$(VsmanProject)" /nologo /v:normal /nr:false /t:GenerateSetupManifest /p:SwixBuildTargets="$(SwixTargetsPath)" /p:WorkloadMsiGenProps="$(WorkloadMsiGenProps)" + + + + + <_PreviewManifests Include="$(PreviewManifestOutputPath)\*" /> + + + + + + + + + _GenerateAndSignMsis;AfterBuild + + diff --git a/build-tools/automation/dnceng/arcade/eng/Signing.props b/build-tools/automation/dnceng/arcade/eng/Signing.props new file mode 100644 index 00000000000..502d8b4499e --- /dev/null +++ b/build-tools/automation/dnceng/arcade/eng/Signing.props @@ -0,0 +1,26 @@ + + + false + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj b/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj new file mode 100644 index 00000000000..c934678358a --- /dev/null +++ b/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj @@ -0,0 +1,83 @@ + + + + netstandard2.0 + false + false + false + build-manifest + true + false + DotNetOptionalWorkloads + vs + 42.42.42 + true + $(ManifestOutputPath) + + + + + + $(TargetName).$(ManifestNameSuffix) + $(TargetName) + $(SYSTEM_TEAMPROJECT) + $(BUILD_REPOSITORY_NAME) + $(BUILD_SOURCEBRANCHNAME) + $(BUILD_BUILDID) + Products/$(ManifestTeamProject)/$(ManifestRepositoryName)/$(ManifestBuildBranch) + $(ManifestDropNameRoot)/$(ManifestBuildNumber) + $(ManifestDropNameRoot)/$(ManifestNameSuffix)/$(ManifestBuildNumber) + https://vsdrop.corp.microsoft.com/file/v1/$(ManifestDropName); + $(OutputPath)\obj\$(MSBuildProject) + + + + + / + + + /CHS/ + + + /CHT/ + + + /CSY/ + + + /DEU/ + + + /ENU/ + + + /ESN/ + + + /FRA/ + + + /ITA/ + + + /JPN/ + + + /KOR/ + + + /PLK/ + + + /PTB/ + + + /RUS/ + + + /TRK/ + + + + + diff --git a/build-tools/automation/dnceng/arcade/vsman/global.json b/build-tools/automation/dnceng/arcade/vsman/global.json new file mode 100644 index 00000000000..ae9f38c48f8 --- /dev/null +++ b/build-tools/automation/dnceng/arcade/vsman/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.400", + "rollForward": "latestFeature" + } +} From 90fdbc3d6b82d17ce4feef4663fa26e20c8947ff Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 12:57:21 -0500 Subject: [PATCH 02/14] [ci] Use root NuGet config for Arcade bootstrap Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/azure-pipelines-internal.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 68468094fa4..18947d65852 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -428,6 +428,7 @@ stages: env: BUILD_REPOSITORY_NAME: dotnet/android BUILD_REPOSITORY_URI: https://github.com/dotnet/android + NUGET_CONFIG: $(Build.SourcesDirectory)\android\NuGet.config NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages SYSTEM_ACCESSTOKEN: $(System.AccessToken) @@ -459,6 +460,7 @@ stages: env: BUILD_REPOSITORY_NAME: dotnet/android BUILD_REPOSITORY_URI: https://github.com/dotnet/android + NUGET_CONFIG: $(Build.SourcesDirectory)\android\NuGet.config NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages SYSTEM_ACCESSTOKEN: $(System.AccessToken) From e3bff6c49ec4e77f6016e2dff854274d24bc579c Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 14:26:54 -0500 Subject: [PATCH 03/14] [ci] Use .NET 11 for Arcade signing Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/azure-pipelines-internal.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 18947d65852..dd4b9b83965 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -340,7 +340,7 @@ stages: - task: UseDotNet@2 displayName: Install .NET SDK for Arcade signing inputs: - version: $(DotNetSdkVersion).x + version: 11.0.x includePreviewVersions: true - pwsh: | From d9fcddadc77a60188d8c8bf8c39e6855171d0b51 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 14:29:11 -0500 Subject: [PATCH 04/14] [ci] Name the Arcade .NET SDK version Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/azure-pipelines-internal.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index dd4b9b83965..618d6557881 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -55,6 +55,8 @@ variables: value: test - name: _BuildConfig value: Release +- name: ArcadeDotNetSdkVersion + value: 11.0 - name: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE value: 'true' - name: DOTNET_SDK_VULNERABILITY_CHECK_DISABLE @@ -340,7 +342,7 @@ stages: - task: UseDotNet@2 displayName: Install .NET SDK for Arcade signing inputs: - version: 11.0.x + version: $(ArcadeDotNetSdkVersion).x includePreviewVersions: true - pwsh: | From 5521756ce4d8ad85ee2f120315beee58900e52de Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 15:35:06 -0500 Subject: [PATCH 05/14] [ci] Archive Arcade signing binlogs Use the exact tracked .NET SDK for the Arcade toolset and publish a distinct binary log for each signing invocation, including failed jobs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 618d6557881..2e45d2a1326 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -56,7 +56,7 @@ variables: - name: _BuildConfig value: Release - name: ArcadeDotNetSdkVersion - value: 11.0 + value: 11.0.100-preview.7.26355.102 - name: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE value: 'true' - name: DOTNET_SDK_VULNERABILITY_CHECK_DISABLE @@ -342,7 +342,7 @@ stages: - task: UseDotNet@2 displayName: Install .NET SDK for Arcade signing inputs: - version: $(ArcadeDotNetSdkVersion).x + version: $(ArcadeDotNetSdkVersion) includePreviewVersions: true - pwsh: | @@ -350,10 +350,12 @@ stages: $artifacts = "$(Build.SourcesDirectory)\android\artifacts" $shipping = Join-Path $artifacts "packages\Release\Shipping" $logs = Join-Path $artifacts "log\Release" + $signingLogs = "$(Build.StagingDirectory)\signing-logs" Remove-Item $artifacts -Recurse -Force -ErrorAction Ignore New-Item $shipping -ItemType Directory -Force | Out-Null New-Item $logs -ItemType Directory -Force | Out-Null + New-Item $signingLogs -ItemType Directory -Force | Out-Null $packages = @(Get-ChildItem $unsigned -Filter "*.nupkg" -File -Recurse) if ($packages.Count -eq 0) { @@ -424,7 +426,7 @@ stages: -sign ` -ci ` -warnAsError:$false ` - -excludeCIBinarylog ` + -binaryLogName "$(Build.StagingDirectory)\signing-logs\sign-nugets.binlog" ` @properties displayName: Sign prebuilt NuGet packages with Arcade env: @@ -457,6 +459,7 @@ stages: -sign ` -ci ` -warnAsError:$false ` + -binaryLogName "$(Build.StagingDirectory)\signing-logs\workload-msis.binlog" ` @properties displayName: Build and sign workload MSIs with Arcade env: @@ -504,10 +507,10 @@ stages: - task: PublishPipelineArtifact@1 displayName: Publish signing logs - condition: succeededOrFailed() + condition: always() inputs: artifactName: signing-logs-$(System.StageAttempt)-$(System.JobAttempt) - targetPath: $(Build.SourcesDirectory)\android\artifacts\log\Release + targetPath: $(Build.StagingDirectory)\signing-logs # MSBuild Tests Stage - stage: msbuild_dotnet From 6acb814b561c32a248398b43133a8c6280c9b055 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 16:49:53 -0500 Subject: [PATCH 06/14] [ci] Install tracked SDK for Arcade signing Use eng/install-dotnet.ps1 so the signing job gets the exact Microsoft.NET.Sdk version from eng/Versions.props, including internal preview builds from ci.dot.net. Create the diagnostics directory before SDK installation so failed setup is still archivable. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 2e45d2a1326..f16ed70dae4 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -55,8 +55,6 @@ variables: value: test - name: _BuildConfig value: Release -- name: ArcadeDotNetSdkVersion - value: 11.0.100-preview.7.26355.102 - name: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE value: 'true' - name: DOTNET_SDK_VULNERABILITY_CHECK_DISABLE @@ -339,23 +337,27 @@ stages: inputs: version: 8.0.x - - task: UseDotNet@2 - displayName: Install .NET SDK for Arcade signing - inputs: - version: $(ArcadeDotNetSdkVersion) - includePreviewVersions: true + - pwsh: | + $signingLogs = "$(Build.StagingDirectory)\signing-logs" + New-Item $signingLogs -ItemType Directory -Force | Out-Null + + & "$(Build.SourcesDirectory)\android\eng\install-dotnet.ps1" -configuration Release + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + Write-Host "##vso[task.prependpath]$(Build.SourcesDirectory)\android\bin\Release\dotnet" + displayName: Install tracked .NET SDK for Arcade signing - pwsh: | $unsigned = "$(Build.StagingDirectory)\unsigned" $artifacts = "$(Build.SourcesDirectory)\android\artifacts" $shipping = Join-Path $artifacts "packages\Release\Shipping" $logs = Join-Path $artifacts "log\Release" - $signingLogs = "$(Build.StagingDirectory)\signing-logs" Remove-Item $artifacts -Recurse -Force -ErrorAction Ignore New-Item $shipping -ItemType Directory -Force | Out-Null New-Item $logs -ItemType Directory -Force | Out-Null - New-Item $signingLogs -ItemType Directory -Force | Out-Null $packages = @(Get-ChildItem $unsigned -Filter "*.nupkg" -File -Recurse) if ($packages.Count -eq 0) { From 251dae0f9cf5e5913ab07e6c449632c863afda7f Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 09:15:09 -0500 Subject: [PATCH 07/14] [ci] Use Arcade-compatible official build ID Generate the signing build ID as YYYYMMDD.Revision so Arcade version calculations receive numeric date components instead of the branch-based Azure build number. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/azure-pipelines-internal.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index f16ed70dae4..671fe462317 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -55,6 +55,8 @@ variables: value: test - name: _BuildConfig value: Release +- name: _BuildOfficialId + value: $[ format('{0}.{1}', format('{0:yyyyMMdd}', pipeline.startTime), counter(format('{0:yyyyMMdd}', pipeline.startTime), 1)) ] - name: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE value: 'true' - name: DOTNET_SDK_VULNERABILITY_CHECK_DISABLE @@ -414,7 +416,7 @@ stages: "/p:NuGetPackageRoot=$(Build.SourcesDirectory)\android\.packages\", "/p:RestorePackagesPath=$(Build.SourcesDirectory)\android\.packages\", "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", - "/p:OfficialBuildId=$(Build.BuildNumber)", + "/p:OfficialBuildId=$(_BuildOfficialId)", "/p:DotNetSignType=$(_SignType)", "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", "/p:RepositoryName=dotnet/android" @@ -447,7 +449,7 @@ stages: "/p:NuGetPackageRoot=$(Build.SourcesDirectory)\android\.packages\", "/p:RestorePackagesPath=$(Build.SourcesDirectory)\android\.packages\", "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", - "/p:OfficialBuildId=$(Build.BuildNumber)", + "/p:OfficialBuildId=$(_BuildOfficialId)", "/p:DotNetSignType=$(_SignType)", "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", "/p:RepositoryName=dotnet/android" From 66b4433b49b494d2ce8e5b0c8fbcc0bbe239bf25 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 09:16:20 -0500 Subject: [PATCH 08/14] [ci] Remove unnecessary signing SDK override Restore the pipeline's standard .NET SDK selection now that the binlog identified the malformed OfficialBuildId as the root cause. Keep signing binlog publication for diagnostics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 671fe462317..fd3325af0b1 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -339,27 +339,23 @@ stages: inputs: version: 8.0.x - - pwsh: | - $signingLogs = "$(Build.StagingDirectory)\signing-logs" - New-Item $signingLogs -ItemType Directory -Force | Out-Null - - & "$(Build.SourcesDirectory)\android\eng\install-dotnet.ps1" -configuration Release - if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE - } - - Write-Host "##vso[task.prependpath]$(Build.SourcesDirectory)\android\bin\Release\dotnet" - displayName: Install tracked .NET SDK for Arcade signing + - task: UseDotNet@2 + displayName: Install .NET SDK for Arcade signing + inputs: + version: $(DotNetSdkVersion).x + includePreviewVersions: true - pwsh: | $unsigned = "$(Build.StagingDirectory)\unsigned" $artifacts = "$(Build.SourcesDirectory)\android\artifacts" $shipping = Join-Path $artifacts "packages\Release\Shipping" $logs = Join-Path $artifacts "log\Release" + $signingLogs = "$(Build.StagingDirectory)\signing-logs" Remove-Item $artifacts -Recurse -Force -ErrorAction Ignore New-Item $shipping -ItemType Directory -Force | Out-Null New-Item $logs -ItemType Directory -Force | Out-Null + New-Item $signingLogs -ItemType Directory -Force | Out-Null $packages = @(Get-ChildItem $unsigned -Filter "*.nupkg" -File -Recurse) if ($packages.Count -eq 0) { From b98a6d07e1155fc645cea3afe74261fc15de2d85 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 11:10:18 -0500 Subject: [PATCH 09/14] [ci] Normalize legacy signing patterns for Arcade Preserve wildcard rules as literal MSBuild items and convert legacy macOS package paths to Arcade's filename-based signing mappings. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index fd3325af0b1..9b6fb7a5cb4 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -373,7 +373,19 @@ stages: if (-not $signList) { throw "The build did not publish SignList.xml." } - Write-Host "##vso[task.setvariable variable=AndroidSignList]$($signList.FullName)" + + [xml] $arcadeSignList = Get-Content $signList.FullName + foreach ($item in $arcadeSignList.SelectNodes("/Project/ItemGroup/*[@Include]")) { + $include = $item.GetAttribute("Include") + if ($item.LocalName -eq "MacDeveloperSign" -or $item.LocalName -eq "MacDeveloperSignHarden") { + $include = [IO.Path]::GetFileName($include.Replace("/", "\")) + } + $include = $include.Replace("%", "%25").Replace("*", "%2A").Replace("?", "%3F").Replace(";", "%3B") + $item.SetAttribute("Include", $include) + } + $arcadeSignListPath = "$(Build.StagingDirectory)\ArcadeSignList.xml" + $arcadeSignList.Save($arcadeSignListPath) + Write-Host "##vso[task.setvariable variable=AndroidSignList]$arcadeSignListPath" $signVerify = Get-ChildItem $unsigned -Filter "SignVerifyIgnore.txt" -File -Recurse | Select-Object -First 1 if (-not $signVerify) { From 36db3398f5139a6c984ae8fd7fe0b1a642f04cb7 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 12:34:52 -0500 Subject: [PATCH 10/14] [ci] Use absolute workload MSI intermediate path Prevent WiX source paths from being resolved relative to their own intermediate directory, which duplicated obj/Release and hid generated eula and icon files. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/dnceng/arcade/Workloads.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/build-tools/automation/dnceng/arcade/Workloads.proj b/build-tools/automation/dnceng/arcade/Workloads.proj index 18686403fcb..0bfd45730c2 100644 --- a/build-tools/automation/dnceng/arcade/Workloads.proj +++ b/build-tools/automation/dnceng/arcade/Workloads.proj @@ -16,6 +16,7 @@ $(ArtifactsPackagesDir)Shipping\ $(_ArtifactsDir)log\Release\ $(ArtifactsLogDir)workload-msi + $(_ArtifactsDir)obj\Release\ $(_ArtifactsDir)workload-msi\ $(MSBuildThisFileDirectory)vsman\Workload.vsmanproj $(ArtifactsShippingPackagesDir)\manifests From 34b94623d9553592449c5dc579973f26d647e599 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 13:56:08 -0500 Subject: [PATCH 11/14] [ci] Write MSI NuGets to shipping directory Generated MSI package projects inherit PackageOutputPath from the parent Workloads project. Override it alongside OutputPath so packaged MSIs are available to signing validation and publication. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/dnceng/arcade/Workloads.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/automation/dnceng/arcade/Workloads.proj b/build-tools/automation/dnceng/arcade/Workloads.proj index 0bfd45730c2..d64398216bc 100644 --- a/build-tools/automation/dnceng/arcade/Workloads.proj +++ b/build-tools/automation/dnceng/arcade/Workloads.proj @@ -167,7 +167,7 @@ + Properties="OutputPath=$(MsiNuGetOutputPath);PackageOutputPath=$(MsiNuGetOutputPath);BundledNETCoreAppTargetFramework=$(BundledNETCoreAppTargetFramework)" /> <_PackProjects Include="@(_SwixProjects)" Condition="'%(PackageType)' == 'msi-pack'" ManifestOutputPath="$(PackManifestOutputPath)" From 27834c20d19132f0abc29810e1e300d4f3801400 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 15:52:39 -0500 Subject: [PATCH 12/14] [ci] Package workload MSIs after signing Persist the generated MSI and Swix project metadata so Arcade's AfterSigning phase can package the signed MSI payloads and create insertion artifacts. Run a final signing pass for the resulting MSI NuGet packages. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 32 +++++++++++++++++++ .../automation/dnceng/arcade/Workloads.proj | 13 +++++++- .../dnceng/arcade/eng/AfterSigning.targets | 7 ++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 build-tools/automation/dnceng/arcade/eng/AfterSigning.targets diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 9b6fb7a5cb4..1c721915aa8 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -481,6 +481,38 @@ stages: NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages SYSTEM_ACCESSTOKEN: $(System.AccessToken) + - pwsh: | + $properties = @( + "/p:RepositoryEngineeringDir=$(ArcadeAdapterRoot)\eng\", + "/p:AndroidRepoRoot=$(Build.SourcesDirectory)\android", + "/p:AndroidSignList=$(AndroidSignList)", + "/p:ArtifactsDir=$(Build.SourcesDirectory)\android\artifacts\", + "/p:NuGetPackageRoot=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestorePackagesPath=$(Build.SourcesDirectory)\android\.packages\", + "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", + "/p:OfficialBuildId=$(_BuildOfficialId)", + "/p:DotNetSignType=$(_SignType)", + "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", + "/p:RepositoryName=dotnet/android" + ) + + & "$(ArcadeAdapterRoot)\eng\common\build.ps1" ` + -configuration Release ` + -msbuildEngine dotnet ` + -projects "$(ArcadeAdapterRoot)\Workloads.proj" ` + -sign ` + -ci ` + -warnAsError:$false ` + -binaryLogName "$(Build.StagingDirectory)\signing-logs\sign-msi-nugets.binlog" ` + @properties + displayName: Sign workload MSI NuGet packages with Arcade + env: + BUILD_REPOSITORY_NAME: dotnet/android + BUILD_REPOSITORY_URI: https://github.com/dotnet/android + NUGET_CONFIG: $(Build.SourcesDirectory)\android\NuGet.config + NUGET_PACKAGES: $(Build.SourcesDirectory)\android\.packages + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + - pwsh: | $shipping = "$(Build.SourcesDirectory)\android\artifacts\packages\Release\Shipping" $inputNames = @(Get-Content "$(Build.StagingDirectory)\unsigned-package-names.json" -Raw | ConvertFrom-Json) diff --git a/build-tools/automation/dnceng/arcade/Workloads.proj b/build-tools/automation/dnceng/arcade/Workloads.proj index d64398216bc..ccfe01c5672 100644 --- a/build-tools/automation/dnceng/arcade/Workloads.proj +++ b/build-tools/automation/dnceng/arcade/Workloads.proj @@ -18,6 +18,7 @@ $(ArtifactsLogDir)workload-msi $(_ArtifactsDir)obj\Release\ $(_ArtifactsDir)workload-msi\ + $(IntermediateOutputPath)WorkloadOutputs.props $(MSBuildThisFileDirectory)vsman\Workload.vsmanproj $(ArtifactsShippingPackagesDir)\manifests $(ArtifactsShippingPackagesDir)\manifests-pre @@ -41,6 +42,7 @@ + false @@ -160,10 +162,19 @@ Microsoft400 + <_WorkloadOutputLines Include="<Project>" /> + <_WorkloadOutputLines Include=" <ItemGroup>" /> + <_WorkloadOutputLines Include="@(_WorkloadMsis->' <_WorkloadMsis Include="%(Identity)" PackageProject="%(PackageProject)" />')" /> + <_WorkloadOutputLines Include="@(_SwixProjects->' <_SwixProjects Include="%(Identity)" PackageType="%(PackageType)" IsPreview="%(IsPreview)" />')" /> + <_WorkloadOutputLines Include=" </ItemGroup>" /> + <_WorkloadOutputLines Include="</Project>" /> + - + + + + + + + From f7c00a98b555669474449bc02226d82da884c443 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Jul 2026 16:58:56 -0500 Subject: [PATCH 13/14] [ci] Include workload MSIs in Arcade signing The workload project item list is not shared with Arcade Sign.proj. Add the raw MSI outputs directly to Signing.props during the workload build pass so insertion manifests contain signed installers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- build-tools/automation/azure-pipelines-internal.yaml | 1 + build-tools/automation/dnceng/arcade/Workloads.proj | 3 --- build-tools/automation/dnceng/arcade/eng/Signing.props | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 1c721915aa8..74515da0e58 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -459,6 +459,7 @@ stages: "/p:RestoreConfigFile=$(Build.SourcesDirectory)\android\NuGet.config", "/p:OfficialBuildId=$(_BuildOfficialId)", "/p:DotNetSignType=$(_SignType)", + "/p:SignWorkloadMsis=true", "/p:MicroBuildOverridePluginDirectory=$(Agent.TempDirectory)\MicroBuild\Plugins", "/p:RepositoryName=dotnet/android" ) diff --git a/build-tools/automation/dnceng/arcade/Workloads.proj b/build-tools/automation/dnceng/arcade/Workloads.proj index ccfe01c5672..f1502298e8a 100644 --- a/build-tools/automation/dnceng/arcade/Workloads.proj +++ b/build-tools/automation/dnceng/arcade/Workloads.proj @@ -159,9 +159,6 @@ - - Microsoft400 - <_WorkloadOutputLines Include="<Project>" /> <_WorkloadOutputLines Include=" <ItemGroup>" /> <_WorkloadOutputLines Include="@(_WorkloadMsis->' <_WorkloadMsis Include="%(Identity)" PackageProject="%(PackageProject)" />')" /> diff --git a/build-tools/automation/dnceng/arcade/eng/Signing.props b/build-tools/automation/dnceng/arcade/eng/Signing.props index 502d8b4499e..205700afe05 100644 --- a/build-tools/automation/dnceng/arcade/eng/Signing.props +++ b/build-tools/automation/dnceng/arcade/eng/Signing.props @@ -9,6 +9,7 @@ + From 95f9ca7eb84095fe51ac237b005177a2f1f9e39d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 17 Jul 2026 10:45:20 -0500 Subject: [PATCH 14/14] [ci] Expand Arcade signing patterns Resolve legacy SignList wildcards against the actual package contents because Arcade matches explicit signing rules by filename. Preserve Skip precedence and fail normalization if certificate assignments conflict. Use the reserved MSBuildProjectName property for VSMan intermediate output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 866271dd-9544-4263-912e-5cfe20e73dbc --- .../automation/azure-pipelines-internal.yaml | 63 +++++++++++++++++-- .../dnceng/arcade/vsman/Workload.vsmanproj | 2 +- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/build-tools/automation/azure-pipelines-internal.yaml b/build-tools/automation/azure-pipelines-internal.yaml index 74515da0e58..232b42f8dad 100644 --- a/build-tools/automation/azure-pipelines-internal.yaml +++ b/build-tools/automation/azure-pipelines-internal.yaml @@ -375,14 +375,65 @@ stages: } [xml] $arcadeSignList = Get-Content $signList.FullName - foreach ($item in $arcadeSignList.SelectNodes("/Project/ItemGroup/*[@Include]")) { - $include = $item.GetAttribute("Include") - if ($item.LocalName -eq "MacDeveloperSign" -or $item.LocalName -eq "MacDeveloperSignHarden") { - $include = [IO.Path]::GetFileName($include.Replace("/", "\")) + $packageEntries = foreach ($package in $packages) { + $archive = [IO.Compression.ZipFile]::OpenRead($package.FullName) + try { + foreach ($entry in $archive.Entries) { + [pscustomobject]@{ + FullName = $entry.FullName.Replace("\", "/") + Name = $entry.Name + } + } + } finally { + $archive.Dispose() } - $include = $include.Replace("%", "%25").Replace("*", "%2A").Replace("?", "%3F").Replace(";", "%3B") - $item.SetAttribute("Include", $include) } + + foreach ($item in @($arcadeSignList.SelectNodes("/Project/ItemGroup/*[@Include]"))) { + $include = $item.GetAttribute("Include").Replace("\", "/") + if ($include.Contains("*") -or $include.Contains("?")) { + $matchedEntries = if ($include.Contains("/")) { + @($packageEntries | Where-Object { $_.FullName -like $include -or $_.FullName -like "*/$include" }) + } else { + @($packageEntries | Where-Object { $_.Name -like $include }) + } + $names = @($matchedEntries.Name | Where-Object { $_ } | Sort-Object -Unique) + if ($names.Count -eq 0) { + throw "Signing pattern '$include' did not match any package entries." + } + foreach ($name in $names) { + $expandedItem = $item.Clone() + $escapedName = $name.Replace("%", "%25").Replace(";", "%3B") + $expandedItem.SetAttribute("Include", $escapedName) + [void] $item.ParentNode.InsertBefore($expandedItem, $item) + } + [void] $item.ParentNode.RemoveChild($item) + } else { + if ($item.LocalName -eq "MacDeveloperSign" -or $item.LocalName -eq "MacDeveloperSignHarden") { + $include = [IO.Path]::GetFileName($include) + } + $include = $include.Replace("%", "%25").Replace(";", "%3B") + $item.SetAttribute("Include", $include) + } + } + + $skipNames = @($arcadeSignList.SelectNodes("/Project/ItemGroup/Skip") | ForEach-Object Include) + foreach ($item in @($arcadeSignList.SelectNodes("/Project/ItemGroup/MacDeveloperSign | /Project/ItemGroup/MacDeveloperSignHarden"))) { + if ($item.GetAttribute("Include") -in $skipNames) { + [void] $item.ParentNode.RemoveChild($item) + } + } + + $certificatesByName = @{} + foreach ($item in $arcadeSignList.SelectNodes("/Project/ItemGroup/ThirdParty | /Project/ItemGroup/ThirdPartyScript | /Project/ItemGroup/MacDeveloperSign | /Project/ItemGroup/MacDeveloperSignHarden")) { + $name = $item.GetAttribute("Include") + $certificateGroup = $item.LocalName + if ($certificatesByName.ContainsKey($name) -and $certificatesByName[$name] -ne $certificateGroup) { + throw "Signing entry '$name' is assigned to both '$($certificatesByName[$name])' and '$certificateGroup'." + } + $certificatesByName[$name] = $certificateGroup + } + $arcadeSignListPath = "$(Build.StagingDirectory)\ArcadeSignList.xml" $arcadeSignList.Save($arcadeSignListPath) Write-Host "##vso[task.setvariable variable=AndroidSignList]$arcadeSignListPath" diff --git a/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj b/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj index c934678358a..75a71f68aa4 100644 --- a/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj +++ b/build-tools/automation/dnceng/arcade/vsman/Workload.vsmanproj @@ -28,7 +28,7 @@ $(ManifestDropNameRoot)/$(ManifestBuildNumber) $(ManifestDropNameRoot)/$(ManifestNameSuffix)/$(ManifestBuildNumber) https://vsdrop.corp.microsoft.com/file/v1/$(ManifestDropName); - $(OutputPath)\obj\$(MSBuildProject) + $(OutputPath)\obj\$(MSBuildProjectName)