From dd478fca469865d5f76e45a31e699f3f13649806 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:42:26 -0700 Subject: [PATCH 001/100] [release/10.0] Make some rpm classes public (#16084) Co-authored-by: Nikola Milosavljevic Co-authored-by: Matt Mitchell --- azure-pipelines-pr.yml | 2 ++ src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs | 2 +- .../src/RpmHeaderEntryType.cs | 2 +- src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs | 2 +- src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index d3459826202..c95c6f81dd3 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -6,6 +6,7 @@ trigger: - release/6.0 - release/8.0 - release/9.0 + - release/10.0 exclude: - .github/* - Documentation/* @@ -23,6 +24,7 @@ pr: - release/6.0 - release/8.0 - release/9.0 + - release/10.0 - templates paths: include: diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs index 35afda693df..8bbdd7ff4dd 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs @@ -14,7 +14,7 @@ namespace Microsoft.DotNet.Build.Tasks.Installers { - internal sealed partial class RpmHeader(List.Entry> entries) + public sealed partial class RpmHeader(List.Entry> entries) where TEntryTag : struct, Enum { diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs index a51257b92eb..3b5558aa8a2 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs @@ -3,7 +3,7 @@ namespace Microsoft.DotNet.Build.Tasks.Installers { - internal enum RpmHeaderEntryType : uint + public enum RpmHeaderEntryType : uint { Null = 0, Char = 1, diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs index d91e2e930aa..6b4546c854f 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs @@ -13,7 +13,7 @@ namespace Microsoft.DotNet.Build.Tasks.Installers { [StructLayout(LayoutKind.Sequential)] - internal struct RpmLead + public struct RpmLead { public string Name { get; set; } public byte Major { get; set; } diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs index e10381eea13..21f28eab24a 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs @@ -12,7 +12,7 @@ namespace Microsoft.DotNet.Build.Tasks.Installers { - internal sealed class RpmPackage(RpmLead lead, RpmHeader signature, RpmHeader header, MemoryStream archiveStream) : IDisposable + public sealed class RpmPackage(RpmLead lead, RpmHeader signature, RpmHeader header, MemoryStream archiveStream) : IDisposable { public RpmLead Lead { get; set; } = lead; public RpmHeader Signature { get; set; } = signature; From 7d2c2bbb02718fb86e8536dd6c4098ee19def075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 28 Aug 2025 17:23:25 +0200 Subject: [PATCH 002/100] [release/10.0] Fix pipeline triggers for release/10.0 (#16090) --- azure-pipelines-codeql.yml | 4 +--- azure-pipelines-pr.yml | 10 ++-------- azure-pipelines.yml | 4 +--- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/azure-pipelines-codeql.yml b/azure-pipelines-codeql.yml index 75f9c91e35d..6e609a45668 100644 --- a/azure-pipelines-codeql.yml +++ b/azure-pipelines-codeql.yml @@ -33,9 +33,7 @@ schedules: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 + - release/*.0 always: true jobs: diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index c95c6f81dd3..5e79c3dda17 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -3,10 +3,7 @@ trigger: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 - - release/10.0 + - release/*.0 exclude: - .github/* - Documentation/* @@ -21,10 +18,7 @@ pr: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 - - release/10.0 + - release/*.0 - templates paths: include: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ff25f1e29f5..dcd2a68d67a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,9 +3,7 @@ trigger: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 + - release/*.0 paths: include: - '*' From f635c9414c4324709ac6d8b823f6979abb94b9ca Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:47:42 +0000 Subject: [PATCH 003/100] [release/10.0] Source code updates from dotnet/dotnet (#16096) Co-authored-by: dotnet-maestro[bot] --- Directory.Build.targets | 5 +++++ eng/Version.Details.props | 1 - eng/Version.Details.xml | 2 +- global.json | 4 ++-- .../tools/ProjectDefaults.targets | 5 +++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 99449926093..1b8d3519ede 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -4,4 +4,9 @@ + + + true + + diff --git a/eng/Version.Details.props b/eng/Version.Details.props index c6ec69cf34c..288def697e7 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -1,4 +1,3 @@ - + + + true + + $(__DeployProjectOutput) From a65f3a250b83a709789c7564394d737da67870b1 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 18:41:20 +0000 Subject: [PATCH 004/100] [release/10.0] Update dependencies from dotnet/arcade (#16097) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 288def697e7..68faffddc0d 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25422.3 - 10.0.0-beta.25422.3 + 10.0.0-beta.25428.2 + 10.0.0-beta.25428.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f9024a347fe..09111ef7288 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - f2cdf946c00a12a2c283835bb41ddc2255832055 + 7d2c2bbb02718fb86e8536dd6c4098ee19def075 - + https://github.com/dotnet/arcade - f2cdf946c00a12a2c283835bb41ddc2255832055 + 7d2c2bbb02718fb86e8536dd6c4098ee19def075 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 641ba6ca1dd..c4b80c604e2 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25420.111" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25422.3", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25422.3", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25428.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25428.2", "Microsoft.Build.NoTargets": "3.7.0" } } From a9acfdf10e78d3b382ece32f168bf84a772b9414 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 07:45:15 +0000 Subject: [PATCH 005/100] [release/10.0] Update dependencies from dotnet/arcade (#16098) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 68faffddc0d..9266ceae126 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25428.2 - 10.0.0-beta.25428.2 + 10.0.0-beta.25452.5 + 10.0.0-beta.25452.5 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 09111ef7288..c1f9497fdb3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 7d2c2bbb02718fb86e8536dd6c4098ee19def075 + a65f3a250b83a709789c7564394d737da67870b1 - + https://github.com/dotnet/arcade - 7d2c2bbb02718fb86e8536dd6c4098ee19def075 + a65f3a250b83a709789c7564394d737da67870b1 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index c4b80c604e2..cde4b814b48 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25420.111" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25428.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25428.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25452.5", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25452.5", "Microsoft.Build.NoTargets": "3.7.0" } } From c8317778dd29de13cb0f825cabf714ab74ce89b3 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 4 Sep 2025 15:41:01 +0200 Subject: [PATCH 006/100] Set RestoreEnablePackagePruning default in props (#16104) --- src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props | 3 +++ src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props index 409f25e07e2..e443c179b2a 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props @@ -20,6 +20,9 @@ Icon.png $(MSBuildThisFileDirectory)Assets\DotNetPackageIcon.png + + true + true diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets b/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets index 3ffd16a4d65..ff765c31141 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets @@ -1,9 +1,9 @@ - + - true + false From c4b67c674e7a36d2291cdb1763426641229de159 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:41:10 +0000 Subject: [PATCH 007/100] [release/10.0] Source code updates from dotnet/dotnet (#16109) [release/10.0] Source code updates from dotnet/dotnet --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c1f9497fdb3..073a5bee3f5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + From 80961dbf97d7cb0b114bbca60aef5bc91d43b322 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 7 Sep 2025 11:12:33 +0200 Subject: [PATCH 008/100] [release/10.0] Update dependencies from dotnet/arcade (#16107) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 9266ceae126..5f4ff99ba18 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25452.5 - 10.0.0-beta.25452.5 + 10.0.0-beta.25455.2 + 10.0.0-beta.25455.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 073a5bee3f5..1b439423f7a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - a65f3a250b83a709789c7564394d737da67870b1 + c4b67c674e7a36d2291cdb1763426641229de159 - + https://github.com/dotnet/arcade - a65f3a250b83a709789c7564394d737da67870b1 + c4b67c674e7a36d2291cdb1763426641229de159 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index cde4b814b48..f711efd05e7 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25420.111" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25452.5", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25452.5", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25455.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25455.2", "Microsoft.Build.NoTargets": "3.7.0" } } From 31e6bc11f42c4108d5f795d2b6ef39139aff8977 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 09:30:31 +0000 Subject: [PATCH 009/100] [release/10.0] Update dependencies from dotnet/arcade (#16112) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 5f4ff99ba18..2a2bbfff075 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25455.2 - 10.0.0-beta.25455.2 + 10.0.0-beta.25457.2 + 10.0.0-beta.25457.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1b439423f7a..f21c594dabf 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - c4b67c674e7a36d2291cdb1763426641229de159 + 80961dbf97d7cb0b114bbca60aef5bc91d43b322 - + https://github.com/dotnet/arcade - c4b67c674e7a36d2291cdb1763426641229de159 + 80961dbf97d7cb0b114bbca60aef5bc91d43b322 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index f711efd05e7..0805e94295e 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25420.111" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25455.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25455.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25457.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25457.2", "Microsoft.Build.NoTargets": "3.7.0" } } From cf230f8347490264e07be8b9969b26700cb2cf12 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 14:21:19 +0000 Subject: [PATCH 010/100] [release/10.0] Update dependencies from dotnet/arcade (#16115) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 2a2bbfff075..6650de662db 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25457.2 - 10.0.0-beta.25457.2 + 10.0.0-beta.25458.2 + 10.0.0-beta.25458.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f21c594dabf..11aa53cb695 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 80961dbf97d7cb0b114bbca60aef5bc91d43b322 + 31e6bc11f42c4108d5f795d2b6ef39139aff8977 - + https://github.com/dotnet/arcade - 80961dbf97d7cb0b114bbca60aef5bc91d43b322 + 31e6bc11f42c4108d5f795d2b6ef39139aff8977 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 0805e94295e..83bd19c5e67 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25420.111" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25457.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25457.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25458.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25458.2", "Microsoft.Build.NoTargets": "3.7.0" } } From fff7a817fd613de7856451a9b4719809090a7b0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:00:09 +0200 Subject: [PATCH 011/100] [release/10.0] Support internal feeds for .NET 10 (#16125) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/SetupNugetSources.ps1 | 2 +- eng/common/SetupNugetSources.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index 792b60b49d4..9445c314325 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -157,7 +157,7 @@ if ($dotnet31Source -ne $null) { AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password } -$dotnetVersions = @('5','6','7','8','9') +$dotnetVersions = @('5','6','7','8','9','10') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index facb415ca6f..ddf4efc81a4 100755 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -99,7 +99,7 @@ if [ "$?" == "0" ]; then PackageSources+=('dotnet3.1-internal-transport') fi -DotNetVersions=('5' '6' '7' '8' '9') +DotNetVersions=('5' '6' '7' '8' '9' '10') for DotNetVersion in ${DotNetVersions[@]} ; do FeedPrefix="dotnet${DotNetVersion}"; From de1b34774f484cfac863d27ce4f975069c8bacc3 Mon Sep 17 00:00:00 2001 From: Michael Yanni Date: Tue, 9 Sep 2025 15:37:21 -0700 Subject: [PATCH 012/100] Allows you to set the OfficialBuildId for the publish build assets job. --- eng/common/core-templates/job/publish-build-assets.yml | 4 +++- eng/common/core-templates/jobs/jobs.yml | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 348cd16376f..79b659fc843 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -40,6 +40,8 @@ parameters: repositoryAlias: self + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -124,7 +126,7 @@ jobs: /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=${{ parameters.officialBuildId }} condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index b637cb6e948..a4f2161be30 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -44,6 +44,7 @@ parameters: artifacts: {} is1ESPipeline: '' repositoryAlias: self + officialBuildId: '' # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -116,3 +117,7 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} + ${{ if ne(parameters.officialBuildId, '') }}: + officialBuildId: ${{ parameters.officialBuildId }} + ${{ else }}: + officialBuildId: $(Build.BuildNumber) From bffa62d357e5ee6c0c654cff6ca563bc6f0826c4 Mon Sep 17 00:00:00 2001 From: Michael Yanni Date: Tue, 9 Sep 2025 15:52:36 -0700 Subject: [PATCH 013/100] Default the OfficialBuildId variable on the publish build assets template. --- eng/common/core-templates/job/publish-build-assets.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 79b659fc843..37dff559fc1 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -64,6 +64,11 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml + - name: OfficialBuildId + ${{ if ne(parameters.officialBuildId, '') }}: + value: ${{ parameters.officialBuildId }} + ${{ else }}: + value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -126,7 +131,7 @@ jobs: /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=${{ parameters.officialBuildId }} + /p:OfficialBuildId=$(OfficialBuildId) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} From 8f32ab6a771356d8cab02ade0698fe41dff36a57 Mon Sep 17 00:00:00 2001 From: Michael Yanni Date: Thu, 11 Sep 2025 15:20:09 -0700 Subject: [PATCH 014/100] Removed redundant defaulting logic for officialBuildId. --- eng/common/core-templates/jobs/jobs.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index a4f2161be30..01ada747665 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -117,7 +117,4 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} - ${{ if ne(parameters.officialBuildId, '') }}: - officialBuildId: ${{ parameters.officialBuildId }} - ${{ else }}: - officialBuildId: $(Build.BuildNumber) + officialBuildId: ${{ parameters.officialBuildId }} From 2ee9fba87ff1d669ed2ca45e240577f509e72e6e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:40:57 +0000 Subject: [PATCH 015/100] [release/10.0] Source code updates from dotnet/dotnet (#16142) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- global.json | 4 +- .../src/CpioEntry.cs | 2 + .../SignToolTests.cs | 2 +- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 40 +++++++++++++++---- .../src/ZipDataEntry.cs | 2 + 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 11aa53cb695..87c3f23a781 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/global.json b/global.json index 83bd19c5e67..30338142de2 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100-rc.1.25420.111", + "version": "10.0.100-rc.1.25451.107", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.100-rc.1.25420.111" + "dotnet": "10.0.100-rc.1.25451.107" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25458.2", diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs index aed27483c65..19377abf9ee 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs @@ -22,6 +22,8 @@ internal sealed class CpioEntry(ulong inode, string name, ulong timestamp, ulong public const uint Directory = 0x4000; + public const uint FilePermissionMask = 0xFFF; + public ulong Inode { get; } = inode; public string Name { get; } = name; public ulong Timestamp { get; } = timestamp; diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 47bcb228381..aaab1f56e66 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -477,7 +477,7 @@ private void ValidateProducedRpmContent( string layout = Path.Combine(tempDir, "layout"); Directory.CreateDirectory(layout); - ZipData.ExtractRpmPayloadContents(rpmPackage, layout); + ZipData.ExtractRpmPayloadContents(log: null, rpmPackage, layout); // Checks: // Expected files are present diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 65681e36a19..2225300b2f6 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -636,7 +636,7 @@ internal static IEnumerable ReadDebContainerEntries(string archive { string relativePath = entry.Name; // lgtm [cs/zipslip] Archive from trusted source - // The relative path ocassionally ends with a '/', which is not a valid path given that the path is a file. + // The relative path occasionally ends with a '/', which is not a valid path given that the path is a file. // Remove the following workaround once https://github.com/dotnet/arcade/issues/15384 is resolved. if (relativePath.EndsWith("/")) { @@ -658,7 +658,10 @@ private static IEnumerable ReadRpmContainerEntries(string archiveP while (archive.GetNextEntry() is CpioEntry entry) { - yield return new ZipDataEntry(entry.Name, entry.DataStream); + yield return new ZipDataEntry(entry.Name, entry.DataStream) + { + UnixFileMode = entry.Mode & CpioEntry.FilePermissionMask, + }; } } @@ -669,7 +672,7 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) Directory.CreateDirectory(workingDir); string layout = Path.Combine(workingDir, "layout"); Directory.CreateDirectory(layout); - ExtractRpmPayloadContents(FileSignInfo.FullPath, layout); + ExtractRpmPayloadContents(log, FileSignInfo.FullPath, layout); // Update signed files in layout foreach (var signedPart in NestedParts.Values) @@ -680,10 +683,10 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) // Create payload.cpio string payload = Path.Combine(workingDir, "payload.cpio"); - RunExternalProcess("bash", $"-c \"find . -depth ! -wholename '.' -print | cpio -H newc -o --quiet > '{payload}'\"", out string _, layout); + RunExternalProcess(log, "bash", $"-c \"find . -depth ! -wholename '.' -print | cpio -H newc -o --quiet > '{payload}'\"", out string _, layout); // Collect file types for all files in layout - RunExternalProcess("bash", $"-c \"find . -depth ! -wholename '.' -exec file {{}} \\;\"", out string output, layout); + RunExternalProcess(log, "bash", $"-c \"find . -depth ! -wholename '.' -exec file {{}} \\;\"", out string output, layout); ITaskItem[] rawPayloadFileKinds = output.Split('\n', StringSplitOptions.RemoveEmptyEntries) .Select(t => new TaskItem(t)) @@ -744,7 +747,7 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) return RpmPackage.Read(stream).Header.Entries; } - internal static void ExtractRpmPayloadContents(string rpmPackage, string layout) + internal static void ExtractRpmPayloadContents(TaskLoggingHelper log, string rpmPackage, string layout) { foreach (var entry in ReadRpmContainerEntries(rpmPackage)) { @@ -754,18 +757,28 @@ internal static void ExtractRpmPayloadContents(string rpmPackage, string layout) if (entry != null) { entry.WriteToFile(outputPath); + + // Set file mode if not the default. + if (entry.UnixFileMode is { } mode and not /* 0644 */ 420) + { + RunExternalProcess(log, "bash", $""" + -c "chmod {Convert.ToString(mode, 8)} '{outputPath}'" + """, out string _, layout); + } } } } - private static bool RunExternalProcess(string cmd, string args, out string output, string workingDir = null) + private static bool RunExternalProcess(TaskLoggingHelper log, string cmd, string args, out string output, string workingDir = null) { + log?.LogMessage(MessageImportance.Low, $"Running command: '{cmd}' {args}"); + ProcessStartInfo psi = new() { FileName = cmd, Arguments = args, RedirectStandardOutput = true, - RedirectStandardError = false, + RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = workingDir @@ -775,6 +788,17 @@ private static bool RunExternalProcess(string cmd, string args, out string outpu output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); + string stderr = process.StandardError.ReadToEnd(); + if (!string.IsNullOrWhiteSpace(stderr)) + { + log?.LogMessage(MessageImportance.Low, $" Stderr: {stderr}"); + } + + if (process.ExitCode != 0) + { + log?.LogMessage(MessageImportance.Low, $" Exit code: {process.ExitCode}"); + } + return process.ExitCode == 0; } #endif diff --git a/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs b/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs index e2d34d86cc1..0bdac8c92e2 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs @@ -66,6 +66,8 @@ public ZipDataEntry(ZipArchiveEntry entry) public ImmutableArray ContentHash => _contentHash; + public uint? UnixFileMode { get; set; } + public void WriteToFile(string path) { using var fs = File.Create(path); From d81f58dba415698c95fb3e51a5a669dc3fc38c04 Mon Sep 17 00:00:00 2001 From: Ella Hathaway <67609881+ellahathaway@users.noreply.github.com> Date: Wed, 17 Sep 2025 08:19:14 -0700 Subject: [PATCH 016/100] [release/10.0] Strong name sign `Microsoft.DotNet.Build.Tasks.Feed.dll` (#16136) --- .../Microsoft.DotNet.Build.Tasks.Feed.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj b/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj index 73ef77e49af..f3cb15a12eb 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj @@ -11,6 +11,7 @@ + true $(DefineConstants);DOTNET_BUILD_SOURCE_ONLY From 6561b93066c963a7472c77b5f5094038126c5849 Mon Sep 17 00:00:00 2001 From: Pranav Senthilnathan Date: Thu, 18 Sep 2025 16:27:35 -0700 Subject: [PATCH 017/100] [release/10.0] Always use x86 burn for bundle (#16150) (#16151) --- .../build/wix5/wix.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets index 4090a151d05..fa3fba60899 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets @@ -23,7 +23,8 @@ $(IntermediateOutputPath)/wix/ $(IntermediateOutputPath)/$(InstallerRuntimeIdentifier)/wix/ - $(InstallerTargetArchitecture) + x86 + $(InstallerTargetArchitecture) Date: Thu, 18 Sep 2025 17:48:41 -0700 Subject: [PATCH 018/100] [release/10.0] Add back license for MSI installers (#16093) (#16153) --- .../build/wix5/eula.rtf | 97 +++++++++++++++++++ .../build/wix5/product/product.common.wxi | 2 + .../build/wix5/wix.targets | 1 + 3 files changed, 100 insertions(+) create mode 100644 src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf new file mode 100644 index 00000000000..7f40e11a8ab --- /dev/null +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf @@ -0,0 +1,97 @@ +{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Tahoma;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\froman\fprq2\fcharset0 Times New Roman;}{\f3\fswiss\fprq2\fcharset0 Calibri;}} +{\colortbl ;\red0\green0\blue0;\red0\green0\blue255;} +{\*\generator Riched20 10.0.10586}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1 +\pard\widctlpar\sb120\sa120\cf1\b\f0\fs24 MICROSOFT SOFTWARE LICENSE TERMS\fs28\par +\fs24 MICROSOFT .NET LIBRARY\fs28\par +\fs19 These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\b0\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 updates,\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 supplements,\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 Internet-based services, and\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 support services\par + +\pard\widctlpar\sb120\sa120\b for this software, unless other terms accompany those items. If so, those terms apply.\par +BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.\par +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE PERPETUAL RIGHTS BELOW.\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\fs20 1.\b0\f2\fs14\~\~\~\~\b\f0\fs19 INSTALLATION AND USE RIGHTS.\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\kerning0\fs20 a.\b0\f2\fs14\~\~\~\~\b\f0\fs19 Installation and Use.\b0\fs20\~You may install and use any number of copies of the software to design, develop and test your programs.\b\fs19\par +\fs20 b.\b0\f2\fs14\~\~\~\~\b\f0\fs19 Third Party Programs.\b0\fs20\~The software may include third party programs that Microsoft, not the third party, licenses to you under this agreement. Notices, if any, for the third party program are included for your information only.\b\fs19\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\fs20 2.\b0\f2\fs14\~\~\~\~\b\f0\fs19 DATA.\~\kerning0\b0\fs20 The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to improve our products and services.\~You can learn more about data collection and use in the help documentation and the privacy statement at\~{\cf0\f3\fs24{\field{\*\fldinst{HYPERLINK "http://go.microsoft.com/fwlink/?LinkId=528096&clcid=0x409"}}{\fldrslt{\ul\cf2\cf2\ul\f0\fs20 http://go.microsoft.com/fwlink/?LinkId=528096}}}}\f0\fs20 . Your use of the software operates as your consent to these practices.\kerning36\b\fs19\par +\fs20 3.\b0\f2\fs14\~\~\~\~\b\f0\fs20 ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.\fs19\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\kerning0\fs20 a.\b0\f2\fs14\~\~\~\~\b\f0\fs20 DISTRIBUTABLE CODE.\~\~\b0 The software is comprised of Distributable Code. \ldblquote Distributable Code\rdblquote is code that you are permitted to distribute in programs you develop if you comply with the terms below.\b\fs19\par + +\pard\widctlpar\fi-357\li1077\sb120\sa120\fs20 i.\b0\f2\fs14\~\~\~\~\~\~\b\f0\fs20 Right to Use and Distribute.\b0\fs19\par + +\pard\widctlpar\fi-357\li1434\sb120\sa120\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 You may copy and distribute the object code form of the software.\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs.\fs19\par + +\pard\widctlpar\fi-357\li1077\sb120\sa120\b\fs20 ii.\b0\f2\fs14\~\~\~\~\b\f0\fs20 Distribution Requirements.\b0\~\b For any Distributable Code you distribute, you must\b0\fs19\par + +\pard\widctlpar\fi-357\li1434\sb120\sa120\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 add significant primary functionality to it in your programs;\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 require distributors and external end users to agree to terms that protect it at least as much as this agreement;\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 display your valid copyright notice on your programs; and\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 indemnify, defend, and hold harmless Microsoft from any claims, including attorneys\rquote fees, related to the distribution or use of your programs.\fs19\par + +\pard\widctlpar\fi-357\li1077\sb120\sa120\b\fs20 iii.\b0\f2\fs14\~\~\~\b\f0\fs20 Distribution Restrictions.\b0\~\b You may not\b0\fs19\par + +\pard\widctlpar\fi-357\li1434\sb120\sa120\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 alter any copyright, trademark or patent notice in the Distributable Code;\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 use Microsoft\rquote s trademarks in your programs\rquote names or in a way that suggests your programs come from or are endorsed by Microsoft;\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 include Distributable Code in malicious, deceptive or unlawful programs; or\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that\fs19\par + +\pard\widctlpar\fi-358\li1792\sb120\sa120\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 the code be disclosed or distributed in source code form; or\fs19\par +\f1\fs20\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs20 others have the right to modify it.\fs19\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\b\fs20 4.\b0\f2\fs14\~\~\~\~\b\f0\fs19 SCOPE OF LICENSE.\~\b0 The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not\b\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\kerning0\b0\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 work around any technical limitations in the software;\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 publish the software for others to copy;\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 rent, lease or lend the software;\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 transfer the software or this agreement to any third party; or\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 use the software for commercial software hosting services.\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\b\fs20 5.\b0\f2\fs14\~\~\~\~\b\f0\fs19 BACKUP COPY.\~\b0 You may make one backup copy of the software. You may use it only to reinstall the software.\b\par +\fs20 6.\b0\f2\fs14\~\~\~\~\b\f0\fs19 DOCUMENTATION.\~\b0 Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.\b\par +\fs20 7.\b0\f2\fs14\~\~\~\~\b\f0\fs19 EXPORT RESTRICTIONS.\~\b0 The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see\~{\cf0\fs20{\field{\*\fldinst{HYPERLINK www.microsoft.com/exporting }}{\fldrslt{www.microsoft.com/exporting\ul0\cf0}}}}\f0\fs19 .\b\par +\fs20 8.\b0\f2\fs14\~\~\~\~\b\f0\fs19 SUPPORT SERVICES.\~\b0 Because this software is \ldblquote as is,\rdblquote we may not provide support services for it.\b\par +\fs20 9.\b0\f2\fs14\~\~\~\~\b\f0\fs19 ENTIRE AGREEMENT.\~\b0 This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.\b\par +\fs20 10.\b0\f2\fs14\~\~\~\b\f0\fs19 APPLICABLE LAW.\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\kerning0\fs20 a.\b0\f2\fs14\~\~\~\~\b\f0\fs19 United States.\~\b0 If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.\b\par +\fs20 b.\b0\f2\fs14\~\~\~\~\b\f0\fs19 Outside the United States. If you acquired the software in any other country, the laws of that country apply.\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\fs20 11.\b0\f2\fs14\~\~\b\f0\fs19 LEGAL EFFECT.\~\b0 This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.\b\par +\fs20 12.\b0\f2\fs14\~\~\b\f0\fs19 DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED \ldblquote AS-IS.\rdblquote YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY HAVE ADDITIONAL CONSUMER RIGHTS OR STATUTORY GUARANTEES UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.\par + +\pard\widctlpar\li357\sb120\sa120\kerning0 FOR AUSTRALIA \endash YOU HAVE STATUTORY GUARANTEES UNDER THE AUSTRALIAN CONSUMER LAW AND NOTHING IN THESE TERMS IS INTENDED TO AFFECT THOSE RIGHTS.\b0\par + +\pard\widctlpar\fi-357\li357\sb120\sa120\kerning36\b\fs20 13.\b0\f2\fs14\~\~\b\f0\fs19 LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.\par + +\pard\widctlpar\li357\sb120\sa120\kerning0\b0 This limitation applies to\par + +\pard\widctlpar\fi-363\li720\sb120\sa120\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and\par +\f1\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.\par + +\pard\widctlpar\sb120\sa120 It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.\par +\lang9 Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.\lang1033\par +\lang9 Remarque : Ce logiciel \'e9tant distribu\'e9 au Qu\'e9bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran\'e7ais.\lang1033\par +\kerning36\b EXON\'c9RATION DE GARANTIE.\~\b0 Le logiciel vis\'e9 par une licence est offert \'ab tel quel \'bb. Toute utilisation de ce logiciel est \'e0 votre seule risque et p\'e9ril. Microsoft n\rquote accorde aucune autre garantie expresse. Vous pouvez b\'e9n\'e9ficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualit\'e9 marchande, d\rquote ad\'e9quation \'e0 un usage particulier et d\rquote absence de contrefa\'e7on sont exclues.\b\par +LIMITATION DES DOMMAGES-INT\'c9R\'caTS ET EXCLUSION DE RESPONSABILIT\'c9 POUR LES DOMMAGES.\~\b0 Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement \'e0 hauteur de 5,00 $ US. Vous ne pouvez pr\'e9tendre \'e0 aucune indemnisation pour les autres dommages, y compris les dommages sp\'e9ciaux, indirects ou accessoires et pertes de b\'e9n\'e9fices.\b\par +\kerning0\b0\lang9 Cette limitation concerne :\lang1033\par + +\pard\widctlpar\li720\sb120\f1\lang9\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 tout ce qui est reli\'e9 au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et\lang1033\par + +\pard\widctlpar\li720\sa120\f1\lang9\'b7\f2\fs14\~\~\~\~\~\~\~\~\~\f0\fs19 les r\'e9clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit\'e9 stricte, de n\'e9gligence ou d\rquote une autre faute dans la limite autoris\'e9e par la loi en vigueur.\lang1033\par + +\pard\widctlpar\sb120\sa120\lang9 Elle s\rquote applique \'e9galement, m\'eame si Microsoft connaissait ou devrait conna\'eetre l\rquote\'e9ventualit\'e9 d\rquote un tel dommage. Si votre pays n\rquote autorise pas l\rquote exclusion ou la limitation de responsabilit\'e9 pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l\rquote exclusion ci-dessus ne s\rquote appliquera pas \'e0 votre \'e9gard.\lang1033\par +\kerning36\b EFFET JURIDIQUE.\~\b0 Le pr\'e9sent contrat d\'e9crit certains droits juridiques. Vous pourriez avoir d\rquote autres droits pr\'e9vus par les lois de votre pays. Le pr\'e9sent contrat ne modifie pas les droits que vous conf\'e8rent les lois de votre pays si celles-ci ne le permettent pas.\b\par +\kerning0\fs20\lang1036\~\fs19\lang1033\par + +\pard\widctlpar\cf0\b0\f3\fs24\par +} + \ No newline at end of file diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi index 52e8236b76c..24a6a6c75a5 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi @@ -25,6 +25,8 @@ + + diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets index fa3fba60899..804d40c7a96 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets @@ -261,6 +261,7 @@ + From 6275af47ebda0d394d4a5a401b77bc6f2304204a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:35:45 +0200 Subject: [PATCH 019/100] [release/10.0] Update dependencies from dotnet/arcade (#16123) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 6650de662db..1c746ea6426 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25458.2 - 10.0.0-beta.25458.2 + 10.0.0-beta.25468.6 + 10.0.0-beta.25468.6 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 87c3f23a781..93603431c8f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 31e6bc11f42c4108d5f795d2b6ef39139aff8977 + 6561b93066c963a7472c77b5f5094038126c5849 - + https://github.com/dotnet/arcade - 31e6bc11f42c4108d5f795d2b6ef39139aff8977 + 6561b93066c963a7472c77b5f5094038126c5849 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 30338142de2..10a060223b3 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25451.107" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25458.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25458.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25468.6", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25468.6", "Microsoft.Build.NoTargets": "3.7.0" } } From 81a615db11811f3b098d4aa3e897b0ab1206d926 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 25 Sep 2025 07:46:20 -0700 Subject: [PATCH 020/100] Use dotnet-public feed for downloading NuGet.VerifyMicrosoftPackage (#16154) --- eng/common/post-build/nuget-verification.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/post-build/nuget-verification.ps1 b/eng/common/post-build/nuget-verification.ps1 index a365194a938..ac5c69ffcac 100644 --- a/eng/common/post-build/nuget-verification.ps1 +++ b/eng/common/post-build/nuget-verification.ps1 @@ -30,7 +30,7 @@ [CmdletBinding(PositionalBinding = $false)] param( [string]$NuGetExePath, - [string]$PackageSource = "https://api.nuget.org/v3/index.json", + [string]$PackageSource = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json", [string]$DownloadPath, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$args From a4c9a07d978c070ef5c19d2ec9f811d6a5b20914 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:44:50 +0000 Subject: [PATCH 021/100] [release/10.0] Update dependencies from dotnet/arcade (#16155) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 1c746ea6426..1281b4a0828 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25468.6 - 10.0.0-beta.25468.6 + 10.0.0-beta.25469.2 + 10.0.0-beta.25469.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 93603431c8f..74b986276ba 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 6561b93066c963a7472c77b5f5094038126c5849 + 6275af47ebda0d394d4a5a401b77bc6f2304204a - + https://github.com/dotnet/arcade - 6561b93066c963a7472c77b5f5094038126c5849 + 6275af47ebda0d394d4a5a401b77bc6f2304204a https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 10a060223b3..a90a1ab74f6 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25451.107" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25468.6", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25468.6", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25469.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25469.2", "Microsoft.Build.NoTargets": "3.7.0" } } From e6f510cb87812d56ad781d93ff0513cdcccd0eb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:24:55 -0700 Subject: [PATCH 022/100] [release/10.0] Build duplicates of all installer packages for new signing keys (#16140) Co-authored-by: Jeremy Koritzinsky Co-authored-by: Jeremy Koritzinsky Co-authored-by: Nikola Milosavljevic --- .../tools/Sign.props | 4 ++ .../build/installer.build.targets | 56 ++++++++++++------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props index ee62c8cf6fc..41d9b0d82ba 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props @@ -103,6 +103,10 @@ + + + + - - true - - false - - - - <_AzureLinuxVersionSuffix>azl - <_InstallerBuildPartAzureLinux>$(Version)-$(_AzureLinuxVersionSuffix)-$(_InstallerArchSuffix) - <_InstallerFileNameWithoutExtensionAzureLinux>$(InstallerName)-$(_InstallerBuildPartAzureLinux)$(CrossArchContentsBuildPart) - <_InstallerFileAzureLinux>$(PackageOutputPath)$(_InstallerFileNameWithoutExtensionAzureLinux)$(InstallerExtension) - @@ -345,10 +325,24 @@ + + Condition="'$(PackageTargetOS)' == ''"> + + <_AzureLinuxVersionSuffix>azl + <_InstallerBuildPartAzureLinux>$(Version)-$(_AzureLinuxVersionSuffix)-$(_InstallerArchSuffix) + <_InstallerFileNameWithoutExtensionAzureLinux>$(InstallerName)-$(_InstallerBuildPartAzureLinux)$(CrossArchContentsBuildPart) + <_InstallerFileAzureLinux>$(PackageOutputPath)$(_InstallerFileNameWithoutExtensionAzureLinux)$(InstallerExtension) + + + + + <_NewKeyVersionSuffix>newkey + <_InstallerBuildPartNewKey>$(Version)-$(_NewKeyVersionSuffix)-$(_InstallerArchSuffix) + <_InstallerBuildPartNewKey Condition="'$(PackageTargetOS)' != ''">$(Version)-$(PackageTargetOS)-$(_NewKeyVersionSuffix)-$(_InstallerArchSuffix) + <_InstallerFileNameWithoutExtensionNewKey>$(InstallerName)-$(_InstallerBuildPartNewKey)$(CrossArchContentsBuildPart) + <_InstallerFileNewKey>$(PackageOutputPath)$(_InstallerFileNameWithoutExtensionNewKey)$(InstallerExtension) + + + + + + From 4eaa220ea860cee9fa61df42411bbf79394edd23 Mon Sep 17 00:00:00 2001 From: William Godbe Date: Tue, 7 Oct 2025 08:12:31 -0700 Subject: [PATCH 023/100] [release/10.0] Drop swidtag in correct Program Files folder per-arch (#16196) --- .../build/wix5/bundle/bundle.wxs | 10 +++++++++- .../build/wix5/variables.wxi | 13 +++++-------- .../build/wix5/wix.targets | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/bundle.wxs b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/bundle.wxs index b7fb42ee99b..1c5db2ade1d 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/bundle.wxs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/bundle.wxs @@ -61,7 +61,15 @@ - + + + + + + + + + diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi index 39ae31f8eff..e446d14c1b8 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi @@ -13,14 +13,11 @@ - - - - - - - - + + + + + diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets index 804d40c7a96..742bf80955f 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets @@ -340,6 +340,7 @@ + @@ -379,7 +380,6 @@ IgnoreStandardErrorWarningFormat="true" RetryDelayBase="2" /> - Date: Wed, 8 Oct 2025 14:59:08 +0000 Subject: [PATCH 024/100] [release/10.0] Update dependencies from dotnet/arcade (#16168) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 1281b4a0828..655f3eb34db 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25469.2 - 10.0.0-beta.25469.2 + 10.0.0-beta.25507.1 + 10.0.0-beta.25507.1 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 74b986276ba..8ab7516b614 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 6275af47ebda0d394d4a5a401b77bc6f2304204a + 4eaa220ea860cee9fa61df42411bbf79394edd23 - + https://github.com/dotnet/arcade - 6275af47ebda0d394d4a5a401b77bc6f2304204a + 4eaa220ea860cee9fa61df42411bbf79394edd23 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index a90a1ab74f6..b8b866ce354 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100-rc.1.25451.107" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25469.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25469.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25507.1", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25507.1", "Microsoft.Build.NoTargets": "3.7.0" } } From 7ff6478d902606d65aa33cb8cedc2730e1843fe1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 08:20:25 -0700 Subject: [PATCH 025/100] [release/10.0] Enable generic detached signature support in SignTool (#16200) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Matt Mitchell (.NET) Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- Arcade.slnx | 2 +- .../Resources/InnerZipFile.zip | Bin 0 -> 17755 bytes .../SignToolTests.cs | 109 +++++++++++++++++- .../src/AdditionalCertificateInformation.cs | 4 + .../src/BatchSignUtil.cs | 41 ++++++- .../src/Configuration.cs | 38 +++++- .../src/ExplicitCertificateKey.cs | 14 --- .../src/FileSignInfo.cs | 3 + src/Microsoft.DotNet.SignTool/src/SignInfo.cs | 32 +++-- src/Microsoft.DotNet.SignTool/src/SignTool.cs | 78 +++++++++++-- .../src/SignToolTask.cs | 9 ++ 11 files changed, 293 insertions(+), 37 deletions(-) create mode 100644 src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip diff --git a/Arcade.slnx b/Arcade.slnx index cc381104afc..a4c51eaafd6 100644 --- a/Arcade.slnx +++ b/Arcade.slnx @@ -45,6 +45,7 @@ + @@ -71,7 +72,6 @@ - diff --git a/src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip b/src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip new file mode 100644 index 0000000000000000000000000000000000000000..333b5199c4e497b6da1970d2e95ca3e571eb5758 GIT binary patch literal 17755 zcmZUaLy#^Etft$xZJ)Mn+qQYSPxsfhZQHhO+qP}{{68T&I`YtHxQb`rV!wnFCIFKVP}SHg5S}n6^jX z>$_a`t7q1~Dib^V&R+LB`;A$z>L;|7njQ+n0mEFdn6z3AOW97V1z9leQZwB$jxeRO zQ(9e3Uh`{9OF!-NOefW~`i!QZefRah1-^uaxU%QPEk8*J4aFify_|h6T>EfX0uT!{ zmc3C6b8dWcr4Lb_nB4!edMr~!WB#0|w0X-RPK7TwJA~|~-dSPX$WzJAU;dD0pK|^w z{Use*>ihRy;B8F3I&(QtcmmR$sCJ8TjVp8JwEB=0l`Lt8yQ%E`H0}$9}~;7HfGIu!F~Tjq@yGC9z%n_ZLZmk zIJWC=RLrXz!(lXT>)nl~A7BVp^nT@;SzpX-Sa>XvUXZeAt<+;s2k@&bm=)~RBzm87 z?az_?De!XLUwchyh-`Px0Zf$+w}%hLf(+LU;TO>^4kl9r#K?r(S7i!>hzNQ&fdy2& z#tqYN3swvY)Wr(jw+VIA4&#UDWK;KS1_p`5%MaE^hYiFY4R%McA_pR4?TPI4i8NIY z^oA}M1FC9t1D0UZcd)e&l+&M;Iwr;l5_t|qyVil_WPwa8!c0HB)r1Vh%Q=s$=>^m8 zH`Aj*q!~8m#OTT}(*oU|<%10aX*C1(+-e7wY2}F?*(=%wZY4>$X0HP}^SP7^G(%(` z&6xzNRB}YrOC<`4;md*iJ}h)R4@59*j1=z+s=o3ffhL7=z!wRxf^1JUGtUYmNY1Vs z<7OA85%ci|ir|Z)Yoi0-0uk4LxYkNAuIG28Zgh!c%NGH^ykz^b)Kp;`~ z7cbyv?G6*~f@aEYu%i?Cs_%dojKL&UAio+P8_tliEvp5^)>5=9J&h*>>>t(8y=E7R zC#~Sit;8K13SiY4s0JT}?cLYw_w7f(2Z8Mf`Wkptps}fH==TIZqr|RJs2%~g*XPgbcTAc;& z&uy)puVuEbWG9^_*+q{X?)fmWbOYC%c*;_e@lw;l^6b(LmteN)Q!f^o<=JJCp2ZuZ zp4jcwas*V_RHc@Gu^#H)uaO$6fy{D>C+W%eeT!T8AghG}b37`1MSme3WhM>7bnHqg zH`FmaK#?;`!jPX&LGB97WuK0)>q2=2>9VNEt`+f(6mrmQw$!Kj=8GZ%hJ4IWDR?9@xs`P2?cpyf;o*JN0Fw?EDCda5~6AAo&2)uH`!6kL5@lLNp-96grpc zplEi(MkKtc3VE9hGZ`XFBiEqkta=2{zpAb2op>XxgTDM3PitC2>ISl1ncK>VXQ;{4!g_;6h77?!wDWQ?FlP2#-z3^{waLr?`CkM?=! zxmVKWh<<3eQJUh8>pu+fAL6@z*O35>2oR=?K!M-Tev7)$LuoX9)Y0DwtXHsY@cs3y zwPut2yFO=0hiC+X38}y!yZ^sN4<$eF60?L%fjzaY{!%0uSMJ5^=_ zTB8+Q8Ouw&b=bEq`uR~cXr7)~Jl%$fAK(0XxmBV3JRftV9E9h`1mb)jdAc%Qj6R?x zR~w6EOQ_H=Y;nS?fnl2D0USL*jK7mw1{-bQ1JDP*1QA2(^i;bb09{L`AV~?z3rJJJtRIO- zwDv|rHM!zY4@Az`L0A=;yY4`r<5pLQp}6qo-x$dl8oIRF2;$|Ul0Swo#!z$G(plGm zCOZ?f$kbn$9U7{bOh~d>GivqEx5lskKIrdHPOAiSb7V)*rovBKFftu9S|yAlISM>y z`A21tR@inewRzb;T$fVf$4lcrIaOz|H+Gbv2))nv2>QXX78J$1|@;QjQ{^1RL*#PZyvpcbq zmk%ew;md_Hr6$C4CFy3uk2@(~6_>_|{}mxF9ta_k{4TH>q^14LAZtEO>7%Qt5yfzouot_7>bOWduZk2q zVaEZS162onLeYSJ=r1!SO9u=ch~!{;n0k8%IcjrG>1j-bzPV*DVn$`bD_tiI47v-c z>KV>S8R-6bYx)Pm74)j!Uw`5WH=eoz+umKjI(o(b!lUcZY5aAs3c=cZ6IVy4h0;Dz@R13uL1>dG z6CIvXzJ@SBrcyVySIKmC?mNH*XGdYRJ?gecac&rgl)*N*%rnVINe3VN1cE z-G=GEZ~O2Etib8&=9%pPFF@-@NPy0?Y_b6Vp;NHeXuYHa;(Pj~|L4o_6M+$uk&FJU z)^N+(QDGngwg)d8;iPui(V2I5e^YbOZW5=ri+`&zilVnHzD7XSr79gjcYcc8;N=aO zHpx=gmk0o-c~5>-B~m5~M9iLnuHyO09C!Rbp^ZjiM zp=oEK;S%Oy_9Smo&Gqj`+Xm6Yuy^E}S`6Lm`p^BD5iB5D%u|VkA0C=1FCiiwqL9tw z;xT-FyE<_h4K?r{~(O zANR(V`7-`Z2eDPHxbv!ZN8`*8wJZ5MF{Jb%tdu)o%n^pR=sXVE9F!?Xp*}GSL8Y{0 zHwu#El3bKIu6Rz+?`heG9CftA0X8~Y19Ajm{QKF&a{uG3TCKH^agV1_&}i?{gD+)d zV|_!xlH)uE_}aP9ro-dnHUDZvA>1jca=cSVk-SSxT{eQOu0PjEMYB0{8F0%3@mvaG znhXG6&~Fp{B$=)E)j?4FT|Q;$^Rfk`WcBaZ4DaGsbc0SYmy;>{>mwr0mYQ5ifJ3KkUCSt5 z+f;*8mA!T~I6Ncs>@>EhD8!d#_MpMW=SgOKMF-F@Ddbq!w4qfjp|BW|NtJ{nI29u| z2obl9y6g-J-G0@h=2~h8^i=Iwqf){if(k+Y3sBW;De>&7-<|pQw`#VORGF-tN`aUH zT>>lZLAEdV7fG1C?qa*9aqaKA^;mCEY>S}fKV$$1-*K~+-VUj?!eflGTi{`|1-f9b zHqjZNhab3V7Q?djtG?F-{4(_{a8iUjJP9 zcW4Bk*eaSxA#U4wu#B5AO1=#Twb97TG?`HxFKwohI_vj)kc=?05eR>9XK=`qG5Y(` zh!4VSWl;OJQ%6=`CRUk{{mjx;^ufR|YW#VG*GtYYGt+-v052w0lby$jjG~?uOKyRh zGj$DR1kp^V1?$m<3Qu-tto1wn21PcD2c76v-UVBWE~2eE{jDn=szd(tMV4hcm~Zrw z<6_3RN?=7R7C;M2?`U;Zq>uT0-o{K4QAqmYuu)TV5Z0E)49i3s!htp~57Sm1GzrB~ zw4sx{WISLdT_W0+58p^sP09Wiscm%n^W@f0HtOx6vU^X~9_0;h%z|d}N?Xz09+cB% zrM8zj)7PevIXZ-DS39@;)>AobE*8}1frw@C2>0@7KL&?hKd!SqG``|-@1{|D*pFVZ zR)lfv!S6p)xi#leRr#!W_u^5Xd`sI3VIQsM{;LWeYO5$0(LJvc-AV|&6^XcT{g2r+ z%=kZ<)A4*@p(o-KQoYOf@*VSFfrwY&Yzl?YWQ+W~T93B**AWo=a@+pv$8>r=V%uU{ zpLu@_i)YaC1G4y`qzd^~z<7a#KGQ5|>78aX&rII(dsqhS>Q>3a`Yyd8GU|oa3|J3p zwW;$YA5B8|Tu9rag5!J|Za6zU`y|K{_Oe7Iwbqn?H%LQr{x#5%eUgoyN4-~4Wj zQr*`U9=(53-*ZvB*JmZ{?n3{k;NEih133^lA@%nvU4-sygK2t26s>cHqo~TnkcWFA z9_ZJIzG9wP(!?lBgmiPq|AE6Fz&`0x28V<7X~JV>E6U~?rZ8O#yJLVj@_w9N8c{{-|7hNznn%KrbL49 z?yJVI{M{R42GPGTa+m491sACjy6biu`@Y~ucs}`!QhJ(4AhoM!4L+&ttMRl;ZFU3@g?fJSxJ_J5BcfaeaV}AfXV{v!e>dtBBr-U;&akZ$g z^6K}P9e9@%3TVgmHEyjkJ>6+}KK$`(z658_oZ{cvk~E(epaWwDP>qQSk#??32HTa@ zalaFiZM9m-x1#PkUT+90_5fu~bp-b=4Bkj&#W$n0+a>=rP`!Kun2-CPYEff>LI@Vt zDTOw=rQ1_YU*Ou<`u4u@;-QRtNI_8kFqfvcoPOBKDcmvO)831#2rhW1hhgrqlr-NPcfDg}N z;{Bf9rm6Atu=#(Le)D_4-}LKOi$~#0BwVY8gFkH{S-)1dT6u>wHJQt!DbZ-qB&ArF zLkxxz5VY^t5(_syt$)TBU|=VW=SK^OGzG3_taG~sdSk1GIa}hEh+e`df)s>>ZH#*S z*~x;Jh=ZkmB?f0Vc$e?3wjKFNh@;RCuFrXDI_wi8pD}tS* z+>~K41=j=swtuvOXGzu0byVK099OfR`VbM~LQq%nH85w}S{T=5Ry!yW2<8yr zC_U5xu9pJHH&inCT`AVr7z#RPc_?4_Q?mp=X_&{edLKDV@Aq4ek6wLK8P)HAI({XKs0*23xH&MaVE=HNh(;J@EOj2kya&WVb-YxAr^rDo+EkkCcT);crb5 zk^dTMAoAfSX8AsReh8Y+u?bONH0ZiScQgm475xiuFLuQgY0`sFxTizipreYIjc&X@ zWkYUr=)EfY$sdo~y=7M6K0yTZFH z{+~~nA)%vODM_%R2sDqMQakVZH_%w}NO9ncoSIU4uN#WeI`(gstjV?$lwxxDK|y?k zWt*D35#a6>m1f4$KDyx;U`x|>_#Rjj2z_Wj6+BXk;S#l$Sl0Qp!O=GS&XLi?EkT>5rZF8fNf~QS_vFe({UfHcBk>zU(RR ztIw6+`)a9nVQlW|!dRb0s4Y$aeZzD`w0n7~kk<_$49c48q7KXw%vwSqAz17=t6Owj z>|El_@U|ZTf`_X(WQ4z+?zJ$S>2uZsjphQiamvjr4agC{54pPcXDejob_5%?!Hi1P z3mLRNC{i=;GU|7m^)$v>&1De|M!=ibGy;d5#pZH;JUdwle{wNK9t46X)6V7c&Z)wvQtUKL7(wjfH;Lw*xY=grehv8} znR$}4L6Ko`w@SN3d zQ}iVD%99h*7xWyPZkV@`EZ;Z_X_vqnRlu<2=6JpIjG;Y$ZyLRso&|s!e9a`=lUHU| z>3@N_)_g}YN6L#gjWBYxKcI>cl|2}W1qz>(qzMa?@JRq%#gkNe!qg&G$i=5B+LGnL z3%b%yRMQ1$N*)Opyh7(p(v;ME(&u2s;M)2)GX+f%3%Yd43&PT~rA^rjl~i{5OLA7A z#g$am#b`nM7Yo3g5G4Npm-c^Ahjdbo z;A*C#ce_ljttYxH2Hq;m%d>VQMyPsoH?=a)Bs0Y<}kj`xs#}(fd37k&D7`UYMkTSo8wOVDN>e&%z(1v>eo_F)amZ#RpppCX;>q=~>trvENX`YIU7Bucd4CQ-BiA+7+8h%shTMjy5-wIRv0dzgMh>cKgUto~e@%DyoF zpdd50G9+)&Ak{zQsfeC|0ZC!@;zM--#aij$Ba|QCngO!8 z`9%Pq9n5<>wz33t@o+y6E--DW`T!C}Y1)W%p!--scm_rxV@g)1 zS3_UXfD$U`OZnG_Ch6kjcr0TA2VyEdz4$V=E@HfOH1iDAI}IonHefhz-2}Aw1po&z zdT@MzD4H-9QyB#K-ex4b;94-*n<$XXyjK!rmU&lyY8k9lClp<Y@7Q(1rnz4nEPbXqY>1{M;Ki5=mh<`b&%(37@UD#6n<#MYEAah1)i>EfrG@w; zW(VVuEgKpws4e6bgqLZhi{9c!VwM}~bKnNW<1(4~_z|8|sRT1y!h*8JNK(VX$SG|y z{5h$B-4f*`eUUoQbVmxs^fz|Nl^+wb_%-7~Mm+9vLGoA_ZCG%GTp!%O#c~;P^P66IU^1!04zmuLrS6tWFN~RAg?`$ z9=ij?JRD;w zq$0nYzD^o<$r+UowdaJ@g> z!^Hr+aVVK&{X$tSX?`P>zvyg1ZM7M9*7clQ&OWO>`HE1e07Kh>t~c#j9`HN(z^u%y zyjLQjH~Fq7%I>(^DoHg1`5~l5aj!vA4u3{M&*5fB0A^2i4u49*&k}h^0BCAa>8-3( zZ{d{UooV_`>a6+O@qlj)xxkCUK;!G*L>cFY%!cxR_&)%{Hy_ZBcYaW1AXnfRU;^+d zV4q1)2vtZ;gf_4{uRh(r#5i`~H|D)>B2R{S;4&b6cuiPM7)`)$>LpX_4I|MXLk>f% zI^?@s;Mc3S-OFyg3oA%ka9xl$ctJ4Ef3?8ZkX~qa7=4xgtH8U?elC5=f)I5`jG%Xj ziY<<}sWz(}p5_m+fjzl)9C;GTZa&Hkd)GM9`~KVWFSGO-ulv8^DvR5Cl+eUo z(omlPn#`n4Xl)G#k8#^Vu~;t>y!^C(l*vw@BN_tcXwU|Tu|>+@+#OmmCD0WFC++Ut zNb?oRqNIf+QZRhb0 zMPanGEMdYG2p^TlCvC*cP~b&N!f3}4*o&M|LLjaOs@9p^YjCzo7rQ6xIF$t02NBOwHC-^eD01^sI+b3?OB{FV%n7|1rXq@s*Z*aC>RL)3A+ zR7_OKqAFt6W#l0de9W3oH7cQZ5)ncBkja5gUQVz)1V8?a75r z&gYepVd*q-xE}{qtIll@kF81YIS4_t)ru#NC3dCw1%VrMBXT>s*l1qD@?&0OD_(P3 zvj#c1u&B#tb`go?>O{EJ(-!tzrI^Yk(k>-xF;J2qmm|Q!tnJFZocGtQir{RIpARV~ zxXP&~5>2>ua0XE@uf~O%{V{?08{QPouZxOA2H@qvwhfmpE$pD}5V{;m5FiN-s>56r zRc+{u>2bF9?gz3*`RfGNg-L>_UtzXzEd_;+Z7r7WCc~m$;JOOcpBGg#S(ST(Fb%)= zsYc}gqM{c;L~Y$f5MqR@%q)-1e5O5MgJc@a|07(+jmj)>K8SKuu+I%G((92lMouH$ zc2p|38j+_1wLn4g6z4BVK@;MWlcd1QA!ONHSw6pP$gcEynK!5f`jPQ7i`(^~pNOys z^a?2m+&X=dXc2m4ad&DKuSO?44+_yFmMc30Ia#UEy(#nj+_Jb4VDJyeun9PtaNyP{GruG4#gaFC3X5hmfP z!MOiJUiZ@i>sZz_sX5hGAoDc%J-V^9Ac#JA7g00;qlHc0q~``}+G z_)rz`j$RTG-9oTg-STP6_2Tl$-8oB8w`=rU{(XCOyCG0-FhqBF(Jo$-jeGo)%`@=+ z4*!MmqwW*t8|E9~8-43&>jKA3;PSO`UVBxJS0x$+thK+vn97$*XUt%{xsJfz7orv1 zopWtuZ~U9l9oU`Oo$8(T4f)b~)mz6OI=DWy-3SRv}j@cPYEpG|Ou3u?`pm46z!0bf34K zqpGC5<2|~c1fSfdF|gxKceQVLrN2u%)xNVohPlVSqfd6GTs2HIsF~53r<*3Z7|5F*Rj}TeG+jMV{uzkFaUC*sExe@>C*BRLv z>zT1R**(xbj{X4q?sK=s|6}k@`bGF|lh5zUUE>mxz3ck3^bWLSGSt5Np77{8!;u{_ zu{9B#OqYB~)7hVStLmc|UIBLoU#3BkIZJOGsl}o*&R(Z?bv&XI9gD@0&OEit$iSYO zHaX=|=hTPCAz=uLv1&jtrI1ZBg`Y_5R^BZL{iT z`G8t^P6ck{_;qtZIIm{G-_G(c_D#;vH>bVi7er)?_1F17@&AB`*E*hVy2H)_ zul27x-j>CBlL=p@-=^~>bXqO-@DP(Jw$zd-w$LV{ik`*Y37EAnHLpfh%Bb%$3ro~V z5D$K}#Jt*;dBo6GmY@a+As7pDWOVPsmco$5 z0JB+6CsXMx7BgAtTGbyrKcPj)@iDK{9O3*T59?CD&n)2_Uh{`WA5FiNmHdTIKF-T? z_W&KlsqSs= zXm7lA7it{VH5wGnmkmoPXMx+O(joFG>eNy8E48E<^$=`1_bVSU#C^e zmi!mRgUbl^Kf7#zvt2G_t4&S1Kd_cdBn3Zr5>Z5<$uC~}xVQ4P?=!9S^ZYOsdO_HR z!z6~`@bEAg#`{ zv{owSxDVR=?C!N&$ffUxsUj&nTvG(ev78N|_@a3cG)61bauA{cVnaGZ8XRaVmmwI6 z&ZJd~f!ex8q;jPMUt%7ru^e^DI=6v6Wf%qny1Cf*Tr$`!1>#=Vq~=Re3>HJyI5FDy z=usbXEElo0U>CYW07jf6Ef~(qG?=`d{pK!H*Zewb zAHMevyxtWYckF1KyT>@8`*3Le^qxBH!uoO?I=T4Yap`WIw7V5TJyvHaAYEP?|D6gu zwte93S%192*pDsAz8fo!3Yn4|Tc6NWYoh6q8~VaA90T}!xK!C&_gMt|!~VQ=JQpA= z;KkbQ|M`@8W8lFVeBpF_t1--^E-o>2 zedR66iyi$yjaqx#RZFS??;D#K`7S#MQ|pee%s6KfZ}INBB5Hid!*nNu#-`$*O{lvm zz0ucG;^gQ@Rh5V%8~w9I_h-oi2stwzqbAwvpYO3~W_0i)LG!HXg5(t1cgGdJ(jT2= zJo7pcH?A|1m*#Zu#3yX7dETeo9k?BE>E6ZeeS@4rnT5c`ZI=+<%lUe;rU` zJPn+`KARy|ssRSUihIvru4N$Eh`f7h#a?px)Nt}4_K~Cj(X?|m@~E~1n20?6EI&`j z=ID7^kmc=_A28_s%ZtmvD~C7xjebAnxl!fTBiPZLAwHp_&&t@XdFc1l(#p~$TL4@> z06zr#H^PFI`0B>lLKV9R*upPxY5Q?Uz;zbY-*O6CR3r;Ds8>13bLCNqj_;9=+0=G< z!Krp4ZsXMEp0s*|*p&6-Kuxy}aW)^p2wlqq79$ZwF}kC8#s^Txn!m!1<9rX=#dC$@ zwMk!M6WC1LeB)t(B->m=&p$-pQuU#IYHb({!4D>9_h_dCXvw8iXHRB%*=a>nz7}(giaDPqL2N||TI96NG%eJ7}PDfkr zkUMih%i9x1T5+@GpUk8Vza8c32mi@3CGcK)zjG>G&}AW(3KF8qccS7E#QPNu8*OX6 zP%4vng89touo-F`<^pERkM}oohlm`QDL3zmcH{pJCN{DN0t1-o&09&CdWog$uT25| z=&>%<+WpI}n6^@ujQdL%B<6exrb(d+K&x!Vs0GjF>$+>0O)mt@VJQ0CMv1*?6|^(v zv=p&_u}8M?Kdn$w_Qye~XnTnqQO-9K^s+Jfsu$C=$MG@3X-Ul^YSn+IOE{L&9Xs+- z+bbUr=iRCw} z2y1emEG;X)x92Noftf6lFc6j0*@s4KCO&~o*r9kPHM^!a_Y_k+Y;+q?>{RQ_vDr(Z z{`0q((y_O?FzW1|mq~rubAaZF$t)%1kmc0G7=o-_b<|bdv3LvHZb5}R#Jk0Tb}4!1 zc9|Bd6}+s`c*p4v(Ywv~sEKy@NMoEOFO`fM zPvCM@1Dn-`Z&|V&2{IsN~oNjD7X<$+5&A{g~NC}ztZ$4=WiF?IbYS~ zlg!m8xxKP^(ccb)Pt5_#9vJB{*BHUh;Xlj?c357c-w4 zCGKO~#kPGRhNiJl8|IGO!4to$NMh)w3!rBiL~NY)`Ib)h>fK!eUovjgId<+b6i-eC z?!^v51pW-*Fd8Y;$wehZa%WLhgm^ZOxRBj0Tx4bI}gt(Lihp<#S@m;<6cF3qMv+;`0ZP$wn&R?E5^;?Cxa2GlcsK{r4ngBZ%SrmcFqO41?mJ-=!ce8;k zgILIOV)4n+ zk(VO|(XW64yqb&jVg4>vp64tpPo@U)JZXRQ<7l}iu2eJRhcqX6urwJM2!URbJ+Dq| z8u`BwK`pf=9e!v>3>T-wQd2%hTJ51o>0SCl&X7>2ez{TPWlAWqdHYUUrcsLcq^4F z_mVkN*m>a$1f!TXSVTZlUO9!MAPcaU|12;95%Z z0`6hP=fb620ef@#d+Ao$eftR?Y5q$odLI&QCMh=5EeN&CL@BYq-9E{lw`pqc(?$S!RUe!_a z0o$eq?v}1`baM)mwSps^VSh_I_~lK_!BMSh-!l0qZm$k*(i2b1Gk1>{!*r$+=I|E% zjqp?JXE8sRllwdS?%iZypefEhb&l)8VvE^a`x+xBmE~bn*j&O=oaK;a+i<2SgU)0@ zqdvC#N~3P_o<hwD<5bBN;ruT53w_k>l#j=Wm`?5y_ZRjD zfe++I%LDFfS8QY|*oUy$Lio0@w7f_tV_{@_ueXTLkHEgtBkZQQ>_zGW!#RYY_?CwB z#uJHJ|cazMlOS?F}*6C1Va}T9$*B)>8SkkpVA(B(0D36t zb|54&nQ$P){1~shrkT2SaofK-qQGm>H~2tfCYZawR&BS0R!JY8Pf#X_0Sv-FwP_GW z#|@3SKw{or((Th(8(eBD8(0LO*)I2xTm6K#fX6K%K)<<8ibfymQDz}!xk zuPP`S((hG$yMSHfwEKc0|FO>aB3Jxc?&NTNtA@);jjX{R*G&LZKnVdWF0Da_b!@=UsqZ3%u&lmobBG21V79@MY%+`X^z z%m4_|3g-^$niYkWWDk17-b;get~sQDd8?d>mtwETFVc$c1fd4@D1I53t7w&$FuD30qF}w6T`B?}07Mv1fiSw1i{j53@>;O`%^28q}>D z-KQ7F0{HlS*b_FmRF4;dkDCMlI>kR51gfW#-!yyYDid9LC0pcb^DMDzeUm0FLzHvA z#aj`td`~zNtEP=RfPA;AXpfOmnc*Xck(;s8L{vjKE{#{y$ZK5xysd_C2uc_+Pu_oh z1k-Dz!%c~DPwT^`G+MG09pWR2*Vmcn5B!vB6Kfi74Se5ZwFn|JHU0UZtZM?2kXNP^ zHePG;#aByyiJ0C9MIk)b`wN;D+=$EaJ_V@S!AE~7%?M>TIcOEVjXW)>1m<%a8>{+e zZpV2Jj}cS`ooKSX6k28pLX~TroNrv1Yi5LvYqOgl&yg?2?so4-t`6pN(C)!4S`+Cu z)=8+$Z{%U3kIGA8lB(8pq)X2p^m%yHd3}b%3ng61CFJ`^Jr`UczJN>PPJB+59TqDW z*3q0P8_T2U=DJ;!-tsAVF8UncUfW39x@RPrRnkUtrm<3obGqzcuklUToy}DE)>USp zGhNnA&Z{ClaS$mUt#Sx1&Sfzj<%RYhw{ND6SFJonN9KfmXd$t#x9LVjvu7l&@aVTv z@mkjl={?nVle_wnDNmV9b=!mRjMh2c@5x@z)e*Qb%kH$JOB&C)9-K6T!|#nKNlDFP zuIbm4=}IC|b0DydJQoJ;Osy4J8SYfWo{6_+19MKTV#npc`7emuP4C+TnQOWzwe`^g z;?4Htl%U0hjjgiVj&Q!if=5qbWL3}nHwj2Q%fj)S+pF628I6BJI(H74#OF(9vGADa4L9&r(m zkeNw`gSn^WjuuTuF{^N9$s18w(EpM_`7rTi%@CQlrO2%0nXUkgS0xJVDHY zN%X}1_m2XB+=g;VC2K0-LdlYDWGatykHj$pVfP94O#T>rI36{%8SH&7YxYK=oDO&Y zqF9gEFV4dy-_AU8zt6-c3Xv?6^x6@Ggr1aX8yY9q$mK#3#;=FW0<>uMW2&70wNfo3^Z4kL zk6klHp5rfFW67CZ%M!R=g6ky}pm4PIEngGx0IJ-{Dc?+%eU2i9zCvc7zB|Q|EoVM? z7eumeeT+$k8g**=bYoV)F6#Ft&w_hkzWEOl3Od7R&w=PLU2x52sL(~QaN{{f;o1~~ z_~Qce_sG~?Yf9a}*qeq<+cQjq0aLn^?|{*@jg^~5wX^Z6WSDs+b!vdgeIt29)l^~q zz2K_z53D^2HJ7`z1HvgEYI#voYiO3gvA!sOj7wAm3jyh0%Ixza%@t~*Qp^*2E2x3g zb|D^L8u&ja>Ync2`rQ^SnO#i$wjo68m^&Y+Y9a@;{?`=I8|}EJMlU^Tag^8$$>`>g zKHvr=l?{Qz9$6Nit5$etZrB(YoP9(XM*+99QXR$(7<&`cFKy`MXr}{RdQ?e`G#Dnnn5@QWREmf>KagV5P9+lbr0K`{6;c75d}H zD5pA8`Sr#vB5*ue^k}FwdWW8CIeE@vl4q{cEAOxAZg&aaTL|J-Kidg@xhS$?*7KBs#mN-CUx)A36n7PR~QiN$2M^Ecf$ zo-(i7uInNk&b!~uXVZjUzY--Nau>t5ZhqXZt!z;Ug28k=V30vq~7s4V?Sfd@q_V~B;yW4>d#nW9#~IIYxYBE zOE(r@jDeR}WZpoDyBtaug=kBN#Mt7%*BF2EtU0F=4kK-!X!7EiOR)C)}z9vLrUoKU!9x4c)BOxQiBy545^usxlq%2anrqAO&@ekA28UT zi&&e73hGY zKb_?^W;O@Sb#~4vCk-m&P`OBgcy}u)@Ho>zEaLV6QT*@HC3!=~2@r`Qg zL?r{M7Nqlyk(in0rVl@7M}hIG_*YKzs4#eudxFrh=pFF@y5VRKtfss zB#_Tk5N7UlK{gsjtS82cC@*3Zgs>w2nq&Z^<_Anr!a;!~c;Z(6y;mTX7~OnVEbc^e)gj2z^NpJWDdlRP9oBof3i|F( z`0@5&Eebm#HsNoDd9Nn>*kARqXP|-jc3)DkzwPJq20#0ME_gS(6PnX|qUX{4gEyCPD?QBQcN;d)5<}8+` z6tkAA3q-dp;+0TwYOBd|zOm|Hzr$LF3vH4nITQIrZ1_3DCoWsmD!y-0*OhZg?M^K3 z+d}8-kk( zKNn5*Hx!#&Q)PKg@zio1!G?0z-3m)TJTSQ5=#cVB+NU@tyO8TasP56fY2Ur(Ee8%t z&8jile*WY%{%&XBAOkP!(V#p2*UYr9Jh;nhRkSnhyTHE3A<>IOxejf8nyq}l*x0l3 zXxyp`kM@1>xTk5c_^8CKXEK$amAN!i9+{=97pg8RYZa>hn~>vp(PfSD04vrz&)5g&Y;zxJLGI|7i|MWsd3(o0rx;K6chcmMd0ILieym_7;vj@7KqcefyRh zZN6;M$t8Ptnr_{>b*t^wU7AyKvhSK%+1%P!`FiQ$+_ll$w({mr($dcVC=zfYJw1Az zTludu?mhn>pSS;a?e+Qzm&_m3E-&cV|N6dJ@cCtWNu1z9_E@va z*Cib8z8!!2R_%TLeg6*q^ldKR+{f!~{=jFloNu29w{*d}TLBN}@V=|bJ8|o%;DwVh zeCO6r@p$s1`S82G-r3tHtge0Cy{@b$;Z0v`_*}cf|DW&2?b`ct=gWVR*;0G{&xv^O zao=Bkd70`@b7%U*3z;AL$P}%(e%+_aV%aTcWMAda`ybq|q_!jR-u|i+`OaJP4m@xF zU;gBR;iAvCe9l(({`Y*UzB795`2*Sx-shcJ_~*t1{jT@Iss`)yzDNs~R$e)B!P!1; zQQSvg>6>cmZ)P==1Z@Eh;_dBU(97)0_e+1;)+a1$WDfdqeZ0Rgw%Yomn9FUG6z$`h zzrU^dl%sUZYV}{oJ(U?VGOxFpt$nw<^;V{GomqEX_JwusJ=1TeB!(_a65;+neSydl zi*6sL@CTgI_nuADnb<6U*WjdHb97wtO%5GbSc>d2QHgk*%lyWdzR=k`^XtEj_vJfoxjDguY4*%k zu`4-C)GoZ;!aFUeW!aJ+8v}21Wo_=bT9zQQG3GW~j`Sakp6#+a^nL&a-{fw6Y0k&W#Jo`Zrh=?F9w%Y() + { + new ItemToSign(GetResourcePath("test.zip")), + new ItemToSign(GetResourcePath("test.tgz")), + new ItemToSign(GetResourcePath("NestedZip.zip")), + new ItemToSign(GetResourcePath("InnerZipFile.zip")) + }; + + var strongNameSignInfo = new Dictionary>(); + + // Overriding information + var explicitCertKeys = new Dictionary() + { + { new ExplicitCertificateKey("test.zip"), "ArchiveCert" }, + { new ExplicitCertificateKey("test.tgz"), "ArchiveCert" }, + { new ExplicitCertificateKey("InnerZipFile.zip"), "ArchiveCert" } + }; + + var additionalCertificateInfo = new Dictionary>() + { + { "ArchiveCert", + new List() { + new AdditionalCertificateInformation() { GeneratesDetachedSignature = true } + } + } + }; + + ValidateFileSignInfos(itemsToSign, strongNameSignInfo, explicitCertKeys, s_fileExtensionSignInfo, new[] + { + "File 'NativeLibrary.dll' Certificate='Microsoft400'", + "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'", + "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'", + "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'", + "File 'test.zip' Certificate='ArchiveCert'", + "File 'test.tgz' Certificate='ArchiveCert'", + "File 'InnerZipFile.zip' Certificate='ArchiveCert'", + "File 'Mid.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'", + "File 'MidNativeLibrary.dll' Certificate='Microsoft400'", + "File 'NestedZip.zip'", + }, + additionalCertificateInfo: additionalCertificateInfo, + expectedCopyFiles: new[] + { + $"{Path.Combine(_tmpDir, "ContainerSigning", "6", "InnerZipFile.zip")} -> {Path.Combine(_tmpDir, "InnerZipFile.zip")}", + $"{Path.Combine(_tmpDir, "ContainerSigning", "6", "InnerZipFile.zip.sig")} -> {Path.Combine(_tmpDir, "InnerZipFile.zip.sig")}" + }); + + ValidateGeneratedProject(itemsToSign, strongNameSignInfo, explicitCertKeys, s_fileExtensionSignInfo, new[] + { +$@" + + Microsoft400 + + + Microsoft400 + + + Microsoft400 + + + Microsoft400 + + + Microsoft400 + + + Microsoft400 + +", +$@" + + ArchiveCert + + + ArchiveCert + + + ArchiveCert + +" + }, additionalCertificateInfo: additionalCertificateInfo); + } + /// /// Verifies that signing of pkgs can be done on Windows, even though /// we will not unpack or repack them. @@ -2587,6 +2674,11 @@ public void ValidateSignToolTaskParsing() }), // Signed pe file new TaskItem(GetResourcePath("SignedLibrary.dll"), new Dictionary + { + { SignToolConstants.CollisionPriorityId, "123" } + }), + // Sign a test.zip + new TaskItem(GetResourcePath("test.zip"), new Dictionary { { SignToolConstants.CollisionPriorityId, "123" } }) @@ -2618,6 +2710,11 @@ public void ValidateSignToolTaskParsing() { "CertificateName", "DualSignCertificate" }, { "PublicKeyToken", "31bf3856ad364e35" }, { "CollisionPriorityId", "123" } + }), + new TaskItem("test.zip", new Dictionary + { + { "CertificateName", "DetachedArchiveCert" }, + { "CollisionPriorityId", "123" } }) }; @@ -2634,7 +2731,11 @@ public void ValidateSignToolTaskParsing() { "MacCertificate", "MacDeveloperHarden" }, { "MacNotarizationAppName", "com.microsoft.dotnet" }, { "CollisionPriorityId", "123" } - }) + }), + new TaskItem("DetachedArchiveCert", new Dictionary + { + { "DetachedSignature", "true" } + }), }; var task = new SignToolTask @@ -2667,7 +2768,11 @@ public void ValidateSignToolTaskParsing() "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='OverrideCertificateName' StrongName='ArcadeStrongTest'", "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'", - "File 'SignedLibrary.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='DualSignCertificate'" + "File 'SignedLibrary.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='DualSignCertificate'", + "File 'SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'", + "File 'Nested.NativeLibrary.dll' Certificate='Microsoft400'", + "File 'Nested.SOS.NETCore.dll' TargetFramework='.NETCoreApp,Version=v1.0' Certificate='Microsoft400'", + "File 'test.zip' Certificate='DetachedArchiveCert'" }; task.ParsedSigningInput.FilesToSign.Select(f => f.ToString()).Should().BeEquivalentTo(expected); } diff --git a/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs b/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs index e7e2ed2f4a2..06f87035dee 100644 --- a/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs +++ b/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs @@ -21,6 +21,10 @@ public class AdditionalCertificateInformation /// If the certificate name represents a sign+notarize operation, this is the name of the notarize operation. /// public string MacNotarizationAppName { get; set; } + /// + /// If true, this certificate should generate detached signatures instead of in-place signing. + /// + public bool GeneratesDetachedSignature { get; set; } public string CollisionPriorityId { get; set; } } } diff --git a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs index 1eaca00b273..ecd63cf0009 100644 --- a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs +++ b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs @@ -554,9 +554,10 @@ private void VerifyCertificates(TaskLoggingHelper log) } else if (fileName.IsZip()) { - if (fileName.SignInfo.Certificate != null) + // Zip files can't be signed without a detached signature. If a certificate is provided but the signature is not detached. + if (!fileName.SignInfo.GeneratesDetachedSignature && fileName.SignInfo.Certificate != null) { - log.LogError($"Zip {fileName} should not be signed with this certificate: {fileName.SignInfo.Certificate}"); + log.LogError($"'{fileName}' may only be signed with a detached signature. '{fileName.SignInfo.Certificate}' does not produce a detached signature"); } if (fileName.SignInfo.StrongName != null) @@ -564,6 +565,19 @@ private void VerifyCertificates(TaskLoggingHelper log) log.LogError($"Zip {fileName} cannot be strong name signed."); } } + else if (fileName.IsTarGZip()) + { + // Tar.gz files can't be signed without a detached signature. If a certificate is provided but the signature is not detached. + if (!fileName.SignInfo.GeneratesDetachedSignature && fileName.SignInfo.Certificate != null) + { + log.LogError($"'{fileName}' may only be signed with a detached signature. '{fileName.SignInfo.Certificate}' does not produce a detached signature"); + } + + if (fileName.SignInfo.StrongName != null) + { + log.LogError($"TarGZip {fileName} cannot be strong name signed."); + } + } if (fileName.IsExecutableWixContainer()) { if (isInvalidEmptyCertificate) @@ -589,7 +603,28 @@ private void VerifyAfterSign(TaskLoggingHelper log, FileSignInfo file) // No need to check if the file should not have been signed. if (file.SignInfo.ShouldSign) { - if (file.IsPEFile()) + // For files with detached signatures, verify the .sig file exists + if (file.SignInfo.GeneratesDetachedSignature) + { + string sigFilePath = file.DetachedSignatureFullPath; + if (!File.Exists(sigFilePath)) + { + _log.LogError($"Detached signature file {sigFilePath} does not exist for {file.FullPath}"); + } + else + { + var fileInfo = new FileInfo(sigFilePath); + if (fileInfo.Length == 0) + { + _log.LogError($"Detached signature file {sigFilePath} is empty."); + } + else + { + _log.LogMessage(MessageImportance.Low, $"Detached signature file {sigFilePath} exists and is non-empty."); + } + } + } + else if (file.IsPEFile()) { using (var stream = File.OpenRead(file.FullPath)) { diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 439d09d35ee..56f8c043e1c 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -224,6 +224,14 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, // Copy the signed content to the destination path. _filesToCopy.Add(new KeyValuePair(existingSignInfo.FullPath, file.FullPath)); + + // If this is a top-level file that uses detached signatures, also copy the detached signature file + if (existingSignInfo.SignInfo.GeneratesDetachedSignature) + { + _filesToCopy.Add(new KeyValuePair(existingSignInfo.DetachedSignatureFullPath, fileSignInfo.DetachedSignatureFullPath)); + _log.LogMessage(MessageImportance.Low, $"Will copy detached signature from '{existingSignInfo.DetachedSignatureFullPath}' to '{fileSignInfo.DetachedSignatureFullPath}'"); + } + return fileSignInfo; } @@ -262,7 +270,7 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, // Only sign containers if the file itself is unsigned, or // an item in the container is unsigned. hasSignableParts = _zipDataMap[fileSignInfo.FileContentKey].NestedParts.Values.Any(b => b.FileSignInfo.SignInfo.ShouldSign || b.FileSignInfo.HasSignableParts); - if(hasSignableParts) + if (hasSignableParts) { // If the file has contents that need to be signed, then re-evaluate the signing info fileSignInfo = fileSignInfo.WithSignableParts(); @@ -529,6 +537,12 @@ private FileSignInfo ExtractSignInfo( Check3rdPartyMicrosoftSignatureMismatch(file, peInfo, signInfo); + // Check if this cert should use detached signatures instead of in-place signing + if (ShouldUseDetachedSignature(file, signInfo)) + { + signInfo = signInfo.WithDetachedSignature(signInfo.Certificate); + } + return new FileSignInfo(file, signInfo, (peInfo != null && peInfo.TargetFramework != "") ? peInfo.TargetFramework : null, wixContentFilePath: wixContentFilePath); } @@ -863,5 +877,27 @@ private bool ShouldSkip3rdPartyCheck(string fileName) { return _itemsToSkip3rdPartyCheck != null && _itemsToSkip3rdPartyCheck.Contains(Path.GetFileName(fileName)); } + + /// + /// Determines if a file should use detached signatures based on certificate configuration. + /// + /// The file to check + /// True if the file should use detached signatures + private bool ShouldUseDetachedSignature(PathWithHash file, SignInfo signInfo) + { + // Check if the certificate is configured for detached signatures + if (signInfo.Certificate != null && _additionalCertificateInformation.TryGetValue(signInfo.Certificate, out var additionalInfo)) + { + var additionalCertInfo = additionalInfo.FirstOrDefault(a => string.IsNullOrEmpty(a.CollisionPriorityId) || + a.CollisionPriorityId == signInfo.CollisionPriorityId); + if (additionalCertInfo != null && additionalCertInfo.GeneratesDetachedSignature) + { + _log.LogMessage(MessageImportance.Low, $"File {file.FileName} will use detached signatures based on certificate configuration"); + return true; + } + } + + return false; + } } } diff --git a/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs b/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs index b8c775ddb36..14f4531cbcf 100644 --- a/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs +++ b/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs @@ -25,20 +25,6 @@ public ExplicitCertificateKey(string fileName, string publicKeyToken = null, str ExecutableType = executableType; } - private static ExecutableType ParseExecutableType(string executableType) - { - if (string.IsNullOrEmpty(executableType)) - return ExecutableType.None; - - return executableType switch - { - "PE" => ExecutableType.PE, - "MachO" => ExecutableType.MachO, - "ELF" => ExecutableType.ELF, - _ => ExecutableType.None - }; - } - public override bool Equals(object obj) => obj is ExplicitCertificateKey key && Equals(key); diff --git a/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs b/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs index 62e5c8636f6..47062c119c6 100644 --- a/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs +++ b/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs @@ -17,6 +17,9 @@ internal readonly struct FileSignInfo internal readonly SignInfo SignInfo; internal ImmutableArray ContentHash => File.ContentHash; internal readonly string WixContentFilePath; + internal string DetachedSignatureFilePath => $"{FileName}.sig"; + internal string DetachedSignatureFullPath => $"{FullPath}.sig"; + internal readonly PathWithHash File; // optional file information that allows to disambiguate among multiple files with the same name: diff --git a/src/Microsoft.DotNet.SignTool/src/SignInfo.cs b/src/Microsoft.DotNet.SignTool/src/SignInfo.cs index a6bb1087218..2f618337432 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignInfo.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignInfo.cs @@ -18,6 +18,11 @@ internal readonly struct SignInfo /// public static readonly SignInfo AlreadySigned = new SignInfo(ignoreThisFile: false, alreadySigned: true, isAlreadyStrongNamed: false); + /// + /// Used to flag that the file should generate a detached signature. + /// + public static readonly SignInfo DetachedSignature = new SignInfo(ignoreThisFile: false, alreadySigned: false, isAlreadyStrongNamed: false, generatesDetachedSignature: true); + /// /// The authenticode certificate which should be used to sign the binary. This can be null /// in cases where we have a zip container where the contents are signed but not the actual @@ -42,6 +47,11 @@ internal readonly struct SignInfo internal bool IsAlreadySigned { get; } + /// + /// True if this file should generate a detached signature rather than being signed in-place. + /// + internal bool GeneratesDetachedSignature { get; } + /// /// This is used to decide what SignInfos to use in the case of a collision. In case of a collision /// we'll use the lower value since it would map a lower node in the graph and has precedence @@ -57,7 +67,7 @@ internal readonly struct SignInfo public bool ShouldNotarize => !string.IsNullOrEmpty(NotarizationAppName) && !ShouldIgnore; - public SignInfo(string certificate, string strongName, string notarizationAppName, string collisionPriorityId, bool shouldIgnore, bool isAlreadySigned, bool isAlreadyStrongNamed) + private SignInfo(string certificate, string strongName, string notarizationAppName, string collisionPriorityId, bool shouldIgnore, bool isAlreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false) { ShouldIgnore = shouldIgnore; IsAlreadySigned = isAlreadySigned; @@ -66,10 +76,11 @@ public SignInfo(string certificate, string strongName, string notarizationAppNam CollisionPriorityId = collisionPriorityId; IsAlreadyStrongNamed = isAlreadyStrongNamed; NotarizationAppName = notarizationAppName; + GeneratesDetachedSignature = generatesDetachedSignature; } - private SignInfo(bool ignoreThisFile, bool alreadySigned, bool isAlreadyStrongNamed) - : this(certificate: null, strongName: null, notarizationAppName: null, collisionPriorityId: null, ignoreThisFile, alreadySigned, isAlreadyStrongNamed) + private SignInfo(bool ignoreThisFile, bool alreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false) + : this(certificate: null, strongName: null, notarizationAppName: null, collisionPriorityId: null, ignoreThisFile, alreadySigned, isAlreadyStrongNamed, generatesDetachedSignature) { } @@ -79,20 +90,23 @@ internal SignInfo(string certificate, string strongName = null, string notarizat } internal SignInfo WithCertificateName(string value, string collisionPriorityId) - => new SignInfo(value, StrongName, NotarizationAppName, collisionPriorityId, false, false, IsAlreadyStrongNamed); + => new SignInfo(value, StrongName, NotarizationAppName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature); internal SignInfo WithNotarization(string appName, string collisionPriorityId) - => new SignInfo(Certificate, StrongName, appName, collisionPriorityId, false, false, IsAlreadyStrongNamed); + => new SignInfo(Certificate, StrongName, appName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature); internal SignInfo WithCollisionPriorityId(string collisionPriorityId) - => new SignInfo(Certificate, StrongName, NotarizationAppName, collisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed); + => new SignInfo(Certificate, StrongName, NotarizationAppName, collisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed, GeneratesDetachedSignature); internal SignInfo WithIsAlreadySigned(bool value = false) => Certificate != null ? - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, value, value, IsAlreadyStrongNamed) : - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, true, value, IsAlreadyStrongNamed); + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, value, value, IsAlreadyStrongNamed, GeneratesDetachedSignature) : + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, true, value, IsAlreadyStrongNamed, GeneratesDetachedSignature); internal SignInfo WithIsAlreadyStrongNamed(bool value = false) => - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, value); + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, value, GeneratesDetachedSignature); + + internal SignInfo WithDetachedSignature(string certificate) + => new SignInfo(certificate, StrongName, NotarizationAppName, CollisionPriorityId, false, false, IsAlreadyStrongNamed, true); } } diff --git a/src/Microsoft.DotNet.SignTool/src/SignTool.cs b/src/Microsoft.DotNet.SignTool/src/SignTool.cs index 6aa74d2ccc1..6b88e6d156b 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignTool.cs @@ -13,6 +13,7 @@ using Microsoft.Build.Utilities; using NuGet.Packaging; using Microsoft.DotNet.StrongName; +using System.ComponentModel; namespace Microsoft.DotNet.SignTool { @@ -148,15 +149,37 @@ private bool AuthenticodeSignAndNotarize(IBuildEngine buildEngine, int round, IE var zippedPaths = ZipMacFiles(filesToSign); - // First the signing pass - var signProjectPath = Path.Combine(dir, $"Round{round}-Sign.proj"); - File.WriteAllText(signProjectPath, GenerateBuildFileContent(filesToSign, zippedPaths, false)); - string signingLogName = $"SigningRound{round}"; - status = RunMSBuild(buildEngine, signProjectPath, Path.Combine(_args.LogDir, $"{signingLogName}.binlog"), Path.Combine(_args.LogDir, $"{signingLogName}.log"), Path.Combine(_args.LogDir, $"{signingLogName}.error.log")); + // Identify files that need detached signatures + var detachedSignatureFiles = filesToSign.Where(f => f.SignInfo.GeneratesDetachedSignature).ToList(); + var originalFileBackups = new Dictionary(); - if (!status) + try { - return false; + PrepareDetachedSignatureFiles(detachedSignatureFiles, originalFileBackups); + + var signProjectPath = Path.Combine(dir, $"Round{round}-Sign.proj"); + File.WriteAllText(signProjectPath, GenerateBuildFileContent(filesToSign, zippedPaths, false)); + string signingLogName = $"SigningRound{round}"; + status = RunMSBuild(buildEngine, signProjectPath, Path.Combine(_args.LogDir, $"{signingLogName}.binlog"), Path.Combine(_args.LogDir, $"{signingLogName}.log"), Path.Combine(_args.LogDir, $"{signingLogName}.error.log")); + + if (!status) + { + return false; + } + + // After signing, handle detached signatures + CompleteDetachedSignatures(detachedSignatureFiles, originalFileBackups); + } + finally + { + // Delete any original detached signature files + foreach (var backupPath in originalFileBackups.Values) + { + if (File.Exists(backupPath)) + { + File.Delete(backupPath); + } + } } // Now unzip. Notarization does not expect zipped packages. @@ -175,6 +198,47 @@ private bool AuthenticodeSignAndNotarize(IBuildEngine buildEngine, int round, IE return status; } + /// + /// Copies the signed content to the .sig file and restores the original file. + /// + /// + /// + private void CompleteDetachedSignatures(List detachedSignatureFiles, Dictionary originalFileBackups) + { + foreach (var fileInfo in detachedSignatureFiles) + { + // Copy the signed content to .sig file + File.Copy(fileInfo.FullPath, fileInfo.DetachedSignatureFullPath); + _log.LogMessage($"Created detached signature file: {fileInfo.DetachedSignatureFullPath}"); + + // Restore the original file + string backupPath = originalFileBackups[fileInfo.FullPath]; + File.Copy(backupPath, fileInfo.FullPath, overwrite: true); + _log.LogMessage($"Restored original file: {fileInfo.FullPath}"); + } + } + + /// + /// Creates backup copies of the specified files to prepare for detached signature operations. + /// + /// Each file is backed up by copying it to a new file with the ".original" extension + /// appended to its path. The method updates the provided dictionary to allow later restoration of the original + /// files if needed. + /// A list of file information objects representing the files for which detached signature backups will be + /// created. Each file in the list will be copied to a backup location. + /// A dictionary that will be populated with mappings from the original file paths to their corresponding backup + /// file paths. The dictionary is updated in place. + private void PrepareDetachedSignatureFiles(List detachedSignatureFiles, Dictionary originalFileBackups) + { + foreach (var fileInfo in detachedSignatureFiles) + { + string backupPath = fileInfo.FullPath + ".original"; + File.Copy(fileInfo.FullPath, backupPath); + originalFileBackups[fileInfo.FullPath] = backupPath; + _log.LogMessage($"Backed up original file for detached signature: {fileInfo.FullPath} -> {backupPath}"); + } + } + private string GenerateBuildFileContent(IEnumerable filesToSign, Dictionary zippedPaths, bool notarize) { var builder = new StringBuilder(); diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs index b999bbd6502..14d65f5bf0f 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs @@ -336,6 +336,8 @@ private Dictionary> ParseAddition var macSigningOperation = certificateSignInfo.GetMetadata("MacCertificate"); var macNotarizationAppName = certificateSignInfo.GetMetadata("MacNotarizationAppName"); var collisionPriorityId = certificateSignInfo.GetMetadata(SignToolConstants.CollisionPriorityId); + var detachedSignatureCertificate = certificateSignInfo.GetMetadata("DetachedSignature"); + bool detachedSignatureCertificateValue = false; if (string.IsNullOrEmpty(macSigningOperation) != string.IsNullOrEmpty(macNotarizationAppName)) { @@ -348,11 +350,18 @@ private Dictionary> ParseAddition continue; } + if (!string.IsNullOrEmpty(detachedSignatureCertificate) && !bool.TryParse(detachedSignatureCertificate, out detachedSignatureCertificateValue)) + { + Log.LogError($"DetachedSignature must be 'true' or 'false"); + continue; + } + var additionalCertInfo = new AdditionalCertificateInformation { DualSigningAllowed = dualSignAllowedValue, MacSigningOperation = macSigningOperation, MacNotarizationAppName = macNotarizationAppName, + GeneratesDetachedSignature = detachedSignatureCertificateValue, CollisionPriorityId = collisionPriorityId }; From 32a401ae573e0b2613c0ec9ce5bb0052d7b79147 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 10:55:37 -0700 Subject: [PATCH 026/100] [release/10.0] Update SetupNugetSource.ps1/sh with new functionality and model (#16205) Co-authored-by: Matt Mitchell (.NET) --- Arcade.slnx | 1 + eng/common/SetupNugetSources.ps1 | 71 +++--- eng/common/SetupNugetSources.sh | 175 ++++++++------ .../BoundaryConditionTests.cs | 186 +++++++++++++++ .../CredentialHandlingTests.cs | 215 ++++++++++++++++++ .../FeedEnablingTests.cs | 179 +++++++++++++++ .../InternalFeedAdditionTests.cs | 149 ++++++++++++ ...soft.DotNet.SetupNugetSources.Tests.csproj | 25 ++ .../NoChangeScenarioTests.cs | 86 +++++++ .../NuGetConfigAssertions.cs | 190 ++++++++++++++++ .../ScriptRunner.cs | 125 ++++++++++ .../SetupNugetSourcesFixture.cs | 65 ++++++ 12 files changed, 1362 insertions(+), 105 deletions(-) create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs create mode 100644 src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs diff --git a/Arcade.slnx b/Arcade.slnx index a4c51eaafd6..337ee9c2279 100644 --- a/Arcade.slnx +++ b/Arcade.slnx @@ -29,6 +29,7 @@ + diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index 9445c314325..fc8d618014e 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -7,7 +7,7 @@ # See example call for this script below. # # - task: PowerShell@2 -# displayName: Setup Private Feeds Credentials +# displayName: Setup internal Feeds Credentials # condition: eq(variables['Agent.OS'], 'Windows_NT') # inputs: # filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 @@ -34,19 +34,28 @@ Set-StrictMode -Version 2.0 . $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 + } +} + # Add source entry to PackageSources function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { $packageSource = $sources.SelectSingleNode("add[@key='$SourceName']") if ($packageSource -eq $null) { + Write-Host "Adding package source $SourceName" + $packageSource = $doc.CreateElement("add") $packageSource.SetAttribute("key", $SourceName) $packageSource.SetAttribute("value", $SourceEndPoint) $sources.AppendChild($packageSource) | Out-Null } else { - Write-Host "Package source $SourceName already present." + Write-Host "Package source $SourceName already present and enabled." } AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd @@ -59,6 +68,8 @@ function AddCredential($creds, $source, $username, $pwd) { return; } + Write-Host "Inserting credential for feed: " $source + # Looks for credential configuration for the given SourceName. Create it if none is found. $sourceElement = $creds.SelectSingleNode($Source) if ($sourceElement -eq $null) @@ -91,24 +102,27 @@ function AddCredential($creds, $source, $username, $pwd) { $passwordElement.SetAttribute("value", $pwd) } -function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $pwd) { - $maestroPrivateSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]") - - Write-Host "Inserting credentials for $($maestroPrivateSources.Count) Maestro's private feeds." - - ForEach ($PackageSource in $maestroPrivateSources) { - Write-Host "`tInserting credential for Maestro's feed:" $PackageSource.Key - AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -pwd $pwd +# Enable all darc-int package sources. +function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds) { + $maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") + ForEach ($DisabledPackageSource in $maestroInternalSources) { + EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key } } -function EnablePrivatePackageSources($DisabledPackageSources) { - $maestroPrivateSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") - ForEach ($DisabledPackageSource in $maestroPrivateSources) { - Write-Host "`tEnsuring private source '$($DisabledPackageSource.key)' is enabled by deleting it from disabledPackageSource" +# 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) { + $DisabledPackageSource = $DisabledPackageSources.SelectSingleNode("add[@key='$PackageSourceName']") + if ($DisabledPackageSource) { + Write-Host "Enabling internal source '$($DisabledPackageSource.key)'." + # 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 + return $true } + return $false } if (!(Test-Path $ConfigFile -PathType Leaf)) { @@ -121,15 +135,17 @@ $doc = New-Object System.Xml.XmlDocument $filename = (Get-Item $ConfigFile).FullName $doc.Load($filename) -# Get reference to or create one if none exist already +# Get reference to - fail if none exist $sources = $doc.DocumentElement.SelectSingleNode("packageSources") if ($sources -eq $null) { - $sources = $doc.CreateElement("packageSources") - $doc.DocumentElement.AppendChild($sources) | Out-Null + Write-PipelineTelemetryError -Category 'Build' -Message "Eng/common/SetupNugetSources.ps1 returned a non-zero exit code. NuGet config file must contain a packageSources section: $ConfigFile" + ExitWithExitCode 1 } $creds = $null +$feedSuffix = "v3/index.json" if ($Password) { + $feedSuffix = "v2" # Looks for a node. Create it if none is found. $creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials") if ($creds -eq $null) { @@ -138,33 +154,22 @@ if ($Password) { } } +$userName = "dn-bot" + # Check for disabledPackageSources; we'll enable any darc-int ones we find there $disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources") if ($disabledSources -ne $null) { Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node" - EnablePrivatePackageSources -DisabledPackageSources $disabledSources -} - -$userName = "dn-bot" - -# Insert credential nodes for Maestro's private feeds -InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -pwd $Password - -# 3.1 uses a different feed url format so it's handled differently here -$dotnet31Source = $sources.SelectSingleNode("add[@key='dotnet3.1']") -if ($dotnet31Source -ne $null) { - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password + EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds } - $dotnetVersions = @('5','6','7','8','9','10') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; $dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']") if ($dotnetSource -ne $null) { - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -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 -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 } } diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index ddf4efc81a4..dd2564aef01 100755 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -52,78 +52,126 @@ if [[ `uname -s` == "Darwin" ]]; then TB='' fi -# Ensure there is a ... section. -grep -i "" $ConfigFile -if [ "$?" != "0" ]; then - echo "Adding ... section." - ConfigNodeHeader="" - PackageSourcesTemplate="${TB}${NL}${TB}" +# Enables an internal package source by name, if found. Returns 0 if found and enabled, 1 if not found. +EnableInternalPackageSource() { + local PackageSourceName="$1" + + # Check if disabledPackageSources section exists + grep -i "" "$ConfigFile" > /dev/null + if [ "$?" != "0" ]; then + return 1 # No disabled sources section + fi + + # Check if this source name is disabled + grep -i " /dev/null + if [ "$?" == "0" ]; then + echo "Enabling internal source '$PackageSourceName'." + # Remove the disabled entry + local OldDisableValue="" + local NewDisableValue="" + sed -i.bak "s|$OldDisableValue|$NewDisableValue|" "$ConfigFile" + + # Add the source name to PackageSources for credential handling + PackageSources+=("$PackageSourceName") + return 0 # Found and enabled + fi + + return 1 # Not found in disabled sources +} + +# Add source entry to PackageSources +AddPackageSource() { + local SourceName="$1" + local SourceEndPoint="$2" + + # Check if source already exists + grep -i " /dev/null + if [ "$?" == "0" ]; then + echo "Package source $SourceName already present and enabled." + PackageSources+=("$SourceName") + return + fi + + echo "Adding package source $SourceName" + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" "$ConfigFile" + PackageSources+=("$SourceName") +} + +# Adds or enables the package source with the given name +AddOrEnablePackageSource() { + local SourceName="$1" + local SourceEndPoint="$2" + + # Try to enable if disabled, if not found then add new source + EnableInternalPackageSource "$SourceName" + if [ "$?" != "0" ]; then + AddPackageSource "$SourceName" "$SourceEndPoint" + fi +} - sed -i.bak "s|$ConfigNodeHeader|$ConfigNodeHeader${NL}$PackageSourcesTemplate|" $ConfigFile -fi +# Enable all darc-int package sources +EnableMaestroInternalPackageSources() { + # Check if disabledPackageSources section exists + grep -i "" "$ConfigFile" > /dev/null + if [ "$?" != "0" ]; then + return # No disabled sources section + fi + + # Find all darc-int disabled sources + local DisabledDarcIntSources=() + DisabledDarcIntSources+=$(grep -oh '"darc-int-[^"]*" value="true"' "$ConfigFile" | tr -d '"') + + for DisabledSourceName in ${DisabledDarcIntSources[@]} ; do + if [[ $DisabledSourceName == darc-int* ]]; then + EnableInternalPackageSource "$DisabledSourceName" + fi + done +} -# Ensure there is a ... section. -grep -i "" $ConfigFile +# Ensure there is a ... section. +grep -i "" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding ... section." - - PackageSourcesNodeFooter="" - PackageSourceCredentialsTemplate="${TB}${NL}${TB}" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourcesNodeFooter${NL}$PackageSourceCredentialsTemplate|" $ConfigFile + Write-PipelineTelemetryError -Category 'Build' "Error: Eng/common/SetupNugetSources.sh returned a non-zero exit code. NuGet config file must contain a packageSources section: $ConfigFile" + ExitWithExitCode 1 fi PackageSources=() -# Ensure dotnet3.1-internal and dotnet3.1-internal-transport are in the packageSources if the public dotnet3.1 feeds are present -grep -i "... section. + grep -i "" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding dotnet3.1-internal to the packageSources." - PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" + echo "Adding ... section." - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=('dotnet3.1-internal') - - grep -i "" $ConfigFile - if [ "$?" != "0" ]; then - echo "Adding dotnet3.1-internal-transport to the packageSources." PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" + PackageSourceCredentialsTemplate="${TB}${NL}${TB}" - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourcesNodeFooter${NL}$PackageSourceCredentialsTemplate|" $ConfigFile fi - PackageSources+=('dotnet3.1-internal-transport') +fi + +# Check for disabledPackageSources; we'll enable any darc-int ones we find there +grep -i "" $ConfigFile > /dev/null +if [ "$?" == "0" ]; then + echo "Checking for any darc-int disabled package sources in the disabledPackageSources node" + EnableMaestroInternalPackageSources fi DotNetVersions=('5' '6' '7' '8' '9' '10') for DotNetVersion in ${DotNetVersions[@]} ; do FeedPrefix="dotnet${DotNetVersion}"; - grep -i " /dev/null if [ "$?" == "0" ]; then - grep -i "" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=("$FeedPrefix-internal") - - grep -i "" $ConfigFile - if [ "$?" != "0" ]; then - echo "Adding $FeedPrefix-internal-transport to the packageSources." - PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=("$FeedPrefix-internal-transport") + AddOrEnablePackageSource "$FeedPrefix-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$FeedPrefix-internal/nuget/$FeedSuffix" + AddOrEnablePackageSource "$FeedPrefix-internal-transport" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$FeedPrefix-internal-transport/nuget/$FeedSuffix" fi done @@ -139,29 +187,12 @@ if [ "$CredToken" ]; then # Check if there is no existing credential for this FeedName grep -i "<$FeedName>" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding credentials for $FeedName." + echo " Inserting credential for feed: $FeedName" PackageSourceCredentialsNodeFooter="" - NewCredential="${TB}${TB}<$FeedName>${NL}${NL}${NL}" + NewCredential="${TB}${TB}<$FeedName>${NL}${TB}${NL}${TB}${TB}${NL}${TB}${TB}" sed -i.bak "s|$PackageSourceCredentialsNodeFooter|$NewCredential${NL}$PackageSourceCredentialsNodeFooter|" $ConfigFile fi done fi - -# Re-enable any entries in disabledPackageSources where the feed name contains darc-int -grep -i "" $ConfigFile -if [ "$?" == "0" ]; then - DisabledDarcIntSources=() - echo "Re-enabling any disabled \"darc-int\" package sources in $ConfigFile" - DisabledDarcIntSources+=$(grep -oh '"darc-int-[^"]*" value="true"' $ConfigFile | tr -d '"') - for DisabledSourceName in ${DisabledDarcIntSources[@]} ; do - if [[ $DisabledSourceName == darc-int* ]] - then - OldDisableValue="" - NewDisableValue="" - sed -i.bak "s|$OldDisableValue|$NewDisableValue|" $ConfigFile - echo "Neutralized disablePackageSources entry for '$DisabledSourceName'" - fi - done -fi diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs new file mode 100644 index 00000000000..96366159bed --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class BoundaryConditionTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public BoundaryConditionTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task EmptyConfiguration_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithoutPackageSourcesSection_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithMissingDisabledPackageSourcesSection_StillAddsInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should still add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + } + + [Fact] + public async Task NonExistentConfigFile_ReturnsError() + { + // Arrange + var nonExistentPath = Path.Combine(_testOutputDirectory, "nonexistent.config"); + // Act + var result = await _scriptRunner.RunScript(nonExistentPath); + + // Assert + result.exitCode.Should().Be(1, "should return error code for nonexistent file"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("Couldn't find the NuGet config file", "should report missing file error"); + } + + [Fact] + public async Task ConfigWithOnlyDisabledSources_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithEmptyPackageSourcesSection_HandlesCorrectly() + { + // Arrange + var originalConfig = @" + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should enable darc-int feeds but not add any dotnet internal feeds since no dotnet feeds exist + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "should enable darc-int feed"); + modifiedConfig.GetPackageSourceCount().Should().Be(0, "should not add dotnet internal feeds without dotnet public feeds"); + } + } +} + + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs new file mode 100644 index 00000000000..a9cf811728b --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class CredentialHandlingTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public CredentialHandlingTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task ConfigWithCredentialProvided_AddsCredentialsForInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + + // Should add credentials for internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed"); + + // Should use v2 endpoints when credentials are provided + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2", + "should use v2 endpoint when credentials provided"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2", + "should use v2 endpoint when credentials provided"); + } + + [Fact] + public async Task ConfigWithNoCredential_DoesNotAddCredentials() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act - No credential provided + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + + // Should NOT add credentials + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal", "should not add credentials without credential"); + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential"); + + // Should use v3 endpoints when no credentials are provided + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should use v3 endpoint when no credentials provided"); + } + + [Fact] + public async Task ConfigWithExistingCredentials_PreservesAndAddsNew() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should preserve existing credentials + modifiedConfig.ShouldContainCredentials("existing-private", "existing-user", "should preserve existing credentials"); + + // Should add new credentials for internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for new internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for new transport feed"); + } + + [Fact] + public async Task ConfigWithDarcIntFeeds_AddsCredentialsForEnabledFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should enable the darc-int feed + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + + // Should add credentials for enabled darc-int feed + modifiedConfig.ShouldContainCredentials("darc-int-dotnet-roslyn-12345", "dn-bot", "should add credentials for enabled darc-int feed"); + + // Should add credentials for new internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed"); + } + + [Fact] + public async Task ConfigWithNoCredentialButExistingCredentials_DoesNotRemoveExistingCredentials() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act - No credential provided + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should preserve existing credentials + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should preserve existing credentials"); + + // Should not add credentials for new feeds without credential + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential"); + } + } +} diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs new file mode 100644 index 00000000000..7af694a1db8 --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class FeedEnablingTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public FeedEnablingTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task ConfigWithDisabledDarcIntFeeds_EnablesFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Darc-int feeds should no longer be disabled + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-runtime-67890", "darc-int feed should be enabled"); + + // Should also add internal feeds for dotnet6 + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should add dotnet6-internal feed"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add dotnet6-internal-transport feed"); + } + + [Fact] + public async Task ConfigWithMixedDisabledFeeds_OnlyEnablesDarcIntFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Darc-int feeds should be enabled + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-runtime-67890", "darc-int feed should be enabled"); + + // Non-darc-int feeds should remain disabled + modifiedConfig.ShouldBeDisabled("some-other-feed", "non-darc-int feed should remain disabled"); + modifiedConfig.ShouldBeDisabled("another-disabled-feed", "non-darc-int feed should remain disabled"); + } + + [Fact] + public async Task ConfigWithDisabledInternalFeed_EnablesExistingInsteadOfAdding() + { + // Arrange + var originalConfig = @" + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // The dotnet6-internal feed should be enabled (removed from disabled sources) + modifiedConfig.ShouldNotBeDisabled("dotnet6-internal", "internal feed should be enabled"); + + // Should still add the transport feed + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add transport feed"); + + // Should have 4 package sources (original 3, with dotnet6-internal enabled + transport added) + modifiedConfig.GetPackageSourceCount().Should().Be(4, "should enable existing feed and add transport feed"); + } + + [Fact] + public async Task ConfigWithNoDisabledSources_StillAddsInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds even without disabled sources section + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should add dotnet6-internal feed"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add dotnet6-internal-transport feed"); + } + } +} diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs new file mode 100644 index 00000000000..ca42421c401 --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class InternalFeedAdditionTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public InternalFeedAdditionTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Theory] + [InlineData("dotnet5")] + [InlineData("dotnet6")] + [InlineData("dotnet7")] + [InlineData("dotnet8")] + [InlineData("dotnet9")] + [InlineData("dotnet10")] + public async Task ConfigWithSpecificDotNetVersion_AddsCorrespondingInternalFeeds(string dotnetVersion) + { + // Arrange + var originalConfig = $@" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldContainPackageSource($"{dotnetVersion}-internal", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{dotnetVersion}-internal/nuget/v3/index.json", + $"should add {dotnetVersion}-internal feed"); + modifiedConfig.ShouldContainPackageSource($"{dotnetVersion}-internal-transport", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{dotnetVersion}-internal-transport/nuget/v3/index.json", + $"should add {dotnetVersion}-internal-transport feed"); + } + + [Fact] + public async Task ConfigWithMultipleDotNetVersions_AddsAllInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds for all versions + var versions = new[] { "dotnet5", "dotnet6", "dotnet7", "dotnet8", "dotnet9", "dotnet10" }; + foreach (var version in versions) + { + modifiedConfig.ShouldContainPackageSource($"{version}-internal", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{version}-internal/nuget/v3/index.json", + $"should add {version}-internal feed"); + modifiedConfig.ShouldContainPackageSource($"{version}-internal-transport", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{version}-internal-transport/nuget/v3/index.json", + $"should add {version}-internal-transport feed"); + } + + // Original count (7 sources) + 12 internal sources = 19 total + modifiedConfig.GetPackageSourceCount().Should().Be(19, "should have all original sources plus internal feeds"); + } + + [Fact] + public async Task ConfigWithExistingInternalFeed_DoesNotDuplicate() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should still contain the dotnet6-internal feed (only once) + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "existing internal feed should be preserved"); + + // Should add the missing transport feed + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add missing transport feed"); + + // Should have 4 total sources (3 original + 1 added transport) + modifiedConfig.GetPackageSourceCount().Should().Be(4, "should not duplicate existing sources"); + } + } +} + + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj b/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj new file mode 100644 index 00000000000..83d868c3bdc --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj @@ -0,0 +1,25 @@ + + + + $(NetToolCurrent) + true + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs new file mode 100644 index 00000000000..632511d3441 --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class NoChangeScenarioTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public NoChangeScenarioTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + + + [Fact] + public async Task BasicConfig_NoChanges() + { + // Arrange + var originalConfig = @" + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + modifiedConfig.ShouldBeSemanticallySame(originalConfig, "basic config with no special feeds should not be modified"); + } + + [Fact] + public async Task ConfigWithNonDotNetFeeds_NoChanges() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + modifiedConfig.ShouldBeSemanticallySame(originalConfig, "config with non-dotnet feeds should not be modified"); + } + } +} + + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs new file mode 100644 index 00000000000..0bd0f5b5dfd --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs @@ -0,0 +1,190 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Linq; +using FluentAssertions; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public static class NuGetConfigAssertions + { + /// + /// Compares two NuGet.config files for semantic equality, ignoring whitespace differences + /// + public static void ShouldBeSemanticallySame(this string actualContent, string expectedContent, string because = "") + { + var actualNormalized = NormalizeXml(actualContent); + var expectedNormalized = NormalizeXml(expectedContent); + + actualNormalized.Should().Be(expectedNormalized, because); + } + + /// + /// Asserts that the config contains a package source with the specified key + /// + public static void ShouldContainPackageSource(this string configContent, string key, string value = null, string because = "") + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + packageSources.Should().NotBeNull($"packageSources section should exist {because}"); + + var source = packageSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == key); + source.Should().NotBeNull($"package source '{key}' should exist {because}"); + + if (value != null) + { + source.Attribute("value")?.Value.Should().Be(value, $"package source '{key}' should have the correct value {because}"); + } + } + + /// + /// Asserts that the config does not contain a package source with the specified key + /// + public static void ShouldNotContainPackageSource(this string configContent, string key, string because = "") + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + + if (packageSources != null) + { + var source = packageSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == key); + source.Should().BeNull($"package source '{key}' should not exist {because}"); + } + } + + /// + /// Asserts that the config contains credentials for the specified source + /// + public static void ShouldContainCredentials(this string configContent, string sourceName, string username = null, string because = "") + { + var doc = XDocument.Parse(configContent); + var credentials = doc.Root?.Element("packageSourceCredentials"); + credentials.Should().NotBeNull($"packageSourceCredentials section should exist {because}"); + + var sourceCredentials = credentials.Element(sourceName); + sourceCredentials.Should().NotBeNull($"credentials for '{sourceName}' should exist {because}"); + + if (username != null) + { + var usernameElement = sourceCredentials.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == "Username"); + usernameElement.Should().NotBeNull($"username credential should exist for '{sourceName}' {because}"); + usernameElement.Attribute("value")?.Value.Should().Be(username, $"username should match for '{sourceName}' {because}"); + } + + var passwordElement = sourceCredentials.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == "ClearTextPassword"); + passwordElement.Should().NotBeNull($"password credential should exist for '{sourceName}' {because}"); + } + + /// + /// Asserts that the config does not contain credentials for the specified source + /// + public static void ShouldNotContainCredentials(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var credentials = doc.Root?.Element("packageSourceCredentials"); + + if (credentials != null) + { + var sourceCredentials = credentials.Element(sourceName); + sourceCredentials.Should().BeNull($"credentials for '{sourceName}' should not exist {because}"); + } + } + + /// + /// Asserts that a source is not in the disabled sources list + /// + public static void ShouldNotBeDisabled(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var disabledSources = doc.Root?.Element("disabledPackageSources"); + + if (disabledSources != null) + { + var disabledSource = disabledSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == sourceName); + disabledSource.Should().BeNull($"source '{sourceName}' should not be disabled {because}"); + } + } + + /// + /// Asserts that a source is in the disabled sources list + /// + public static void ShouldBeDisabled(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var disabledSources = doc.Root?.Element("disabledPackageSources"); + disabledSources.Should().NotBeNull($"disabledPackageSources section should exist {because}"); + + var disabledSource = disabledSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == sourceName); + disabledSource.Should().NotBeNull($"source '{sourceName}' should be disabled {because}"); + } + + /// + /// Counts the number of package sources in the config + /// + public static int GetPackageSourceCount(this string configContent) + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + return packageSources?.Elements("add").Count() ?? 0; + } + + /// + /// Normalizes XML content for comparison by removing whitespace differences and sorting elements consistently + /// + private static string NormalizeXml(string xmlContent) + { + var doc = XDocument.Parse(xmlContent); + + // Sort package sources by key for consistent comparison + var packageSources = doc.Root?.Element("packageSources"); + if (packageSources != null) + { + var sortedSources = packageSources.Elements("add") + .OrderBy(e => e.Attribute("key")?.Value) + .ToList(); + packageSources.RemoveAll(); + foreach (var source in sortedSources) + { + packageSources.Add(source); + } + } + + // Sort disabled sources by key + var disabledSources = doc.Root?.Element("disabledPackageSources"); + if (disabledSources != null) + { + var sortedDisabled = disabledSources.Elements("add") + .OrderBy(e => e.Attribute("key")?.Value) + .ToList(); + disabledSources.RemoveAll(); + foreach (var source in sortedDisabled) + { + disabledSources.Add(source); + } + } + + // Sort credentials by source name + var credentials = doc.Root?.Element("packageSourceCredentials"); + if (credentials != null) + { + var sortedCredentials = credentials.Elements() + .OrderBy(e => e.Name.LocalName) + .ToList(); + credentials.RemoveAll(); + foreach (var cred in sortedCredentials) + { + credentials.Add(cred); + } + } + + return doc.ToString(SaveOptions.DisableFormatting); + } + } +} + + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs new file mode 100644 index 00000000000..37dd8cb9d37 --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public enum ScriptType + { + PowerShell, + Shell + } + + public class ScriptRunner + { + private readonly string _repoRoot; + + public ScriptRunner(string repoRoot) + { + _repoRoot = repoRoot ?? throw new ArgumentNullException(nameof(repoRoot)); + } + + public async Task<(int exitCode, string output, string error)> RunPowerShellScript(string configFilePath, string password = null) + { + var scriptPath = Path.Combine(_repoRoot, "eng", "common", "SetupNugetSources.ps1"); + var arguments = $"-ExecutionPolicy Bypass -File \"{scriptPath}\" -ConfigFile \"{configFilePath}\""; + + if (!string.IsNullOrEmpty(password)) + { + arguments += $" -Password \"{password}\""; + } + + return await RunProcess("powershell.exe", arguments, _repoRoot); + } + + public async Task<(int exitCode, string output, string error)> RunShellScript(string configFilePath, string credToken = null) + { + var scriptPath = Path.Combine(_repoRoot, "eng", "common", "SetupNugetSources.sh"); + + // Make script executable if on Unix + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + await RunProcess("chmod", $"+x \"{scriptPath}\"", _repoRoot); + } + + var arguments = $"\"{scriptPath}\" \"{configFilePath}\""; + if (!string.IsNullOrEmpty(credToken)) + { + arguments += $" \"{credToken}\""; + } + + var shell = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bash.exe" : "/bin/bash"; + return await RunProcess(shell, arguments, _repoRoot); + } + + private async Task<(int exitCode, string output, string error)> RunProcess(string fileName, string arguments, string workingDirectory = null) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory() + } + }; + + var outputBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + + process.OutputDataReceived += (sender, e) => + { + if (e.Data != null) + { + outputBuilder.AppendLine(e.Data); + } + }; + + process.ErrorDataReceived += (sender, e) => + { + if (e.Data != null) + { + errorBuilder.AppendLine(e.Data); + } + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + await Task.Run(() => process.WaitForExit()); + + return (process.ExitCode, outputBuilder.ToString(), errorBuilder.ToString()); + } + + public async Task<(int exitCode, string output, string error)> RunScript(string configFilePath, string credential = null) + { + var scriptType = GetPlatformAppropriateScriptType(); + switch (scriptType) + { + case ScriptType.PowerShell: + return await RunPowerShellScript(configFilePath, credential); + case ScriptType.Shell: + return await RunShellScript(configFilePath, credential); + default: + throw new ArgumentException($"Unsupported script type: {scriptType}"); + } + } + + public static ScriptType GetPlatformAppropriateScriptType() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ScriptType.PowerShell : ScriptType.Shell; + } + } +} + diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs new file mode 100644 index 00000000000..7e25ccd1b88 --- /dev/null +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + /// + /// xUnit fixture that prepares a temporary repository root containing the + /// scaffolded files (global.json + eng/common scripts) copied from the + /// build output's RepoScaffold directory. Shared per test class. + /// + public class SetupNugetSourcesFixture : IDisposable + { + public string RepoRoot { get; } + public ScriptRunner ScriptRunner { get; } + + public SetupNugetSourcesFixture() + { + var scaffoldRoot = Path.Combine(AppContext.BaseDirectory, "RepoScaffold"); + if (!Directory.Exists(scaffoldRoot)) + { + throw new InvalidOperationException($"Expected scaffold directory not found: {scaffoldRoot}"); + } + + RepoRoot = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTestRepo", Guid.NewGuid().ToString()); + CopyDirectory(scaffoldRoot, RepoRoot); + + ScriptRunner = new ScriptRunner(RepoRoot); + } + + private static void CopyDirectory(string sourceDir, string destinationDir) + { + foreach (var dir in Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories)) + { + var relative = Path.GetRelativePath(sourceDir, dir); + Directory.CreateDirectory(Path.Combine(destinationDir, relative)); + } + + foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)) + { + var relative = Path.GetRelativePath(sourceDir, file); + var destPath = Path.Combine(destinationDir, relative); + Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); + File.Copy(file, destPath, overwrite: true); + } + } + + public void Dispose() + { + try + { + if (Directory.Exists(RepoRoot)) + { + Directory.Delete(RepoRoot, recursive: true); + } + } + catch + { + // Best effort cleanup. + } + } + } +} From ba45f22d375e21c42fdc89695451932731d94552 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:03:25 +0200 Subject: [PATCH 027/100] [release/10.0] Source code updates from dotnet/dotnet (#16202) Co-authored-by: dotnet-maestro[bot] Co-authored-by: William Godbe Co-authored-by: Jan Jones --- eng/Version.Details.xml | 2 +- es-metadata.yml | 8 +++ .../tools/Sign.props | 7 +- .../build/installer.build.targets | 2 +- .../src/ArEntry.cs | 2 + .../src/CreateWixBuildWixpack.cs | 19 ++++- .../SignToolTests.cs | 11 ++- .../src/Configuration.cs | 1 + .../src/VerifySignatures.cs | 3 +- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 70 +++++++++++++------ src/SignCheck/Microsoft.SignCheck/Utils.cs | 1 + 11 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 es-metadata.yml diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8ab7516b614..a780e10e741 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/es-metadata.yml b/es-metadata.yml new file mode 100644 index 00000000000..f28b35357a5 --- /dev/null +++ b/es-metadata.yml @@ -0,0 +1,8 @@ +schemaVersion: 0.0.1 +isProduction: true +accountableOwners: + service: b3bbd815-183a-4142-8056-3a676d687f71 +routing: + defaultAreaPath: + org: devdiv + path: DevDiv\NET Fundamentals\Infrastructure\Arcade\SDL diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props index 41d9b0d82ba..38e60705fed 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props @@ -104,9 +104,10 @@ - - - + + + + <_NewKeyVersionSuffix>newkey diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs index e7ba349668f..93dfab25875 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs @@ -7,6 +7,8 @@ namespace Microsoft.DotNet.Build.Tasks.Installers { public sealed class ArEntry { + public const uint FilePermissionMask = 0xFFF; + public ArEntry(string name, ulong timestamp, ulong ownerID, ulong groupID, uint mode, Stream dataStream) { Name = name; diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs index 41d4b72a434..31b730e8959 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs @@ -71,6 +71,8 @@ public class CreateWixBuildWixpack : Task [Required] public ITaskItem[] SourceFiles { get; set; } + public string[] SuppressSpecificWarnings { get; set; } + [Required] public string WixpackWorkingDir { get; set; } @@ -194,7 +196,7 @@ private void ProcessIncludeFile(string includeFile) // We want to keep original files in wixpack, and only preprocess // them for wixpack creation. This ensures that repacking process would not be // affected by some unintentional change, or a bug in preprocessor. - var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(includeFile)); + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); File.Copy(includeFile, tempFilePath, overwrite: true); // We're processing a Wix include file, which contains preprocessor @@ -334,6 +336,15 @@ private void GenerateWixBuildCommandLineFile() } } + // Add each Warning from SuppressSpecificWarnings array + if (SuppressSpecificWarnings != null && SuppressSpecificWarnings.Length > 0) + { + foreach (var warning in SuppressSpecificWarnings) + { + commandLineArgs.Add($"-sw{warning}"); + } + } + // Add all define constants from dictionary if (_defineConstantsDictionary != null && _defineConstantsDictionary.Count > 0) { @@ -408,7 +419,11 @@ private void GenerateWixBuildCommandLineFile() } } - string commandLine = "wix.exe build " + string.Join(" ", commandLineArgs); + // The command lines can be quite long, and cmd would reject them. Wix does support + // response files, so create a response file (create.rsp) to package alongside. + File.WriteAllText(Path.Combine(WixpackWorkingDir, "create.rsp"), string.Join(System.Environment.NewLine, commandLineArgs)); + + string commandLine = "wix.exe build @create.rsp"; StringBuilder createCmdFileContents = new(); createCmdFileContents.AppendLine("@echo off"); diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index bc5a4f9d660..03672d1c263 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -420,8 +420,11 @@ private void ValidateProducedDebContent( Directory.CreateDirectory(controlLayout); Directory.CreateDirectory(dataLayout); - ZipData.ExtractTarballContents(dataArchive, dataLayout, skipSymlinks: false); - ZipData.ExtractTarballContents(controlArchive, controlLayout); + var fakeBuildEngine = new FakeBuildEngine(_output); + var fakeLog = new TaskLoggingHelper(fakeBuildEngine, "TestLog"); + + ZipData.ExtractTarballContents(fakeLog, dataArchive, dataLayout, skipSymlinks: false); + ZipData.ExtractTarballContents(fakeLog, controlArchive, controlLayout); string md5sumsContents = File.ReadAllText(Path.Combine(controlLayout, "md5sums")); @@ -477,7 +480,9 @@ private void ValidateProducedRpmContent( string layout = Path.Combine(tempDir, "layout"); Directory.CreateDirectory(layout); - ZipData.ExtractRpmPayloadContents(log: null, rpmPackage, layout); + var fakeBuildEngine = new FakeBuildEngine(_output); + var fakeLog = new TaskLoggingHelper(fakeBuildEngine, "TestLog"); + ZipData.ExtractRpmPayloadContents(fakeLog, rpmPackage, layout); // Checks: // Expected files are present diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 56f8c043e1c..1eb5b38408c 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -837,6 +837,7 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); entry.WriteToFile(tempPath); + ZipData.SetUnixFileMode(_log, entry.UnixFileMode, tempPath); _hashToCollisionIdMap.TryGetValue(fileUniqueKey, out string collisionPriorityId); PathWithHash nestedFile = new PathWithHash(tempPath, entry.ContentHash); diff --git a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs index 4d0acc311a9..4d47364f24a 100644 --- a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs +++ b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs @@ -262,7 +262,8 @@ private static void DownloadAndConfigurePublicKeys(string tempDir) { string[] keyUrls = new string[] { - "https://packages.microsoft.com/keys/microsoft.asc", // Microsoft public key + "https://packages.microsoft.com/keys/microsoft.asc", // SHA-1 Microsoft public key + "https://packages.microsoft.com/keys/microsoft-rolling.asc", // Non-SHA1 Microsoft public keys for non-Azure Linux distributions "https://raw.githubusercontent.com/microsoft/azurelinux/3.0/SPECS/azurelinux-repos/MICROSOFT-RPM-GPG-KEY" // Azure linux public key }; foreach (string keyUrl in keyUrls) diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 2225300b2f6..cb0446869eb 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -63,7 +63,10 @@ public static IEnumerable ReadEntries(string archivePath, string t return ReadTarGZipEntries(archivePath, tempDir, tarToolPath, ignoreContent); #else return ReadTarGZipEntries(archivePath) - .Select(entry => new ZipDataEntry(entry.Name, entry.DataStream, entry.Length)); + .Select(static entry => new ZipDataEntry(entry.Name, entry.DataStream, entry.Length) + { + UnixFileMode = (uint)entry.Mode, + }); #endif } else if (FileSignInfo.IsPkg(archivePath) || FileSignInfo.IsAppBundle(archivePath)) @@ -333,7 +336,10 @@ private static IEnumerable ReadPkgOrAppBundleEntries(string archiv { var relativePath = path.Substring(extractDir.Length + 1).Replace(Path.DirectorySeparatorChar, '/'); using var stream = ignoreContent ? null : (Stream)File.Open(path, FileMode.Open); - yield return new ZipDataEntry(relativePath, stream); + yield return new ZipDataEntry(relativePath, stream) + { + UnixFileMode = GetUnixFileMode(path), + }; } } finally @@ -413,7 +419,7 @@ private static IEnumerable ReadTarGZipEntries(string archivePath, foreach (var path in Directory.EnumerateFiles(extractDir, "*.*", SearchOption.AllDirectories)) { var relativePath = path.Substring(extractDir.Length + 1).Replace(Path.DirectorySeparatorChar, '/'); - using var stream = ignoreContent ? null : (Stream)File.Open(path, FileMode.Open); + using var stream = ignoreContent ? null : (Stream)File.Open(path, FileMode.Open); yield return new ZipDataEntry(relativePath, stream); } } @@ -523,7 +529,7 @@ private void RepackDebContainer(TaskLoggingHelper log, string tempDir) string controlArchive; try { - controlArchive = GetUpdatedControlArchive(FileSignInfo.FullPath, dataArchive, tempDir); + controlArchive = GetUpdatedControlArchive(log, FileSignInfo.FullPath, dataArchive, tempDir); } catch(Exception e) { @@ -553,7 +559,7 @@ private void RepackDebContainer(TaskLoggingHelper log, string tempDir) /// /// /// - private string GetUpdatedControlArchive(string debianPackage, string dataArchive, string tempDir) + private string GetUpdatedControlArchive(TaskLoggingHelper log, string debianPackage, string dataArchive, string tempDir) { var workingDirGuidSegment = Guid.NewGuid().ToString().Split('-')[0]; @@ -569,8 +575,8 @@ private string GetUpdatedControlArchive(string debianPackage, string dataArchive string controlArchive = Path.Combine(workingDir, entry.RelativePath); entry.WriteToFile(controlArchive); - ExtractTarballContents(dataArchive, dataLayout); - ExtractTarballContents(controlArchive, controlLayout); + ExtractTarballContents(log, dataArchive, dataLayout); + ExtractTarballContents(log, controlArchive, controlLayout); string sumsFile = Path.Combine(workingDir, "md5sums"); CreateMD5SumsFile createMD5SumsFileTask = new() @@ -610,7 +616,7 @@ private string GetUpdatedControlArchive(string debianPackage, string dataArchive return controlArchive; } - internal static void ExtractTarballContents(string file, string destination, bool skipSymlinks = true) + internal static void ExtractTarballContents(TaskLoggingHelper log, string file, string destination, bool skipSymlinks = true) { foreach (TarEntry tar in ReadTarGZipEntries(file)) { @@ -623,8 +629,11 @@ internal static void ExtractTarballContents(string file, string destination, boo string outputPath = Path.Join(destination, tar.Name); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); - using FileStream outputFileStream = File.Create(outputPath); - tar.DataStream?.CopyTo(outputFileStream); + using (FileStream outputFileStream = File.Create(outputPath)) + { + tar.DataStream?.CopyTo(outputFileStream); + } + SetUnixFileMode(log, (uint)tar.Mode, outputPath); } } @@ -645,7 +654,10 @@ internal static IEnumerable ReadDebContainerEntries(string archive if (match == null || relativePath.StartsWith(match)) { - yield return new ZipDataEntry(relativePath, entry.DataStream); + yield return new ZipDataEntry(relativePath, entry.DataStream) + { + UnixFileMode = entry.Mode & ArEntry.FilePermissionMask, + }; } } } @@ -757,21 +769,14 @@ internal static void ExtractRpmPayloadContents(TaskLoggingHelper log, string rpm if (entry != null) { entry.WriteToFile(outputPath); - - // Set file mode if not the default. - if (entry.UnixFileMode is { } mode and not /* 0644 */ 420) - { - RunExternalProcess(log, "bash", $""" - -c "chmod {Convert.ToString(mode, 8)} '{outputPath}'" - """, out string _, layout); - } + SetUnixFileMode(log, entry.UnixFileMode, outputPath); } } } private static bool RunExternalProcess(TaskLoggingHelper log, string cmd, string args, out string output, string workingDir = null) { - log?.LogMessage(MessageImportance.Low, $"Running command: '{cmd}' {args}"); + log.LogMessage(MessageImportance.Low, $"Running command: '{cmd}' {args}"); ProcessStartInfo psi = new() { @@ -791,16 +796,37 @@ private static bool RunExternalProcess(TaskLoggingHelper log, string cmd, string string stderr = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(stderr)) { - log?.LogMessage(MessageImportance.Low, $" Stderr: {stderr}"); + log.LogMessage(MessageImportance.Low, $" Stderr: {stderr}"); } if (process.ExitCode != 0) { - log?.LogMessage(MessageImportance.Low, $" Exit code: {process.ExitCode}"); + log.LogMessage(MessageImportance.Low, $" Exit code: {process.ExitCode}"); } return process.ExitCode == 0; } #endif + + internal static void SetUnixFileMode(TaskLoggingHelper log, uint? unixFileMode, string outputPath) + { +#if NET + // Set file mode if not the default. + if (!OperatingSystem.IsWindows() && unixFileMode is { } mode and not /* 0644 */ 420) + { + log.LogMessage(MessageImportance.Low, $"Setting file mode {Convert.ToString(mode, 8)} on: {outputPath}"); + File.SetUnixFileMode(outputPath, (UnixFileMode)mode); + } +#endif + } + + private static uint? GetUnixFileMode(string filePath) + { +#if NET + return OperatingSystem.IsWindows() ? null : (uint)File.GetUnixFileMode(filePath); +#else + return null; +#endif + } } } diff --git a/src/SignCheck/Microsoft.SignCheck/Utils.cs b/src/SignCheck/Microsoft.SignCheck/Utils.cs index 570bc46df23..65f03d27215 100644 --- a/src/SignCheck/Microsoft.SignCheck/Utils.cs +++ b/src/SignCheck/Microsoft.SignCheck/Utils.cs @@ -200,6 +200,7 @@ public static void DownloadAndConfigurePublicKeys(string tempDir) string[] keyUrls = new string[] { "https://packages.microsoft.com/keys/microsoft.asc", // Microsoft public key + "https://packages.microsoft.com/keys/microsoft-rolling.asc", // Non-SHA1 Microsoft public keys for non-Azure Linux distributions "https://raw.githubusercontent.com/microsoft/azurelinux/3.0/SPECS/azurelinux-repos/MICROSOFT-RPM-GPG-KEY" // Azure linux public key }; foreach (string keyUrl in keyUrls) From ee88eceeec4fc087e8f8fede7a4ec0c9d0412274 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:40:54 +0000 Subject: [PATCH 028/100] [release/10.0] Source code updates from dotnet/dotnet (#16208) [release/10.0] Source code updates from dotnet/dotnet --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a780e10e741..504fdcbcc77 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + From e8ca69398033dd1eea35e9667bf857234465de2b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:41:48 +0000 Subject: [PATCH 029/100] [release/10.0] Fix SetupNugetSources.sh to avoid creating invalid XML comments when re-enabling disabled feeds (#16211) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/SetupNugetSources.sh | 6 ++-- .../FeedEnablingTests.cs | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index dd2564aef01..b97cc536379 100755 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -66,10 +66,8 @@ EnableInternalPackageSource() { grep -i " /dev/null if [ "$?" == "0" ]; then echo "Enabling internal source '$PackageSourceName'." - # Remove the disabled entry - local OldDisableValue="" - local NewDisableValue="" - sed -i.bak "s|$OldDisableValue|$NewDisableValue|" "$ConfigFile" + # Remove the disabled entry (including any surrounding comments or whitespace on the same line) + sed -i.bak "//d" "$ConfigFile" # Add the source name to PackageSources for credential handling PackageSources+=("$PackageSourceName") diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs index 7af694a1db8..d38cab4887b 100644 --- a/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs @@ -175,5 +175,40 @@ public async Task ConfigWithNoDisabledSources_StillAddsInternalFeeds() "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", "should add dotnet6-internal-transport feed"); } + + [Fact] + public async Task ConfigWithCommentedOutDisabledDarcIntFeeds_RemovesEntriesAndProducesValidXml() + { + // Arrange - this test covers the issue where commented-out disabled entries would create invalid XML + var originalConfig = @" + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // The modified config should be valid XML (this would fail if nested comments were created) + Action parseXml = () => System.Xml.Linq.XDocument.Parse(modifiedConfig); + parseXml.Should().NotThrow("modified config should be valid XML without nested comments"); + + // The darc-int feed should not be disabled + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + + // The commented-out line should be removed entirely (no comment remnants) + modifiedConfig.Should().NotContain("Reenabled for build", "should not add comments when removing disabled entries"); + } } } From c90342f0f0c37b0e632888b189a71e81eaf1fb3f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:00:44 +0200 Subject: [PATCH 030/100] [release/10.0] Source code updates from dotnet/dotnet (#16227) Co-authored-by: dotnet-maestro[bot] --- Directory.Packages.props | 2 +- eng/Version.Details.props | 8 ++++---- eng/Version.Details.xml | 10 +++++----- .../tools/DefaultVersions.props | 2 +- .../build/wix5/wix.targets | 4 ++++ .../src/CreateWixBuildWixpack.cs | 8 ++++++++ 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3b6b934b425..9e2d6effc94 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,7 +11,7 @@ 5.8.4 3.14.1-9323.2545153 - 5.0.2-dotnet.2737382 + 5.0.2-dotnet.2811440 2.9.3 diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 655f3eb34db..a1ca91b9461 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -56,10 +56,10 @@ This file should be imported by eng/Versions.props 1.1.0-beta.25421.1 - 17.12.36 - 17.12.36 - 17.12.36 - 17.12.36 + 17.12.50 + 17.12.50 + 17.12.50 + 17.12.50 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 504fdcbcc77..ae8825bcc69 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + @@ -163,19 +163,19 @@ 949759ff23ff663a2c3e105cf504b281fe667d01 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props index c1ccc8727a3..1f3433c1f8e 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props @@ -99,7 +99,7 @@ 2.1.3 1.1.286 3.14.1-9323.2545153 - 5.0.2-dotnet.2737382 + 5.0.2-dotnet.2811440 diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets index 742bf80955f..f1e21028a38 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets @@ -325,6 +325,8 @@ RunHeatHarvester"> $(InstallerName.Replace('-', '_')) + + -bcgg @@ -363,6 +365,7 @@ <_wixArgs>$(_wixArgs) $(AdditionalCandleArgs) <_wixArgs>$(_wixArgs) $(AdditionalLightArgs) + <_wixArgs>$(_wixArgs) $(WixAdditionalOptions) <_wixCommand>wix.exe build $(_wixArgs) @@ -381,6 +384,7 @@ RetryDelayBase="2" /> Date: Mon, 20 Oct 2025 09:52:46 +0000 Subject: [PATCH 031/100] [release/10.0] PublishBuildToMaestro should have minimum retry delay (#16225) Co-authored-by: Andy Zivkovic --- .../src/PublishBuildToMaestro.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs index 7b284481ff2..80e05cb284a 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs @@ -567,6 +567,13 @@ private void LookupForMatchingGitHubRepository(BuildIdentity buildIdentity) break; } + // Due to latency in receiving the response and calculating the value, we may have a negative timespan + TimeSpan minWait = TimeSpan.FromMilliseconds(100); + if (timeSpan < minWait) + { + timeSpan = minWait; + } + Log.LogMessage(MessageImportance.High, $"API rate limit exceeded, retrying in {timeSpan.Value.TotalSeconds} seconds. Retry attempt: {retry}"); Thread.Sleep(timeSpan.Value); From 63eed293471985f6fc4a60fd8b6f8f5ce3d03f38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 23:50:21 +0000 Subject: [PATCH 032/100] [release/10.0] Add a second .NET path for MicroBuild (#16238) Co-authored-by: Ella Hathaway --- .../steps/install-microbuild.yml | 15 +- .../tools/Sign.proj | 51 ++++-- src/Microsoft.DotNet.SignTool.Tests/Config.cs | 20 +++ .../FakeSignTool.cs | 6 +- .../Microsoft.DotNet.SignTool.Tests.csproj | 6 + .../SignToolTests.cs | 152 ++++++++++++------ .../src/BatchSignUtil.cs | 11 +- .../src/Configuration.cs | 8 +- .../src/RealSignTool.cs | 12 +- src/Microsoft.DotNet.SignTool/src/SignTool.cs | 7 +- .../src/SignToolArgs.cs | 22 ++- .../src/SignToolTask.cs | 41 ++++- .../src/ValidationOnlySignTool.cs | 7 +- .../src/VerifySignatures.cs | 4 +- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 74 ++++++--- 15 files changed, 318 insertions(+), 118 deletions(-) create mode 100644 src/Microsoft.DotNet.SignTool.Tests/Config.cs diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index d6b9878f54d..f2248ebfd73 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -11,23 +11,22 @@ parameters: # Unfortunately, _SignType can't be used to exclude the use of the service connection in non-real sign scenarios. The # variable is not available in template expression. _SignType has a very large proliferation across .NET, so replacing it is tough. microbuildUseESRP: true - # Location of the MicroBuild output folder - # NOTE: There's something that relies on this being in the "default" source directory for tasks such as Signing to work properly. - microBuildOutputFolder: '$(Build.SourcesDirectory)' continueOnError: false steps: - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}: - # Needed to download the MicroBuild plugin nupkgs on Mac and Linux when nuget.exe is unavailable + # Installing .NET 8 is required to use the MicroBuild signing plugin on non-Windows platforms - task: UseDotNet@2 displayName: Install .NET 8.0 SDK for MicroBuild Plugin inputs: packageType: sdk version: 8.0.x - installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet - workingDirectory: ${{ parameters.microBuildOutputFolder }} + # Installing the SDK in a '.dotnet-microbuild' directory is required for signing. + # See target FindDotNetPathForMicroBuild in arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj + # Do not remove '.dotnet-microbuild' from the path without changing the corresponding logic. + installationPath: $(Agent.TempDirectory)/.dotnet-microbuild condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - script: | @@ -65,7 +64,7 @@ steps: ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca env: TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + MicroBuildOutputFolderOverride: $(Agent.TempDirectory)/MicroBuild SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) @@ -85,7 +84,7 @@ steps: ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc env: TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + MicroBuildOutputFolderOverride: $(Agent.TempDirectory)/MicroBuild SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj index cf2a163077a..5fe136c5000 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj @@ -1,5 +1,5 @@ - + $(NuGetPackageRoot)sn\$(SNVersion)\sn.exe @@ -64,14 +64,45 @@ $(NuGetPackageRoot)microsoft.dotnet.macospkg.cli\$(MicrosoftDotNetMacOsPkgVersion)\tools\$(NetToolCurrent)\any\Microsoft.Dotnet.MacOsPkg.Cli.dll + - + + + + false + true + + + <_DotNetPathMicroBuild>$(DotNetTool) + + + + <_ItemInPath Include="$([System.Text.RegularExpressions.Regex]::Split('$(PATH)', '$([System.Convert]::ToString($([System.IO.Path]::PathSeparator)))'))" /> - - <_DotNetCorePath>$(DotNetTool) + <_DotNetPathMicroBuildCandidate + Include="@(_ItemInPath->'%(Identity)/dotnet')" + Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Identity)', '.dotnet-microbuild')) and Exists('%(Identity)/dotnet')" /> + + + + + <_DotNetPathMicroBuild>%(_DotNetPathMicroBuildCandidate.Identity) + + + + + + - diff --git a/src/Microsoft.DotNet.SignTool.Tests/Config.cs b/src/Microsoft.DotNet.SignTool.Tests/Config.cs new file mode 100644 index 00000000000..f1e235d0e24 --- /dev/null +++ b/src/Microsoft.DotNet.SignTool.Tests/Config.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.Versioning; + +namespace Microsoft.DotNet.SignTool.Tests; + +internal static class Config +{ + const string ConfigSwitchPrefix = "Microsoft.DotNet.SignTool.Tests."; + + public static string DotNetPathTooling => (string)AppContext.GetData(ConfigSwitchPrefix + nameof(DotNetPathTooling))! ?? throw new InvalidOperationException("DotNetPathTooling must be specified"); + public static string TarToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "tar", "Microsoft.Dotnet.Tar.dll"); + public static string PkgToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "pkg", "Microsoft.Dotnet.MacOsPkg.dll"); + public static string SNPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "sn", "sn.exe"); + public static string Wix3ToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix3"); + public static string WixToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix", "net472", "x64"); +} diff --git a/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs b/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs index 74fc9d120ad..36e4615fd33 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs @@ -66,6 +66,10 @@ private static void SignPEFile(string path) public override SigningStatus VerifySignedVSIX(string filePath) => SigningStatus.Signed; - public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) => SigningStatus.Signed; + public override SigningStatus VerifySignedPkgOrAppBundle( + TaskLoggingHelper log, + string filePath, + string dotNetPathTooling, + string pkgToolPath) => SigningStatus.Signed; } } diff --git a/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj b/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj index d5266900d99..c18c243db31 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj +++ b/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj @@ -80,4 +80,10 @@ + + + + $(DotNetTool) + + diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 03672d1c263..26689d80cfd 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -268,20 +268,6 @@ public SignToolTests(ITestOutputHelper output) _output = output; } - private string GetWix3ToolPath() - { - return Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix3"); - } - - private string GetWixToolPath() - { - return Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix", "net472", "x64"); - } - - private static string s_snPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "sn", "sn.exe"); - private static string s_tarToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "tar", "Microsoft.Dotnet.Tar.dll"); - private static string s_pkgToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "pkg", "Microsoft.Dotnet.MacOsPkg.dll"); - private string GetResourcePath(string name, string relativePath = null) { var srcPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "Resources", name); @@ -341,11 +327,37 @@ private void ValidateGeneratedProject( // The path to DotNet will always be null in these tests, this will force // the signing logic to call our FakeBuildEngine.BuildProjectFile with a path // to the XML that store the content of the would be Microbuild sign request. - var signToolArgs = new SignToolArgs(_tmpDir, microBuildCorePath: "MicroBuildCorePath", testSign: true, dotnetPath: null, msbuildVerbosity: "quiet", _tmpDir, enclosingDir: "", "", wix3ToolsPath: wix3ToolsPath, wixToolsPath: wixToolsPath, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, dotnetTimeout: -1); + var signToolArgs = new SignToolArgs( + _tmpDir, + microBuildCorePath: "MicroBuildCorePath", + testSign: true, + dotNetPathMicroBuild: null, + dotNetPathTooling: Config.DotNetPathTooling, + msbuildVerbosity: "quiet", + _tmpDir, + enclosingDir: "", + "", + wix3ToolsPath: wix3ToolsPath, + wixToolsPath: wixToolsPath, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + dotnetTimeout: -1); var signTool = new FakeSignTool(signToolArgs, task.Log); // Passing null for the 3rd party check skip as this doesn't affect the generated project. - var configuration = new Configuration(signToolArgs.TempDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log); + var configuration = new Configuration( + signToolArgs.TempDir, + itemsToSign, + strongNameSignInfo, + fileSignInfo, + extensionsSignInfo, + additionalCertificateInfo, + null, + dotNetPathTooling: Config.DotNetPathTooling, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + snPath: Config.SNPath, + task.Log); var signingInput = configuration.GenerateListOfFiles(); var util = new BatchSignUtil( task.BuildEngine, @@ -392,8 +404,19 @@ private void ValidateFileSignInfos( { var engine = new FakeBuildEngine(); var task = new SignToolTask { BuildEngine = engine }; - var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, - skip3rdPartyCheckFiles, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles(); + var signingInput = new Configuration( + _tmpDir, + itemsToSign, + strongNameSignInfo, + fileSignInfo, + extensionsSignInfo, + additionalCertificateInfo, + skip3rdPartyCheckFiles, + dotNetPathTooling: Config.DotNetPathTooling, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + snPath: Config.SNPath, + task.Log).GenerateListOfFiles(); signingInput.FilesToSign.Select(f => f.ToString()).Should().BeEquivalentTo(expected); signingInput.FilesToCopy.Select(f => $"{f.Key} -> {f.Value}").Should().BeEquivalentTo(expectedCopyFiles ?? Array.Empty()); @@ -534,7 +557,20 @@ public void EmptySigningList() var fileSignInfo = new Dictionary(); var task = new SignToolTask { BuildEngine = new FakeBuildEngine() }; - var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, null, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles(); + var signingInput = new Configuration( + _tmpDir, + itemsToSign, + strongNameSignInfo, + fileSignInfo, + s_fileExtensionSignInfo, + null, + null, + dotNetPathTooling: Config.DotNetPathTooling, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + snPath: Config.SNPath, + task.Log + ).GenerateListOfFiles(); signingInput.FilesToSign.Should().BeEmpty(); signingInput.ZipDataMap.Should().BeEmpty(); @@ -553,9 +589,10 @@ public void EmptySigningListForTask() TempDir = "TempDir", DryRun = false, TestSign = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), SNBinaryPath = CreateTestResource("fake.sn.exe"), - PkgToolPath = s_pkgToolPath, + PkgToolPath = Config.PkgToolPath, }; task.Execute().Should().BeTrue(); @@ -573,10 +610,11 @@ public void SignWhenSnExeIsNotRequired() TempDir = "TempDir", DryRun = false, TestSign = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), DoStrongNameCheck = false, SNBinaryPath = null, - PkgToolPath = s_pkgToolPath, + PkgToolPath = Config.PkgToolPath, }; task.Execute().Should().BeTrue(); @@ -1065,8 +1103,8 @@ public void DoubleNestedContainer() NuGet " }, - wix3ToolsPath: GetWix3ToolPath(), - wixToolsPath: GetWixToolPath()); + wix3ToolsPath: Config.Wix3ToolPath, + wixToolsPath: Config.WixToolPath); } @@ -2084,8 +2122,8 @@ public void SignMsiEngine() Microsoft400 " }, - wix3ToolsPath: GetWix3ToolPath(), - wixToolsPath: GetWixToolPath()); + wix3ToolsPath: Config.Wix3ToolPath, + wixToolsPath: Config.WixToolPath); } @@ -2137,8 +2175,8 @@ public void SignBundleDoubleNested() Microsoft400 " }, - wix3ToolsPath: GetWix3ToolPath(), - wixToolsPath: GetWixToolPath()); + wix3ToolsPath: Config.Wix3ToolPath, + wixToolsPath: Config.WixToolPath); } @@ -2177,8 +2215,8 @@ public void MsiWithWixpack() Microsoft400 " }, - wix3ToolsPath: GetWix3ToolPath(), - wixToolsPath: GetWixToolPath()); + wix3ToolsPath: Config.Wix3ToolPath, + wixToolsPath: Config.WixToolPath); } /// @@ -2187,7 +2225,7 @@ public void MsiWithWixpack() [WindowsOnlyFact] public void BadWixToolsetPath() { - var badPath = Path.Combine(GetWixToolPath(), "badpath"); + var badPath = Path.Combine(Config.WixToolPath, "badpath"); var fakeBuildEngine = new FakeBuildEngine(_output); var task = new SignToolTask @@ -2199,10 +2237,11 @@ public void BadWixToolsetPath() LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), DoStrongNameCheck = false, SNBinaryPath = null, - Wix3ToolsPath = GetWix3ToolPath(), + Wix3ToolsPath = Config.Wix3ToolPath, WixToolsPath = badPath }; @@ -2754,12 +2793,13 @@ public void ValidateSignToolTaskParsing() LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), MicroBuildCorePath = "MicroBuildCorePath", DoStrongNameCheck = false, SNBinaryPath = null, - TarToolPath = s_tarToolPath, - PkgToolPath = s_pkgToolPath, + TarToolPath = Config.TarToolPath, + PkgToolPath = Config.PkgToolPath, }; task.Execute().Should().BeTrue(); @@ -2793,7 +2833,8 @@ private bool runTask(ITaskItem[] itemsToSign = null, ITaskItem[] strongNameSignI LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), DoStrongNameCheck = false, SNBinaryPath = null, }; @@ -3129,9 +3170,10 @@ public void MissingCertificateName(string extension) new Dictionary>(), new(), null, - tarToolPath: s_tarToolPath, - pkgToolPath: s_pkgToolPath, - snPath: s_snPath, + dotNetPathTooling: Config.DotNetPathTooling, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + snPath: Config.SNPath, task.Log) .GenerateListOfFiles(); @@ -3180,9 +3222,10 @@ public void MissingCertificateNameButExtensionIsIgnored(string extension) extensionSignInfo, new(), null, - tarToolPath: s_tarToolPath, - pkgToolPath: s_pkgToolPath, - snPath: s_snPath, + dotNetPathTooling: Config.DotNetPathTooling, + tarToolPath: Config.TarToolPath, + pkgToolPath: Config.PkgToolPath, + snPath: Config.SNPath, task.Log) .GenerateListOfFiles(); @@ -3227,7 +3270,7 @@ public void RunWixToolRunsOrFailsProperly(bool deleteWxsBeforeRunningTool) const string expectedExe = "MsiBootstrapper5.exe"; const string wixPack = "MsiBootstrapper5.exe.wixpack.zip"; - var wixToolsPath = GetWixToolPath(); + var wixToolsPath = Config.WixToolPath; var wixpackPath = GetResourcePath(wixPack); var tempDir = Path.GetTempPath(); string workingDir = Path.Combine(tempDir, "extract", Guid.NewGuid().ToString()); @@ -3407,8 +3450,8 @@ public void ValidStrongNameSignaturesValidate() [WindowsOnlyFact] public void ValidStrongNameSignaturesValidateWithFallback() { - StrongNameHelper.IsSigned_Legacy(GetResourcePath("SignedLibrary.dll"), s_snPath).Should().BeTrue(); - StrongNameHelper.IsSigned_Legacy(GetResourcePath("StrongNamedWithEcmaKey.dll"), s_snPath).Should().BeTrue(); + StrongNameHelper.IsSigned_Legacy(GetResourcePath("SignedLibrary.dll"), Config.SNPath).Should().BeTrue(); + StrongNameHelper.IsSigned_Legacy(GetResourcePath("StrongNamedWithEcmaKey.dll"), Config.SNPath).Should().BeTrue(); } [ConditionalTheory(nameof(PlatformSupportsStrongNameAlgorithm))] @@ -3423,7 +3466,7 @@ public void SigningSignsAsExpected(string file, string key, bool initiallySigned StrongNameHelper.IsSigned(resourcePath).Should().BeTrue(); // Legacy sn verification works on on Windows only - StrongNameHelper.IsSigned_Legacy(resourcePath, s_snPath).Should().Be( + StrongNameHelper.IsSigned_Legacy(resourcePath, Config.SNPath).Should().Be( RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); } @@ -3435,10 +3478,10 @@ public void SigningSignsAsExpectedWithLegacyAndVerifiesWithNonLegacy(string file { // Make sure this is unique string resourcePath = GetResourcePath(file, Guid.NewGuid().ToString()); - StrongNameHelper.IsSigned_Legacy(resourcePath, s_snPath).Should().Be(initiallySigned); + StrongNameHelper.IsSigned_Legacy(resourcePath, Config.SNPath).Should().Be(initiallySigned); // Unset the strong name bit first StrongNameHelper.ClearStrongNameSignedBit(resourcePath); - StrongNameHelper.Sign_Legacy(resourcePath, GetResourcePath(key), s_snPath).Should().BeTrue(); + StrongNameHelper.Sign_Legacy(resourcePath, GetResourcePath(key), Config.SNPath).Should().BeTrue(); StrongNameHelper.IsSigned(resourcePath).Should().BeTrue(); } @@ -3495,7 +3538,8 @@ public void ExecutableTypeValidation() TempDir = "TempDir", DryRun = true, TestSign = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), AllowEmptySignList = true }; @@ -3520,7 +3564,8 @@ public void ExecutableTypeValidation() TempDir = "TempDir", DryRun = true, TestSign = true, - DotNetPath = CreateTestResource("dotnet.fake"), + DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), + DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), AllowEmptySignList = true }; @@ -3536,7 +3581,8 @@ public void TestSignShouldNotValidateNuGetSignatures() tempPath: _tmpDir, microBuildCorePath: "MockPath", testSign: true, // This is the key - TestSign should be true - dotnetPath: "MockDotNetPath", + dotNetPathMicroBuild: "MockDotNetPathMicroBuild", + dotNetPathTooling: "MockdotNetPathTooling", msbuildVerbosity: "quiet", logDir: "MockLogDir", enclosingDir: "MockEnclosingDir", diff --git a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs index ecd63cf0009..1e5c1bbf2c5 100644 --- a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs +++ b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs @@ -282,7 +282,14 @@ void repackContainer(FileSignInfo file) if (file.IsUnpackableContainer()) { _log.LogMessage($"Repacking container: '{file.FileName}'"); - _batchData.ZipDataMap[file.FileContentKey].Repack(_log, _signTool.TempDir, _signTool.Wix3ToolsPath, _signTool.WixToolsPath, _signTool.TarToolPath, _signTool.PkgToolPath); + _batchData.ZipDataMap[file.FileContentKey].Repack( + _log, + _signTool.TempDir, + _signTool.Wix3ToolsPath, + _signTool.WixToolsPath, + _signTool.DotNetPathTooling, + _signTool.TarToolPath, + _signTool.PkgToolPath); } else { @@ -649,7 +656,7 @@ private void VerifyAfterSign(TaskLoggingHelper log, FileSignInfo file) } else if (file.IsPkg() || file.IsAppBundle()) { - var status = _signTool.VerifySignedPkgOrAppBundle(_log, file.FullPath, _signTool.PkgToolPath); + var status = _signTool.VerifySignedPkgOrAppBundle(_log, file.FullPath, _signTool.DotNetPathTooling, _signTool.PkgToolPath); LogSigningStatus(file, status, "Pkg or app"); } else if (file.IsNupkg()) diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 1eb5b38408c..df344185267 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -98,8 +98,8 @@ internal class Configuration private Telemetry _telemetry; + private string _dotNetPathTooling; private string _tarToolPath; - private string _pkgToolPath; private string _snPath; @@ -115,6 +115,7 @@ public Configuration( Dictionary> extensionSignInfo, Dictionary> additionalCertificateInformation, HashSet itemsToSkip3rdPartyCheck, + string dotNetPathTooling, string tarToolPath, string pkgToolPath, string snPath, @@ -145,6 +146,7 @@ public Configuration( _wixPacks = _itemsToSign.Where(w => WixPackInfo.IsWixPack(w.FullPath))?.Select(s => new WixPackInfo(s.FullPath)).ToList(); _hashToCollisionIdMap = new Dictionary(); _telemetry = telemetry; + _dotNetPathTooling = dotNetPathTooling; _tarToolPath = tarToolPath; _pkgToolPath = pkgToolPath; _snPath = snPath; @@ -438,7 +440,7 @@ private FileSignInfo ExtractSignInfo( } else if (FileSignInfo.IsPkg(file.FullPath) || FileSignInfo.IsAppBundle(file.FullPath)) { - isAlreadyAuthenticodeSigned = IsSigned(file, VerifySignatures.IsSignedPkgOrAppBundle(_log, file.FullPath, _pkgToolPath)); + isAlreadyAuthenticodeSigned = IsSigned(file, VerifySignatures.IsSignedPkgOrAppBundle(_log, file.FullPath, _dotNetPathTooling, _pkgToolPath)); } else if (FileSignInfo.IsNupkg(file.FullPath)) { @@ -805,7 +807,7 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, { var nestedParts = new Dictionary(); - foreach (var entry in ZipData.ReadEntries(archivePath, _pathToContainerUnpackingDirectory, _tarToolPath, _pkgToolPath)) + foreach (var entry in ZipData.ReadEntries(archivePath, _pathToContainerUnpackingDirectory, _dotNetPathTooling, _tarToolPath, _pkgToolPath)) { using (entry) { diff --git a/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs b/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs index 04c9a7c97c4..82a403d011b 100644 --- a/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs @@ -19,7 +19,7 @@ namespace Microsoft.DotNet.SignTool /// internal sealed class RealSignTool : SignTool { - private readonly string _dotnetPath; + private readonly string _dotNetPathMicroBuild; private readonly string _logDir; private readonly string _msbuildVerbosity; private readonly string _snPath; @@ -39,7 +39,7 @@ internal sealed class RealSignTool : SignTool internal RealSignTool(SignToolArgs args, TaskLoggingHelper log) : base(args, log) { TestSign = args.TestSign; - _dotnetPath = args.DotNetPath; + _dotNetPathMicroBuild = args.DotNetPathMicroBuild; _msbuildVerbosity = args.MSBuildVerbosity; _snPath = args.SNBinaryPath; _logDir = args.LogDir; @@ -48,7 +48,7 @@ internal RealSignTool(SignToolArgs args, TaskLoggingHelper log) : base(args, log public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath, string binLogPath, string logPath, string errorLogPath) { - if (_dotnetPath == null) + if (_dotNetPathMicroBuild == null) { return buildEngine.BuildProjectFile(projectFilePath, null, null, null); } @@ -59,7 +59,7 @@ public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath { process.StartInfo = new ProcessStartInfo() { - FileName = _dotnetPath, + FileName = _dotNetPathMicroBuild, Arguments = $@"build ""{projectFilePath}"" -v:""{_msbuildVerbosity}"" -bl:""{binLogPath}""", UseShellExecute = false, WorkingDirectory = TempDir, @@ -167,9 +167,9 @@ public override SigningStatus VerifySignedVSIX(string filePath) return VerifySignatures.IsSignedVSIXByFileMarker(filePath); } - public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string fullPath, string pkgToolPath) + public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string fullPath, string dotNetPathTooling, string pkgToolPath) { - return VerifySignatures.IsSignedPkgOrAppBundle(log, fullPath, pkgToolPath); + return VerifySignatures.IsSignedPkgOrAppBundle(log, fullPath, dotNetPathTooling, pkgToolPath); } public override bool LocalStrongNameSign(IBuildEngine buildEngine, int round, IEnumerable files) diff --git a/src/Microsoft.DotNet.SignTool/src/SignTool.cs b/src/Microsoft.DotNet.SignTool/src/SignTool.cs index 6b88e6d156b..643d4f25352 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignTool.cs @@ -26,6 +26,7 @@ internal abstract class SignTool internal string Wix3ToolsPath => _args.Wix3ToolsPath; internal string WixToolsPath => _args.WixToolsPath; + internal string DotNetPathTooling => _args.DotNetPathTooling; internal string TarToolPath => _args.TarToolPath; internal string PkgToolPath => _args.PkgToolPath; @@ -45,7 +46,11 @@ internal SignTool(SignToolArgs args, TaskLoggingHelper log) public abstract SigningStatus VerifySignedPowerShellFile(string filePath); public abstract SigningStatus VerifySignedNuGet(string filePath); public abstract SigningStatus VerifySignedVSIX(string filePath); - public abstract SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath); + public abstract SigningStatus VerifySignedPkgOrAppBundle( + TaskLoggingHelper log, + string filePath, + string dotNetPathTooling, + string pkgToolPath); public abstract SigningStatus VerifyStrongNameSign(string fileFullPath); diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs b/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs index c65a2def62c..186477cf216 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs @@ -8,7 +8,8 @@ internal readonly struct SignToolArgs internal string TempDir { get; } internal string MicroBuildCorePath { get; } internal bool TestSign { get; } - internal string DotNetPath { get; } + internal string DotNetPathMicroBuild { get; } + internal string DotNetPathTooling { get; } internal string MSBuildVerbosity { get; } internal string SNBinaryPath { get; } internal string LogDir { get; } @@ -19,12 +20,27 @@ internal readonly struct SignToolArgs internal string PkgToolPath { get; } internal int DotNetTimeout { get; } - internal SignToolArgs(string tempPath, string microBuildCorePath, bool testSign, string dotnetPath, string msbuildVerbosity, string logDir, string enclosingDir, string snBinaryPath, string wix3ToolsPath, string wixToolsPath, string tarToolPath, string pkgToolPath, int dotnetTimeout) + internal SignToolArgs( + string tempPath, + string microBuildCorePath, + bool testSign, + string dotNetPathMicroBuild, + string dotNetPathTooling, + string msbuildVerbosity, + string logDir, + string enclosingDir, + string snBinaryPath, + string wix3ToolsPath, + string wixToolsPath, + string tarToolPath, + string pkgToolPath, + int dotnetTimeout) { TempDir = tempPath; MicroBuildCorePath = microBuildCorePath; TestSign = testSign; - DotNetPath = dotnetPath; + DotNetPathMicroBuild = dotNetPathMicroBuild; + DotNetPathTooling = dotNetPathTooling; MSBuildVerbosity = msbuildVerbosity; LogDir = logDir; EnclosingDir = enclosingDir; diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs index 14d65f5bf0f..b5ce0443eb0 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs @@ -130,9 +130,19 @@ public class SignToolTask : BuildTask public ITaskItem[] CertificatesSignInfo { get; set; } /// - /// Path to dotnet executable. Required if is false. + /// Path to dotnet executable for running MicroBuild. + /// Must match the .NET version required by MicroBuild. + /// Required if is false. /// - public string DotNetPath { get; set; } + public string DotNetPathMicroBuild { get; set; } + + + /// + /// Path to dotnet executable for running tooling tasks. + /// Must match the .NET version required by the .NET Arcade SDK. + /// + [Required] + public string DotNetPathTooling { get; set; } /// /// Verbosity level for MSBuild. @@ -213,11 +223,17 @@ public void ExecuteImpl() message: $"An empty list of files to sign was passed as parameter."); } + if (!File.Exists(DotNetPathTooling)) + { + Log.LogError($"DotNet for tooling was not found at this path: '{DotNetPathTooling}'."); + return; + } + if (!DryRun) { - if (!File.Exists(DotNetPath)) + if (!File.Exists(DotNetPathMicroBuild)) { - Log.LogError($"DotNet was not found at this path: '{DotNetPath}'."); + Log.LogError($"DotNet for MicroBuild was not found at this path: '{DotNetPathMicroBuild}'."); return; } @@ -253,7 +269,21 @@ public void ExecuteImpl() if (Log.HasLoggedErrors) return; - var signToolArgs = new SignToolArgs(TempDir, MicroBuildCorePath, TestSign, DotNetPath, MSBuildVerbosity, LogDir, enclosingDir, SNBinaryPath, Wix3ToolsPath, WixToolsPath, TarToolPath, PkgToolPath, DotNetTimeout); + var signToolArgs = new SignToolArgs( + TempDir, + MicroBuildCorePath, + TestSign, + DotNetPathMicroBuild, + DotNetPathTooling, + MSBuildVerbosity, + LogDir, + enclosingDir, + SNBinaryPath, + Wix3ToolsPath, + WixToolsPath, + TarToolPath, + PkgToolPath, + DotNetTimeout); var signTool = DryRun ? new ValidationOnlySignTool(signToolArgs, Log) : (SignTool)new RealSignTool(signToolArgs, Log); var itemsToSign = ItemsToSign.Select(i => new ItemToSign(i.ItemSpec, i.GetMetadata(SignToolConstants.CollisionPriorityId))).OrderBy(i => i.CollisionPriorityId).ToList(); @@ -269,6 +299,7 @@ public void ExecuteImpl() extensionSignInfo, dualCertificates, filesToSkip3rdPartyCheck, + dotNetPathTooling: DotNetPathTooling, tarToolPath: TarToolPath, pkgToolPath: PkgToolPath, snPath: SNBinaryPath, diff --git a/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs b/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs index 98bdd73b160..74767625add 100644 --- a/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs @@ -73,6 +73,11 @@ public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath public override SigningStatus VerifySignedVSIX(string filePath) => SigningStatus.Signed; - public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) => SigningStatus.Signed; + public override SigningStatus VerifySignedPkgOrAppBundle( + TaskLoggingHelper log, + string filePath, + string dotNetPathTooling, + string pkgToolPath) + => SigningStatus.Signed; } } diff --git a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs index 4d47364f24a..5457f88ce77 100644 --- a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs +++ b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs @@ -174,7 +174,7 @@ internal static SigningStatus IsSignedVSIXByFileMarker(string filePath) SigningStatus.Signed : SigningStatus.NotSigned; } - internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) + internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string dotNetPathTooling, string pkgToolPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { @@ -182,7 +182,7 @@ internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, stri return SigningStatus.Unknown; } - return ZipData.RunPkgProcess(filePath, null, "verify", pkgToolPath) ? SigningStatus.Signed : SigningStatus.NotSigned; + return ZipData.RunPkgProcess(filePath, null, "verify", dotNetPathTooling, pkgToolPath) ? SigningStatus.Signed : SigningStatus.NotSigned; } public static SigningStatus IsSignedPE(string filePath) diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index cb0446869eb..7635a7f45f4 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -54,13 +54,19 @@ internal ZipData(FileSignInfo fileSignInfo, ImmutableDictionary return null; } - public static IEnumerable ReadEntries(string archivePath, string tempDir, string tarToolPath, string pkgToolPath, bool ignoreContent = false) + public static IEnumerable ReadEntries( + string archivePath, + string tempDir, + string dotNetPathTooling, + string tarToolPath, + string pkgToolPath, + bool ignoreContent = false) { if (FileSignInfo.IsTarGZip(archivePath)) { // Tar APIs not available on .NET FX. We need sign tool to run on desktop msbuild because building VSIX packages requires desktop. #if NET472 - return ReadTarGZipEntries(archivePath, tempDir, tarToolPath, ignoreContent); + return ReadTarGZipEntries(archivePath, tempDir, dotNetPathTooling, tarToolPath, ignoreContent); #else return ReadTarGZipEntries(archivePath) .Select(static entry => new ZipDataEntry(entry.Name, entry.DataStream, entry.Length) @@ -71,7 +77,7 @@ public static IEnumerable ReadEntries(string archivePath, string t } else if (FileSignInfo.IsPkg(archivePath) || FileSignInfo.IsAppBundle(archivePath)) { - return ReadPkgOrAppBundleEntries(archivePath, tempDir, pkgToolPath, ignoreContent); + return ReadPkgOrAppBundleEntries(archivePath, tempDir, dotNetPathTooling, pkgToolPath, ignoreContent); } else if (FileSignInfo.IsDeb(archivePath)) { @@ -103,7 +109,14 @@ public static IEnumerable ReadEntries(string archivePath, string t /// /// Repack the zip container with the signed files. /// - public void Repack(TaskLoggingHelper log, string tempDir, string wix3ToolsPath, string wixToolsPath, string tarToolPath, string pkgToolPath) + public void Repack( + TaskLoggingHelper log, + string tempDir, + string wix3ToolsPath, + string wixToolsPath, + string dotNetPathTooling, + string tarToolPath, + string pkgToolPath) { #if NET472 if (FileSignInfo.IsVsix()) @@ -114,7 +127,7 @@ public void Repack(TaskLoggingHelper log, string tempDir, string wix3ToolsPath, #endif if (FileSignInfo.IsTarGZip()) { - RepackTarGZip(log, tempDir, tarToolPath); + RepackTarGZip(log, tempDir, dotNetPathTooling, tarToolPath); } else if (FileSignInfo.IsUnpackableWixContainer()) { @@ -122,7 +135,7 @@ public void Repack(TaskLoggingHelper log, string tempDir, string wix3ToolsPath, } else if (FileSignInfo.IsPkg() || FileSignInfo.IsAppBundle()) { - RepackPkgOrAppBundles(log, tempDir, pkgToolPath); + RepackPkgOrAppBundles(log, tempDir, dotNetPathTooling, pkgToolPath); } else if (FileSignInfo.IsDeb()) { @@ -296,7 +309,12 @@ private void RepackWixPack(TaskLoggingHelper log, string tempDir, string wix3Too } } - internal static bool RunPkgProcess(string srcPath, string dstPath, string action, string pkgToolPath) + internal static bool RunPkgProcess( + string srcPath, + string dstPath, + string action, + string dotNetPathTooling, + string pkgToolPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { @@ -312,7 +330,7 @@ internal static bool RunPkgProcess(string srcPath, string dstPath, string action var process = Process.Start(new ProcessStartInfo() { - FileName = "dotnet", + FileName = dotNetPathTooling, Arguments = $@"exec ""{pkgToolPath}"" {args}", UseShellExecute = false, RedirectStandardError = true @@ -322,12 +340,17 @@ internal static bool RunPkgProcess(string srcPath, string dstPath, string action return process.ExitCode == 0; } - private static IEnumerable ReadPkgOrAppBundleEntries(string archivePath, string tempDir, string pkgToolPath, bool ignoreContent) + private static IEnumerable ReadPkgOrAppBundleEntries( + string archivePath, + string tempDir, + string dotNetPathTooling, + string pkgToolPath, + bool ignoreContent) { string extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { - if (!RunPkgProcess(archivePath, extractDir, "unpack", pkgToolPath)) + if (!RunPkgProcess(archivePath, extractDir, "unpack", dotNetPathTooling, pkgToolPath)) { throw new Exception($"Failed to unpack pkg {archivePath}"); } @@ -351,12 +374,12 @@ private static IEnumerable ReadPkgOrAppBundleEntries(string archiv } } - private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string pkgToolPath) + private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string pkgToolPath) { string extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { - if (!RunPkgProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, "unpack", pkgToolPath)) + if (!RunPkgProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, "unpack", dotNetPathTooling, pkgToolPath)) { return; } @@ -376,7 +399,7 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string File.Copy(signedPart.Value.FileSignInfo.FullPath, path, overwrite: true); } - if (!RunPkgProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, "pack", pkgToolPath)) + if (!RunPkgProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, "pack", dotNetPathTooling, pkgToolPath)) { return; } @@ -391,11 +414,11 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string } #if NETFRAMEWORK - private static bool RunTarProcess(string srcPath, string dstPath, string tarToolPath) + private static bool RunTarProcess(string srcPath, string dstPath, string dotNetPathTooling, string tarToolPath) { var process = Process.Start(new ProcessStartInfo() { - FileName = "dotnet", + FileName = dotNetPathTooling, Arguments = $@"exec ""{tarToolPath}"" ""{srcPath}"" ""{dstPath}""", UseShellExecute = false }); @@ -404,14 +427,19 @@ private static bool RunTarProcess(string srcPath, string dstPath, string tarTool return process.ExitCode == 0; } - private static IEnumerable ReadTarGZipEntries(string archivePath, string tempDir, string tarToolPath, bool ignoreContent) + private static IEnumerable ReadTarGZipEntries( + string archivePath, + string tempDir, + string dotNetPathTooling, + string tarToolPath, + bool ignoreContent) { var extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { Directory.CreateDirectory(extractDir); - if (!RunTarProcess(archivePath, extractDir, tarToolPath)) + if (!RunTarProcess(archivePath, extractDir, dotNetPathTooling, tarToolPath)) { throw new Exception($"Failed to unpack tar archive: {archivePath}"); } @@ -429,16 +457,16 @@ private static IEnumerable ReadTarGZipEntries(string archivePath, } } - private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarToolPath) + private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string tarToolPath) { var extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { Directory.CreateDirectory(extractDir); - if (!RunTarProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, tarToolPath)) + if (!RunTarProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, dotNetPathTooling, tarToolPath)) { - log.LogMessage(MessageImportance.Low, $"Failed to unpack tar archive: dotnet {tarToolPath} {FileSignInfo.FullPath}"); + log.LogMessage(MessageImportance.Low, $"Failed to unpack tar archive: {dotNetPathTooling} {tarToolPath} {FileSignInfo.FullPath}"); return; } @@ -457,9 +485,9 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarTool File.Copy(signedPart.Value.FileSignInfo.FullPath, path, overwrite: true); } - if (!RunTarProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, tarToolPath)) + if (!RunTarProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, dotNetPathTooling, tarToolPath)) { - log.LogMessage(MessageImportance.Low, $"Failed to pack tar archive: dotnet {tarToolPath} {FileSignInfo.FullPath}"); + log.LogMessage(MessageImportance.Low, $"Failed to pack tar archive: {dotNetPathTooling} {tarToolPath} {FileSignInfo.FullPath}"); return; } } @@ -469,7 +497,7 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarTool } } #else - private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarToolPath) + private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string tarToolPath) { using MemoryStream streamToCompress = new(); using (TarWriter writer = new(streamToCompress, leaveOpen: true)) From fb1799f60d1cbae6ef22fa0bcd694d9e16f7767a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:21:22 -0700 Subject: [PATCH 033/100] Add support for dotnet-eng-internal and dotnet-tools-internal feeds in SetupNuGetSources scripts (#16246) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/SetupNugetSources.ps1 | 17 ++- eng/common/SetupNugetSources.sh | 17 ++- .../InternalFeedAdditionTests.cs | 123 ++++++++++++++++++ 3 files changed, 153 insertions(+), 4 deletions(-) diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index fc8d618014e..65ed3a8adef 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -1,6 +1,7 @@ # This script adds internal feeds required to build commits that depend on internal package sources. For instance, -# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables -# disabled internal Maestro (darc-int*) feeds. +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. Similarly, +# dotnet-eng-internal and dotnet-tools-internal are added if dotnet-eng and dotnet-tools are present. +# In addition, this script also enables disabled internal Maestro (darc-int*) feeds. # # Optionally, this script also adds a credential entry for each of the internal feeds if supplied. # @@ -173,4 +174,16 @@ foreach ($dotnetVersion in $dotnetVersions) { } } +# Check for dotnet-eng and add dotnet-eng-internal if present +$dotnetEngSource = $sources.SelectSingleNode("add[@key='dotnet-eng']") +if ($dotnetEngSource -ne $null) { + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "dotnet-eng-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password +} + +# Check for dotnet-tools and add dotnet-tools-internal if present +$dotnetToolsSource = $sources.SelectSingleNode("add[@key='dotnet-tools']") +if ($dotnetToolsSource -ne $null) { + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "dotnet-tools-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password +} + $doc.Save($filename) diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index b97cc536379..b2163abbe71 100755 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # This script adds internal feeds required to build commits that depend on internal package sources. For instance, -# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables -# disabled internal Maestro (darc-int*) feeds. +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. Similarly, +# dotnet-eng-internal and dotnet-tools-internal are added if dotnet-eng and dotnet-tools are present. +# In addition, this script also enables disabled internal Maestro (darc-int*) feeds. # # Optionally, this script also adds a credential entry for each of the internal feeds if supplied. # @@ -173,6 +174,18 @@ for DotNetVersion in ${DotNetVersions[@]} ; do fi done +# Check for dotnet-eng and add dotnet-eng-internal if present +grep -i " /dev/null +if [ "$?" == "0" ]; then + AddOrEnablePackageSource "dotnet-eng-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/$FeedSuffix" +fi + +# Check for dotnet-tools and add dotnet-tools-internal if present +grep -i " /dev/null +if [ "$?" == "0" ]; then + AddOrEnablePackageSource "dotnet-tools-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/$FeedSuffix" +fi + # I want things split line by line PrevIFS=$IFS IFS=$'\n' diff --git a/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs b/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs index ca42421c401..3427526ae02 100644 --- a/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs +++ b/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs @@ -143,6 +143,129 @@ public async Task ConfigWithExistingInternalFeed_DoesNotDuplicate() // Should have 4 total sources (3 original + 1 added transport) modifiedConfig.GetPackageSourceCount().Should().Be(4, "should not duplicate existing sources"); } + + [Fact] + public async Task ConfigWithDotNetEng_AddsDotNetEngInternal() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {0}", result.error); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldContainPackageSource("dotnet-eng-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/v3/index.json", + "should add dotnet-eng-internal feed"); + + // Should have 3 total sources (2 original + 1 added internal) + modifiedConfig.GetPackageSourceCount().Should().Be(3, "should add internal feed"); + } + + [Fact] + public async Task ConfigWithDotNetTools_AddsDotNetToolsInternal() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {0}", result.error); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldContainPackageSource("dotnet-tools-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/v3/index.json", + "should add dotnet-tools-internal feed"); + + // Should have 3 total sources (2 original + 1 added internal) + modifiedConfig.GetPackageSourceCount().Should().Be(3, "should add internal feed"); + } + + [Fact] + public async Task ConfigWithDotNetEngAndTools_AddsBothInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {0}", result.error); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldContainPackageSource("dotnet-eng-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/v3/index.json", + "should add dotnet-eng-internal feed"); + + modifiedConfig.ShouldContainPackageSource("dotnet-tools-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/v3/index.json", + "should add dotnet-tools-internal feed"); + + // Should have 5 total sources (3 original + 2 added internal) + modifiedConfig.GetPackageSourceCount().Should().Be(5, "should add both internal feeds"); + } + + [Fact] + public async Task ConfigWithoutDotNetEngOrTools_DoesNotAddInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {0}", result.error); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldNotContainPackageSource("dotnet-eng-internal", + "should not add dotnet-eng-internal when dotnet-eng is not present"); + modifiedConfig.ShouldNotContainPackageSource("dotnet-tools-internal", + "should not add dotnet-tools-internal when dotnet-tools is not present"); + + // Should add dotnet9 internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet9-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet9-internal-transport"); + } } } From c982c6f948044a5828e7559d43e5fd4520fcada5 Mon Sep 17 00:00:00 2001 From: Djuradj Kurepa <91743470+dkurepa@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:27:58 +0100 Subject: [PATCH 034/100] [release/10.0] Install .NET 8 before running Publish Using Darc (#16264) --- eng/common/core-templates/job/publish-build-assets.yml | 5 +++++ eng/common/core-templates/post-build/post-build.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 37dff559fc1..e7daa6d2faf 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -180,6 +180,11 @@ jobs: PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} is1ESPipeline: ${{ parameters.is1ESPipeline }} + # Darc is targeting 8.0, so make sure it's installed + - task: UseDotNet@2 + inputs: + version: 8.0.x + - task: AzureCLI@2 displayName: Publish Using Darc inputs: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index f6f87fe5c67..55361908c2e 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -307,6 +307,11 @@ stages: - task: NuGetAuthenticate@1 + # Darc is targeting 8.0, so make sure it's installed + - task: UseDotNet@2 + inputs: + version: 8.0.x + - task: AzureCLI@2 displayName: Publish Using Darc inputs: From 987d1a73ea67d323c0fc7537bce8ec65d87eb43f Mon Sep 17 00:00:00 2001 From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:42:07 -0800 Subject: [PATCH 035/100] [release/10.0] Enhance logging for file extraction and repackaging with matching permissions (#16271) --- .../src/Configuration.cs | 7 ++++++- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index df344185267..e1ac30c37df 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -830,11 +830,12 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, // if we already encountered file that has the same content we can reuse its signed version when repackaging the container. var fileName = Path.GetFileName(entry.RelativePath); + string entryPerms = entry.UnixFileMode.HasValue ? Convert.ToString((uint)entry.UnixFileMode.Value, 8) : "default perms"; if (!_filesByContentKey.TryGetValue(fileUniqueKey, out var fileSignInfo)) { string extractPathRoot = _useHashInExtractionPath ? fileUniqueKey.StringHash : _filesByContentKey.Count().ToString(); string tempPath = Path.Combine(_pathToContainerUnpackingDirectory, extractPathRoot, entry.RelativePath); - _log.LogMessage($"Extracting file '{fileName}' from '{archivePath}' to '{tempPath}'."); + _log.LogMessage($"Extracting file '{fileName}' from '{archivePath}' to '{tempPath}'. (Caching with perms: {entryPerms})"); Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); @@ -845,6 +846,10 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, PathWithHash nestedFile = new PathWithHash(tempPath, entry.ContentHash); fileSignInfo = TrackFile(nestedFile, zipFileSignInfo.File, collisionPriorityId); } + else + { + _log.LogMessage($"Reusing previously extracted file '{fileName}' for '{archivePath}'. (Archival perms: {entryPerms})"); + } if (fileSignInfo.ShouldTrack) { diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 7635a7f45f4..0cb09b8327c 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -376,6 +376,15 @@ private static IEnumerable ReadPkgOrAppBundleEntries( private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string pkgToolPath) { +#if NET472 + throw new NotImplementedException("PKG manipulation is not supported on .NET Framework"); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + log.LogError("Pkg/AppBundle repackaging is not supported on Windows."); + return; + } + string extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { @@ -395,8 +404,12 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string continue; } - log.LogMessage(MessageImportance.Low, $"Copying signed stream from {signedPart.Value.FileSignInfo.FullPath} to {FileSignInfo.FullPath} -> {relativePath}."); + // Preserve the original file mode from the PKG/App. The sign cache might bring if from an entry in an archive with different perms. + UnixFileMode extractedFileMode = File.GetUnixFileMode(path); + + log.LogMessage(MessageImportance.Low, $"Copying signed stream from {signedPart.Value.FileSignInfo.FullPath} to {FileSignInfo.FullPath} -> {relativePath} (perms: {Convert.ToString((uint)extractedFileMode, 8)})."); File.Copy(signedPart.Value.FileSignInfo.FullPath, path, overwrite: true); + File.SetUnixFileMode(path, extractedFileMode); } if (!RunPkgProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, "pack", dotNetPathTooling, pkgToolPath)) @@ -411,6 +424,7 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string Directory.Delete(extractDir, recursive: true); } } +#endif } #if NETFRAMEWORK @@ -515,8 +529,7 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetP entry.DataStream = signedStream; entry.DataStream.Position = 0; writer.WriteEntry(entry); - - log.LogMessage(MessageImportance.Low, $"Copying signed stream from {signedPart.Value.FileSignInfo.FullPath} to {FileSignInfo.FullPath} -> {relativeName}."); + log.LogMessage(MessageImportance.Low, $"Copying signed stream from {signedPart.Value.FileSignInfo.FullPath} to {FileSignInfo.FullPath} -> {relativeName} (perms: {Convert.ToString((uint)entry.Mode, 8)})."); continue; } From 412dad55d53c9fd4c60be8fef8e86107647a63a2 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Thu, 6 Nov 2025 15:10:54 +0000 Subject: [PATCH 036/100] [release/10.0] Allow use of internal sdk (#16267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Přemek Vysoký --- .../core-templates/job/publish-build-assets.yml | 15 +++++++++++++++ .../core-templates/post-build/post-build.yml | 9 +++++++++ eng/common/core-templates/steps/publish-logs.yml | 4 +++- eng/common/core-templates/steps/source-build.yml | 2 +- eng/common/post-build/publish-using-darc.ps1 | 4 +++- eng/common/post-build/redact-logs.ps1 | 5 +++-- eng/common/sdk-task.ps1 | 2 ++ 7 files changed, 36 insertions(+), 5 deletions(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index e7daa6d2faf..7b07b2ec523 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -120,6 +120,14 @@ jobs: - task: NuGetAuthenticate@1 + # Populate internal runtime variables. + - template: /eng/common/templates/steps/enable-internal-sources.yml + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + + - template: /eng/common/templates/steps/enable-internal-runtimes.yml + - task: AzureCLI@2 displayName: Publish Build Assets inputs: @@ -128,10 +136,15 @@ jobs: scriptLocation: scriptPath scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net /p:OfficialBuildId=$(OfficialBuildId) + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' + condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} @@ -200,6 +213,8 @@ jobs: -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' -SkipAssetsPublishing '${{ parameters.isAssetlessBuild }}' + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - template: /eng/common/core-templates/steps/publish-logs.yml diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 55361908c2e..eb043787197 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -307,6 +307,13 @@ stages: - task: NuGetAuthenticate@1 + # Populate internal runtime variables. + - template: /eng/common/templates/steps/enable-internal-sources.yml + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + + - template: /eng/common/templates/steps/enable-internal-runtimes.yml + # Darc is targeting 8.0, so make sure it's installed - task: UseDotNet@2 inputs: @@ -328,3 +335,5 @@ stages: -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' -SkipAssetsPublishing '${{ parameters.isAssetlessBuild }}' + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 10f825e270a..5a927b4c7bc 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -26,8 +26,10 @@ steps: # If the file exists - sensitive data for redaction will be sourced from it # (single entry per line, lines starting with '# ' are considered comments and skipped) arguments: -InputPath '$(System.DefaultWorkingDirectory)/PostBuildLogs' - -BinlogToolVersion ${{parameters.BinlogToolVersion}} + -BinlogToolVersion '${{parameters.BinlogToolVersion}}' -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)' '$(MaestroAccessToken)' '$(dn-bot-all-orgs-artifact-feeds-rw)' diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index acf16ed3496..b9c86c18ae4 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -24,7 +24,7 @@ steps: # in the default public locations. internalRuntimeDownloadArgs= if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://ci.dot.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://ci.dot.net/internal --runtimesourcefeedkey '$(dotnetbuilds-internal-container-read-token-base64)'' fi buildConfig=Release diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 1eda208a3bb..48e55598bdd 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -7,7 +7,9 @@ param( [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters, [Parameter(Mandatory=$false)][string] $RequireDefaultChannels, - [Parameter(Mandatory=$false)][string] $SkipAssetsPublishing + [Parameter(Mandatory=$false)][string] $SkipAssetsPublishing, + [Parameter(Mandatory=$false)][string] $runtimeSourceFeed, + [Parameter(Mandatory=$false)][string] $runtimeSourceFeedKey ) try { diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 index b7fc1959150..472d5bb562c 100644 --- a/eng/common/post-build/redact-logs.ps1 +++ b/eng/common/post-build/redact-logs.ps1 @@ -7,8 +7,9 @@ param( # File with strings to redact - separated by newlines. # For comments start the line with '# ' - such lines are ignored [Parameter(Mandatory=$false)][string] $TokensFilePath, - [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact -) + [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact, + [Parameter(Mandatory=$false)][string] $runtimeSourceFeed, + [Parameter(Mandatory=$false)][string] $runtimeSourceFeedKey) try { $ErrorActionPreference = 'Stop' diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index b62e132d32a..73183deff5f 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -9,6 +9,8 @@ Param( [switch][Alias('nobl')]$excludeCIBinaryLog, [switch]$noWarnAsError, [switch] $help, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) From 695d0f1a72d82576e8302deecbfd207c9acb8afd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 08:46:56 -0800 Subject: [PATCH 037/100] [release/10.0] Reduce the paths that binskim scans (#16289) Co-authored-by: Matt Mitchell (.NET) --- azure-pipelines.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dcd2a68d67a..373ee74dd6b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -47,6 +47,9 @@ extends: enabled: true tsa: enabled: true + binskim: + scanOutputDirectoryOnly: true + analyzeTargetGlob: +:f|**/artifacts/**/*.dll;+:f|**/artifacts/**/*.exe;-:f|**/*.Tests/**/*;-:f|**/wix/**/*;-:f|**/wix3/**/*;-:f|**/winterop.dll; stages: - template: /eng/build.yml@self From 27004a109943e230c270776b1deab32dd1161953 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Wed, 12 Nov 2025 09:25:28 -0800 Subject: [PATCH 038/100] Update .NET SDK version to 10.0.100 (#16286) --- global.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global.json b/global.json index b8b866ce354..257233c5a6d 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100-rc.1.25451.107", + "version": "10.0.100", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.100-rc.1.25451.107" + "dotnet": "10.0.100" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25507.1", From b0cc10a982900591cffcf3de7686629dfeacd615 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 13 Nov 2025 07:00:37 -0800 Subject: [PATCH 039/100] Port fail open CRL fixes to 10.0 (#16299) Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: agocke <515774+agocke@users.noreply.github.com> Co-authored-by: Andy Gocke Co-authored-by: Missy Messa <47990216+missymessa@users.noreply.github.com> --- .../Sdk/AzureDevOpsTask.cs | 34 +++++++++++++++++++ .../Sdk/FindDotNetCliPackage.cs | 30 +++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs b/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs index 3a77b797d74..a102acb64dc 100644 --- a/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs +++ b/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs @@ -8,6 +8,7 @@ using System.Net.Http.Headers; using System.Net.Sockets; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -56,11 +57,44 @@ private async Task ExecuteAsync() } else { + // Configure the cert revocation check in a fail-open state to avoid intermittent failures + // on Mac if the endpoint is not available. This is only available on .NET Core, but has only been + // observed on Mac anyway. + // https://github.com/dotnet/dnceng/issues/6410 + +#if NET + using SocketsHttpHandler handler = new SocketsHttpHandler + { + AllowAutoRedirect = false, + }; + handler.SslOptions.CertificateChainPolicy = new X509ChainPolicy + { + // Yes, check revocation. + // Yes, allow it to be downloaded if needed. + // Online is the default, but it doesn't hurt to be explicit. + RevocationMode = X509RevocationMode.Online, + // Roots never bother with revocation. + // ExcludeRoot is the default, but it doesn't hurt to be explicit. + RevocationFlag = X509RevocationFlag.ExcludeRoot, + // RevocationStatusUnknown at the EndEntity/Leaf certificate will not fail the chain build. + // RevocationStatusUnknown for any intermediate CA will not fail the chain build. + // IgnoreRootRevocationUnknown could also be specified, but it won't apply given ExcludeRoot above. + // The default is that all status codes are bad, this is not the default. + VerificationFlags = + X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown | + X509VerificationFlags.IgnoreEndRevocationUnknown, + // Always use the "now" when building the chain, rather than the "now" of when this policy object was constructed. + VerificationTimeIgnored = true, + }; + + using (var client = new HttpClient(handler) +#else using (var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true, }) +#endif { DefaultRequestHeaders = { diff --git a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs index 671215d884a..490e2238bdb 100644 --- a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs +++ b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.Arcade.Common; using Microsoft.Build.Framework; @@ -59,7 +60,34 @@ public class FindDotNetCliPackage : MSBuildTaskBase public override void ConfigureServices(IServiceCollection collection) { - _httpMessageHandler = new HttpClientHandler { CheckCertificateRevocationList = true }; +#if NET + var socketsHandler = new SocketsHttpHandler + { + AllowAutoRedirect = true, + }; + socketsHandler.SslOptions.CertificateChainPolicy = new X509ChainPolicy + { + // Yes, check revocation. + // Yes, allow it to be downloaded if needed. + // Online is the default, but it doesn't hurt to be explicit. + RevocationMode = X509RevocationMode.Online, + // Roots never bother with revocation. + // ExcludeRoot is the default, but it doesn't hurt to be explicit. + RevocationFlag = X509RevocationFlag.ExcludeRoot, + // RevocationStatusUnknown at the EndEntity/Leaf certificate will not fail the chain build. + // RevocationStatusUnknown for any intermediate CA will not fail the chain build. + // IgnoreRootRevocationUnknown could also be specified, but it won't apply given ExcludeRoot above. + // The default is that all status codes are bad, this is not the default. + VerificationFlags = + X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown | + X509VerificationFlags.IgnoreEndRevocationUnknown, + // Always use the "now" when building the chain, rather than the "now" of when this policy object was constructed. + VerificationTimeIgnored = true, + }; + _httpMessageHandler = socketsHandler; +#else + _httpMessageHandler = new HttpClientHandler { CheckCertificateRevocationList = true }; +#endif collection.TryAddSingleton(_httpMessageHandler); collection.TryAddSingleton(Log); } From a3c350283e8d81832fb07eac42fe6484a38c8d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 19 Nov 2025 11:57:14 +0100 Subject: [PATCH 040/100] Fix duplicated variables in publish-build-assets.yml (#16327) --- eng/common/core-templates/job/publish-build-assets.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 7b07b2ec523..3437087c80f 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -136,8 +136,6 @@ jobs: scriptLocation: scriptPath scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - -runtimeSourceFeed https://ci.dot.net/internal - -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net From 71e05cb6834d8a60b15c859ad5933bef075c8226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emek=20Vysok=C3=BD?= Date: Wed, 19 Nov 2025 20:57:16 +0100 Subject: [PATCH 041/100] [release/10.0] Update Microsoft.NET.Test.Sdk to 18.0.1 (#16321) --- src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props index 1f3433c1f8e..bb618cf9850 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props @@ -66,13 +66,13 @@ 19.239.34923-buildid28260713 1.0.422 5.1.0-beta.21356.1 - 17.12.0 + 18.0.1 16.9.1050 $(ArcadeSdkVersion) $(ArcadeSdkVersion) $(ArcadeSdkVersion) $(ArcadeSdkVersion) - 17.12.0 + 18.0.1 2.9.3 From 3992c1005d561b77190534a8374b7e210a60f58d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:57:08 +0100 Subject: [PATCH 042/100] [release/10.0] Source code updates from dotnet/dotnet (#16325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 2 +- .../src/CreateMD5SumsFile.cs | 2 +- src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ae8825bcc69..92435edfa4c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs index a454a7c61d2..b877d72e75c 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs @@ -47,7 +47,7 @@ public override bool Execute() #endif } - InstalledSize = installedSize.ToString(); + InstalledSize = (installedSize / 1024).ToString(); return true; } diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 26689d80cfd..b887a859d10 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -1874,7 +1874,7 @@ public void CheckDebSigning() }; string[] signableFiles = ["usr/local/bin/mscorlib.dll"]; string expectedControlFileContent = "Package: test\nVersion: 1.0\nSection: base\nPriority: optional\nArchitecture: all\n"; - expectedControlFileContent +="Maintainer: Arcade \nInstalled-Size: 49697\nDescription: A simple test package\n This is a simple generated .deb package for testing purposes.\n"; + expectedControlFileContent +="Maintainer: Arcade \nInstalled-Size: 48\nDescription: A simple test package\n This is a simple generated .deb package for testing purposes.\n"; ValidateProducedDebContent(Path.Combine(_tmpDir, "test.deb"), expectedFilesOriginalHashes, signableFiles, expectedControlFileContent); } From 204a3013ae587aba45c3b7f01a81acfb55abd54d Mon Sep 17 00:00:00 2001 From: Ella Hathaway <67609881+ellahathaway@users.noreply.github.com> Date: Fri, 21 Nov 2025 06:28:13 -0800 Subject: [PATCH 043/100] Revert "[release/10.0] Add a second .NET path for MicroBuild" (#16308) --- .../steps/install-microbuild.yml | 15 +- .../tools/Sign.proj | 51 ++---- src/Microsoft.DotNet.SignTool.Tests/Config.cs | 20 --- .../FakeSignTool.cs | 6 +- .../Microsoft.DotNet.SignTool.Tests.csproj | 6 - .../SignToolTests.cs | 152 ++++++------------ .../src/BatchSignUtil.cs | 11 +- .../src/Configuration.cs | 8 +- .../src/RealSignTool.cs | 12 +- src/Microsoft.DotNet.SignTool/src/SignTool.cs | 7 +- .../src/SignToolArgs.cs | 22 +-- .../src/SignToolTask.cs | 41 +---- .../src/ValidationOnlySignTool.cs | 7 +- .../src/VerifySignatures.cs | 4 +- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 74 +++------ 15 files changed, 118 insertions(+), 318 deletions(-) delete mode 100644 src/Microsoft.DotNet.SignTool.Tests/Config.cs diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index f2248ebfd73..d6b9878f54d 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -11,22 +11,23 @@ parameters: # Unfortunately, _SignType can't be used to exclude the use of the service connection in non-real sign scenarios. The # variable is not available in template expression. _SignType has a very large proliferation across .NET, so replacing it is tough. microbuildUseESRP: true + # Location of the MicroBuild output folder + # NOTE: There's something that relies on this being in the "default" source directory for tasks such as Signing to work properly. + microBuildOutputFolder: '$(Build.SourcesDirectory)' continueOnError: false steps: - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}: - # Installing .NET 8 is required to use the MicroBuild signing plugin on non-Windows platforms + # Needed to download the MicroBuild plugin nupkgs on Mac and Linux when nuget.exe is unavailable - task: UseDotNet@2 displayName: Install .NET 8.0 SDK for MicroBuild Plugin inputs: packageType: sdk version: 8.0.x - # Installing the SDK in a '.dotnet-microbuild' directory is required for signing. - # See target FindDotNetPathForMicroBuild in arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj - # Do not remove '.dotnet-microbuild' from the path without changing the corresponding logic. - installationPath: $(Agent.TempDirectory)/.dotnet-microbuild + installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet + workingDirectory: ${{ parameters.microBuildOutputFolder }} condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - script: | @@ -64,7 +65,7 @@ steps: ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca env: TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: $(Agent.TempDirectory)/MicroBuild + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) @@ -84,7 +85,7 @@ steps: ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc env: TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: $(Agent.TempDirectory)/MicroBuild + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj index 5fe136c5000..cf2a163077a 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj @@ -1,5 +1,5 @@ - + $(NuGetPackageRoot)sn\$(SNVersion)\sn.exe @@ -64,45 +64,14 @@ $(NuGetPackageRoot)microsoft.dotnet.macospkg.cli\$(MicrosoftDotNetMacOsPkgVersion)\tools\$(NetToolCurrent)\any\Microsoft.Dotnet.MacOsPkg.Cli.dll - - - - - - false - true - - - <_DotNetPathMicroBuild>$(DotNetTool) - - - - <_ItemInPath Include="$([System.Text.RegularExpressions.Regex]::Split('$(PATH)', '$([System.Convert]::ToString($([System.IO.Path]::PathSeparator)))'))" /> - - <_DotNetPathMicroBuildCandidate - Include="@(_ItemInPath->'%(Identity)/dotnet')" - Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Identity)', '.dotnet-microbuild')) and Exists('%(Identity)/dotnet')" /> - - - - - <_DotNetPathMicroBuild>%(_DotNetPathMicroBuildCandidate.Identity) - - - - - + + <_DotNetCorePath>$(DotNetTool) + + + diff --git a/src/Microsoft.DotNet.SignTool.Tests/Config.cs b/src/Microsoft.DotNet.SignTool.Tests/Config.cs deleted file mode 100644 index f1e235d0e24..00000000000 --- a/src/Microsoft.DotNet.SignTool.Tests/Config.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Runtime.Versioning; - -namespace Microsoft.DotNet.SignTool.Tests; - -internal static class Config -{ - const string ConfigSwitchPrefix = "Microsoft.DotNet.SignTool.Tests."; - - public static string DotNetPathTooling => (string)AppContext.GetData(ConfigSwitchPrefix + nameof(DotNetPathTooling))! ?? throw new InvalidOperationException("DotNetPathTooling must be specified"); - public static string TarToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "tar", "Microsoft.Dotnet.Tar.dll"); - public static string PkgToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "pkg", "Microsoft.Dotnet.MacOsPkg.dll"); - public static string SNPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "sn", "sn.exe"); - public static string Wix3ToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix3"); - public static string WixToolPath => Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix", "net472", "x64"); -} diff --git a/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs b/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs index 36e4615fd33..74fc9d120ad 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/FakeSignTool.cs @@ -66,10 +66,6 @@ private static void SignPEFile(string path) public override SigningStatus VerifySignedVSIX(string filePath) => SigningStatus.Signed; - public override SigningStatus VerifySignedPkgOrAppBundle( - TaskLoggingHelper log, - string filePath, - string dotNetPathTooling, - string pkgToolPath) => SigningStatus.Signed; + public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) => SigningStatus.Signed; } } diff --git a/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj b/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj index c18c243db31..d5266900d99 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj +++ b/src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj @@ -80,10 +80,4 @@ - - - - $(DotNetTool) - - diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index b887a859d10..d4b0ef0c48f 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -268,6 +268,20 @@ public SignToolTests(ITestOutputHelper output) _output = output; } + private string GetWix3ToolPath() + { + return Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix3"); + } + + private string GetWixToolPath() + { + return Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "wix", "net472", "x64"); + } + + private static string s_snPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "sn", "sn.exe"); + private static string s_tarToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "tar", "Microsoft.Dotnet.Tar.dll"); + private static string s_pkgToolPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "tools", "pkg", "Microsoft.Dotnet.MacOsPkg.dll"); + private string GetResourcePath(string name, string relativePath = null) { var srcPath = Path.Combine(Path.GetDirectoryName(typeof(SignToolTests).Assembly.Location), "Resources", name); @@ -327,37 +341,11 @@ private void ValidateGeneratedProject( // The path to DotNet will always be null in these tests, this will force // the signing logic to call our FakeBuildEngine.BuildProjectFile with a path // to the XML that store the content of the would be Microbuild sign request. - var signToolArgs = new SignToolArgs( - _tmpDir, - microBuildCorePath: "MicroBuildCorePath", - testSign: true, - dotNetPathMicroBuild: null, - dotNetPathTooling: Config.DotNetPathTooling, - msbuildVerbosity: "quiet", - _tmpDir, - enclosingDir: "", - "", - wix3ToolsPath: wix3ToolsPath, - wixToolsPath: wixToolsPath, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - dotnetTimeout: -1); + var signToolArgs = new SignToolArgs(_tmpDir, microBuildCorePath: "MicroBuildCorePath", testSign: true, dotnetPath: null, msbuildVerbosity: "quiet", _tmpDir, enclosingDir: "", "", wix3ToolsPath: wix3ToolsPath, wixToolsPath: wixToolsPath, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, dotnetTimeout: -1); var signTool = new FakeSignTool(signToolArgs, task.Log); // Passing null for the 3rd party check skip as this doesn't affect the generated project. - var configuration = new Configuration( - signToolArgs.TempDir, - itemsToSign, - strongNameSignInfo, - fileSignInfo, - extensionsSignInfo, - additionalCertificateInfo, - null, - dotNetPathTooling: Config.DotNetPathTooling, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - snPath: Config.SNPath, - task.Log); + var configuration = new Configuration(signToolArgs.TempDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log); var signingInput = configuration.GenerateListOfFiles(); var util = new BatchSignUtil( task.BuildEngine, @@ -404,19 +392,8 @@ private void ValidateFileSignInfos( { var engine = new FakeBuildEngine(); var task = new SignToolTask { BuildEngine = engine }; - var signingInput = new Configuration( - _tmpDir, - itemsToSign, - strongNameSignInfo, - fileSignInfo, - extensionsSignInfo, - additionalCertificateInfo, - skip3rdPartyCheckFiles, - dotNetPathTooling: Config.DotNetPathTooling, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - snPath: Config.SNPath, - task.Log).GenerateListOfFiles(); + var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, extensionsSignInfo, additionalCertificateInfo, + skip3rdPartyCheckFiles, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles(); signingInput.FilesToSign.Select(f => f.ToString()).Should().BeEquivalentTo(expected); signingInput.FilesToCopy.Select(f => $"{f.Key} -> {f.Value}").Should().BeEquivalentTo(expectedCopyFiles ?? Array.Empty()); @@ -557,20 +534,7 @@ public void EmptySigningList() var fileSignInfo = new Dictionary(); var task = new SignToolTask { BuildEngine = new FakeBuildEngine() }; - var signingInput = new Configuration( - _tmpDir, - itemsToSign, - strongNameSignInfo, - fileSignInfo, - s_fileExtensionSignInfo, - null, - null, - dotNetPathTooling: Config.DotNetPathTooling, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - snPath: Config.SNPath, - task.Log - ).GenerateListOfFiles(); + var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, null, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles(); signingInput.FilesToSign.Should().BeEmpty(); signingInput.ZipDataMap.Should().BeEmpty(); @@ -589,10 +553,9 @@ public void EmptySigningListForTask() TempDir = "TempDir", DryRun = false, TestSign = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), SNBinaryPath = CreateTestResource("fake.sn.exe"), - PkgToolPath = Config.PkgToolPath, + PkgToolPath = s_pkgToolPath, }; task.Execute().Should().BeTrue(); @@ -610,11 +573,10 @@ public void SignWhenSnExeIsNotRequired() TempDir = "TempDir", DryRun = false, TestSign = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), DoStrongNameCheck = false, SNBinaryPath = null, - PkgToolPath = Config.PkgToolPath, + PkgToolPath = s_pkgToolPath, }; task.Execute().Should().BeTrue(); @@ -1103,8 +1065,8 @@ public void DoubleNestedContainer() NuGet " }, - wix3ToolsPath: Config.Wix3ToolPath, - wixToolsPath: Config.WixToolPath); + wix3ToolsPath: GetWix3ToolPath(), + wixToolsPath: GetWixToolPath()); } @@ -2122,8 +2084,8 @@ public void SignMsiEngine() Microsoft400 " }, - wix3ToolsPath: Config.Wix3ToolPath, - wixToolsPath: Config.WixToolPath); + wix3ToolsPath: GetWix3ToolPath(), + wixToolsPath: GetWixToolPath()); } @@ -2175,8 +2137,8 @@ public void SignBundleDoubleNested() Microsoft400 " }, - wix3ToolsPath: Config.Wix3ToolPath, - wixToolsPath: Config.WixToolPath); + wix3ToolsPath: GetWix3ToolPath(), + wixToolsPath: GetWixToolPath()); } @@ -2215,8 +2177,8 @@ public void MsiWithWixpack() Microsoft400 " }, - wix3ToolsPath: Config.Wix3ToolPath, - wixToolsPath: Config.WixToolPath); + wix3ToolsPath: GetWix3ToolPath(), + wixToolsPath: GetWixToolPath()); } /// @@ -2225,7 +2187,7 @@ public void MsiWithWixpack() [WindowsOnlyFact] public void BadWixToolsetPath() { - var badPath = Path.Combine(Config.WixToolPath, "badpath"); + var badPath = Path.Combine(GetWixToolPath(), "badpath"); var fakeBuildEngine = new FakeBuildEngine(_output); var task = new SignToolTask @@ -2237,11 +2199,10 @@ public void BadWixToolsetPath() LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), DoStrongNameCheck = false, SNBinaryPath = null, - Wix3ToolsPath = Config.Wix3ToolPath, + Wix3ToolsPath = GetWix3ToolPath(), WixToolsPath = badPath }; @@ -2793,13 +2754,12 @@ public void ValidateSignToolTaskParsing() LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), MicroBuildCorePath = "MicroBuildCorePath", DoStrongNameCheck = false, SNBinaryPath = null, - TarToolPath = Config.TarToolPath, - PkgToolPath = Config.PkgToolPath, + TarToolPath = s_tarToolPath, + PkgToolPath = s_pkgToolPath, }; task.Execute().Should().BeTrue(); @@ -2833,8 +2793,7 @@ private bool runTask(ITaskItem[] itemsToSign = null, ITaskItem[] strongNameSignI LogDir = "LogDir", TempDir = "TempDir", DryRun = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), DoStrongNameCheck = false, SNBinaryPath = null, }; @@ -3170,10 +3129,9 @@ public void MissingCertificateName(string extension) new Dictionary>(), new(), null, - dotNetPathTooling: Config.DotNetPathTooling, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - snPath: Config.SNPath, + tarToolPath: s_tarToolPath, + pkgToolPath: s_pkgToolPath, + snPath: s_snPath, task.Log) .GenerateListOfFiles(); @@ -3222,10 +3180,9 @@ public void MissingCertificateNameButExtensionIsIgnored(string extension) extensionSignInfo, new(), null, - dotNetPathTooling: Config.DotNetPathTooling, - tarToolPath: Config.TarToolPath, - pkgToolPath: Config.PkgToolPath, - snPath: Config.SNPath, + tarToolPath: s_tarToolPath, + pkgToolPath: s_pkgToolPath, + snPath: s_snPath, task.Log) .GenerateListOfFiles(); @@ -3270,7 +3227,7 @@ public void RunWixToolRunsOrFailsProperly(bool deleteWxsBeforeRunningTool) const string expectedExe = "MsiBootstrapper5.exe"; const string wixPack = "MsiBootstrapper5.exe.wixpack.zip"; - var wixToolsPath = Config.WixToolPath; + var wixToolsPath = GetWixToolPath(); var wixpackPath = GetResourcePath(wixPack); var tempDir = Path.GetTempPath(); string workingDir = Path.Combine(tempDir, "extract", Guid.NewGuid().ToString()); @@ -3450,8 +3407,8 @@ public void ValidStrongNameSignaturesValidate() [WindowsOnlyFact] public void ValidStrongNameSignaturesValidateWithFallback() { - StrongNameHelper.IsSigned_Legacy(GetResourcePath("SignedLibrary.dll"), Config.SNPath).Should().BeTrue(); - StrongNameHelper.IsSigned_Legacy(GetResourcePath("StrongNamedWithEcmaKey.dll"), Config.SNPath).Should().BeTrue(); + StrongNameHelper.IsSigned_Legacy(GetResourcePath("SignedLibrary.dll"), s_snPath).Should().BeTrue(); + StrongNameHelper.IsSigned_Legacy(GetResourcePath("StrongNamedWithEcmaKey.dll"), s_snPath).Should().BeTrue(); } [ConditionalTheory(nameof(PlatformSupportsStrongNameAlgorithm))] @@ -3466,7 +3423,7 @@ public void SigningSignsAsExpected(string file, string key, bool initiallySigned StrongNameHelper.IsSigned(resourcePath).Should().BeTrue(); // Legacy sn verification works on on Windows only - StrongNameHelper.IsSigned_Legacy(resourcePath, Config.SNPath).Should().Be( + StrongNameHelper.IsSigned_Legacy(resourcePath, s_snPath).Should().Be( RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); } @@ -3478,10 +3435,10 @@ public void SigningSignsAsExpectedWithLegacyAndVerifiesWithNonLegacy(string file { // Make sure this is unique string resourcePath = GetResourcePath(file, Guid.NewGuid().ToString()); - StrongNameHelper.IsSigned_Legacy(resourcePath, Config.SNPath).Should().Be(initiallySigned); + StrongNameHelper.IsSigned_Legacy(resourcePath, s_snPath).Should().Be(initiallySigned); // Unset the strong name bit first StrongNameHelper.ClearStrongNameSignedBit(resourcePath); - StrongNameHelper.Sign_Legacy(resourcePath, GetResourcePath(key), Config.SNPath).Should().BeTrue(); + StrongNameHelper.Sign_Legacy(resourcePath, GetResourcePath(key), s_snPath).Should().BeTrue(); StrongNameHelper.IsSigned(resourcePath).Should().BeTrue(); } @@ -3538,8 +3495,7 @@ public void ExecutableTypeValidation() TempDir = "TempDir", DryRun = true, TestSign = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), AllowEmptySignList = true }; @@ -3564,8 +3520,7 @@ public void ExecutableTypeValidation() TempDir = "TempDir", DryRun = true, TestSign = true, - DotNetPathMicroBuild = CreateTestResource("dotnet.mb.fake"), - DotNetPathTooling = CreateTestResource("dotnet.tool.fake"), + DotNetPath = CreateTestResource("dotnet.fake"), AllowEmptySignList = true }; @@ -3581,8 +3536,7 @@ public void TestSignShouldNotValidateNuGetSignatures() tempPath: _tmpDir, microBuildCorePath: "MockPath", testSign: true, // This is the key - TestSign should be true - dotNetPathMicroBuild: "MockDotNetPathMicroBuild", - dotNetPathTooling: "MockdotNetPathTooling", + dotnetPath: "MockDotNetPath", msbuildVerbosity: "quiet", logDir: "MockLogDir", enclosingDir: "MockEnclosingDir", diff --git a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs index 1e5c1bbf2c5..ecd63cf0009 100644 --- a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs +++ b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs @@ -282,14 +282,7 @@ void repackContainer(FileSignInfo file) if (file.IsUnpackableContainer()) { _log.LogMessage($"Repacking container: '{file.FileName}'"); - _batchData.ZipDataMap[file.FileContentKey].Repack( - _log, - _signTool.TempDir, - _signTool.Wix3ToolsPath, - _signTool.WixToolsPath, - _signTool.DotNetPathTooling, - _signTool.TarToolPath, - _signTool.PkgToolPath); + _batchData.ZipDataMap[file.FileContentKey].Repack(_log, _signTool.TempDir, _signTool.Wix3ToolsPath, _signTool.WixToolsPath, _signTool.TarToolPath, _signTool.PkgToolPath); } else { @@ -656,7 +649,7 @@ private void VerifyAfterSign(TaskLoggingHelper log, FileSignInfo file) } else if (file.IsPkg() || file.IsAppBundle()) { - var status = _signTool.VerifySignedPkgOrAppBundle(_log, file.FullPath, _signTool.DotNetPathTooling, _signTool.PkgToolPath); + var status = _signTool.VerifySignedPkgOrAppBundle(_log, file.FullPath, _signTool.PkgToolPath); LogSigningStatus(file, status, "Pkg or app"); } else if (file.IsNupkg()) diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index e1ac30c37df..074a9528ae0 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -98,8 +98,8 @@ internal class Configuration private Telemetry _telemetry; - private string _dotNetPathTooling; private string _tarToolPath; + private string _pkgToolPath; private string _snPath; @@ -115,7 +115,6 @@ public Configuration( Dictionary> extensionSignInfo, Dictionary> additionalCertificateInformation, HashSet itemsToSkip3rdPartyCheck, - string dotNetPathTooling, string tarToolPath, string pkgToolPath, string snPath, @@ -146,7 +145,6 @@ public Configuration( _wixPacks = _itemsToSign.Where(w => WixPackInfo.IsWixPack(w.FullPath))?.Select(s => new WixPackInfo(s.FullPath)).ToList(); _hashToCollisionIdMap = new Dictionary(); _telemetry = telemetry; - _dotNetPathTooling = dotNetPathTooling; _tarToolPath = tarToolPath; _pkgToolPath = pkgToolPath; _snPath = snPath; @@ -440,7 +438,7 @@ private FileSignInfo ExtractSignInfo( } else if (FileSignInfo.IsPkg(file.FullPath) || FileSignInfo.IsAppBundle(file.FullPath)) { - isAlreadyAuthenticodeSigned = IsSigned(file, VerifySignatures.IsSignedPkgOrAppBundle(_log, file.FullPath, _dotNetPathTooling, _pkgToolPath)); + isAlreadyAuthenticodeSigned = IsSigned(file, VerifySignatures.IsSignedPkgOrAppBundle(_log, file.FullPath, _pkgToolPath)); } else if (FileSignInfo.IsNupkg(file.FullPath)) { @@ -807,7 +805,7 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, { var nestedParts = new Dictionary(); - foreach (var entry in ZipData.ReadEntries(archivePath, _pathToContainerUnpackingDirectory, _dotNetPathTooling, _tarToolPath, _pkgToolPath)) + foreach (var entry in ZipData.ReadEntries(archivePath, _pathToContainerUnpackingDirectory, _tarToolPath, _pkgToolPath)) { using (entry) { diff --git a/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs b/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs index 82a403d011b..04c9a7c97c4 100644 --- a/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/RealSignTool.cs @@ -19,7 +19,7 @@ namespace Microsoft.DotNet.SignTool /// internal sealed class RealSignTool : SignTool { - private readonly string _dotNetPathMicroBuild; + private readonly string _dotnetPath; private readonly string _logDir; private readonly string _msbuildVerbosity; private readonly string _snPath; @@ -39,7 +39,7 @@ internal sealed class RealSignTool : SignTool internal RealSignTool(SignToolArgs args, TaskLoggingHelper log) : base(args, log) { TestSign = args.TestSign; - _dotNetPathMicroBuild = args.DotNetPathMicroBuild; + _dotnetPath = args.DotNetPath; _msbuildVerbosity = args.MSBuildVerbosity; _snPath = args.SNBinaryPath; _logDir = args.LogDir; @@ -48,7 +48,7 @@ internal RealSignTool(SignToolArgs args, TaskLoggingHelper log) : base(args, log public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath, string binLogPath, string logPath, string errorLogPath) { - if (_dotNetPathMicroBuild == null) + if (_dotnetPath == null) { return buildEngine.BuildProjectFile(projectFilePath, null, null, null); } @@ -59,7 +59,7 @@ public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath { process.StartInfo = new ProcessStartInfo() { - FileName = _dotNetPathMicroBuild, + FileName = _dotnetPath, Arguments = $@"build ""{projectFilePath}"" -v:""{_msbuildVerbosity}"" -bl:""{binLogPath}""", UseShellExecute = false, WorkingDirectory = TempDir, @@ -167,9 +167,9 @@ public override SigningStatus VerifySignedVSIX(string filePath) return VerifySignatures.IsSignedVSIXByFileMarker(filePath); } - public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string fullPath, string dotNetPathTooling, string pkgToolPath) + public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string fullPath, string pkgToolPath) { - return VerifySignatures.IsSignedPkgOrAppBundle(log, fullPath, dotNetPathTooling, pkgToolPath); + return VerifySignatures.IsSignedPkgOrAppBundle(log, fullPath, pkgToolPath); } public override bool LocalStrongNameSign(IBuildEngine buildEngine, int round, IEnumerable files) diff --git a/src/Microsoft.DotNet.SignTool/src/SignTool.cs b/src/Microsoft.DotNet.SignTool/src/SignTool.cs index 643d4f25352..6b88e6d156b 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignTool.cs @@ -26,7 +26,6 @@ internal abstract class SignTool internal string Wix3ToolsPath => _args.Wix3ToolsPath; internal string WixToolsPath => _args.WixToolsPath; - internal string DotNetPathTooling => _args.DotNetPathTooling; internal string TarToolPath => _args.TarToolPath; internal string PkgToolPath => _args.PkgToolPath; @@ -46,11 +45,7 @@ internal SignTool(SignToolArgs args, TaskLoggingHelper log) public abstract SigningStatus VerifySignedPowerShellFile(string filePath); public abstract SigningStatus VerifySignedNuGet(string filePath); public abstract SigningStatus VerifySignedVSIX(string filePath); - public abstract SigningStatus VerifySignedPkgOrAppBundle( - TaskLoggingHelper log, - string filePath, - string dotNetPathTooling, - string pkgToolPath); + public abstract SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath); public abstract SigningStatus VerifyStrongNameSign(string fileFullPath); diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs b/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs index 186477cf216..c65a2def62c 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolArgs.cs @@ -8,8 +8,7 @@ internal readonly struct SignToolArgs internal string TempDir { get; } internal string MicroBuildCorePath { get; } internal bool TestSign { get; } - internal string DotNetPathMicroBuild { get; } - internal string DotNetPathTooling { get; } + internal string DotNetPath { get; } internal string MSBuildVerbosity { get; } internal string SNBinaryPath { get; } internal string LogDir { get; } @@ -20,27 +19,12 @@ internal readonly struct SignToolArgs internal string PkgToolPath { get; } internal int DotNetTimeout { get; } - internal SignToolArgs( - string tempPath, - string microBuildCorePath, - bool testSign, - string dotNetPathMicroBuild, - string dotNetPathTooling, - string msbuildVerbosity, - string logDir, - string enclosingDir, - string snBinaryPath, - string wix3ToolsPath, - string wixToolsPath, - string tarToolPath, - string pkgToolPath, - int dotnetTimeout) + internal SignToolArgs(string tempPath, string microBuildCorePath, bool testSign, string dotnetPath, string msbuildVerbosity, string logDir, string enclosingDir, string snBinaryPath, string wix3ToolsPath, string wixToolsPath, string tarToolPath, string pkgToolPath, int dotnetTimeout) { TempDir = tempPath; MicroBuildCorePath = microBuildCorePath; TestSign = testSign; - DotNetPathMicroBuild = dotNetPathMicroBuild; - DotNetPathTooling = dotNetPathTooling; + DotNetPath = dotnetPath; MSBuildVerbosity = msbuildVerbosity; LogDir = logDir; EnclosingDir = enclosingDir; diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs index b5ce0443eb0..14d65f5bf0f 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs @@ -130,19 +130,9 @@ public class SignToolTask : BuildTask public ITaskItem[] CertificatesSignInfo { get; set; } /// - /// Path to dotnet executable for running MicroBuild. - /// Must match the .NET version required by MicroBuild. - /// Required if is false. + /// Path to dotnet executable. Required if is false. /// - public string DotNetPathMicroBuild { get; set; } - - - /// - /// Path to dotnet executable for running tooling tasks. - /// Must match the .NET version required by the .NET Arcade SDK. - /// - [Required] - public string DotNetPathTooling { get; set; } + public string DotNetPath { get; set; } /// /// Verbosity level for MSBuild. @@ -223,17 +213,11 @@ public void ExecuteImpl() message: $"An empty list of files to sign was passed as parameter."); } - if (!File.Exists(DotNetPathTooling)) - { - Log.LogError($"DotNet for tooling was not found at this path: '{DotNetPathTooling}'."); - return; - } - if (!DryRun) { - if (!File.Exists(DotNetPathMicroBuild)) + if (!File.Exists(DotNetPath)) { - Log.LogError($"DotNet for MicroBuild was not found at this path: '{DotNetPathMicroBuild}'."); + Log.LogError($"DotNet was not found at this path: '{DotNetPath}'."); return; } @@ -269,21 +253,7 @@ public void ExecuteImpl() if (Log.HasLoggedErrors) return; - var signToolArgs = new SignToolArgs( - TempDir, - MicroBuildCorePath, - TestSign, - DotNetPathMicroBuild, - DotNetPathTooling, - MSBuildVerbosity, - LogDir, - enclosingDir, - SNBinaryPath, - Wix3ToolsPath, - WixToolsPath, - TarToolPath, - PkgToolPath, - DotNetTimeout); + var signToolArgs = new SignToolArgs(TempDir, MicroBuildCorePath, TestSign, DotNetPath, MSBuildVerbosity, LogDir, enclosingDir, SNBinaryPath, Wix3ToolsPath, WixToolsPath, TarToolPath, PkgToolPath, DotNetTimeout); var signTool = DryRun ? new ValidationOnlySignTool(signToolArgs, Log) : (SignTool)new RealSignTool(signToolArgs, Log); var itemsToSign = ItemsToSign.Select(i => new ItemToSign(i.ItemSpec, i.GetMetadata(SignToolConstants.CollisionPriorityId))).OrderBy(i => i.CollisionPriorityId).ToList(); @@ -299,7 +269,6 @@ public void ExecuteImpl() extensionSignInfo, dualCertificates, filesToSkip3rdPartyCheck, - dotNetPathTooling: DotNetPathTooling, tarToolPath: TarToolPath, pkgToolPath: PkgToolPath, snPath: SNBinaryPath, diff --git a/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs b/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs index 74767625add..98bdd73b160 100644 --- a/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/ValidationOnlySignTool.cs @@ -73,11 +73,6 @@ public override bool RunMSBuild(IBuildEngine buildEngine, string projectFilePath public override SigningStatus VerifySignedVSIX(string filePath) => SigningStatus.Signed; - public override SigningStatus VerifySignedPkgOrAppBundle( - TaskLoggingHelper log, - string filePath, - string dotNetPathTooling, - string pkgToolPath) - => SigningStatus.Signed; + public override SigningStatus VerifySignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) => SigningStatus.Signed; } } diff --git a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs index 5457f88ce77..4d47364f24a 100644 --- a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs +++ b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs @@ -174,7 +174,7 @@ internal static SigningStatus IsSignedVSIXByFileMarker(string filePath) SigningStatus.Signed : SigningStatus.NotSigned; } - internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string dotNetPathTooling, string pkgToolPath) + internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, string filePath, string pkgToolPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { @@ -182,7 +182,7 @@ internal static SigningStatus IsSignedPkgOrAppBundle(TaskLoggingHelper log, stri return SigningStatus.Unknown; } - return ZipData.RunPkgProcess(filePath, null, "verify", dotNetPathTooling, pkgToolPath) ? SigningStatus.Signed : SigningStatus.NotSigned; + return ZipData.RunPkgProcess(filePath, null, "verify", pkgToolPath) ? SigningStatus.Signed : SigningStatus.NotSigned; } public static SigningStatus IsSignedPE(string filePath) diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 0cb09b8327c..b07dd3668b8 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -54,19 +54,13 @@ internal ZipData(FileSignInfo fileSignInfo, ImmutableDictionary return null; } - public static IEnumerable ReadEntries( - string archivePath, - string tempDir, - string dotNetPathTooling, - string tarToolPath, - string pkgToolPath, - bool ignoreContent = false) + public static IEnumerable ReadEntries(string archivePath, string tempDir, string tarToolPath, string pkgToolPath, bool ignoreContent = false) { if (FileSignInfo.IsTarGZip(archivePath)) { // Tar APIs not available on .NET FX. We need sign tool to run on desktop msbuild because building VSIX packages requires desktop. #if NET472 - return ReadTarGZipEntries(archivePath, tempDir, dotNetPathTooling, tarToolPath, ignoreContent); + return ReadTarGZipEntries(archivePath, tempDir, tarToolPath, ignoreContent); #else return ReadTarGZipEntries(archivePath) .Select(static entry => new ZipDataEntry(entry.Name, entry.DataStream, entry.Length) @@ -77,7 +71,7 @@ public static IEnumerable ReadEntries( } else if (FileSignInfo.IsPkg(archivePath) || FileSignInfo.IsAppBundle(archivePath)) { - return ReadPkgOrAppBundleEntries(archivePath, tempDir, dotNetPathTooling, pkgToolPath, ignoreContent); + return ReadPkgOrAppBundleEntries(archivePath, tempDir, pkgToolPath, ignoreContent); } else if (FileSignInfo.IsDeb(archivePath)) { @@ -109,14 +103,7 @@ public static IEnumerable ReadEntries( /// /// Repack the zip container with the signed files. /// - public void Repack( - TaskLoggingHelper log, - string tempDir, - string wix3ToolsPath, - string wixToolsPath, - string dotNetPathTooling, - string tarToolPath, - string pkgToolPath) + public void Repack(TaskLoggingHelper log, string tempDir, string wix3ToolsPath, string wixToolsPath, string tarToolPath, string pkgToolPath) { #if NET472 if (FileSignInfo.IsVsix()) @@ -127,7 +114,7 @@ public void Repack( #endif if (FileSignInfo.IsTarGZip()) { - RepackTarGZip(log, tempDir, dotNetPathTooling, tarToolPath); + RepackTarGZip(log, tempDir, tarToolPath); } else if (FileSignInfo.IsUnpackableWixContainer()) { @@ -135,7 +122,7 @@ public void Repack( } else if (FileSignInfo.IsPkg() || FileSignInfo.IsAppBundle()) { - RepackPkgOrAppBundles(log, tempDir, dotNetPathTooling, pkgToolPath); + RepackPkgOrAppBundles(log, tempDir, pkgToolPath); } else if (FileSignInfo.IsDeb()) { @@ -309,12 +296,7 @@ private void RepackWixPack(TaskLoggingHelper log, string tempDir, string wix3Too } } - internal static bool RunPkgProcess( - string srcPath, - string dstPath, - string action, - string dotNetPathTooling, - string pkgToolPath) + internal static bool RunPkgProcess(string srcPath, string dstPath, string action, string pkgToolPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { @@ -330,7 +312,7 @@ internal static bool RunPkgProcess( var process = Process.Start(new ProcessStartInfo() { - FileName = dotNetPathTooling, + FileName = "dotnet", Arguments = $@"exec ""{pkgToolPath}"" {args}", UseShellExecute = false, RedirectStandardError = true @@ -340,17 +322,12 @@ internal static bool RunPkgProcess( return process.ExitCode == 0; } - private static IEnumerable ReadPkgOrAppBundleEntries( - string archivePath, - string tempDir, - string dotNetPathTooling, - string pkgToolPath, - bool ignoreContent) + private static IEnumerable ReadPkgOrAppBundleEntries(string archivePath, string tempDir, string pkgToolPath, bool ignoreContent) { string extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { - if (!RunPkgProcess(archivePath, extractDir, "unpack", dotNetPathTooling, pkgToolPath)) + if (!RunPkgProcess(archivePath, extractDir, "unpack", pkgToolPath)) { throw new Exception($"Failed to unpack pkg {archivePath}"); } @@ -374,7 +351,7 @@ private static IEnumerable ReadPkgOrAppBundleEntries( } } - private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string pkgToolPath) + private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string pkgToolPath) { #if NET472 throw new NotImplementedException("PKG manipulation is not supported on .NET Framework"); @@ -388,7 +365,7 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string string extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { - if (!RunPkgProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, "unpack", dotNetPathTooling, pkgToolPath)) + if (!RunPkgProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, "unpack", pkgToolPath)) { return; } @@ -412,7 +389,7 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string File.SetUnixFileMode(path, extractedFileMode); } - if (!RunPkgProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, "pack", dotNetPathTooling, pkgToolPath)) + if (!RunPkgProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, "pack", pkgToolPath)) { return; } @@ -428,11 +405,11 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string } #if NETFRAMEWORK - private static bool RunTarProcess(string srcPath, string dstPath, string dotNetPathTooling, string tarToolPath) + private static bool RunTarProcess(string srcPath, string dstPath, string tarToolPath) { var process = Process.Start(new ProcessStartInfo() { - FileName = dotNetPathTooling, + FileName = "dotnet", Arguments = $@"exec ""{tarToolPath}"" ""{srcPath}"" ""{dstPath}""", UseShellExecute = false }); @@ -441,19 +418,14 @@ private static bool RunTarProcess(string srcPath, string dstPath, string dotNetP return process.ExitCode == 0; } - private static IEnumerable ReadTarGZipEntries( - string archivePath, - string tempDir, - string dotNetPathTooling, - string tarToolPath, - bool ignoreContent) + private static IEnumerable ReadTarGZipEntries(string archivePath, string tempDir, string tarToolPath, bool ignoreContent) { var extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { Directory.CreateDirectory(extractDir); - if (!RunTarProcess(archivePath, extractDir, dotNetPathTooling, tarToolPath)) + if (!RunTarProcess(archivePath, extractDir, tarToolPath)) { throw new Exception($"Failed to unpack tar archive: {archivePath}"); } @@ -471,16 +443,16 @@ private static IEnumerable ReadTarGZipEntries( } } - private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string tarToolPath) + private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarToolPath) { var extractDir = Path.Combine(tempDir, Guid.NewGuid().ToString()); try { Directory.CreateDirectory(extractDir); - if (!RunTarProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, dotNetPathTooling, tarToolPath)) + if (!RunTarProcess(srcPath: FileSignInfo.FullPath, dstPath: extractDir, tarToolPath)) { - log.LogMessage(MessageImportance.Low, $"Failed to unpack tar archive: {dotNetPathTooling} {tarToolPath} {FileSignInfo.FullPath}"); + log.LogMessage(MessageImportance.Low, $"Failed to unpack tar archive: dotnet {tarToolPath} {FileSignInfo.FullPath}"); return; } @@ -499,9 +471,9 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetP File.Copy(signedPart.Value.FileSignInfo.FullPath, path, overwrite: true); } - if (!RunTarProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, dotNetPathTooling, tarToolPath)) + if (!RunTarProcess(srcPath: extractDir, dstPath: FileSignInfo.FullPath, tarToolPath)) { - log.LogMessage(MessageImportance.Low, $"Failed to pack tar archive: {dotNetPathTooling} {tarToolPath} {FileSignInfo.FullPath}"); + log.LogMessage(MessageImportance.Low, $"Failed to pack tar archive: dotnet {tarToolPath} {FileSignInfo.FullPath}"); return; } } @@ -511,7 +483,7 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetP } } #else - private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string dotNetPathTooling, string tarToolPath) + private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarToolPath) { using MemoryStream streamToCompress = new(); using (TarWriter writer = new(streamToCompress, leaveOpen: true)) From eb29fbaa4fe1d32e53d9d5875ddd5ee801475124 Mon Sep 17 00:00:00 2001 From: Ella Hathaway <67609881+ellahathaway@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:30:33 -0800 Subject: [PATCH 044/100] [release/10.0] Install MicroBuild in temporary directory (#16307) (#16342) --- .../steps/install-microbuild.yml | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index d6b9878f54d..553fce66b94 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -11,9 +11,8 @@ parameters: # Unfortunately, _SignType can't be used to exclude the use of the service connection in non-real sign scenarios. The # variable is not available in template expression. _SignType has a very large proliferation across .NET, so replacing it is tough. microbuildUseESRP: true - # Location of the MicroBuild output folder - # NOTE: There's something that relies on this being in the "default" source directory for tasks such as Signing to work properly. - microBuildOutputFolder: '$(Build.SourcesDirectory)' + # Microbuild installation directory + microBuildOutputFolder: $(Agent.TempDirectory)/MicroBuild continueOnError: false @@ -26,8 +25,27 @@ steps: inputs: packageType: sdk version: 8.0.x - installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet - workingDirectory: ${{ parameters.microBuildOutputFolder }} + installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet-microbuild + condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) + + - script: | + set -euo pipefail + + # UseDotNet@2 prepends the dotnet executable path to the PATH variable, so we can call dotnet directly + version=$(dotnet --version) + cat << 'EOF' > ${{ parameters.microBuildOutputFolder }}/global.json + { + "sdk": { + "version": "$version", + "paths": [ + "${{ parameters.microBuildOutputFolder }}/.dotnet-microbuild" + ], + "errorMessage": "The .NET SDK version $version is required to install the MicroBuild signing plugin." + } + } + EOF + displayName: 'Add global.json to MicroBuild Installation path' + workingDirectory: ${{ parameters.microBuildOutputFolder }} condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - script: | @@ -77,6 +95,7 @@ steps: signType: $(_SignType) zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + workingDirectory: ${{ parameters.microBuildOutputFolder }} ${{ if eq(parameters.microbuildUseESRP, true) }}: ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: From f1d31d0a6c11a3f4111c39d5c5f13dbbbcc4e184 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:49:04 -0800 Subject: [PATCH 045/100] [release/10.0] Update dependencies from dotnet/xharness (#16346) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 2 +- eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index a1ca91b9461..c7c44f559dd 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -22,7 +22,7 @@ This file should be imported by eng/Versions.props 1.1.0-beta.25424.1 1.1.0-beta.25424.1 - 10.0.0-prerelease.25412.1 + 10.0.0-prerelease.25570.1 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 92435edfa4c..a58bb82130b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - fe850ca677efa9ed62009b929f55ff59d5e24c0e + a55de89dc6c6a094629e11bdc0b6078826831740 https://github.com/dotnet/roslyn From b94b1e6caf7b46c8b51a5d2f8d8f398215314036 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:28:38 +0000 Subject: [PATCH 046/100] [release/10.0] Use CDN URL instead of blob in FindDotNetCliPackage (#16350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs index 490e2238bdb..76a6faa1848 100644 --- a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs +++ b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs @@ -109,7 +109,7 @@ private async Task FindCliPackage() { NormalizeParameters(); var feeds = new List(); - feeds.Add(new MSBuild.TaskItem("https://dotnetcli.blob.core.windows.net/dotnet")); + feeds.Add(new MSBuild.TaskItem("https://builds.dotnet.microsoft.com/dotnet")); feeds.Add(new MSBuild.TaskItem("https://ci.dot.net/public")); if (AdditionalFeeds != null) { From 2daeb3ef7f6851bd6aa9a39fb6ed03830e8572b9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:07:21 +0000 Subject: [PATCH 047/100] [release/10.0] Remove audit sources from NuGet.config (#16357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- NuGet.config | 4 ---- 1 file changed, 4 deletions(-) diff --git a/NuGet.config b/NuGet.config index 98d5bfbc246..20c5dc3fe74 100644 --- a/NuGet.config +++ b/NuGet.config @@ -54,8 +54,4 @@ - - - - From 19e800e4e5495b5f17dce1cd4299e67036dd1209 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 20:23:27 +0100 Subject: [PATCH 048/100] [release/10.0] Source code updates from dotnet/dotnet (#16356) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- src/Microsoft.DotNet.SignTool/src/ZipData.cs | 36 ++++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a58bb82130b..01998ae05e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/Microsoft.DotNet.SignTool/src/ZipData.cs index b07dd3668b8..8f8a0ffb0cb 100644 --- a/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -584,9 +584,9 @@ private string GetUpdatedControlArchive(TaskLoggingHelper log, string debianPack Directory.CreateDirectory(dataLayout); // Get the original control archive - to reuse package metadata and scripts - var entry = ReadDebContainerEntries(debianPackage, "control.tar").Single(); - string controlArchive = Path.Combine(workingDir, entry.RelativePath); - entry.WriteToFile(controlArchive); + var controlEntry = ReadDebContainerEntries(debianPackage, "control.tar").Single(); + string controlArchive = Path.Combine(workingDir, controlEntry.RelativePath); + controlEntry.WriteToFile(controlArchive); ExtractTarballContents(log, dataArchive, dataLayout); ExtractTarballContents(log, controlArchive, controlLayout); @@ -619,11 +619,33 @@ private string GetUpdatedControlArchive(TaskLoggingHelper log, string debianPack File.WriteAllText(controlFile, fileContents); } - // Repack the control tarball - using (var dstStream = File.Open(controlArchive, FileMode.Create)) + // Update the control tarball contents. We update the contents of the control entry streams + // rather than recreating from the unpacked directory layout to ensure that + // the original entry field metadata and tar format is preserved. + using MemoryStream streamToCompress = new(); + using (TarWriter writer = new(streamToCompress, leaveOpen: true)) { - using var gzip = new GZipStream(dstStream, CompressionMode.Compress); - TarFile.CreateFromDirectory(controlLayout, gzip, includeBaseDirectory: false); + foreach (TarEntry entry in ReadTarGZipEntries(controlArchive)) + { + string relativeName = entry.Name; + if (relativeName is "./control" or "./md5sums") + { + using FileStream fileStream = File.OpenRead(Path.Combine(controlLayout, relativeName)); + entry.DataStream = fileStream; + entry.DataStream.Position = 0; + writer.WriteEntry(entry); + continue; + } + + writer.WriteEntry(entry); + } + } + + streamToCompress.Position = 0; + using (FileStream outputStream = File.Open(controlArchive, FileMode.Create)) + { + using GZipStream compressor = new(outputStream, CompressionMode.Compress); + streamToCompress.CopyTo(compressor); } return controlArchive; From c6fd10d86918d2bf2fa0ed7611a20f009ef02fd9 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:10:58 -0800 Subject: [PATCH 049/100] [release/10.0] Update dependencies from dotnet/xharness (#16363) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 2 +- eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index c7c44f559dd..f26b9239906 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -22,7 +22,7 @@ This file should be imported by eng/Versions.props 1.1.0-beta.25424.1 1.1.0-beta.25424.1 - 10.0.0-prerelease.25570.1 + 10.0.0-prerelease.25575.2 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 01998ae05e1..bab3992301c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - a55de89dc6c6a094629e11bdc0b6078826831740 + bdf0f92540d68762bb028b8cb859146048f3c356 https://github.com/dotnet/roslyn From 93816e21093d25b3e038376f76ddb027528c7152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 2 Dec 2025 19:42:54 +0100 Subject: [PATCH 050/100] [release/10.0] Update Windows pool images from VS2022 to VS2026 Preview Scout (#16365) --- eng/common/core-templates/job/source-index-stage1.yml | 4 ++-- eng/common/core-templates/post-build/post-build.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml index 30530359a5d..58b7a76814e 100644 --- a/eng/common/core-templates/job/source-index-stage1.yml +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -25,10 +25,10 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $(DncEngPublicBuildPool) - image: windows.vs2022.amd64.open + image: windows.vs2026preview.scout.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 + image: windows.vs2026preview.scout.amd64 steps: - ${{ if eq(parameters.is1ESPipeline, '') }}: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index eb043787197..9423d71ca3a 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -127,11 +127,11 @@ stages: ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 + image: windows.vs2026preview.scout.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2022.amd64 + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -175,7 +175,7 @@ stages: os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2022.amd64 + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: @@ -236,7 +236,7 @@ stages: os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2022.amd64 + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: From 93a17c71536d794e248c077bb66ad6cee000eada Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:10:46 +0000 Subject: [PATCH 051/100] [release/10.0] Add detached signature CertificatesSignInfo (#16372) Co-authored-by: Michael Simons --- src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props | 2 ++ src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs | 2 +- src/Microsoft.DotNet.SignTool/src/SignToolTask.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props index 38e60705fed..3ff26ef1da2 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props @@ -30,6 +30,8 @@ + + - 10.0.0-prerelease.25575.2 + 10.0.0-prerelease.25605.1 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index bab3992301c..1a0dfc994c0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - bdf0f92540d68762bb028b8cb859146048f3c356 + ccb455efce66706ea8b3927fefb425f621e5af0a https://github.com/dotnet/roslyn From 8d3de8e3bac9556115664bb8aa7e162dde3cedef Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:34:24 +0000 Subject: [PATCH 055/100] [release/10.0] Update dependencies from dotnet/arcade (#16389) [release/10.0] Update dependencies from dotnet/arcade --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 8da1a62d63d..43861da6649 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25507.1 - 10.0.0-beta.25507.1 + 10.0.0-beta.25608.3 + 10.0.0-beta.25608.3 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1a0dfc994c0..d86790c9f41 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 4eaa220ea860cee9fa61df42411bbf79394edd23 + 6605fb5db42d56000a040ed7a0736bc505be9743 - + https://github.com/dotnet/arcade - 4eaa220ea860cee9fa61df42411bbf79394edd23 + 6605fb5db42d56000a040ed7a0736bc505be9743 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 257233c5a6d..5cbbc0dad8f 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.100" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25507.1", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25507.1", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25608.3", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25608.3", "Microsoft.Build.NoTargets": "3.7.0" } } From 9f286ddee40065ea225611cb43ab0415e48994c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 11 Dec 2025 12:17:09 +0100 Subject: [PATCH 056/100] [release/10.0] Always pass -UseBasicParsing for Invoke-WebRequest calls in powershell (#16397) --- .github/workflows/scripts/read-configuration.ps1 | 2 +- Documentation/Policy/PowershellBestPractices.md | 4 ++++ eng/common/internal-feed-operations.ps1 | 2 +- eng/common/post-build/nuget-verification.ps1 | 2 +- eng/common/tools.ps1 | 6 +++--- eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 | 4 ++-- scripts/add-build-variables.ps1 | 6 +++--- scripts/cqb-utilities.ps1 | 2 +- scripts/duplicate-feed.ps1 | 6 +++--- scripts/launch-mirrors.ps1 | 2 +- scripts/list-dotnet-install-versions.ps1 | 2 +- scripts/pause-all-pipelines.ps1 | 6 +++--- 12 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.github/workflows/scripts/read-configuration.ps1 b/.github/workflows/scripts/read-configuration.ps1 index 1aeae31238e..8fd99968fda 100644 --- a/.github/workflows/scripts/read-configuration.ps1 +++ b/.github/workflows/scripts/read-configuration.ps1 @@ -53,7 +53,7 @@ function GetConfiguration { Write-Host "Fetching configuration file from $urlToConfigurationFile" try{ - $response = Invoke-WebRequest -Method GET -MaximumRetryCount 3 -Headers $headers ` + $response = Invoke-WebRequest -UseBasicParsing -Method GET -MaximumRetryCount 3 -Headers $headers ` $urlToConfigurationFile $mergeFlowConfig = ConvertFrom-Json -InputObject $response.Content -AsHashTable diff --git a/Documentation/Policy/PowershellBestPractices.md b/Documentation/Policy/PowershellBestPractices.md index ff25d5a91ec..27a9c9abe05 100644 --- a/Documentation/Policy/PowershellBestPractices.md +++ b/Documentation/Policy/PowershellBestPractices.md @@ -169,3 +169,7 @@ will force this behavior. Was this helpful? [![Yes](https://helix.dot.net/f/ip/5?p=Documentation%5CPolicy%5CPowershellBestPractices.md)](https://helix.dot.net/f/p/5?p=Documentation%5CPolicy%5CPowershellBestPractices.md) [![No](https://helix.dot.net/f/in)](https://helix.dot.net/f/n/5?p=Documentation%5CPolicy%5CPowershellBestPractices.md) + +## Always pass -UseBasicParsing to Invoke-WebRequest + +To prevent blocking execution on older versions of PowerShell after the [KB5074596](https://support.microsoft.com/help/5074596) security update. diff --git a/eng/common/internal-feed-operations.ps1 b/eng/common/internal-feed-operations.ps1 index 92b77347d99..c282d3ae403 100644 --- a/eng/common/internal-feed-operations.ps1 +++ b/eng/common/internal-feed-operations.ps1 @@ -26,7 +26,7 @@ function SetupCredProvider { $url = 'https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1' Write-Host "Writing the contents of 'installcredprovider.ps1' locally..." - Invoke-WebRequest $url -OutFile installcredprovider.ps1 + Invoke-WebRequest $url -UseBasicParsing -OutFile installcredprovider.ps1 Write-Host 'Installing plugin...' .\installcredprovider.ps1 -Force diff --git a/eng/common/post-build/nuget-verification.ps1 b/eng/common/post-build/nuget-verification.ps1 index ac5c69ffcac..eea88e653c9 100644 --- a/eng/common/post-build/nuget-verification.ps1 +++ b/eng/common/post-build/nuget-verification.ps1 @@ -65,7 +65,7 @@ if ($NuGetExePath) { Write-Host "Downloading nuget.exe from $nugetExeUrl..." $ProgressPreference = 'SilentlyContinue' try { - Invoke-WebRequest $nugetExeUrl -OutFile $downloadedNuGetExe + Invoke-WebRequest $nugetExeUrl -UseBasicParsing -OutFile $downloadedNuGetExe $ProgressPreference = 'Continue' } catch { $ProgressPreference = 'Continue' diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 578705ee4db..bef4affa4a4 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -277,7 +277,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { Retry({ Write-Host "GET $uri" - Invoke-WebRequest $uri -OutFile $installScript + Invoke-WebRequest $uri -UseBasicParsing -OutFile $installScript }) } @@ -510,7 +510,7 @@ function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) { Write-Host "Downloading $packageName $packageVersion" $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit Retry({ - Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -OutFile $packagePath + Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -UseBasicParsing -OutFile $packagePath }) if (!(Test-Path $packagePath)) { @@ -556,7 +556,7 @@ function LocateVisualStudio([object]$vsRequirements = $null){ Write-Host "Downloading vswhere $vswhereVersion" $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit Retry({ - Invoke-WebRequest "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/vswhere/$vswhereVersion/vswhere.exe" -OutFile $vswhereExe + Invoke-WebRequest "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/vswhere/$vswhereVersion/vswhere.exe" -UseBasicParsing -OutFile $vswhereExe }) } diff --git a/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 b/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 index 91bc94cf866..766b683ed42 100644 --- a/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 +++ b/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 @@ -21,14 +21,14 @@ if(-Not (Test-Path $destinationDir)) New-Item -ItemType 'Directory' -Path "$destinationDir" -Force | Out-Null } # Query the page to get the download link -$response = Invoke-WebRequest $downloadUrl +$response = Invoke-WebRequest $downloadUrl -UseBasicParsing $regex = "downloadUrl: '(?[^']+)'" $response.Content -Match $regex | Out-Null $downloadLink = $Matches['downloadUrl'] Write-Host "download link: $downloadLink" -$response = Invoke-WebRequest $downloadLink -OutFile "$installerPath" +$response = Invoke-WebRequest $downloadLink -UseBasicParsing -OutFile "$installerPath" if(-Not (Test-Path $outputDirectory)) { diff --git a/scripts/add-build-variables.ps1 b/scripts/add-build-variables.ps1 index cb0137303b0..de1fcf2084a 100644 --- a/scripts/add-build-variables.ps1 +++ b/scripts/add-build-variables.ps1 @@ -47,7 +47,7 @@ param ( function UpdatePipeline($id, $authHeaders) { $pipelineUri = "https://dev.azure.com/$Org/$Project/_apis/build/definitions/$($id)?api-version=6.1-preview.7" - $existingPipeline = Invoke-WebRequest -Headers $authHeaders -Method Get -Uri $pipelineUri -ContentType 'application/json' | ConvertFrom-Json + $existingPipeline = Invoke-WebRequest -UseBasicParsing -Headers $authHeaders -Method Get -Uri $pipelineUri -ContentType 'application/json' | ConvertFrom-Json Write-Host "Updating pipeline $Org/$Project/$($existingPipeline.name) (pipeline id: $id)" # Update the variables with the new one if not already done. If variable value is null, remove @@ -72,7 +72,7 @@ function UpdatePipeline($id, $authHeaders) # Attempt the update $bodyJson = $existingPipeline | ConvertTo-Json -Depth 10 - $updatedPipelineJson = Invoke-WebRequest -Headers $azdoAuthHeader -Method Put $pipelineUri -Body $bodyJson -ContentType 'application/json' | ConvertFrom-Json + $updatedPipelineJson = Invoke-WebRequest -UseBasicParsing -Headers $azdoAuthHeader -Method Put $pipelineUri -Body $bodyJson -ContentType 'application/json' | ConvertFrom-Json } $base64authinfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$AzDoPAT")) @@ -81,7 +81,7 @@ $azdoAuthHeader = @{"Authorization"="Basic $base64authinfo"} if ($PipelineId) { UpdatePipeline $PipelineId $azdoAuthHeader } else { - $allPipelines = Invoke-WebRequest -ContentType 'application/json' -Method Get -Headers $azdoAuthHeader -Uri "https://dev.azure.com/$Org/$Project/_apis/build/definitions?api-version=6.1-preview.7" | ConvertFrom-Json + $allPipelines = Invoke-WebRequest -UseBasicParsing -ContentType 'application/json' -Method Get -Headers $azdoAuthHeader -Uri "https://dev.azure.com/$Org/$Project/_apis/build/definitions?api-version=6.1-preview.7" | ConvertFrom-Json Write-Host "Updating $($allPipelines.count) pipelines" foreach ($pipeline in $allPipelines.value) { UpdatePipeline $pipeline.Id $azdoAuthHeader diff --git a/scripts/cqb-utilities.ps1 b/scripts/cqb-utilities.ps1 index a792c7271bc..c3e4ad35b96 100644 --- a/scripts/cqb-utilities.ps1 +++ b/scripts/cqb-utilities.ps1 @@ -48,7 +48,7 @@ Function Gen-Internal-Merge-PR } try { - $resp = Invoke-WebRequest -Method Post -Body $($prBody | ConvertTo-Json) -Headers $header -Uri "https://dev.azure.com/$Org/$Project/_apis/git/repositories/$RepoName/pullrequests?api-version=7.0" -ContentType application/json + $resp = Invoke-WebRequest -UseBasicParsing -Method Post -Body $($prBody | ConvertTo-Json) -Headers $header -Uri "https://dev.azure.com/$Org/$Project/_apis/git/repositories/$RepoName/pullrequests?api-version=7.0" -ContentType application/json $result = $resp | ConvertFrom-Json Write-Host https://dev.azure.com/$Org/$Project/_git/$RepoName/pullrequest/$($result.pullRequestId) } catch { diff --git a/scripts/duplicate-feed.ps1 b/scripts/duplicate-feed.ps1 index 7a27caa6b7c..fa67529d6ec 100644 --- a/scripts/duplicate-feed.ps1 +++ b/scripts/duplicate-feed.ps1 @@ -48,11 +48,11 @@ function Get-Package-List($vstsAuthHeader, $account, $visibility, $feed) { try { $packageListUri = "https://feeds.dev.azure.com/$account/${visibility}_apis/packaging/Feeds/$feed/packages?api-version=5.1-preview.1" Write-Host "Looking up packages on feed at: $packageListUri" - $result = Invoke-WebRequest -Headers $vstsAuthHeader $packageListUri + $result = Invoke-WebRequest -UseBasicParsing -Headers $vstsAuthHeader $packageListUri $resultJson = $result | ConvertFrom-Json Write-Host "Feed $SourceFeedUri has $($resultJson.count) packages" foreach ($package in $resultJson.value) { - $versionsResult = Invoke-WebRequest -Headers $vstsAuthHeader $package._links.versions.href + $versionsResult = Invoke-WebRequest -UseBasicParsing -Headers $vstsAuthHeader $package._links.versions.href $versionsResultJson = $versionsResult | ConvertFrom-Json foreach ($version in $versionsResultJson.value) { if (-not $version.isDeleted) { @@ -118,7 +118,7 @@ foreach ($packageToCopy in $listOfSourcePackages) { $packageContentUrl = "https://pkgs.dev.azure.com/$($sourceFeedInfo.account)/$($sourceFeedInfo.visibility)_apis/packaging/feeds/$($sourceFeedInfo.feed)/nuget/packages/$($packageToCopy.name)/versions/$($packageToCopy.version)/content"; Write-Host "Downloading package $($packageToCopy.name) @ $($packageToCopy.version) from $packageContentUrl" $localPackagePath = Join-Path -Path $downloadRoot -ChildPath "$($packageToCopy.name).$($packageToCopy.version).nupkg" - Invoke-WebRequest -Headers $vstsAuthHeader $packageContentUrl -OutFile $localPackagePath + Invoke-WebRequest -UseBasicParsing -Headers $vstsAuthHeader $packageContentUrl -OutFile $localPackagePath & $NugetPath push -Source $TargetFeedUri -ApiKey AzureDevOps $localPackagePath -SkipDuplicate } catch { Write-Error $_ diff --git a/scripts/launch-mirrors.ps1 b/scripts/launch-mirrors.ps1 index 71c4ea59d8e..4bc09eb66fd 100644 --- a/scripts/launch-mirrors.ps1 +++ b/scripts/launch-mirrors.ps1 @@ -59,7 +59,7 @@ function LaunchMirrorBuild { $bodyStr = ConvertTo-Json $body $uri = "${AzDOInstance}/_apis/build/builds?api-version=5.1" Write-Host "Launching $mirrorType build for $repo @ $branch" - $queueResponse = Invoke-WebRequest -Method Post -ContentType "application/json" -Headers $AzDOAuthHeader -Uri "${AzDOInstance}/_apis/build/builds?api-version=5.1" -Body $bodyStr | ConvertFrom-Json + $queueResponse = Invoke-WebRequest -UseBasicParsing -Method Post -ContentType "application/json" -Headers $AzDOAuthHeader -Uri "${AzDOInstance}/_apis/build/builds?api-version=5.1" -Body $bodyStr | ConvertFrom-Json $buildId = $queueResponse.id Write-Host "Launched $AzDOInstance/_build/results?buildId=$buildId" } diff --git a/scripts/list-dotnet-install-versions.ps1 b/scripts/list-dotnet-install-versions.ps1 index b12ce62e100..ea15a791e92 100644 --- a/scripts/list-dotnet-install-versions.ps1 +++ b/scripts/list-dotnet-install-versions.ps1 @@ -62,7 +62,7 @@ $productVersions += $queries | ForEach-Object -Parallel { } try { - $response = Invoke-WebRequest $versionStringUrl + $response = Invoke-WebRequest $versionStringUrl -UseBasicParsing # It's difficult to tell whether the aka.ms link 404s or not, since it redirects in case of # a not found. diff --git a/scripts/pause-all-pipelines.ps1 b/scripts/pause-all-pipelines.ps1 index e109d06e079..47c0de52c6a 100644 --- a/scripts/pause-all-pipelines.ps1 +++ b/scripts/pause-all-pipelines.ps1 @@ -26,7 +26,7 @@ param ( $base64authinfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$AzDOPat")) $AzDOAuthHeader = @{"Authorization"="Basic $base64authinfo"} -$allPipelines = Invoke-WebRequest -Uri "https://dev.azure.com/$Organization/$Project/_apis/build/definitions?api-version=6.0" -Headers $AzDOAuthHeader | ConvertFrom-Json +$allPipelines = Invoke-WebRequest -UseBasicParsing -Uri "https://dev.azure.com/$Organization/$Project/_apis/build/definitions?api-version=6.0" -Headers $AzDOAuthHeader | ConvertFrom-Json $queueStatusString = $null $uxString = $null @@ -48,13 +48,13 @@ foreach ($pipeline in $allPipelines.value) { Write-Host -NoNewLine " $uxString '$($pipeline.name)' (id: $pipelineId)..." $pipelineUri = "https://dev.azure.com/$Organization/$Project/_apis/build/definitions/$($pipelineId)?api-version=6.0" - $pipelineInfo = Invoke-WebRequest -Uri $pipelineUri -Headers $AzDOAuthHeader | ConvertFrom-Json + $pipelineInfo = Invoke-WebRequest -UseBasicParsing -Uri $pipelineUri -Headers $AzDOAuthHeader | ConvertFrom-Json # Update the pipeline $pipelineInfo.queueStatus = $queueStatusString #update the definition - $result = Invoke-WebRequest -Uri $pipelineUri -Headers $AzDOAuthHeader -Method Put -Body (ConvertTo-Json $pipelineInfo -Depth 100) -ContentType "application/json" + $result = Invoke-WebRequest -UseBasicParsing -Uri $pipelineUri -Headers $AzDOAuthHeader -Method Put -Body (ConvertTo-Json $pipelineInfo -Depth 100) -ContentType "application/json" if ($result.StatusCode -eq 200) { Write-Host "done" From cae95dc17801ee9c7f24cc01d91a34cf220debe3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:44:23 +0100 Subject: [PATCH 057/100] [release/10.0] Source code updates from dotnet/dotnet (#16400) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs | 1 + src/SignCheck/Microsoft.SignCheck/Utils.cs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d86790c9f41..265e4993eff 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs index 4d47364f24a..da4b1ae2610 100644 --- a/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs +++ b/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs @@ -263,6 +263,7 @@ private static void DownloadAndConfigurePublicKeys(string tempDir) string[] keyUrls = new string[] { "https://packages.microsoft.com/keys/microsoft.asc", // SHA-1 Microsoft public key + "https://packages.microsoft.com/keys/microsoft-2025.asc", // Microsoft public key for new repos not compatible with SHA-1 "https://packages.microsoft.com/keys/microsoft-rolling.asc", // Non-SHA1 Microsoft public keys for non-Azure Linux distributions "https://raw.githubusercontent.com/microsoft/azurelinux/3.0/SPECS/azurelinux-repos/MICROSOFT-RPM-GPG-KEY" // Azure linux public key }; diff --git a/src/SignCheck/Microsoft.SignCheck/Utils.cs b/src/SignCheck/Microsoft.SignCheck/Utils.cs index 65f03d27215..a93733604c4 100644 --- a/src/SignCheck/Microsoft.SignCheck/Utils.cs +++ b/src/SignCheck/Microsoft.SignCheck/Utils.cs @@ -200,6 +200,7 @@ public static void DownloadAndConfigurePublicKeys(string tempDir) string[] keyUrls = new string[] { "https://packages.microsoft.com/keys/microsoft.asc", // Microsoft public key + "https://packages.microsoft.com/keys/microsoft-2025.asc", // Microsoft public key for distributions that do not allow SHA1 "https://packages.microsoft.com/keys/microsoft-rolling.asc", // Non-SHA1 Microsoft public keys for non-Azure Linux distributions "https://raw.githubusercontent.com/microsoft/azurelinux/3.0/SPECS/azurelinux-repos/MICROSOFT-RPM-GPG-KEY" // Azure linux public key }; From 676d6a2d75c2a2c7c885e0bd95168512efca62fc Mon Sep 17 00:00:00 2001 From: Marc Paine Date: Fri, 14 Nov 2025 10:27:34 -0800 Subject: [PATCH 058/100] Refactor vsRequirements assignment logic in tools.ps1 Because 17.14 went stable, we needed a way to run vswhere, find the VS node, and enable preview SDKs. Noah found a way to do this but he had to modify tools.ps1 because the SDK repo didn't have a VS node in our global.json https://github.com/dotnet/sdk/pull/51558/files#diff-72b8f8e899b94872c6ead31fd06ec109da15bcb9ad2af6e78103d6763a31c637 Porting his change here to see if folks want this centrally. Alternatively, I'm trying adding the vs node to global.json to see if it unblocks us. --- eng/common/tools.ps1 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index bef4affa4a4..049fe6db994 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -560,19 +560,26 @@ function LocateVisualStudio([object]$vsRequirements = $null){ }) } - if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs } + if (!$vsRequirements) { + if (Get-Member -InputObject $GlobalJson.tools -Name 'vs' -ErrorAction SilentlyContinue) { + $vsRequirements = $GlobalJson.tools.vs + } else { + $vsRequirements = $null + } + } + $args = @('-latest', '-format', 'json', '-requires', 'Microsoft.Component.MSBuild', '-products', '*') if (!$excludePrereleaseVS) { $args += '-prerelease' } - if (Get-Member -InputObject $vsRequirements -Name 'version') { + if ($vsRequirements -and (Get-Member -InputObject $vsRequirements -Name 'version' -ErrorAction SilentlyContinue)) { $args += '-version' $args += $vsRequirements.version } - if (Get-Member -InputObject $vsRequirements -Name 'components') { + if ($vsRequirements -and (Get-Member -InputObject $vsRequirements -Name 'components' -ErrorAction SilentlyContinue)) { foreach ($component in $vsRequirements.components) { $args += '-requires' $args += $component From b451a05bbc73248819e3248ee0fe7982bdc4ba33 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:54:43 +0100 Subject: [PATCH 059/100] [release/10.0] Source code updates from dotnet/dotnet (#16411) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- global.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 265e4993eff..25f947b30e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/global.json b/global.json index 5cbbc0dad8f..90c2d0c8035 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100", + "version": "10.0.101", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.100" + "dotnet": "10.0.101" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25608.3", From d8dca0b41b903e7182e64543773390b969dab96b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:58:19 +0100 Subject: [PATCH 060/100] [release/10.0] Update dependencies from dotnet/arcade (#16414) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 43861da6649..fb420c73e6a 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25608.3 - 10.0.0-beta.25608.3 + 10.0.0-beta.25612.5 + 10.0.0-beta.25612.5 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 25f947b30e1..0bf77a32dc7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 6605fb5db42d56000a040ed7a0736bc505be9743 + 55631983c8583162122687fddeac13424c1e40a8 - + https://github.com/dotnet/arcade - 6605fb5db42d56000a040ed7a0736bc505be9743 + 55631983c8583162122687fddeac13424c1e40a8 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 90c2d0c8035..ac7ddb5efeb 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.101" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25608.3", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25608.3", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25612.5", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25612.5", "Microsoft.Build.NoTargets": "3.7.0" } } From a3f5c45f6be2f2959069f69ece11ae8daa880124 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:13:44 -0800 Subject: [PATCH 061/100] [release/10.0] Update dependencies from dotnet/arcade (#16426) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index fb420c73e6a..9b013ec6acc 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.25612.5 - 10.0.0-beta.25612.5 + 10.0.0-beta.25626.5 + 10.0.0-beta.25626.5 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0bf77a32dc7..58fc3be4728 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 55631983c8583162122687fddeac13424c1e40a8 + d8dca0b41b903e7182e64543773390b969dab96b - + https://github.com/dotnet/arcade - 55631983c8583162122687fddeac13424c1e40a8 + d8dca0b41b903e7182e64543773390b969dab96b https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index ac7ddb5efeb..5ee21614359 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.101" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25612.5", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25612.5", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25626.5", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25626.5", "Microsoft.Build.NoTargets": "3.7.0" } } From 13323fc374efc77953ec0ac9a0927da69f14a584 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:10:01 +0000 Subject: [PATCH 062/100] [release/10.0] Add DoNotUnpack flag to sign containers without extracting contents (#16433) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ellahathaway <67609881+ellahathaway@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- .../SignToolTests.cs | 272 +++++++++++------- .../src/Configuration.cs | 96 ++++--- ...rtificateKey.cs => ExplicitSignInfoKey.cs} | 17 +- .../src/FileSignInfo.cs | 5 +- .../src/FileSignInfoEntry.cs | 21 ++ src/Microsoft.DotNet.SignTool/src/SignInfo.cs | 34 ++- .../src/SignToolConstants.cs | 6 + .../src/SignToolTask.cs | 30 +- 8 files changed, 316 insertions(+), 165 deletions(-) rename src/Microsoft.DotNet.SignTool/src/{ExplicitCertificateKey.cs => ExplicitSignInfoKey.cs} (60%) create mode 100644 src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 488ddf0e89e..879174e3149 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -327,7 +327,7 @@ public void Dispose() private void ValidateGeneratedProject( List itemsToSign, Dictionary> strongNameSignInfo, - Dictionary fileSignInfo, + Dictionary fileSignInfo, Dictionary> extensionsSignInfo, string[] expectedXmlElementsPerSigningRound, Dictionary> additionalCertificateInfo = null, @@ -381,7 +381,7 @@ private void ValidateGeneratedProject( private void ValidateFileSignInfos( List itemsToSign, Dictionary> strongNameSignInfo, - Dictionary fileSignInfo, + Dictionary fileSignInfo, Dictionary> extensionsSignInfo, string[] expected, string[] expectedCopyFiles = null, @@ -531,7 +531,7 @@ public void EmptySigningList() var strongNameSignInfo = new Dictionary>(); - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var task = new SignToolTask { BuildEngine = new FakeBuildEngine() }; var signingInput = new Configuration(_tmpDir, itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, null, null, tarToolPath: s_tarToolPath, pkgToolPath: s_pkgToolPath, snPath: s_snPath, task.Log).GenerateListOfFiles(); @@ -598,7 +598,7 @@ public void OnlyContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -633,10 +633,10 @@ public void SkipSigning() }; // Overriding information - var fileSignInfo = new Dictionary + var fileSignInfo = new Dictionary { - { new ExplicitCertificateKey("NativeLibrary.dll"), "None" }, - { new ExplicitCertificateKey("ProjectOne.dll", publicKeyToken: "581d91ccdfc4ea9c", targetFramework: ".NETCoreApp,Version=v2.1"), "None" } + { new ExplicitSignInfoKey("NativeLibrary.dll"), new FileSignInfoEntry("None") }, + { new ExplicitSignInfoKey("ProjectOne.dll", publicKeyToken: "581d91ccdfc4ea9c", targetFramework: ".NETCoreApp,Version=v2.1"), new FileSignInfoEntry("None") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -666,7 +666,7 @@ public void SkipStrongNamingForAlreadyStrongNamedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, Array.Empty()); } @@ -687,7 +687,7 @@ public void DoNotSkipStrongNamingForDelaySignedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -711,7 +711,7 @@ public void SkipStrongNamingForCrossGennedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -735,7 +735,7 @@ public void SkipStrongNamingBinaryButDontSkipAuthenticode() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -763,7 +763,7 @@ public void OnlyAuthenticodeSignByPKT() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary>(), new[] { @@ -796,9 +796,9 @@ public void OnlyContainerAndOverridingByPKT() }; // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c"), "OverriddenCertificate" } + { new ExplicitSignInfoKey("ProjectOne.dll", "581d91ccdfc4ea9c"), new FileSignInfoEntry("OverriddenCertificate") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -838,10 +838,10 @@ public void OnlyContainerAndOverridingByFileName() }; // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("NativeLibrary.dll", collisionPriorityId: "123"), "OverriddenCertificate1" }, - { new ExplicitCertificateKey("ProjectOne.dll", collisionPriorityId: "123"), "3PartySHA2" } + { new ExplicitSignInfoKey("NativeLibrary.dll", collisionPriorityId: "123"), new FileSignInfoEntry("OverriddenCertificate1") }, + { new ExplicitSignInfoKey("ProjectOne.dll", collisionPriorityId: "123"), new FileSignInfoEntry("3PartySHA2") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] @@ -878,9 +878,9 @@ public void EmptyPKT() }; // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("EmptyPKT.dll"), "3PartySHA2" } + { new ExplicitSignInfoKey("EmptyPKT.dll"), new FileSignInfoEntry("3PartySHA2") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -907,9 +907,9 @@ public void CrossGenerated() }; // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("EmptyPKT.dll", collisionPriorityId: "123"), "3PartySHA2" } + { new ExplicitSignInfoKey("EmptyPKT.dll", collisionPriorityId: "123"), new FileSignInfoEntry("3PartySHA2") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary>(), new[] @@ -943,7 +943,7 @@ public void DefaultCertificateForAssemblyWithoutStrongName() { "", new List{ new SignInfo("3PartySHA2", collisionPriorityId: "123") } } }; - var fileSignInfo = new Dictionary() { }; + var fileSignInfo = new Dictionary() { }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -965,9 +965,9 @@ public void CustomTargetFrameworkAttribute() { "", new List{ new SignInfo("DefaultCertificate", collisionPriorityId: "123") } } }; - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("CustomTargetFrameworkAttribute.dll", targetFramework: ".NETFramework,Version=v2.0", collisionPriorityId: "123"), "3PartySHA2" } + { new ExplicitSignInfoKey("CustomTargetFrameworkAttribute.dll", targetFramework: ".NETFramework,Version=v2.0", collisionPriorityId: "123"), new FileSignInfoEntry("3PartySHA2") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -986,7 +986,7 @@ public void ThirdPartyLibraryMicrosoftCertificate() }; var strongNameSignInfo = new Dictionary>() { }; - var fileSignInfo = new Dictionary() { }; + var fileSignInfo = new Dictionary() { }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1008,7 +1008,7 @@ public void NoWarnThirdPartyLibraryMicrosoftCertificate() }; var strongNameSignInfo = new Dictionary>() { }; - var fileSignInfo = new Dictionary() { }; + var fileSignInfo = new Dictionary() { }; var noWarn3rdPartySet = new HashSet(StringComparer.OrdinalIgnoreCase) { "EmptyPKT.dll" }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -1035,7 +1035,7 @@ public void DoubleNestedContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -1086,7 +1086,7 @@ public void NestedContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -1165,9 +1165,9 @@ public void NestedContainerWithCollisions() // Overriding information. Since ContainerOne.dll collides with ContainerTwo.dll already in the hash mapping // table with collition id 123, we end up using ArcadeStrongTest instead of OverriddenCertificate1 - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("ContainerOne.dll", collisionPriorityId: "456"), "OverriddenCertificate1" } + { new ExplicitSignInfoKey("ContainerOne.dll", collisionPriorityId: "456"), new FileSignInfoEntry("OverriddenCertificate1") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] @@ -1247,7 +1247,7 @@ public void SignZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1298,11 +1298,11 @@ public void SignArchivesUsingDetachedSignature() var strongNameSignInfo = new Dictionary>(); // Overriding information - var explicitCertKeys = new Dictionary() + var explicitCertKeys = new Dictionary() { - { new ExplicitCertificateKey("test.zip"), "ArchiveCert" }, - { new ExplicitCertificateKey("test.tgz"), "ArchiveCert" }, - { new ExplicitCertificateKey("InnerZipFile.zip"), "ArchiveCert" } + { new ExplicitSignInfoKey("test.zip"), new FileSignInfoEntry("ArchiveCert") }, + { new ExplicitSignInfoKey("test.tgz"), new FileSignInfoEntry("ArchiveCert") }, + { new ExplicitSignInfoKey("InnerZipFile.zip"), new FileSignInfoEntry("ArchiveCert") } }; var additionalCertificateInfo = new Dictionary>() @@ -1390,7 +1390,7 @@ public void SignJustPkgWithoutUnpack() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1424,7 +1424,7 @@ public void UnpackAndSignPkg() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1491,9 +1491,9 @@ public void SignAndNotarizePkgFile() }; // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("test.pkg"), "MacDeveloperHardenWithNotarization" } + { new ExplicitSignInfoKey("test.pkg"), new FileSignInfoEntry("MacDeveloperHardenWithNotarization") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -1557,7 +1557,7 @@ public void SignNestedPkgFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1609,7 +1609,7 @@ public void SignPkgFileWithApp() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); // When .apps are unpacked from .pkgs, they get zipped so they can be signed ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -1655,7 +1655,7 @@ public void SignTarGZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1707,7 +1707,7 @@ public void SymbolsNupkg() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1759,7 +1759,7 @@ public void SignedSymbolsNupkg() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var tempFileExtensionSignInfo = s_fileExtensionSignInfo.Where(s => s.Key != ".symbols.nupkg").ToDictionary(e => e.Key, e => e.Value); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, tempFileExtensionSignInfo, new[] @@ -1810,7 +1810,7 @@ public void CheckDebSigning() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1854,7 +1854,7 @@ public void CheckRpmSigningOnWindows() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1882,7 +1882,7 @@ public void CheckRpmSigning() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1925,7 +1925,7 @@ public void VerifyDebIntegrity() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var expectedFilesToBeSigned = new List { @@ -1956,7 +1956,7 @@ public void VerifyRpmIntegrity() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var expectedFilesToBeSigned = new List { @@ -1988,7 +1988,7 @@ public void CheckPowershellSigning() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2012,7 +2012,7 @@ public void VerifyNupkgIntegrity() ValidateFileSignInfos(itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), s_fileExtensionSignInfo, new[] { "File 'IncorrectlySignedPackage.1.0.0.nupkg' Certificate='NuGet'" }); } @@ -2031,7 +2031,7 @@ public void SignNupkgWithUnsignedContents() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2059,7 +2059,7 @@ public void SignMsiEngine() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2108,7 +2108,7 @@ public void SignBundleDoubleNested() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2160,7 +2160,7 @@ public void MsiWithWixpack() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2227,7 +2227,7 @@ public void MPackFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2262,7 +2262,7 @@ public void VsixPackage_DuplicateVsixAfter() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2323,7 +2323,7 @@ public void VsixPackage_WithSpaces() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2384,7 +2384,7 @@ public void VsixPackage_DuplicateVsixBefore() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2442,7 +2442,7 @@ public void VsixPackage_DuplicateVsixBeforeAndAfter() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2502,7 +2502,7 @@ public void VsixPackageWithRelationships() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2536,9 +2536,9 @@ public void ZeroLengthFilesShouldNotBeSigned() // Default signing information var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("ZeroLengthPythonFile.py"), "3PartySHA2" } + { new ExplicitSignInfoKey("ZeroLengthPythonFile.py"), new FileSignInfoEntry("3PartySHA2") } }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, Array.Empty()); } @@ -2561,7 +2561,7 @@ public void CheckFileExtensionSignInfo() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2647,7 +2647,7 @@ public void FilesAreUniqueByName() new ItemToSign(GetResourcePath("SameFiles2.zip"), "123"), }; - ValidateFileSignInfos(itemsToSign, new Dictionary>(), new Dictionary(), s_fileExtensionSignInfoWithCollisionId, new[] + ValidateFileSignInfos(itemsToSign, new Dictionary>(), new Dictionary(), s_fileExtensionSignInfoWithCollisionId, new[] { "File 'Simple1.exe' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='Microsoft400'", "File 'Simple2.exe' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='Microsoft400'", @@ -2821,7 +2821,7 @@ public void ValidateAppendingCertificate() { "31bf3856ad364e35", new List{ new SignInfo(certificate: dualCertName, strongName: null) } } }; - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2854,7 +2854,7 @@ public void ValidateCertNotAppendedWithNonMatchingCollisionId() { "31bf3856ad364e35", new List{ new SignInfo(certificate: dualCertName, strongName: null) } } }; - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new string[] { }, additionalCertificateInfo: additionalCertInfo); } @@ -2875,7 +2875,7 @@ public void PackageWithZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2907,7 +2907,7 @@ public void NestedZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2958,26 +2958,26 @@ public void SpecificFileSignInfos() }; // Overriding information - var fileSignInfo = new Dictionary() - { - { new ExplicitCertificateKey("test.jar", collisionPriorityId: "123"), "JARCertificate" }, - { new ExplicitCertificateKey("test.ps1", collisionPriorityId: "123"), "PS1Certificate" }, - { new ExplicitCertificateKey("test.psd1", collisionPriorityId: "123"), "PSD1Certificate" }, - { new ExplicitCertificateKey("test.psm1", collisionPriorityId: "123"), "PSM1Certificate" }, - { new ExplicitCertificateKey("test.psc1", collisionPriorityId: "123"), "PSC1Certificate" }, - { new ExplicitCertificateKey("test.dylib", collisionPriorityId: "123"), "DYLIBCertificate" }, - { new ExplicitCertificateKey("EmptyPKT.dll", collisionPriorityId: "123"), "DLLCertificate" }, - { new ExplicitCertificateKey("test.vsix", collisionPriorityId: "123"), "VSIXCertificate" }, - { new ExplicitCertificateKey("PackageWithRelationships.vsix", collisionPriorityId: "123"), "VSIXCertificate2" }, - { new ExplicitCertificateKey("Simple.dll", collisionPriorityId: "123"), "DLLCertificate2" }, - { new ExplicitCertificateKey("Simple.nupkg", collisionPriorityId: "123"), "NUPKGCertificate" }, - { new ExplicitCertificateKey("Simple.symbols.nupkg", collisionPriorityId: "123"), "NUPKGCertificate2" }, - { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETFramework,Version=v4.6.1", "123"), "DLLCertificate3" }, - { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETStandard,Version=v2.0", "123"), "DLLCertificate4" }, - { new ExplicitCertificateKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETCoreApp,Version=v2.0", "123"), "DLLCertificate5" }, - { new ExplicitCertificateKey("filewithoutextension", collisionPriorityId: "123"), "MacDeveloperHarden" }, - { new ExplicitCertificateKey("SPCNoPKT.dll", collisionPriorityId: "123"), "None" }, - { new ExplicitCertificateKey("Simple.exe", collisionPriorityId: "1234"), "MacDeveloperHardenWithNotarization" }, + var fileSignInfo = new Dictionary() + { + { new ExplicitSignInfoKey("test.jar", collisionPriorityId: "123"), new FileSignInfoEntry("JARCertificate") }, + { new ExplicitSignInfoKey("test.ps1", collisionPriorityId: "123"), new FileSignInfoEntry("PS1Certificate") }, + { new ExplicitSignInfoKey("test.psd1", collisionPriorityId: "123"), new FileSignInfoEntry("PSD1Certificate") }, + { new ExplicitSignInfoKey("test.psm1", collisionPriorityId: "123"), new FileSignInfoEntry("PSM1Certificate") }, + { new ExplicitSignInfoKey("test.psc1", collisionPriorityId: "123"), new FileSignInfoEntry("PSC1Certificate") }, + { new ExplicitSignInfoKey("test.dylib", collisionPriorityId: "123"), new FileSignInfoEntry("DYLIBCertificate") }, + { new ExplicitSignInfoKey("EmptyPKT.dll", collisionPriorityId: "123"), new FileSignInfoEntry("DLLCertificate") }, + { new ExplicitSignInfoKey("test.vsix", collisionPriorityId: "123"), new FileSignInfoEntry("VSIXCertificate") }, + { new ExplicitSignInfoKey("PackageWithRelationships.vsix", collisionPriorityId: "123"), new FileSignInfoEntry("VSIXCertificate2") }, + { new ExplicitSignInfoKey("Simple.dll", collisionPriorityId: "123"), new FileSignInfoEntry("DLLCertificate2") }, + { new ExplicitSignInfoKey("Simple.nupkg", collisionPriorityId: "123"), new FileSignInfoEntry("NUPKGCertificate") }, + { new ExplicitSignInfoKey("Simple.symbols.nupkg", collisionPriorityId: "123"), new FileSignInfoEntry("NUPKGCertificate2") }, + { new ExplicitSignInfoKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETFramework,Version=v4.6.1", "123"), new FileSignInfoEntry("DLLCertificate3") }, + { new ExplicitSignInfoKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETStandard,Version=v2.0", "123"), new FileSignInfoEntry("DLLCertificate4") }, + { new ExplicitSignInfoKey("ProjectOne.dll", "581d91ccdfc4ea9c", ".NETCoreApp,Version=v2.0", "123"), new FileSignInfoEntry("DLLCertificate5") }, + { new ExplicitSignInfoKey("filewithoutextension", collisionPriorityId: "123"), new FileSignInfoEntry("MacDeveloperHarden") }, + { new ExplicitSignInfoKey("SPCNoPKT.dll", collisionPriorityId: "123"), new FileSignInfoEntry("None") }, + { new ExplicitSignInfoKey("Simple.exe", collisionPriorityId: "1234"), new FileSignInfoEntry("MacDeveloperHardenWithNotarization") }, }; // Set up the cert to allow for signing and notarization. @@ -3041,11 +3041,11 @@ public void ExecutableTypeFileSignInfos() var strongNameSignInfo = new Dictionary>(); // File-specific signing information with ExecutableType - var fileSignInfo = new Dictionary() + var fileSignInfo = new Dictionary() { - { new ExplicitCertificateKey("windows-exe.exe", executableType: ExecutableType.PE, collisionPriorityId: "123"), "WindowsCertificate" }, - { new ExplicitCertificateKey("linux-elf", executableType: ExecutableType.ELF, collisionPriorityId: "123"), "LinuxCertificate" }, - { new ExplicitCertificateKey("macos-macho", executableType: ExecutableType.MachO, collisionPriorityId: "123"), "MacDeveloperHarden" }, + { new ExplicitSignInfoKey("windows-exe.exe", executableType: ExecutableType.PE, collisionPriorityId: "123"), new FileSignInfoEntry("WindowsCertificate") }, + { new ExplicitSignInfoKey("linux-elf", executableType: ExecutableType.ELF, collisionPriorityId: "123"), new FileSignInfoEntry("LinuxCertificate") }, + { new ExplicitSignInfoKey("macos-macho", executableType: ExecutableType.MachO, collisionPriorityId: "123"), new FileSignInfoEntry("MacDeveloperHarden") }, }; ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] @@ -3125,7 +3125,7 @@ public void MissingCertificateName(string extension) new Configuration(_tmpDir, itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), new Dictionary>(), new(), null, @@ -3176,7 +3176,7 @@ public void MissingCertificateNameButExtensionIsIgnored(string extension) new Configuration(_tmpDir, itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), extensionSignInfo, new(), null, @@ -3200,15 +3200,14 @@ public void CrossGeneratedLibraryWithoutPKT() ValidateFileSignInfos( itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), s_fileExtensionSignInfoWithCollisionId, new string[0]); ValidateGeneratedProject( itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), s_fileExtensionSignInfoWithCollisionId, new string[0]); } @@ -3559,5 +3558,82 @@ public void TestSignShouldNotValidateNuGetSignatures() var testSignResult = testSignTool.VerifySignedNuGet(nupkgPath); testSignResult.Should().Be(SigningStatus.Signed, "TestSign mode should return Signed without validation"); } + + [Fact] + public void ContainerSigningWithDoNotUnpackViaFileSignInfo() + { + // Test DoNotUnpack for both top-level and nested containers + // Uses NestedContainer.1.0.0.nupkg which contains ContainerOne.1.0.0.nupkg + var itemsToSign = new List() + { + new ItemToSign(GetResourcePath("NestedContainer.1.0.0.nupkg"), collisionPriorityId: "123") + }; + + var strongNameSignInfo = new Dictionary>() + { + { "581d91ccdfc4ea9c", new List {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } } + }; + + // Test 1: Set DoNotUnpack=true on top-level container without certificate (uses default from extension) + // Result: Only the top-level container is signed, nested container is not extracted + var fileSignInfo = new Dictionary() + { + { new ExplicitSignInfoKey("NestedContainer.1.0.0.nupkg", collisionPriorityId: "123"), new FileSignInfoEntry(doNotUnpack: true) } + }; + + ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] + { + "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'", + }); + + // Test 2: Set DoNotUnpack=true on nested container with explicit certificate, but allow top-level to unpack + // Result: Top-level is unpacked and its contents signed, but nested container is signed without unpacking + fileSignInfo = new Dictionary() + { + { new ExplicitSignInfoKey("ContainerOne.1.0.0.nupkg", collisionPriorityId: "123"), new FileSignInfoEntry("NuGet", doNotUnpack: true) } + }; + + ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] + { + "File 'NativeLibrary.dll' Certificate='Microsoft400'", + "File 'ProjectOne.dll' TargetFramework='.NETFramework,Version=v4.6.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", + "File 'ContainerTwo.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", + "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", + "File 'ProjectOne.dll' TargetFramework='.NETCoreApp,Version=v2.1' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", + "File 'ProjectOne.dll' TargetFramework='.NETStandard,Version=v2.0' Certificate='3PartySHA2' StrongName='ArcadeStrongTest'", + "File 'ContainerOne.1.0.0.nupkg' Certificate='NuGet'", + "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'", + }); + } + + [Fact] + public void ContainerSigningWithDoNotUnpackViaFileExtensionSignInfo() + { + // Test DoNotUnpack for both top-level and nested containers + var itemsToSign = new List() + { + new ItemToSign(GetResourcePath("NestedContainer.1.0.0.nupkg"), collisionPriorityId: "123") + }; + + var strongNameSignInfo = new Dictionary>() + { + { "581d91ccdfc4ea9c", new List {new SignInfo(certificate: "3PartySHA2", strongName: "ArcadeStrongTest", collisionPriorityId: "123") } } + }; + + var fileSignInfo = new Dictionary(); + + // Configure DoNotUnpack via FileExtensionSignInfo for all .nupkg files + // Result: Only the top-level container is signed because DoNotUnpack=true prevents extraction + var extensionSignInfoWithDoNotUnpack = new Dictionary>() + { + { ".nupkg", new List{ new SignInfo("NuGet", collisionPriorityId: "123", doNotUnpack: true) } }, + { ".dll", new List{ new SignInfo("Microsoft400", collisionPriorityId: "123") } }, + }; + + ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, extensionSignInfoWithDoNotUnpack, new[] + { + "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'", + }); + } } } diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 074a9528ae0..84d85749c7f 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -34,11 +34,11 @@ internal class Configuration private readonly string _pathToContainerUnpackingDirectory; /// - /// This enable the overriding of the default certificate for a given file+token+target_framework. - /// It also contains a SignToolConstants.IgnoreFileCertificateSentinel flag in the certificate name in case the file does not need to be signed - /// for that + /// This enables the overriding of the default certificate for a given file+token+target_framework. + /// It also contains a SignToolConstants.IgnoreFileCertificateSentinel flag in the certificate name in case the file does not need to be signed. + /// Additionally, this can specify DoNotUnpack behavior for files without requiring a certificate to be specified. /// - private readonly Dictionary _fileSignInfo; + private readonly Dictionary _fileSignInfo; /// /// Used to look for signing information when we have the PublicKeyToken of a file. @@ -54,9 +54,9 @@ internal class Configuration private List _wixPacks; /// - /// Mapping of ".ext" to certificate. Files that have an extension on this map - /// will be signed using the specified certificate. Input list might contain - /// duplicate entries + /// Mapping of ".ext" to signing information including certificate and DoNotUnpack flag. + /// Files that have an extension on this map will be signed using the specified certificate + /// and/or have DoNotUnpack behavior applied. Input list might contain duplicate entries. /// private readonly Dictionary> _fileExtensionSignInfo; @@ -111,7 +111,7 @@ public Configuration( string tempDir, List itemsToSign, Dictionary> strongNameInfo, - Dictionary fileSignInfo, + Dictionary fileSignInfo, Dictionary> extensionSignInfo, Dictionary> additionalCertificateInformation, HashSet itemsToSkip3rdPartyCheck, @@ -235,37 +235,47 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, return fileSignInfo; } + // Skip unpacking if DoNotUnpack is set on the SignInfo (from FileSignInfo or FileExtensionSignInfo) + bool doNotUnpack = fileSignInfo.SignInfo.DoNotUnpack; if (fileSignInfo.IsUnpackableContainer()) { - if (fileSignInfo.IsUnpackableWixContainer()) + if (doNotUnpack) { - _log.LogMessage($"Trying to gather data for wix container {fileSignInfo.FullPath}"); - if (TryBuildWixData(fileSignInfo, out var msiData)) - { - _zipDataMap[fileSignInfo.FileContentKey] = msiData; - } - else - { - _log.LogError($"Failed to build wix data for {fileSignInfo.FullPath}"); - } + _log.LogMessage(MessageImportance.Normal, "Skipping container unpacking for '{0}' due to DoNotUnpack flag", file.FullPath); } else { - if (TryBuildZipData(fileSignInfo, out var zipData)) + if (fileSignInfo.IsUnpackableWixContainer()) { - _zipDataMap[fileSignInfo.FileContentKey] = zipData; + _log.LogMessage($"Trying to gather data for wix container {fileSignInfo.FullPath}"); + if (TryBuildWixData(fileSignInfo, out var msiData)) + { + _zipDataMap[fileSignInfo.FileContentKey] = msiData; + } + else + { + _log.LogError($"Failed to build wix data for {fileSignInfo.FullPath}"); + } } else { - _log.LogError($"Failed to build zip data for {fileSignInfo.FullPath}"); + if (TryBuildZipData(fileSignInfo, out var zipData)) + { + _zipDataMap[fileSignInfo.FileContentKey] = zipData; + } + else + { + _log.LogError($"Failed to build zip data for {fileSignInfo.FullPath}"); + } } } } + _log.LogMessage(MessageImportance.Low, $"Caching file {fileSignInfo.FileContentKey.FileName} {fileSignInfo.FileContentKey.StringHash}"); _filesByContentKey.Add(fileSignInfo.FileContentKey, fileSignInfo); bool hasSignableParts = false; - if (fileSignInfo.IsUnpackableContainer()) + if (fileSignInfo.IsUnpackableContainer() && !doNotUnpack) { // Only sign containers if the file itself is unsigned, or // an item in the container is unsigned. @@ -306,7 +316,7 @@ private FileSignInfo ExtractSignInfo( string wixContentFilePath) { var extension = Path.GetExtension(file.FileName); - string explicitCertificateName = null; + FileSignInfoEntry explicitFileSignInfoEntry = null; var fileSpec = string.Empty; var isAlreadyAuthenticodeSigned = false; var isAlreadyStrongNamed = false; @@ -426,12 +436,12 @@ private FileSignInfo ExtractSignInfo( // Check if we have more specific sign info: matchedNameTokenFramework = _fileSignInfo.TryGetValue( - new ExplicitCertificateKey(file.FileName, peInfo.PublicKeyToken, peInfo.TargetFramework, _hashToCollisionIdMap[signedFileContentKey]), - out explicitCertificateName); + new ExplicitSignInfoKey(file.FileName, peInfo.PublicKeyToken, peInfo.TargetFramework, _hashToCollisionIdMap[signedFileContentKey]), + out explicitFileSignInfoEntry); matchedNameToken = !matchedNameTokenFramework && _fileSignInfo.TryGetValue( - new ExplicitCertificateKey(file.FileName, peInfo.PublicKeyToken, collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey]), - out explicitCertificateName); + new ExplicitSignInfoKey(file.FileName, peInfo.PublicKeyToken, collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey]), + out explicitFileSignInfoEntry); fileSpec = matchedNameTokenFramework ? $" (PublicKeyToken = {peInfo.PublicKeyToken}, Framework = {peInfo.TargetFramework})" : matchedNameToken ? $" (PublicKeyToken = {peInfo.PublicKeyToken})" : string.Empty; @@ -462,17 +472,17 @@ private FileSignInfo ExtractSignInfo( } // We didn't find any specific information for PE files using PKT + TargetFramework - if (explicitCertificateName == null) + if (explicitFileSignInfoEntry == null) { // First try with ExecutableType - var matchedNameAndExecutableType = _fileSignInfo.TryGetValue(new ExplicitCertificateKey(file.FileName, - collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey], executableType: executableType), out explicitCertificateName); + var matchedNameAndExecutableType = _fileSignInfo.TryGetValue(new ExplicitSignInfoKey(file.FileName, + collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey], executableType: executableType), out explicitFileSignInfoEntry); // If no match with ExecutableType, try without it for backward compatibility if (!matchedNameAndExecutableType) { - matchedName = _fileSignInfo.TryGetValue(new ExplicitCertificateKey(file.FileName, - collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey]), out explicitCertificateName); + matchedName = _fileSignInfo.TryGetValue(new ExplicitSignInfoKey(file.FileName, + collisionPriorityId: _hashToCollisionIdMap[signedFileContentKey]), out explicitFileSignInfoEntry); } else { @@ -480,11 +490,21 @@ private FileSignInfo ExtractSignInfo( } } + // Extract explicit certificate name and DoNotUnpack flag from FileSignInfoEntry + string explicitCertificateName = explicitFileSignInfoEntry?.CertificateName; + + // Determine DoNotUnpack value: + // - If FileSignInfo is present, use its DoNotUnpack value (takes precedence) + // - Otherwise, use the DoNotUnpack from FileExtensionSignInfo (via signInfo) + bool doNotUnpack = explicitFileSignInfoEntry != null + ? explicitFileSignInfoEntry.DoNotUnpack + : signInfo.DoNotUnpack; + // If has overriding info, is it for ignoring the file? if (SignToolConstants.IgnoreFileCertificateSentinel.Equals(explicitCertificateName, StringComparison.OrdinalIgnoreCase)) { _log.LogMessage(MessageImportance.Low, $"File configured to not be signed: {file.FullPath}{fileSpec}"); - return new FileSignInfo(file, SignInfo.Ignore); + return new FileSignInfo(file, SignInfo.Ignore.WithDoNotUnpack(doNotUnpack)); } // Do we have an explicit certificate after all? @@ -493,6 +513,12 @@ private FileSignInfo ExtractSignInfo( signInfo = signInfo.WithCertificateName(explicitCertificateName, _hashToCollisionIdMap[signedFileContentKey]); hasSignInfo = true; } + + // Apply DoNotUnpack from FileSignInfo if present (takes precedence over extension-based DoNotUnpack) + if (explicitFileSignInfoEntry != null) + { + signInfo = signInfo.WithDoNotUnpack(explicitFileSignInfoEntry.DoNotUnpack); + } if (hasSignInfo) { @@ -517,7 +543,7 @@ private FileSignInfo ExtractSignInfo( _log.LogWarning($"Skipping file '{file.FullPath}' because .js files are no longer signed by default. " + "To disable this warning, please explicitly define the FileExtensionSignInfo for the .js extension " + "or set the MSBuild property 'NoSignJS' to 'true'."); - return new FileSignInfo(file, SignInfo.Ignore, wixContentFilePath: wixContentFilePath); + return new FileSignInfo(file, SignInfo.Ignore.WithDoNotUnpack(doNotUnpack), wixContentFilePath: wixContentFilePath); } // If the file is already signed and we are not allowed to dual sign, and we are not doing a mac notarization operation, @@ -556,7 +582,7 @@ private FileSignInfo ExtractSignInfo( _log.LogMessage(MessageImportance.Low, $"Ignoring non-signable file: {file.FullPath}"); } - return new FileSignInfo(file, SignInfo.Ignore, wixContentFilePath: wixContentFilePath); + return new FileSignInfo(file, SignInfo.Ignore.WithDoNotUnpack(doNotUnpack), wixContentFilePath: wixContentFilePath); bool IsSigned(PathWithHash file, SigningStatus signingStatus) { diff --git a/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs b/src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs similarity index 60% rename from src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs rename to src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs index 14f4531cbcf..66cf9e8ad9c 100644 --- a/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs +++ b/src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs @@ -6,7 +6,12 @@ namespace Microsoft.DotNet.SignTool { - internal readonly struct ExplicitCertificateKey : IEquatable + /// + /// Key used to identify file-specific signing information in FileSignInfo. + /// Represents the entity being signed (file name, path, attributes) rather than just certificate correlation. + /// Can be used to specify signing properties like DoNotUnpack independently of certificates. + /// + internal readonly struct ExplicitSignInfoKey : IEquatable { public readonly string FileName; public readonly string PublicKeyToken; @@ -14,7 +19,7 @@ namespace Microsoft.DotNet.SignTool public readonly string CollisionPriorityId; public readonly ExecutableType ExecutableType; - public ExplicitCertificateKey(string fileName, string publicKeyToken = null, string targetFramework = null, string collisionPriorityId = null, ExecutableType executableType = ExecutableType.None) + public ExplicitSignInfoKey(string fileName, string publicKeyToken = null, string targetFramework = null, string collisionPriorityId = null, ExecutableType executableType = ExecutableType.None) { Debug.Assert(fileName != null); @@ -26,22 +31,22 @@ public ExplicitCertificateKey(string fileName, string publicKeyToken = null, str } public override bool Equals(object obj) - => obj is ExplicitCertificateKey key && Equals(key); + => obj is ExplicitSignInfoKey key && Equals(key); public override int GetHashCode() => Hash.Combine(Hash.Combine(FileName.GetHashCode(), PublicKeyToken.GetHashCode()), Hash.Combine(TargetFramework.GetHashCode(), ExecutableType.GetHashCode())); - bool IEquatable.Equals(ExplicitCertificateKey other) + bool IEquatable.Equals(ExplicitSignInfoKey other) => FileName == other.FileName && CollisionPriorityId == other.CollisionPriorityId && string.Equals(PublicKeyToken, other.PublicKeyToken, StringComparison.OrdinalIgnoreCase) && TargetFramework == other.TargetFramework && ExecutableType == other.ExecutableType; - public static bool operator ==(ExplicitCertificateKey key1, ExplicitCertificateKey key2) + public static bool operator ==(ExplicitSignInfoKey key1, ExplicitSignInfoKey key2) => key1.Equals(key2); - public static bool operator !=(ExplicitCertificateKey key1, ExplicitCertificateKey key2) + public static bool operator !=(ExplicitSignInfoKey key1, ExplicitSignInfoKey key2) => !(key1 == key2); } } diff --git a/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs b/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs index 47062c119c6..efe8825d81f 100644 --- a/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs +++ b/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs @@ -127,7 +127,7 @@ internal bool IsUnpackableContainer() => IsZip() || internal bool HasSignableParts { get; } - internal bool ShouldRepack => HasSignableParts; + internal bool ShouldRepack => HasSignableParts && !SignInfo.DoNotUnpack; internal bool ShouldTrack => SignInfo.ShouldSign || ShouldRepack; @@ -150,7 +150,8 @@ public override string ToString() (TargetFramework != null ? $" TargetFramework='{TargetFramework}'" : "") + (SignInfo.ShouldSign ? $" Certificate='{SignInfo.Certificate}'" : "") + (SignInfo.ShouldStrongName ? $" StrongName='{SignInfo.StrongName}'" : "") + - (SignInfo.ShouldNotarize ? $" NotarizationAppName='{SignInfo.NotarizationAppName}'" : ""); + (SignInfo.ShouldNotarize ? $" NotarizationAppName='{SignInfo.NotarizationAppName}'" : "") + + (HasSignableParts && SignInfo.DoNotUnpack ? " DoNotUnpack='true'" : ""); internal FileSignInfo WithSignableParts() => new FileSignInfo(File, SignInfo.WithIsAlreadySigned(false), TargetFramework, WixContentFilePath, true); diff --git a/src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs b/src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs new file mode 100644 index 00000000000..7b0baef84e5 --- /dev/null +++ b/src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.DotNet.SignTool +{ + /// + /// Represents file signing information including certificate name and DoNotUnpack flag. + /// Certificate name can be null if only DoNotUnpack behavior needs to be specified. + /// + internal class FileSignInfoEntry + { + public string CertificateName { get; } + public bool DoNotUnpack { get; } + + public FileSignInfoEntry(string certificateName = null, bool doNotUnpack = false) + { + CertificateName = certificateName; + DoNotUnpack = doNotUnpack; + } + } +} diff --git a/src/Microsoft.DotNet.SignTool/src/SignInfo.cs b/src/Microsoft.DotNet.SignTool/src/SignInfo.cs index 2f618337432..6b9d0cf73e0 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignInfo.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignInfo.cs @@ -59,6 +59,12 @@ internal readonly struct SignInfo /// internal string CollisionPriorityId { get; } + /// + /// True if this container should not be unpacked during signing. + /// When set, only the container itself is signed without extracting and signing nested contents. + /// + internal bool DoNotUnpack { get; } + public bool ShouldLocallyStrongNameSign => ShouldStrongName && StrongName.EndsWith(".snk", StringComparison.OrdinalIgnoreCase); public bool ShouldSign => !IsAlreadySigned && !ShouldIgnore; @@ -67,7 +73,7 @@ internal readonly struct SignInfo public bool ShouldNotarize => !string.IsNullOrEmpty(NotarizationAppName) && !ShouldIgnore; - private SignInfo(string certificate, string strongName, string notarizationAppName, string collisionPriorityId, bool shouldIgnore, bool isAlreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false) + private SignInfo(string certificate, string strongName, string notarizationAppName, string collisionPriorityId, bool shouldIgnore, bool isAlreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false, bool doNotUnpack = false) { ShouldIgnore = shouldIgnore; IsAlreadySigned = isAlreadySigned; @@ -77,36 +83,40 @@ private SignInfo(string certificate, string strongName, string notarizationAppNa IsAlreadyStrongNamed = isAlreadyStrongNamed; NotarizationAppName = notarizationAppName; GeneratesDetachedSignature = generatesDetachedSignature; + DoNotUnpack = doNotUnpack; } - private SignInfo(bool ignoreThisFile, bool alreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false) - : this(certificate: null, strongName: null, notarizationAppName: null, collisionPriorityId: null, ignoreThisFile, alreadySigned, isAlreadyStrongNamed, generatesDetachedSignature) + private SignInfo(bool ignoreThisFile, bool alreadySigned, bool isAlreadyStrongNamed, bool generatesDetachedSignature = false, bool doNotUnpack = false) + : this(certificate: null, strongName: null, notarizationAppName: null, collisionPriorityId: null, ignoreThisFile, alreadySigned, isAlreadyStrongNamed, generatesDetachedSignature, doNotUnpack) { } - internal SignInfo(string certificate, string strongName = null, string notarization = null, string collisionPriorityId = null) - : this(certificate, strongName, notarization, collisionPriorityId, shouldIgnore: false, isAlreadySigned: false, isAlreadyStrongNamed: false) + internal SignInfo(string certificate, string strongName = null, string notarization = null, string collisionPriorityId = null, bool doNotUnpack = false) + : this(certificate, strongName, notarization, collisionPriorityId, shouldIgnore: false, isAlreadySigned: false, isAlreadyStrongNamed: false, generatesDetachedSignature: false, doNotUnpack: doNotUnpack) { } internal SignInfo WithCertificateName(string value, string collisionPriorityId) - => new SignInfo(value, StrongName, NotarizationAppName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature); + => new SignInfo(value, StrongName, NotarizationAppName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature, DoNotUnpack); internal SignInfo WithNotarization(string appName, string collisionPriorityId) - => new SignInfo(Certificate, StrongName, appName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature); + => new SignInfo(Certificate, StrongName, appName, collisionPriorityId, false, false, IsAlreadyStrongNamed, GeneratesDetachedSignature, DoNotUnpack); internal SignInfo WithCollisionPriorityId(string collisionPriorityId) - => new SignInfo(Certificate, StrongName, NotarizationAppName, collisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed, GeneratesDetachedSignature); + => new SignInfo(Certificate, StrongName, NotarizationAppName, collisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed, GeneratesDetachedSignature, DoNotUnpack); internal SignInfo WithIsAlreadySigned(bool value = false) => Certificate != null ? - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, value, value, IsAlreadyStrongNamed, GeneratesDetachedSignature) : - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, true, value, IsAlreadyStrongNamed, GeneratesDetachedSignature); + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, value, value, IsAlreadyStrongNamed, GeneratesDetachedSignature, DoNotUnpack) : + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, true, value, IsAlreadyStrongNamed, GeneratesDetachedSignature, DoNotUnpack); internal SignInfo WithIsAlreadyStrongNamed(bool value = false) => - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, value, GeneratesDetachedSignature); + new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, value, GeneratesDetachedSignature, DoNotUnpack); internal SignInfo WithDetachedSignature(string certificate) - => new SignInfo(certificate, StrongName, NotarizationAppName, CollisionPriorityId, false, false, IsAlreadyStrongNamed, true); + => new SignInfo(certificate, StrongName, NotarizationAppName, CollisionPriorityId, false, false, IsAlreadyStrongNamed, true, DoNotUnpack); + + internal SignInfo WithDoNotUnpack(bool doNotUnpack) + => new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed, GeneratesDetachedSignature, doNotUnpack); } } diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs b/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs index f0b8b879869..a3be92ae932 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs @@ -136,6 +136,12 @@ internal static class SignToolConstants /// public const string CollisionPriorityId = "CollisionPriorityId"; + /// + /// Attribute to indicate that a container should be signed without unpacking. + /// When set to true, only the top-level container is signed and nested contents are not extracted or signed. + /// + public const string DoNotUnpack = "DoNotUnpack"; + /// /// Notarization operation microbuild ID. Microbuild does not currently support the friendly name, MacNotarize /// diff --git a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs index 8350a285c22..a5d2cea441a 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs @@ -447,6 +447,8 @@ private Dictionary> ParseFileExtensionSignInfo() var extension = item.ItemSpec; var certificate = item.GetMetadata("CertificateName"); var collisionPriorityId = item.GetMetadata(SignToolConstants.CollisionPriorityId); + var doNotUnpackStr = item.GetMetadata(SignToolConstants.DoNotUnpack); + bool.TryParse(doNotUnpackStr, out bool doNotUnpack); // Some supported extensions have multiple dots. Special case these so that we don't throw an error below. if (!extension.Equals(Path.GetExtension(extension)) && !specialExtensions.Contains(extension)) @@ -455,15 +457,16 @@ private Dictionary> ParseFileExtensionSignInfo() continue; } - if (string.IsNullOrWhiteSpace(certificate)) + // Certificate is only required when DoNotUnpack is not set + if (!doNotUnpack && string.IsNullOrWhiteSpace(certificate)) { - Log.LogError($"CertificateName metadata of {nameof(FileExtensionSignInfo)} is invalid: '{certificate}'"); + Log.LogError($"CertificateName metadata of {nameof(FileExtensionSignInfo)} is required when DoNotUnpack is not set: '{extension}'"); continue; } SignInfo signInfo = certificate.Equals(SignToolConstants.IgnoreFileCertificateSentinel, StringComparison.InvariantCultureIgnoreCase) ? - SignInfo.Ignore.WithCollisionPriorityId(collisionPriorityId) : - new SignInfo(certificate, collisionPriorityId: collisionPriorityId); + SignInfo.Ignore.WithCollisionPriorityId(collisionPriorityId).WithDoNotUnpack(doNotUnpack) : + new SignInfo(certificate, collisionPriorityId: collisionPriorityId, doNotUnpack: doNotUnpack); if (map.ContainsKey(extension)) { @@ -539,9 +542,9 @@ private Dictionary> ParseStrongNameSignInfo() return map; } - private Dictionary ParseFileSignInfo() + private Dictionary ParseFileSignInfo() { - var map = new Dictionary(); + var map = new Dictionary(); if (FileSignInfo != null) { @@ -553,6 +556,8 @@ private Dictionary ParseFileSignInfo() var certificateName = item.GetMetadata("CertificateName"); var collisionPriorityId = item.GetMetadata(SignToolConstants.CollisionPriorityId); var executableTypeMetadata = item.GetMetadata("ExecutableType"); + var doNotUnpackStr = item.GetMetadata(SignToolConstants.DoNotUnpack); + bool.TryParse(doNotUnpackStr, out bool doNotUnpack); if (fileName.IndexOfAny(new[] { '/', '\\' }) >= 0) { @@ -566,9 +571,10 @@ private Dictionary ParseFileSignInfo() continue; } - if (string.IsNullOrWhiteSpace(certificateName)) + // Certificate is only required when DoNotUnpack is not set + if (!doNotUnpack && string.IsNullOrWhiteSpace(certificateName)) { - Log.LogError($"CertificateName metadata of {nameof(FileSignInfo)} is invalid: '{certificateName}'"); + Log.LogError($"CertificateName metadata of {nameof(FileSignInfo)} is required when DoNotUnpack is not set: '{fileName}'"); continue; } @@ -585,14 +591,14 @@ private Dictionary ParseFileSignInfo() continue; } - var key = new ExplicitCertificateKey(fileName, publicKeyToken, targetFramework, collisionPriorityId, executableType); - if (map.TryGetValue(key, out var existingCert)) + var key = new ExplicitSignInfoKey(fileName, publicKeyToken, targetFramework, collisionPriorityId, executableType); + if (map.TryGetValue(key, out var existingEntry)) { - Log.LogError($"Duplicate entries in {nameof(FileSignInfo)} with the same key ('{fileName}', '{publicKeyToken}', '{targetFramework}', '{executableTypeMetadata}'): '{existingCert}', '{certificateName}'."); + Log.LogError($"Duplicate entries in {nameof(FileSignInfo)} with the same key ('{fileName}', '{publicKeyToken}', '{targetFramework}', '{executableTypeMetadata}'): '{existingEntry.CertificateName}', '{certificateName}'."); continue; } - map.Add(key, certificateName); + map.Add(key, new FileSignInfoEntry(certificateName, doNotUnpack)); } } From 9f518f2be968c4c0102c2e3f8c793c5b7f28b731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emek=20Vysok=C3=BD?= Date: Mon, 12 Jan 2026 13:55:33 +0100 Subject: [PATCH 063/100] [release/10.0] Use VS 2022 images in Arcade & Publishing infra (#16439) --- azure-pipelines-unofficial.yml | 2 +- azure-pipelines.yml | 2 +- eng/common/core-templates/job/publish-build-assets.yml | 2 +- eng/common/core-templates/post-build/post-build.yml | 4 ++-- eng/common/templates/variables/pool-providers.yml | 2 +- eng/promote-build.yml | 2 +- eng/validate-sdk.yml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/azure-pipelines-unofficial.yml b/azure-pipelines-unofficial.yml index 887f46d621d..d63cd797c6c 100644 --- a/azure-pipelines-unofficial.yml +++ b/azure-pipelines-unofficial.yml @@ -20,7 +20,7 @@ extends: parameters: pool: name: $(DncEngInternalBuildPool) - image: windows.vs2019.amd64 + image: windows.vs2022.amd64 os: windows stages: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 373ee74dd6b..1e74fd378aa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -40,7 +40,7 @@ extends: parameters: pool: name: $(DncEngInternalBuildPool) - image: windows.vs2019.amd64 + image: windows.vs2022.amd64 os: windows sdl: policheck: diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 3437087c80f..b955fac6e13 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -80,7 +80,7 @@ jobs: # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 + image: windows.vs2022.amd64 os: windows steps: - ${{ if eq(parameters.is1ESPipeline, '') }}: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 9423d71ca3a..b942a79ef02 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -293,11 +293,11 @@ stages: ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 + image: windows.vs2022.amd64 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2022.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml index e0b19c14a07..18693ea120d 100644 --- a/eng/common/templates/variables/pool-providers.yml +++ b/eng/common/templates/variables/pool-providers.yml @@ -23,7 +23,7 @@ # # pool: # name: $(DncEngInternalBuildPool) -# demands: ImageOverride -equals windows.vs2019.amd64 +# demands: ImageOverride -equals windows.vs2022.amd64 variables: - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - template: /eng/common/templates-official/variables/pool-providers.yml diff --git a/eng/promote-build.yml b/eng/promote-build.yml index ed690454a15..e2693fc36d8 100644 --- a/eng/promote-build.yml +++ b/eng/promote-build.yml @@ -69,7 +69,7 @@ extends: os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 + image: windows.vs2022.amd64 os: windows sdl: policheck: diff --git a/eng/validate-sdk.yml b/eng/validate-sdk.yml index 5dd1a0e4d08..4b1cfbd8a0d 100644 --- a/eng/validate-sdk.yml +++ b/eng/validate-sdk.yml @@ -29,7 +29,7 @@ jobs: - template: /eng/common/templates-official/variables/pool-providers.yml@self pool: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2022.amd64 preSteps: - checkout: self clean: true From af17297350d5e5357d2ab3d69369d2a58b8bc4ab Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 16 Jan 2026 16:41:14 -0600 Subject: [PATCH 064/100] Update image in source-build.yml for Linux pool (#16463) --- eng/common/core-templates/job/source-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index d805d5faeb9..c08b3ad8ad0 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -63,7 +63,7 @@ jobs: demands: ImageOverride -equals build.ubuntu.2004.amd64 ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - image: 1es-mariner-2 + image: Azure-Linux-3-Amd64 os: linux ${{ else }}: pool: From bf2212b303617f25bd3df75421569f1707931380 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Thu, 22 Jan 2026 12:40:44 -0800 Subject: [PATCH 065/100] [release/10.0] Use Azure-Linux-3-Amd64 for SB (#16459) From c53d03a7ba57390a9ffd9cef45ec884ad948cb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 26 Jan 2026 12:54:11 +0100 Subject: [PATCH 066/100] [release/10.0] Use AzureLinux3 image for source-build.yml (#16477) --- eng/common/core-templates/job/source-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index c08b3ad8ad0..4cb769473f7 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -60,7 +60,7 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals build.ubuntu.2004.amd64 + demands: ImageOverride -equals Azure-Linux-3-Amd64-Public ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] image: Azure-Linux-3-Amd64 @@ -69,10 +69,10 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open + demands: ImageOverride -equals Azure-Linux-3-Amd64-Public ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 + demands: ImageOverride -equals Azure-Linux-3-Amd64 ${{ if ne(parameters.platform.pool, '') }}: pool: ${{ parameters.platform.pool }} From f33d1fd2cfd9136281ed8b2f78e97c7b4c8c335d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 08:02:58 -0800 Subject: [PATCH 067/100] [release/10.0] Fix reading container contents when DoNotUnpack is set (#16483) Co-authored-by: Ella Hathaway --- .../src/BatchSignUtil.cs | 21 ++++++++++++------- .../src/Configuration.cs | 14 ++++++------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs index ecd63cf0009..b3500c39d96 100644 --- a/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs +++ b/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs @@ -281,8 +281,18 @@ void repackContainer(FileSignInfo file) { if (file.IsUnpackableContainer()) { - _log.LogMessage($"Repacking container: '{file.FileName}'"); - _batchData.ZipDataMap[file.FileContentKey].Repack(_log, _signTool.TempDir, _signTool.Wix3ToolsPath, _signTool.WixToolsPath, _signTool.TarToolPath, _signTool.PkgToolPath); + if (_batchData.ZipDataMap.TryGetValue(file.FileContentKey, out var zipData)) + { + _log.LogMessage($"Repacking container: '{file.FileName}'"); + zipData.Repack(_log, _signTool.TempDir, _signTool.Wix3ToolsPath, _signTool.WixToolsPath, _signTool.TarToolPath, _signTool.PkgToolPath); + } + else + { + if (!file.SignInfo.DoNotUnpack) + { + _log.LogError($"No zip data found for file '{file.FullPath}' to repack."); + } + } } else { @@ -294,9 +304,8 @@ void repackContainer(FileSignInfo file) // signed, don't need signing, and are repacked. bool isReady(FileSignInfo file) { - if (file.IsUnpackableContainer()) + if (_batchData.ZipDataMap.TryGetValue(file.FileContentKey, out var zipData)) { - var zipData = _batchData.ZipDataMap[file.FileContentKey]; return zipData.NestedParts.Values.All(x => (!x.FileSignInfo.SignInfo.ShouldSign || trackedSet.Contains(x.FileSignInfo.FileContentKey)) && !toRepackSet.Contains(x.FileSignInfo.FullPath) ); @@ -664,10 +673,8 @@ private void VerifyAfterSign(TaskLoggingHelper log, FileSignInfo file) } } - if (file.IsUnpackableContainer()) + if (_batchData.ZipDataMap.TryGetValue(file.FileContentKey, out var zipData)) { - var zipData = _batchData.ZipDataMap[file.FileContentKey]; - foreach (var nestedPart in zipData.NestedParts.Values) { VerifyAfterSign(log, nestedPart.FileSignInfo); diff --git a/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 84d85749c7f..df7f373d942 100644 --- a/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/Microsoft.DotNet.SignTool/src/Configuration.cs @@ -235,11 +235,9 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, return fileSignInfo; } - // Skip unpacking if DoNotUnpack is set on the SignInfo (from FileSignInfo or FileExtensionSignInfo) - bool doNotUnpack = fileSignInfo.SignInfo.DoNotUnpack; if (fileSignInfo.IsUnpackableContainer()) { - if (doNotUnpack) + if (fileSignInfo.SignInfo.DoNotUnpack) { _log.LogMessage(MessageImportance.Normal, "Skipping container unpacking for '{0}' due to DoNotUnpack flag", file.FullPath); } @@ -259,9 +257,9 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, } else { - if (TryBuildZipData(fileSignInfo, out var zipData)) + if (TryBuildZipData(fileSignInfo, out var builtZipData)) { - _zipDataMap[fileSignInfo.FileContentKey] = zipData; + _zipDataMap[fileSignInfo.FileContentKey] = builtZipData; } else { @@ -275,11 +273,11 @@ private FileSignInfo TrackFile(PathWithHash file, PathWithHash parentContainer, _filesByContentKey.Add(fileSignInfo.FileContentKey, fileSignInfo); bool hasSignableParts = false; - if (fileSignInfo.IsUnpackableContainer() && !doNotUnpack) + if (_zipDataMap.TryGetValue(fileSignInfo.FileContentKey, out var cachedZipData)) { // Only sign containers if the file itself is unsigned, or // an item in the container is unsigned. - hasSignableParts = _zipDataMap[fileSignInfo.FileContentKey].NestedParts.Values.Any(b => b.FileSignInfo.SignInfo.ShouldSign || b.FileSignInfo.HasSignableParts); + hasSignableParts = cachedZipData.NestedParts.Values.Any(b => b.FileSignInfo.SignInfo.ShouldSign || (b.FileSignInfo.HasSignableParts && !b.FileSignInfo.SignInfo.DoNotUnpack)); if (hasSignableParts) { // If the file has contents that need to be signed, then re-evaluate the signing info @@ -824,7 +822,7 @@ private bool TryBuildZipData(FileSignInfo zipFileSignInfo, out ZipData zipData, } else { - Debug.Assert(zipFileSignInfo.IsUnpackableContainer()); + Debug.Assert(zipFileSignInfo.IsUnpackableContainer() && !zipFileSignInfo.SignInfo.DoNotUnpack); } try From eaf117110254c63b10ded8112372f35039eded15 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:24:16 +0000 Subject: [PATCH 068/100] [release/10.0] Add LinuxSign500180PGP cert (#16482) Co-authored-by: Ella Hathaway --- src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props index 3ff26ef1da2..06147aea727 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props +++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props @@ -32,6 +32,7 @@ + 10.0.100-preview.4.25220.1 - 10.0.0-beta.25626.5 - 10.0.0-beta.25626.5 + 10.0.0-beta.26080.4 + 10.0.0-beta.26080.4 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 58fc3be4728..74d736eb7af 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - d8dca0b41b903e7182e64543773390b969dab96b + b0f891cf7c1febc0ebbae7dd787f8a0b4c0cbc95 - + https://github.com/dotnet/arcade - d8dca0b41b903e7182e64543773390b969dab96b + b0f891cf7c1febc0ebbae7dd787f8a0b4c0cbc95 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 5ee21614359..8f5fc848068 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.101" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25626.5", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25626.5", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26080.4", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26080.4", "Microsoft.Build.NoTargets": "3.7.0" } } From c45c9fbade8b6406d22f8100575a102a3a2c053f Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Wed, 4 Feb 2026 11:10:25 -0800 Subject: [PATCH 072/100] [release/10.0] Enable Permissive, CFSClean policies (#16430) (#16496) --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1e74fd378aa..123ab961fae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -38,6 +38,8 @@ resources: extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: + settings: + networkIsolationPolicy: Permissive, CFSClean, CFSClean2 pool: name: $(DncEngInternalBuildPool) image: windows.vs2022.amd64 From 58d75363f4b03e408aa2502e0db546173be722d4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:53:17 +0000 Subject: [PATCH 073/100] [release/10.0] Use correct name for the AZL3 build image (#16511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- eng/common/core-templates/job/source-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index 4cb769473f7..1997c2ae00d 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -60,19 +60,19 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Azure-Linux-3-Amd64-Public + demands: ImageOverride -equals build.azurelinux.3.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - image: Azure-Linux-3-Amd64 + image: build.azurelinux.3.amd64 os: linux ${{ else }}: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Azure-Linux-3-Amd64-Public + demands: ImageOverride -equals build.azurelinux.3.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - demands: ImageOverride -equals Azure-Linux-3-Amd64 + demands: ImageOverride -equals build.azurelinux.3.amd64 ${{ if ne(parameters.platform.pool, '') }}: pool: ${{ parameters.platform.pool }} From 4bf37ce670528cf2aef4d9b1cd892554b1b02d9d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 10:44:22 +0100 Subject: [PATCH 074/100] [release/10.0] Update dependencies from dotnet/xharness (#16423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.props | 2 +- eng/Version.Details.xml | 4 ++-- tests/XHarness.Apple.DeviceTests.proj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 572c8122e92..f0c838fc651 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -22,7 +22,7 @@ This file should be imported by eng/Versions.props 1.1.0-beta.25424.1 1.1.0-beta.25424.1 - 10.0.0-prerelease.25605.1 + 11.0.0-prerelease.26107.1 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 74d736eb7af..e1d9e857be7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - ccb455efce66706ea8b3927fefb425f621e5af0a + bfbac237157e59cdbd19334325b2af80bd6e9828 https://github.com/dotnet/roslyn diff --git a/tests/XHarness.Apple.DeviceTests.proj b/tests/XHarness.Apple.DeviceTests.proj index bbc98aee82b..07b271c9784 100644 --- a/tests/XHarness.Apple.DeviceTests.proj +++ b/tests/XHarness.Apple.DeviceTests.proj @@ -10,7 +10,7 @@ - + From eae2786f0de623ee284aea80713ec2c61531e9bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:03:30 -0800 Subject: [PATCH 075/100] [release/10.0] Support for hotfix version tag (#16531) Co-authored-by: Pavel Purma --- .../VersionIdentiferTests.cs | 1 + src/Microsoft.DotNet.Build.Manifest/VersionIdentifier.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Build.Manifest.Tests/VersionIdentiferTests.cs b/src/Microsoft.DotNet.Build.Manifest.Tests/VersionIdentiferTests.cs index bafff16608b..7274e41055b 100644 --- a/src/Microsoft.DotNet.Build.Manifest.Tests/VersionIdentiferTests.cs +++ b/src/Microsoft.DotNet.Build.Manifest.Tests/VersionIdentiferTests.cs @@ -38,6 +38,7 @@ public class VersionTests [InlineData("What-Is-A.FooPackage", null)] [InlineData("What-Is-A.FooPackage-2.2-64", null)] [InlineData("What-Is-A.FooPackage-2.2.nupkg", null)] + [InlineData("What-Is-A.FooPackage.10.0.3-hotfix.12345.1", "10.0.3-hotfix.12345.1")] public void ValidateSimpleVersions(string assetName, string version) { VersionIdentifier.GetVersion(assetName).Should().Be(version); diff --git a/src/Microsoft.DotNet.Build.Manifest/VersionIdentifier.cs b/src/Microsoft.DotNet.Build.Manifest/VersionIdentifier.cs index 0477a151e79..b83b8c190ce 100644 --- a/src/Microsoft.DotNet.Build.Manifest/VersionIdentifier.cs +++ b/src/Microsoft.DotNet.Build.Manifest/VersionIdentifier.cs @@ -19,7 +19,8 @@ public static class VersionIdentifier "prerelease", "servicing", "rtm", - "rc" + "rc", + "hotfix" }; private static readonly SortedDictionary _sequencesToReplace = From 2d5256413d60ce92a7b7033ae7165629e7fcf29f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:32:21 +0100 Subject: [PATCH 076/100] [main] Source code updates from dotnet/dotnet (#16533) Co-authored-by: dotnet-maestro[bot] --- eng/common/templates/steps/vmr-sync.yml | 21 -------------- eng/common/templates/vmr-build-pr.yml | 1 + eng/common/tools.ps1 | 5 ++++ eng/common/tools.sh | 8 +++++- eng/common/vmr-sync.ps1 | 38 +++++++++++++++++++++---- eng/common/vmr-sync.sh | 30 +++++++++++++++---- 6 files changed, 70 insertions(+), 33 deletions(-) diff --git a/eng/common/templates/steps/vmr-sync.yml b/eng/common/templates/steps/vmr-sync.yml index 599afb6186b..eb619c50268 100644 --- a/eng/common/templates/steps/vmr-sync.yml +++ b/eng/common/templates/steps/vmr-sync.yml @@ -38,27 +38,6 @@ steps: displayName: Label PR commit workingDirectory: $(Agent.BuildDirectory)/repo -- script: | - vmr_sha=$(grep -oP '(?<=Sha=")[^"]*' $(Agent.BuildDirectory)/repo/eng/Version.Details.xml) - echo "##vso[task.setvariable variable=vmr_sha]$vmr_sha" - displayName: Obtain the vmr sha from Version.Details.xml (Unix) - condition: ne(variables['Agent.OS'], 'Windows_NT') - workingDirectory: $(Agent.BuildDirectory)/repo - -- powershell: | - [xml]$xml = Get-Content -Path $(Agent.BuildDirectory)/repo/eng/Version.Details.xml - $vmr_sha = $xml.SelectSingleNode("//Source").Sha - Write-Output "##vso[task.setvariable variable=vmr_sha]$vmr_sha" - displayName: Obtain the vmr sha from Version.Details.xml (Windows) - condition: eq(variables['Agent.OS'], 'Windows_NT') - workingDirectory: $(Agent.BuildDirectory)/repo - -- script: | - git fetch --all - git checkout $(vmr_sha) - displayName: Checkout VMR at correct sha for repo flow - workingDirectory: ${{ parameters.vmrPath }} - - script: | git config --global user.name "dotnet-maestro[bot]" git config --global user.email "dotnet-maestro[bot]@users.noreply.github.com" diff --git a/eng/common/templates/vmr-build-pr.yml b/eng/common/templates/vmr-build-pr.yml index ce3c29a62fa..2f3694fa132 100644 --- a/eng/common/templates/vmr-build-pr.yml +++ b/eng/common/templates/vmr-build-pr.yml @@ -34,6 +34,7 @@ resources: type: github name: dotnet/dotnet endpoint: dotnet + ref: refs/heads/main # Set to whatever VMR branch the PR build should insert into stages: - template: /eng/pipelines/templates/stages/vmr-build.yml@vmr diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 049fe6db994..977a2d4b103 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -824,6 +824,11 @@ function MSBuild-Core() { $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") { + $cmdArgs += ' -mt' + } + if ($warnAsError) { $cmdArgs += ' /warnaserror /p:TreatWarningsAsErrors=true' } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index c1841c9dfd0..1b296f646c2 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -526,7 +526,13 @@ function MSBuild-Core { } } - RunBuildTool "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" + # Add -mt flag for MSBuild multithreaded mode if enabled via environment variable + local mt_switch="" + if [[ "${MSBUILD_MT_ENABLED:-}" == "1" ]]; then + mt_switch="-mt" + fi + + RunBuildTool "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch $mt_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" } function GetDarc { diff --git a/eng/common/vmr-sync.ps1 b/eng/common/vmr-sync.ps1 index 97302f3205b..b37992d91cf 100755 --- a/eng/common/vmr-sync.ps1 +++ b/eng/common/vmr-sync.ps1 @@ -103,12 +103,20 @@ Set-StrictMode -Version Latest Highlight 'Installing .NET, preparing the tooling..' . .\eng\common\tools.ps1 $dotnetRoot = InitializeDotNetCli -install:$true +$env:DOTNET_ROOT = $dotnetRoot $darc = Get-Darc -$dotnet = "$dotnetRoot\dotnet.exe" Highlight "Starting the synchronization of VMR.." # Synchronize the VMR +$versionDetailsPath = Resolve-Path (Join-Path $PSScriptRoot '..\Version.Details.xml') | Select-Object -ExpandProperty Path +[xml]$versionDetails = Get-Content -Path $versionDetailsPath +$repoName = $versionDetails.SelectSingleNode('//Source').Mapping +if (-not $repoName) { + Fail "Failed to resolve repo mapping from $versionDetailsPath" + exit 1 +} + $darcArgs = ( "vmr", "forwardflow", "--tmp", $tmpDir, @@ -130,9 +138,27 @@ if ($LASTEXITCODE -eq 0) { Highlight "Synchronization succeeded" } else { - Fail "Synchronization of repo to VMR failed!" - Fail "'$vmrDir' is left in its last state (re-run of this script will reset it)." - Fail "Please inspect the logs which contain path to the failing patch file (use -debugOutput to get all the details)." - Fail "Once you make changes to the conflicting VMR patch, commit it locally and re-run this script." - exit 1 + Highlight "Failed to flow code into the local VMR. Falling back to resetting the VMR to match repo contents..." + git -C $vmrDir reset --hard + + $resetArgs = ( + "vmr", "reset", + "${repoName}:HEAD", + "--vmr", $vmrDir, + "--tmp", $tmpDir, + "--additional-remotes", "${repoName}:${repoRoot}" + ) + + & "$darc" $resetArgs + + if ($LASTEXITCODE -eq 0) { + Highlight "Successfully reset the VMR using 'darc vmr reset'" + } + else { + Fail "Synchronization of repo to VMR failed!" + Fail "'$vmrDir' is left in its last state (re-run of this script will reset it)." + Fail "Please inspect the logs which contain path to the failing patch file (use -debugOutput to get all the details)." + Fail "Once you make changes to the conflicting VMR patch, commit it locally and re-run this script." + exit 1 + } } diff --git a/eng/common/vmr-sync.sh b/eng/common/vmr-sync.sh index 44239e331c0..198caec59bd 100755 --- a/eng/common/vmr-sync.sh +++ b/eng/common/vmr-sync.sh @@ -186,6 +186,13 @@ fi # Synchronize the VMR +version_details_path=$(cd "$scriptroot/.."; pwd -P)/Version.Details.xml +repo_name=$(grep -m 1 ' Date: Mon, 23 Feb 2026 12:54:42 +0100 Subject: [PATCH 077/100] [release/10.0] Update dependencies from dotnet/xharness (#16542) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 2 +- eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index f0c838fc651..f9aaf9e7331 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -22,7 +22,7 @@ This file should be imported by eng/Versions.props 1.1.0-beta.25424.1 1.1.0-beta.25424.1 - 11.0.0-prerelease.26107.1 + 11.0.0-prerelease.26117.1 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e1d9e857be7..40a06601311 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - bfbac237157e59cdbd19334325b2af80bd6e9828 + 0eeaa60169fe6a95932d29d822e20eb225ce0143 https://github.com/dotnet/roslyn From bea9ad37b2362138e9019ee9e23577357403f806 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 12:49:38 +0100 Subject: [PATCH 078/100] [release/10.0] Update dependencies from dotnet/arcade (#16550) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index f9aaf9e7331..76779735802 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -16,8 +16,8 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 10.0.0-beta.26080.4 - 10.0.0-beta.26080.4 + 10.0.0-beta.26123.2 + 10.0.0-beta.26123.2 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 40a06601311..17047116ed3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - b0f891cf7c1febc0ebbae7dd787f8a0b4c0cbc95 + 4d898652733deb7dd274237ac06d27ee2ad85b36 - + https://github.com/dotnet/arcade - b0f891cf7c1febc0ebbae7dd787f8a0b4c0cbc95 + 4d898652733deb7dd274237ac06d27ee2ad85b36 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index 8f5fc848068..d03d51aac6e 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.101" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26080.4", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26080.4", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26123.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26123.2", "Microsoft.Build.NoTargets": "3.7.0" } } From 5976b9d3d62aaac137169c638af21096bd8e8fcd Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 3 Mar 2026 04:34:23 -0800 Subject: [PATCH 079/100] Remove MaestroAccessToken from publish logs YAML (#16557) --- eng/common/core-templates/steps/publish-logs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 5a927b4c7bc..a9ea99ba6aa 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -31,7 +31,6 @@ steps: -runtimeSourceFeed https://ci.dot.net/internal -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' '$(publishing-dnceng-devdiv-code-r-build-re)' - '$(MaestroAccessToken)' '$(dn-bot-all-orgs-artifact-feeds-rw)' '$(akams-client-id)' '$(microsoft-symbol-server-pat)' From 6f1da2af699e35f16100c86933b8c65e36fa6f38 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:05:27 +0100 Subject: [PATCH 080/100] [release/10.0] Source code updates from dotnet/dotnet (#16556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 2 +- global.json | 4 ++-- .../src/CreateMD5SumsFile.cs | 4 ++-- src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 17047116ed3..2c0ba6ca109 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/global.json b/global.json index d03d51aac6e..7b51b431eee 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.101", + "version": "10.0.103", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.101" + "dotnet": "10.0.103" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26123.2", diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs index b877d72e75c..69ab5340c52 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs @@ -41,9 +41,9 @@ public override bool Execute() string relativePath = file.ItemSpec.Substring(RootDirectory.Length).TrimStart(Path.DirectorySeparatorChar).Replace('\\', '/'); // Always use Linux line-endings #if NET - writer.Write($"{Convert.ToHexString(hash)} {relativePath}\n"); + writer.Write($"{Convert.ToHexStringLower(hash)} {relativePath}\n"); #else - writer.Write($"{BitConverter.ToString(hash).Replace("-", "")} {relativePath}\n"); + writer.Write($"{BitConverter.ToString(hash).Replace("-", "").ToLower()} {relativePath}\n"); #endif } diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 879174e3149..51d241d90ef 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -440,18 +440,18 @@ private void ValidateProducedDebContent( using MD5 md5 = MD5.Create(); using FileStream fileStream = File.OpenRead(layoutFilePath); - string newHash = Convert.ToHexString(md5.ComputeHash(fileStream)); + string newHash = Convert.ToHexStringLower(md5.ComputeHash(fileStream)); if (signableFiles.Contains(targetSystemFilePath)) { newHash.Should().NotBe(originalHash); - md5sumsContents.Should().Contain($"{newHash} {targetSystemFilePath}"); - md5sumsContents.Should().NotContain($"{originalHash} {targetSystemFilePath}"); + md5sumsContents.Should().Contain($"{newHash} {targetSystemFilePath}"); + md5sumsContents.Should().NotContain($"{originalHash} {targetSystemFilePath}"); } else { newHash.Should().Be(originalHash); - md5sumsContents.Should().Contain($"{originalHash} {targetSystemFilePath}"); + md5sumsContents.Should().Contain($"{originalHash} {targetSystemFilePath}"); } } @@ -1831,8 +1831,8 @@ public void CheckDebSigning() var expectedFilesOriginalHashes = new (string, string)[] { - ("usr/local/bin/hello", "644981BBD6F4ED1B3CF68CD0F47981AA"), - ("usr/local/bin/mscorlib.dll", "B80EEBA2B8616B7C37E49B004D69BBB7") + ("usr/local/bin/hello", "644981bbd6f4ed1b3cf68cd0f47981aa"), + ("usr/local/bin/mscorlib.dll", "b80eeba2b8616b7c37e49b004d69bbb7") }; string[] signableFiles = ["usr/local/bin/mscorlib.dll"]; string expectedControlFileContent = "Package: test\nVersion: 1.0\nSection: base\nPriority: optional\nArchitecture: all\n"; From 8b0ca8dba65be0853690ce98ae8f950a25ff8421 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:34:04 +0100 Subject: [PATCH 081/100] [release/10.0] Source code updates from dotnet/dotnet (#16576) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 132 +++++++++++++++++++------------------- eng/Version.Details.xml | 2 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 76779735802..49e239b7501 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -5,30 +5,29 @@ This file should be imported by eng/Versions.props --> - - 2.23.0 - - 4.8.0 - 4.8.0 - - 2.0.0-preview.1.23470.14 - 2.0.0-preview.1.23470.14 - - 10.0.100-preview.4.25220.1 - + 10.0.0-beta.26123.2 10.0.0-beta.26123.2 - + 1.1.0-beta.25424.1 1.1.0-beta.25424.1 - - 11.0.0-prerelease.26117.1 - - 1.1.0-beta2-19575-01 - 1.1.0-beta2-19575-01 - + + 2.0.0-beta5.25210.1 + + 2.0.0-preview.1.24305.1 + 8.0.0-preview.24461.2 - + + 1.1.0-beta.25421.1 + + 17.12.50 + 17.12.50 + 17.12.50 + 17.12.50 + + 4.8.0 + 4.8.0 + 9.0.0-rc.2.24473.5 9.0.0-rc.2.24473.5 9.0.0-rc.2.24473.5 @@ -45,48 +44,48 @@ This file should be imported by eng/Versions.props 9.0.0-rc.2.24473.5 9.0.0-rc.2.24473.5 9.0.0-rc.2.24473.5 - - 13.0.3 - - 2.0.0-preview.1.24305.1 - + 9.0.100-preview.6.24328.19 - - 2.0.0-beta5.25210.1 - - 1.1.0-beta.25421.1 - - 17.12.50 - 17.12.50 - 17.12.50 - 17.12.50 + + 1.1.0-beta2-19575-01 + 1.1.0-beta2-19575-01 + + 2.0.0-preview.1.23470.14 + 2.0.0-preview.1.23470.14 + + 10.0.100-preview.4.25220.1 + + 11.0.0-prerelease.26117.1 + + 13.0.3 + + 2.23.0 - - $(MicrosoftApplicationInsightsPackageVersion) - - $(MicrosoftCodeAnalysisCSharpPackageVersion) - $(MicrosoftNetCompilersToolsetPackageVersion) - - $(MicrosoftSymbolUploaderPackageVersion) - $(MicrosoftSymbolUploaderBuildTaskPackageVersion) - - $(MicrosoftTemplateEngineAuthoringTasksPackageVersion) - + $(MicrosoftDotNetArcadeSdkPackageVersion) $(MicrosoftDotNetHelixSdkPackageVersion) - + $(MicrosoftDotNetDarcLibPackageVersion) $(MicrosoftDotNetProductConstructionServiceClientPackageVersion) - - $(MicrosoftDotNetXHarnessCLIPackageVersion) - - $(MicrosoftDiaSymReaderConverterPackageVersion) - $(MicrosoftDiaSymReaderPdb2PdbPackageVersion) - + + $(SystemCommandLinePackageVersion) + + $(MicrosoftDeploymentDotNetReleasesPackageVersion) + $(MicrosoftSymbolManifestGeneratorPackageVersion) - + + $(MicrosoftDncEngSecretManagerPackageVersion) + + $(MicrosoftBuildPackageVersion) + $(MicrosoftBuildFrameworkPackageVersion) + $(MicrosoftBuildTasksCorePackageVersion) + $(MicrosoftBuildUtilitiesCorePackageVersion) + + $(MicrosoftCodeAnalysisCSharpPackageVersion) + $(MicrosoftNetCompilersToolsetPackageVersion) + $(MicrosoftBclAsyncInterfacesPackageVersion) $(MicrosoftExtensionsDependencyInjectionPackageVersion) $(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion) @@ -103,20 +102,21 @@ This file should be imported by eng/Versions.props $(SystemSecurityCryptographyXmlPackageVersion) $(SystemTextEncodingsWebPackageVersion) $(SystemTextJsonPackageVersion) - - $(NewtonsoftJsonPackageVersion) - - $(MicrosoftDeploymentDotNetReleasesPackageVersion) - + $(MicrosoftNETSdkWorkloadManifestReaderPackageVersion) - - $(SystemCommandLinePackageVersion) - - $(MicrosoftDncEngSecretManagerPackageVersion) - - $(MicrosoftBuildPackageVersion) - $(MicrosoftBuildFrameworkPackageVersion) - $(MicrosoftBuildTasksCorePackageVersion) - $(MicrosoftBuildUtilitiesCorePackageVersion) + + $(MicrosoftDiaSymReaderConverterPackageVersion) + $(MicrosoftDiaSymReaderPdb2PdbPackageVersion) + + $(MicrosoftSymbolUploaderPackageVersion) + $(MicrosoftSymbolUploaderBuildTaskPackageVersion) + + $(MicrosoftTemplateEngineAuthoringTasksPackageVersion) + + $(MicrosoftDotNetXHarnessCLIPackageVersion) + + $(NewtonsoftJsonPackageVersion) + + $(MicrosoftApplicationInsightsPackageVersion) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2c0ba6ca109..fbc7b0406fd 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + From b4f8503acfdf742ed699db9c04335d5124f4a017 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:24:09 +0100 Subject: [PATCH 082/100] [release/10.0] Source code updates from dotnet/dotnet (#16583) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- global.json | 4 ++-- .../build/installer.build.targets | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index fbc7b0406fd..08bafab806c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/global.json b/global.json index 7b51b431eee..6f5c48ba44a 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.103", + "version": "10.0.104", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.103" + "dotnet": "10.0.104" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26123.2", diff --git a/src/Microsoft.DotNet.Build.Tasks.Installers/build/installer.build.targets b/src/Microsoft.DotNet.Build.Tasks.Installers/build/installer.build.targets index 8ece7b4a189..eb188a5b2dd 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Installers/build/installer.build.targets +++ b/src/Microsoft.DotNet.Build.Tasks.Installers/build/installer.build.targets @@ -249,11 +249,11 @@ - - From e365cfcb58176841c2fb80d1a40926f394776970 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:15:46 -0700 Subject: [PATCH 083/100] [release/10.0] Add automatic retry for notarization failures in SignTool (#16586) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- .../SignToolTests.cs | 127 ++++++++++++++++++ src/Microsoft.DotNet.SignTool/src/SignTool.cs | 33 ++++- 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 51d241d90ef..1e53899ba88 100644 --- a/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -3635,5 +3635,132 @@ public void ContainerSigningWithDoNotUnpackViaFileExtensionSignInfo() "File 'NestedContainer.1.0.0.nupkg' Certificate='NuGet'", }); } + + [Fact] + public void NotarizationRetriesOnFailure() + { + // List of files to be considered for signing + var itemsToSign = new List() + { + new ItemToSign(GetResourcePath("test.pkg")) + }; + + // Default signing information + var strongNameSignInfo = new Dictionary>() + { + { "581d91ccdfc4ea9c", new List{ new SignInfo(certificate: "ArcadeCertTest", strongName: "ArcadeStrongTest") } } + }; + + // Set up the cert to allow for signing and notarization. + var additionalCertificateInfo = new Dictionary>() + { + { "MacDeveloperHardenWithNotarization", + new List() { + new AdditionalCertificateInformation() { MacNotarizationAppName = "dotnet", MacSigningOperation = "MacDeveloperHarden" } + } + } + }; + + // Overriding information + var fileSignInfo = new Dictionary() + { + { new ExplicitSignInfoKey("test.pkg"), new FileSignInfoEntry("MacDeveloperHardenWithNotarization") } + }; + + var configuration = new Configuration(_tmpDir, + itemsToSign, + strongNameSignInfo, + fileSignInfo, + s_fileExtensionSignInfo, + additionalCertificateInfo, + itemsToSkip3rdPartyCheck: null, + tarToolPath: null, + pkgToolPath: null, + snPath: null, + new TaskLoggingHelper(new FakeBuildEngine(_output), "SignToolTests"), + telemetry: null); + + var parsedSigningInput = configuration.GenerateListOfFiles(); + + // Create a fake build engine to track build calls + var fakeBuildEngine = new FakeBuildEngineWithFailures(_output, failNotarizationCount: 3); + var fakeLog = new TaskLoggingHelper(fakeBuildEngine, "SignToolTests"); + + var args = new SignToolArgs( + tempPath: _tmpDir, + microBuildCorePath: CreateTestResource("MicroBuild.Core"), + testSign: true, + dotnetPath: null, + msbuildVerbosity: "quiet", + logDir: _tmpDir, + enclosingDir: "", + snBinaryPath: null, + wix3ToolsPath: null, + wixToolsPath: null, + tarToolPath: null, + pkgToolPath: null, + dotnetTimeout: -1); + + var signTool = new FakeSignTool(args, fakeLog); + + var util = new BatchSignUtil( + fakeBuildEngine, + fakeLog, + signTool, + parsedSigningInput, + Array.Empty(), + null); + + util.Go(false); + + // Verify that notarization was retried + fakeBuildEngine.NotarizationAttempts.Should().Be(4, "Notarization should succeed on the 4th attempt after 3 failures"); + } + } + + /// + /// Fake build engine that can simulate notarization failures + /// + internal class FakeBuildEngineWithFailures : IBuildEngine + { + private readonly int _failNotarizationCount; + private int _notarizationAttemptsSoFar = 0; + private readonly FakeBuildEngine _innerEngine; + + public int NotarizationAttempts => _notarizationAttemptsSoFar; + + public FakeBuildEngineWithFailures(ITestOutputHelper output, int failNotarizationCount) + { + _failNotarizationCount = failNotarizationCount; + _innerEngine = new FakeBuildEngine(output); + } + + public bool BuildProjectFile(string projectFileName, string[] targetNames, System.Collections.IDictionary globalProperties, System.Collections.IDictionary targetOutputs) + { + // Check if this is a notarization project + if (projectFileName.Contains("Notarize")) + { + _notarizationAttemptsSoFar++; + + // Fail the first N attempts + if (_notarizationAttemptsSoFar <= _failNotarizationCount) + { + return false; + } + } + + // Otherwise use the inner engine implementation + return _innerEngine.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs); + } + + public int ColumnNumberOfTaskNode => _innerEngine.ColumnNumberOfTaskNode; + public bool ContinueOnError { get => _innerEngine.ContinueOnError; set => _innerEngine.ContinueOnError = value; } + public int LineNumberOfTaskNode => _innerEngine.LineNumberOfTaskNode; + public string ProjectFileOfTaskNode => _innerEngine.ProjectFileOfTaskNode; + + public void LogCustomEvent(CustomBuildEventArgs e) => _innerEngine.LogCustomEvent(e); + public void LogErrorEvent(BuildErrorEventArgs e) => _innerEngine.LogErrorEvent(e); + public void LogMessageEvent(BuildMessageEventArgs e) => _innerEngine.LogMessageEvent(e); + public void LogWarningEvent(BuildWarningEventArgs e) => _innerEngine.LogWarningEvent(e); } } diff --git a/src/Microsoft.DotNet.SignTool/src/SignTool.cs b/src/Microsoft.DotNet.SignTool/src/SignTool.cs index b22fc815ef9..e1eecfe9efa 100644 --- a/src/Microsoft.DotNet.SignTool/src/SignTool.cs +++ b/src/Microsoft.DotNet.SignTool/src/SignTool.cs @@ -191,8 +191,37 @@ private bool AuthenticodeSignAndNotarize(IBuildEngine buildEngine, int round, IE { var notarizeProjectPath = Path.Combine(dir, $"Round{round}-Notarize.proj"); File.WriteAllText(notarizeProjectPath, GenerateBuildFileContent(filesToNotarize, null, true)); - string notarizeLogName = $"NotarizationRound{round}"; - status = RunMSBuild(buildEngine, notarizeProjectPath, Path.Combine(_args.LogDir, $"{notarizeLogName}.binlog"), Path.Combine(_args.LogDir, $"{notarizeLogName}.log"), Path.Combine(_args.LogDir, $"{notarizeLogName}.error.log")); + + // Notarization can be flaky, so retry up to 5 times with no wait between retries + const int maxRetries = 5; + int attempt = 0; + bool notarizationSucceeded = false; + + _log.LogMessage(MessageImportance.High, $"Starting notarization with up to {maxRetries} attempts"); + + while (attempt < maxRetries && !notarizationSucceeded) + { + attempt++; + _log.LogMessage(MessageImportance.High, $"Notarization attempt {attempt} of {maxRetries}"); + + string notarizeLogName = $"NotarizationRound{round}-Attempt{attempt}"; + notarizationSucceeded = RunMSBuild(buildEngine, notarizeProjectPath, + Path.Combine(_args.LogDir, $"{notarizeLogName}.binlog"), + Path.Combine(_args.LogDir, $"{notarizeLogName}.log"), + Path.Combine(_args.LogDir, $"{notarizeLogName}.error.log")); + + if (!notarizationSucceeded && attempt < maxRetries) + { + _log.LogMessage(MessageImportance.High, $"Notarization failed on attempt {attempt}. Retrying..."); + } + } + + if (!notarizationSucceeded) + { + _log.LogError($"Notarization failed after {maxRetries} attempts"); + } + + status = notarizationSucceeded; } return status; From 3907f62e877e105b6196b1bd9c309203d6362a0a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 10:29:41 +0100 Subject: [PATCH 084/100] [release/10.0] Source code updates from dotnet/dotnet (#16600) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 2 +- global.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 08bafab806c..6f839b1b551 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/global.json b/global.json index 6f5c48ba44a..ce375bf32e5 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.104", + "version": "10.0.105", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,7 +9,7 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.104" + "dotnet": "10.0.105" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26123.2", From 8f7569ade864f9310fd0051f5aff7a74105c225d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 00:41:19 +0000 Subject: [PATCH 085/100] [release/10.0] Fix incorrect "NOT ... OR ..." pattern in LoadExclusions filter (#16627) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- .../src/PublishArtifactsInManifestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs index 2176a1e1f6a..90f94d2cee9 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs @@ -754,7 +754,7 @@ FrozenSet LoadExclusions(string symbolPublishingExclusionsFile) // These files tend to be short - load it all at once. string[] files = File.ReadAllLines(symbolPublishingExclusionsFile); - FrozenSet excludeFiles = files.Where(x => x is not null or "").ToFrozenSet(); + FrozenSet excludeFiles = files.Where(x => !string.IsNullOrEmpty(x)).ToFrozenSet(); if (excludeFiles.Count > 0) { From 52b039724971b022dcdba22f37fa1739ff84fc09 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:29:23 +0000 Subject: [PATCH 086/100] [release/10.0] Use dnceng build pool instead of hosted pool in azure-pipelines-pr.yml (#16633) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- azure-pipelines-pr.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml index 5e79c3dda17..6c5a6c28ec5 100644 --- a/azure-pipelines-pr.yml +++ b/azure-pipelines-pr.yml @@ -64,7 +64,8 @@ stages: - job: Windows_NT timeoutInMinutes: 90 pool: - vmImage: windows-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals windows.vs2026.amd64.open strategy: matrix: Build_Release: @@ -109,7 +110,8 @@ stages: - job: Linux container: LinuxContainer pool: - vmImage: ubuntu-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals build.azurelinux.3.amd64.open strategy: matrix: Build_Debug: @@ -140,7 +142,8 @@ stages: - job: Windows_NT timeoutInMinutes: 90 pool: - vmimage: windows-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals windows.vs2026.amd64.open strategy: matrix: Build_Release: @@ -182,7 +185,8 @@ stages: timeoutInMinutes: 90 container: LinuxContainer pool: - vmimage: ubuntu-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals build.azurelinux.3.amd64.open strategy: matrix: Build_Debug: @@ -227,7 +231,8 @@ stages: timeoutInMinutes: 90 container: LinuxContainer pool: - vmimage: ubuntu-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals build.azurelinux.3.amd64.open strategy: matrix: Build_Debug: @@ -257,7 +262,8 @@ stages: timeoutInMinutes: 90 container: LinuxContainer pool: - vmimage: ubuntu-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals build.azurelinux.3.amd64.open strategy: matrix: Build_Debug: @@ -287,7 +293,8 @@ stages: timeoutInMinutes: 90 container: LinuxContainer pool: - vmimage: ubuntu-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals build.azurelinux.3.amd64.open strategy: matrix: Build_Debug: @@ -316,7 +323,8 @@ stages: - job: Android_Devices timeoutInMinutes: 90 pool: - vmimage: windows-latest + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals windows.vs2026.amd64.open strategy: matrix: Build_Release: From 7a177ed7a6fa1214dc0c08169f30cb68f8bfc793 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:31:26 +0000 Subject: [PATCH 087/100] [release/10.0] Update dependencies from dotnet/xharness (#16590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.props | 2 +- eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 49e239b7501..5fc35c850d6 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -55,7 +55,7 @@ This file should be imported by eng/Versions.props 10.0.100-preview.4.25220.1 - 11.0.0-prerelease.26117.1 + 11.0.0-prerelease.26169.1 13.0.3 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6f839b1b551..51da6261bb1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -43,9 +43,9 @@ https://github.com/dotnet/arcade-services e6a28a549cc932140719fd63ba0387254db0ac51 - + https://github.com/dotnet/xharness - 0eeaa60169fe6a95932d29d822e20eb225ce0143 + b0c8bf6dba87c70e284cff06819f0cd714c8f2e4 https://github.com/dotnet/roslyn From 62dc2defffeadabf6761a9ed7e142692107330c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 27 Mar 2026 15:29:18 +0100 Subject: [PATCH 088/100] [release/10.0] Update build images to VS 2026 (#16635) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- azure-pipelines-codeql.yml | 2 +- azure-pipelines-daily.yaml | 2 +- azure-pipelines-unofficial.yml | 2 +- azure-pipelines.yml | 2 +- eng/common/core-templates/job/onelocbuild.yml | 4 ++-- .../core-templates/job/publish-build-assets.yml | 4 ++-- .../core-templates/post-build/post-build.yml | 16 ++++++++-------- .../variables/pool-providers.yml | 2 +- .../templates/variables/pool-providers.yml | 2 +- eng/promote-build.yml | 4 ++-- eng/validate-sdk.yml | 2 +- .../azure-pipelines-xcopy-msbuild.yml | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/azure-pipelines-codeql.yml b/azure-pipelines-codeql.yml index 6e609a45668..f7ddaff115f 100644 --- a/azure-pipelines-codeql.yml +++ b/azure-pipelines-codeql.yml @@ -41,7 +41,7 @@ jobs: displayName: CodeQL pool: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals 1es-windows-2022 + demands: ImageOverride -equals windows.vs2026.amd64 timeoutInMinutes: 90 steps: diff --git a/azure-pipelines-daily.yaml b/azure-pipelines-daily.yaml index 396bd9c0572..9bb7e6fb9b1 100644 --- a/azure-pipelines-daily.yaml +++ b/azure-pipelines-daily.yaml @@ -16,7 +16,7 @@ stages: displayName: Synchronize secrets pool: name: NetCore1ESPool-Internal-NoMSI - demands: ImageOverride -equals 1es-windows-2022 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - script: restore.cmd -ci diff --git a/azure-pipelines-unofficial.yml b/azure-pipelines-unofficial.yml index d63cd797c6c..6bf716ec884 100644 --- a/azure-pipelines-unofficial.yml +++ b/azure-pipelines-unofficial.yml @@ -20,7 +20,7 @@ extends: parameters: pool: name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 + image: windows.vs2026.amd64 os: windows stages: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 123ab961fae..c8514906f39 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -42,7 +42,7 @@ extends: networkIsolationPolicy: Permissive, CFSClean, CFSClean2 pool: name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 + image: windows.vs2026.amd64 os: windows sdl: policheck: diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml index c5788829a87..eefed3b667a 100644 --- a/eng/common/core-templates/job/onelocbuild.yml +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -52,13 +52,13 @@ jobs: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 + image: windows.vs2026.amd64 os: windows steps: diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index b955fac6e13..9afcb8ae159 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -74,13 +74,13 @@ jobs: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2022.amd64 + image: windows.vs2026.amd64 os: windows steps: - ${{ if eq(parameters.is1ESPipeline, '') }}: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index b942a79ef02..2df4acb7685 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -120,7 +120,7 @@ stages: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng @@ -164,14 +164,14 @@ stages: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 + image: windows.vs2026.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) @@ -225,14 +225,14 @@ stages: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 + image: windows.vs2026.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) @@ -286,18 +286,18 @@ stages: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2022 + image: 1ESPT-Windows2025 demands: Cmd os: windows # If it's not devdiv, it's dnceng ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2022.amd64 + image: windows.vs2026.amd64 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2022.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: diff --git a/eng/common/templates-official/variables/pool-providers.yml b/eng/common/templates-official/variables/pool-providers.yml index 1f308b24efc..2cc3ae305d5 100644 --- a/eng/common/templates-official/variables/pool-providers.yml +++ b/eng/common/templates-official/variables/pool-providers.yml @@ -23,7 +23,7 @@ # # pool: # name: $(DncEngInternalBuildPool) -# image: 1es-windows-2022 +# image: windows.vs2026.amd64 variables: # Coalesce the target and source branches so we know when a PR targets a release branch diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml index 18693ea120d..587770f0add 100644 --- a/eng/common/templates/variables/pool-providers.yml +++ b/eng/common/templates/variables/pool-providers.yml @@ -23,7 +23,7 @@ # # pool: # name: $(DncEngInternalBuildPool) -# demands: ImageOverride -equals windows.vs2022.amd64 +# demands: ImageOverride -equals windows.vs2026.amd64 variables: - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - template: /eng/common/templates-official/variables/pool-providers.yml diff --git a/eng/promote-build.yml b/eng/promote-build.yml index e2693fc36d8..d1faf9ac4b3 100644 --- a/eng/promote-build.yml +++ b/eng/promote-build.yml @@ -65,11 +65,11 @@ extends: pool: ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: name: AzurePipelines-EO - image: 1ESPT-Windows2019 + image: 1ESPT-Windows2025 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2022.amd64 + image: windows.vs2026.amd64 os: windows sdl: policheck: diff --git a/eng/validate-sdk.yml b/eng/validate-sdk.yml index 4b1cfbd8a0d..ef727937070 100644 --- a/eng/validate-sdk.yml +++ b/eng/validate-sdk.yml @@ -29,7 +29,7 @@ jobs: - template: /eng/common/templates-official/variables/pool-providers.yml@self pool: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2022.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 preSteps: - checkout: self clean: true diff --git a/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml b/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml index c4f49addcc9..1de479e6e9b 100644 --- a/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml +++ b/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml @@ -22,7 +22,7 @@ jobs: displayName: Build xcopy-msbuild package pool: name: NetCore1ESPool-Internal - demands: ImageOverride -equals 1es-windows-2022 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - task: PowerShell@2 displayName: Download Visual Studio Build Tools From 2ad6e0a00c692279222642dbcd6e72eb21572d93 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:26:46 +0200 Subject: [PATCH 089/100] [release/10.0] Update dependencies from dotnet/arcade (#16654) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.props | 4 ++-- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 5fc35c850d6..dc2a4e1e273 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,8 +6,8 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.26123.2 - 10.0.0-beta.26123.2 + 10.0.0-beta.26177.7 + 10.0.0-beta.26177.7 1.1.0-beta.25424.1 1.1.0-beta.25424.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 51da6261bb1..319ef2ef662 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -27,13 +27,13 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 4d898652733deb7dd274237ac06d27ee2ad85b36 + 62dc2defffeadabf6761a9ed7e142692107330c0 - + https://github.com/dotnet/arcade - 4d898652733deb7dd274237ac06d27ee2ad85b36 + 62dc2defffeadabf6761a9ed7e142692107330c0 https://github.com/dotnet/arcade-services diff --git a/global.json b/global.json index ce375bf32e5..4e7b5e61606 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "dotnet": "10.0.105" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26123.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26123.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26177.7", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.26177.7", "Microsoft.Build.NoTargets": "3.7.0" } } From 9cc200f0846f3c95abc7bac351c748545ac8be1a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:55:49 +0000 Subject: [PATCH 090/100] Switch enablePublishBuildArtifacts to use pipeline artifacts (#16630) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/templates-official/job/job.yml | 7 +++---- eng/common/templates/job/job.yml | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 92a0664f564..f70224eaa45 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -44,11 +44,10 @@ jobs: sbomEnabled: false # we don't need SBOM for logs - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: - - output: buildArtifacts + - output: pipelineArtifact displayName: Publish Logs - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' - publishLocation: Container - ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }} + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }} continueOnError: true condition: always() sbomEnabled: false # we don't need SBOM for logs diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 238fa0818f7..7f1b5d97d1a 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -61,16 +61,16 @@ jobs: sbomEnabled: false # we don't need SBOM for logs - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: false args: displayName: Publish Logs - pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' - publishLocation: Container + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }} continueOnError: true condition: always() + sbomEnabled: false - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml From c269587958d776e5594f7817afc9b98b0f39fc00 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Mon, 30 Mar 2026 09:24:27 -0700 Subject: [PATCH 091/100] Migrate from PublishBuildArtifacts to PublishPipelineArtifact (#16638) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- .../core-templates/job/publish-build-assets.yml | 5 ++--- .../post-build/setup-maestro-vars.yml | 2 +- .../core-templates/steps/publish-logs.yml | 8 ++++---- eng/common/templates-official/job/job.yml | 15 +++++++++++---- eng/common/templates/job/job.yml | 17 +++++++++++++---- .../azure-pipelines-xcopy-msbuild.yml | 7 +++---- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 9afcb8ae159..215ff7a78c5 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -175,13 +175,12 @@ jobs: retryCountOnTaskFailure: 10 # for any logs being locked sbomEnabled: false # we don't need SBOM for logs - - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} args: displayName: Publish ReleaseConfigs Artifact - pathToPublish: '$(Build.StagingDirectory)/ReleaseConfigs' - publishLocation: Container + targetPath: '$(Build.StagingDirectory)/ReleaseConfigs' artifactName: ReleaseConfigs - ${{ if or(eq(parameters.publishAssetsImmediately, 'true'), eq(parameters.isAssetlessBuild, 'true')) }}: diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml index a7abd58c4bb..b9ba3da9c87 100644 --- a/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -8,7 +8,7 @@ steps: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@0 displayName: Download Release Configs inputs: buildType: current diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index a9ea99ba6aa..8f827c6c7fd 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -50,13 +50,13 @@ steps: TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' condition: always() -- template: /eng/common/core-templates/steps/publish-build-artifacts.yml +- template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} args: displayName: Publish Logs - pathToPublish: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' - publishLocation: Container - artifactName: PostBuildLogs + targetPath: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + artifactName: PostBuildLogs_Attempt$(System.JobAttempt) continueOnError: true condition: always() + sbomEnabled: false # we don't need SBOM for logs diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index f70224eaa45..ddf7aecea88 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -26,11 +26,18 @@ jobs: outputs: - ${{ if ne(parameters.artifacts.publish, '') }}: - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - output: buildArtifacts + - output: pipelineArtifact displayName: Publish pipeline artifacts - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} - condition: always() + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + condition: succeeded() + retryCountOnTaskFailure: 10 # for any logs being locked + continueOnError: true + - output: pipelineArtifact + displayName: Publish pipeline artifacts + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}_Attempt$(System.JobAttempt) + condition: not(succeeded()) retryCountOnTaskFailure: 10 # for any logs being locked continueOnError: true - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 7f1b5d97d1a..c096f906375 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -36,16 +36,25 @@ jobs: artifactPublishSteps: - ${{ if ne(parameters.artifacts.publish, '') }}: - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: false args: displayName: Publish pipeline artifacts - pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - publishLocation: Container + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} continueOnError: true - condition: always() + condition: succeeded() + retryCountOnTaskFailure: 10 # for any logs being locked + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish pipeline artifacts + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}_Attempt$(System.JobAttempt) + continueOnError: true + condition: not(succeeded()) retryCountOnTaskFailure: 10 # for any logs being locked - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml diff --git a/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml b/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml index 1de479e6e9b..55b41095290 100644 --- a/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml +++ b/eng/xcopy-msbuild/azure-pipelines-xcopy-msbuild.yml @@ -43,8 +43,7 @@ jobs: TargetFolder: '$(Build.ArtifactStagingDirectory)\publish' OverWrite: true - - task: PublishBuildArtifacts@1 + - task: PublishPipelineArtifact@1 inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)\publish' - ArtifactName: 'package' - publishLocation: 'Container' \ No newline at end of file + targetPath: '$(Build.ArtifactStagingDirectory)\publish' + artifactName: 'package' \ No newline at end of file From aa21b5fed400e4bb47baceebadce93f0369ce4cf Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Tue, 31 Mar 2026 00:41:17 -0700 Subject: [PATCH 092/100] Fix ReleaseConfigs download path after pipeline artifacts migration (#16645) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/core-templates/post-build/setup-maestro-vars.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml index b9ba3da9c87..6dfa99ec5e3 100644 --- a/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -8,12 +8,11 @@ steps: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadPipelineArtifact@0 + - task: DownloadPipelineArtifact@2 displayName: Download Release Configs inputs: - buildType: current artifactName: ReleaseConfigs - checkDownloadedFiles: true + targetPath: '$(Build.StagingDirectory)/ReleaseConfigs' - task: AzureCLI@2 name: setReleaseVars From 4c3e42ccb3b43ee468c89a902c4c178a180ddc30 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Tue, 31 Mar 2026 07:08:34 -0700 Subject: [PATCH 093/100] Move to v4 publishing. (#16639) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/Publishing.props | 1 + eng/build.yml | 5 +- eng/common-variables.yml | 2 - eng/common/core-templates/jobs/jobs.yml | 5 + .../core-templates/post-build/post-build.yml | 105 ++++++++++++------ eng/common/templates-official/job/job.yml | 10 ++ 6 files changed, 93 insertions(+), 35 deletions(-) diff --git a/eng/Publishing.props b/eng/Publishing.props index fa47f5eb4b6..065b4af8b38 100644 --- a/eng/Publishing.props +++ b/eng/Publishing.props @@ -8,6 +8,7 @@ false + 4 diff --git a/eng/build.yml b/eng/build.yml index 2c7cb0452de..4165b0ba787 100644 --- a/eng/build.yml +++ b/eng/build.yml @@ -22,11 +22,12 @@ stages: publish: artifacts: true logs: true - manifests: true enableMicrobuild: true microbuildUseESRP: ${{ parameters.microbuildUseESRP }} + enablePublishBuildAssets: true enableSourceIndex: true enableSourceBuild: true + publishingVersion: 4 workspace: clean: all jobs: @@ -62,7 +63,7 @@ stages: - template: /eng/common/templates-official/post-build/post-build.yml@self parameters: - publishingInfraVersion: 3 + publishingInfraVersion: 4 # signing validation will not run, even if the below value is 'true', if the 'PostBuildSign' variable is set to 'true' enableSigningValidation: false # Sourcelink validation isn't passing for Arcade due to some regressions. This should be diff --git a/eng/common-variables.yml b/eng/common-variables.yml index 4892a24959c..28da5202913 100644 --- a/eng/common-variables.yml +++ b/eng/common-variables.yml @@ -28,11 +28,9 @@ variables: - group: Publish-Build-Assets - group: DotNet-HelixApi-Access - group: SDL_Settings - # DotNetPublishUsingPipelines can be removed when Arcade itself consumes a new Arcade version. - name: _InternalBuildArgs value: /p:DotNetSignType=${{ parameters.signType }} /p:TeamName=$(_TeamName) - /p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER) - name: PostBuildSign value: true diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index 01ada747665..cc8cce45278 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -43,6 +43,10 @@ parameters: artifacts: {} is1ESPipeline: '' + + # Publishing version w/default. + publishingVersion: 3 + repositoryAlias: self officialBuildId: '' @@ -102,6 +106,7 @@ jobs: parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} continueOnError: ${{ parameters.continueOnError }} + publishingVersion: ${{ parameters.publishingVersion }} dependsOn: - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: - ${{ each job in parameters.publishBuildAssetsDependsOn }}: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 2df4acb7685..905a6315e2d 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -9,6 +9,7 @@ parameters: default: 3 values: - 3 + - 4 - name: BARBuildId displayName: BAR Build Id @@ -140,16 +141,30 @@ stages: PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} is1ESPipeline: ${{ parameters.is1ESPipeline }} - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true + - ${{ if ne(parameters.publishingInfraVersion, 4) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + - ${{ if eq(parameters.publishingInfraVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts (V4) + inputs: + itemPattern: '*/packages/**/*.nupkg' + targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + - task: CopyFiles@2 + displayName: Flatten packages to PackageArtifacts + inputs: + SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + Contents: '**/*.nupkg' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' + flattenFolders: true - task: PowerShell@2 displayName: Validate @@ -183,16 +198,30 @@ stages: PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} is1ESPipeline: ${{ parameters.is1ESPipeline }} - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true + - ${{ if ne(parameters.publishingInfraVersion, 4) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + - ${{ if eq(parameters.publishingInfraVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts (V4) + inputs: + itemPattern: '*/packages/**/*.nupkg' + targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + - task: CopyFiles@2 + displayName: Flatten packages to PackageArtifacts + inputs: + SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + Contents: '**/*.nupkg' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' + flattenFolders: true # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -244,16 +273,30 @@ stages: PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} is1ESPipeline: ${{ parameters.is1ESPipeline }} - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true + - ${{ if ne(parameters.publishingInfraVersion, 4) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Blob Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: BlobArtifacts + checkDownloadedFiles: true + - ${{ if eq(parameters.publishingInfraVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts (V4) + inputs: + itemPattern: '*/assets/**' + targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + - task: CopyFiles@2 + displayName: Flatten assets to BlobArtifacts + inputs: + SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + Contents: '**/*' + TargetFolder: '$(Build.ArtifactStagingDirectory)/BlobArtifacts' + flattenFolders: true - task: PowerShell@2 displayName: Validate @@ -328,7 +371,7 @@ stages: scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) - -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} + -PublishingInfraVersion 3 -AzdoToken '$(System.AccessToken)' -WaitPublishingFinish true -RequireDefaultChannels ${{ parameters.requireDefaultChannels }} diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index ddf7aecea88..bdad742a19e 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -74,6 +74,16 @@ jobs: targetPath: $(Build.ArtifactStagingDirectory)/sbom artifactName: $(ARTIFACT_NAME) + # V4 publishing: automatically publish staged artifacts as a pipeline artifact. + # The artifact name matches the SDK's FutureArtifactName ($(System.PhaseName)_Artifacts), + # which is encoded in the asset manifest for downstream publishing to discover. + - ${{ if eq(parameters.publishingVersion, 4) }}: + - output: pipelineArtifact + displayName: 'Publish V4 pipeline artifacts' + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' + artifactName: '$(System.PhaseName)_Artifacts' + continueOnError: true + # add any outputs provided via root yaml - ${{ if ne(parameters.templateContext.outputs, '') }}: - ${{ each output in parameters.templateContext.outputs }}: From 3c7af9b4fb4895e5489f58126a1f4fab0a0fa227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 31 Mar 2026 19:03:44 +0200 Subject: [PATCH 094/100] Remove SourceLink validation from post-build pipeline (#16648) The SourceLink CLI tool targets netcoreapp2.1 and has not functioned correctly for years. Only 6 repos opted in, and none were getting real validation. - Delete eng/common/post-build/sourcelink-validation.ps1 - Replace the SourceLink Validation job in post-build.yml with a lightweight warning step - Remove the SourceLinkCLIVersion variable from common-variables.yml - Keep the enableSourceLinkValidation parameter so existing pipelines don't break Fixes https://github.com/dotnet/arcade/issues/16647 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- Documentation/CorePackages/Publishing.md | 2 - Documentation/ReleaseRingsPlan.md | 1 - Documentation/Validation.md | 9 +- Documentation/YamlStagesRepoStatus.md | 2 +- eng/build.yml | 3 - .../post-build/common-variables.yml | 2 - .../core-templates/post-build/post-build.yml | 73 +--- .../post-build/sourcelink-validation.ps1 | 327 ------------------ 8 files changed, 18 insertions(+), 401 deletions(-) delete mode 100644 eng/common/post-build/sourcelink-validation.ps1 diff --git a/Documentation/CorePackages/Publishing.md b/Documentation/CorePackages/Publishing.md index 3c8528497d6..67da815b6f5 100644 --- a/Documentation/CorePackages/Publishing.md +++ b/Documentation/CorePackages/Publishing.md @@ -103,7 +103,6 @@ These steps are needed for Arcade versions before `10.0.0`. After that, V3 is th - template: eng\common\templates\post-build\post-build.yml parameters: publishingInfraVersion: 3 - enableSourceLinkValidation: false ... ``` @@ -112,7 +111,6 @@ These steps are needed for Arcade versions before `10.0.0`. After that, V3 is th | Name | Type | Description |Default Value | | --------------------------------------- | -------- | -----------------------------------------------------------------------------------------------------|----- | | publishingInfraVersion | int | Publishing infrastructure version - Use 3 for latest publishing infra. Accepted values are 3 (.NET 5.0+) and 2 (.NET 3.1). | 3 | - | enableSourceLinkValidation | bool | Run SourceLink validation during the post-build stage. | false | | enableSigningValidation | bool | Run signing validation during the post-build stage. | true | | enableNugetValidation | bool | Run NuGet package validation tool during the post build stage. | true | | symbolPublishingAdditionalParameters | string | Additional arguments for the PublishToSymbolServers sdk task. | '' | diff --git a/Documentation/ReleaseRingsPlan.md b/Documentation/ReleaseRingsPlan.md index b5afbf41e83..a85a30cb3fd 100644 --- a/Documentation/ReleaseRingsPlan.md +++ b/Documentation/ReleaseRingsPlan.md @@ -35,7 +35,6 @@ validation checks. * BinSkim validation * Packages validation * Symbols validation -* Sourcelink validation * Checksum validation * Asset validation * Signing validation diff --git a/Documentation/Validation.md b/Documentation/Validation.md index d2aa1a641eb..ac739ac0ada 100644 --- a/Documentation/Validation.md +++ b/Documentation/Validation.md @@ -5,11 +5,11 @@ Validating builds is an important part of producing a releasable product. There * CI/PR build time * Repository-level functional testing. Owned by the product teams and defined in the repositories, and used to identify bugs * Official build time (optionally) - * Source code validation: this includes SDL testing and localization testing. Used to confirm that source code meets Microsoft's standards. - * Package validation: includes sourcelink validation, symbols validation, nupkg metadata validation. Used to confirm that customers will be able to install and debug packages from .NET. + * Source code validation: this includes SDL testing (handled by 1ES Pipeline Templates) and localization testing. Used to confirm that source code meets Microsoft's standards. + * Package validation: includes symbols validation, nupkg metadata validation. Used to confirm that customers will be able to install and debug packages from .NET. * Nightly validation pipeline (optionally) - * Source code validation: this includes SDL testing and localization testing. Used to confirm that source code meets Microsoft's standards. - * Package validation: includes sourcelink validation, symbols validation, nupkg metadata validation. Used to confirm that customers will be able to install and debug packages from .NET. + * Source code validation: this includes SDL testing (handled by 1ES Pipeline Templates) and localization testing. Used to confirm that source code meets Microsoft's standards. + * Package validation: includes symbols validation, nupkg metadata validation. Used to confirm that customers will be able to install and debug packages from .NET. * Signing validation: Used to validate that all bits that we ship have been signed properly. While many of these validation steps can be performed in official builds, the supported model is to onboard to the nightly validation pipeline. All of these steps will be performed as part of shipping the product, and all product teams have the option of running these exact validation steps at a nightly cadence. @@ -26,7 +26,6 @@ To gather all of the assets to be validated, Validate-DotNet uses information fo * SDL Validation (which will open TSA issues for any failures found) * Localization Validation * NuGet Metadata Validation -* Sourcelink Validation * Symbols Validation * NuGet Package Icon Validation * Checksums Validation diff --git a/Documentation/YamlStagesRepoStatus.md b/Documentation/YamlStagesRepoStatus.md index cd697b35fcc..acb6b359ead 100644 --- a/Documentation/YamlStagesRepoStatus.md +++ b/Documentation/YamlStagesRepoStatus.md @@ -22,7 +22,7 @@ Target completion date is 8/13/2019. | CLI | licavalc | Complete | ✔️ | | | CLICommandLineParser | licavalc | N/A | | This repo is not being developed anymore. We are taking a pinned version of it | | CoreClr | jeffschw/arobins | Complete | ✔️ | | -| CoreFx | danmose/safern | Complete | ✔️ | SourceLink validation disabled: https://github.com/dotnet/arcade/issues/3603 | +| CoreFx | danmose/safern | Complete | ✔️ | | | IoT | joperezr | Complete | ✔️ | | | Core-SDK | licavalc | In progress | ➖ | Working in parallel.Will need https://github.com/dotnet/arcade/issues/3607 to be done before completing. | | Core-Setup | dleeapho | Complete | ✔️ | Uses workarounds and skips most validation. Uses custom publish steps. | diff --git a/eng/build.yml b/eng/build.yml index 4165b0ba787..f5a4c4bcc33 100644 --- a/eng/build.yml +++ b/eng/build.yml @@ -66,9 +66,6 @@ stages: publishingInfraVersion: 4 # signing validation will not run, even if the below value is 'true', if the 'PostBuildSign' variable is set to 'true' enableSigningValidation: false - # Sourcelink validation isn't passing for Arcade due to some regressions. This should be - # enabled back once this issue is resolved: https://github.com/dotnet/arcade/issues/2912 - enableSourceLinkValidation: false publishDependsOn: - Validate - ValidateSdk diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml index d5627a994ae..db298ae16ba 100644 --- a/eng/common/core-templates/post-build/common-variables.yml +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -11,8 +11,6 @@ variables: - name: MaestroApiVersion value: "2020-02-20" - - name: SourceLinkCLIVersion - value: 3.0.0 - name: SymbolToolVersion value: 1.0.1 - name: BinlogToolVersion diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 905a6315e2d..b54db518826 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -247,67 +247,20 @@ stages: JobLabel: 'Signing' BinlogToolVersion: $(BinlogToolVersion) - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2025 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: - name: $(DncEngInternalBuildPool) - image: windows.vs2026.amd64 - os: windows - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 - steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - ${{ if ne(parameters.publishingInfraVersion, 4) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.publishingInfraVersion, 4) }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts (V4) - inputs: - itemPattern: '*/assets/**' - targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - - task: CopyFiles@2 - displayName: Flatten assets to BlobArtifacts - inputs: - SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - Contents: '**/*' - TargetFolder: '$(Build.ArtifactStagingDirectory)/BlobArtifacts' - flattenFolders: true - - - task: PowerShell@2 - displayName: Validate + # SourceLink validation has been removed — the underlying CLI tool + # (targeting netcoreapp2.1) has not functioned for years. + # The enableSourceLinkValidation parameter is kept but ignored so + # existing pipelines that pass it are not broken. + # See https://github.com/dotnet/arcade/issues/16647 + - ${{ if eq(parameters.enableSourceLinkValidation, 'true') }}: + - job: + displayName: 'SourceLink Validation Removed - please remove enableSourceLinkValidation from your pipeline' + pool: server + steps: + - task: Delay@1 + displayName: 'Warning: SourceLink validation removed (see https://github.com/dotnet/arcade/issues/16647)' inputs: - filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true + delayForMinutes: '0' - ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - stage: publish_using_darc diff --git a/eng/common/post-build/sourcelink-validation.ps1 b/eng/common/post-build/sourcelink-validation.ps1 deleted file mode 100644 index 1976ef70fb8..00000000000 --- a/eng/common/post-build/sourcelink-validation.ps1 +++ /dev/null @@ -1,327 +0,0 @@ -param( - [Parameter(Mandatory=$true)][string] $InputPath, # Full path to directory where Symbols.NuGet packages to be checked are stored - [Parameter(Mandatory=$true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation - [Parameter(Mandatory=$false)][string] $GHRepoName, # GitHub name of the repo including the Org. E.g., dotnet/arcade - [Parameter(Mandatory=$false)][string] $GHCommit, # GitHub commit SHA used to build the packages - [Parameter(Mandatory=$true)][string] $SourcelinkCliVersion # Version of SourceLink CLI to use -) - -$ErrorActionPreference = 'Stop' -Set-StrictMode -Version 2.0 - -# `tools.ps1` checks $ci to perform some actions. Since the post-build -# scripts don't necessarily execute in the same agent that run the -# build.ps1/sh script this variable isn't automatically set. -$ci = $true -$disableConfigureToolsetImport = $true -. $PSScriptRoot\..\tools.ps1 - -# Cache/HashMap (File -> Exist flag) used to consult whether a file exist -# in the repository at a specific commit point. This is populated by inserting -# all files present in the repo at a specific commit point. -$global:RepoFiles = @{} - -# Maximum number of jobs to run in parallel -$MaxParallelJobs = 16 - -$MaxRetries = 5 -$RetryWaitTimeInSeconds = 30 - -# Wait time between check for system load -$SecondsBetweenLoadChecks = 10 - -if (!$InputPath -or !(Test-Path $InputPath)){ - Write-Host "No files to validate." - ExitWithExitCode 0 -} - -$ValidatePackage = { - param( - [string] $PackagePath # Full path to a Symbols.NuGet package - ) - - . $using:PSScriptRoot\..\tools.ps1 - - # Ensure input file exist - if (!(Test-Path $PackagePath)) { - Write-Host "Input file does not exist: $PackagePath" - return [pscustomobject]@{ - result = 1 - packagePath = $PackagePath - } - } - - # Extensions for which we'll look for SourceLink information - # For now we'll only care about Portable & Embedded PDBs - $RelevantExtensions = @('.dll', '.exe', '.pdb') - - Write-Host -NoNewLine 'Validating ' ([System.IO.Path]::GetFileName($PackagePath)) '...' - - $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath) - $ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId - $FailedFiles = 0 - - Add-Type -AssemblyName System.IO.Compression.FileSystem - - [System.IO.Directory]::CreateDirectory($ExtractPath) | Out-Null - - try { - $zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath) - - $zip.Entries | - Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} | - ForEach-Object { - $FileName = $_.FullName - $Extension = [System.IO.Path]::GetExtension($_.Name) - $FakeName = -Join((New-Guid), $Extension) - $TargetFile = Join-Path -Path $ExtractPath -ChildPath $FakeName - - # We ignore resource DLLs - if ($FileName.EndsWith('.resources.dll')) { - return [pscustomobject]@{ - result = 0 - packagePath = $PackagePath - } - } - - [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true) - - $ValidateFile = { - param( - [string] $FullPath, # Full path to the module that has to be checked - [string] $RealPath, - [ref] $FailedFiles - ) - - $sourcelinkExe = "$env:USERPROFILE\.dotnet\tools" - $sourcelinkExe = Resolve-Path "$sourcelinkExe\sourcelink.exe" - $SourceLinkInfos = & $sourcelinkExe print-urls $FullPath | Out-String - - if ($LASTEXITCODE -eq 0 -and -not ([string]::IsNullOrEmpty($SourceLinkInfos))) { - $NumFailedLinks = 0 - - # We only care about Http addresses - $Matches = (Select-String '(http[s]?)(:\/\/)([^\s,]+)' -Input $SourceLinkInfos -AllMatches).Matches - - if ($Matches.Count -ne 0) { - $Matches.Value | - ForEach-Object { - $Link = $_ - $CommitUrl = "https://raw.githubusercontent.com/${using:GHRepoName}/${using:GHCommit}/" - - $FilePath = $Link.Replace($CommitUrl, "") - $Status = 200 - $Cache = $using:RepoFiles - - $attempts = 0 - - while ($attempts -lt $using:MaxRetries) { - if ( !($Cache.ContainsKey($FilePath)) ) { - try { - $Uri = $Link -as [System.URI] - - if ($Link -match "submodules") { - # Skip submodule links until sourcelink properly handles submodules - $Status = 200 - } - elseif ($Uri.AbsoluteURI -ne $null -and ($Uri.Host -match 'github' -or $Uri.Host -match 'githubusercontent')) { - # Only GitHub links are valid - $Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode - } - else { - # If it's not a github link, we want to break out of the loop and not retry. - $Status = 0 - $attempts = $using:MaxRetries - } - } - catch { - Write-Host $_ - $Status = 0 - } - } - - if ($Status -ne 200) { - $attempts++ - - if ($attempts -lt $using:MaxRetries) - { - $attemptsLeft = $using:MaxRetries - $attempts - Write-Warning "Download failed, $attemptsLeft attempts remaining, will retry in $using:RetryWaitTimeInSeconds seconds" - Start-Sleep -Seconds $using:RetryWaitTimeInSeconds - } - else { - if ($NumFailedLinks -eq 0) { - if ($FailedFiles.Value -eq 0) { - Write-Host - } - - Write-Host "`tFile $RealPath has broken links:" - } - - Write-Host "`t`tFailed to retrieve $Link" - - $NumFailedLinks++ - } - } - else { - break - } - } - } - } - - if ($NumFailedLinks -ne 0) { - $FailedFiles.value++ - $global:LASTEXITCODE = 1 - } - } - } - - &$ValidateFile $TargetFile $FileName ([ref]$FailedFiles) - } - } - catch { - Write-Host $_ - } - finally { - $zip.Dispose() - } - - if ($FailedFiles -eq 0) { - Write-Host 'Passed.' - return [pscustomobject]@{ - result = 0 - packagePath = $PackagePath - } - } - else { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "$PackagePath has broken SourceLink links." - return [pscustomobject]@{ - result = 1 - packagePath = $PackagePath - } - } -} - -function CheckJobResult( - $result, - $packagePath, - [ref]$ValidationFailures, - [switch]$logErrors) { - if ($result -ne '0') { - if ($logErrors) { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "$packagePath has broken SourceLink links." - } - $ValidationFailures.Value++ - } -} - -function ValidateSourceLinkLinks { - if ($GHRepoName -ne '' -and !($GHRepoName -Match '^[^\s\/]+/[^\s\/]+$')) { - if (!($GHRepoName -Match '^[^\s-]+-[^\s]+$')) { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "GHRepoName should be in the format / or -. '$GHRepoName'" - ExitWithExitCode 1 - } - else { - $GHRepoName = $GHRepoName -replace '^([^\s-]+)-([^\s]+)$', '$1/$2'; - } - } - - if ($GHCommit -ne '' -and !($GHCommit -Match '^[0-9a-fA-F]{40}$')) { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "GHCommit should be a 40 chars hexadecimal string. '$GHCommit'" - ExitWithExitCode 1 - } - - if ($GHRepoName -ne '' -and $GHCommit -ne '') { - $RepoTreeURL = -Join('http://api.github.com/repos/', $GHRepoName, '/git/trees/', $GHCommit, '?recursive=1') - $CodeExtensions = @('.cs', '.vb', '.fs', '.fsi', '.fsx', '.fsscript') - - try { - # Retrieve the list of files in the repo at that particular commit point and store them in the RepoFiles hash - $Data = Invoke-WebRequest $RepoTreeURL -UseBasicParsing | ConvertFrom-Json | Select-Object -ExpandProperty tree - - foreach ($file in $Data) { - $Extension = [System.IO.Path]::GetExtension($file.path) - - if ($CodeExtensions.Contains($Extension)) { - $RepoFiles[$file.path] = 1 - } - } - } - catch { - Write-Host "Problems downloading the list of files from the repo. Url used: $RepoTreeURL . Execution will proceed without caching." - } - } - elseif ($GHRepoName -ne '' -or $GHCommit -ne '') { - Write-Host 'For using the http caching mechanism both GHRepoName and GHCommit should be informed.' - } - - if (Test-Path $ExtractPath) { - Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue - } - - $ValidationFailures = 0 - - # Process each NuGet package in parallel - Get-ChildItem "$InputPath\*.symbols.nupkg" | - ForEach-Object { - Write-Host "Starting $($_.FullName)" - Start-Job -ScriptBlock $ValidatePackage -ArgumentList $_.FullName | Out-Null - $NumJobs = @(Get-Job -State 'Running').Count - - while ($NumJobs -ge $MaxParallelJobs) { - Write-Host "There are $NumJobs validation jobs running right now. Waiting $SecondsBetweenLoadChecks seconds to check again." - sleep $SecondsBetweenLoadChecks - $NumJobs = @(Get-Job -State 'Running').Count - } - - foreach ($Job in @(Get-Job -State 'Completed')) { - $jobResult = Wait-Job -Id $Job.Id | Receive-Job - CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) -LogErrors - Remove-Job -Id $Job.Id - } - } - - foreach ($Job in @(Get-Job)) { - $jobResult = Wait-Job -Id $Job.Id | Receive-Job - CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) - Remove-Job -Id $Job.Id - } - if ($ValidationFailures -gt 0) { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "$ValidationFailures package(s) failed validation." - ExitWithExitCode 1 - } -} - -function InstallSourcelinkCli { - $sourcelinkCliPackageName = 'sourcelink' - - $dotnetRoot = InitializeDotNetCli -install:$true - $dotnet = "$dotnetRoot\dotnet.exe" - $toolList = & "$dotnet" tool list --global - - if (($toolList -like "*$sourcelinkCliPackageName*") -and ($toolList -like "*$sourcelinkCliVersion*")) { - Write-Host "SourceLink CLI version $sourcelinkCliVersion is already installed." - } - else { - Write-Host "Installing SourceLink CLI version $sourcelinkCliVersion..." - Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.' - & "$dotnet" tool install $sourcelinkCliPackageName --version $sourcelinkCliVersion --verbosity "minimal" --global - } -} - -try { - InstallSourcelinkCli - - foreach ($Job in @(Get-Job)) { - Remove-Job -Id $Job.Id - } - - ValidateSourceLinkLinks -} -catch { - Write-Host $_.Exception - Write-Host $_.ScriptStackTrace - Write-PipelineTelemetryError -Category 'SourceLink' -Message $_ - ExitWithExitCode 1 -} From c198583109d8a95e11b152250f5473896d9d7a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 31 Mar 2026 21:01:38 +0200 Subject: [PATCH 095/100] Set isProduction: false for logs in 1ES PT and remove SBOM/CG (#16649) Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/core-templates/job/job.yml | 4 -- .../job/publish-build-assets.yml | 4 +- .../steps/component-governance.yml | 16 ----- .../core-templates/steps/generate-sbom.yml | 60 ++++--------------- .../core-templates/steps/publish-logs.yml | 4 +- .../core-templates/steps/source-build.yml | 2 +- eng/common/generate-sbom-prep.ps1 | 29 --------- eng/common/generate-sbom-prep.sh | 39 ------------ eng/common/template-guidance.md | 2 - eng/common/templates-official/job/job.yml | 39 ++++-------- .../steps/component-governance.yml | 7 --- .../steps/publish-pipeline-artifacts.yml | 2 + eng/common/templates/job/job.yml | 38 +++++------- .../templates/steps/component-governance.yml | 7 --- 14 files changed, 46 insertions(+), 207 deletions(-) delete mode 100644 eng/common/core-templates/steps/component-governance.yml delete mode 100644 eng/common/generate-sbom-prep.ps1 delete mode 100755 eng/common/generate-sbom-prep.sh delete mode 100644 eng/common/templates-official/steps/component-governance.yml delete mode 100644 eng/common/templates/steps/component-governance.yml diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index 5ce51840619..c5787948afb 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -29,7 +29,6 @@ parameters: testRunTitle: '' testResultsFormat: '' name: '' - componentGovernanceSteps: [] preSteps: [] artifactPublishSteps: [] runAsPublic: false @@ -146,9 +145,6 @@ jobs: - ${{ each step in parameters.steps }}: - ${{ step }} - - ${{ each step in parameters.componentGovernanceSteps }}: - - ${{ step }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: /eng/common/core-templates/steps/cleanup-microbuild.yml parameters: diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 215ff7a78c5..64dc1a557cc 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -172,8 +172,7 @@ jobs: targetPath: '$(Build.ArtifactStagingDirectory)/MergedManifest.xml' artifactName: AssetManifests displayName: 'Publish Merged Manifest' - retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + retryCountOnTaskFailure: 10 # for any files being locked - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: @@ -182,6 +181,7 @@ jobs: displayName: Publish ReleaseConfigs Artifact targetPath: '$(Build.StagingDirectory)/ReleaseConfigs' artifactName: ReleaseConfigs + retryCountOnTaskFailure: 10 # for any files being locked - ${{ if or(eq(parameters.publishAssetsImmediately, 'true'), eq(parameters.isAssetlessBuild, 'true')) }}: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml diff --git a/eng/common/core-templates/steps/component-governance.yml b/eng/common/core-templates/steps/component-governance.yml deleted file mode 100644 index cf0649aa956..00000000000 --- a/eng/common/core-templates/steps/component-governance.yml +++ /dev/null @@ -1,16 +0,0 @@ -parameters: - disableComponentGovernance: false - componentGovernanceIgnoreDirectories: '' - is1ESPipeline: false - displayName: 'Component Detection' - -steps: -- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: - - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" - displayName: Set skipComponentGovernanceDetection variable -- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true - displayName: ${{ parameters.displayName }} - inputs: - ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml index c05f6502797..aad0a8aeda3 100644 --- a/eng/common/core-templates/steps/generate-sbom.yml +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -1,54 +1,14 @@ -# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. -# PackageName - The name of the package this SBOM represents. -# PackageVersion - The version of the package this SBOM represents. -# ManifestDirPath - The path of the directory where the generated manifest files will be placed -# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. - parameters: - PackageVersion: 10.0.0 - BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' - PackageName: '.NET' - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - IgnoreDirectories: '' - sbomContinueOnError: true - is1ESPipeline: false - # disable publishArtifacts if some other step is publishing the artifacts (like job.yml). - publishArtifacts: true + PackageVersion: unused + BuildDropPath: unused + PackageName: unused + ManifestDirPath: unused + IgnoreDirectories: unused + sbomContinueOnError: unused + is1ESPipeline: unused + publishArtifacts: unused steps: -- task: PowerShell@2 - displayName: Prep for SBOM generation in (Non-linux) - condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) - inputs: - filePath: ./eng/common/generate-sbom-prep.ps1 - arguments: ${{parameters.manifestDirPath}} - -# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 - script: | - chmod +x ./eng/common/generate-sbom-prep.sh - ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} - displayName: Prep for SBOM generation in (Linux) - condition: eq(variables['Agent.Os'], 'Linux') - continueOnError: ${{ parameters.sbomContinueOnError }} - -- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: 'Generate SBOM manifest' - continueOnError: ${{ parameters.sbomContinueOnError }} - inputs: - PackageName: ${{ parameters.packageName }} - BuildDropPath: ${{ parameters.buildDropPath }} - PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }}/$(ARTIFACT_NAME) - ${{ if ne(parameters.IgnoreDirectories, '') }}: - AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' - -- ${{ if eq(parameters.publishArtifacts, 'true')}}: - - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - args: - displayName: Publish SBOM manifest - continueOnError: ${{parameters.sbomContinueOnError}} - targetPath: '${{ parameters.manifestDirPath }}' - artifactName: $(ARTIFACT_NAME) - + echo "##vso[task.logissue type=warning]Including generate-sbom.yml is deprecated, SBOM generation is handled 1ES PT now. Remove this include." + displayName: Issue generate-sbom.yml deprecation warning diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 8f827c6c7fd..89090667d4f 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -59,4 +59,6 @@ steps: artifactName: PostBuildLogs_Attempt$(System.JobAttempt) continueOnError: true condition: always() - sbomEnabled: false # we don't need SBOM for logs + retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # logs are non-production artifacts + diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index b9c86c18ae4..09ae5cd73ae 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -62,4 +62,4 @@ steps: artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) continueOnError: true condition: succeededOrFailed() - sbomEnabled: false # we don't need SBOM for logs + isProduction: false # logs are non-production artifacts diff --git a/eng/common/generate-sbom-prep.ps1 b/eng/common/generate-sbom-prep.ps1 deleted file mode 100644 index a0c7d792a76..00000000000 --- a/eng/common/generate-sbom-prep.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -Param( - [Parameter(Mandatory=$true)][string] $ManifestDirPath # Manifest directory where sbom will be placed -) - -. $PSScriptRoot\pipeline-logging-functions.ps1 - -# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly -# with their own overwriting ours. So we create it as a sub directory of the requested manifest path. -$ArtifactName = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" -$SafeArtifactName = $ArtifactName -replace '["/:<>\\|?@*"() ]', '_' -$SbomGenerationDir = Join-Path $ManifestDirPath $SafeArtifactName - -Write-Host "Artifact name before : $ArtifactName" -Write-Host "Artifact name after : $SafeArtifactName" - -Write-Host "Creating dir $ManifestDirPath" - -# create directory for sbom manifest to be placed -if (!(Test-Path -path $SbomGenerationDir)) -{ - New-Item -ItemType Directory -path $SbomGenerationDir - Write-Host "Successfully created directory $SbomGenerationDir" -} -else{ - Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." -} - -Write-Host "Updating artifact name" -Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$SafeArtifactName" diff --git a/eng/common/generate-sbom-prep.sh b/eng/common/generate-sbom-prep.sh deleted file mode 100755 index b8ecca72bbf..00000000000 --- a/eng/common/generate-sbom-prep.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -source="${BASH_SOURCE[0]}" - -# resolve $SOURCE until the file is no longer a symlink -while [[ -h $source ]]; do - scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" - source="$(readlink "$source")" - - # if $source was a relative symlink, we need to resolve it relative to the path where the - # symlink file was located - [[ $source != /* ]] && source="$scriptroot/$source" -done -scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" -. $scriptroot/pipeline-logging-functions.sh - - -# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts. -artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM" -safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}" -manifest_dir=$1 - -# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly -# with their own overwriting ours. So we create it as a sub directory of the requested manifest path. -sbom_generation_dir="$manifest_dir/$safe_artifact_name" - -if [ ! -d "$sbom_generation_dir" ] ; then - mkdir -p "$sbom_generation_dir" - echo "Sbom directory created." $sbom_generation_dir -else - Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." -fi - -echo "Artifact name before : "$artifact_name -echo "Artifact name after : "$safe_artifact_name -export ARTIFACT_NAME=$safe_artifact_name -echo "##vso[task.setvariable variable=ARTIFACT_NAME]$safe_artifact_name" - -exit 0 diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md index 4bf4cf41bd7..e2b07a865f1 100644 --- a/eng/common/template-guidance.md +++ b/eng/common/template-guidance.md @@ -82,7 +82,6 @@ eng\common\ publish-build-artifacts.yml (logic) publish-pipeline-artifacts.yml (logic) component-governance.yml (shim) - generate-sbom.yml (shim) publish-logs.yml (shim) retain-build.yml (shim) send-to-helix.yml (shim) @@ -107,7 +106,6 @@ eng\common\ setup-maestro-vars.yml (logic) steps\ component-governance.yml (logic) - generate-sbom.yml (logic) publish-build-artifacts.yml (redirect) publish-logs.yml (logic) publish-pipeline-artifacts.yml (redirect) diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index bdad742a19e..a9d2839fdbf 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -1,24 +1,15 @@ parameters: -# Sbom related params - enableSbom: true runAsPublic: false - PackageVersion: 9.0.0 - BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' +# Sbom related params, unused now and can eventually be removed + enableSbom: unused + PackageVersion: unused + BuildDropPath: unused jobs: - template: /eng/common/core-templates/job/job.yml parameters: is1ESPipeline: true - componentGovernanceSteps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - template: /eng/common/templates/steps/generate-sbom.yml - parameters: - PackageVersion: ${{ parameters.packageVersion }} - BuildDropPath: ${{ parameters.buildDropPath }} - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - publishArtifacts: false - # publish artifacts # for 1ES managed templates, use the templateContext.output to handle multiple outputs. templateContext: @@ -31,14 +22,14 @@ jobs: targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} condition: succeeded() - retryCountOnTaskFailure: 10 # for any logs being locked + retryCountOnTaskFailure: 10 # for any files being locked continueOnError: true - output: pipelineArtifact displayName: Publish pipeline artifacts targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}_Attempt$(System.JobAttempt) condition: not(succeeded()) - retryCountOnTaskFailure: 10 # for any logs being locked + retryCountOnTaskFailure: 10 # for any files being locked continueOnError: true - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - output: pipelineArtifact @@ -47,8 +38,8 @@ jobs: displayName: 'Publish logs' continueOnError: true condition: always() - retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # logs are non-production artifacts - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: - output: pipelineArtifact @@ -57,7 +48,8 @@ jobs: artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }} continueOnError: true condition: always() - sbomEnabled: false # we don't need SBOM for logs + retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # logs are non-production artifacts - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - output: pipelineArtifact @@ -65,14 +57,8 @@ jobs: artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true - sbomEnabled: false # we don't need SBOM for BuildConfiguration - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - output: pipelineArtifact - displayName: Publish SBOM manifest - continueOnError: true - targetPath: $(Build.ArtifactStagingDirectory)/sbom - artifactName: $(ARTIFACT_NAME) + retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # BuildConfiguration is a non-production artifact # V4 publishing: automatically publish staged artifacts as a pipeline artifact. # The artifact name matches the SDK's FutureArtifactName ($(System.PhaseName)_Artifacts), @@ -83,6 +69,7 @@ jobs: targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' artifactName: '$(System.PhaseName)_Artifacts' continueOnError: true + retryCountOnTaskFailure: 10 # for any files being locked # add any outputs provided via root yaml - ${{ if ne(parameters.templateContext.outputs, '') }}: diff --git a/eng/common/templates-official/steps/component-governance.yml b/eng/common/templates-official/steps/component-governance.yml deleted file mode 100644 index 30bb3985ca2..00000000000 --- a/eng/common/templates-official/steps/component-governance.yml +++ /dev/null @@ -1,7 +0,0 @@ -steps: -- template: /eng/common/core-templates/steps/component-governance.yml - parameters: - is1ESPipeline: true - - ${{ each parameter in parameters }}: - ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml index 172f9f0fdc9..6a652ae1cfe 100644 --- a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -26,3 +26,5 @@ steps: properties: ${{ parameters.args.properties }} ${{ if parameters.args.sbomEnabled }}: sbomEnabled: ${{ parameters.args.sbomEnabled }} + ${{ if parameters.args.isProduction }}: + isProduction: ${{ parameters.args.isProduction }} diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index c096f906375..5e261f34db4 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -1,12 +1,12 @@ parameters: enablePublishBuildArtifacts: false - disableComponentGovernance: '' - componentGovernanceIgnoreDirectories: '' -# Sbom related params - enableSbom: true runAsPublic: false - PackageVersion: 9.0.0 - BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' +# CG related params, unused now and can eventually be removed + disableComponentGovernance: unused +# Sbom related params, unused now and can eventually be removed + enableSbom: unused + PackageVersion: unused + BuildDropPath: unused jobs: - template: /eng/common/core-templates/job/job.yml @@ -21,17 +21,10 @@ jobs: - ${{ each step in parameters.steps }}: - ${{ step }} - componentGovernanceSteps: - - template: /eng/common/templates/steps/component-governance.yml - parameters: - ${{ if eq(parameters.disableComponentGovernance, '') }}: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: - disableComponentGovernance: false - ${{ else }}: - disableComponentGovernance: true - ${{ else }}: - disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + # we don't run CG in public + - ${{ if eq(variables['System.TeamProject'], 'public') }}: + - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" + displayName: Set skipComponentGovernanceDetection variable artifactPublishSteps: - ${{ if ne(parameters.artifacts.publish, '') }}: @@ -45,7 +38,7 @@ jobs: artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} continueOnError: true condition: succeeded() - retryCountOnTaskFailure: 10 # for any logs being locked + retryCountOnTaskFailure: 10 # for any files being locked - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: is1ESPipeline: false @@ -55,7 +48,7 @@ jobs: artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}_Attempt$(System.JobAttempt) continueOnError: true condition: not(succeeded()) - retryCountOnTaskFailure: 10 # for any logs being locked + retryCountOnTaskFailure: 10 # for any files being locked - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: @@ -66,8 +59,7 @@ jobs: displayName: 'Publish logs' continueOnError: true condition: always() - retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + retryCountOnTaskFailure: 10 # for any files being locked - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml @@ -79,7 +71,7 @@ jobs: artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }} continueOnError: true condition: always() - sbomEnabled: false + retryCountOnTaskFailure: 10 # for any files being locked - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml @@ -90,4 +82,4 @@ jobs: artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true - sbomEnabled: false # we don't need SBOM for BuildConfiguration + retryCountOnTaskFailure: 10 # for any files being locked diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml deleted file mode 100644 index c12a5f8d21d..00000000000 --- a/eng/common/templates/steps/component-governance.yml +++ /dev/null @@ -1,7 +0,0 @@ -steps: -- template: /eng/common/core-templates/steps/component-governance.yml - parameters: - is1ESPipeline: false - - ${{ each parameter in parameters }}: - ${{ parameter.key }}: ${{ parameter.value }} From a5f0eb91954f7f59db001a92a90e7549097141ba Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Tue, 31 Mar 2026 13:15:01 -0700 Subject: [PATCH 096/100] Fix PostBuildLogs artifact name collision across jobs (#16651) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/core-templates/job/publish-build-assets.yml | 3 ++- eng/common/core-templates/steps/publish-logs.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 64dc1a557cc..498d6d9539f 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -217,4 +217,5 @@ jobs: - template: /eng/common/core-templates/steps/publish-logs.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - JobLabel: 'Publish_Artifacts_Logs' + StageLabel: 'BuildAssetRegistry' + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 89090667d4f..84a1922c73f 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -56,7 +56,7 @@ steps: args: displayName: Publish Logs targetPath: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' - artifactName: PostBuildLogs_Attempt$(System.JobAttempt) + artifactName: PostBuildLogs_${{ parameters.StageLabel }}_${{ parameters.JobLabel }}_Attempt$(System.JobAttempt) continueOnError: true condition: always() retryCountOnTaskFailure: 10 # for any files being locked From 54b2ce2daa9f50a54842da5de80a19a26e0f529b Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Thu, 2 Apr 2026 09:25:19 -0700 Subject: [PATCH 097/100] Add enablePublishing parameter to opt out of V4 artifact publishing (#16664) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/core-templates/job/job.yml | 1 + eng/common/templates-official/job/job.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index c5787948afb..eaed6d87e65 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -24,6 +24,7 @@ parameters: enablePublishBuildArtifacts: false enablePublishBuildAssets: false enablePublishTestResults: false + enablePublishing: false enableBuildRetry: false mergeTestResults: false testRunTitle: '' diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index a9d2839fdbf..d68e9fbc265 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -63,7 +63,8 @@ jobs: # V4 publishing: automatically publish staged artifacts as a pipeline artifact. # The artifact name matches the SDK's FutureArtifactName ($(System.PhaseName)_Artifacts), # which is encoded in the asset manifest for downstream publishing to discover. - - ${{ if eq(parameters.publishingVersion, 4) }}: + # Jobs can opt in by setting enablePublishing: true. + - ${{ if and(eq(parameters.publishingVersion, 4), eq(parameters.enablePublishing, 'true')) }}: - output: pipelineArtifact displayName: 'Publish V4 pipeline artifacts' targetPath: '$(Build.ArtifactStagingDirectory)/artifacts' From 57e4e77230aeed59cca8144d5a6263ceff8296aa Mon Sep 17 00:00:00 2001 From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> Date: Thu, 2 Apr 2026 20:57:19 -0700 Subject: [PATCH 098/100] Unblock publish (#16676) Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/promote-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/promote-build.yml b/eng/promote-build.yml index d1faf9ac4b3..447a2d3eeaf 100644 --- a/eng/promote-build.yml +++ b/eng/promote-build.yml @@ -69,7 +69,7 @@ extends: os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - image: windows.vs2026.amd64 + image: windows.vs2022.amd64 os: windows sdl: policheck: @@ -83,4 +83,4 @@ extends: PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} BARBuildId: ${{ parameters.BARBuildId }} symbolPublishingAdditionalParameters: ${{ parameters.SymbolPublishingAdditionalParameters }} - artifactsPublishingAdditionalParameters: ${{ parameters.ArtifactsPublishingAdditionalParameters }} \ No newline at end of file + artifactsPublishingAdditionalParameters: ${{ parameters.ArtifactsPublishingAdditionalParameters }} From 1ad138412feb0429a24aece92c4238616b4bb6f8 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Fri, 3 Apr 2026 06:54:33 -0700 Subject: [PATCH 099/100] Enable V4 publishing for Windows_NT official job (#16674) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/build.yml b/eng/build.yml index f5a4c4bcc33..496acdaa77d 100644 --- a/eng/build.yml +++ b/eng/build.yml @@ -32,6 +32,7 @@ stages: clean: all jobs: - job: Windows_NT + enablePublishing: true timeoutInMinutes: 90 strategy: matrix: From c4cbe74c23e2196d9fde7b6dd63fbad6f688b80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 3 Apr 2026 18:08:16 +0200 Subject: [PATCH 100/100] Mark publishing artifacts as non-production and fix boolean property pass-through in 1ES publish template (#16672) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: akoeplinger <1376924+akoeplinger@users.noreply.github.com> Co-authored-by: mmitche <8725170+mmitche@users.noreply.github.com> --- eng/common/core-templates/job/publish-build-assets.yml | 2 ++ .../templates-official/steps/publish-pipeline-artifacts.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 498d6d9539f..06f2eed0323 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -173,6 +173,7 @@ jobs: artifactName: AssetManifests displayName: 'Publish Merged Manifest' retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # just metadata for publishing - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml parameters: @@ -182,6 +183,7 @@ jobs: targetPath: '$(Build.StagingDirectory)/ReleaseConfigs' artifactName: ReleaseConfigs retryCountOnTaskFailure: 10 # for any files being locked + isProduction: false # just metadata for publishing - ${{ if or(eq(parameters.publishAssetsImmediately, 'true'), eq(parameters.isAssetlessBuild, 'true')) }}: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml index 6a652ae1cfe..9e5981365e5 100644 --- a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -24,7 +24,7 @@ steps: artifactName: ${{ parameters.args.artifactName }} ${{ if parameters.args.properties }}: properties: ${{ parameters.args.properties }} - ${{ if parameters.args.sbomEnabled }}: + ${{ if ne(parameters.args.sbomEnabled, '') }}: sbomEnabled: ${{ parameters.args.sbomEnabled }} - ${{ if parameters.args.isProduction }}: + ${{ if ne(parameters.args.isProduction, '') }}: isProduction: ${{ parameters.args.isProduction }}