diff --git a/.azuredevops/dependabot.yml b/.azuredevops/dependabot.yml new file mode 100644 index 000000000000..f18e60565a42 --- /dev/null +++ b/.azuredevops/dependabot.yml @@ -0,0 +1,5 @@ +version: 2 + +# Disabling dependabot on Azure DevOps as this is a mirrored repo. Updates should go through github. +enable-campaigned-updates: false +enable-security-updates: false diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml index ff5dcdf1e37b..528461221ba3 100644 --- a/.config/1espt/PipelineAutobaseliningConfig.yml +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -1,23 +1,146 @@ -## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. - -pipelines: - 1330: - retail: - source: - credscan: - lastModifiedDate: 2024-03-25 - eslint: - lastModifiedDate: 2024-03-25 - psscriptanalyzer: - lastModifiedDate: 2024-03-25 - armory: - lastModifiedDate: 2024-03-25 - policheck: - lastModifiedDate: 2025-03-27 - binary: - credscan: - lastModifiedDate: 2024-03-25 - binskim: - lastModifiedDate: 2025-01-23 - spotbugs: - lastModifiedDate: 2024-03-25 +## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. + +pipelines: + 1330: + retail: + source: + credscan: + lastModifiedDate: 2024-03-25 + eslint: + lastModifiedDate: 2024-03-25 + psscriptanalyzer: + lastModifiedDate: 2024-03-25 + armory: + lastModifiedDate: 2024-03-25 + policheck: + lastModifiedDate: 2025-03-27 + binary: + credscan: + lastModifiedDate: 2024-03-25 + binskim: + lastModifiedDate: 2025-10-03 + spotbugs: + lastModifiedDate: 2024-03-25 + usedBinskimScanAllExtensions: true + 1299: + retail: + source: + credscan: + lastModifiedDate: 2024-04-02 + eslint: + lastModifiedDate: 2024-04-02 + psscriptanalyzer: + lastModifiedDate: 2024-04-02 + armory: + lastModifiedDate: 2024-04-02 + policheck: + lastModifiedDate: 2025-03-26 + binary: + credscan: + lastModifiedDate: 2024-12-20 + binskim: + lastModifiedDate: 2025-01-09 + 1219: + retail: + source: + credscan: + lastModifiedDate: 2024-04-02 + eslint: + lastModifiedDate: 2024-04-02 + psscriptanalyzer: + lastModifiedDate: 2024-04-02 + armory: + lastModifiedDate: 2024-04-02 + policheck: + lastModifiedDate: 2025-03-27 + binary: + credscan: + lastModifiedDate: 2024-12-23 + binskim: + lastModifiedDate: 2025-01-09 + 1231: + retail: + source: + credscan: + lastModifiedDate: 2024-10-17 + eslint: + lastModifiedDate: 2024-10-17 + psscriptanalyzer: + lastModifiedDate: 2024-10-17 + armory: + lastModifiedDate: 2024-10-17 + policheck: + lastModifiedDate: 2025-03-26 + binary: + credscan: + lastModifiedDate: 2024-10-17 + binskim: + lastModifiedDate: 2025-01-10 + spotbugs: + lastModifiedDate: 2024-10-17 + 1301: + retail: + source: + credscan: + lastModifiedDate: 2024-10-24 + eslint: + lastModifiedDate: 2024-10-24 + psscriptanalyzer: + lastModifiedDate: 2024-10-24 + armory: + lastModifiedDate: 2024-10-24 + policheck: + lastModifiedDate: 2025-04-01 + binary: + credscan: + lastModifiedDate: 2024-10-24 + binskim: + lastModifiedDate: 2025-01-13 + spotbugs: + lastModifiedDate: 2024-10-24 + 1447: + retail: + source: + credscan: + lastModifiedDate: 2025-05-09 + policheck: + lastModifiedDate: 2025-05-09 + eslint: + lastModifiedDate: 2025-05-09 + psscriptanalyzer: + lastModifiedDate: 2025-05-09 + armory: + lastModifiedDate: 2025-05-09 + binary: + credscan: + lastModifiedDate: 2025-05-09 + binskim: + lastModifiedDate: 2025-05-09 + spotbugs: + lastModifiedDate: 2025-05-09 + 1484: + retail: + source: + eslint: + lastModifiedDate: 2025-08-06 + armory: + lastModifiedDate: 2025-08-06 + binary: + binskim: + lastModifiedDate: 2025-08-06 + 1525: + retail: + source: + credscan: + lastModifiedDate: 2025-09-19 + eslint: + lastModifiedDate: 2025-09-19 + psscriptanalyzer: + lastModifiedDate: 2025-09-19 + armory: + lastModifiedDate: 2025-09-19 + binary: + credscan: + lastModifiedDate: 2025-09-19 + binskim: + lastModifiedDate: 2025-09-19 diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json index ccd35fe83c71..79d6b4056224 100644 --- a/.config/CredScanSuppressions.json +++ b/.config/CredScanSuppressions.json @@ -265,14 +265,7 @@ "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", - "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs", - "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.key" - ] - }, - { - "_justification": "Suppression approved. Private key for testing purpose.", - "file": [ - "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx" + "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs" ] }, { @@ -289,6 +282,25 @@ "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx", "/src/source-build-reference-packages/src/externalPackages/src/xunit/appveyor.yml" ] + }, + { + "_justification": "Files contain private keys used by test code.", + "file": [ + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA1.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA256.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256_2.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA384.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA512.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp_TemporaryKey.pfx" + ] + }, + { + "_justification": "File uses a test-only credential.", + "file": [ + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs" + ] } ] } \ No newline at end of file diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ab9278bf1531..2b07d7d4face 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "microsoft.dotnet.darc": { - "version": "1.1.0-beta.25412.1", + "version": "1.1.0-beta.25619.4", "commands": [ "darc" ] diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines new file mode 100644 index 000000000000..78f0f5f72ca5 --- /dev/null +++ b/.config/guardian/.gdnbaselines @@ -0,0 +1,2602 @@ +{ + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" + }, + "version": "1.0.0", + "baselines": { + "default": { + "name": "default", + "createdDate": "2024-03-25 18:56:33Z", + "lastUpdatedDate": "2024-03-25 18:56:33Z" + } + }, + "results": { + "5f3b52e23f96eb01bcfd73ead3cbaa2e1430de0006e5103109dd39bf9f292165": { + "signature": "5f3b52e23f96eb01bcfd73ead3cbaa2e1430de0006e5103109dd39bf9f292165", + "alternativeSignatures": [], + "target": "src/nuget-client/docs/cross-platform-debugging.md", + "line": 66, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "72b28f655eadc78b21ab36a7f572708315d8d909d1b460162511e37086288e30": { + "signature": "72b28f655eadc78b21ab36a7f572708315d8d909d1b460162511e37086288e30", + "alternativeSignatures": [ + "60efb04c6e0431e477e792a96d32b30b3a309b4ee19fad084a015e2946985459" + ], + "target": "src/roslyn/eng/build.ps1", + "line": 339, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "e0aafe4de4d762a800edeca36ed2144ed173a138d126423863b1710425b90b33": { + "signature": "e0aafe4de4d762a800edeca36ed2144ed173a138d126423863b1710425b90b33", + "alternativeSignatures": [], + "target": "src/aspire/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs", + "line": 20, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0030", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "a5142e7bafbf664fdcb2d4d7071ca8427c7da0c8ba66cc7706c9c07b816f1201": { + "signature": "a5142e7bafbf664fdcb2d4d7071ca8427c7da0c8ba66cc7706c9c07b816f1201", + "alternativeSignatures": [], + "target": "src/aspire/tests/Aspire.Npgsql.Tests/ConformanceTests.cs", + "line": 17, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0030", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "b6aecc1d8697beab291f9925633b5ec3e37a088033efc7e93928fd9cac96cda4": { + "signature": "b6aecc1d8697beab291f9925633b5ec3e37a088033efc7e93928fd9cac96cda4", + "alternativeSignatures": [ + "985838b2d1518f507c85ae0f635951bad92dde58eb24c252d7e56fb6ccda6191" + ], + "target": "src/aspnetcore/src/DataProtection/CreateTestCert.ps1", + "line": 11, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "5c349e8f183364d99cde545c6da7549c9d6227957c820fcde8e8beb2b40de39c": { + "signature": "5c349e8f183364d99cde545c6da7549c9d6227957c820fcde8e8beb2b40de39c", + "alternativeSignatures": [ + "8546393d391f4010c04ed43788c36626f870b02028937cf390014c660f657f7b" + ], + "target": "src/command-line-api/eng/common/SetupNugetSources.ps1", + "line": 38, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-01 22:10:09Z", + "expirationDate": "2024-09-19 00:45:33Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 00:45:33Z" + }, + "a9e7b46f71cc21fd96e3bbb1c30a7beb36470f0a4c857794b4444856e54ffc2b": { + "signature": "a9e7b46f71cc21fd96e3bbb1c30a7beb36470f0a4c857794b4444856e54ffc2b", + "alternativeSignatures": [ + "34597b8dc5d2e482d7178a50440f3b8815c44e510906dd92a4d31d434c87053b" + ], + "target": "src/command-line-api/eng/common/SetupNugetSources.ps1", + "line": 56, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-01 22:10:09Z", + "expirationDate": "2024-09-19 00:45:33Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 00:45:33Z" + }, + "3df69ea15defeb820ba0823dc80513e75a79b049dee023b51dee4419cd1d2276": { + "signature": "3df69ea15defeb820ba0823dc80513e75a79b049dee023b51dee4419cd1d2276", + "alternativeSignatures": [ + "deb5cfe250ae8f9c1bbcdf230c425dc071067ee26cc7b3d41b9fc078782febfc" + ], + "target": "src/command-line-api/eng/common/SetupNugetSources.ps1", + "line": 88, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-01 22:10:09Z", + "expirationDate": "2024-09-19 00:45:33Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 00:45:33Z" + }, + "32eb952e8561b331092ec83b626102388202071d192f9eb22c233b06ea90d2a1": { + "signature": "32eb952e8561b331092ec83b626102388202071d192f9eb22c233b06ea90d2a1", + "alternativeSignatures": [ + "cce04b0a7c54b775c1464a32d85804de7d1777ac2a6d21da8b080c29ac46162c" + ], + "target": "src/diagnostics/eng/common/SetupNugetSources.ps1", + "line": 38, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "7d3c27b6bd42f58e624890a1669c188c6afee080e6330673942d364641876d52": { + "signature": "7d3c27b6bd42f58e624890a1669c188c6afee080e6330673942d364641876d52", + "alternativeSignatures": [ + "29d9fec9e2b10d721512bb68a68759baa33b6bbc0683a2d3d5cdcb74894917ee" + ], + "target": "src/diagnostics/eng/common/SetupNugetSources.ps1", + "line": 56, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "42c9ee2033a50a53e2fcc83cb08b3a8c38a5c9e2230414ae0590d3c8f45a9a8d": { + "signature": "42c9ee2033a50a53e2fcc83cb08b3a8c38a5c9e2230414ae0590d3c8f45a9a8d", + "alternativeSignatures": [ + "44e2cd3b2773d63b155f2093202415744b74f8b5c47612ad92f2ffee3939cb2f" + ], + "target": "src/diagnostics/eng/common/SetupNugetSources.ps1", + "line": 88, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "11d1ee60346a9d183bdfead22ccfd12efe5b121e24d50ea479e6ed51c5b284f8": { + "signature": "11d1ee60346a9d183bdfead22ccfd12efe5b121e24d50ea479e6ed51c5b284f8", + "alternativeSignatures": [ + "f1095a63813963bb218b2e2a11bcfdf33c712ac8e182a37903ac05aa27618431" + ], + "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1", + "line": 38, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "df1e4e250d2fec7e1df1b811d11ee5fde3f74ad34a4cfbb3c347fa0fa1f5f021": { + "signature": "df1e4e250d2fec7e1df1b811d11ee5fde3f74ad34a4cfbb3c347fa0fa1f5f021", + "alternativeSignatures": [ + "5721e98504beccf9da5a70c8ea673f82adfa53a36883886e1c81097e7d2dc3a8" + ], + "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1", + "line": 56, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "697b46955be8c055972365abf2b21287b19595c0204245465e3d05869bdf3b2b": { + "signature": "697b46955be8c055972365abf2b21287b19595c0204245465e3d05869bdf3b2b", + "alternativeSignatures": [ + "fcef36ad5176c97ff601e5636a327fb6a2d7e89b0767b2a3c515f6f10b21ed4a" + ], + "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1", + "line": 88, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "148a534bb099170811f8dcc0d51c1caa399488739a5ee98fb12bee51c7a9244d": { + "signature": "148a534bb099170811f8dcc0d51c1caa399488739a5ee98fb12bee51c7a9244d", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/xunit/appveyor.yml", + "line": 7, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0130", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "2960b8fc6b1f6665b5988544f1d44a05dfe83b9b39a14efef5e042d7a78e4e19": { + "signature": "2960b8fc6b1f6665b5988544f1d44a05dfe83b9b39a14efef5e042d7a78e4e19", + "alternativeSignatures": [ + "b4177488d7a45f4a54472adf8bb97026f0799e61e10f580ea52fbbd74cf08f10" + ], + "target": "src/symreader/eng/common/SetupNugetSources.ps1", + "line": 38, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-03-25 18:56:33Z", + "expirationDate": "2024-09-11 20:34:12Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z" + }, + "c1c411bf7b80d684d2c444ed611f333f08f1073fbaaf4c6bd0238c16ffccbe4d": { + "signature": "c1c411bf7b80d684d2c444ed611f333f08f1073fbaaf4c6bd0238c16ffccbe4d", + "alternativeSignatures": [ + "8ca9e6612eb3802d5c1fd93ce0f1de61c2559512966fc97dcbeb017d1942c0fe" + ], + "target": "src/symreader/eng/common/SetupNugetSources.ps1", + "line": 56, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-03-25 18:56:33Z", + "expirationDate": "2024-09-11 20:34:12Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z" + }, + "a0437af80b26a79fc6c7e101114a0a455bd0bc7a4e9ccea1fa3b355aaac07390": { + "signature": "a0437af80b26a79fc6c7e101114a0a455bd0bc7a4e9ccea1fa3b355aaac07390", + "alternativeSignatures": [ + "495012003aa9faede4c4ad115a12784f6a8f549e1ebe976537b021d6e5296da9" + ], + "target": "src/symreader/eng/common/SetupNugetSources.ps1", + "line": 88, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingUsernameAndPasswordParams", + "createdDate": "2024-03-25 18:56:33Z", + "expirationDate": "2024-09-11 20:34:12Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z" + }, + "d43d77b6bd95a33891a3f76e3b20bf3001f300e0fe05b477faa4b74a35735740": { + "signature": "d43d77b6bd95a33891a3f76e3b20bf3001f300e0fe05b477faa4b74a35735740", + "alternativeSignatures": [], + "target": "src/aspire/tests/Shared/TestCertificates/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "3c494c3cdc3d2ab897a96f5b498fbf1731ba2c6dcc73e49399083635bc084e8a": { + "signature": "3c494c3cdc3d2ab897a96f5b498fbf1731ba2c6dcc73e49399083635bc084e8a", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/aspnetdevcert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8bb20ad2a210dc906e39ceb48b0a6a39b240878688ad6854161240ae3a597c87": { + "signature": "8bb20ad2a210dc906e39ceb48b0a6a39b240878688ad6854161240ae3a597c87", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/eku.client.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "486f45a6c6f00cc927a87f4b7b122829bee893d6f523fed279e7e2deda450aff": { + "signature": "486f45a6c6f00cc927a87f4b7b122829bee893d6f523fed279e7e2deda450aff", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/eku.code_signing.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "643ded93447723ad1faad1de45b19108d986db08e488174a5422f3f1f7f0f7a3": { + "signature": "643ded93447723ad1faad1de45b19108d986db08e488174a5422f3f1f7f0f7a3", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/eku.multiple_usages.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "51d877cdee6cab498e1a0fd4c4dd2702b3e5da9eb308e0a631d2104e2d3d2a8c": { + "signature": "51d877cdee6cab498e1a0fd4c4dd2702b3e5da9eb308e0a631d2104e2d3d2a8c", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/eku.server.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "849b1bea0ba2f8d1c24a58896b9d230ca317e8cff7e9540f73d578dd1aba12cb": { + "signature": "849b1bea0ba2f8d1c24a58896b9d230ca317e8cff7e9540f73d578dd1aba12cb", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-aspnet.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "c535332f090c89ae22a79aac4b9d344333c5479b891b79d72d252611c9364450": { + "signature": "c535332f090c89ae22a79aac4b9d344333c5479b891b79d72d252611c9364450", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-dsa-protected.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "bc8ce9f1fd92dd123db1b6f8c33d15123bc8f3c4cb4fd42c54b4a2c5210bb158": { + "signature": "bc8ce9f1fd92dd123db1b6f8c33d15123bc8f3c4cb4fd42c54b4a2c5210bb158", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-dsa.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "9507f913c4445bebb9bdd471960011afa198c1e42d19c45d44aea58af61a137d": { + "signature": "9507f913c4445bebb9bdd471960011afa198c1e42d19c45d44aea58af61a137d", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa-protected.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "9ac9e0a5efc7c0d1e6d89422ca8e70e913eaba647ef42650f0bbc50da080a556": { + "signature": "9ac9e0a5efc7c0d1e6d89422ca8e70e913eaba647ef42650f0bbc50da080a556", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "5d49433b67f4a2f1b6b323c498722d16aeda2d8781dafe9fcb9faaf9db4ee3e1": { + "signature": "5d49433b67f4a2f1b6b323c498722d16aeda2d8781dafe9fcb9faaf9db4ee3e1", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key", + "line": 4, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "71ee57f56c77339e4a0cb3f5bdfd05d15191136fc8ad887cef26fe1488522529": { + "signature": "71ee57f56c77339e4a0cb3f5bdfd05d15191136fc8ad887cef26fe1488522529", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-rsa-protected.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "74b8ab85959da39f4da6710c6375080c44c8929d6b68f59a06dede355aeffacc": { + "signature": "74b8ab85959da39f4da6710c6375080c44c8929d6b68f59a06dede355aeffacc", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/https-rsa.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "ca37e6fb6eb26bdec92893b3f6b08f8f41e6241f573a1b8e14d4e4d4ff1d2c7a": { + "signature": "ca37e6fb6eb26bdec92893b3f6b08f8f41e6241f573a1b8e14d4e4d4ff1d2c7a", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/intermediate2_ca.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "d7657bb2e0603e7b353f7b1e1d884306fe44f116c7665192fd6b33003333ef7a": { + "signature": "d7657bb2e0603e7b353f7b1e1d884306fe44f116c7665192fd6b33003333ef7a", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/intermediate_ca.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "90b7e7a132c8df4864a9ee49670cbaec9cbc5f5c53bf009eba5a583e0934a24f": { + "signature": "90b7e7a132c8df4864a9ee49670cbaec9cbc5f5c53bf009eba5a583e0934a24f", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/leaf.com.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8a5003e2db05146eaba3a7fc7aba715f51a5506b741a6ac2662e47e39c6165aa": { + "signature": "8a5003e2db05146eaba3a7fc7aba715f51a5506b741a6ac2662e47e39c6165aa", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/no_extensions.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "02590eb0efbb241f09bae58f60907c6dee5b33507519a4be87e168a458c2b9cb": { + "signature": "02590eb0efbb241f09bae58f60907c6dee5b33507519a4be87e168a458c2b9cb", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/root_ca.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "df250772236d85876a9d789cca90b48e5eb79ad6cb13782465c8c88366c5d845": { + "signature": "df250772236d85876a9d789cca90b48e5eb79ad6cb13782465c8c88366c5d845", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Shared/TestCertificates/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "0d9b21b17336fcaa4d1e6360b228eef8db6a2afe199d9db14a245eba8e59f9f7": { + "signature": "0d9b21b17336fcaa4d1e6360b228eef8db6a2afe199d9db14a245eba8e59f9f7", + "alternativeSignatures": [], + "target": "src/msbuild/src/Tasks.UnitTests/TestResources/mycert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "3cd8e3eb9b94b01c93591c685406ea91d9d31b16aace0f109734e4bacb3838f2": { + "signature": "3cd8e3eb9b94b01c93591c685406ea91d9d31b16aace0f109734e4bacb3838f2", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "e91e45a96bf36327f551eadf27d9598b3d058fc051b0f9f0f1da9420410dc79a": { + "signature": "e91e45a96bf36327f551eadf27d9598b3d058fc051b0f9f0f1da9420410dc79a", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Servers/IIS/tools/TestCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "1c8a3d52bb83c1fbd1208b94663769b6452e73988540113dff20bfb4df4ca010": { + "signature": "1c8a3d52bb83c1fbd1208b94663769b6452e73988540113dff20bfb4df4ca010", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/SignalR/common/Shared/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8839c1c5ab6e962faf123ad7b79a584170d6491855f69555664986a425984a36": { + "signature": "8839c1c5ab6e962faf123ad7b79a584170d6491855f69555664986a425984a36", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/SignalR/common/Shared/testCertECC.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "d2df996be35f179b45a3bdc28fcd9d5254a924ab52d2ca14b068bfea35e65284": { + "signature": "d2df996be35f179b45a3bdc28fcd9d5254a924ab52d2ca14b068bfea35e65284", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA1.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "7a20b01096651f581c51904be7cd1281150c40efc61352b70aabbca3c40ea177": { + "signature": "7a20b01096651f581c51904be7cd1281150c40efc61352b70aabbca3c40ea177", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA256.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "ef394eaa05e6eb5af1f5e523fd01f4e970f36cb2c7eacb3d363b47c9f70b0fec": { + "signature": "ef394eaa05e6eb5af1f5e523fd01f4e970f36cb2c7eacb3d363b47c9f70b0fec", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "cb83a69b59f2de3fa1d049750ddeb855030d5662c43a1c5fea6b95f01e21547f": { + "signature": "cb83a69b59f2de3fa1d049750ddeb855030d5662c43a1c5fea6b95f01e21547f", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256_2.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "d08376cdfec80b5c0884a8b85f18c8b34ffee19f1395d12aef1ffc2821120f03": { + "signature": "d08376cdfec80b5c0884a8b85f18c8b34ffee19f1395d12aef1ffc2821120f03", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA384.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "ef471993ee9839701feba16b5b56a926545a165bf95224130d6c8a2bdafdd451": { + "signature": "ef471993ee9839701feba16b5b56a926545a165bf95224130d6c8a2bdafdd451", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA512.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "d742600df5b4b6b71f65cf0079b09ec36a5d58bb4b8b07923b13ab8458f68a15": { + "signature": "d742600df5b4b6b71f65cf0079b09ec36a5d58bb4b8b07923b13ab8458f68a15", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 31, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "fd9536ec348269dbb12b813270403b7410fc13575d1cbb7770604dcf54ee776b": { + "signature": "fd9536ec348269dbb12b813270403b7410fc13575d1cbb7770604dcf54ee776b", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 40, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "1614f1f0821f8900c7c8d2cb3a784272518ef6b86fb82070bda88b4bac9dbda8": { + "signature": "1614f1f0821f8900c7c8d2cb3a784272518ef6b86fb82070bda88b4bac9dbda8", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 50, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "3479973e9ab00c84264e70c5d7290a5bfda506aafdb08c3277cb1df5db688ab9": { + "signature": "3479973e9ab00c84264e70c5d7290a5bfda506aafdb08c3277cb1df5db688ab9", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 56, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "e5bd231f55be121ed2579da9651c4ec0e661a386991ef8463facf3588c306a06": { + "signature": "e5bd231f55be121ed2579da9651c4ec0e661a386991ef8463facf3588c306a06", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 65, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "19661fe89c5e1f59089cdec06cbdf6bdda2439b52731dfd630b50ff5885d6223": { + "signature": "19661fe89c5e1f59089cdec06cbdf6bdda2439b52731dfd630b50ff5885d6223", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 75, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "3bf81d57b0872ef6c8ac7878513cb8f58044e07238883f71f24b09542a8d1a07": { + "signature": "3bf81d57b0872ef6c8ac7878513cb8f58044e07238883f71f24b09542a8d1a07", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 80, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "2e07fdf491a04fc8a6cc50ae299f8345a81798499bda961a118e974970bb71a8": { + "signature": "2e07fdf491a04fc8a6cc50ae299f8345a81798499bda961a118e974970bb71a8", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 86, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "560fdab2979120fa3cfd2d9865d1ce2c0ba164982cc086cdc871b7e16fd12466": { + "signature": "560fdab2979120fa3cfd2d9865d1ce2c0ba164982cc086cdc871b7e16fd12466", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 92, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "489c5d92c47b3c961db0d5c32426a4d5fb48311e46ae49791be0f6ef03ddfc6e": { + "signature": "489c5d92c47b3c961db0d5c32426a4d5fb48311e46ae49791be0f6ef03ddfc6e", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 101, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "5890ec8b0f533c8186a824f1b46b2d99c2d54e7ed09917e5fedcdaea19b34706": { + "signature": "5890ec8b0f533c8186a824f1b46b2d99c2d54e7ed09917e5fedcdaea19b34706", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "line": 230, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0140", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "70a8cd9f176fa02a78470ae78c285f56b617060ec339f0c8dfda095a5b0fc6c9": { + "signature": "70a8cd9f176fa02a78470ae78c285f56b617060ec339f0c8dfda095a5b0fc6c9", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/humanizer/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp_TemporaryKey.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "7afd296a74705d2e561497c0ab8c3750179c1eff425f496297d3b877ff718526": { + "signature": "7afd296a74705d2e561497c0ab8c3750179c1eff425f496297d3b877ff718526", + "alternativeSignatures": [], + "target": "src/source-build-externals/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "6ffd69e0724ed3b57e926224932b0aeeed7a834ee5dc1ad24c3262deb49172d7": { + "signature": "6ffd69e0724ed3b57e926224932b0aeeed7a834ee5dc1ad24c3262deb49172d7", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "ca298b6b96ebfae8f14e29993264310b89fd738b365c2e7ca04e7af1a5cd67b7": { + "signature": "ca298b6b96ebfae8f14e29993264310b89fd738b365c2e7ca04e7af1a5cd67b7", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert2.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8764975fced41745e1a9c844144b2aacd30d3fcd77a8c4b1067adc5f2cd8b990": { + "signature": "8764975fced41745e1a9c844144b2aacd30d3fcd77a8c4b1067adc5f2cd8b990", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "7cc3b3782554547d94e0f2e8817dbf823a84de8a95b6b358bdd29067bcff64c3": { + "signature": "7cc3b3782554547d94e0f2e8817dbf823a84de8a95b6b358bdd29067bcff64c3", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3WithoutPrivateKey.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "db15b8200219494e427c3943404a81e931bf17af175823bc51da9b85b63a6831": { + "signature": "db15b8200219494e427c3943404a81e931bf17af175823bc51da9b85b63a6831", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCertWithoutPrivateKey.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "979582e68b87ec6e4cd7d90df4e05f01a6f9150ed07a0abc205112a241f0c16f": { + "signature": "979582e68b87ec6e4cd7d90df4e05f01a6f9150ed07a0abc205112a241f0c16f", + "alternativeSignatures": [], + "target": "src/diagnostics/src/SOS/SOS.UnitTests/Debuggees/WebApp3/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "56b251ecea166720fac682142922d79e01699a8cc576683b6d8944dfd3158de2": { + "signature": "56b251ecea166720fac682142922d79e01699a8cc576683b6d8944dfd3158de2", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 72, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "cbcd76cbd2a6b0a8028fd6c3d1d11ee2e211519af2420396399c3c111a4da667": { + "signature": "cbcd76cbd2a6b0a8028fd6c3d1d11ee2e211519af2420396399c3c111a4da667", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 73, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8060e90601237ded928ae57570ef982dd3a57e7bffbec010fcd37e0bda518f10": { + "signature": "8060e90601237ded928ae57570ef982dd3a57e7bffbec010fcd37e0bda518f10", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 90, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "bc4b0a2231b13e5472e1548250ef4d7174d130daf559bca81f5d3c2c0c169690": { + "signature": "bc4b0a2231b13e5472e1548250ef4d7174d130daf559bca81f5d3c2c0c169690", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 91, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "91f0752674a19d0db604c50e23950746ff4d231cc76e40a7fa0f53dd5e855f4d": { + "signature": "91f0752674a19d0db604c50e23950746ff4d231cc76e40a7fa0f53dd5e855f4d", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 109, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "889e33f8e52ecb1d2b98d28772c572c2a10f0dc0c22fcc03b1da03df008d5f91": { + "signature": "889e33f8e52ecb1d2b98d28772c572c2a10f0dc0c22fcc03b1da03df008d5f91", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 110, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "6969ae787ed7e1caef670545de569929814479666e60a227abcb36395c3d3f60": { + "signature": "6969ae787ed7e1caef670545de569929814479666e60a227abcb36395c3d3f60", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 111, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "81adfccafd8eb134b75b59de6d4d0d8198c4639a3972d8072b61c67c9e1a104d": { + "signature": "81adfccafd8eb134b75b59de6d4d0d8198c4639a3972d8072b61c67c9e1a104d", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "line": 112, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "cf0223227addcd8b17bc89e4e5f0ee9174bca83c9ffc5b5493ef74940b33b58c": { + "signature": "cf0223227addcd8b17bc89e4e5f0ee9174bca83c9ffc5b5493ef74940b33b58c", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs", + "line": 49, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "fc000248c70e613f381ee770fb96dfd28e26766fa9627d28e5b9eb5fb314a3c8": { + "signature": "fc000248c70e613f381ee770fb96dfd28e26766fa9627d28e5b9eb5fb314a3c8", + "alternativeSignatures": [], + "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs", + "line": 72, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "b93b213c68cbbde7c5d0f64b48ad5289efccfcb18e202930a256ac7dbdd2264a": { + "signature": "b93b213c68cbbde7c5d0f64b48ad5289efccfcb18e202930a256ac7dbdd2264a", + "alternativeSignatures": [], + "target": "src/roslyn/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs", + "line": 4207, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "5ac1e0f1847cf476ae1c810ac0a74ee5993d05873feca939c0df1ac691a1527c": { + "signature": "5ac1e0f1847cf476ae1c810ac0a74ee5993d05873feca939c0df1ac691a1527c", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs", + "line": 3441, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0060", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "2987ea627ea4f2effb417244c4aac8f554bf42136d6ebe0fef1df440410be09c": { + "signature": "2987ea627ea4f2effb417244c4aac8f554bf42136d6ebe0fef1df440410be09c", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs", + "line": 2743, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "4c2880cbace8da677942b80be039a982e356c18c827ebeddab265175c9640427": { + "signature": "4c2880cbace8da677942b80be039a982e356c18c827ebeddab265175c9640427", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs", + "line": 2879, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "849ea2c1bdff1b36df8848a7f8898f11e981c3ba8f9b033abd9d1e8b1a03c8ca": { + "signature": "849ea2c1bdff1b36df8848a7f8898f11e981c3ba8f9b033abd9d1e8b1a03c8ca", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs", + "line": 2962, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "1706aea6ec9bfbbe540cfab3713db9b1a9ebc7f475b49f2348860e3ee6d8e682": { + "signature": "1706aea6ec9bfbbe540cfab3713db9b1a9ebc7f475b49f2348860e3ee6d8e682", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert1.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "8369054f43a96d4f463976b23773b2d22f8081d95f715161f019ceb58296d64a": { + "signature": "8369054f43a96d4f463976b23773b2d22f8081d95f715161f019ceb58296d64a", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert2.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "4ab520e2ed77b36c88f9eeeb5473205fd7f8f0a761eacee4cbbff389368dad8f": { + "signature": "4ab520e2ed77b36c88f9eeeb5473205fd7f8f0a761eacee4cbbff389368dad8f", + "alternativeSignatures": [ + "7ad51ffeb7d5438f15781162de5183c58d5db2d195f96e8c3527451adeb4e02c" + ], + "target": "src/aspnetcore/src/Hosting/Server.IntegrationTesting/src/Deployers/RemoteWindowsDeployer/RemotePSSessionHelper.ps1", + "line": 40, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "96ab8bbaf30065be77880d679174d028a151c0fc888fca38a79038d341710563": { + "signature": "96ab8bbaf30065be77880d679174d028a151c0fc888fca38a79038d341710563", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.cs", + "line": 30, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-MSFT0090", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "7bda7ae4037698bbdf188b4d3fb8a82e99063a8df2dcfe5f66ef747e3bd3f1a0": { + "signature": "7bda7ae4037698bbdf188b4d3fb8a82e99063a8df2dcfe5f66ef747e3bd3f1a0", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.FunctionalTest/negotiateAuthCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "a2dcb4a5d7e266049f9f865b27e69af19447e94f63ec9de8290763ddf901c756": { + "signature": "a2dcb4a5d7e266049f9f865b27e69af19447e94f63ec9de8290763ddf901c756", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.Test/LdapSettingsValidationTests.cs", + "line": 25, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-MSFT0090", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "3aaf66a9a1f5bfba3b78eb7964ce8bf4dcabf7132465a31e219c6961f53dec56": { + "signature": "3aaf66a9a1f5bfba3b78eb7964ce8bf4dcabf7132465a31e219c6961f53dec56", + "alternativeSignatures": [], + "target": "src/aspnetcore/src/Middleware/WebSockets/test/ConformanceTests/AutobahnTestApp/TestResources/testCert.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "c6ed8bf76382b72621892d895d0659eb8ed66ef400f5d38506a3b62129b0f60e": { + "signature": "c6ed8bf76382b72621892d895d0659eb8ed66ef400f5d38506a3b62129b0f60e", + "alternativeSignatures": [ + "07873a6bbdd04caf121ed279cd4c24e55fb79ae3e86083c413b839d8d5e81cba" + ], + "target": "src/runtime/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_activedirectory_domaincontroller.ps1", + "line": 36, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "c511a0f0c15b79748a62ee0198689e7a0c8c2af102730c337823f6cd52b3ec66": { + "signature": "c511a0f0c15b79748a62ee0198689e7a0c8c2af102730c337823f6cd52b3ec66", + "alternativeSignatures": [ + "7142b2e2126a0c0e5bf2ad08e9e56d405620fbb9f12dfcd3f90a9dfcc30f8bf5" + ], + "target": "src/runtime/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_iisserver.ps1", + "line": 82, + "memberOf": [ + "default" + ], + "tool": "psscriptanalyzer", + "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "bfe258b52e19062b9009a68549bff3b2c99a6105f493cbf14332b3366691d446": { + "signature": "bfe258b52e19062b9009a68549bff3b2c99a6105f493cbf14332b3366691d446", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", + "line": 54, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "52364f6839cf4bc824f1c82a31f3c7ee1cfb228383b3bee476ef7442526c0de8": { + "signature": "52364f6839cf4bc824f1c82a31f3c7ee1cfb228383b3bee476ef7442526c0de8", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", + "line": 358, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "cef12040ed2c91d3bd7eba717e6c4bff8e547cb6d2b40363f1d859b02c873276": { + "signature": "cef12040ed2c91d3bd7eba717e6c4bff8e547cb6d2b40363f1d859b02c873276", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", + "line": 61, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "ab290d190fa6582f2826c9b8524a4013ea67380f65328bc39c31dbcba59ec63c": { + "signature": "ab290d190fa6582f2826c9b8524a4013ea67380f65328bc39c31dbcba59ec63c", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", + "line": 168, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "710963884a4d1e73d1ff4da0db7e1c8e1eeae25fe4a9e3c1de8b5019bb5d9d74": { + "signature": "710963884a4d1e73d1ff4da0db7e1c8e1eeae25fe4a9e3c1de8b5019bb5d9d74", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", + "line": 412, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "db276fb1ea2a8e74e7ab80522362b8f402d361652ca823d7cab59465d038eb82": { + "signature": "db276fb1ea2a8e74e7ab80522362b8f402d361652ca823d7cab59465d038eb82", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", + "line": 229, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "807277ed6647bcdc3eab2e24d8fbcaca0a6506d2ed455248b0497ceb42831e30": { + "signature": "807277ed6647bcdc3eab2e24d8fbcaca0a6506d2ed455248b0497ceb42831e30", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", + "line": 328, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "685288ad132baddbcdf7a2201960f6d48805151f1aef2e094c4dbc16841b3e54": { + "signature": "685288ad132baddbcdf7a2201960f6d48805151f1aef2e094c4dbc16841b3e54", + "alternativeSignatures": [], + "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", + "line": 489, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "e972eb97ea7ea4a9524820036a42557f13a48240b6083c7baa0d465c00adfdee": { + "signature": "e972eb97ea7ea4a9524820036a42557f13a48240b6083c7baa0d465c00adfdee", + "alternativeSignatures": [], + "target": "src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.key", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "1b2c907630acaff5b6e62eb4895043c82e93d885331f1f46296812634fd30abe": { + "signature": "1b2c907630acaff5b6e62eb4895043c82e93d885331f1f46296812634fd30abe", + "alternativeSignatures": [], + "target": "src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx", + "line": 1, + "memberOf": [ + "default" + ], + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2024-04-02 08:30:33Z", + "expirationDate": "2024-09-19 11:21:54Z", + "justification": "This error is baselined with an expiration date of 180 days from 2024-04-02 11:21:54Z" + }, + "b1a4e35c6f6e2d47dbcac13c715893653c3bb2c754a5a7fe6399f5ff21e74038": { + "signature": "b1a4e35c6f6e2d47dbcac13c715893653c3bb2c754a5a7fe6399f5ff21e74038", + "alternativeSignatures": [ + "89a6120a7a327b4237773410934c46acfe5519839e770df5cb3e1468e65cb71d" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-x86.zip/shared/Microsoft.NETCore.App/10.0.0-rc.1.25502.108/pgort140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:27:46Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "13e53e10c92cc5effa6cfa9ee9a12076f3013ff2bb350387077b141c6497169f": { + "signature": "13e53e10c92cc5effa6cfa9ee9a12076f3013ff2bb350387077b141c6497169f", + "alternativeSignatures": [ + "52ed7325288709348459996965423183afecf0fafdb0c6e2f32de37fa7b8020f" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-x86.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:27:46Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "faf0f1cf576e3ae00c738698fb086b2edb23118e441aec3a44501073bfbd57d1": { + "signature": "faf0f1cf576e3ae00c738698fb086b2edb23118e441aec3a44501073bfbd57d1", + "alternativeSignatures": [ + "72577e042c4deb1859f569d414ea39b44bf3301330a78242e0d459e1d57dce73" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-x86.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:27:46Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "f0641e291fd7b4c041d2f5a1c788a6f4bdc540f825c0b2d9a42d2fc5e5d42cf8": { + "signature": "f0641e291fd7b4c041d2f5a1c788a6f4bdc540f825c0b2d9a42d2fc5e5d42cf8", + "alternativeSignatures": [ + "968ab4f609cb8418bd48e95e0528daa2cfa64d30f15f3b23b1716d27559f3721" + ], + "target": "artifacts/packages/Release/NonShipping/arcade/gdn-Microsoft.DotNet.SignCheck.11.0.0-beta.25502.108.nupkg/tools/winterop.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "67713a5842aee81d885c418ad955663ca50804a3406ffa54210c5b4f802d235e": { + "signature": "67713a5842aee81d885c418ad955663ca50804a3406ffa54210c5b4f802d235e", + "alternativeSignatures": [ + "5be43d512e86d10c1f195fe42e38a2a31b27a2a22a2598df92e79b603b93d02e" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/python.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "0fee4802d05838bc2ac1693c62c2d06eded190d02af69e1592f8c466991a8784": { + "signature": "0fee4802d05838bc2ac1693c62c2d06eded190d02af69e1592f8c466991a8784", + "alternativeSignatures": [ + "967b8e9808f44938a1eafa10c4df03f1f4728cb7c622c05556d2495929a0732b" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/python311.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "3c95e62450aba7299a724597b109084e0b16152feed00033115c1fdf2f364db0": { + "signature": "3c95e62450aba7299a724597b109084e0b16152feed00033115c1fdf2f364db0", + "alternativeSignatures": [ + "9fd3a1add81af22778a6794ec31ce343319c9b9fb2a36a5b7d11ae57f3300b5f" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/pythonw.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "9f7b8c414f7aefbca8ceafe605b486bb1835cfcaafb3013137718e0f916688a8": { + "signature": "9f7b8c414f7aefbca8ceafe605b486bb1835cfcaafb3013137718e0f916688a8", + "alternativeSignatures": [ + "f166031a8e2a689be432e308af8548ba7d1586c48123c051d1be9c6123c227f6" + ], + "target": "artifacts/packages/Release/NonShipping/arcade/gdn-Microsoft.DotNet.SignCheckTask.11.0.0-beta.25502.108.nupkg/lib/net/winterop.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "05189863b82096832a660f1e01ec735359f58e9c6693ed5f1125055d86f9169d": { + "signature": "05189863b82096832a660f1e01ec735359f58e9c6693ed5f1125055d86f9169d", + "alternativeSignatures": [ + "d68bd067927adf3129aefd09ad6a350f00eeb570daedcf30a6546840c084d81f" + ], + "target": "artifacts/packages/Release/NonShipping/arcade/gdn-Microsoft.DotNet.SignCheckTask.11.0.0-beta.25502.108.nupkg/lib/netframework/winterop.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "6cfc27271fe2d31d5d4f21f80e54a1a610637b5d8f7359995f939322ce3f916b": { + "signature": "6cfc27271fe2d31d5d4f21f80e54a1a610637b5d8f7359995f939322ce3f916b", + "alternativeSignatures": [ + "acb7542d2d43f6851c3e77b9eb72527779838bb133adf6db891a5631a35f6620" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Node.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/node.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "02d4643dee81896d22745590731ee2367603cdaca456790429ce81e6031ddca8": { + "signature": "02d4643dee81896d22745590731ee2367603cdaca456790429ce81e6031ddca8", + "alternativeSignatures": [ + "03fca0aa1b32cecac33dd9e65703b3bd03ccc12b1b2bdbf72729bad511227a71" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libcrypto-1_1.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "7bfd851dd05c872084901d1191fac7a460a6b2aa44d15ea4d09d9206c8135efb": { + "signature": "7bfd851dd05c872084901d1191fac7a460a6b2aa44d15ea4d09d9206c8135efb", + "alternativeSignatures": [ + "8c5739b132bdd4dc841dff1f920859f8a1f72633b0247e12f3dcdb1ed119118c" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libffi-8.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "0e3871e0517f179825f08c32840c37cd8127fbbfbdc28f05461317cdcbdf7ce8": { + "signature": "0e3871e0517f179825f08c32840c37cd8127fbbfbdc28f05461317cdcbdf7ce8", + "alternativeSignatures": [ + "62c9390883ffade1edcbbaa81b42faafb0ef623876afd25eed91a09d28da4b30" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libssl-1_1.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "b2483930fe85bb60fd8ccfc009c24017a008fa917a12f2db595309dd98af813b": { + "signature": "b2483930fe85bb60fd8ccfc009c24017a008fa917a12f2db595309dd98af813b", + "alternativeSignatures": [ + "b3ef03cd0f2d65f68e7dd260944954af496f2f09b9259765c53355f33a09b1b3" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/sqlite3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "1dbd981155b4350d707c97d60d13439782c804155e0f713d553da4bdcf0cf745": { + "signature": "1dbd981155b4350d707c97d60d13439782c804155e0f713d553da4bdcf0cf745", + "alternativeSignatures": [ + "db8eada4fa32f729ae94d3ea31adf0f7b378bbd42b78868c13f9d2f14dba743a" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-as.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "cbb656150acc03a17364565046449f56afc76408ced114a655a0dcb05f31ed91": { + "signature": "cbb656150acc03a17364565046449f56afc76408ced114a655a0dcb05f31ed91", + "alternativeSignatures": [ + "9373981f810bce595d6f8a43c75c0b2bce7d2f52deab202ce02b41eee9ee8ff9" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-ctor-eval.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ec41a8a87efa39dbd9d17926a95fadc5630aba170c9e054a1ae42078ece2a377": { + "signature": "ec41a8a87efa39dbd9d17926a95fadc5630aba170c9e054a1ae42078ece2a377", + "alternativeSignatures": [ + "a488a332ce32157923f7c87987e2eaf87e16d7769f8dc4caaa6498440227405f" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-dis.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "76f59cc377c3ea7a99b2e2447e43781a2bf3be48a33d6ace9c1c54cbd1691b7e": { + "signature": "76f59cc377c3ea7a99b2e2447e43781a2bf3be48a33d6ace9c1c54cbd1691b7e", + "alternativeSignatures": [ + "705d36060ccb25ac33c988f1421e2178b02bcc92634275de56b117d8ec0b7f96" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-emscripten-finalize.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "024fa5301f3575390086ae804602ab4af21b2141dd73916ff86dc443361f52aa": { + "signature": "024fa5301f3575390086ae804602ab4af21b2141dd73916ff86dc443361f52aa", + "alternativeSignatures": [ + "3a45c46312cc741895275bbeca3379649406a380b5975410cbca3e4ef1650db6" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-fuzz-lattices.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "8f04767dfd27efcda4fdfbec9f04315400a9730b396c8e4ff5cb67c1c4e77e72": { + "signature": "8f04767dfd27efcda4fdfbec9f04315400a9730b396c8e4ff5cb67c1c4e77e72", + "alternativeSignatures": [ + "c7d9993738cb57b61ccd7dd628ff036bd373ea927bf39aaea469dbe3e77ff5d2" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-fuzz-types.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "d40e6851155762cfc6a8ff8b0dcdfe550c17a55d5d5cfc405e350f5b1c29d144": { + "signature": "d40e6851155762cfc6a8ff8b0dcdfe550c17a55d5d5cfc405e350f5b1c29d144", + "alternativeSignatures": [ + "8c3028867026eeba81df7ea7b82758af6c74fb793374e542760c0f7965592965" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-merge.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "781ded98de7ac07f9cf926252ceeb0c4cd9f8569de11cd55d88701608530b5a9": { + "signature": "781ded98de7ac07f9cf926252ceeb0c4cd9f8569de11cd55d88701608530b5a9", + "alternativeSignatures": [ + "8c1167dad9ce205146a7618bbbc7c0fc9da3fb7d072396acefbc72078f30bc74" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-metadce.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "9fbf6e887d56593bef399aa3c9b9395db73822bbcfb81c8a6ea78fd183a0d00e": { + "signature": "9fbf6e887d56593bef399aa3c9b9395db73822bbcfb81c8a6ea78fd183a0d00e", + "alternativeSignatures": [ + "eab53ad84490648fd035541e728d9a554e35a10c07f66a384a3ffebb5b0e87e6" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-opt.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "8f90fbd9eee9fec759935fb4d02c1392946a22533412a61cfdb91eb4501e68cf": { + "signature": "8f90fbd9eee9fec759935fb4d02c1392946a22533412a61cfdb91eb4501e68cf", + "alternativeSignatures": [ + "000f79bdb952139fe4ef8510783559c9966ef197c578b5389b6569e237963e14" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-reduce.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "de0defd9b26cc2046d60827b3102c2b178415ece5076480b9744a3701efbbf38": { + "signature": "de0defd9b26cc2046d60827b3102c2b178415ece5076480b9744a3701efbbf38", + "alternativeSignatures": [ + "70f004bde6290488c2f023a1e1dfd0b35d66e1a2bccc11034daae0f1f5f95f0d" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-shell.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "a81975b343c5313cab035a5d322c81181e4f7ebd10e3399a78df1234579bb7e7": { + "signature": "a81975b343c5313cab035a5d322c81181e4f7ebd10e3399a78df1234579bb7e7", + "alternativeSignatures": [ + "12cb40097fd2ef1a6635e96297a0aad21327059c8aaef2d172dbda81ab32ae1b" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-split.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "e5a2edd2e2dcc3edfcceca7cd1a604b7851a67100e89286bd200c1c2772a8044": { + "signature": "e5a2edd2e2dcc3edfcceca7cd1a604b7851a67100e89286bd200c1c2772a8044", + "alternativeSignatures": [ + "308c6aae6e167c90312ad5eea661937440456fa2586c2013d38812f3d831e024" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-x64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm2js.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "df1aa6cc2afc109c2cf28c097fab07badd9bba1a6ae033271b3f6235e3291d7a": { + "signature": "df1aa6cc2afc109c2cf28c097fab07badd9bba1a6ae033271b3f6235e3291d7a", + "alternativeSignatures": [ + "c357dfb740203d92abd503e3d69a1d948af9091442deccce970462d86aeb5f5e" + ], + "target": "artifacts/packages/Release/NonShipping/wpf/gdn-runtime.win-x64.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.nupkg/runtimes/win-x64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "c1be0f6fb53b6ea4050ea7424603669f0982a551138ac02245bbd64227326a5d": { + "signature": "c1be0f6fb53b6ea4050ea7424603669f0982a551138ac02245bbd64227326a5d", + "alternativeSignatures": [ + "3932ecc7c7cdfdac506abc1e52e0256592b24cbf150f62a051958ac2a0ca6e3d" + ], + "target": "artifacts/packages/Release/Shipping/vstest/gdn-Microsoft.TestPlatform.TestHost.18.0.0-preview-25502-108.nupkg/lib/net8.0/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "5cdbef1911a7a7c21e9fb5cacbf8b8677c9971092ab64a7f04d68065f50fe92f": { + "signature": "5cdbef1911a7a7c21e9fb5cacbf8b8677c9971092ab64a7f04d68065f50fe92f", + "alternativeSignatures": [ + "9bcf8577915ccab267862ce1a00373abed5e4d928663bd5b49f10ff2e9fd7965" + ], + "target": "artifacts/packages/Release/Shipping/vstest/gdn-Microsoft.TestPlatform.TestHost.18.0.0-preview-25502-108.nupkg/lib/net8.0/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "d5a52ebd5c210820734e5d7f05dcab401bfd9ece76811cba7cfd175891ef73d3": { + "signature": "d5a52ebd5c210820734e5d7f05dcab401bfd9ece76811cba7cfd175891ef73d3", + "alternativeSignatures": [ + "2425b2e7ecf48101e65a899c03380a895655b58c6231af3a1d44061f7322163f" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-x64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "fb2231919574d47b4b621dce6ffabc4a8587f3a401dbc2d93cba3989101c0991": { + "signature": "fb2231919574d47b4b621dce6ffabc4a8587f3a401dbc2d93cba3989101c0991", + "alternativeSignatures": [ + "b32f25f713894911af41e646e9566c92b94d3911ac7e45a673c0455c8a240e89" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-x64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ca6c4c6ed2f6d813f89ac02d3dffe6474131e87377b74dbd2e0d2b1160000047": { + "signature": "ca6c4c6ed2f6d813f89ac02d3dffe6474131e87377b74dbd2e0d2b1160000047", + "alternativeSignatures": [ + "ec078fc50ce8b43ea2a65d035af9fc8306a3655e683523eda5c60f7a399e3d3c" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.TestPlatform.TestHost.18.0.0-preview-25502-108.symbols.nupkg/lib/net8.0/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "3442a8439d57ca8a0379c5e41bbdd10f52c74f0ade7409a08403d7a6338d5094": { + "signature": "3442a8439d57ca8a0379c5e41bbdd10f52c74f0ade7409a08403d7a6338d5094", + "alternativeSignatures": [ + "12906435fa67e59f08c2f92ae5669bccccba39a307e31e767c26f7ec5e0c668e" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.TestPlatform.TestHost.18.0.0-preview-25502-108.symbols.nupkg/lib/net8.0/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "c2380a81994190aef6609573ff05a793aa1c13fe3b0ff8ab3e7472207c90bb0f": { + "signature": "c2380a81994190aef6609573ff05a793aa1c13fe3b0ff8ab3e7472207c90bb0f", + "alternativeSignatures": [ + "5b70d443252104a129c66d974d671df6b13674d114c0714c62f3685742b69e8c" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.WindowsDesktop.App.Runtime.win-x64.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-x64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "aaccf2efeeb282e6b1a2959af9303390439ccd2776e31af5b51664dad32a9aa6": { + "signature": "aaccf2efeeb282e6b1a2959af9303390439ccd2776e31af5b51664dad32a9aa6", + "alternativeSignatures": [ + "6c8747ce0c0af3335d012feb7e7b13c7cef449db978d20521fe51ace1d82fe5a" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-runtime.win-x64.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-x64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ef571cdd3dded762c12349c914f6e061b2a58104527396926500a425c0f28fe9": { + "signature": "ef571cdd3dded762c12349c914f6e061b2a58104527396926500a425c0f28fe9", + "alternativeSignatures": [ + "17d3df0e6fe15fffd13ce3c9971210822b13e58687bd0046a7e0d2a208763708" + ], + "target": "artifacts/packages/Release/Shipping/vstest/gdn-Microsoft.TestPlatform.CLI.18.0.0-preview-25502-108.nupkg/contentFiles/any/net9.0/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "edf2ab0c86c89aed232c27937c192e4a035cb89809ba0d5b89dae8e5176165c5": { + "signature": "edf2ab0c86c89aed232c27937c192e4a035cb89809ba0d5b89dae8e5176165c5", + "alternativeSignatures": [ + "0a4a24a5b055631d0d7045fc2b368eca3a00648cc8bb9c9f49e1c4fc3a03765a" + ], + "target": "artifacts/packages/Release/Shipping/vstest/gdn-Microsoft.TestPlatform.CLI.18.0.0-preview-25502-108.nupkg/contentFiles/any/net9.0/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "2033877cf8794a81127de497c63057f338d090083649de819baf086429c7fea5": { + "signature": "2033877cf8794a81127de497c63057f338d090083649de819baf086429c7fea5", + "alternativeSignatures": [ + "4f92264f1ee7f5da6487cf5a5c52e54f3312636090f0a6d160d9b6a92d8c10df" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.TestPlatform.CLI.18.0.0-preview-25502-108.symbols.nupkg/contentFiles/any/net9.0/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ccb5151bf7ff40b005a8b6a657310c8a398c50253dcd803ef19ee6f9c3631677": { + "signature": "ccb5151bf7ff40b005a8b6a657310c8a398c50253dcd803ef19ee6f9c3631677", + "alternativeSignatures": [ + "ec9e9e897bf291eef6b4346aa7d8cb37a86dfb5decdb5260db0fc0f8a77ad712" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.TestPlatform.CLI.18.0.0-preview-25502-108.symbols.nupkg/contentFiles/any/net9.0/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:01:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ea3f5bded86323088d7076ed6f743874f3464abedb2809ada060b093e20e26b1": { + "signature": "ea3f5bded86323088d7076ed6f743874f3464abedb2809ada060b093e20e26b1", + "alternativeSignatures": [ + "f7c0707362b152fc8ce120cd4c23d53cee7d7066219b4f4a33bfb5dcce318d88" + ], + "target": "artifacts/packages/Release/NonShipping/wpf/gdn-runtime.win-x86.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.nupkg/runtimes/win-x86/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:30:12Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "b70ac6417c02105848419028ade16fcc44972d631b9fbd7e5ec3130ebfdb18c3": { + "signature": "b70ac6417c02105848419028ade16fcc44972d631b9fbd7e5ec3130ebfdb18c3", + "alternativeSignatures": [ + "b68f35127803d50fbc42cda951e529f57b0b2aa8bfb2a4a9e06fea3cbe7c507d" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-x86.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:30:12Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "0e77e5c8a4fc1fc39f17e58230601665e9a5cce134bc463306e5edf69113b4a3": { + "signature": "0e77e5c8a4fc1fc39f17e58230601665e9a5cce134bc463306e5edf69113b4a3", + "alternativeSignatures": [ + "df24689d7c5c9e01eb0bc5f1e252c5b288f71f593ad9597520f9596a6b07b9c5" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-x86.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:30:12Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "2c4082f5972891f8ee540d8b9aac9dcc86d58fb0562833491731fa358e3d0b19": { + "signature": "2c4082f5972891f8ee540d8b9aac9dcc86d58fb0562833491731fa358e3d0b19", + "alternativeSignatures": [ + "5de4b311481c71137f4af527fbf8f0b84bb7c731b7f7d65c6da1fd62e4a2b469" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.WindowsDesktop.App.Runtime.win-x86.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-x86/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:30:12Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "86072ada24477bf451b98d951ad8bc10c930c4e3a291f29837d1b2138b02969f": { + "signature": "86072ada24477bf451b98d951ad8bc10c930c4e3a291f29837d1b2138b02969f", + "alternativeSignatures": [ + "c65128ca8c8a7c533a0f16a68e8d8216f06636d9a921da9e6d9f817d83a121bc" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-runtime.win-x86.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-x86/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:30:12Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "1ba08945e0c66c73328584ad4884def99c0008fdc7edd415a7c5d6b529bf8bde": { + "signature": "1ba08945e0c66c73328584ad4884def99c0008fdc7edd415a7c5d6b529bf8bde", + "alternativeSignatures": [ + "ca05e5cf0615f7ad08756f9e7da2c7fe0dee4a82a0bd6754160f92734dbb0a6c" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/python.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "0b449732366978c1acaa71aea325a05df77daafed5c7e196196294daf6edb244": { + "signature": "0b449732366978c1acaa71aea325a05df77daafed5c7e196196294daf6edb244", + "alternativeSignatures": [ + "b988b00755ae4051d64263c6b9fe62ccc5365c0920168db4352cc775595a59a1" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/python311.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "35720572ff89bc08fec585f0e196e8e71e77701b1a69a2c5f61babb9d5e1018d": { + "signature": "35720572ff89bc08fec585f0e196e8e71e77701b1a69a2c5f61babb9d5e1018d", + "alternativeSignatures": [ + "a62e810f9d2405c546e9b072b614030caf7a510758eedf005a1e4640aca69e0d" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/pythonw.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "40d640724c84e5e1ba3f3e634cbe103c7d99d6cff6a7971f774ea17741c7eb28": { + "signature": "40d640724c84e5e1ba3f3e634cbe103c7d99d6cff6a7971f774ea17741c7eb28", + "alternativeSignatures": [ + "1d746633d2ba961237adf2f4f5fdc5c1ad18a8f70f7f1023d5ed2314f9056f6d" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Node.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/node.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "1f7c3f3d8efcdd183753099acb13d4c62e8fbdde72bfa01813822c4f434085f9": { + "signature": "1f7c3f3d8efcdd183753099acb13d4c62e8fbdde72bfa01813822c4f434085f9", + "alternativeSignatures": [ + "013f212b819eda0c7ef336486fc08b322d9b489e48af62a84979fef2f1888b7a" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libcrypto-1_1-arm64.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "b5e56198a2166716f8d1180717469eec17817e302af806c3ca1095bda1299149": { + "signature": "b5e56198a2166716f8d1180717469eec17817e302af806c3ca1095bda1299149", + "alternativeSignatures": [ + "17fc098435410868788691b1e8ebc6f1c51c53a5e4b41d3b6a86b281f62847a5" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libffi-8.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "5fcd00b6f7a0e4897dc8f2c267fce3bdeca2961fc269bbf093f98fa0abc905d7": { + "signature": "5fcd00b6f7a0e4897dc8f2c267fce3bdeca2961fc269bbf093f98fa0abc905d7", + "alternativeSignatures": [ + "aab83bcf0f7d509903dcefc262b11fad3a96bd79d1285adc88aad294861affb1" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/libssl-1_1-arm64.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "ed6e5d4963ea3a4d978d6c543366aa9452c890e9f1a7c0f1247325e29bcea782": { + "signature": "ed6e5d4963ea3a4d978d6c543366aa9452c890e9f1a7c0f1247325e29bcea782", + "alternativeSignatures": [ + "0beff131e1fb8419072f461a2c5f24df9efac487782eac1afb7c9ba898c4c855" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Python.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/DLLs/sqlite3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "14f8f9189ed31b84a0ad36698646c7cff58d6167124ff9375e37b7ffbffa849b": { + "signature": "14f8f9189ed31b84a0ad36698646c7cff58d6167124ff9375e37b7ffbffa849b", + "alternativeSignatures": [ + "c47610878022fa177a28f6eab8301663750817f694deeba90df796938ebfa5b2" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-as.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "63386d2b07d75fe2db413cfc2fd99810b1885d3c9960cac67c26c7129f21e71a": { + "signature": "63386d2b07d75fe2db413cfc2fd99810b1885d3c9960cac67c26c7129f21e71a", + "alternativeSignatures": [ + "c8ba98ce42b285c391708713bee9e144c31597902b9a00b8ccc6b7dfa9d2cd32" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-ctor-eval.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "4234798402cdb18c0f1181f054147a87abd16c016ed68279c499933dead8f18f": { + "signature": "4234798402cdb18c0f1181f054147a87abd16c016ed68279c499933dead8f18f", + "alternativeSignatures": [ + "3de42dc30777140eeaa5a9bfc793c8733c06e2654a4856ca79a0f7c0718d3489" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-dis.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "c3deb33ac73aacb03e45c81f7b0c87158c253557eb1f4afdf0c9d21a5a5de242": { + "signature": "c3deb33ac73aacb03e45c81f7b0c87158c253557eb1f4afdf0c9d21a5a5de242", + "alternativeSignatures": [ + "ec5fe5158ea4b571b38e02ba42f905cab55d2ab23e9cf05ac4e3c0de83f68a13" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-emscripten-finalize.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "e05ab16f68102cf7c299e4686d55cbe075d72a1f18957b98d350640653a22131": { + "signature": "e05ab16f68102cf7c299e4686d55cbe075d72a1f18957b98d350640653a22131", + "alternativeSignatures": [ + "b976d229fe6cf84bfb8f4cc130a0fc8cb857732422d986b522e5c00ef4402acf" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-fuzz-lattices.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "e86426235304d266a679b898b747b4ce36515f3d80a437d21c91dafc0e72f469": { + "signature": "e86426235304d266a679b898b747b4ce36515f3d80a437d21c91dafc0e72f469", + "alternativeSignatures": [ + "d17fd3b360e9c2f6183c25e815d3340c3351d9345112e437ae92d0f327d5328e" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-fuzz-types.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "4168f212ef0117b3095a51061f0c69b4c106d1b8b67819a292dff159f2ce93c4": { + "signature": "4168f212ef0117b3095a51061f0c69b4c106d1b8b67819a292dff159f2ce93c4", + "alternativeSignatures": [ + "57c374509d926289985697614003ee9a0495cfec23c023445a84b713b26d4bff" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-merge.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "aa3a79ba341ea50fd4b8b647183729d027f81a82ee6bcd175f8b45b817f625cf": { + "signature": "aa3a79ba341ea50fd4b8b647183729d027f81a82ee6bcd175f8b45b817f625cf", + "alternativeSignatures": [ + "af0fde3afeaa1130d35daafe3413c1e324135385d686dcc576809bdb25326609" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-metadce.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "f7d19d83158eac73990cc59e1ee44fcca2e451058e5ac5c99a9c875b9c941099": { + "signature": "f7d19d83158eac73990cc59e1ee44fcca2e451058e5ac5c99a9c875b9c941099", + "alternativeSignatures": [ + "36094b8e3b70d6f1f94dc8a7f8c870f6c569d2e876c6c16db1a12cf24cc53478" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-opt.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "3624c6ca4296839fc1110446e9e8a310f15f1b2d3412b98404152441c11fa77f": { + "signature": "3624c6ca4296839fc1110446e9e8a310f15f1b2d3412b98404152441c11fa77f", + "alternativeSignatures": [ + "d38f36b0f1b23122ea94b58ca5f9f8797e0eaa02caeda293bfe4722404b3cceb" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-reduce.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "04f8f5404ddd74178f02f47cd3a50261f9587fe01273f0dc39d9fa72410af77e": { + "signature": "04f8f5404ddd74178f02f47cd3a50261f9587fe01273f0dc39d9fa72410af77e", + "alternativeSignatures": [ + "dad2707f29c9acbea3eda1305da9f096b5f42add7500104fdd0d0778178e77ff" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-shell.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "e90ac9380e7420e98535cca35dfcbe1693491bbe6c6304d3b29c1f7739a61339": { + "signature": "e90ac9380e7420e98535cca35dfcbe1693491bbe6c6304d3b29c1f7739a61339", + "alternativeSignatures": [ + "a106ee5a986aa4eb4af594c7c149846058e5807af2fd02549cad3fc60e5b82a5" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm-split.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "2d8d2dc6bed5dcad23eb78e05d88b037b9e7599cc0d15e1022614757dce9d867": { + "signature": "2d8d2dc6bed5dcad23eb78e05d88b037b9e7599cc0d15e1022614757dce9d867", + "alternativeSignatures": [ + "b0b22882021bda24b1a4e748223dd6da166d09711782bdadae1c5b56270dbcbf" + ], + "target": "artifacts/packages/Release/Shipping/emsdk/gdn-Microsoft.NET.Runtime.Emscripten.3.1.56.Sdk.win-arm64.10.0.0-rc.1.25502.108.nupkg/tools/bin/wasm2js.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "6259412a4e5276951d2cdf13c684bea47102fd44b010189a60bb94dc52fc7139": { + "signature": "6259412a4e5276951d2cdf13c684bea47102fd44b010189a60bb94dc52fc7139", + "alternativeSignatures": [ + "3e2ce2f85fe2d39e1910328c5f150da1819ac4174906ce77aaa1c7911dbe8532" + ], + "target": "artifacts/packages/Release/NonShipping/wpf/gdn-runtime.win-arm64.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.nupkg/runtimes/win-arm64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "b1570a73fa466fca46dd44ef8708889b7a30cc7d7ee6baa3039fa8c6f6a00a30": { + "signature": "b1570a73fa466fca46dd44ef8708889b7a30cc7d7ee6baa3039fa8c6f6a00a30", + "alternativeSignatures": [ + "28e3bdb04b186359d73f970e872f1e0b44577c23d55d754348f64e84bd2adf53" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-arm64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "da9d9479c9c0b83810d01a5b96c7683036556a738c7802132af6de96ddf0be7d": { + "signature": "da9d9479c9c0b83810d01a5b96c7683036556a738c7802132af6de96ddf0be7d", + "alternativeSignatures": [ + "706689d7bd92d8fb0103027d72a74ec4a852fce529c8d9aba53882e9f9813b35" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-10.0.100-rc.2.25502.108-win-arm64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "11ece6129bfdcc0fd8b77ae979b71fe2437e37217a74a407b66366c3af4cd7a7": { + "signature": "11ece6129bfdcc0fd8b77ae979b71fe2437e37217a74a407b66366c3af4cd7a7", + "alternativeSignatures": [ + "8d4825ad2e8e89b2eb1e80497ab858287ed270c024c794571a65e121a2d75b4b" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-Microsoft.WindowsDesktop.App.Runtime.win-arm64.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-arm64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "179ad8f8c976b5f59750a91911ad43810a56a8f7dc9cffe62d80be9e49a91bc0": { + "signature": "179ad8f8c976b5f59750a91911ad43810a56a8f7dc9cffe62d80be9e49a91bc0", + "alternativeSignatures": [ + "31dfd79e271d36f0d104313ebe6b56e30e4736c4666259b465eb0020778c93fb" + ], + "target": "artifacts/assets/Release/assets/symbols/dotnet-dotnet/20251002.8/gdn-runtime.win-arm64.Microsoft.DotNet.Wpf.GitHub.10.0.0-rc.1.25502.108.symbols.nupkg/runtimes/win-arm64/native/PresentationNative_cor3.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2007", + "createdDate": "2025-10-02 21:50:29Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "29c68f5f4fa12fac1bce05d79696e6920a12255e5cfb92f8462c36e7adce0ebf": { + "signature": "29c68f5f4fa12fac1bce05d79696e6920a12255e5cfb92f8462c36e7adce0ebf", + "alternativeSignatures": [ + "5b3215012d46284ab33aafdf007458a49c10f389af74c8debe5ec33b57c178cf" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-x64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:42:43Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "fb815f05aca12b80acbbb7c4f200e4657660fe3d6ee1af6419dd312f81d98a06": { + "signature": "fb815f05aca12b80acbbb7c4f200e4657660fe3d6ee1af6419dd312f81d98a06", + "alternativeSignatures": [ + "fe169538efbb430a89a48a1fb75d5e51de9376f5060530ef5eee622e0988bcd5" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-x64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 22:42:43Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "bd462cd1515b7f004badbcf510229dff9fabbe5dae3486faa119d7f8d5a2bd98": { + "signature": "bd462cd1515b7f004badbcf510229dff9fabbe5dae3486faa119d7f8d5a2bd98", + "alternativeSignatures": [ + "926e6f98a4124e10b1186470517addaed0909f0dfcaf86c7525abd470a7ec1c5" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-arm64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x64/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:43:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + }, + "d905ffe10b6aa3c41965a455ab64944ac1c1dca94a554cf7e49c213a4b9f2369": { + "signature": "d905ffe10b6aa3c41965a455ab64944ac1c1dca94a554cf7e49c213a4b9f2369", + "alternativeSignatures": [ + "f926d889635beb5cc9e8134e09b81383c7a0303556c34d2da8f7ed1e25f5040b" + ], + "target": "artifacts/assets/Release/Sdk/10.0.100-rc.2.25502.108/gdn-dotnet-sdk-pgo-10.0.100-rc.2.25502.108-win-arm64.zip/sdk/10.0.100-rc.2.25502.108/TestHostNetFramework/x86/msdia140.dll", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2008", + "createdDate": "2025-10-02 21:43:50Z", + "expirationDate": "2026-03-22 00:14:24Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-10-03 00:14:24Z" + } + } +} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 77c6d68c2bd2..597d3891c030 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,6 +20,8 @@ }, "remoteEnv": { "PATH": "${containerWorkspaceFolder}/.dotnet:${containerEnv:PATH}", + // Enable SHA1. Required for compilers to strong name assemblies. + "OPENSSL_ENABLE_SHA1_SIGNATURES":"1", }, "onCreateCommand": ".devcontainer/init.sh" } diff --git a/.github/ISSUE_TEMPLATE/unified-build-operational-issue.yml b/.github/ISSUE_TEMPLATE/unified-build-operational-issue.yml new file mode 100644 index 000000000000..0b909aea51e2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/unified-build-operational-issue.yml @@ -0,0 +1,57 @@ +name: Unified Build Operational Issue +description: Report operational issues related to unified build infrastructure +title: "[Operational Issue]: " +labels: ["area-unified-build-OperationalIssue"] +body: + - type: markdown + attributes: + value: | + Use this template to report operational issues with the unified build infrastructure. + + - type: textarea + id: error + attributes: + label: Error + description: Provide the error message or description of the operational issue + placeholder: | + Include stack traces, error messages, or detailed descriptions of the problem. + validations: + required: true + + - type: textarea + id: links + attributes: + label: Links + description: Provide relevant links (build logs, pipeline runs, etc.) + placeholder: | + - https://dev.azure.com/... + - https://github.com/... + validations: + required: false + + - type: input + id: root-cause + attributes: + label: Root Cause + description: Current understanding of the root cause + value: unknown + validations: + required: false + + - type: checkboxes + id: blocking + attributes: + label: Blocking + description: Is this issue blocking any releases or critical workflows? + options: + - label: This issue is blocking + + - type: checkboxes + id: affected-releases + attributes: + label: Affected Releases + description: Select the releases affected by this operational issue + options: + - label: .NET 10 + - label: .NET 10 (SDK band build) + - label: .NET 11 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..87ca1035f54a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/eng/dependabot" + open-pull-requests-limit: 5 + schedule: + interval: "weekly" + labels: + - "dependencies" diff --git a/.github/policies/assign_ownership.yml b/.github/policies/assign_ownership.yml index 5abc3fd72525..8598f02c8d1e 100644 --- a/.github/policies/assign_ownership.yml +++ b/.github/policies/assign_ownership.yml @@ -9,7 +9,215 @@ configuration: if: - payloadType: Pull_Request - filesMatchPattern: - pattern: src/arcade/.* + pattern: 'src/arcade/.*' + matchAny: true then: - requestReview: teamReviewer: dnceng + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/aspnetcore/.*' + matchAny: true + then: + - requestReview: + teamReviewer: aspnet-build + # - description: + # if: + # - payloadType: Pull_Request + # - filesMatchPattern: + # pattern: 'src/cecil/.*' + # matchAny: true + # then: + # - requestReview: + # teamReviewer: PLACEHOLDER + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/command-line-api/.*' + matchAny: true + then: + - requestReview: + teamReviewer: system-commandline + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/deployment-tools/.*' + matchAny: true + then: + - requestReview: + teamReviewer: deployment-tools-admins + # - description: + # if: + # - payloadType: Pull_Request + # - filesMatchPattern: + # pattern: 'src/diagnostics/.*' + # matchAny: true + # then: + # - requestReview: + # teamReviewer: PLACEHOLDER + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/efcore/.*' + matchAny: true + then: + - requestReview: + teamReviewer: efteam + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/emsdk/.*' + matchAny: true + then: + - requestReview: + teamReviewer: dnr-codeflow + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/fsharp/.*' + matchAny: true + then: + - requestReview: + teamReviewer: fsharp + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/msbuild/.*' + matchAny: true + then: + - requestReview: + teamReviewer: msbuild + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/nuget-client/.*' + matchAny: true + then: + - requestReview: + teamReviewer: nuget-team + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/razor/.*' + matchAny: true + then: + - requestReview: + teamReviewer: roslyn-infrastructure-current-swat + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/roslyn/.*' + matchAny: true + then: + - requestReview: + teamReviewer: roslyn-infrastructure-current-swat + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/runtime/.*' + matchAny: true + then: + - requestReview: + teamReviewer: dnr-codeflow + # - description: + # if: + # - payloadType: Pull_Request + # - filesMatchPattern: + # pattern: 'src/scenario-tests/.*' + # matchAny: true + # then: + # - requestReview: + # teamReviewer: PLACEHOLDER + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/sdk/.*' + matchAny: true + then: + - requestReview: + teamReviewer: dotnet-cli + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/source-build-reference-packages/.*' + matchAny: true + then: + - requestReview: + teamReviewer: source-build + # - description: + # if: + # - payloadType: Pull_Request + # - filesMatchPattern: + # pattern: 'src/sourcelink/.*' + # matchAny: true + # then: + # - requestReview: + # teamReviewer: PLACEHOLDER + # - description: + # if: + # - payloadType: Pull_Request + # - filesMatchPattern: + # pattern: 'src/symreader/.*' + # matchAny: true + # then: + # - requestReview: + # teamReviewer: PLACEHOLDER + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/templating/.*' + matchAny: true + then: + - requestReview: + teamReviewer: templating-engine-maintainers + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/vstest/.*' + matchAny: true + then: + - requestReview: + teamReviewer: testing-admin + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/windowsdesktop/.*' + matchAny: true + then: + - requestReview: + teamReviewer: wpf-developers + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/winforms/.*' + matchAny: true + then: + - requestReview: + teamReviewer: dotnet-winforms-admin + - description: + if: + - payloadType: Pull_Request + - filesMatchPattern: + pattern: 'src/wpf/.*' + matchAny: true + then: + - requestReview: + teamReviewer: wpf-developers diff --git a/.github/workflows/protected-files-validation.yml b/.github/workflows/protected-files-validation.yml deleted file mode 100644 index 2e7c8e3c1271..000000000000 --- a/.github/workflows/protected-files-validation.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: 'Check Protected Files for Changes' - -on: - pull_request: - # Update the error message when changing these paths - paths: - - eng/common/**/* - - eng/Version.Details.xml - - eng/Versions.props - -permissions: - pull-requests: read - -jobs: - check-protect-files: - runs-on: ubuntu-22.04 - if: ${{ github.event.pull_request.user.login != 'dotnet-sb-bot' && github.event.pull_request.user.login != 'dotnet-maestro[bot]' }} - steps: - - name: Protected File has Changes - run: | - echo "${{ github.event.pull_request.user.login }} cannot make changes to 'eng/Version.Details.xml', 'eng/Versions.props', and 'eng/common/'." - exit 1 diff --git a/.gitignore b/.gitignore index 1f77e38b1215..c5f20d339622 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ /prereqs/packages /src/nuget-client/NuGet.config *.binlog + +# Visual Studio +.vs/ \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index c7200938e920..749f68c6f687 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -61,8 +61,11 @@ true true - + + true + true + latest false diff --git a/Directory.Build.targets b/Directory.Build.targets index a2cc483d74e0..4d65de75c2a6 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,6 +1,7 @@ + @@ -37,72 +38,19 @@ - - dotnet-runtime- - + + + - + - - - - - %(RuntimeArchiveItem.Filename)%(RuntimeArchiveItem.Extension) - %(RuntimeArchiveItem.Identity) - $(RuntimeFilename.Replace('$(RuntimeFilenamePrefix)','').Replace('-$(TargetRid)$(ArchiveExtension)','')) + $([System.IO.Path]::GetFileName($([System.IO.Path]::GetDirectoryName('%(RuntimeVersionFile.Identity)')))) - - - - <_PreviouslySourceBuiltSharedComponentAssetManifests>$(SharedComponentsArtifactsPath)VerticalManifest.xml - ;@(SharedRepositoryReference); - - false - - - - - - - - - - <_ItemsToRemove Include="@(_SharedComponentFilteredPackages)" - Condition="!$(SharedRepositoryReferenceString.Contains(';%(RepoOrigin);')) or $([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';%(RepoOrigin);'))" /> - - - - - <_ItemsToRemove Include="@(_SharedComponentFilteredPackages)" - Condition="$(SharedRepositoryReferenceString.Contains(';%(RepoOrigin);')) and !$([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';%(RepoOrigin);'))" /> - - - - - <_ItemsToRemoveString>@(_ItemsToRemove->'%(Identity)::%(Version)', ';') - - - - <_SharedComponentFilteredPackages Remove="@(_SharedComponentFilteredPackages)" - Condition="'@(_ItemsToRemove)' != '' and - '$(_ItemsToRemoveString)' != '' and - $(_ItemsToRemoveString.Contains($([System.String]::Concat('%(Identity)', '::', '%(Version)'))))" /> - - - diff --git a/Directory.Packages.props b/Directory.Packages.props index d54bcf6c82dc..144830a2968b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,5 +1,8 @@ + + + true true @@ -10,6 +13,9 @@ + + + @@ -18,10 +24,15 @@ + + + + + @@ -29,7 +40,6 @@ - diff --git a/NuGet.config b/NuGet.config index 5d682a461ed3..2cdbcd1c13e1 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,12 +9,11 @@ + + + - - - - diff --git a/README.md b/README.md index 0afab37865af..70b39bebf18e 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ For the latest information about Source-Build support for new .NET versions, ple ## Code flow -The VMR's code flow operates in two directions. Individual repositories flow source changes into the VMR upon promotion of their local official builds (forward flow). The VMR changes are checked in, an official build happens, and then source changes + packages flows backward into the constituent repositories (back flow). For more details on code flow and code flow pull requests, please see this information on [Code Flow PRs](src/arcade/Documentation/UnifiedBuild/Codeflow-PRs.md). +The contents of the VMR are two-way synchronized with the product repositories via code flow PRs. Individual repositories flow source changes into the VMR upon promotion of their local official builds (forward flow). The VMR changes are checked in, an official build happens, and then source changes + packages flow backward into the constituent repositories (back flow). For more details on code flow and code flow pull requests, please see this information on [Code Flow PRs](docs/Codeflow-PRs.md). ## Contribution @@ -193,11 +193,11 @@ Usually, this means the [dotnet/dotnet repository](https://github.com/dotnet/dot In practice, this means that when calling the main build script, you need to provide additional arguments when building outside of a context of a git repository. Alternatively, you can also provide a manifest file where this information can be read from. This file (`release.json`) can be found attached with the [dotnet/dotnet release](https://github.com/dotnet/dotnet/releases). -### Synchronizing code into the VMR +### Manually synchronizing code with the VMR -Sometimes you want to make a change in a repository and test that change in the VMR. You could of course make the change in the VMR directly, but in case it's already available in your repository, you can synchronize it locally into your clone of the VMR, commit, and then open a PR. +Sometimes you want to make a change in a repository and test that change in the VMR locally (or vice versa). You could of course make the change in the VMR directly, but in case it's already available in your repository, you can synchronize it locally into your clone of the VMR, commit, and then open a PR. -To do this, you need to use the [`darc vmr forwardflow` command](https://github.com/dotnet/arcade-services/blob/main/docs/Darc.md#forwardflow) which can move your changes from your repository's dev branch into a local VMR one. Please refer to command's documentation (`--help`) for more details. +To do this, you need to use the [`darc vmr forwardflow`](https://github.com/dotnet/arcade-services/blob/main/docs/Darc.md#forwardflow) or [`darc vmr backflow`](https://github.com/dotnet/arcade-services/blob/main/docs/Darc.md#backflow) commands which can move your changes from your repository's dev branch into a local VMR one. Please refer to command's documentation (`--help`) for more details. ## Filing Issues diff --git a/SECURITY.md b/SECURITY.md index e0dfff56a956..fc35b099d319 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,11 +10,9 @@ If you believe you have found a security vulnerability in any Microsoft-owned re **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). +Please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). +You should receive a response within 24 hours. If for some reason you do not, please follow up via the [MSRC Researcher Portal](https://msrc.microsoft.com/report/vulnerability/), using the Message functionality found at the bottom of the Activity tab on your vulnerability report. Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index 5878c2c180e2..cdabd1859a1f 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -3270,57 +3270,6542 @@ Copyright: (c) Microsoft Corporation License: https://visualstudio.microsoft.com/license-terms/mt736442/ ############################################# -### roslyn-analyzers +### roslyn ############################################# -Roslyn-Analyzers uses third-party libraries or other resources that may be -distributed under licenses different than the Roslyn-Analyzers software. +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +--------------------------------------------------------- + +JSONTestSuite + +https://github.com/nst/JSONTestSuite + +Copyright (c) 2016 Nicolas Seriot + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +BasicUndo 0.9.3 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Castle.Core 4.3.1 - Apache-2.0 + + +(c) 2004-2018 Castle Project - http://www.castleproject.org +Copyright 2004-2016 Castle Project - http://www.castleproject.org +Copyright (c) 2004-2018 Castle Project - http://www.castleproject.org + +Copyright 2004-2016 Castle Project - http://www.castleproject.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +DiffPlex 1.7.2 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Common 6.3.4 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Configuration 6.3.4 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Frameworks 6.3.4 - Apache-2.0 + + +(c) Microsoft Corporation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Packaging 6.3.4 - Apache-2.0 + + +(c) Microsoft Corporation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Protocol 6.3.4 - Apache-2.0 + + +(c) Microsoft Corporation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Resolver 6.3.4 - Apache-2.0 + + +(c) Microsoft Corporation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.SolutionRestoreManager.Interop 4.8.0 - Apache-2.0 + + +(c) Microsoft Corporation. + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NuGet.Versioning 6.3.4 - Apache-2.0 + + +(c) Microsoft Corporation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +SQLitePCLRaw.bundle_green 2.1.0 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +SQLitePCLRaw.core 2.1.0 - Apache-2.0 + + +Copyright 2014-2022 SourceGear, LLC +Copyright 2014-2022 SourceGear, LLC SSQLitePCLRaw + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +SQLitePCLRaw.lib.e_sqlite3 2.1.0 - Apache-2.0 + + + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +SQLitePCLRaw.provider.dynamic_cdecl 2.1.0 - Apache-2.0 + + +Copyright 2014-2022 SourceGear, LLC +Copyright 2014-2022 SourceGear, LLC SSQLitePCLRaw + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +SQLitePCLRaw.provider.e_sqlite3 2.1.0 - Apache-2.0 + + +Copyright 2014-2022 SourceGear, LLC +Copyright 2014-2022 SourceGear, LLC SSQLitePCLRaw + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.abstractions 2.0.1 - Apache-2.0 + + +Copyright (c) Outercurve Foundation +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.abstractions 2.0.3 - Apache-2.0 + + +Copyright (c) Outercurve Foundation +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.analyzers 1.17.0 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.assert 2.9.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.core 2.9.2 - Apache-2.0 + + +Copyright (c) .NET Foundation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.extensibility.core 2.2.0 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation RSDSk C BuildAgent + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.extensibility.core 2.9.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net +Copyright (c) .NET Foundation 0xUnit.net +Copyright (c) .NET Foundation xUnit.net Runner Utility + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.extensibility.execution 2.9.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.runner.utility 2.9.2 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) .NET Foundation ,xUnit.net Runner Utility + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +xunit.runner.visualstudio 3.1.3 - Apache-2.0 + + +Copyright (c) .NET Foundation +Copyright (c) Outercurve Foundation + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Moq 4.10.1 - BSD-3-Clause + + +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +LibGit2Sharp.NativeBinaries 2.0.306 - GPL-2.0-only WITH GCC-exception-2.0 + + +Copyright Rich Salz. +Copyright 1995-2017 Mark Adler +Copyright (c) 2007 Francois Gouget +Copyright (c) 2011 by Vicent Marti +Copyright (c) 2011-2015 Vicent Marti +Copyright 1995-2017 Mark Adler +3 CScs DEFG +copyrighted by the Free Software Foundation +Copyright (c) 2017 Marc Stevens Cryptology Group +Copyright 2006-2010 The Apache Software Foundation +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright 2010 Volkan Yazici +Copyright (c) 1989, 1991 Free Software Foundation, Inc. +Copyright (c) 1991, 1999 Free Software Foundation, Inc. +Copyright (c) 1995-2010 Jean-loup Gailly and Mark Adler +CopyrightInfo AbstractInfo FormattingImplementationInfo +Copyright (c) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 Free Software Foundation, Inc. + +GPL-2.0-only WITH GCC-exception-2.0 + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VsSDK.CompatibilityAnalyzer 17.14.1043-preview2 - LGPL-2.1-or-later AND Apache-2.0 AND Apache-2.0 AND Apache-2.0 AND MPL-2.0 AND BSD-2-Clause AND BSD-3-Clause AND LicenseRef-scancode-ms-net-library-2019-06 AND LicenseRef-scancode-ms-net-library-2018-11 AND LicenseRef-scancode-ms-vs-addons-ext-17.2.0 AND MIT AND MS-PL AND MS-RL + + +(c) Andrew Arnott +copyright company +(c) Microsoft 2023 +(c) Microsoft 2024 +(c) Microsoft 2025 +Copyright (c) 2021 +(c) 2003-2004 Various +(c) 2019 GitHub, Inc. +(c) 2022 GitHub, Inc. +(c) 2023 GitHub, Inc. +(c) 2006 Entrust, Inc. +Copyright (c) Microsoft +Copyright 2015 The gRPC +Copyright 2019 The gRPC +Copyright Nate McMaster +Copyright (c) Six Labors +Copyright Microsoft 2015 +(c) Microsoft Corporation +Copyright (c) Hank McCord +Copyright (c) Manuel Romer +Copyright (c) Andrew Arnott +Copyright (c) Nate McMaster +Copyright 2015, Google Inc. +Copyright 2019 LLVM Project +(c) 1999 Entrust.net Limited +(c) 2009 Entrust, Inc. - for +(c) 2012 Entrust, Inc. - for +(c) 2015 Entrust, Inc. - for +Copyright (c) 1998 Microsoft +Copyright 2012 Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright Andrew Arnott 2012 +Copyright (c) .NET Foundation +Copyright (c) Six Labors Gets +Copyright (c) by P.J. Plauger +Copyright 2012 the V8 project +Copyright 1995-2017 Mark Adler +Copyright 1995-2022 Mark Adler +Copyright 2008 - 2018 Jb Evain +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +Copyright (c) 2023 Sergio Pedri +Copyright 2000-2022 SharpZipLib +(c) 1997-2005 Sean Eron Anderson +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright James Newton-King 2017 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) 2022, Wojciech Mula +Copyright 2012 Andrew Arnott RSA1 +Copyright 2012-2017 Mehdi Khalili +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2015 .NET Foundation +Copyright (c) 2015 Christian Klutz +Copyright (c) 2017 Marcos Lopez C. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright EvidenceFile FileVersion +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +(c) Yoshifumi Kawai and contributors +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) James Newton-King 2017 +Copyright Nate McMaster Command-line +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008 - 2011 Novell, Inc. +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright 2014 Giovanni Bassi and Elemar +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) Sven Groot (Ookii.org) 2009 +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright (c) 2021 Copyright (c) Six Labors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright Tunnel Vision Laboratories, LLC 2015 +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) Copyright (c) 2015 Dennis Fischer +Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 +Copyright AssemblyCompany AssemblyConfiguration +Copyright (c) 2005 - 2020 Giacomo Stelluti Scala +copyright tag should contain a non-empty company +Copyright (c) 2024 http://microsoft.com Microsoft +Copyright (c) 2017 Yoshifumi Kawai and contributors +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +(c) 2005 - 2020 Giacomo Stelluti Scala & Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright 2012-2016 Copyright 2012-2017 Mehdi Khalili +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) +(c) 2006 Entrust, Inc. Label Entrust Root Certification +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Unknown Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) Andrew Arnott InputValidation IntegrityCheck +(c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation xUnit.net Console Test Runner +Copyright (c) .NET Foundation -xUnit.net Console Test Runner +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2005 - 2015 Giacomo Stelluti Scala & Contributors +Copyright (c) 2005 - 2020 Giacomo Stelluti Scala & Contributors +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright 2004-2021 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) 2023 Sergio Pedri OriginalFilename PolySharp.SourceGenerators.dll +Copyright 2015, Google Inc. Protocol Buffers Binary Serialization Format Google +(c) Cure53 and other contributors Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +(c) 1999 Entrust.net Limited Label Entrust.net Premium 2048 Secure Server CA Serial +Copyright (c) .NET Foundation and Contributors. xUnit.net Testing Framework +xUnit.net +Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS. +The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft's privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give 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 these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is "as is", we may not provide support services for it. +7. ENTIRE AGREEMENT. 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. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS". YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +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. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.TeamFoundation.DistributedTask.Common.Contracts 19.232.0-preview - LicenseRef-scancode-ms-net-library-2018-11 + + +(c) Microsoft 2024 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT .NET LIBRARY + +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. + +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. + +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to design, develop and test you�re applications. You may modify, copy, distribute or deploy any .js files contained in the software as part of your applications. + +2. THIRD PARTY COMPONENTS. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. +a. DISTRIBUTABLE CODE. In addition to the .js files described above, the software is comprised of Distributable Code. �Distributable Code� is code that you are permitted to distribute in programs you develop if you comply with the terms below. +i. Right to Use and Distribute. +� You may copy and distribute the object code form of the software. + +� Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs. + +ii. Distribution Requirements. For any Distributable Code you distribute, you must +� use the Distributable Code in your programs and not as a standalone distribution; + +� require distributors and external end users to agree to terms that protect it at least as much as this agreement; + +� display your valid copyright notice on your programs; and + +� indemnify, defend, and hold harmless Microsoft from any claims, including attorneys� fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the Distributable Code. + +iii. Distribution Restrictions. You may not +� alter any copyright, trademark or patent notice in the Distributable Code; + +� use Microsoft�s trademarks in your programs� names or in a way that suggests your programs come from or are endorsed by Microsoft; + +� include Distributable Code in malicious, deceptive or unlawful programs; or + +� 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 + +� the code be disclosed or distributed in source code form; or + +� others have the right to modify it. + +4. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft�s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at http://go.microsoft.com/?linkid=9840733. +5. SCOPE OF LICENSE. 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 +� work around any technical limitations in the software; + +� reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included in the software; + +� remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; + +� use the software in any way that is against the law; or + +� share, publish, rent or lease the software, provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party. + +6. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +7. SUPPORT SERVICES. Because this software is �as is,� we may not provide support services for it. +8. ENTIRE AGREEMENT. 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. +9. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +10. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. +c) Germany and Austria. +(i) Warranty. The software will perform substantially as described in any Microsoft materials that accompany it. However, Microsoft gives no contractual guarantee in relation to the software. + +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as in case of death or personal or physical injury, Microsoft is liable according to the statutory law. + +Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence +11. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS.� YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +12. 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. + +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 state or country may not allow the exclusion or limitation of incidental, consequential or other damages. + +Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. + + + +Remarque : Ce logiciel �tant distribu� au Qu�bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran�ais. + + + +EXON�RATION DE GARANTIE. Le logiciel vis� par une licence est offert � tel quel �. Toute utilisation de ce logiciel est � votre seule risque et p�ril. Microsoft n�accorde aucune autre garantie expresse. Vous pouvez b�n�ficier 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� marchande, d�ad�quation � un usage particulier et d�absence de contrefa�on sont exclues. + + + +LIMITATION DES DOMMAGES-INT�R�TS ET EXCLUSION DE RESPONSABILIT� POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement � hauteur de 5,00 $ US. Vous ne pouvez pr�tendre � aucune indemnisation pour les autres dommages, y compris les dommages sp�ciaux, indirects ou accessoires et pertes de b�n�fices. + + + +Cette limitation concerne: + +� tout ce qui est reli� au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et + +� les r�clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit� stricte, de n�gligence ou d�une autre faute dans la limite autoris�e par la loi en vigueur. + + + +Elle s�applique �galement, m�me si Microsoft connaissait ou devrait conna�tre l��ventualit� d�un tel dommage. Si votre pays n�autorise pas l�exclusion ou la limitation de responsabilit� pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l�exclusion ci-dessus ne s�appliquera pas � votre �gard. + + + +EFFET JURIDIQUE. Le pr�sent contrat d�crit certains droits juridiques. Vous pourriez avoir d�autres droits pr�vus par les lois de votre pays. Le pr�sent contrat ne modifie pas les droits que vous conf�rent les lois de votre pays si celles-ci ne le permettent pas. + + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.TeamFoundationServer.Client 19.232.0-preview - LicenseRef-scancode-ms-net-library-2018-11 + + +(c) Microsoft 2023 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT .NET LIBRARY + +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. + +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. + +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to design, develop and test you�re applications. You may modify, copy, distribute or deploy any .js files contained in the software as part of your applications. + +2. THIRD PARTY COMPONENTS. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. +a. DISTRIBUTABLE CODE. In addition to the .js files described above, the software is comprised of Distributable Code. �Distributable Code� is code that you are permitted to distribute in programs you develop if you comply with the terms below. +i. Right to Use and Distribute. +� You may copy and distribute the object code form of the software. + +� Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs. + +ii. Distribution Requirements. For any Distributable Code you distribute, you must +� use the Distributable Code in your programs and not as a standalone distribution; + +� require distributors and external end users to agree to terms that protect it at least as much as this agreement; + +� display your valid copyright notice on your programs; and + +� indemnify, defend, and hold harmless Microsoft from any claims, including attorneys� fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the Distributable Code. + +iii. Distribution Restrictions. You may not +� alter any copyright, trademark or patent notice in the Distributable Code; + +� use Microsoft�s trademarks in your programs� names or in a way that suggests your programs come from or are endorsed by Microsoft; + +� include Distributable Code in malicious, deceptive or unlawful programs; or + +� 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 + +� the code be disclosed or distributed in source code form; or + +� others have the right to modify it. + +4. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft�s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at http://go.microsoft.com/?linkid=9840733. +5. SCOPE OF LICENSE. 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 +� work around any technical limitations in the software; + +� reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included in the software; + +� remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; + +� use the software in any way that is against the law; or + +� share, publish, rent or lease the software, provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party. + +6. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +7. SUPPORT SERVICES. Because this software is �as is,� we may not provide support services for it. +8. ENTIRE AGREEMENT. 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. +9. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +10. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. +c) Germany and Austria. +(i) Warranty. The software will perform substantially as described in any Microsoft materials that accompany it. However, Microsoft gives no contractual guarantee in relation to the software. + +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as in case of death or personal or physical injury, Microsoft is liable according to the statutory law. + +Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence +11. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS.� YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +12. 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. + +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 state or country may not allow the exclusion or limitation of incidental, consequential or other damages. + +Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. + + + +Remarque : Ce logiciel �tant distribu� au Qu�bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran�ais. + + + +EXON�RATION DE GARANTIE. Le logiciel vis� par une licence est offert � tel quel �. Toute utilisation de ce logiciel est � votre seule risque et p�ril. Microsoft n�accorde aucune autre garantie expresse. Vous pouvez b�n�ficier 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� marchande, d�ad�quation � un usage particulier et d�absence de contrefa�on sont exclues. + + + +LIMITATION DES DOMMAGES-INT�R�TS ET EXCLUSION DE RESPONSABILIT� POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement � hauteur de 5,00 $ US. Vous ne pouvez pr�tendre � aucune indemnisation pour les autres dommages, y compris les dommages sp�ciaux, indirects ou accessoires et pertes de b�n�fices. + + + +Cette limitation concerne: + +� tout ce qui est reli� au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et + +� les r�clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit� stricte, de n�gligence ou d�une autre faute dans la limite autoris�e par la loi en vigueur. + + + +Elle s�applique �galement, m�me si Microsoft connaissait ou devrait conna�tre l��ventualit� d�un tel dommage. Si votre pays n�autorise pas l�exclusion ou la limitation de responsabilit� pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l�exclusion ci-dessus ne s�appliquera pas � votre �gard. + + + +EFFET JURIDIQUE. Le pr�sent contrat d�crit certains droits juridiques. Vous pourriez avoir d�autres droits pr�vus par les lois de votre pays. Le pr�sent contrat ne modifie pas les droits que vous conf�rent les lois de votre pays si celles-ci ne le permettent pas. + + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Services.Client 19.232.0-preview - LicenseRef-scancode-ms-net-library-2018-11 + + +(c) Microsoft 2024 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT .NET LIBRARY + +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. + +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. + +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to design, develop and test you�re applications. You may modify, copy, distribute or deploy any .js files contained in the software as part of your applications. + +2. THIRD PARTY COMPONENTS. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. +a. DISTRIBUTABLE CODE. In addition to the .js files described above, the software is comprised of Distributable Code. �Distributable Code� is code that you are permitted to distribute in programs you develop if you comply with the terms below. +i. Right to Use and Distribute. +� You may copy and distribute the object code form of the software. + +� Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs. + +ii. Distribution Requirements. For any Distributable Code you distribute, you must +� use the Distributable Code in your programs and not as a standalone distribution; + +� require distributors and external end users to agree to terms that protect it at least as much as this agreement; + +� display your valid copyright notice on your programs; and + +� indemnify, defend, and hold harmless Microsoft from any claims, including attorneys� fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the Distributable Code. + +iii. Distribution Restrictions. You may not +� alter any copyright, trademark or patent notice in the Distributable Code; + +� use Microsoft�s trademarks in your programs� names or in a way that suggests your programs come from or are endorsed by Microsoft; + +� include Distributable Code in malicious, deceptive or unlawful programs; or + +� 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 + +� the code be disclosed or distributed in source code form; or + +� others have the right to modify it. + +4. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft�s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at http://go.microsoft.com/?linkid=9840733. +5. SCOPE OF LICENSE. 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 +� work around any technical limitations in the software; + +� reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included in the software; + +� remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; + +� use the software in any way that is against the law; or + +� share, publish, rent or lease the software, provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party. + +6. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +7. SUPPORT SERVICES. Because this software is �as is,� we may not provide support services for it. +8. ENTIRE AGREEMENT. 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. +9. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +10. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. +c) Germany and Austria. +(i) Warranty. The software will perform substantially as described in any Microsoft materials that accompany it. However, Microsoft gives no contractual guarantee in relation to the software. + +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as in case of death or personal or physical injury, Microsoft is liable according to the statutory law. + +Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence +11. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS.� YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +12. 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. + +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 state or country may not allow the exclusion or limitation of incidental, consequential or other damages. + +Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. + + + +Remarque : Ce logiciel �tant distribu� au Qu�bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran�ais. + + + +EXON�RATION DE GARANTIE. Le logiciel vis� par une licence est offert � tel quel �. Toute utilisation de ce logiciel est � votre seule risque et p�ril. Microsoft n�accorde aucune autre garantie expresse. Vous pouvez b�n�ficier 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� marchande, d�ad�quation � un usage particulier et d�absence de contrefa�on sont exclues. + + + +LIMITATION DES DOMMAGES-INT�R�TS ET EXCLUSION DE RESPONSABILIT� POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement � hauteur de 5,00 $ US. Vous ne pouvez pr�tendre � aucune indemnisation pour les autres dommages, y compris les dommages sp�ciaux, indirects ou accessoires et pertes de b�n�fices. + + + +Cette limitation concerne: + +� tout ce qui est reli� au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et + +� les r�clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit� stricte, de n�gligence ou d�une autre faute dans la limite autoris�e par la loi en vigueur. + + + +Elle s�applique �galement, m�me si Microsoft connaissait ou devrait conna�tre l��ventualit� d�un tel dommage. Si votre pays n�autorise pas l�exclusion ou la limitation de responsabilit� pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l�exclusion ci-dessus ne s�appliquera pas � votre �gard. + + + +EFFET JURIDIQUE. Le pr�sent contrat d�crit certains droits juridiques. Vous pourriez avoir d�autres droits pr�vus par les lois de votre pays. Le pr�sent contrat ne modifie pas les droits que vous conf�rent les lois de votre pays si celles-ci ne le permettent pas. + + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Debugger.InteropA 17.0.31902.203 - LicenseRef-scancode-ms-vs-addons-ext-17.2.0 + + +(c) Microsoft 2024 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS.� The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software.� +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft�s privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give 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 these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is �as is�, we may not provide support services for it. +7. ENTIRE AGREEMENT. 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. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS�. YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +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. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Interop 17.0.31902.203 - LicenseRef-scancode-ms-vs-addons-ext-17.2.0 + + +(c) Microsoft 2024 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS.� The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software.� +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft�s privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give 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 these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is �as is�, we may not provide support services for it. +7. ENTIRE AGREEMENT. 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. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS�. YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +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. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VSSDK.BuildTools 17.14.1043-preview2 - LicenseRef-scancode-ms-vs-addons-ext-17.2.0 + + +(c) Andrew Arnott +copyright company +(c) Microsoft 2023 +(c) Microsoft 2024 +(c) Microsoft 2025 +Copyright (c) 2021 +(c) 2003-2004 Various +(c) 2019 GitHub, Inc. +(c) 2022 GitHub, Inc. +(c) 2023 GitHub, Inc. +(c) 2006 Entrust, Inc. +Copyright (c) Microsoft +Copyright 2015 The gRPC +Copyright 2019 The gRPC +Copyright Nate McMaster +Copyright (c) Six Labors +Copyright Microsoft 2010 +Copyright Microsoft 2015 +(c) Microsoft Corporation +Copyright (c) Hank McCord +Copyright (c) Manuel Romer +Copyright (c) Andrew Arnott +Copyright (c) Nate McMaster +Copyright 2015, Google Inc. +Copyright 2019 LLVM Project +(c) 1999 Entrust.net Limited +(c) 2009 Entrust, Inc. - for +(c) 2012 Entrust, Inc. - for +(c) 2015 Entrust, Inc. - for +Copyright (c) 1998 Microsoft +Copyright 2012 Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright Andrew Arnott 2012 +Copyright (c) .NET Foundation +Copyright (c) Six Labors Gets +Copyright (c) by P.J. Plauger +Copyright 2012 the V8 project +Copyright 1995-2017 Mark Adler +Copyright 1995-2022 Mark Adler +Copyright 2008 - 2018 Jb Evain +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +Copyright (c) 2023 Sergio Pedri +Copyright 2000-2022 SharpZipLib +(c) 1997-2005 Sean Eron Anderson +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright James Newton-King 2017 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) 2022, Wojciech Mula +Copyright 2012 Andrew Arnott RSA1 +Copyright 2012-2017 Mehdi Khalili +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2015 .NET Foundation +Copyright (c) 2015 Christian Klutz +Copyright (c) 2017 Marcos Lopez C. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright EvidenceFile FileVersion +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +Copyright Microsoft 2010 TraceEvent +(c) Yoshifumi Kawai and contributors +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) James Newton-King 2017 +Copyright Nate McMaster Command-line +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008 - 2011 Novell, Inc. +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright Microsoft 2010 Serialization +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright 2014 Giovanni Bassi and Elemar +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) Sven Groot (Ookii.org) 2009 +Copyright (c) The Internet Society (2003) +Copyright 1995-2003 Microsoft Corporation +Copyright 1996-1997 Microsoft Corporation +Copyright 1996-2000 Microsoft Corporation +Copyright 1996-2003 Microsoft Corporation +Copyright 1997-1998 Microsoft Corporation +Copyright 1997-2002 Microsoft Corporation +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright (c) 2021 Copyright (c) Six Labors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 1997-2000, Microsoft Corporation +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright Tunnel Vision Laboratories, LLC 2015 +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) Copyright (c) 2015 Dennis Fischer +Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 +Copyright AssemblyCompany AssemblyConfiguration +Copyright (c) 2005 - 2020 Giacomo Stelluti Scala +copyright tag should contain a non-empty company +Copyright (c) 2024 http://microsoft.com Microsoft +Copyright (c) 2017 Yoshifumi Kawai and contributors +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 1998 - 2003 Microsoft Corporation Inc. +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright Microsoft 2010 Operating System Extensions +(c) 2005 - 2020 Giacomo Stelluti Scala & Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright 2012-2016 Copyright 2012-2017 Mehdi Khalili +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) +(c) 2006 Entrust, Inc. Label Entrust Root Certification +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Unknown Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) Andrew Arnott InputValidation IntegrityCheck +(c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation xUnit.net Console Test Runner +Copyright (c) .NET Foundation -xUnit.net Console Test Runner +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2005 - 2015 Giacomo Stelluti Scala & Contributors +Copyright (c) 2005 - 2020 Giacomo Stelluti Scala & Contributors +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright 2004-2021 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) 2023 Sergio Pedri OriginalFilename PolySharp.SourceGenerators.dll +Copyright 2015, Google Inc. Protocol Buffers Binary Serialization Format Google +(c) Cure53 and other contributors Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +(c) 1999 Entrust.net Limited Label Entrust.net Premium 2048 Secure Server CA Serial +Copyright (c) .NET Foundation and Contributors. xUnit.net Testing Framework +xUnit.net +Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS. +The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft's privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give 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 these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is "as is", we may not provide support services for it. +7. ENTIRE AGREEMENT. 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. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS". YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +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. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.NETFramework.ReferenceAssemblies.net472 1.0.3 - LicenseRef-scancode-unknown + + +(c) 2024 GitHub,  Inc. +Copyright Init Default +(c) Microsoft Corporation +Copyright SevenBit SetBit +(c) Communication Foundation +Copyright (c) .NET Foundation +Copyright (c) Microsoft Corp. +Copyright PagefileLimit TimeLimit +Copyright (c) Microsoft Corporation +Copyright Microsoft Corp. 1990-2001 +Copyright (c) Microsoft Corporation 2005 +Copyright (c) 1998 Hewlett-Packard Company +Copyright (c) 2019 Microsoft,','Permission +CCopyright (c) Microsoft Corporation 1996-2005 + +LicenseRef-scancode-unknown + +--------------------------------------------------------- + +--------------------------------------------------------- + +Azure.Core 1.25.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Azure.Core 1.41.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Azure.Storage.Blobs 12.13.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Azure.Storage.Common 12.12.0 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net100 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net20 1.8.3 - MIT + + +Copyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation 1996-2005 + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net40 1.8.3 - MIT + + +Copyright SevenBit SetBit +(c) Communication Foundation +Copyright (c) Microsoft Corp. +Copyright PagefileLimit TimeLimit +Copyright ConvertExplicit Constant +Copyright (c) Microsoft Corporation +Copyright (c) Microsoft Corporation 2005 +Copyright (c) 1998 Hewlett-Packard Company +CCopyright (c) Microsoft Corporation 1996-2005 +Copyright (c) 1997 Microsoft Corp.1 Microsoft Corporation1!0 Microsoft Root + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net461 1.8.3 - MIT + + +Copyright Init Default +Copyright SevenBit SetBit +(c) Communication Foundation +Copyright (c) Microsoft Corp. +Copyright PagefileLimit TimeLimit +Copyright (c) Microsoft Corporation +Copyright (c) Microsoft Corporation 2005 +Copyright (c) 1998 Hewlett-Packard Company +CCopyright (c) Microsoft Corporation 1996-2005 + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net50 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net60 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net70 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net80 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.Net90 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.NetStandard13 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Basic.Reference.Assemblies.NetStandard20 1.8.3 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Humanizer.Core 2.14.1 - MIT + + +Copyright .NET Foundation and Contributors +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +ICSharpCode.Decompiler 9.1.0.7988 - MIT + + +Copyright 2011-2025 AlphaSierraPapa C Decompiler +Copyright 2011-2025 AlphaSierraPapa ICSharpCode.Decompiler +Copyright 2011-2025 AlphaSierraPapa LegalTrademarks OriginalFilename + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +LibGit2Sharp 0.26.2 - MIT + + +Copyright LibGit2Sharp contributors +Copyright (c) LibGit2Sharp contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +MessagePack 2.5.198 - MIT + + +(c) Yoshifumi Kawai and contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +MessagePack.Annotations 2.5.198 - MIT + + +(c) Yoshifumi Kawai and contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +MessagePackAnalyzer 2.5.124 - MIT + + +(c) Yoshifumi Kawai and contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Build.Locator 1.8.1 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Common 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.CSharp 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.CSharp.Features 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.CSharp.Workspaces 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Elfie 1.0.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Features 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Scripting.Common 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.VisualBasic 4.12.0 - MIT + + +(c) Key (c) +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.VisualBasic.Features 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.VisualBasic.Workspaces 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Workspaces.Common 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Workspaces.MSBuild 4.12.0 - MIT + + +(c) Microsoft Corporation +Copyright James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Identity.Client 4.61.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.IdentityModel.JsonWebTokens 6.34.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.IdentityModel.Logging 6.34.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.IdentityModel.Tokens 6.34.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.ILVerification 9.0.7 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.IO.Redist 6.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.IO.Redist 6.1.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.NET.StringTools 17.6.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) 2015 Christian Klutz +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.TestPlatform.TranslationLayer 17.13.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2007 James Newton-King +Copyright (c) 2008 - 2011 Novell, Inc. +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualBasic 10.3.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 1991-2017 Unicode, Inc. +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Composition 16.1.8 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Composition.NetFxAttributes 16.1.8 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.SDK.Analyzers 17.7.47 - MIT + + +(c) Andrew Arnott +copyright company +(c) 2019 GitHub, Inc. +(c) 2023 GitHub, Inc. +Copyright (c) Microsoft +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright 2012 the V8 project +Copyright 1995-2022 Mark Adler +Copyright 2008 - 2018 Jb Evain +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2015 Christian Klutz +Copyright (c) 2017 Marcos Lopez C. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +(c) Yoshifumi Kawai and contributors +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2008 - 2011 Novell, Inc. +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright 2014 Giovanni Bassi and Elemar Jr +Copyright (c) .NET Foundation and Contributors +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright Tunnel Vision Laboratories, LLC 2015 +Copyright (c) Copyright (c) 2015 Dennis Fischer +copyright tag should contain a non-empty company +Copyright (c) 2017 Yoshifumi Kawai and contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright 2012-2016 Copyright 2012-2017 Mehdi Khalili +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright 1995-2022 Mark Adler +3 CScs 0123456789ABCDEF +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) .NET Foundation xUnit.net Console Test Runner +Copyright (c) .NET Foundation -xUnit.net Console Test Runner +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +Copyright (c) .NET Foundation and Contributors. xUnit.net Testing Framework +xUnit.net +Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic Analyzer +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 NAn +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic Analyzer +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.SolutionPersistence 1.0.52 - MIT + + +(c) Andrew Arnott +copyright company +Copyright (c) 2021 +(c) 2022 GitHub, Inc. +(c) 2023 GitHub, Inc. +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright 1995-2022 Mark Adler +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +Copyright (c) 2023 Sergio Pedri +(c) 1997-2005 Sean Eron Anderson +Copyright James Newton-King 2008 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Marcos Lopez C. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright 2014 Giovanni Bassi and Elemar +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) Tunnel Vision Laboratories, LLC +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright Tunnel Vision Laboratories, LLC 2015 +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) Copyright (c) 2015 Dennis Fischer +Copyright AssemblyCompany AssemblyConfiguration +copyright tag should contain a non-empty company +Copyright (c) 2024 http://microsoft.com Microsoft +Copyright 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) 2023 Sergio Pedri OriginalFilename PolySharp.SourceGenerators.dll +Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Threading.Only 17.14.15 - MIT + + +(c) Andrew Arnott +copyright company +(c) Microsoft 2024 +Copyright (c) 2021 +(c) 2023 GitHub, Inc. +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) Manuel Romer +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +(c) (c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright (c) Six Labors Gets +Copyright 2012 the V8 project +Copyright 1995-2017 Mark Adler +Copyright 1995-2022 Mark Adler +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +Copyright (c) 2023 Sergio Pedri +Copyright 2020 Aaron R Robinson +Copyright 2023 Aaron R Robinson +copyright IsUncaught FileSought +(c) 1997-2005 Sean Eron Anderson +Copyright (c) Rackspace, US Inc. +Copyright James Newton-King 2008 +Copyright (c) 2013 Scott Kirkland +Copyright (c) 2014, Karlis Gangis +Copyright (c) 2015 Dennis Fischer +Copyright (c) 2022, Wojciech Mula +Copyright 2012-2017 Mehdi Khalili +Copyright (c) 2015 .NET Foundation +Copyright (c) 2017 Marcos Lopez C. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) Outercurve Foundation +Copyright LibGit2Sharp contributors +(c) Yoshifumi Kawai and contributors +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) James Newton-King 2008 +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2012-2014 Mehdi Khalili +Copyright (c) 2013-2014 Omar Khudeira +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) .NET Foundation xUnit.net +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) .NET Foundation 0xUnit.net +Copyright 2014 Giovanni Bassi and Elemar +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright James Newton-King 2008 Json.NET +Copyright (c) .NET Foundation Contributors +Copyright .NET Foundation and Contributors +Copyright 2012-2016 (c) 2008 VeriSign, Inc. +Copyright (c) 2020 Mara Bos +(c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) Tunnel Vision Laboratories, LLC. +Copyright Tunnel Vision Laboratories, LLC 2015 +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) Copyright (c) 2015 Dennis Fischer +Copyright AssemblyCompany AssemblyConfiguration +copyright tag should contain a non-empty company +Copyright (c) 2024 http://microsoft.com Microsoft +Copyright 1995-2017 Jean-loup Gailly and Mark Adler +Copyright 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) .NET Foundation xUnit.net Runner Utility +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) .NET Foundation xUnit.net Runner Reporters +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) Antoine Aubry and contributors 2008 - 2019 +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +(c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright 2004-2021 Castle Project - http://www.castleproject.org +Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright (c) 2023 Sergio Pedri OriginalFilename PolySharp.SourceGenerators.dll +Copyright (c) .NET Foundation xunit.analyzers, analyzers, roslyn, xunit, xunit.net +Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors +Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors +Copyright 2018 Tunnel Vision Laboratories, LLC Documentation DotNetAnalyzers Roslyn Diagnostic +Copyright Tunnel Vision Laboratories, LLC 2018 1Copyright Tunnel Vision Laboratories, LLC 2018 +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Validation 15.0.82 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudioEng.MicroBuild.Core 1.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) 2024 http://microsoft.com Microsoft + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Mono.Options 6.6.0.161 - MIT + + +(c) Microsoft Corporation. +Copyright (c) 1990-2009 Info-ZIP. +Copyright (c) .NET Foundation Contributors +copyrighted by the Free Software Foundation +Copyright (c) 1989, 1991 Free Software Foundation, Inc. +Copyright 2006 James Newton-King http://www.newtonsoft.com + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +MSBuild.StructuredLogger 2.2.386 - MIT + + + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Nerdbank.Streams 2.12.87 - MIT + + +(c) Andrew Arnott +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +NETStandard.Library 2.0.0 - MIT + + +copyright Unmanaged32Bit Required32Bit +Copyright (c) .NET Foundation and Contributors + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +NETStandard.Library 2.0.3 - MIT + + +copyright Unmanaged32Bit Required32Bit +Copyright (c) .NET Foundation and Contributors + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.1 - MIT + + +Copyright James Newton-King 2008 +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.3 - MIT + + +Copyright James Newton-King 2008 +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json.Bson 1.0.1 - MIT + + +Copyright James Newton-King 2017 +Copyright (c) James Newton-King 2017 + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.native.System.Data.SqlClient.sni 4.7.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Buffers 4.5.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Buffers 4.6.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ClientModel 1.0.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Collections.Immutable 9.0.0 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ComponentModel.Composition 4.5.0 - MIT + + +(c) 2025 GitHub,  Inc. +(c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Data.DataSetExtensions 4.5.0 - MIT + + +(c) 2025 GitHub,  Inc. +(c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Diagnostics.DiagnosticSource 9.0.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.IdentityModel.Tokens.Jwt 6.34.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Memory 4.5.4 - MIT + + +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Memory 4.5.5 - MIT + + +(c) 2022 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Memory 4.6.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Memory.Data 1.0.2 - MIT + + +(c) Microsoft Corporation. + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Numerics.Vectors 4.5.0 - MIT + + +(c) 2023 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Numerics.Vectors 4.6.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Emit 4.7.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Emit.ILGeneration 4.7.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Emit.Lightweight 4.7.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Metadata 9.0.0 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +Gets the Copyright Table +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Runtime.CompilerServices.Unsafe 6.1.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.AccessControl 6.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2020 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Cryptography.ProtectedData 4.4.0 - MIT + + +(c) Microsoft Corporation. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 1991-2017 Unicode, Inc. +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Permissions 4.5.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 1991-2017 Unicode, Inc. +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Text.Encodings.Web 4.7.2 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) -In the event that we accidentally failed to list a required notice, please -bring it to our attention. Post an issue or email us: +Copyright (c) .NET Foundation and Contributors - dotnet@microsoft.com +All rights reserved. -The attached notices are provided for information only. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -License notice for MSBuild Locator -------------------------------------- +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -https://github.com/Microsoft/MSBuildLocator -Copyright (c) 2018 .NET Foundation and Contributors +--------------------------------------------------------- -This software is licensed subject to the MIT license, available at -https://opensource.org/licenses/MIT +--------------------------------------------------------- -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +System.Threading.Tasks.Dataflow 9.0.0 - MIT -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +The MIT License (MIT) -License notice for Roslyn Clr Heap Allocation Analyzer -------------------------------------- +Copyright (c) .NET Foundation and Contributors -https://github.com/Microsoft/RoslynClrHeapAllocationAnalyzer +All rights reserved. -Copyright (c) 2018 Microsoft Corporation +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -http://www.apache.org/licenses/LICENSE-2.0 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +--------------------------------------------------------- -License notice for StyleCop Analyzers -------------------------------------- +--------------------------------------------------------- + +System.Threading.Tasks.Extensions 4.5.4 - MIT + + +(c) 2023 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) -Copyright (c) Tunnel Vision Laboratories, LLC +Copyright (c) .NET Foundation and Contributors All rights reserved. @@ -3343,29 +9828,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -############################################# -### roslyn -############################################# - -Roslyn uses third-party libraries or other resources that may be -distributed under licenses different than the Roslyn software. - -In the event that we accidentally failed to list a required notice, please -bring it to our attention. Post an issue or email us: +--------------------------------------------------------- - dotnet@microsoft.com +--------------------------------------------------------- -The attached notices are provided for information only. +System.Threading.Tasks.Extensions 4.6.0 - MIT -License notice for .NET Core Libraries (CoreFX) -------------------------------------- -https://github.com/dotnet/corefx +(c) Microsoft Corporation -Copyright (c) 2018 .NET Foundation and Contributors +MIT License -This software is licensed subject to the MIT license, available at -https://opensource.org/licenses/MIT +Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -3373,37 +9847,69 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- -License notice for DotNetTools -------------------------------------- +--------------------------------------------------------- + +System.ValueTuple 4.5.0 - MIT + + +(c) 2023 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers -https://github.com/aspnet/DotNetTools +The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - http://www.apache.org/licenses/LICENSE-2.0 +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -License notice for DocFX -------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +vswhere 3.1.7 - MIT -https://github.com/dotnet/docfx +(c) 2023 GitHub, Inc. +(c) Microsoft Corporation +Copyright (c) by P.J. Plauger Copyright (c) Microsoft Corporation -This software is licensed subject to the MIT license, available at -https://opensource.org/licenses/MIT +MIT License + +Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -3411,16 +9917,18 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- -License notice for MSBuild Locator -------------------------------------- +--------------------------------------------------------- -https://github.com/Microsoft/MSBuildLocator +XunitXml.TestLogger 3.1.17 - MIT -Copyright (c) Microsoft Corporation. All rights reserved. -This software is licensed subject to the MIT license, available at -https://opensource.org/licenses/MIT +Copyright 2020 Spekt Contributors + +MIT License + +Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -3428,73 +9936,164 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- -License notice for Json.NET -------------------------------------- +--------------------------------------------------------- -https://github.com/JamesNK/Newtonsoft.Json +InputSimulatorPlus 1.0.7 - MS-PL -Copyright (c) 2007 James Newton-King -This software is licensed subject to the MIT license, available at -https://opensource.org/licenses/MIT +Copyright 2009-2018 +Copyright michaelnoonan 2010 +LCopyright michaelnoonan 2010 +Copyright Theodoros Chatzigiannakis 2018 -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Microsoft Public License (Ms-PL) -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + 1. Definitions + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. -JSON Parsing Test Suite -------------------------------------- + 2. Grant of Rights -https://github.com/nst/JSONTestSuite + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. -Copyright (c) 2016 Nicolas Seriot + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + 3. Conditions and Limitations -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. -MSBuild Structured Logging -------------------------------------- + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. -https://github.com/KirillOsenkov/MSBuildStructuredLog + (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -Copyright (c) 2016 Kirill Osenkov +--------------------------------------------------------- -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +--------------------------------------------------------- -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +UIAComWrapper 1.1.0.14 - MS-PL -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -ICSharpCode.Decompiler -------------------------------------- -https://github.com/icsharpcode/ILSpy +Microsoft Public License (Ms-PL) -Copyright (c) 2011-2023 AlphaSierraPapa for the ILSpy team +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, subject to the following conditions: + 1. Definitions + + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. + + 2. Grant of Rights + + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + + 3. Conditions and Limitations + + (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + + (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Xunit.Combinatorial 1.6.24 - MS-PL + + +(c) Andrew Arnott + +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + + 1. Definitions + + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. + + 2. Grant of Rights + + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + + 3. Conditions and Limitations + + (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + + (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.VisualStudio.Debugger.Interop.11.0 17.0.31902.203 + + +(c) Microsoft 2024 +(c) Microsoft Corporation + +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS.� The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software.� +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft�s privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give 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 these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is �as is�, we may not provide support services for it. +7. ENTIRE AGREEMENT. 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. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED �AS-IS�. YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF 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. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +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. + + +--------------------------------------------------------- -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. ############################################# ### runtime @@ -3604,33 +10203,6 @@ http://www.apache.org/licenses/ Copyright The OpenTelemetry Authors -License notice for LinuxTracepoints ------------------------------------ - -https://github.com/microsoft/LinuxTracepoints/blob/main/LICENSE - -Copyright (c) Microsoft Corporation. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - License notice for Mono ------------------------------- diff --git a/build.proj b/build.proj index 548f325cc2c2..fd8460bc80d7 100644 --- a/build.proj +++ b/build.proj @@ -9,6 +9,14 @@ + + + + + - diff --git a/build.sh b/build.sh index 01556a9f9f03..35ba7a0c975f 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,12 @@ usage() echo " --rid, --target-rid Overrides the rid that is produced by the build. e.g. alpine.3.18-arm64, fedora.37-x64, freebsd.13-arm64, ubuntu.19.10-x64" echo " --os, --target-os Target operating system: e.g. linux, osx, freebsd. Note: this is the base OS name, not the distro" echo " --arch, --target-arch Target architecture: e.g. x64, x86, arm64, arm, riscv64" - echo " --branding Specify versioning for shipping packages/assets. 'preview' will produce assets suffixed with '.final', 'rtm' will not contain a pre-release suffix. Default or unspecified will use VMR repo defaults." + echo " --branding Specify the branding suffix for shipping packages/assets. By default uses VMR branding defaults (RepoDotNetFinalVersionKind property in eng/Versions.props)" + echo " for release branch builds or otherwise repo branding defaults." + echo " 'repodefault' uses the repo branding defaults." + echo " 'unstable' produces assets with the repo specified branding suffix." + echo " 'preview' produces assets with a '.final' branding suffix." + echo " 'release' produces assets without a branding suffix." echo " --verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)" echo " --with-system-libs Use system versions of these libraries. Combine with a plus. 'all' will use all supported libraries. e.g. brotli+libunwind+rapidjson+zlib" echo " --official-build-id Official build ID to use for the build. This is used to set the OfficialBuildId MSBuild property." @@ -59,23 +64,6 @@ usage() echo "Arguments can also be passed in with a single hyphen." } -function SetBranding() -{ - local brandingValue="$1" - - if [[ "$brandingValue" == "preview" ]]; then - properties+=( "/p:DotNetFinalVersionKind=prerelease" ) - elif [[ "$brandingValue" == "rtm" ]]; then - properties+=( "/p:DotNetFinalVersionKind=release" ) - elif [[ "$brandingValue" == "default" ]]; then - # default branding; no extra property needed - : - else - echo "ERROR: Invalid branding '$brandingValue'. Allowed values are 'preview', 'rtm', or 'default'." - exit 1 - fi -} - function SetOfficialBuildId() { local officialBuildIdValue="$1" @@ -103,19 +91,19 @@ binary_log='' configuration='Release' verbosity='minimal' officialBuildId='' -branding='' # Actions clean=false test=false # Source-only settings +prep=false sourceOnly=false releaseManifest='' sourceRepository='' sourceVersion='' -CUSTOM_PACKAGES_DIR='' -CUSTOM_SDK_DIR='' +customPackagesDir='' +customSdkDir='' packagesDir="$scriptroot/prereqs/packages/" packagesArchiveDir="${packagesDir}archive/" packagesPreviouslySourceBuiltDir="${packagesDir}previously-source-built/" @@ -127,6 +115,8 @@ exclude_ci_binary_log='' node_reuse='' prepare_machine='' warn_as_error='' +targetOS='' +targetArch='' properties=() while [[ $# > 0 ]]; do @@ -149,15 +139,32 @@ while [[ $# > 0 ]]; do ;; -os|-target-os) properties+=( "/p:TargetOS=$2" ) + targetOS="$2" shift ;; -arch|-target-arch) properties+=( "/p:TargetArchitecture=$2" ) + targetArch="$2" shift ;; -branding) - branding="$2" - SetBranding "$branding" + if [ -z ${2+x} ]; then + echo "No branding type supplied. See help (--help) for supported values." 1>&2 + exit 1 + fi + passedBrandingType="$(echo "$2" | tr "[:upper:]" "[:lower:]")" + case "$passedBrandingType" in + repodefault|unstable|preview|release) + ;; + *) + echo "Unsupported branding type '$2'." + echo "The allowed values are repodefault, unstable, preview or release." + echo "" + echo "IMPORTANT: If you previously passed in the '-branding rtm' argument for .NET 10, remove it entirely. The default is now set correctly (VMR branding defaults for release branch builds or otherwise repo branding defaults)." + exit 1 + ;; + esac + properties+=( "/p:RepoDotNetFinalVersionKind=$passedBrandingType" ) shift ;; -with-system-libs) @@ -218,9 +225,9 @@ while [[ $# > 0 ]]; do shift ;; -with-packages) - CUSTOM_PACKAGES_DIR="$(cd -P "$2" && pwd)" - if [ ! -d "$CUSTOM_PACKAGES_DIR" ]; then - echo "Custom previously built packages directory '$CUSTOM_PACKAGES_DIR' does not exist" + customPackagesDir="$(cd -P "$2" && pwd)" + if [ ! -d "$customPackagesDir" ]; then + echo "Custom previously built packages directory '$customPackagesDir' does not exist" exit 1 fi shift @@ -235,21 +242,12 @@ while [[ $# > 0 ]]; do shift ;; -with-sdk) - CUSTOM_SDK_DIR="$(cd -P "$2" && pwd)" - if [ ! -d "$CUSTOM_SDK_DIR" ]; then - echo "Custom SDK directory '$CUSTOM_SDK_DIR' does not exist" - exit 1 - fi - if [ ! -x "$CUSTOM_SDK_DIR/dotnet" ]; then - echo "Custom SDK '$CUSTOM_SDK_DIR/dotnet' does not exist or is not executable" - exit 1 - fi + customSdkDir="$(cd -P "$2" && pwd)" shift ;; -prep) - "$scriptroot/prep-source-build.sh" + prep=true ;; - # Advanced settings -build-repo-tests) properties+=( "/p:DotNetBuildTests=true" ) @@ -295,7 +293,8 @@ if [[ "$ci" == true ]]; then fi fi -. "$scriptroot/eng/common/tools.sh" +source "$scriptroot/eng/common/tools.sh" +source "$scriptroot/eng/source-build-toolset-init.sh" # Default properties properties+=( "/p:RepoRoot=$repo_root" ) @@ -314,29 +313,6 @@ if [[ "$test" == true ]]; then fi function Build { - # Source-only toolset prep steps - if [[ "$sourceOnly" == "true" ]]; then - InitializeBuildTool - - initSourceOnlyBinaryLog="" - if [[ "$binary_log" == true ]]; then - initSourceOnlyBinaryLog="/bl:\"$log_dir/init-source-only.binlog\"" - fi - - "$_InitializeBuildTool" build-server shutdown --msbuild - MSBuild-Core "$scriptroot/eng/init-source-only.proj" $initSourceOnlyBinaryLog "${properties[@]}" - # kill off the MSBuild server so that on future invocations we pick up our custom SDK Resolver - "$_InitializeBuildTool" build-server shutdown --msbuild - - local bootstrapArcadeDir=$(cat "$scriptroot/artifacts/toolset/bootstrap-sdks.txt" | grep "microsoft.dotnet.arcade.sdk") - local arcadeBuildStepsDir="$bootstrapArcadeDir/tools/" - - # Point MSBuild to the custom SDK resolvers folder, so it will pick up our custom SDK Resolver - export MSBUILDADDITIONALSDKRESOLVERSFOLDER="$scriptroot/artifacts/toolset/VSSdkResolvers/" - - # Set _InitializeToolset so that eng/common/tools.sh doesn't attempt to restore the arcade toolset again. - _InitializeToolset="${arcadeBuildStepsDir}/Build.proj" - fi local bl="" if [[ "$binary_log" == true ]]; then @@ -371,21 +347,28 @@ fi # Initialize __DistroRid and __PortableTargetOS source $scriptroot/eng/common/native/init-os-and-arch.sh source $scriptroot/eng/common/native/init-distro-rid.sh + initDistroRidGlobal "$os" "$arch" "" +if [[ -n "$__DistroRid" ]]; then + properties+=( "/p:BuildRid=$__DistroRid" ) +fi + +# if targetOS and targetArch were provided, recompute __PortableTargetOS +if [[ -n "$targetOS" && -n "$targetArch" ]]; then + unset __PortableTargetOS + initDistroRidGlobal "$targetOS" "$targetArch" "" +fi + +properties+=( "/p:PortableTargetOS=$__PortableTargetOS" ) + # Source-only settings if [[ "$sourceOnly" == "true" ]]; then - # Don't use the global nuget cache folder when building source-only which - # restores prebuilt packages that should never get into the global nuget cache. - export NUGET_PACKAGES="$scriptroot/.packages/" - - if [[ "$test" == true ]]; then - # Use a custom package cache for tests to make prebuilt detection work. - export NUGET_PACKAGES="${NUGET_PACKAGES}tests/" + # Run prep if requested + if [[ "$prep" == true ]]; then + "$scriptroot/prep-source-build.sh" --configuration "$configuration" fi - echo "NuGet packages cache: '${NUGET_PACKAGES}'" - # For build purposes, we need to make sure we have all the SourceLink information if [ "$test" != "true" ]; then get_property() { @@ -395,7 +378,16 @@ if [[ "$sourceOnly" == "true" ]]; then } GIT_DIR="$scriptroot/.git" - if [ -f "$GIT_DIR/index" ]; then # We check for index because if outside of git, we create config and HEAD manually + # Check if exist .git file + if [ -f "$GIT_DIR" ]; then + if ! grep -iq '^gitdir: ' "$GIT_DIR"; then + echo "ERROR: $GIT_DIR exist, but does not contain a valid 'gitdir:' pointer." + exit 1 + else + GIT_DIR=$(grep '^gitdir: ' "$GIT_DIR" | awk -F'gitdir: ' '{ print $2 }') + fi + fi + if [ -f "$GIT_DIR/config" ] && [ -f "$GIT_DIR/HEAD" ]; then # We check for config/HEAD because if outside of git, we create config and HEAD manually if [ -n "$sourceRepository" ] || [ -n "$sourceVersion" ] || [ -n "$releaseManifest" ]; then echo "ERROR: Source Link arguments cannot be used in a git repository" exit 1 @@ -438,111 +430,24 @@ if [[ "$sourceOnly" == "true" ]]; then || (echo "ERROR: Failed to find officialBuildId in $releaseManifest" && exit 1) SetOfficialBuildId "$officialBuildId" fi - - # If branding was not provided, extract it from the release manifest - if [[ "$branding" == "" ]]; then - branding=$(get_property "$releaseManifest" branding) \ - || (echo "ERROR: Failed to find branding in $releaseManifest" && exit 1) - SetBranding "$branding" - fi fi - fi - - # Support custom source built package locations - if [ "$CUSTOM_PACKAGES_DIR" != "" ]; then - if [ "$test" == "true" ]; then - properties+=( "/p:CustomSourceBuiltPackagesPath=$CUSTOM_PACKAGES_DIR" ) - else - properties+=( "/p:CustomPreviouslySourceBuiltPackagesPath=$CUSTOM_PACKAGES_DIR" ) - fi - fi - - if [ ! -d "$scriptroot/.git" ]; then - echo "ERROR: $scriptroot is not a git repository." - exit 1 - fi - - # Allow a custom SDK directory to be specified - if [ -d "$CUSTOM_SDK_DIR" ]; then - export SDK_VERSION=$("$CUSTOM_SDK_DIR/dotnet" --version) - export CLI_ROOT="$CUSTOM_SDK_DIR" - echo "Using custom bootstrap SDK from '$CLI_ROOT', version '$SDK_VERSION'" + # else, running tests else - sdkLine=$(grep -m 1 'dotnet' "$scriptroot/global.json") - sdkPattern="\"dotnet\" *: *\"(.*)\"" - if [[ $sdkLine =~ $sdkPattern ]]; then - export SDK_VERSION=${BASH_REMATCH[1]} - export CLI_ROOT="$scriptroot/.dotnet" - fi + properties+=( "/p:DisableSharedComponentValidation=true" ) fi - # Set _InitializeDotNetCli & DOTNET_INSTALL_DIR so that eng/common/tools.sh doesn't attempt to restore the SDK. - export _InitializeDotNetCli="$CLI_ROOT" - export DOTNET_INSTALL_DIR="$CLI_ROOT" - - # Find the Arcade SDK version and set env vars for the msbuild sdk resolver - packageVersionsPath='' - - if [[ "$CUSTOM_PACKAGES_DIR" != "" && -f "$CUSTOM_PACKAGES_DIR/PackageVersions.props" ]]; then - packageVersionsPath="$CUSTOM_PACKAGES_DIR/PackageVersions.props" - elif [ -d "$packagesArchiveDir" ]; then - sourceBuiltArchive=$(find "$packagesArchiveDir" -maxdepth 1 -name 'Private.SourceBuilt.Artifacts*.tar.gz') - if [ -f "${packagesPreviouslySourceBuiltDir}PackageVersions.props" ]; then - packageVersionsPath=${packagesPreviouslySourceBuiltDir}PackageVersions.props - elif [ -f "$sourceBuiltArchive" ]; then - tar -xzf "$sourceBuiltArchive" -C /tmp PackageVersions.props - packageVersionsPath=/tmp/PackageVersions.props - fi + # Support custom source built package locations + if [ "$customPackagesDir" != "" ]; then + properties+=( "/p:CustomPreviouslySourceBuiltPackagesPath=$customPackagesDir" ) fi - if [ ! -f "$packageVersionsPath" ]; then - echo "Cannot find PackagesVersions.props. Debugging info:" - echo " Attempted custom PVP path: $CUSTOM_PACKAGES_DIR/PackageVersions.props" - echo " Attempted previously-source-built path: ${packagesPreviouslySourceBuiltDir}PackageVersions.props" - echo " Attempted archive path: $packagesArchiveDir" + if [ ! -e "$scriptroot/.git" ]; then + echo "ERROR: $scriptroot is not a git repository/worktree." exit 1 fi - # Extract toolset packages - - # Ensure that by default, the bootstrap version of the toolset SDK is used. Source-build infra - # projects use bootstrap toolset SDKs, and would fail to find it in the build. The repo - # projects overwrite this so that they use the source-built toolset SDK instad. - - # 1. Microsoft.DotNet.Arcade.Sdk - arcadeSdkLine=$(grep -m 1 'MicrosoftDotNetArcadeSdkVersion' "$packageVersionsPath") - arcadeSdkPattern="(.*)" - if [[ $arcadeSdkLine =~ $arcadeSdkPattern ]]; then - export ARCADE_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} - - export SOURCE_BUILT_SDK_ID_ARCADE=Microsoft.DotNet.Arcade.Sdk - export SOURCE_BUILT_SDK_VERSION_ARCADE=$ARCADE_BOOTSTRAP_VERSION - export SOURCE_BUILT_SDK_DIR_ARCADE=${NUGET_PACKAGES}BootstrapPackages/microsoft.dotnet.arcade.sdk/$ARCADE_BOOTSTRAP_VERSION - fi - - # 2. Microsoft.Build.NoTargets - notargetsSdkLine=$(grep -m 1 'Microsoft.Build.NoTargets' "$scriptroot/global.json") - notargetsSdkPattern="\"Microsoft\.Build\.NoTargets\" *: *\"(.*)\"" - if [[ $notargetsSdkLine =~ $notargetsSdkPattern ]]; then - export NOTARGETS_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} - - export SOURCE_BUILT_SDK_ID_NOTARGETS=Microsoft.Build.NoTargets - export SOURCE_BUILT_SDK_VERSION_NOTARGETS=$NOTARGETS_BOOTSTRAP_VERSION - export SOURCE_BUILT_SDK_DIR_NOTARGETS=${NUGET_PACKAGES}BootstrapPackages/microsoft.build.notargets/$NOTARGETS_BOOTSTRAP_VERSION - fi - - # 3. Microsoft.Build.Traversal - traversalSdkLine=$(grep -m 1 'Microsoft.Build.Traversal' "$scriptroot/global.json") - traversalSdkPattern="\"Microsoft\.Build\.Traversal\" *: *\"(.*)\"" - if [[ $traversalSdkLine =~ $traversalSdkPattern ]]; then - export TRAVERSAL_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} - - export SOURCE_BUILT_SDK_ID_TRAVERSAL=Microsoft.Build.Traversal - export SOURCE_BUILT_SDK_VERSION_TRAVERSAL=$TRAVERSAL_BOOTSTRAP_VERSION - export SOURCE_BUILT_SDK_DIR_TRAVERSAL=${NUGET_PACKAGES}BootstrapPackages/microsoft.build.traversal/$TRAVERSAL_BOOTSTRAP_VERSION - fi - - echo "Found bootstrap versions: SDK $SDK_VERSION, Arcade $ARCADE_BOOTSTRAP_VERSION, NoTargets $NOTARGETS_BOOTSTRAP_VERSION and Traversal $TRAVERSAL_BOOTSTRAP_VERSION" + # Initialize source-only toolset (includes custom SDK setup, MSBuild resolver, and source-built resolver) + source_only_toolset_init "$customSdkDir" "$customPackagesDir" "$binary_log" "$test" "${properties[@]}" fi Build diff --git a/docs/Repository-Onboarding.md b/docs/Repository-Onboarding.md new file mode 100644 index 000000000000..2bf768846674 --- /dev/null +++ b/docs/Repository-Onboarding.md @@ -0,0 +1,81 @@ +# VMR Repository Onboarding Guide + +This document provides a step-by-step guide for onboarding repositories into the VMR (Virtual Monolithic Repository). +For background on VMR concepts and architecture, see [VMR Design and Operation](./VMR-Design-And-Operation.md). + +## Prerequisites + +Before onboarding a repository to the VMR, ensure the following are met: + +- Repository is a dependency of the .NET product (e.g. a dependency of the SDK) +- Repository is onboarded to the [Arcade SDK](https://github.com/dotnet/arcade/blob/main/Documentation/StartHere.md) and its builds are registered in [BAR](https://github.com/dotnet/arcade-services/blob/main/docs/Darc.md#locating-the-bar-build-id-for-a-build). + - If not, see if it can be added via an [external source-build-reference-package](https://github.com/dotnet/source-build-reference-packages/blob/main/README.md#external) +- Repository is compatible with [source build](https://github.com/dotnet/source-build/blob/main/Documentation/sourcebuild-in-repos/README.md) +- Open an [issue](https://github.com/dotnet/dotnet/issues/new/choose) tracking this work and mention [@dotnet/product-construction](https://github.com/orgs/dotnet/teams/product-construction) + +## Onboarding Steps + +### Step 1: Configure Source Mappings + +Add your repository to `/src/source-mappings.json` and open a PR to merge the change. +For detailed configuration options, see [Repository Source Mappings](./VMR-Full-Code-Flow.md#repository-source-mappings). + +### Step 2: Define the Darc Code Flow Subscriptions + +Define the appropriate Darc [code flow subscriptions](./Codeflow-PRs.md). +After defined manually trigger the forward flow subscription via `darc` or [Maestro](https://maestro.dot.net/subscriptions). +This will open a [VMR PR](https://github.com/dotnet/dotnet/pulls?q=is%3Apr+is%3Aopen+%22Source+code+updates%22) that brings in the repo's source code. +Add the `NO-MERGE` label to the PR while completing the next steps necessary to integrate the repo into the VMR. + +#### Step 3: Ensure no Checked in Binaries + +The VMR has restrictions on checked-in binaries. +PR and CI validation exists to ensure the [binary policy](./VMR-Permissible-Sources.md#binary-policy) is satisfied. +If the code flow PR is green, the repo complies with the binary policy. + +#### Step 4: Validate OSS Compliant Licenses + +The VMR has restrictions on allowed licenses. +Before the code flow PR is merged, queue a run of the [license scan pipeline](https://dev.azure.com/dnceng/internal/_build?definitionId=1490) (internal Microsoft link) to validate the repo complies with the [license policy](./VMR-Permissible-Sources.md#license-policy). + +### Step 5: Update Ownership Assignment Policy + +Add an entry in `/.github/policies/assign_ownership.yml` to automatically assign reviewers when changes are detected in your repository's source directory. +Add an event responder task that matches the pattern `src//.*` and specifies an appropriate GitHub team as the `teamReviewer`. +This helps ensure that the right team is notified when changes are made to your repository within the VMR. + +### Step 6: Create Repository Project File + +Create a `/repo-projects/.proj` that integrates the repo's build into to the VMR's build. +Browse the existing [`repo-projects`](https://github.com/dotnet/dotnet/tree/main/repo-projects) for examples. +Ensure the correct dependencies are defined within the new and existing projects. + +### Step 7: Build and Validate + +[Build](./README.md#building) the VMR with the new repo. +Push changes to the PR to get complete valdation. +Resolve any build issues that arise. +This may require adjusting the [repository project file](#step-6-create-repository-project-file) and utilizing the [VMR controls](./VMR-Controls.md) to adjust how the repo is built within the VMR. + +Validate the assets produced from the build by checking the `/artifacts` directory contents. + +### Step 8: Merge PR + +Once your PR is green and the artifacts have been validated, remove the `NO-MERGE` label from the PR. +Get approval from the repo experts and [@dotnet/product-construction](https://github.com/orgs/dotnet/teams/product-construction) before merging the PR. + +### Step 9: Post Merge Validation + +After your PR has been merged validate the following: + +1. Validate the [CI builds](https://dev.azure.com/dnceng/internal/_build?definitionId=1330) are passing. +1. Ensure the [forward and back code flows](https://maestro.dot.net) work correctly + +### Step 10: Enable Repo Level VMR PR Validation + +Consider enabling [repo level VMR PR validation](https://github.com/dotnet/arcade/blob/main/Documentation/VmrValidation.md) as either an optional or required check. + +## Getting Help + +- Utilize the issue created in [Repository Requirements](#prerequisites) to discuss any issues encountered. +- For source build specific issues mention [@dotnet/source-build](https://github.com/orgs/dotnet/teams/source-build) in your issue. diff --git a/docs/VMR-Official-Build-Bar.md b/docs/VMR-Official-Build-Bar.md new file mode 100644 index 000000000000..5416513a820b --- /dev/null +++ b/docs/VMR-Official-Build-Bar.md @@ -0,0 +1,50 @@ +# Defining the Bar for Processes in the VMR Official Build + +## Purpose + +To establish clear criteria and a decision-making framework for determining which processes should be included in the VMR official build. This ensures that the build is efficient, reliable, and maintains the necessary quality standards for shipping assets, while supporting code flow velocity and minimizing unnecessary delays. + +## Principles + +- **Shippability** - The offical build must produce assets and information to ship .NET in a confident and compliant manner. +- **Velocity**: The build must meet our SLAs (currently 4 hours unsigned, 7 hours signed). +- **Reliability**: The official build must be reliable, with minimal flakiness or unnecessary failures that could block code flow. + +## Description of the Bar + +Processes that do not contribute to the essential production or validation of shipping assets, or that introduce unnecessary delays, flakiness, or complexity, should be excluded. The goal is that we should never stage (ready for release) any failed build. *Note that this does not mean that a green build is ready for release. Plenty of additional validation processes may be performed (e.g. VS insertion, CTI signoff etc.)*. Any process excluded from the official build must have a compensating mechanism to ensure its results are still considered before shipping or sign-off. + +## Rubric + +Evaluate each of the questions in order. You reach the end without answering "yes", then the process might be excluded. + +- Is the process required to produce assets necessary for shipping the product or downstream builds? **If so, it should be included.** +- Does the process directly support the integrity and readiness and compliance of shipping assets for release, without putting the official build SLAs or reliability at risk? **If so, it should be included.** +- Even if inclusion puts SLAs at risk, is there no possible alternative or compensating mechanism to ensure the process’s results are considered before shipping or sign-off? **If so, it should be included.** + +## Examples + +### Example: Processes That Meet the Bar + +- **Source-build CentOS jobs** + - These jobs produce assets used by distro partners to bootstrap their builds. They can't ship without them. + +- **Scenario Tests** + - Scenario tests aren't required to produce shipping assets. + - These tests **do** validate the basic quality of the build. A failure means that the build is not shippable. They're also quick and reliable. + + +### Example: Process That Does Not Meet the Bar + +- **Source build License scanning** + - Does not produce shipping assets + - Is critical validation, but is very expensive. + - Can be run async in a separate pipelione +- **SDK Diff testing** + - Does not produce shipping assets + - Is necessary validation, but failure does not necessarily mean an unshippable product. + - Validation can be run as a separate, async pipeline. +- **Mono Source Build Validation Legs**: + - Mono Source build legs do not produce shipping assets. + - Mono Source build legs do validate our distro partners' ability to bootstrap on non-MS supported archictures, but the builds can be flaky. + - There is a possibility of a compensating mechanism, a separate validation job which runs the mono validation. diff --git a/docs/VMR-Servicing-Workflows.md b/docs/VMR-Servicing-Workflows.md new file mode 100644 index 000000000000..ce0a2a50e139 --- /dev/null +++ b/docs/VMR-Servicing-Workflows.md @@ -0,0 +1,368 @@ +# The Unified Build Almanac (TUBA) - Servicing Workflow + +This document describes the workflow for branching, branding, and build management in servicing for 10.0. An alternative model is included later for comparison. + +Terminology for the info below: +- **Current Month** – The month being built, but not yet released. +- **Current+1 Month** – The next release after the current month. +- **Current+2 Month** – The next release after the current+1 month. +- **Release Specific** – Refers to a specific release, rather than an overall release train (e.g. `release/10.0.101`). +- **General Servicing** – Refers to an overall servicing train (e.g. `release/10.0.1xx`). + +## Branch the VMR for each release + +Utilizes a **continuously** open servicing branch. A new VMR **Release Specific** branch is created off of this branch for each actual release (e.g. `release/10.0.101` and `release/10.0.200`) with additional temporary validation branches. These **Release Specific** branches may receive late-breaking fixes without endangering a future release. + +### TL;DR + +- Release builds are cut from short-lived servicing branches. +- Long-lived servicing branches are always open. +- Day-to-day validation occurs against long-lived validation and repository branches. +- Final validation occurs against short-lived servicing branches. +- Developers switch where they check in based on the point in time during the month. + +### Detailed Description + +- Tactics approves bugs continuously. These bugs are approved for a specific release (milestone). The target release may change (e.g., delayed to align with another product). Changes are **immediately** checked into the branch that matches the milestone, if it exists. This target shifts depending on where we are in the month. +- On a specified day after the **Current Month** content is code complete (validation may not yet be complete), **Release Specific** VMR and validation branches are created: + - Public **Release Specific** VMR: `release/10.0.Nxx` -> `release/10.0.NOM` (off HEAD) + - Internal **Release Specific** VMR: `internal/release/10.0.Nxx` -> `internal/release/10.0.NOM` (off HEAD) + - Internal **Release Specific** VMR validation branches: `validation/release/10.0.N[0M]` off the last forward-flow SHAs. +- **Current Month** changes then move to being checked into the following locations: + - Public **Release Specific** VMR branches (`release/10.0.NOM`) – for public VMR fixes. + - Internal **Release Specific** VMR branches (`internal/release/10.0.NOM`) – for internal fixes. +- The Public VMR branches are rebranded for **Current+1 Month**. +- After rebranding for **Current+1 Month** and until the next branch point, **General Servicing** branches are open for **Current+1 Month** changes. While the **General Servicing** VMR branch represents **Current+1 Month**, changes for **Current+1 Month** may be checked into the following locations: + - Public **General Servicing** component repository branches (e.g., SDK @ `release/10.0.1xx`, runtime @ `release/10.0`) – flows to public VMR (`release/10.0.Nxx`) via forward flow. + - Public **General Servicing** VMR branches (`release/10.0.Nxx`) – for public VMR fixes. + - Internal **General Servicing** VMR branches (`internal/release/10.0.Nxx`) – for internal fixes. +- Changes in public VMR branches automatically merge into internal VMR branches (`release/10.0.Nxx` -> `internal/release/10.0.Nxx`, `release/10.0.NOM` -> `internal/release/10.0.NOM`). This happens continuously for both **Release Specific** and **General Servicing**. +- Automated validation for changes is provided in the following ways: + - **General Servicing** – Public forward flows from component repositories receive validation on the public PRs to those repositories (e.g., `my-fix-12345` validated before merging to runtime @ `release/10.0`). + - **General Servicing** and **Release Specific** – Public VMR-targeted changes are validated before merge via VMR PR pipelines and scenario testing. + - **General Servicing** and **Release Specific** – Internal VMR-targeted changes are validated before merge via VMR PR pipelines and scenario testing. + - **General Servicing** and **Release Specific** – Internal component repository changes may be validated before cherry-picking into the VMR via PRs against internal validation branches (e.g., runtime @ `validation/release/10.0`, SDK @ `validation/release/10.0.202`). These changes may be merged into the validation branch but do not flow further. + - **General Servicing** – Public VMR builds of servicing branches backflow to the public **General Servicing** component repository branches (e.g., builds of the VMR applying to channel .NET 10.0.1xx SDK backflow to runtime @ `release/10.0` and SDK @ `release/10.0.1xx`). PR validation occurs on those backflows. This covers VMR-only changes and validates component repository sources against the latest built dependency packages. + - **General Servicing** and **Release Specific** – Internal VMR builds of servicing branches backflow to internal component repository validation branches (e.g., runtime @ `validation/release/10.0` and runtime @ `validation/release/10.0.2`). +- On the release day for the **Current Month**: + - The product is released. + - The VMR release tag is merged back into the public **General Servicing** and **Release Specific** VMR release branches (e.g., `v10.0.101` -> `release/10.0.1xx`, `v10.0.200` -> `release/10.0.2xx`, `v10.0.101` -> `release/10.0.102`, `v10.0.200` -> `release/10.0.201`). + - The VMR bootstrap SDK is updated (the version of the SDK used to build the VMR) in the **General Servicing** branch. + - Arcade gets the new bootstrap SDK version, which then flows to component repositories. +- Approved changes that do not meet the bar for **Current+1 Month** are marked with the **Current+2 Month** milestone. + +### Pros + +- **General Servicing** is always open. +- Roughly similar to current servicing workflow. +- Relatively simple branch and flow setup. +- Because **General Servicing** is always open and building regularly as new fixes are approved and merged, there is a higher chance of catching infrastructure rot before it causes schedule pressure. + +### Cons + +- Developers need to know where to check in to meet the current release. If the **Release Specific** branch point is too early, this could be an issue. +- Some fixes may have to wait if they are not approved for a currently active branch. +- For late-breaking changes, there is no forward flow. +- Care is required around branch time to ensure all approved fixes make it into the VMR before the branch point. For example: a runtime fix is approved, checked into the runtime repo, and begins flowing into the VMR via a PR. If the release branch is cut before that PR merges, the fix misses the release. Tooling can mitigate this risk. *Note: A similar situation exists today; long-lived branches likely reduce the chance of missing a change.* + +### Sample Schedule + +```mermaid +gantt + title Alt Branch Model + dateFormat YYYY-MM-DD + axisFormat %m/%d + + section General Servicing (GS) + GS-Feb Branch Cut (from GS HEAD) :milestone, rs_mar_cut, 2026-01-20, 0d + GS-Rebrand -> March (from GS HEAD) :milestone, gs_to_mar, 2026-01-20, 0d + GS-March Dev :gs_mar_dev, 2026-01-20, 35d + GS-Merge From Feb Release Tag :milestone, 2026-02-10, 0d + GS-Mar Branch Cut (from GS HEAD) :milestone, rs_mar_cut, 2026-02-24, 0d + GS Rebrand → April :milestone, gs_to_apr, 2026-02-24, 0d + GS April Development :gs_apr_dev, 2026-02-24, 29d + GS-Merge From Mar Release Tag :milestone, 2026-03-10, 0d + + section RS-Feb (release/10.0.103) + RS-Feb Stabilization / Late Fixes :rs_feb_stab, 2026-01-20, 7d + RS-Feb Validation :rs_feb_build, 2026-01-27, 5d + RS-Feb Staging / Release :rs_feb_build, 2026-02-01, 9d + RS-Feb Release :milestone, 2026-02-10, 0d + + + section RS-Mar (release/10.0.104) + RS-Mar Stabilization Phase / Late Fixes :rs_mar_stab, 2026-02-24, 3d + RS-Mar Validation :rs_mar_build2, 2026-02-27, 3d + RS-Mar Staging / Release :rs_feb_build, 2026-03-01, 9d + RS-Mar Release :milestone, 2026-03-10, 0d +``` +#### Notes on schedule + +- Choosing the RS branch day is a key component. The branch day should balance the following: + - **Late enough** to avoid requiring devs to switch check-in targets as much as possible (devs should primarily care about GS branches). + - **Early enough** to avoid 'parking' too many approved fixes while waiting for the matching GS branch to open. + +#### Developer – Public Fix + +1. Developer prepares source for their fix in their component repository, or in the VMR if the fix is VMR-specific. +2. Developer prepares an approval template and brings the fix to Tactics. +3. Tactics approves the bug for a specific release (e.g. 10.0.3 or 10.0.4) +4. Developer merges the fix based on the current branch status and schedule, choosing the appropriate place to check in: + - If the approved milestone corresponds to the **Release Specific** branch, then the PR is targeted to the public VMR **Release Specific** branch and merged. + - If the approved milestone corresponds to the current state of the **General Servicing** branch (the branch is branded for that milestone), then the PR is targeted to the public VMR **General Servicing** branch, or the component repository **General Servicing** branch and merged. + - If the approved milestone does not correspond to any currently open branch, the fix is parked until a **General Servicing** VMR/component repo branch matches the milestone, and then the fix is merged. +5. Confirm that the fix flowed into the VMR servicing branch matching the approved milestone. +6. Wait for a validation build. +7. Validate the fix in the shipping product. + +#### Developer – Internal Fix + +1. Developer prepares source for their fix. This can be done in two ways: + - In the internal validation branch of their component repository (to enable repo-specific PR validation), followed by cherry-picking to the internal VMR servicing branch. + - Directly in the internal VMR servicing branch. +2. Developer prepares an approval template and brings the fix to .NET Tactics. +3. Tactics approves the bug for a specific release (e.g. 10.0.3 or 10.0.4) +4. Developer merges the fix based on the current branch status and schedule, choosing the appropriate place to check-in: + - If the approved milestone corresponds to the **Release Specific** branch, then the PR is targeted to the internal VMR **Release Specific** branch and merged. + - If the approved milestone corresponds to the current state of the **General Servicing** branch (the branch is branded for that milestone), then the PR is targeted to the internal VMR **General Servicing** branch and merged. + - If the approved milestone does not correspond to any currently open branch, the fix is parked until a **General Servicing** internal VMR branch matches the milestone, and then the fix is merged. +5. Confirm that the fix made it into the VMR servicing branch matching the approved milestone. +6. Wait for a validation build. +7. Validate the fix in the shipping product. + +#### Repository Owner - Preparing for Upcoming Release + +1. Continually review approved fixes. Ensure that any approved fixes are merged to a branch that matches their milestone. +2. Before the **Release Specific** branch date, ensure that all approved fixes have been merged into the VMR **General Servicing** branch. *Note: Tooling for this will be available*. +3. After **Release Specific** branching, ensure that fixes approved for the milestone matching the **Release Specific** VMR branch are merged into the **Release Specific** branch. + +#### Repository Owner – Release Sign-off + +1. Regularly review status of **General Servicing** validation flows. The status of these branches represents the future shipping release before **Release Specific** branching is completed. + - Public and internal validation PRs should be passing +2. Receive release candidate build for signoff, typically via the [release tracker](https://aka.ms/release-tracker) or signoff mails sent to Tactics. +3. Ensure that expected shas and fixes from component repository are included in the build via inspection of the release tracker metadata, BarViz, or the VMR src/source-manifest.json file. +4. Perform validations on component scenarios. This may include validations for specific fixes as well as general scenarios. +5. Review status of internal **Release Specific** backflow validation PRs to your component repository. + - PRs should be passing. + - Backflow should be from latest internal VMR build (release candidate, or later if additional builds were completed.) +6. Review additional rolling CI jobs as per repository validation requirements (e.g. rolling gcstress) + +#### Coherency QB Tasks + +The coherency QB would be called upon to complete the following tasks: + +##### Update branding, create internal validation branches + +On branding update day, the coherency QB will update branding to the next version via the VMR and create **Release Specific** internal validation branches. This will use the NETMechanic tool. + +``` +# Exact command TBD. Command NYI. +NETMechanic.dll prepare-for-servicing --config net10.json --previous-release-sha 12345 --next-release 10.0.1,10.0.103,10.0.200 +``` + +##### Merge in internal release tags + +On release day, the coherency QB will merge internal release tags to the public branches. This will use the NETMechanic tool. + +``` +# Exact command TBD. Command NYI. +NETMechanic.dll merge-latest-release-tags --config net10.json +``` + +#### Coherency QB/Release QB/Repository Owners/Devs - Ensure desired changes are in build. + +Various parties may want to validate that a set of approved fixes from a specific milestone exist in the VMR build. .NET will invest in mechanisms to ensure that all intended fixes make it from their source locations into the validated build. This includes: +- [Services Approved Fixes Tooling](https://github.com/dotnet/release/issues/1586) +- [Linking PRs in forward flows](https://github.com/dotnet/arcade-services/pull/5355) + +### Special Considerations - Cherry-pick Release Specific to General Servicing? + +When a fix is made directly to a **Release Specific** branch, that fix will make its way back into **General Servicing** on release day, when the VMR tag is merged in. However, this is primarily an insurance measure, and cannot account for: +- Additional special case **Release Specific** branches created before the release day. +- The **General Servicing** branch not having a fix required for release available in daily validation. +- Current infrastructure issues in the **General Servicing** branch resolved by the fix. + +It is recommended, though not required, that fixes made directly to the **Release Specific** VMR branch be merged back into the appropriate **General Servicing** branch (public->public, internal->internal). + +### Special Considerations - OOB Releases + +There are times when an OOB release is required. These are releases that contain a minimal set of fixes with new branding. These typically come in two forms: +- A full release that includes a new runtime and new SDKs. The current in-progress release becomes N+1. +- An SDK-only release. The SDKs move forward to N+1. + +#### Process + +- Tactics decides that an OOB release is required. +- Tactics defines the content of this OOB release. This includes: + - **Release Base** – What is the basis for the release? (e.g., an SDK-only OOB after January might base off the unreleased .NET 10.0.102 SDK.) + - **Release Versioning** – What version should this release use, and how does that affect in-flight versions? + - **Release Scope** – Which components are released? + - **Release Content** – Additional content or changes required: + - Required fixes (may include the primary fix plus any supporting fixes such as NuGet thumbprint updates). + - Ancillary changes (e.g., implicit versions). +- Based on the release base and content, the coherency QB chooses a branch point. This is *usually*, but not always, the previous (or soon-to-be-released) VMR SHA. +- The coherency QB performs the branching and branding process (roughly equivalent to the monthly branching process), possibly with an additional step to move in-process release branding forward. + - Rebrand and rename any active **Release Specific** branches that need to change based on the introduction of a new release. For example, if an SDK-only OOB for 10.0.102 is to be released and `release/10.0.102` already exists, that branch is rebranded and then renamed to `release/10.0.103`. Typically, versions increase by 1 to leave space. The original `release/10.0.102` is deleted (it will be recreated with new content). + - Rebrand any active **General Release** branches that need updating (typically increment the patch version by 1). + - Use the `NETMechanic` tool to create a new **Release Specific** VMR branch with associated validation branches from the desired VMR base sha. + - As necessary, update **Release Specific** branch(es) to match desired release scope. (E.g., for an SDK-only 1xx release, disable the runtime build.) + - Update content in the **Release Specific** VMR branch to match approved content. This may include: + - *SDK-only releases*: Update to required runtime. + - Approved fixes + - Implicit versions + - ... + - As necessary, update in-progress releases to ensure that they will have the required content. This typically involves integrating the OOB release fixes into the **General Release** and **Release Specific** branches. +- Build the release and perform validation (see sign-off tasks for more info). +- On release day: + - Release the OOB. + - Merge the VMR OOB release tag back into the **General Release** VMR branch. + - If there is still runway, merge the tag back into yet-unreleased **Release Specific** VMR branches. + +## Alternative considered - General Servicing branches only + +This approach closely matches the workflow used in 9.0 and prior releases, with adjustments required due to the VMR workflow. + +### TL;DR + +- Release builds are cut from a long-lived servicing branch. +- Validation occurs against long-lived validation and repository branches. +- Branches are closed for a period during stabilization and validation. + +### Detailed Description + +- Tactics approves bugs continuously. Bugs are approved for a specific release (milestone) based on whether we have reached code complete for the **Current Month**. +- On a specified day after the **Current Month** has been signed off and is on its way out the door, the public VMR branches (`release/10.0.Nxx`) are rebranded to the **Current+1 Month** release. +- Internal component repository validation branches (e.g., SDK @ `validation/release/10.0.1xx`) are force-reset to the last forward flow of the corresponding component repositories of the internal VMR branch (which should match public). +- After rebranding for **Current+1 Month**, branches open for changes. Approved **Current+1 Month** changes are checked into: + - Public component repository branches (e.g., SDK @ `release/10.0.1xx`, runtime @ `release/10.0`) – flows to public VMR (`release/10.0.Nxx`) via forward flow. + - Public VMR branches (`release/10.0.Nxx`) – public VMR fixes. + - Internal VMR branches (`internal/release/10.0.Nxx`) – internal fixes. +- Changes in public VMR branches automatically merge into internal VMR branches (`release/10.0.Nxx` -> `internal/release/10.0.Nxx`) continuously. +- Automated validation for changes is provided in the following ways: + - Public forward flows from component repositories receive validation on the original public PRs (e.g., `my-fix-12345` validated before merging to runtime @ `release/10.0`). + - Public VMR-targeted changes are validated before merge via the VMR PR pipelines and scenario testing. + - Internal VMR-targeted changes are validated before merge via the VMR PR pipelines and scenario testing. + - Internal component repository changes may be validated before cherry-picking into the VMR by PRs against the internal validation branches (e.g. runtime @ validation/release/10.0). + These changes may be merged into the validation branch, but do not flow anywhere if they are. + - Public VMR builds of servicing branches backflow to public component repositories (e.g., builds applying to channel .NET 10.0.1xx SDK backflow to runtime @ `release/10.0` and SDK @ `release/10.0.1xx`). PR validation covers VMR-only changes and dependency coherency. + - Internal VMR builds of servicing branches back flow to internal component repository validation branches (e.g. runtime @ validation/release/10.0). + These changes may be merged into the validation branch, but do not flow anywhere if they are. +- On the release day for the **Current Month**: + - The VMR release tag is merged back into the public VMR release branch (e.g., `v10.0.101` -> `release/10.0.1xx`, `v10.0.200` -> `release/10.0.2xx`). + - The VMR bootstrap SDK is updated (the version of the SDK used to build the VMR). + - Arcade receives the new bootstrap SDK version, which then flows to component repositories. +- **Current+1 Month** builds as new changes merge into the VMR. Validation occurs via backflow, CTI teams, VS insertion, etc. Approved changes for **Current+1 Month** are allowed as quality, risk, and time permit. +- Approved changes that do not meet the bar for **Current+1 Month** are marked with the **Current+2 Month** milestone. + +### Pros + +- Well-trodden servicing workflow +- Simplest dependency flow and code flow strategy +- Fewest places for devs to merge code. + +### Cons + +- Branches 'close' for a period of time while building **Current+1 Month**, but before branding for **Current+2 Month** is complete. This period allows for a stable, predictable branch while validation + is completed. If critical changes are required for **Current+1 Month**, they can be made in component repositories or the VMR directly without inadvertently bringing in **Current+2 Month** changes. +- Because branches close for a period of time, there is a higher likelihood that infrastructure 'rot' will be discovered when a servicing release is closer to the end of its build or validation window. This can cause schedule pressure. + +### Sample Schedule + +Schedule + +```mermaid +gantt + title .NET 10 Servicing – February & March 2026 + dateFormat YYYY-MM-DD + axisFormat %m/%d + + %% FEBRUARY (Current) + section February Release (Current) + Release Day (Ship February) :milestone, feb_release, 2026-02-10, 0d + Post-Release Tag Merge & Validation Branch Reset (Feb) :milestone, feb_reset, 2026-02-10, 0d + + %% MARCH (Current+1 before Feb 10; becomes Current after Feb 10) + section March Release (Current+1) + March Branding Update :milestone, mar_branding, 2026-02-03, 0d + Merge Approved Fixes for Current+1 :milestone, 2026-02-03, 0d + March Branches Open :mar_open, 2026-02-03, 10d + March Build / Validation Window :mar_build, 2026-02-10, 9d + March Staging :mar_staging, 2026-02-19, 10d + March Release Day (Ship March) :milestone, mar_release, 2026-03-10, 0d + March Post-Release Tag Merge & Reset :milestone, mar_reset, 2026-03-10, 0d + + %% APRIL (Emerging Current+2 → becomes Current+1 after Mar 10) + section April Release (Current+2) + April Branding Update :milestone, apr_branding, 2026-03-03, 0d + Merge Approved Fixes for Current+2 :milestone, 2026-03-03, 0d + April Branches Open :apr_open, 2026-03-03, 10d +``` + +### Participant Workflows + +#### Developer – Public Fix + +1. Developer prepares source for their fix in their component repository, or in the VMR if the fix is VMR-specific. +2. Developer prepares an approval template and brings the fix to Tactics. +3. Tactics approves the bug for a specific release (e.g. 10.0.3 or 10.0.4) +4. Developer merges the fix based on current branch status and schedule: + - If release branches are currently closed, or the component release branch/VMR servicing branch corresponds to a different milestone, the fix is parked until the branch opens for that milestone. + - If the branch corresponds to the approved milestone (i.e., branded for that month) and is open, the fix is merged immediately. +5. If the fix was made to the component repository, confirm that the fix flowed into the VMR servicing branch via a merged forward flow PR. +6. Wait for a validation build. +7. Validate the fix in the shipping product. + +#### Developer – Internal Fix + +1. Developer prepares source for their fix. This can be done in two ways: + - In the internal validation branch of their component repository (enables repo-specific PR validation), then cherry-pick to the internal VMR servicing branch. + - Directly in the internal VMR servicing branch. +2. Developer prepares an approval template and brings the fix to .NET Tactics. +3. Tactics approves the bug for a specific release (e.g. 10.0.3 or 10.0.4) +4. Developer merges the fix based on current branch status and schedule: + - If release branches are currently closed, or the internal VMR servicing branch corresponds to a different milestone, the fix is parked until the branch opens for that milestone. + - If the branch corresponds to the approved milestone (i.e., branded for that month) and is open, the fix is merged immediately. +5. Wait for a validation build. +6. Validate the fix in the shipping product. + +#### Repository Owner – Release Sign-off + +1. Receive release candidate build for signoff, typically via the [release tracker](https://aka.ms/release-tracker) or signoff mails sent to Tactics. +2. Ensure that expected shas and fixes from component repository are included in the build via inspection of the release tracker metadata, BarViz, or the VMR src/source-manifest.json file. +3. Perform validations on component scenarios. May include validations for specific fixes as well as general scenarios. +4. Review status of internal backflow validation PRs to your component repository. + - PRs should be passing. + - Backflow should be from latest internal VMR build (release candidate, or later if additional builds were completed.) +5. Review status of public validation PRs to your component repository + - PRs should be passing. + - Backflow should be from latest internal VMR build (release candidate, or later if additional builds were completed.) +6. Review additional rolling CI jobs as per repository validation requirements (e.g. rolling gcstress) + +#### Coherency QB Tasks + +The coherency QB would be called upon to complete the following tasks: + +##### Update branding and reset internal validation branches + +On branding update day, the coherency QB will update branding to the next version via the VMR and reset the internal validation branches to the known state. This will use the NETMechanic tool. + +``` +# Exact command TBD. Command NYI. +NETMechanic.dll prepare-for-servicing --config net10.json --previous-release-sha 12345 --next-release 10.0.1,10.0.103,10.0.200 +``` + +##### Merge in internal release tags + +On release day, the coherency QB will merge internal release tags to the public branches. This will use the NETMechanic tool. + +``` +# Exact command TBD. Command NYI. +NETMechanic.dll merge-latest-release-tags --config net10.json +``` + +#### Coherency QB/Release QB/Repository Owners/Devs - Ensure desired changes are in build. + +Various parties may be interested in validating that a set of approved fixes from a specific milestone exist in the VMR build. .NET will invest in various ways to ensure that all fixes intended for a given servicing release make it from their various source locations and into the validated build. This includes: +- [Services Approved Fixes Tooling](https://github.com/dotnet/release/issues/1586) +- [Linking PRs in forward flows](https://github.com/dotnet/arcade-services/pull/5355) diff --git a/docs/builds-table.md b/docs/builds-table.md index 27bde87c4047..1f5f88889da6 100644 --- a/docs/builds-table.md +++ b/docs/builds-table.md @@ -1,162 +1,304 @@ +> [!NOTE] +> When acquiring installers from the .NET SDK latest builds table, be aware that the installers are the **latest bits**. With development builds, internal NuGet feeds are necessary for some scenarios (for example, to acquire the runtime pack for self-contained apps). You can use the following NuGet.config to configure these feeds. See the following document [Configuring NuGet behavior](https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior) for more information on where to modify your NuGet.config to apply the changes. + +### For .NET 11 builds +```xml + + + + + +``` + +### For .NET 10 builds +```xml + + + + + +``` + ### Supported Platforms --------------------------------------------------------------------------------------- -| Platform | main
(10.0.x Runtime) | 10.0.1xx-preview7
(10.0-preview7 Runtime) | +-------------------------------------------------------------------------------------------------------------------------------- +| Platform | main
(11.0.x Runtime) | 10.0.2xx
(10.0.2xx Runtime) | | :--------- | :----------: | :----------: | -| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-10.0.1XX-preview7]][win-x64-version-10.0.1XX-preview7]
[Installer][win-x64-installer-10.0.1XX-preview7] - [Checksum][win-x64-installer-checksum-10.0.1XX-preview7]
[zip][win-x64-zip-10.0.1XX-preview7] - [Checksum][win-x64-zip-checksum-10.0.1XX-preview7] | -| **Windows x86** | [![][win-x86-badge-main]][win-x86-version-main]
[Installer][win-x86-installer-main] - [Checksum][win-x86-installer-checksum-main]
[zip][win-x86-zip-main] - [Checksum][win-x86-zip-checksum-main] | [![][win-x86-badge-10.0.1XX-preview7]][win-x86-version-10.0.1XX-preview7]
[Installer][win-x86-installer-10.0.1XX-preview7] - [Checksum][win-x86-installer-checksum-10.0.1XX-preview7]
[zip][win-x86-zip-10.0.1XX-preview7] - [Checksum][win-x86-zip-checksum-10.0.1XX-preview7] | -| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] - [Checksum][win-arm64-zip-checksum-main] | [![][win-arm64-badge-10.0.1XX-preview7]][win-arm64-version-10.0.1XX-preview7]
[Installer][win-arm64-installer-10.0.1XX-preview7] - [Checksum][win-arm64-installer-checksum-10.0.1XX-preview7]
[zip][win-arm64-zip-10.0.1XX-preview7] | -| **macOS x64** | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-10.0.1XX-preview7]][osx-x64-version-10.0.1XX-preview7]
[Installer][osx-x64-installer-10.0.1XX-preview7] - [Checksum][osx-x64-installer-checksum-10.0.1XX-preview7]
[tar.gz][osx-x64-targz-10.0.1XX-preview7] - [Checksum][osx-x64-targz-checksum-10.0.1XX-preview7] | -| **macOS arm64** | [![][osx-arm64-badge-main]][osx-arm64-version-main]
[Installer][osx-arm64-installer-main] - [Checksum][osx-arm64-installer-checksum-main]
[tar.gz][osx-arm64-targz-main] - [Checksum][osx-arm64-targz-checksum-main] | [![][osx-arm64-badge-10.0.1XX-preview7]][osx-arm64-version-10.0.1XX-preview7]
[Installer][osx-arm64-installer-10.0.1XX-preview7] - [Checksum][osx-arm64-installer-checksum-10.0.1XX-preview7]
[tar.gz][osx-arm64-targz-10.0.1XX-preview7] - [Checksum][osx-arm64-targz-checksum-10.0.1XX-preview7] | -| **Linux x64** | [![][linux-badge-main]][linux-version-main]
[DEB Installer][linux-DEB-installer-main] - [Checksum][linux-DEB-installer-checksum-main]
[RPM Installer][linux-RPM-installer-main] - [Checksum][linux-RPM-installer-checksum-main]
_see installer note below_1
[tar.gz][linux-targz-main] - [Checksum][linux-targz-checksum-main] | [![][linux-badge-10.0.1XX-preview7]][linux-version-10.0.1XX-preview7]
[DEB Installer][linux-DEB-installer-10.0.1XX-preview7] - [Checksum][linux-DEB-installer-checksum-10.0.1XX-preview7]
[RPM Installer][linux-RPM-installer-10.0.1XX-preview7] - [Checksum][linux-RPM-installer-checksum-10.0.1XX-preview7]
_see installer note below_1
[tar.gz][linux-targz-10.0.1XX-preview7] - [Checksum][linux-targz-checksum-10.0.1XX-preview7] | -| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-10.0.1XX-preview7]][linux-arm-version-10.0.1XX-preview7]
[tar.gz][linux-arm-targz-10.0.1XX-preview7] - [Checksum][linux-arm-targz-checksum-10.0.1XX-preview7] | -| **Linux arm64** | [![][linux-arm64-badge-main]][linux-arm64-version-main]
[tar.gz][linux-arm64-targz-main] - [Checksum][linux-arm64-targz-checksum-main] | [![][linux-arm64-badge-10.0.1XX-preview7]][linux-arm64-version-10.0.1XX-preview7]
[tar.gz][linux-arm64-targz-10.0.1XX-preview7] - [Checksum][linux-arm64-targz-checksum-10.0.1XX-preview7] | -| **Linux-musl-x64** | [![][linux-musl-x64-badge-main]][linux-musl-x64-version-main]
[tar.gz][linux-musl-x64-targz-main] - [Checksum][linux-musl-x64-targz-checksum-main] | [![][linux-musl-x64-badge-10.0.1XX-preview7]][linux-musl-x64-version-10.0.1XX-preview7]
[tar.gz][linux-musl-x64-targz-10.0.1XX-preview7] - [Checksum][linux-musl-x64-targz-checksum-10.0.1XX-preview7] | -| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | [![][linux-musl-arm-badge-10.0.1XX-preview7]][linux-musl-arm-version-10.0.1XX-preview7]
[tar.gz][linux-musl-arm-targz-10.0.1XX-preview7] - [Checksum][linux-musl-arm-targz-checksum-10.0.1XX-preview7] | -| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | [![][linux-musl-arm64-badge-10.0.1XX-preview7]][linux-musl-arm64-version-10.0.1XX-preview7]
[tar.gz][linux-musl-arm64-targz-10.0.1XX-preview7] - [Checksum][linux-musl-arm64-targz-checksum-10.0.1XX-preview7] | - -Reference notes: -> **1**: Our Debian packages are put together slightly differently than the other OS specific installers. Instead of combining everything, we have separate component packages that depend on each other. If you're installing the SDK from the .deb file (via dpkg or similar), then you'll need to install the corresponding dependencies first: -> * [Host, Host FX Resolver, and Shared Framework](https://github.com/dotnet/runtime/blob/main/docs/project/dogfooding.md#nightly-builds-table) -> * [ASP.NET Core Shared Framework](https://github.com/aspnet/AspNetCore/blob/main/docs/DailyBuilds.md) - -#### Manually construct a link to download an SDK msi (example for dotnet-sdk msi for win-x64): -> `https://ci.dot.net/public/Sdk//dotnet-sdk--win-x64.exe` - -[win-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_x64_Release_version_badge.svg?no-cache -[win-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-x64.txt -[win-x64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.exe -[win-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.exe.sha512 -[win-x64-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.zip -[win-x64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.zip.sha512 - -[win-x64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/win_x64_Release_version_badge.svg?no-cache -[win-x64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-win-x64.txt -[win-x64-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x64.exe -[win-x64-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x64.exe.sha512 -[win-x64-zip-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x64.zip -[win-x64-zip-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x64.zip.sha512 - -[win-x86-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_x86_Release_version_badge.svg?no-cache -[win-x86-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-x86.txt -[win-x86-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.exe -[win-x86-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.exe.sha512 -[win-x86-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.zip -[win-x86-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.zip.sha512 - -[win-x86-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/win_x86_Release_version_badge.svg?no-cache -[win-x86-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-win-x86.txt -[win-x86-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x86.exe -[win-x86-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x86.exe.sha512 -[win-x86-zip-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x86.zip -[win-x86-zip-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-x86.zip.sha512 - -[osx-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/osx_x64_Release_version_badge.svg?no-cache -[osx-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-osx-x64.txt -[osx-x64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg -[osx-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.sha512 -[osx-x64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.tar.gz -[osx-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512 - -[osx-x64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/osx_x64_Release_version_badge.svg?no-cache -[osx-x64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-osx-x64.txt -[osx-x64-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-x64.pkg -[osx-x64-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-x64.pkg.sha512 -[osx-x64-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-x64.tar.gz -[osx-x64-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512 - -[osx-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/osx_arm64_Release_version_badge.svg?no-cache -[osx-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-osx-arm64.txt -[osx-arm64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg -[osx-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.sha512 -[osx-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.tar.gz -[osx-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512 - -[osx-arm64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/osx_arm64_Release_version_badge.svg?no-cache -[osx-arm64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-osx-arm64.txt -[osx-arm64-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-arm64.pkg -[osx-arm64-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-arm64.pkg.sha512 -[osx-arm64-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-arm64.tar.gz -[osx-arm64-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512 - -[linux-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_x64_Release_version_badge.svg?no-cache -[linux-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-x64.txt -[linux-DEB-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.deb -[linux-DEB-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.deb.sha512 -[linux-RPM-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.rpm -[linux-RPM-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.rpm.sha512 -[linux-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz -[linux-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz.sha512 - -[linux-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_x64_Release_version_badge.svg?no-cache -[linux-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-x64.txt -[linux-DEB-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-x64.deb -[linux-DEB-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-x64.deb.sha512 -[linux-RPM-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-x64.rpm -[linux-RPM-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-x64.rpm.sha512 -[linux-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-x64.tar.gz -[linux-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-x64.tar.gz.sha512 - -[linux-arm-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_arm_Release_version_badge.svg?no-cache -[linux-arm-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-arm.txt -[linux-arm-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz -[linux-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz.sha512 - -[linux-arm-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_arm_Release_version_badge.svg?no-cache -[linux-arm-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-arm.txt -[linux-arm-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-arm.tar.gz -[linux-arm-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-arm.tar.gz.sha512 - -[linux-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_arm64_Release_version_badge.svg?no-cache -[linux-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-arm64.txt -[linux-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz -[linux-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha512 - -[linux-arm64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_arm64_Release_version_badge.svg?no-cache -[linux-arm64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-arm64.txt -[linux-arm64-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-arm64.tar.gz -[linux-arm64-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-arm64.tar.gz.sha512 - -[linux-musl-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache -[linux-musl-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-x64.txt -[linux-musl-x64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz -[linux-musl-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512 - -[linux-musl-x64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_musl_x64_Release_version_badge.svg?no-cache -[linux-musl-x64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-musl-x64.txt -[linux-musl-x64-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-x64.tar.gz -[linux-musl-x64-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512 - -[linux-musl-arm-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache -[linux-musl-arm-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-arm.txt -[linux-musl-arm-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz -[linux-musl-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512 - -[linux-musl-arm-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_musl_arm_Release_version_badge.svg?no-cache -[linux-musl-arm-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-musl-arm.txt -[linux-musl-arm-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-arm.tar.gz -[linux-musl-arm-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512 - -[linux-musl-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache -[linux-musl-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-arm64.txt -[linux-musl-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz -[linux-musl-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512 - -[linux-musl-arm64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/linux_musl_arm64_Release_version_badge.svg?no-cache -[linux-musl-arm64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-linux-musl-arm64.txt -[linux-musl-arm64-targz-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-arm64.tar.gz -[linux-musl-arm64-targz-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512 - -[win-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_arm64_Release_version_badge.svg?no-cache -[win-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-arm64.txt -[win-arm64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.exe -[win-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.exe.sha512 -[win-arm64-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.zip -[win-arm64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.zip.sha512 - -[win-arm64-badge-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/win_arm64_Release_version_badge.svg?no-cache -[win-arm64-version-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/productCommit-win-arm64.txt -[win-arm64-installer-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-arm64.exe -[win-arm64-installer-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-arm64.exe.sha512 -[win-arm64-zip-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-arm64.zip -[win-arm64-zip-checksum-10.0.1XX-preview7]: https://aka.ms/dotnet/10.0.1xx-preview7/daily/dotnet-sdk-win-arm64.zip.sha512 +| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-10.0.2XX]][win-x64-version-10.0.2XX]
[Installer][win-x64-installer-10.0.2XX] - [Checksum][win-x64-installer-checksum-10.0.2XX]
[zip][win-x64-zip-10.0.2XX] - [Checksum][win-x64-zip-checksum-10.0.2XX] | +| **Windows x86** | [![][win-x86-badge-main]][win-x86-version-main]
[Installer][win-x86-installer-main] - [Checksum][win-x86-installer-checksum-main]
[zip][win-x86-zip-main] - [Checksum][win-x86-zip-checksum-main] | [![][win-x86-badge-10.0.2XX]][win-x86-version-10.0.2XX]
[Installer][win-x86-installer-10.0.2XX] - [Checksum][win-x86-installer-checksum-10.0.2XX]
[zip][win-x86-zip-10.0.2XX] - [Checksum][win-x86-zip-checksum-10.0.2XX] | +| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] - [Checksum][win-arm64-zip-checksum-main] | [![][win-arm64-badge-10.0.2XX]][win-arm64-version-10.0.2XX]
[Installer][win-arm64-installer-10.0.2XX] - [Checksum][win-arm64-installer-checksum-10.0.2XX]
[zip][win-arm64-zip-10.0.2XX] | +| **macOS arm64**
(M-series CPUs) | [![][osx-arm64-badge-main]][osx-arm64-version-main]
[Installer][osx-arm64-installer-main] - [Checksum][osx-arm64-installer-checksum-main]
[tar.gz][osx-arm64-targz-main] - [Checksum][osx-arm64-targz-checksum-main] | [![][osx-arm64-badge-10.0.2XX]][osx-arm64-version-10.0.2XX]
[Installer][osx-arm64-installer-10.0.2XX] - [Checksum][osx-arm64-installer-checksum-10.0.2XX]
[tar.gz][osx-arm64-targz-10.0.2XX] - [Checksum][osx-arm64-targz-checksum-10.0.2XX] | +| **macOS x64**
(Intel CPUs) | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-10.0.2XX]][osx-x64-version-10.0.2XX]
[Installer][osx-x64-installer-10.0.2XX] - [Checksum][osx-x64-installer-checksum-10.0.2XX]
[tar.gz][osx-x64-targz-10.0.2XX] - [Checksum][osx-x64-targz-checksum-10.0.2XX] | +| **Linux x64** | [![][linux-badge-main]][linux-version-main]
[DEB Installer][linux-DEB-installer-main] - [Checksum][linux-DEB-installer-checksum-main]
[RPM Installer][linux-RPM-installer-main] - [Checksum][linux-RPM-installer-checksum-main]
_see installer note below_1
[tar.gz][linux-targz-main] - [Checksum][linux-targz-checksum-main] | [![][linux-badge-10.0.2XX]][linux-version-10.0.2XX]
[DEB Installer][linux-DEB-installer-10.0.2XX] - [Checksum][linux-DEB-installer-checksum-10.0.2XX]
[RPM Installer][linux-RPM-installer-10.0.2XX] - [Checksum][linux-RPM-installer-checksum-10.0.2XX]
_see installer note below_1
[tar.gz][linux-targz-10.0.2XX] - [Checksum][linux-targz-checksum-10.0.2XX] | +| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-10.0.2XX]][linux-arm-version-10.0.2XX]
[tar.gz][linux-arm-targz-10.0.2XX] - [Checksum][linux-arm-targz-checksum-10.0.2XX] | +| **Linux arm64** | [![][linux-arm64-badge-main]][linux-arm64-version-main]
[tar.gz][linux-arm64-targz-main] - [Checksum][linux-arm64-targz-checksum-main] | [![][linux-arm64-badge-10.0.2XX]][linux-arm64-version-10.0.2XX]
[tar.gz][linux-arm64-targz-10.0.2XX] - [Checksum][linux-arm64-targz-checksum-10.0.2XX] | +| **Linux-musl-x64** | [![][linux-musl-x64-badge-main]][linux-musl-x64-version-main]
[tar.gz][linux-musl-x64-targz-main] - [Checksum][linux-musl-x64-targz-checksum-main] | [![][linux-musl-x64-badge-10.0.2XX]][linux-musl-x64-version-10.0.2XX]
[tar.gz][linux-musl-x64-targz-10.0.2XX] - [Checksum][linux-musl-x64-targz-checksum-10.0.2XX] | +| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | [![][linux-musl-arm-badge-10.0.2XX]][linux-musl-arm-version-10.0.2XX]
[tar.gz][linux-musl-arm-targz-10.0.2XX] - [Checksum][linux-musl-arm-targz-checksum-10.0.2XX] | +| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | [![][linux-musl-arm64-badge-10.0.2XX]][linux-musl-arm64-version-10.0.2XX]
[tar.gz][linux-musl-arm64-targz-10.0.2XX] - [Checksum][linux-musl-arm64-targz-checksum-10.0.2XX] | + +# .NET SDK Daily Builds + +## Installation Instructions + +### Windows + +Download the latest SDK installer or binaries from the builds table above. + +Windows SDK and Runtime installers are complete packages and do not require separate dependency installation. + +### Linux (DEB and RPM Packages) + +#### SDK Packages + +Download the appropriate SDK package for your distribution: +- **Debian/Ubuntu:** `dotnet-sdk--x64.deb` +- **Red Hat/Fedora/CentOS:** `dotnet-sdk--x64.rpm` + +#### Dependencies + +The following runtime packages are required dependencies and must be installed before installing the SDK: + +**For Debian/Ubuntu (.deb):** +- `dotnet-host--x64.deb` +- `dotnet-targeting-pack--x64.deb` +- `dotnet-hostfxr--x64.deb` +- `dotnet-apphost-pack--x64.deb` +- `dotnet-runtime-deps--x64.deb` +- `dotnet-runtime--x64.deb` + +**For Red Hat/Fedora/CentOS (.rpm):** +- `dotnet-host--x64.rpm` +- `dotnet-targeting-pack--x64.rpm` +- `dotnet-hostfxr--x64.rpm` +- `dotnet-apphost-pack--x64.rpm` +- `dotnet-runtime-deps--x64.rpm` +- `dotnet-runtime--x64.rpm` + +The following ASP.NET Core packages are required for ASP.NET Core development: + +**For Debian/Ubuntu (.deb):** +- `aspnetcore-targeting-pack--x64.deb` +- `aspnetcore-runtime--x64.deb` + +**For Red Hat/Fedora/CentOS (.rpm):** +- `aspnetcore-targeting-pack--x64.rpm` +- `aspnetcore-runtime--x64.rpm` + +These packages can be downloaded from: + +**Runtime packages:** +``` +https://ci.dot.net/public/Runtime//dotnet-host--x64. +https://ci.dot.net/public/Runtime//dotnet-targeting-pack--x64. +https://ci.dot.net/public/Runtime//dotnet-hostfxr--x64. +https://ci.dot.net/public/Runtime//dotnet-apphost-pack--x64. +https://ci.dot.net/public/Runtime//dotnet-runtime-deps--x64. +https://ci.dot.net/public/Runtime//dotnet-runtime--x64. +``` + +**ASP.NET Core packages:** +``` +https://ci.dot.net/public/aspnetcore/Runtime//aspnetcore-targeting-pack--x64. +https://ci.dot.net/public/aspnetcore/Runtime//aspnetcore-runtime--x64. +``` + +Where: +- `` is the same for both SDK and runtime (can be obtained from the version badge links or productCommit files in the table above) +- `` is either `deb` for Debian/Ubuntu or `rpm` for Red Hat/Fedora/CentOS + +#### Installation Order + +Install the packages in the following order: + +1. Runtime dependencies (in order): + - `dotnet-host--x64.` + - `dotnet-targeting-pack--x64.` + - `dotnet-hostfxr--x64.` + - `dotnet-apphost-pack--x64.` + - `dotnet-runtime-deps--x64.` + - `dotnet-runtime--x64.` + +2. ASP.NET Core dependencies (if needed): + - `aspnetcore-targeting-pack--x64.` + - `aspnetcore-runtime--x64.` + +3. Finally, install the SDK: + - `dotnet-sdk--x64.` + +**Example installation commands:** + +For Debian/Ubuntu: +```bash +sudo dpkg -i dotnet-host--x64.deb +sudo dpkg -i dotnet-targeting-pack--x64.deb +sudo dpkg -i dotnet-hostfxr--x64.deb +sudo dpkg -i dotnet-apphost-pack--x64.deb +sudo dpkg -i dotnet-runtime-deps--x64.deb +sudo dpkg -i dotnet-runtime--x64.deb +sudo dpkg -i aspnetcore-targeting-pack--x64.deb +sudo dpkg -i aspnetcore-runtime--x64.deb +sudo dpkg -i dotnet-sdk--x64.deb +``` + +For Red Hat/Fedora/CentOS: +```bash +sudo rpm -i dotnet-host--x64.rpm +sudo rpm -i dotnet-targeting-pack--x64.rpm +sudo rpm -i dotnet-hostfxr--x64.rpm +sudo rpm -i dotnet-apphost-pack--x64.rpm +sudo rpm -i dotnet-runtime-deps--x64.rpm +sudo rpm -i dotnet-runtime--x64.rpm +sudo rpm -i aspnetcore-targeting-pack--x64.rpm +sudo rpm -i aspnetcore-runtime--x64.rpm +sudo rpm -i dotnet-sdk--x64.rpm +``` + +### macOS + +Download the latest SDK installer or binaries from the builds table above. + +macOS SDK and Runtime installers are complete packages and do not require separate dependency installation. + +## Version Information + +For the 1xx band, the runtime and SDK are built together in the same build. They will differ on patch version (e.g. SDK version 10.0.100 == runtime patch version 10.0.0). The build suffix (e.g. -rc2.1234.105) will always match. + +For the 2xx and later bands, the 1xx runtime flows to 2xx+. The version of the runtime that will be used in the 2xx SDK can be found in sdk's `eng/Version.Details.xml` file. Look for the version of the `Microsoft.NETCore.App.Ref` dependency. + +[win-x64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/win_x64_Release_version_badge.svg?no-cache +[win-x64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-win-x64.txt +[win-x64-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x64.exe.sha512 +[win-x64-zip-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x64.zip.sha512 + +[win-x64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/win_x64_Release_version_badge.svg?no-cache +[win-x64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-win-x64.txt +[win-x64-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x64.exe +[win-x64-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x64.exe.sha512 +[win-x64-zip-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x64.zip +[win-x64-zip-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x64.zip.sha512 + +[win-x86-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/win_x86_Release_version_badge.svg?no-cache +[win-x86-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-win-x86.txt +[win-x86-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x86.exe +[win-x86-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x86.exe.sha512 +[win-x86-zip-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x86.zip +[win-x86-zip-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-x86.zip.sha512 + +[win-x86-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/win_x86_Release_version_badge.svg?no-cache +[win-x86-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-win-x86.txt +[win-x86-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x86.exe +[win-x86-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x86.exe.sha512 +[win-x86-zip-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x86.zip +[win-x86-zip-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-x86.zip.sha512 + +[osx-x64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/osx_x64_Release_version_badge.svg?no-cache +[osx-x64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-osx-x64.txt +[osx-x64-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-x64.pkg +[osx-x64-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-x64.pkg.sha512 +[osx-x64-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-x64.tar.gz +[osx-x64-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512 + +[osx-x64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/osx_x64_Release_version_badge.svg?no-cache +[osx-x64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-osx-x64.txt +[osx-x64-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-x64.pkg +[osx-x64-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-x64.pkg.sha512 +[osx-x64-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-x64.tar.gz +[osx-x64-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512 + +[osx-arm64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/osx_arm64_Release_version_badge.svg?no-cache +[osx-arm64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-osx-arm64.txt +[osx-arm64-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-arm64.pkg +[osx-arm64-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.sha512 +[osx-arm64-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-arm64.tar.gz +[osx-arm64-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512 + +[osx-arm64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/osx_arm64_Release_version_badge.svg?no-cache +[osx-arm64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-osx-arm64.txt +[osx-arm64-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-arm64.pkg +[osx-arm64-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-arm64.pkg.sha512 +[osx-arm64-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-arm64.tar.gz +[osx-arm64-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512 + +[linux-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_x64_Release_version_badge.svg?no-cache +[linux-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-x64.txt +[linux-DEB-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-x64.deb.sha512 +[linux-RPM-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-x64.rpm.sha512 +[linux-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz +[linux-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz.sha512 + +[linux-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_x64_Release_version_badge.svg?no-cache +[linux-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-x64.txt +[linux-DEB-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-x64.deb +[linux-DEB-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-x64.deb.sha512 +[linux-RPM-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-x64.rpm +[linux-RPM-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-x64.rpm.sha512 +[linux-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-x64.tar.gz +[linux-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-x64.tar.gz.sha512 + +[linux-arm-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_arm_Release_version_badge.svg?no-cache +[linux-arm-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-arm.txt +[linux-arm-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz +[linux-arm-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz.sha512 + +[linux-arm-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_arm_Release_version_badge.svg?no-cache +[linux-arm-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-arm.txt +[linux-arm-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-arm.tar.gz +[linux-arm-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-arm.tar.gz.sha512 + +[linux-arm64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_arm64_Release_version_badge.svg?no-cache +[linux-arm64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-arm64.txt +[linux-arm64-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz +[linux-arm64-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha512 + +[linux-arm64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_arm64_Release_version_badge.svg?no-cache +[linux-arm64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-arm64.txt +[linux-arm64-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-arm64.tar.gz +[linux-arm64-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-arm64.tar.gz.sha512 + +[linux-musl-x64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache +[linux-musl-x64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-musl-x64.txt +[linux-musl-x64-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512 + +[linux-musl-x64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_musl_x64_Release_version_badge.svg?no-cache +[linux-musl-x64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-musl-x64.txt +[linux-musl-x64-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512 + +[linux-musl-arm-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache +[linux-musl-arm-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-musl-arm.txt +[linux-musl-arm-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz +[linux-musl-arm-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512 + +[linux-musl-arm-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_musl_arm_Release_version_badge.svg?no-cache +[linux-musl-arm-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-musl-arm.txt +[linux-musl-arm-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-arm.tar.gz +[linux-musl-arm-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512 + +[linux-musl-arm64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache +[linux-musl-arm64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-linux-musl-arm64.txt +[linux-musl-arm64-targz-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz +[linux-musl-arm64-targz-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512 + +[linux-musl-arm64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/linux_musl_arm64_Release_version_badge.svg?no-cache +[linux-musl-arm64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-linux-musl-arm64.txt +[linux-musl-arm64-targz-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-arm64.tar.gz +[linux-musl-arm64-targz-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512 + +[win-arm64-badge-main]: https://aka.ms/dotnet/11.0.1xx/daily/win_arm64_Release_version_badge.svg?no-cache +[win-arm64-version-main]: https://aka.ms/dotnet/11.0.1xx/daily/productCommit-win-arm64.txt +[win-arm64-installer-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-arm64.exe +[win-arm64-installer-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-arm64.exe.sha512 +[win-arm64-zip-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-arm64.zip +[win-arm64-zip-checksum-main]: https://aka.ms/dotnet/11.0.1xx/daily/dotnet-sdk-win-arm64.zip.sha512 + +[win-arm64-badge-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/win_arm64_Release_version_badge.svg?no-cache +[win-arm64-version-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/productCommit-win-arm64.txt +[win-arm64-installer-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-arm64.exe +[win-arm64-installer-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-arm64.exe.sha512 +[win-arm64-zip-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-arm64.zip +[win-arm64-zip-checksum-10.0.2XX]: https://aka.ms/dotnet/10.0.2XX/daily/dotnet-sdk-win-arm64.zip.sha512 diff --git a/eng/PublishSourceBuild.props b/eng/PublishSourceBuild.props index a09901f786b7..380cfb96e7ed 100644 --- a/eng/PublishSourceBuild.props +++ b/eng/PublishSourceBuild.props @@ -1,4 +1,6 @@ + +
+ + + + + + + + + + - @@ -80,17 +90,33 @@ + + $(SharedComponentFilter_NonToolingOnly) + DiscoverArtifacts;GetOutputsForCreatePrivateSourceBuiltArtifactsArchive + + $(CreatePrivateSourceBuiltArtifactsArchiveDependsOn);GetFilteredSharedComponentPackages + + + + Outputs="$(SourceBuiltTarballName);$(SourceBuiltVersionName);$(AllPackageVersionsPropsName);$(SourceBuiltMergedAssetManifestName)"> + + + true + + + true + + + - @@ -117,7 +143,7 @@ UseSymbolicLinksIfPossible="true" /> - + + + + + Package + + + + + + + + dotnet-sdk- + + + + + + + + + + + + + Blob + true + + + + Blob + true + + + + Blob + true + + + + Blob + true + + + true + Blob + %(Filename)%(Extension) + + + + + <_SourceBuildArtifactsDir>$(ArtifactsAssetsDir)/source-build/ + $(_SourceBuildArtifactsDir)/$(SdkFileNamePrefix)$(SourceBuiltSdkNonStableVersion)-$(TargetRid)$(ArchiveExtension) + + + + + + + + + + + + + true + true + Blob + $([System.IO.Path]::GetFileName($(SourceBuiltPublishAssetsDir)))/%(Filename)%(Extension) + + + + + diff --git a/eng/Publishing.props b/eng/Publishing.props index 8375db8485ea..4115c507150d 100644 --- a/eng/Publishing.props +++ b/eng/Publishing.props @@ -9,6 +9,7 @@ $(ArtifactsDir)/staging/ $(ArtifactsStagingDir)/artifacts $(ArtifactsPublishStagingDir)/assets/$(Configuration) + $(SourceBuiltAssetsDir)/source-build $(ArtifactsPublishStagingDir)/packages/$(Configuration)/Shipping $(ArtifactsPublishStagingDir)/packages/$(Configuration)/NonShipping $(ArtifactsPublishStagingDir)/manifests/$(Configuration) @@ -82,44 +83,6 @@ - - - - Blob - true - - - - - - - Blob - true - - - - Blob - true - - - true - Blob - %(Filename)%(Extension) - - - - - - - true - Blob - source-build/%(Filename)%(Extension) - - - linux + linux-musl osx freebsd netbsd @@ -43,7 +44,6 @@ - $(__DistroRid) $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant) @@ -52,10 +52,10 @@ - false + false true - $(__PortableTargetOS)-$(TargetArchitecture) + $(PortableTargetOS)-$(TargetArchitecture) freebsd-$(TargetArchitecture) osx-$(TargetArchitecture) linux-$(TargetArchitecture) diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt index 5d4a5146c6cc..bb82a95e4c77 100644 --- a/eng/SignCheckExclusionsFile.txt +++ b/eng/SignCheckExclusionsFile.txt @@ -20,6 +20,12 @@ dotnet-*-internal-*-osx-*.pkg;*.pkg;; https://github.com/dotnet/source-build/iss Valleysoft.DockerCredsProvider.dll;; IGNORE-STRONG-NAME, https://github.com/dotnet/source-build/issues/4985 ILVerify.dll;; IGNORE-STRONG-NAME, https://github.com/dotnet/source-build/issues/4985 ILCompiler.Build.Tasks.dll;; IGNORE-STRONG-NAME, https://github.com/dotnet/source-build/issues/4985 +Humanizer.dll;; IGNORE-STRONG-NAME +Microsoft.ApplicationInsights.dll;; IGNORE-STRONG-NAME +Microsoft.Build.Locator.dll;; IGNORE-STRONG-NAME +Mono.Cecil.*dll;; IGNORE-STRONG-NAME +Newtonsoft.Json.dll;; IGNORE-STRONG-NAME +Spectre.Console.dll;; IGNORE-STRONG-NAME *.mibc;; IGNORE-STRONG-NAME, .mibc files are not strong-named ;; ## MACH-O ## diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 88d450b941a9..c939d51d3226 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -1,4 +1,3 @@ - - 10.0.0-beta.25411.109 - 10.0.0-beta.25411.109 + 11.0.0-beta.25618.104 + 11.0.0-beta.25618.104 + 1.1.0-beta.25619.4 @@ -17,5 +17,6 @@ This file should be imported by eng/Versions.props $(MicrosoftDotNetArcadeSdkPackageVersion) $(MicrosoftDotNetBuildManifestPackageVersion) + $(MicrosoftDotNetDarcLibPackageVersion) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2e837d8485ce..2317e2139993 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,17 +2,21 @@ - + https://github.com/dotnet/dotnet - 520c71b5277fc1f72dbec14da03ca55205d6c8e5 + a8faa5ebe301d5a258c5622975df78254e5395db - + https://github.com/dotnet/dotnet - 520c71b5277fc1f72dbec14da03ca55205d6c8e5 + a8faa5ebe301d5a258c5622975df78254e5395db - + https://github.com/dotnet/arcade-services - 396eba49bd539a8791adbe3ef60fcbaa02d60b13 + db9ea03a5ea7330d52ca996e08d50ba2f3d09ad9 + + + https://github.com/dotnet/arcade-services + db9ea03a5ea7330d52ca996e08d50ba2f3d09ad9 diff --git a/eng/Versions.props b/eng/Versions.props index 859b9ab8a1b9..9ee88d12244a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,12 +1,28 @@ - - + + + - 0.1.0 - alpha.1 - true 1 + 11 + 0 + $(MajorVersion).$(MinorVersion).$(VersionSDKMinor)00 + alpha + + + + repodefault + + + true + - 10.0.100-rc.1.25411.109 - 10.0.100-rc.1.25411.109 + 11.0.100-alpha.1.25618.104 + 11.0.100-alpha.1.25618.104 2.0.0-beta5.25208.1 - 17.12.36 + 17.12.50 6.13.1 6.13.1 + + 4.4.0 - 9.0.0 - 9.0.0 - 9.0.0 + 10.0.0 + 10.0.0 + 10.0.0 + 10.0.0 + + 10.0.100 - 13.0.3 - 10.0.0 + 13.0.4 + diff --git a/eng/VmrLayout.props b/eng/VmrLayout.props index 183f71c9de88..46e1bf3f758f 100644 --- a/eng/VmrLayout.props +++ b/eng/VmrLayout.props @@ -38,18 +38,19 @@ $(PreviouslySourceBuiltPackagesPath)PackageVersions.props $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'shared-components')) $([MSBuild]::EnsureTrailingSlash('$(CustomSharedComponentsArtifactsPath)')) + $(SharedComponentsArtifactsPath)VerticalManifest.xml + $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'extra-test-dependencies')) $([MSBuild]::NormalizeDirectory('$(SrcDir)', 'source-build-reference-packages', 'src')) $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'reference')) - SourceBuildReferencePackages - $([MSBuild]::NormalizeDirectory('$(PreviouslySourceBuiltPackagesPath)', '$(PreviouslySourceBuiltReferencePackagesDirName)')) + $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'archive')) Private.SourceBuilt.Artifacts Private.SourceBuilt.SharedComponents Private.SourceBuilt.Prebuilts + dotnet-source dotnet-symbols-all dotnet-symbols-sdk - $(ToolsDir)prebuilt-baseline.xml $([MSBuild]::NormalizeDirectory('$(ArtifactsObjDir)', 'PackageVersions')) @@ -65,8 +66,12 @@ - .prebuilt.xml - $(ArtifactsLogDir)poison-catalog.xml + .prebuilt.xml + $(ArtifactsLogDir)poison-prebuilt-catalog.xml + .previously-source-built.xml + $(ArtifactsLogDir)poison-previously-source-built-catalog.xml + .shared-component.xml + $(ArtifactsLogDir)poison-shared-component-catalog.xml $(ArtifactsLogDir)poisoned.txt $(ArtifactsLogDir)poison-usage.xml @@ -93,6 +98,13 @@ + + + + + @(SharedComponentsPrereqsArchivePathItem) + + @@ -103,7 +115,6 @@ - diff --git a/eng/allowed-sb-binaries.txt b/eng/allowed-sb-binaries.txt index c3612631e390..d3845b3e5bfe 100644 --- a/eng/allowed-sb-binaries.txt +++ b/eng/allowed-sb-binaries.txt @@ -9,6 +9,7 @@ artifacts/ .git/ .packages/ prereqs/packages/ +MicroBuild/ # This is introduced in real and test signed CI builds. Remove with https://github.com/dotnet/arcade/issues/15731 **/*.bmp **/*.doc diff --git a/eng/allowed-vmr-binaries.txt b/eng/allowed-vmr-binaries.txt index cc918a471b70..0bacaa9e1ff6 100644 --- a/eng/allowed-vmr-binaries.txt +++ b/eng/allowed-vmr-binaries.txt @@ -5,8 +5,6 @@ # Import the allowed souce-build binaries (a stricter set). import:allowed-sb-binaries.txt -MicroBuild/ - **/testCert*.pfx **/TestCert*.pfx @@ -105,7 +103,6 @@ src/runtime/src/mono/mono/eglib/test/*.txt src/runtime/src/mono/mono/tests/exiting/*.out src/runtime/src/mono/wasm/testassets/**/*.dat src/runtime/src/mono/wasm/testassets/**/*.o -src/runtime/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED/*.mibc src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED/*.nettrace src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx diff --git a/eng/build.ps1 b/eng/build.ps1 index 3d4fcc1c056d..4d6eaa269c1d 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -7,8 +7,8 @@ Param( [string][Alias('os')]$targetOS, [string][Alias('arch')]$targetArch, [string][Alias('v')]$verbosity = "minimal", - [Parameter()][ValidateSet("preview", "rtm", "default")] - [string]$branding = "default", + [Parameter()][ValidateSet("repodefault", "unstable", "preview", "release")] + [string]$branding, [Parameter()][ValidatePattern("^\d{8}\.\d{1,3}$")] [string][Alias('obid')]$officialBuildId, @@ -39,7 +39,12 @@ function Get-Usage() { Write-Host " -os, -targetOS Target operating system: e.g. windows." Write-Host " -arch, -targetArch Target architecture: e.g. x64, x86, arm64, arm, riscv64" Write-Host " -verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)" - Write-Host " -branding Specify versioning for shipping packages/assets. 'preview' will produce assets suffixed with '.final', 'rtm' will not contain a pre-release suffix. Default or unspecified will use VMR repo defaults." + Write-Host " -branding Specify the branding suffix for shipping packages/assets. By default uses VMR branding defaults (RepoDotNetFinalVersionKind property in eng/Versions.props)" + Write-Host " for release branch builds or otherwise repo branding defaults." + Write-Host " 'repodefault' uses the repo branding defaults." + Write-Host " 'unstable' produces assets with the repo specified branding suffix." + Write-Host " 'preview' produces assets with a '.final' branding suffix." + Write-Host " 'release' produces assets without a branding suffix." Write-Host " -officialBuildId Official build ID to use for the build. This is used to set the OfficialBuildId MSBuild property." Write-Host "" @@ -91,13 +96,7 @@ if ($targetArch) { $arguments += "/p:TargetArchitecture=$targetArch" } if ($sign) { $arguments += "/p:DotNetBuildSign=true" } if ($buildRepoTests) { $arguments += "/p:DotNetBuildTests=true" } if ($cleanWhileBuilding) { $arguments += "/p:CleanWhileBuilding=true" } -if ($branding) { - switch ($branding) { - "preview" { $arguments += "/p:DotNetFinalVersionKind=prerelease" } - "rtm" { $arguments += "/p:DotNetFinalVersionKind=release" } - "default" { $arguments += "" } - } -} +if ($branding) { $arguments += "/p:RepoDotNetFinalVersionKind=$branding" } if ($officialBuildId) { $arguments += "/p:OfficialBuildId=$officialBuildId" } function Build { diff --git a/eng/clean-shared-components.proj b/eng/clean-shared-components.proj new file mode 100644 index 000000000000..96cf4115f292 --- /dev/null +++ b/eng/clean-shared-components.proj @@ -0,0 +1,33 @@ + + + + + $(NetCurrent) + true + $(SharedComponentFilter_BootstrapOnly) + + + + + + + + + + <_BootstrapPackagesToDelete Include="@(SharedComponentFilteredPackages->'%(PackagePath)')" /> + + + + + + + diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index 5db4ad71ee2f..fc8d618014e0 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -7,11 +7,11 @@ # 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: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 +# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token # env: # Token: $(dn-bot-dnceng-artifact-feeds-rw) # @@ -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') +$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 4604b61b0323..b97cc536379d 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -11,8 +11,8 @@ # - task: Bash@3 # displayName: Setup Internal Feeds # inputs: -# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh -# arguments: $(Build.SourcesDirectory)/NuGet.config +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh +# arguments: $(System.DefaultWorkingDirectory)/NuGet.config # condition: ne(variables['Agent.OS'], 'Windows_NT') # - task: NuGetAuthenticate@1 # @@ -52,78 +52,124 @@ 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 (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") + 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 -DotNetVersions=('5' '6' '7' '8' '9') +# 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 +185,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/eng/common/build.sh b/eng/common/build.sh index 9767bb411a4f..ec3e80d189ea 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -92,7 +92,7 @@ runtime_source_feed='' runtime_source_feed_key='' properties=() -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -help|-h) diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index d9013251542c..748c4f07a64d 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -19,6 +19,8 @@ parameters: # publishing defaults artifacts: '' enableMicrobuild: false + enablePreviewMicrobuild: false + microbuildPluginVersion: 'latest' enableMicrobuildForMacAndLinux: false microbuildUseESRP: true enablePublishBuildArtifacts: false @@ -71,6 +73,8 @@ jobs: templateContext: ${{ parameters.templateContext }} variables: + - name: AllowPtrToDetectTestRunRetryFiles + value: true - ${{ if ne(parameters.enableTelemetry, 'false') }}: - name: DOTNET_CLI_TELEMETRY_PROFILE value: '$(Build.Repository.Uri)' @@ -128,6 +132,8 @@ jobs: - template: /eng/common/core-templates/steps/install-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} microbuildUseESRP: ${{ parameters.microbuildUseESRP }} continueOnError: ${{ parameters.continueOnError }} @@ -153,6 +159,8 @@ jobs: - template: /eng/common/core-templates/steps/cleanup-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} continueOnError: ${{ parameters.continueOnError }} @@ -163,7 +171,7 @@ jobs: inputs: testResultsFormat: 'xUnit' testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -174,7 +182,7 @@ jobs: inputs: testResultsFormat: 'VSTest' testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -218,7 +226,7 @@ jobs: - task: CopyFiles@2 displayName: Gather buildconfiguration for build retry inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/common/BuildConfiguration' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' continueOnError: true diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml index 8bf7d23355bc..c5788829a872 100644 --- a/eng/common/core-templates/job/onelocbuild.yml +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -8,7 +8,7 @@ parameters: CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - SourcesDirectory: $(Build.SourcesDirectory) + SourcesDirectory: $(System.DefaultWorkingDirectory) CreatePr: true AutoCompletePr: false ReusePr: true @@ -68,7 +68,7 @@ jobs: - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - task: Powershell@2 inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/generate-locproject.ps1 arguments: $(_GenerateLocProjectArguments) displayName: Generate LocProject.json condition: ${{ parameters.condition }} @@ -103,7 +103,7 @@ jobs: - task: CopyFiles@2 displayName: Copy LocProject.json inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/Localize/' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/Localize/' Contents: 'LocProject.json' TargetFolder: '$(Build.ArtifactStagingDirectory)/loc' condition: ${{ parameters.condition }} diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index d5303229c97e..8b5c635fe807 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -38,6 +38,10 @@ parameters: # Optional: A minimatch pattern for the asset manifests to publish to BAR assetManifestsPattern: '*/manifests/**/*.xml' + repositoryAlias: self + + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -60,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) @@ -78,12 +87,12 @@ jobs: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - checkout: self + - checkout: ${{ parameters.repositoryAlias }} fetchDepth: 3 clean: true - - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: - - ${{ if eq(parameters.publishingVersion, 3) }}: + - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: + - ${{ if eq(parameters.publishingVersion, 3) }}: - task: DownloadPipelineArtifact@2 displayName: Download Asset Manifests inputs: @@ -108,24 +117,35 @@ jobs: flattenFolders: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - 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: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' + condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: powershell@2 displayName: Create ReleaseConfigs Artifact inputs: @@ -137,7 +157,7 @@ jobs: Add-Content -Path $filePath -Value "$(DefaultChannels)" Add-Content -Path $filePath -Value $(IsStableBuild) - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + $symbolExclusionfile = "$(System.DefaultWorkingDirectory)/eng/SymbolPublishingExclusionsFile.txt" if (Test-Path -Path $symbolExclusionfile) { Write-Host "SymbolExclusionFile exists" @@ -153,7 +173,7 @@ jobs: artifactName: AssetManifests displayName: 'Publish Merged Manifest' retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + sbomEnabled: false # we don't need SBOM for logs - template: /eng/common/core-templates/steps/publish-build-artifacts.yml parameters: @@ -170,6 +190,11 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} 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 @@ -177,7 +202,7 @@ jobs: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion 3 @@ -186,9 +211,11 @@ 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 parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - JobLabel: 'Publish_Artifacts_Logs' + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index d805d5faeb94..9d820f974211 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -60,10 +60,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.2004.amd64 + demands: ImageOverride -equals build.ubuntu.2204.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: 1es-azurelinux-3 os: linux ${{ else }}: pool: diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml index 30530359a5d6..76baf5c27258 100644 --- a/eng/common/core-templates/job/source-index-stage1.yml +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -3,7 +3,7 @@ parameters: sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] binlogPath: artifacts/log/Debug/Build.binlog - condition: '' + condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') dependsOn: '' pool: '' is1ESPipeline: '' @@ -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, '') }}: @@ -41,4 +41,4 @@ jobs: - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml parameters: - binLogPath: ${{ parameters.binLogPath }} \ No newline at end of file + binLogPath: ${{ parameters.binLogPath }} diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml index 693b00b37044..dbc14ac580a2 100644 --- a/eng/common/core-templates/jobs/codeql-build.yml +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -24,7 +24,7 @@ jobs: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config - name: GuardianVersion value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index 2f992b2c6ecc..01ada7476651 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -43,6 +43,8 @@ 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. @@ -114,3 +116,5 @@ jobs: enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index a151fd811e3e..06864cd1feb8 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -1,106 +1,106 @@ parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: requireDefaultChannels - displayName: Fail the build if there are no default channel(s) registrations for the current build - type: boolean - default: false - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - - name: isAssetlessBuild - type: boolean - displayName: Is Assetless Build - default: false - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - - - name: is1ESPipeline - type: boolean - default: false +# Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. +# Publishing V1 is no longer supported +# Publishing V2 is no longer supported +# Publishing V3 is the default +- name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + +- name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + +- name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + +- name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + +- name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + +- name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + +- name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + +- name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + +- name: requireDefaultChannels + displayName: Fail the build if there are no default channel(s) registrations for the current build + type: boolean + default: false + +- name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + +- name: isAssetlessBuild + type: boolean + displayName: Is Assetless Build + default: false + +# These parameters let the user customize the call to sdk-task.ps1 for publishing +# symbols & general artifacts as well as for signing validation +- name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + +- name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + +- name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + +# Which stages should finish execution before post-build stages start +- name: validateDependsOn + type: object + default: + - build + +- name: publishDependsOn + type: object + default: + - Validate + +# Optional: Call asset publishing rather than running in a separate stage +- name: publishAssetsImmediately + type: boolean + default: false + +- name: is1ESPipeline + type: boolean + default: false stages: - ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: @@ -108,10 +108,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Validate Build Assets variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: NuGet Validation @@ -127,35 +127,35 @@ 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 - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - job: displayName: Signing Validation @@ -169,54 +169,54 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - # 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 - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: /eng/common/core-templates/steps/publish-logs.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + # 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 + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) - job: displayName: SourceLink Validation @@ -230,41 +230,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/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 + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + 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 - ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - stage: publish_using_darc @@ -274,10 +274,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Publish using Darc variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: Publish Using Darc @@ -291,30 +291,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal image: windows.vs2019.amd64 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - task: NuGetAuthenticate@1 - - - task: AzureCLI@2 - displayName: Publish Using Darc - inputs: - azureSubscription: "Darc: Maestro Production" - scriptType: ps - scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: > + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - 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 + + - task: UseDotNet@2 + inputs: + version: 8.0.x + + - task: AzureCLI@2 + displayName: Publish Using Darc + inputs: + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(System.AccessToken)' @@ -323,3 +334,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/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml index f7602980dbe7..a7abd58c4bb6 100644 --- a/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -36,7 +36,7 @@ steps: $AzureDevOpsBuildId = $Env:Build_BuildId } else { - . $(Build.SourcesDirectory)\eng\common\tools.ps1 + . $(System.DefaultWorkingDirectory)\eng\common\tools.ps1 $darc = Get-Darc $buildInfo = & $darc get-build ` --id ${{ parameters.BARBuildId }} ` diff --git a/eng/common/core-templates/steps/enable-internal-sources.yml b/eng/common/core-templates/steps/enable-internal-sources.yml index 64f881bffc3c..4085512b6909 100644 --- a/eng/common/core-templates/steps/enable-internal-sources.yml +++ b/eng/common/core-templates/steps/enable-internal-sources.yml @@ -17,8 +17,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token env: Token: ${{ parameters.legacyCredential }} # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. @@ -29,8 +29,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config - ${{ else }}: - template: /eng/common/templates/steps/get-federated-access-token.yml parameters: @@ -39,8 +39,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) # This is required in certain scenarios to install the ADO credential provider. # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others # (e.g. dotnet msbuild). diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml index 44a9636cdff9..003f7eae0fa5 100644 --- a/eng/common/core-templates/steps/generate-sbom.yml +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -5,8 +5,8 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageVersion: 11.0.0 + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom IgnoreDirectories: '' diff --git a/eng/common/core-templates/steps/install-microbuild-impl.yml b/eng/common/core-templates/steps/install-microbuild-impl.yml new file mode 100644 index 000000000000..b9e0143ee92a --- /dev/null +++ b/eng/common/core-templates/steps/install-microbuild-impl.yml @@ -0,0 +1,34 @@ +parameters: + - name: microbuildTaskInputs + type: object + default: {} + + - name: microbuildEnv + type: object + default: {} + + - name: enablePreviewMicrobuild + type: boolean + default: false + + - name: condition + type: string + + - name: continueOnError + type: boolean + +steps: +- ${{ if eq(parameters.enablePreviewMicrobuild, 'true') }}: + - task: MicroBuildSigningPluginPreview@4 + displayName: Install Preview MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} +- ${{ else }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index d6b9878f54db..4f4b56ed2a6b 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -4,6 +4,8 @@ parameters: # Enable install tasks for MicroBuild on Mac and Linux # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT' enableMicrobuildForMacAndLinux: false + # Enable preview version of MB signing plugin + enablePreviewMicrobuild: false # Determines whether the ESRP service connection information should be passed to the signing plugin. # This overlaps with _SignType to some degree. We only need the service connection for real signing. # It's important that the service connection not be passed to the MicroBuildSigningPlugin task in this place. @@ -11,9 +13,10 @@ 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 + # Microbuild version + microbuildPluginVersion: 'latest' continueOnError: false @@ -26,8 +29,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: | @@ -51,41 +73,46 @@ steps: # YAML expansion, and Windows vs. Linux/Mac uses different service connections. However, # we can avoid including the MB install step if not enabled at all. This avoids a bunch of # extra pipeline authorizations, since most pipelines do not sign on non-Windows. - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin (Windows) - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - ${{ if eq(parameters.microbuildUseESRP, true) }}: - ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea - ${{ else }}: - ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) - - - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin (non-Windows) - inputs: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: signType: $(_SignType) zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} ${{ if eq(parameters.microbuildUseESRP, true) }}: ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea ${{ else }}: - ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc - env: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca + microbuildEnv: TeamName: $(_TeamName) MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) + + - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + workingDirectory: ${{ parameters.microBuildOutputFolder }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ${{ else }}: + ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc + microbuildEnv: + TeamName: $(_TeamName) + 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/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index de24d0087c58..5a927b4c7bcb 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -12,22 +12,24 @@ steps: inputs: targetType: inline script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + New-Item -ItemType Directory $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(System.DefaultWorkingDirectory)/artifacts/log/Debug/* $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ continueOnError: true condition: always() - task: PowerShell@2 displayName: Redact Logs inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/redact-logs.ps1 # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml - # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # Sensitive data can as well be added to $(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' # 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 '$(Build.SourcesDirectory)/PostBuildLogs' - -BinlogToolVersion ${{parameters.BinlogToolVersion}} - -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + arguments: -InputPath '$(System.DefaultWorkingDirectory)/PostBuildLogs' + -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)' @@ -44,7 +46,7 @@ steps: - task: CopyFiles@2 displayName: Gather post build logs inputs: - SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + SourceFolder: '$(System.DefaultWorkingDirectory)/PostBuildLogs' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' condition: always() diff --git a/eng/common/core-templates/steps/source-index-stage1-publish.yml b/eng/common/core-templates/steps/source-index-stage1-publish.yml index c2917c1efc1c..ac019e2d0337 100644 --- a/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250425.2 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250515.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog @@ -14,13 +14,13 @@ steps: workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: "Source Index: Download netsourceindex Tools" # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) -- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output +- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(System.DefaultWorkingDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output displayName: "Source Index: Process Binlog into indexable sln" - ${{ if and(ne(parameters.runAsPublic, 'true'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 8abfb71f7275..9b7eede4e50f 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -72,7 +72,7 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.4-RELEASE" +__FreeBSDBase="13.5-RELEASE" __FreeBSDPkg="1.21.3" __FreeBSDABI="13" __FreeBSDPackages="libunwind" @@ -383,7 +383,7 @@ while :; do ;; freebsd14) __CodeName=freebsd - __FreeBSDBase="14.2-RELEASE" + __FreeBSDBase="14.3-RELEASE" __FreeBSDABI="14" __SkipUnmount=1 ;; diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index e889f439b8dc..9f5ad6b763b5 100755 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -5,7 +5,7 @@ darcVersion='' versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2020-02-20' verbosity='minimal' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --darcversion) diff --git a/eng/common/dotnet-install.sh b/eng/common/dotnet-install.sh index 7b9d97e3bd4d..61f302bb6775 100755 --- a/eng/common/dotnet-install.sh +++ b/eng/common/dotnet-install.sh @@ -18,7 +18,7 @@ architecture='' runtime='dotnet' runtimeSourceFeed='' runtimeSourceFeedKey='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in -version|-v) diff --git a/eng/common/dotnet.sh b/eng/common/dotnet.sh index 2ef68235675f..f6d24871c1d4 100644 --- a/eng/common/dotnet.sh +++ b/eng/common/dotnet.sh @@ -19,7 +19,7 @@ source $scriptroot/tools.sh InitializeDotNetCli true # install # Invoke acquired SDK with args if they are provided -if [[ $# > 0 ]]; then +if [[ $# -gt 0 ]]; then __dotnetDir=${_InitializeDotNetCli} dotnetPath=${__dotnetDir}/dotnet ${dotnetPath} "$@" diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index 524aaa57f2b7..fa1cdc2b3007 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -33,15 +33,27 @@ $jsonTemplateFiles | ForEach-Object { $jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$wxlFilesV3 = @() +$wxlFilesV5 = @() $wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them if (-not $wxlFiles) { $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files if ($wxlEnFiles) { - $wxlFiles = @() - $wxlEnFiles | ForEach-Object { - $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" - $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru - } + $wxlFiles = @() + $wxlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $content = Get-Content $_.FullName -Raw + + # Split files on schema to select different parser settings in the generated project. + if ($content -like "*http://wixtoolset.org/schemas/v4/wxl*") + { + $wxlFilesV5 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + elseif ($content -like "*http://schemas.microsoft.com/wix/2006/localization*") + { + $wxlFilesV3 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + } } } @@ -114,7 +126,32 @@ $locJson = @{ CloneLanguageSet = "WiX_CloneLanguages" LssFiles = @( "wxl_loc.lss" ) LocItems = @( - $wxlFiles | ForEach-Object { + $wxlFilesV3 | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "WiX_CloneLanguages" + LssFiles = @( "P210WxlSchemaV4.lss" ) + LocItems = @( + $wxlFilesV5 | ForEach-Object { $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" $continue = $true foreach ($exclusion in $exclusions.Exclusions) { diff --git a/eng/common/internal-feed-operations.ps1 b/eng/common/internal-feed-operations.ps1 index 92b77347d990..c282d3ae403a 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/internal-feed-operations.sh b/eng/common/internal-feed-operations.sh index 9378223ba095..6299e7effd4c 100755 --- a/eng/common/internal-feed-operations.sh +++ b/eng/common/internal-feed-operations.sh @@ -100,7 +100,7 @@ operation='' authToken='' repoName='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --operation) diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh index 477a44f335be..64b87d0bcc3c 100755 --- a/eng/common/native/install-dependencies.sh +++ b/eng/common/native/install-dependencies.sh @@ -27,9 +27,11 @@ case "$os" in libssl-dev libkrb5-dev pigz cpio localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 - elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then + elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ] || [ "$ID" = "centos"]; then pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)" $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio + elif [ "$ID" = "amzn" ]; then + dnf install -y cmake llvm lld lldb clang python libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio elif [ "$ID" = "alpine" ]; then apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio else diff --git a/eng/common/post-build/nuget-verification.ps1 b/eng/common/post-build/nuget-verification.ps1 index a365194a9389..eea88e653c91 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 @@ -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/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 1eda208a3bbf..48e55598bdd2 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 b7fc19591507..fc0218a013d1 100644 --- a/eng/common/post-build/redact-logs.ps1 +++ b/eng/common/post-build/redact-logs.ps1 @@ -7,7 +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 { diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index a9d2a2d26996..b64b66a6275b 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -7,13 +7,16 @@ Param( [switch] $restore, [switch] $prepareMachine, [switch][Alias('nobl')]$excludeCIBinaryLog, + [switch]$noWarnAsError, [switch] $help, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) $ci = $true $binaryLog = if ($excludeCIBinaryLog) { $false } else { $true } -$warnAsError = $true +$warnAsError = if ($noWarnAsError) { $false } else { $true } . $PSScriptRoot\tools.ps1 @@ -67,7 +70,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.13.0" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "18.0.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/sdk-task.sh b/eng/common/sdk-task.sh index 2f83adc0269f..3270f83fa9a7 100755 --- a/eng/common/sdk-task.sh +++ b/eng/common/sdk-task.sh @@ -10,6 +10,7 @@ show_usage() { echo "Advanced settings:" echo " --excludeCIBinarylog Don't output binary log (short: -nobl)" + echo " --noWarnAsError Do not warn as error" echo "" echo "Command line arguments not listed above are passed thru to msbuild." } @@ -52,6 +53,7 @@ exclude_ci_binary_log=false restore=false help=false properties='' +warnAsError=true while (($# > 0)); do lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" @@ -73,6 +75,10 @@ while (($# > 0)); do exclude_ci_binary_log=true shift 1 ;; + --noWarnAsError) + warnAsError=false + shift 1 + ;; --help) help=true shift 1 @@ -85,7 +91,6 @@ while (($# > 0)); do done ci=true -warnAsError=true if $help; then show_usage diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md index 98bbc1ded0ba..4bf4cf41bd7c 100644 --- a/eng/common/template-guidance.md +++ b/eng/common/template-guidance.md @@ -50,7 +50,7 @@ extends: - task: CopyFiles@2 displayName: Gather build output inputs: - SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + SourceFolder: '$(System.DefaultWorkingDirectory)/artifacts/marvel' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' ``` diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index a8a943287458..92a0664f5647 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -3,7 +3,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml diff --git a/eng/common/templates-official/variables/sdl-variables.yml b/eng/common/templates-official/variables/sdl-variables.yml index dbdd66d4a4b3..f1311bbb1b33 100644 --- a/eng/common/templates-official/variables/sdl-variables.yml +++ b/eng/common/templates-official/variables/sdl-variables.yml @@ -4,4 +4,4 @@ variables: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 7cbf668c22bc..238fa0818f7b 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -6,7 +6,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml @@ -77,7 +77,7 @@ jobs: parameters: is1ESPipeline: false args: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + targetPath: '$(System.DefaultWorkingDirectory)\eng\common\BuildConfiguration' artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index d4cfd9ccd806..f6bde2683794 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -157,9 +157,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { return $global:_DotNetInstallDir } - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - $env:DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we do not need all ASP.NET packages restored. $env:DOTNET_NOLOGO=1 @@ -225,7 +222,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build Write-PipelinePrependPath -Path $dotnetRoot - Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' return $global:_DotNetInstallDir = $dotnetRoot @@ -277,7 +273,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { Retry({ Write-Host "GET $uri" - Invoke-WebRequest $uri -OutFile $installScript + Invoke-WebRequest $uri -UseBasicParsing -OutFile $installScript }) } @@ -394,8 +390,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.13.0 - $defaultXCopyMSBuildVersion = '17.13.0' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/18.0.0 + $defaultXCopyMSBuildVersion = '18.0.0' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -510,7 +506,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)) { @@ -544,7 +540,8 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') { $vswhereVersion = $GlobalJson.tools.vswhere } else { - $vswhereVersion = '2.5.2' + # keep this in sync with the VSWhereVersion in DefaultVersions.props + $vswhereVersion = '3.1.7' } $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion" @@ -552,25 +549,33 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (!(Test-Path $vsWhereExe)) { Create-Directory $vsWhereDir - Write-Host 'Downloading vswhere' + 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 }) } - 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 diff --git a/eng/common/tools.sh b/eng/common/tools.sh index c1841c9dfd0f..6c121300ac7d 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -115,9 +115,6 @@ function InitializeDotNetCli { local install=$1 - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - export DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we want to control all package sources export DOTNET_NOLOGO=1 @@ -166,7 +163,6 @@ function InitializeDotNetCli { # build steps from using anything other than what we've downloaded. Write-PipelinePrependPath -path "$dotnet_root" - Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" # return value diff --git a/eng/dependabot/Directory.Packages.props b/eng/dependabot/Directory.Packages.props new file mode 100644 index 000000000000..25a158bb5f38 --- /dev/null +++ b/eng/dependabot/Directory.Packages.props @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/eng/dependabot/global.json b/eng/dependabot/global.json new file mode 100644 index 000000000000..9e26dfeeb6e6 --- /dev/null +++ b/eng/dependabot/global.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/eng/download-source-built-archive.sh b/eng/download-source-built-archive.sh new file mode 100755 index 000000000000..9d50110add14 --- /dev/null +++ b/eng/download-source-built-archive.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# Common helper functions for downloading archives based on versions in XML files + +# Get the repository root directory +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +PACKAGE_VERSIONS_PATH="$REPO_ROOT/eng/Versions.props" +PACKAGE_VERSION_DETAILS_PATH="$REPO_ROOT/eng/Version.Details.props" + +# Helper to extract a property value from an XML file +function GetXmlPropertyValue { + local propName="$1" + local filePath="$2" + local value="" + local line pattern + line=$(grep -m 1 "<$propName>" "$filePath" || :) + pattern="<$propName>(.*)" + if [[ $line =~ $pattern ]]; then + value="${BASH_REMATCH[1]}" + fi + echo "$value" +} + +# Helper to download a file with retries +function DownloadWithRetries { + local url="$1" + local targetDir="$2" + ( + cd "$targetDir" && + for i in {1..5}; do + if curl -fL --retry 5 -O "$url"; then + return 0 + else + local exitCode=$? + case $exitCode in + 22) + # HTTP error (including 404) - don't retry + return 22 + ;; + *) + # For all other errors (including partial transfers), sleep and retry + sleep 3 + ;; + esac + fi + done + return 1 + ) +} + +# Generic function to download an archive based on property name +function DownloadArchive { + local label="$1" + local propertyName="$2" + local isRequired="$3" + local artifactsRid="$4" + local outputDir="$5" + local destinationFileNamePrefix="${6:-}" + local versionPropertyOverride="${7:-}" + local storageKey="${8:-}" + + local notFoundMessage="No $label found to download..." + + local archiveVersion + local versionsPath + local versionProperty="${versionPropertyOverride:-$propertyName}" + if [[ "$versionProperty" == "MicrosoftNETSdkPackageVersion" ]]; then + versionsPath="$PACKAGE_VERSION_DETAILS_PATH" + else + versionsPath="$PACKAGE_VERSIONS_PATH" + fi + archiveVersion=$(GetXmlPropertyValue "$versionProperty" "$versionsPath") + if [[ -z "$archiveVersion" ]]; then + if [ "$isRequired" == true ]; then + echo " ERROR: $notFoundMessage" + return 1 + else + echo " $notFoundMessage" + return 0 + fi + fi + + local archiveUrl + local artifactsBaseFileName="Private.SourceBuilt.Artifacts" + local prebuiltsBaseFileName="Private.SourceBuilt.Prebuilts" + local sdkBaseFileName="dotnet-sdk" + local defaultArtifactsRid='centos.10-x64' + + local versionDelimiter="." + if [[ "$propertyName" == "PrivateSourceBuiltSdkVersion" ]]; then + versionDelimiter="-" + fi + + archiveVersion="${versionDelimiter}${archiveVersion}${versionDelimiter}" + + # Build array of base URL entries to try in order + # Each entry is in the format "url" or "url=storageKey" + local baseUrls=() + + # Assets for the default RID are hosted in builds.dotnet.microsoft.com first + if [[ $artifactsRid == $defaultArtifactsRid ]]; then + baseUrls+=("https://builds.dotnet.microsoft.com/dotnet/source-build") + fi + + # Always try public CI location + baseUrls+=("https://ci.dot.net/public/source-build") + + # Try internal CI location if storage key is provided + if [[ -n "$storageKey" ]]; then + baseUrls+=("https://ci.dot.net/internal/source-build=$storageKey") + fi + + # Determine the archive filename based on property name + local archiveFileName + if [[ "$propertyName" == "PrivateSourceBuiltPrebuiltsVersion" ]]; then + archiveFileName="${prebuiltsBaseFileName}${archiveVersion}${defaultArtifactsRid}.tar.gz" + elif [[ "$propertyName" == "PrivateSourceBuiltArtifactsVersion" ]]; then + archiveFileName="${artifactsBaseFileName}${archiveVersion}${artifactsRid}.tar.gz" + elif [[ "$propertyName" == "PrivateSourceBuiltSdkVersion" ]]; then + archiveFileName="${sdkBaseFileName}${archiveVersion}${artifactsRid}.tar.gz" + else + echo " ERROR: Unknown archive property name: $propertyName" + return 1 + fi + + local archiveUrl + local displayUrl + local downloadedFilename=$(basename "${archiveFileName}") + local downloadSucceeded=false + + # Try each base URL in order + for entry in "${baseUrls[@]}"; do + # Parse the entry: "url" or "url=storageKey" + local baseUrl="${entry%%=*}" + local entryStorageKey="" + if [[ "$entry" == *"="* ]]; then + entryStorageKey="${entry#*=}" + fi + + archiveUrl="${baseUrl}/${archiveFileName}" + displayUrl="$archiveUrl" + + # Append storage key as query parameter if present + if [[ -n "$entryStorageKey" ]]; then + local decodedKey + decodedKey=$(echo "$entryStorageKey" | base64 -d) + archiveUrl="${archiveUrl}?${decodedKey}" + displayUrl="${displayUrl}?[redacted]" + fi + + echo " Downloading $label from $displayUrl..." + if DownloadWithRetries "$archiveUrl" "$outputDir"; then + downloadSucceeded=true + break + else + local downloadResult=$? + # Only continue to next URL if it was a 404 + if [[ $downloadResult -ne 22 ]]; then + echo " ERROR: Failed to download $displayUrl" + return 1 + fi + echo " Not found, trying next location..." + fi + done + + if [[ "$downloadSucceeded" == false ]]; then + echo " ERROR: Failed to download from all available locations" + return 1 + fi + + # Rename the file if a destination filename prefix is provided + if [[ -n "$destinationFileNamePrefix" ]]; then + # Extract the suffix from the downloaded filename + local baseFilenameForSuffix="$artifactsBaseFileName" + if [[ "$propertyName" == *Prebuilts* ]]; then + baseFilenameForSuffix="$prebuiltsBaseFileName" + elif [[ "$propertyName" == *Sdk* ]]; then + baseFilenameForSuffix="$sdkBaseFileName" + fi + local suffix="${downloadedFilename#$baseFilenameForSuffix}" + local newFilename="$destinationFileNamePrefix$suffix" + mv "$outputDir/$downloadedFilename" "$outputDir/$newFilename" + echo " Renamed $downloadedFilename to $newFilename" + fi + + return 0 +} diff --git a/eng/finish-source-only.proj b/eng/finish-source-only.proj index 5fda89ea08d5..f6ab5c028cc5 100644 --- a/eng/finish-source-only.proj +++ b/eng/finish-source-only.proj @@ -6,7 +6,8 @@ false - $(ArtifactsLogDir)prebuilt-usage.xml + $(ArtifactsLogDir)prebuilt-usage.xml + $(ArtifactsLogDir)prebuilt-annotated-usage.xml $(ArtifactsLogDir)all-project-assets-json-files.zip $([MSBuild]::NormalizeDirectory('$(ArtifactsLogDir)', 'prebuilt-packages')) @@ -65,18 +66,19 @@ - + + + @@ -101,45 +103,38 @@ - - - + - - false - - - - - - false - true + + + + + Prebuilt packages are not allowed in source-only builds.%0A%0A + $(PrebuiltErrorMessage)Detected @(DetectedPrebuiltPackage->Count()) prebuilt package(s):%0A + $(PrebuiltErrorMessage)@(DetectedPrebuiltPackage->' - %(Identity)', '%0A')%0A%0A + $(PrebuiltErrorMessage)For detailed usage information, see: $(PrebuiltAnnotatedUsageReportFile)%0A%0A + $(PrebuiltErrorMessage)See https://aka.ms/dotnet/prebuilts for guidance on what prebuilts are and how to eliminate them. - + @@ -169,7 +164,8 @@ - $([System.Text.RegularExpressions.Regex]::Split('$([System.IO.File]::ReadAllText('%(SdkVersionFileItem.Identity)'))', '\r\n|\r|\n')[3]) + %(SdkVersionFileItem.Identity) + $([System.Text.RegularExpressions.Regex]::Split('$([System.IO.File]::ReadAllText('$(SourceBuiltSdkVersionFile)'))', '\r\n|\r|\n')[3]) @@ -177,26 +173,10 @@ - - - - - - - - unknown commit SHA - - - - - - - - + + + + + + Validate - $(RepositoryEngineeringDir)allowed-vmr-binaries.txt + $(RepositoryEngineeringDir)allowed-vmr-binaries.txt + $(RepositoryEngineeringDir)allowed-sb-binaries.txt + $(AllowedVmrBinariesFile) + BeforeTargets="Build" + Condition="'$(SkipBinaryScan)' != 'true'" + Inputs="$(AllowedBinariesFile);$(AllowedVmrBinariesFile);$(AllowedSbBinariesFile);$(BinaryToolKitAssembly)" + Outputs="$(BaseIntermediateOutputPath)DetectBinaries$(BinariesMode).complete"> + + diff --git a/eng/init-poison.proj b/eng/init-poison.proj index 24420ddce6e9..dad11f1784bb 100644 --- a/eng/init-poison.proj +++ b/eng/init-poison.proj @@ -6,17 +6,18 @@ + + + - - - .source-built.xml - $(ArtifactsLogDir)poison-source-built-catalog.xml - + Outputs="$(BaseIntermediateOutputPath)PoisonPackages.complete"> @@ -32,7 +33,7 @@ + Properties="SharedComponentFilterMode=$(SharedComponentFilter_ToolingOnly)"> @@ -40,18 +41,14 @@ Text="Expected NuGet packages to be present from shared components artifacts." /> - <_SharedToolingComponentFilenames Include="@(_FilteredSharedComponentPackages)"> - $(SharedComponentsArtifactsPath)$([System.IO.Path]::GetFileName('%(PipelineArtifactPath)')) - - + - - - + + + diff --git a/eng/init-source-only.proj b/eng/init-source-only.proj index 8ceeca2f04db..ec2b67ccc083 100644 --- a/eng/init-source-only.proj +++ b/eng/init-source-only.proj @@ -13,12 +13,9 @@ - - $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'archive')) - - @@ -40,7 +37,13 @@ @(SharedComponentsPrereqsArchivePathItem) - + + + + + @@ -64,7 +67,7 @@ @@ -78,6 +81,13 @@ + + + + + + @@ -98,7 +108,6 @@ - @@ -109,10 +118,6 @@ - - - - @@ -172,24 +178,8 @@ - - - - - - + + diff --git a/eng/pipelines/official.yml b/eng/pipelines/official.yml index 6a0889659660..cc77a3da5171 100644 --- a/eng/pipelines/official.yml +++ b/eng/pipelines/official.yml @@ -42,16 +42,24 @@ parameters: - name: desiredFinalVersionKind displayName: 'Final version kind' type: string - default: 'Default (repo specifies)' + default: 'Default (uses VMR branding defaults for release branch builds or otherwise repo branding defaults)' values: - - Default (repo specifies) - - Stable Preview - - Stable Final + - Default (uses VMR branding defaults for release branch builds or otherwise repo branding defaults) + - Repo default (uses the repo branding defaults) + - Unstable (produces assets with the repo specified branding suffix) + - Preview (produces assets with a '.final' branding suffix) + - Release (produces assets without a branding suffix) variables: - name: isScheduleTrigger value: ${{ eq(variables['Build.Reason'], 'Schedule') }} +- name: GDN_EXTRACT_TOOLS + value: 'binskim,bandit,roslynanalyzers' + +- name: GDN_EXTRACT_FILTER + value: 'f|**/*.zip;f|**/*.nupkg;f|**/*.vsix;f|**/*.cspkg;f|**/*.sfpkg;f|**/*.package' + - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: skipComponentGovernanceDetection # we run CG on internal builds only value: true @@ -73,7 +81,10 @@ extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: featureFlags: + binskimScanAllExtensions: true incrementalSDLBinaryAnalysis: true + autoEnablePREfastWithNewRuleset: false + autoEnableRoslynWithNewRuleset: false sdl: sourceAnalysisPool: name: $(DncEngInternalBuildPool) @@ -93,17 +104,12 @@ extends: tsaEnabled: true binskim: enabled: true - # This may need to be updated to exclude files we don't want scanned. - # This pattern was pulled from dotnet/sdk, with the ESRPClient added. - # When binskim supports scanning of archive contents, this can be removed altogether. - # This is of the form: ; - # +:f| - Include glob - # -:f| - Exclude glob - analyzeTargetGlob: +:f|eng\**\*.props;+:f|**\artifacts\bin\**\*.dll;+:f|**\artifacts\bin\**\*.exe;-:f|**\artifacts\bin\**\msdia140.dll;-:f|**\artifacts\bin\**\pgort140.dll;-:f|**\artifacts\bin\*Tests\**;-:f|**\Microsoft.NET.Runtime.Emscripten**\tools\**;-:f|**\CodeCoverage\**;-:f|**\artifacts\bin\**\capstone.dll;-:f|**\Microsoft.EsrpClient\**; policheck: enabled: true tsa: enabled: true + credscan: + suppressionsFile: $(Build.SourcesDirectory)/.config/CredScanSuppressions.json containers: ${{ variables.almaLinuxContainerName }}: @@ -167,3 +173,6 @@ extends: desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} isOfficialBuild: true scope: full + # Exclude runtime dependent jobs for any branch not producing a 1xx version since runtime-related repos aren't built in that context + # This is enabled for all branches except main and 1xx + excludeRuntimeDependentJobs: false diff --git a/eng/pipelines/pr.yml b/eng/pipelines/pr.yml index 407821d5569e..e1d08edfb549 100644 --- a/eng/pipelines/pr.yml +++ b/eng/pipelines/pr.yml @@ -13,14 +13,30 @@ pr: include: - main - release/* + paths: + include: + - '*' + exclude: + - 'docs/*' + - '*.md' schedules: - - cron: "0 1 * * *" # run at 01:00 (UTC) + - cron: "0 14 * * *" # run at 14:00 (UTC) branches: include: - main always: false # run only if there were changes since the last successful scheduled run. +parameters: +- name: buildScope + displayName: 'Build Scope' + type: string + default: 'auto' + values: + - auto + - lite + - full + variables: - name: isScheduleTrigger value: ${{ eq(variables['Build.Reason'], 'Schedule') }} @@ -40,9 +56,53 @@ variables: stages: - template: /eng/pipelines/templates/stages/vmr-build.yml parameters: - ${{ if or(eq(variables.isScheduleTrigger, 'true'), contains(variables['Build.DefinitionName'], '-full')) }}: - scope: full - ${{ elseif eq(variables.isPRTrigger, 'true') }}: - scope: lite + ${{ if eq(parameters.buildScope, 'auto') }}: + ${{ if or(eq(variables.isScheduleTrigger, 'true'), contains(variables['Build.DefinitionName'], '-full')) }}: + scope: full + ${{ elseif eq(variables.isPRTrigger, 'true') }}: + scope: lite + ${{ else }}: + scope: full ${{ else }}: scope: full + # Exclude runtime dependent jobs for any branch not producing a 1xx version since runtime-related repos aren't built in that context + # This is enabled for all branches except main and 1xx + excludeRuntimeDependentJobs: false + +- stage: ValidateUserChanges + displayName: Validate user changes in VMR + dependsOn: [] + condition: and( + eq(variables['isPRTrigger'], 'true'), + and( + not(startsWith(variables['System.PullRequest.SourceBranch'], 'refs/heads/darc-')), + not(startsWith(variables['System.PullRequest.SourceBranch'], 'darc-')) + ) + ) + jobs: + - job: Validate + displayName: Run Validation Script + pool: + vmImage: windows-latest + steps: + - checkout: self + fetchDepth: 0 + + - powershell: | + Write-Host "Fetching target branch: $(System.PullRequest.TargetBranch)" + git fetch origin $(System.PullRequest.TargetBranch) + displayName: Fetch target branch + + - powershell: | + . "$(Build.SourcesDirectory)\eng\common\tools.ps1" + InitializeDotNetCli -install $true + Write-Host "##vso[task.setvariable variable=DOTNET_INSTALL_DIR]$env:DOTNET_INSTALL_DIR" + displayName: Initialize .NET CLI + + - powershell: | + & "$(DOTNET_INSTALL_DIR)\dotnet.exe" run --project eng/tools/ChangeValidation/ChangeValidation.csproj + displayName: Run ChangeValidation tool + env: + SYSTEM_PULLREQUEST_TARGETBRANCH: $(System.PullRequest.TargetBranch) + SYSTEM_PULLREQUEST_SOURCEBRANCH: $(System.PullRequest.SourceBranch) + SYSTEM_PULLREQUEST_PULLREQUESTNUMBER: $(System.PullRequest.PullRequestNumber) diff --git a/eng/pipelines/scout-build.yml b/eng/pipelines/scout-build.yml index c4e8ea5bb2fe..0cb923a5b9d5 100644 --- a/eng/pipelines/scout-build.yml +++ b/eng/pipelines/scout-build.yml @@ -32,3 +32,6 @@ stages: - template: /eng/pipelines/templates/stages/vmr-build.yml parameters: scope: scout + # Exclude runtime dependent jobs for any branch not producing a 1xx version since runtime-related repos aren't built in that context + # This is enabled for all branches except main and 1xx + excludeRuntimeDependentJobs: false diff --git a/eng/pipelines/source-build-outer-loop.yml b/eng/pipelines/source-build-outer-loop.yml new file mode 100644 index 000000000000..ab417c177a32 --- /dev/null +++ b/eng/pipelines/source-build-outer-loop.yml @@ -0,0 +1,61 @@ +trigger: + batch: true + branches: + include: + - main + - release/*.0.1xx-preview* + - internal/release/*.0.1xx* + +variables: +- name: skipComponentGovernanceDetection + value: true + +- name: Codeql.Enabled + value: false + +- template: /eng/common/templates-official/variables/pool-providers.yml@self +- template: /eng/pipelines/templates/variables/vmr-build.yml@self + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + featureFlags: + incrementalSDLBinaryAnalysis: true + sdl: + sourceAnalysisPool: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + + spotBugs: + enabled: false + + codeql: + compiled: + enabled: false + runSourceLanguagesInSourceAnalysis: false + tsaEnabled: false + binskim: + enabled: false + policheck: + enabled: false + tsa: + enabled: false + + containers: + ${{ variables.centOSStreamContainerName }}: + image: ${{ variables.centOSStreamContainerImage }} + options: ${{ variables.defaultContainerOptions }} + + stages: + - template: /eng/pipelines/templates/stages/vmr-build.yml@self + parameters: + isOfficialBuild: false + scope: source-build-outer-loop diff --git a/eng/pipelines/source-build-sdk-diff-tests.yml b/eng/pipelines/source-build-sdk-diff-tests.yml index ed3ecb21c70f..8981cd1a054d 100644 --- a/eng/pipelines/source-build-sdk-diff-tests.yml +++ b/eng/pipelines/source-build-sdk-diff-tests.yml @@ -2,8 +2,13 @@ schedules: - cron: "0 12 * * 1-5" displayName: Run on weekdays at 12pm UTC branches: + # The 2xx, 3xx, 4xx triggers exist to cover the feature band pre-release time periods. + # They can be removed once released as the pipeline triggers below will handle the branches after release. include: - main + - release/*.0.2xx* + - release/*.0.3xx* + - release/*.0.4xx* # Relies on dotnet-unified-build being in the same repo as this pipeline # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/pipeline-triggers?view=azure-devops#branch-considerations @@ -17,7 +22,7 @@ resources: branches: include: - refs/heads/release/*.0.1xx-preview* - - refs/heads/internal/release/*.0.1xx* + - refs/heads/internal/release/*.0.?xx* # Trigger on build stages vs successful pipeline completion for a higher run frequency when there are # publish/validation failures. stages: @@ -27,11 +32,9 @@ resources: - pipeline: dotnet-source-build-pre10.0 source: dotnet-source-build-pre10.0 # For releases branches only run on internal/release branches because that's where dependencies flow. - # Previews don't have internal/release branches so they must be run from non-internal release branches. trigger: branches: include: - - refs/heads/release/*.0.1xx-preview* - refs/heads/internal/release/*.0.1xx* repositories: diff --git a/eng/pipelines/templates/jobs/sdk-diff-tests.yml b/eng/pipelines/templates/jobs/sdk-diff-tests.yml index 27ec52de16e2..6fd0d4829882 100644 --- a/eng/pipelines/templates/jobs/sdk-diff-tests.yml +++ b/eng/pipelines/templates/jobs/sdk-diff-tests.yml @@ -127,14 +127,18 @@ jobs: eng/common/build.sh -bl --projects $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.Tests/Microsoft.DotNet.SourceBuild.Tests.csproj --restore + source ./eng/common/tools.sh + InitializeDotNetCli true + echo "##vso[task.setvariable variable=MsftSdkTarballPath]$(Pipeline.Workspace)/Artifacts/$msft_sdk_tarball_name" echo "##vso[task.setvariable variable=SdkTarballPath]$(Pipeline.Workspace)/Artifacts/$sdk_tarball_name" echo "##vso[task.setvariable variable=SourceBuiltArtifactsPath]$(Pipeline.Workspace)/Artifacts/$artifacts_path" + echo "##vso[task.setvariable variable=DotNetPath]$_InitializeDotNetCli" displayName: Prepare Tests workingDirectory: $(Build.SourcesDirectory) - script: > - .dotnet/dotnet test + $(DotNetPath)/dotnet test $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.Tests/Microsoft.DotNet.SourceBuild.Tests.csproj --filter "Category=SdkContent" --logger:'trx;LogFileName=$(Agent.JobName)_SDKDiffTests.trx' @@ -161,6 +165,7 @@ jobs: find artifacts/TestResults/ -type f -name "*.binlog" -exec cp {} --parents -t ${targetFolder} \; find artifacts/TestResults/ -type f -name "*.log" -exec cp {} --parents -t ${targetFolder} \; find artifacts/TestResults/ -type f -name "*.diff" -exec cp {} --parents -t ${targetFolder} \; + find artifacts/TestResults/ -type f -name "*.suppression" -exec cp {} --parents -t ${targetFolder} \; find artifacts/TestResults/ -type f -name "Updated*.txt" -exec cp {} --parents -t ${targetFolder} \; displayName: Prepare BuildLogs staging directory continueOnError: true @@ -181,6 +186,7 @@ jobs: - ${{ if and(eq(parameters.publishTestResultsPr, 'true'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release'))) }}: - template: ../steps/create-baseline-update-pr.yml parameters: + dotnetPath: $(DotNetPath) pipeline: sdk repo: dotnet/dotnet originalFilesDirectory: test/Microsoft.DotNet.SourceBuild.Tests/assets/SdkContentTests diff --git a/eng/pipelines/templates/jobs/vmr-build.yml b/eng/pipelines/templates/jobs/vmr-build.yml index 07e3c7a50424..bc4d15ba77c2 100644 --- a/eng/pipelines/templates/jobs/vmr-build.yml +++ b/eng/pipelines/templates/jobs/vmr-build.yml @@ -1,755 +1,818 @@ -### This job builds https://github.com/dotnet/dotnet with given parameters -### If run in an sdk PR, new changes are applied to a local copy of the VMR, then it is built and tested - -parameters: -- name: artifactsRid - type: string - default: '' - -- name: buildName - type: string - -- name: buildPass - type: string - default: '' - -- name: configuration - type: string - default: 'Release' - -- name: container - type: object - default: - image: '' - name: '' - -- name: crossRootFs - type: string - default: '' - -- name: pool - type: object - -- name: targetOS - type: string - default: '' - -- name: targetArchitecture - type: string - default: '' - -- name: useMonoRuntime - displayName: True when build output uses the mono runtime - type: boolean - default: false - -- name: sign - displayName: True when build output should be signed (includes dry runs) - type: boolean - default: false - -- name: signType - displayName: Signing type - type: string - default: '' - -- name: signDac - displayName: True when the diagnostic files should be signed - type: boolean - default: false - - # Remove with https://github.com/dotnet/source-build/issues/5288 -- name: enableCoreDumpGeneration - displayName: True when core dump generation should be enabled - type: boolean - default: false - -# Overrides the rid that is produced by the build. -- name: targetRid - type: string - default: '' - -# Enables IBC data on when building internally. -- name: enableIBCOptimization - type: boolean - default: false - -# Name of previous job(s) (from the same template as this) whose output will be used to build this job -# The SDK from its artifacts is copied to $(sourcesPath)/.dotnet -- name: reuseBuildArtifactsFrom - type: object - default: '' - -# Enables usage of the system libraries when building. -- name: useSystemLibraries - type: boolean - default: false - -#### SOURCE-ONLY parameters #### - -# Instead of building the VMR directly, exports the sources into a tarball and builds from that -- name: buildFromArchive - type: boolean - default: false - -# Enable for source-building the VMR -- name: buildSourceOnly - type: boolean - default: false - -- name: enablePoison - type: boolean - default: false - -# Allow downloading artifacts from the internet during the build -- name: runOnline - type: boolean - default: true - -- name: runTests - type: boolean - default: true - -# Freeform field for extra values to pass to build.sh for special build modes -- name: extraProperties - type: string - default: '' - -# Use the previous version's SDK to build the current one -- name: withPreviousSDK - type: boolean - default: false - -# Skip the build step (this would be used when wanting to run tests only) -- name: skipBuild - type: boolean - default: false - -# Custom steps to run before running tests -- name: testInitSteps - type: stepList - default: [] - -#### repo parameters #### - -- name: isBuiltFromVmr - displayName: True when build is running from dotnet/dotnet directly - type: boolean - -jobs: -- job: ${{ parameters.buildName }}_${{ parameters.targetArchitecture }}${{ replace(format('_BuildPass{0}', coalesce(parameters.buildPass, '1')), '_BuildPass1', '') }} - pool: ${{ parameters.pool }} - - # 1ES pipeline template requires that the container is specified in the job level. - # If we are using a container image, we set the container to correct name. - # Otherwise, we set the container to host so that the job runs on the host agent. - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - ${{ if ne(parameters.container.name, '') }}: - container: ${{ parameters.container.name }} - ${{ else }}: - container: host - - # For public projects, we always use the container image if it is specified. - ${{ else }}: - ${{ if ne(parameters.container.image, '') }}: - container: - image: ${{ parameters.container.image }} - options: $(defaultContainerOptions) - - ${{ if ne(parameters.reuseBuildArtifactsFrom, '') }}: - ${{ if eq(parameters.buildPass, '') }}: - # For PR builds, skip the stage 2 build if the stage 1 build fails. - # Otherwise, run the stage 2 build even if the stage 1 build fails so that we can get a complete assessment of the build status. - # The build shortcuts when stage 1 build fails and doesn't produce the SDK. - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: - condition: succeeded() - ${{ else }}: - condition: succeededOrFailed() - ${{ else }}: - condition: succeeded() - dependsOn: - - ${{ if ne(parameters.reuseBuildArtifactsFrom, '') }}: - - ${{ parameters.reuseBuildArtifactsFrom }} - variables: - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - - group: AzureDevOps-Artifact-Feeds-Pats - - ${{ if eq(parameters.enableIBCOptimization, 'true') }}: - - group: DotNet-VSTS-Infra-Access - - ${{ else }}: - - name: BotAccount-dotnet-bot-repo-PAT - value: N/A - - name: additionalBuildArgs - value: '' - - name: runTestsTimeout - value: 30 - - - ${{ if parameters.isBuiltFromVmr }}: - - name: vmrPath - value: $(Build.SourcesDirectory) - - ${{ else }}: - - name: vmrPath - value: $(Agent.BuildDirectory)/vmr - - # Location of the VMR sources - # We either build the repo directly, or we extract them outside (which is what partners do) - - ${{ if parameters.buildFromArchive }}: - - name: sourcesPath - value: $(Agent.TempDirectory)/dotnet-sources/ - - ${{ else }}: - - name: sourcesPath - value: $(vmrPath) - - - name: artifactsStagingDir - value: $(Build.ArtifactStagingDirectory)/artifacts - - - name: artifactsPrepublishDir - value: $(Build.ArtifactStagingDirectory)/prepublish - - - name: successfulJobArtifactName - value: $(Agent.JobName)_Artifacts - - - name: failedJobArtifactName - value: $(successfulJobArtifactName)_Attempt$(System.JobAttempt) - - # manually disable CodeQL until https://dev.azure.com/mseng/1ES/_workitems/edit/2211548 is implemented - # CodeQL doesn't work on arm64 macOS, see https://portal.microsofticm.com/imp/v5/incidents/details/532165079/summary - - ${{ if eq(parameters.pool.os, 'macOS') }}: - - name: ONEES_ENFORCED_CODEQL_ENABLED - value: false - - # Build up the command line variables. We avoid doing this in the script sections below - # because AzDO will not echo command lines if they are more than a single line. - - ## Build command line - - ### Command line prefix - - name: commandPrefix - ${{ if eq(parameters.targetOS, 'windows') }}: - value: '-' - ${{ else }}: - value: '--' - - ### Basic arguments - - name: cleanArgument - # Don't clean-while-building on OSs that have enough disk space to keep - # intermediates for compliance scanners. - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(parameters.targetOS, 'osx')) }}: - value: '' - ${{ else }}: - ${{ if eq(parameters.targetOS, 'windows') }}: - value: $(commandPrefix)cleanWhileBuilding - ${{ else }}: - value: $(commandPrefix)clean-while-building - - - name: brandingArgument - value: $(commandPrefix)branding $(brandingType) - - - ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - name: officialBuildArgument - ${{ if eq(parameters.targetOS, 'windows') }}: - value: $(commandPrefix)officialBuildId $(Build.BuildNumber) - ${{ else }}: - value: $(commandPrefix)official-build-id $(Build.BuildNumber) - - ${{ else }}: - - name: officialBuildArgument - value: '' - - - name: useSystemLibrariesArgument - ${{ if eq(parameters.useSystemLibraries, 'true') }}: - # Do not use all (or include libunwind) because of the issues described in https://github.com/dotnet/source-build/issues/5288 - value: $(commandPrefix)with-system-libs brotli+rapidjson+zlib - ${{ else }}: - value: '' - - - name: baseArguments - value: $(commandPrefix)ci $(cleanArgument) $(commandPrefix)prepareMachine -c ${{ parameters.configuration }} $(brandingArgument) $(useSystemLibrariesArgument) $(officialBuildArgument) - - - name: baseProperties - value: $(officialBuildProperties) /p:VerticalName=$(Agent.JobName) - - - name: targetProperties - ${{ if and(ne(parameters.targetOS, ''), ne(parameters.targetArchitecture, '')) }}: - value: -os ${{ parameters.targetOS }} -arch ${{ parameters.targetArchitecture }} - ${{ elseif ne(parameters.targetOS, '') }}: - value: -os ${{ parameters.targetOS }} - ${{ else }}: - value: -arch ${{ parameters.targetArchitecture }} - - ### Signing - - name: _SignDiagnosticFilesArgs - value: '' - - ${{ if eq(parameters.sign, 'True') }}: - - name: signArguments - value: $(commandPrefix)sign - - ${{ if eq(parameters.signType, 'DryRun') }}: - # The _SignType variable is used by microbuild installation - - name: _SignType - value: '' - - name: signProperties - value: /p:ForceDryRunSigning=true - - ${{ else }}: - - name: _SignType - value: ${{ parameters.signType }} - - name: signProperties - value: /p:DotNetSignType=${{ parameters.signType }} /p:TeamName=$(_TeamName) - - ${{ else }}: - - name: signArguments - value: '' - - name: signProperties - value: '' - - name: _SignType - value: '' - - ### Build Pass - - ${{ if ne(parameters.buildPass, '') }}: - - name: buildPassProperties - value: /p:DotNetBuildPass=${{ parameters.buildPass }} - - ${{ else }}: - - name: buildPassProperties - value: '' - - ### Additional properties - - ${{ if eq(parameters.enableIBCOptimization, 'true') }}: - - name: ibcProperties - value: '/p:EnableIBCOptimization=true /p:IBCDropAccessToken=$(dn-bot-devdiv-drop-r-code-r)' - - ${{ else }}: - - name: ibcProperties - value: '' - - # Timeout - ## Real signing takes a while - increase the timeout to allow for that - ${{ if eq(variables['_SignType'], 'real') }}: - timeoutInMinutes: 720 - ## Currently, CodeQL slows the build down too much - ## https://github.com/dotnet/source-build/issues/4276 - ${{ elseif and(parameters.isBuiltFromVmr, startswith(parameters.buildName, 'Windows'), eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: - timeoutInMinutes: 720 - ${{ else }}: - timeoutInMinutes: 240 - - templateContext: - outputParentDirectory: $(Build.ArtifactStagingDirectory) - outputs: - - output: pipelineArtifact - displayName: Publish BuildLogs - condition: succeededOrFailed() - targetPath: $(Build.ArtifactStagingDirectory)/BuildLogs - artifactName: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt) - sbomEnabled: false - - # Both publishing steps are necessary to ensure artifacts are published on both success and failure. - # This prevents overwrite conflicts in the event of a failed build followed by a rerun. - # Additionally, the 'Download Previous Build *' steps depend on a fixed name to acquire specific assets in multi-stage builds. - - output: pipelineArtifact - path: $(Build.ArtifactStagingDirectory)/artifacts - artifact: $(successfulJobArtifactName) - displayName: Publish Artifacts (On Success) - condition: succeeded() - sbomEnabled: true - - - output: pipelineArtifact - path: $(artifactsPrepublishDir) - artifact: $(failedJobArtifactName) - displayName: Publish Artifacts (On Failure) - condition: failed() - continueOnError: true - sbomEnabled: true - - # Using build artifacts to enable publishing the vertical manifests to a single artifact from different jobs - - ${{ if ne(parameters.buildSourceOnly, true) }}: - - output: buildArtifacts - PathtoPublish: $(Build.ArtifactStagingDirectory)/artifacts/manifests/${{ parameters.configuration }}/$(Agent.JobName).xml - ArtifactName: VerticalManifests - displayName: Publish Vertical Manifest - sbomEnabled: false - - steps: - - checkout: self - fetchDepth: 1 - - - ${{ if not(parameters.isBuiltFromVmr) }}: - # Synchronize new content in the VMR during PRs - - template: /eng/common/templates/steps/vmr-sync.yml@self - parameters: - vmrPath: $(vmrPath) - targetRef: $(Build.SourceVersion) # Synchronize the current repo commit - - - ${{ if parameters.buildFromArchive }}: - - script: | - set -ex - cp -r "$(vmrPath)" "$(sourcesPath)" - rm -rf "$(sourcesPath)/.git" - displayName: Export VMR sources - workingDirectory: $(Build.ArtifactStagingDirectory) - - - ${{ if ne(parameters.reuseBuildArtifactsFrom,'') }}: - - ${{ each reuseBuildArtifacts in parameters.reuseBuildArtifactsFrom }}: - - ${{ if eq(parameters.buildSourceOnly, true) }}: - - template: ../steps/download-artifacts.yml - parameters: - artifactDescription: Previous Build (${{ reuseBuildArtifacts }} - Source Build artifacts) - artifactName: ${{ reuseBuildArtifacts }}_Artifacts - downloadFilePatterns: | - **/Private.SourceBuilt.Artifacts.*.tar.gz - **/dotnet-sdk-*.tar.gz - copyDestination: $(sourcesPath)/prereqs/packages/archive/ - flattenDirs: true - - - ${{ else }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifactName: ${{ reuseBuildArtifacts }}_Artifacts - targetPath: $(sourcesPath)/artifacts/ - displayName: Download Previous Build (${{ reuseBuildArtifacts }}) - - - ${{ if eq(parameters.withPreviousSDK, 'true') }}: - - script: | - set -euo pipefail - - if [[ '${{ parameters.artifactsRid }}' == '' ]]; then - echo "'artifactsRid' is not specified. Cannot download source-built SDK." - exit 1 - fi - - packageVersionsPath="$(sourcesPath)/eng/Versions.props" - notFoundMessage="No source-built SDK found to download..." - - echo "Looking for source-built SDK to download..." - archiveVersionLine=$(grep -m 1 "" "$packageVersionsPath" || :) - versionPattern="(.*)" - - if [[ $archiveVersionLine =~ $versionPattern ]]; then - archiveVersion="${BASH_REMATCH[1]}" - archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/sdks/dotnet-sdk-$archiveVersion-${{ parameters.artifactsRid }}.tar.gz" - downloadDir="$(sourcesPath)/prereqs/packages/archive/" - - echo "Downloading source-built SDK from $archiveUrl..." - (cd "$downloadDir" && curl --retry 5 -O "$archiveUrl") - else - echo "$notFoundMessage" - exit 1 - fi - displayName: Setup Previously Source-Built SDK - - # https://github.com/dotnet/dotnet/issues/1758 - # After a re-boostrap: - # - Uncomment the line below, replacing the one after it. - # - Uncomment the two parameter lines at the end of the template parameters. - # - ${{ if and(eq(parameters.sign, 'True'), ne(parameters.signType, 'DryRun')) }}: - - ${{ if and(eq(parameters.sign, 'True'), eq(parameters.signType, 'Real')) }}: - - template: ${{ variables['Build.SourcesDirectory'] }}/eng/common/core-templates/steps/install-microbuild.yml - parameters: - enableMicrobuild: true - enableMicrobuildForMacAndLinux: true - # ${{ if ne(parameters.signType, 'Real') }}: - # microbuildUseESRP: false - - # When building internal, authenticate to internal feeds that may be in use - # We do not need these for source-only builds - - ${{ if and(ne(variables['System.TeamProject'], 'public'), ne(parameters.buildSourceOnly, 'True')) }}: - # Authenticate with service connections to be able to pull packages to external nuget feeds. - - task: NuGetAuthenticate@1 - inputs: - nuGetServiceConnections: devdiv/engineering, devdiv/dotnet-core-internal-tooling - - - ${{ if eq(parameters.targetOS, 'windows') }}: - # Node 20.x is a toolset dependency to build aspnetcore - # Keep in sync with aspnetcore: https://github.com/dotnet/aspnetcore/blob/7d5309210d8f7bae8fa074da495e9d009d67f1b4/.azure/pipelines/ci.yml#L719-L722 - - task: NodeTool@0 - displayName: Install Node 20.x - inputs: - versionSpec: 20.x - - - ${{ if eq(parameters.signDac, 'true') }}: - # TODO: Once we turn off the dotnet/runtime official build, move these templates into the VMR's eng folder. - - template: ../steps/setup-diagnostics-esrp.yml@self - parameters: - isOfficialBuild: true - serviceConnectionGuid: 0a3b81f8-02bd-434b-960a-1e45cfcca801 - serviceConnectionName: 'diagnostics-esrp-kvcertuser-pme' - scriptRoot: '${{ variables.sourcesPath }}/src/runtime/eng/native/signing' - - - - script: build.cmd - $(baseArguments) - $(signArguments) - $(baseProperties) - /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) - $(targetProperties) - $(signProperties) - $(buildPassProperties) - $(ibcProperties) - $(_SignDiagnosticFilesArgs) - ${{ parameters.extraProperties }} - displayName: Build - workingDirectory: ${{ variables.sourcesPath }} - ${{ if eq(parameters.sign, 'true') }}: - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - ${{ if eq(parameters.signDac, 'true') }}: - ESRP_TOKEN: $(System.AccessToken) - - - ${{ if eq(parameters.runTests, 'True') }}: - - script: build.cmd - $(baseArguments) - $(targetProperties) - $(buildPassProperties) - ${{ parameters.extraProperties }} - -test - -excludeCIBinarylog - /bl:artifacts/log/Release/Test.binlog - displayName: Run Tests - workingDirectory: ${{ variables.sourcesPath }} - timeoutInMinutes: ${{ variables.runTestsTimeout }} - - - ${{ else }}: - - ${{ if eq(parameters.targetOS, 'osx') }}: - - script: | - $(sourcesPath)/eng/common/native/install-dependencies.sh osx - displayName: Install dependencies - - ${{ if eq(parameters.buildSourceOnly, 'true') }}: - - script: | - set -ex - - customPrepArgs="" - prepSdk=true - - if [[ -n '${{ parameters.artifactsRid }}' ]]; then - customPrepArgs="${customPrepArgs} --artifacts-rid ${{ parameters.artifactsRid }}" - fi - - if [[ '${{ parameters.withPreviousSDK }}' == 'True' ]]; then - # Using previous SDK implies we do not want to bootstrap. - customPrepArgs="${customPrepArgs} --no-sdk --no-bootstrap" - prepSdk=false - elif [[ '${{ length(parameters.reuseBuildArtifactsFrom) }}' -gt '0' ]]; then - customPrepArgs="${customPrepArgs} --no-sdk --no-artifacts" - prepSdk=false - fi - - if [[ "$prepSdk" == "false" ]]; then - mkdir $(sourcesPath)/.dotnet - previousSdkPath="$(sourcesPath)/prereqs/packages/archive/dotnet-sdk-*.tar.gz" - eval tar -ozxf "$previousSdkPath" -C "$(sourcesPath)/.dotnet" - eval rm -f "$previousSdkPath" - - echo "##vso[task.setvariable variable=additionalBuildArgs]--with-sdk $(sourcesPath)/.dotnet" - fi - - ./prep-source-build.sh $customPrepArgs - displayName: Prep the Build - workingDirectory: $(sourcesPath) - - - ${{ if ne(parameters.skipBuild, 'true') }}: - - - script: | - set -ex - df -h - - customEnvVars="" - customPreBuildArgs="" - customBuildArgs="$(baseArguments) $(signArguments) $(_SignDiagnosticFilesArgs)" - extraBuildProperties="$(baseProperties) /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) $(targetProperties) $(signProperties) $(buildPassProperties) ${{ parameters.extraProperties }}" - - if [[ '${{ parameters.runOnline }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --online" - else - customPreBuildArgs="$customPreBuildArgs sudo -E unshare -n" - fi - - if [[ '${{ parameters.enablePoison }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --poison" - fi - - if [[ '${{ parameters.buildFromArchive }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --source-repository https://github.com/dotnet/dotnet" - customBuildArgs="$customBuildArgs --source-version $(git -C "$(vmrPath)" rev-parse HEAD)" - fi - - if [[ '${{ parameters.buildSourceOnly }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --source-only" - extraBuildProperties="$extraBuildProperties /p:ReportSbrpUsage=true" - fi - - if [[ '${{ parameters.useMonoRuntime }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --use-mono-runtime" - fi - - if [[ -n "${{ parameters.targetRid }}" ]]; then - customBuildArgs="$customBuildArgs --target-rid ${{ parameters.targetRid }}" - fi - - if [[ -n "${{ parameters.crossRootFs }}" ]]; then - customEnvVars="$customEnvVars ROOTFS_DIR=${{ parameters.crossRootFs}}" - if [[ '${{ parameters.targetArchitecture }}' != 'wasm' ]]; then - extraBuildProperties="$extraBuildProperties /p:CrossBuild=true" - fi - fi - - # Temporarily enable core dump generation to diagnose https://github.com/dotnet/source-build/issues/5288 - if [[ '${{ parameters.enableCoreDumpGeneration }}' == 'True' ]]; then - echo "$(sourcesPath)artifacts/log/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern - ulimit -c unlimited - fi - - buildArgs="$(additionalBuildArgs) $customBuildArgs $extraBuildProperties" - - for envVar in $customEnvVars; do - customEnvVarsWithBashSyntax="$customEnvVarsWithBashSyntax export $envVar;" - done - - eval $customEnvVarsWithBashSyntax - $customPreBuildArgs ./build.sh $buildArgs - displayName: Build - workingDirectory: $(sourcesPath) - ${{ if eq(parameters.sign, 'true') }}: - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - - - ${{ if ne(parameters.runOnline, 'True' )}}: - - script: | - set -ex - # Update the owner of the staging directory to the current user - sudo chown -R $(whoami) $(Build.ArtifactStagingDirectory) - displayName: Update owner of artifacts staging directory - - # Only run tests if enabled - - ${{ if eq(parameters.runTests, 'True') }}: - - ${{ parameters.testInitSteps }} - - # Setup the NuGet sources used by the tests to use private feeds. This is necessary when testing internal-only product - # builds where the packages are only available in the private feeds. This allows the tests to restore from those feeds. - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - - task: Bash@3 - displayName: Setup Private Feeds Credentials - inputs: - filePath: $(sourcesPath)/src/sdk/eng/common/SetupNugetSources.sh - arguments: $(sourcesPath)/src/sdk/NuGet.config $Token - env: - Token: $(dn-bot-dnceng-artifact-feeds-rw) - - - script: cp "$(sourcesPath)/src/sdk/NuGet.config" "$(sourcesPath)/test/Microsoft.DotNet.SourceBuild.Tests/assets/online.NuGet.Config" - displayName: Copy Test NuGet Config for Smoke Tests - - - script: | - set -ex - - customPreBuildArgs='' - customBuildArgs="--test --excludeCIBinarylog /bl:artifacts/log/Release/Test.binlog $(baseArguments)" - extraBuildProperties="$(baseProperties) /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) $(targetProperties) ${{ parameters.extraProperties }}" - - if [[ '${{ parameters.runOnline }}' == 'False' ]]; then - customPreBuildArgs="$customPreBuildArgs sudo" - fi - - if [[ '${{ parameters.buildSourceOnly }}' == 'True' ]]; then - if [[ '${{ parameters.enablePoison }}' == 'True' ]]; then - customBuildArgs="$customBuildArgs --poison" - fi - customBuildArgs="$customBuildArgs --source-only" - fi - - if [[ -n "${{ parameters.targetRid }}" ]]; then - customBuildArgs="$customBuildArgs --target-rid ${{ parameters.targetRid }}" - fi - - buildArgs="$(additionalBuildArgs) $customBuildArgs $extraBuildProperties" - buildArgs=$(echo $buildArgs | xargs) # Remove extra spaces - - cd $(sourcesPath) - $customPreBuildArgs ./build.sh $buildArgs - - displayName: Run Tests - timeoutInMinutes: ${{ variables.runTestsTimeout }} - - - ${{ if and(eq(parameters.sign, 'True'), ne(parameters.buildSourceOnly, 'True'), eq(variables['System.TeamProject'], 'internal')) }}: - - template: ${{ variables['Build.SourcesDirectory'] }}/eng/common/core-templates/steps/cleanup-microbuild.yml - parameters: - enableMicrobuild: true - enableMicrobuildForMacAndLinux: true - - - task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(sourcesPath)' - Contents: | - artifacts/log/** - artifacts/TestResults/**/*.binlog - artifacts/TestResults/**/*.diff - artifacts/TestResults/**/Updated*.txt - artifacts/TestResults/**/*.trx - artifacts/TestResults/**/*.xml - TargetFolder: '$(Build.ArtifactStagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - - - task: CopyFiles@2 - displayName: Copy unmerged artifacts to staging directory - inputs: - SourceFolder: '$(sourcesPath)/artifacts' - Contents: | - packages/**/* - assets/**/* - obj/manifests/**/* - TargetFolder: '$(artifactsPrepublishDir)' - CleanTargetFolder: true - condition: failed() - continueOnError: true - - - ${{ if or(ne(variables['System.TeamProject'], 'internal'), eq(variables['Build.Reason'], 'PullRequest')) }}: - - publish: $(Build.ArtifactStagingDirectory)/BuildLogs - artifact: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt) - displayName: Publish BuildLogs - continueOnError: true - condition: always() - - # Only upload test results if enabled - - ${{ if eq(parameters.runTests, 'True') }}: - - task: PublishTestResults@2 - displayName: Publish Test Results - condition: succeededOrFailed() - continueOnError: true - inputs: - testRunner: VSTest - testResultsFiles: 'artifacts/TestResults/Release/*.trx' - searchFolder: $(sourcesPath) - mergeTestResults: true - publishRunAttachments: true - testRunTitle: Tests_$(Agent.JobName) - - - task: PublishTestResults@2 - displayName: Publish Scenario Test Results - condition: and(eq(variables['hasScenarioTestResults'], 'true'), succeededOrFailed()) - continueOnError: true - inputs: - testRunner: xUnit - testResultsFiles: 'artifacts/TestResults/**/scenario-tests/*.xml' - searchFolder: $(sourcesPath) - mergeTestResults: true - publishRunAttachments: true - testRunTitle: ScenarioTests_$(Agent.JobName) - - - ${{ if or(ne(variables['System.TeamProject'], 'internal'), eq(variables['Build.Reason'], 'PullRequest')) }}: - # Both publishing steps are necessary to ensure artifacts are published on both success and failure. - # This prevents overwrite conflicts in the event of a failed build followed by a rerun. - # Additionally, the 'Download Previous Build *' steps depend on a fixed name to acquire specific assets in multi-stage builds. - - publish: $(Build.ArtifactStagingDirectory)/artifacts - artifact: $(successfulJobArtifactName) - displayName: Publish Artifacts (On Success) - condition: succeeded() - continueOnError: true - - - publish: $(artifactsPrepublishDir) - artifact: $(failedJobArtifactName) - displayName: Publish Artifacts (On Failure) - condition: failed() - continueOnError: true - - # Using build artifacts to enable publishing the vertical manifests to a single artifact from different jobs - - ${{ if ne(parameters.buildSourceOnly, true) }}: - - task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/artifacts/manifests/${{ parameters.configuration }}/$(Agent.JobName).xml - ArtifactName: VerticalManifests - displayName: Publish Vertical Manifest +### This job builds https://github.com/dotnet/dotnet with given parameters +### If run in an sdk PR, new changes are applied to a local copy of the VMR, then it is built and tested + +parameters: +- name: artifactsRid + type: string + default: '' + +- name: buildName + type: string + +- name: buildPass + type: string + default: '' + +- name: configuration + type: string + default: 'Release' + +- name: container + type: object + default: + image: '' + name: '' + +- name: crossRootFs + type: string + default: '' + +- name: pool + type: object + +- name: targetOS + type: string + default: '' + +- name: targetArchitecture + type: string + default: '' + +- name: useMonoRuntime + displayName: True when build output uses the mono runtime + type: boolean + default: false + +- name: sign + displayName: True when build output should be signed (includes dry runs) + type: boolean + default: false + +- name: signType + displayName: Signing type + type: string + default: '' + +- name: signDac + displayName: True when the diagnostic files should be signed + type: boolean + default: false + +# Overrides the rid that is produced by the build. +- name: targetRid + type: string + default: '' + +# Enables IBC data on when building internally. +- name: enableIBCOptimization + type: boolean + default: false + +# Name of previous job(s) (from the same template as this) whose output will be used to build this job +# The SDK from its artifacts is copied to $(sourcesPath)/.dotnet +- name: reuseBuildArtifactsFrom + type: object + default: '' + +# Enables usage of the system libraries when building. +- name: useSystemLibraries + type: boolean + default: false + +# Indicates whether to runtime dependent jobs are excluded in the build (this occurs for non-1xx branches). +- name: excludeRuntimeDependentJobs + type: boolean + default: false + +#### SOURCE-ONLY parameters #### + +# Instead of building the VMR directly, exports the sources into a tarball and builds from that +- name: buildFromArchive + type: boolean + default: false + +# Enable for source-building the VMR +- name: buildSourceOnly + type: boolean + default: false + +- name: enablePoison + type: boolean + default: false + +# Allow downloading artifacts from the internet during the build +- name: runOnline + type: boolean + default: true + +- name: runTests + type: boolean + default: true + +# Freeform field for extra values to pass to build.sh for special build modes +- name: extraProperties + type: string + default: '' + +# Use the previous version's SDK to build the current one +- name: withPreviousSDK + type: boolean + default: false + +# Create and publish the source artifact +- name: createSourceArtifact + type: boolean + default: false + +# Skip the build step (this would be used when wanting to run tests only) +- name: skipBuild + type: boolean + default: false + +# Custom steps to run before running tests +- name: testInitSteps + type: stepList + default: [] + +#### repo parameters #### + +- name: isBuiltFromVmr + displayName: True when build is running from dotnet/dotnet directly + type: boolean + +jobs: +- job: ${{ parameters.buildName }}_${{ parameters.targetArchitecture }}${{ replace(format('_BuildPass{0}', coalesce(parameters.buildPass, '1')), '_BuildPass1', '') }} + pool: ${{ parameters.pool }} + + # 1ES pipeline template requires that the container is specified in the job level. + # If we are using a container image, we set the container to correct name. + # Otherwise, we set the container to host so that the job runs on the host agent. + ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: + ${{ if ne(parameters.container.name, '') }}: + container: ${{ parameters.container.name }} + ${{ else }}: + container: host + + # For public projects, we always use the container image if it is specified. + ${{ else }}: + ${{ if ne(parameters.container.image, '') }}: + container: + image: ${{ parameters.container.image }} + options: $(defaultContainerOptions) + + ${{ if ne(parameters.reuseBuildArtifactsFrom, '') }}: + ${{ if eq(parameters.buildPass, '') }}: + # For PR builds, skip the stage 2 build if the stage 1 build fails. + # Otherwise, run the stage 2 build even if the stage 1 build fails so that we can get a complete assessment of the build status. + # The build shortcuts when stage 1 build fails and doesn't produce the SDK. + ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + condition: succeeded() + ${{ else }}: + condition: succeededOrFailed() + ${{ else }}: + condition: succeeded() + dependsOn: + - ${{ if ne(parameters.reuseBuildArtifactsFrom, '') }}: + - ${{ parameters.reuseBuildArtifactsFrom }} + variables: + - ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - group: AzureDevOps-Artifact-Feeds-Pats + - ${{ if eq(parameters.enableIBCOptimization, 'true') }}: + - group: DotNet-VSTS-Infra-Access + - ${{ else }}: + - name: BotAccount-dotnet-bot-repo-PAT + value: N/A + - name: additionalBuildArgs + value: '' + - name: runTestsTimeout + value: 30 + + - ${{ if parameters.buildSourceOnly }}: + - name: sharedComponentsDir + value: $(Agent.TempDirectory)/shared-components + + - ${{ if parameters.isBuiltFromVmr }}: + - name: vmrPath + value: $(Build.SourcesDirectory) + - ${{ else }}: + - name: vmrPath + value: $(Agent.BuildDirectory)/vmr + + # Location of the VMR sources + # We either build the repo directly, or we extract them outside (which is what partners do) + - ${{ if parameters.buildFromArchive }}: + - name: sourcesPath + value: $(Agent.TempDirectory)/dotnet-sources/ + - ${{ else }}: + - name: sourcesPath + value: $(vmrPath) + + - name: artifactsStagingDir + value: $(Build.ArtifactStagingDirectory)/artifacts + + - name: artifactsPrepublishDir + value: $(Build.ArtifactStagingDirectory)/prepublish + + - name: successfulJobArtifactName + value: $(Agent.JobName)_Artifacts + + - name: failedJobArtifactName + value: $(successfulJobArtifactName)_Attempt$(System.JobAttempt) + + # manually disable CodeQL until https://dev.azure.com/mseng/1ES/_workitems/edit/2211548 is implemented + # CodeQL doesn't work on arm64 macOS, see https://portal.microsofticm.com/imp/v5/incidents/details/532165079/summary + - ${{ if eq(parameters.pool.os, 'macOS') }}: + - name: ONEES_ENFORCED_CODEQL_ENABLED + value: false + + # Build up the command line variables. We avoid doing this in the script sections below + # because AzDO will not echo command lines if they are more than a single line. + + ## Build command line + + ### Command line prefix + - name: commandPrefix + ${{ if eq(parameters.targetOS, 'windows') }}: + value: '-' + ${{ else }}: + value: '--' + + ### Basic arguments + - name: cleanArgument + # Don't clean-while-building on internal builds to keep intermediates for compliance scanners. + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + value: '' + ${{ else }}: + ${{ if eq(parameters.targetOS, 'windows') }}: + value: $(commandPrefix)cleanWhileBuilding + ${{ else }}: + value: $(commandPrefix)clean-while-building + + - name: brandingArgument + ${{ if ne(variables['brandingType'], '') }}: + value: $(commandPrefix)branding $(brandingType) + ${{ else }}: + value: '' + + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - name: officialBuildArgument + ${{ if eq(parameters.targetOS, 'windows') }}: + value: $(commandPrefix)officialBuildId $(Build.BuildNumber) + ${{ else }}: + value: $(commandPrefix)official-build-id $(Build.BuildNumber) + - ${{ else }}: + - name: officialBuildArgument + value: '' + + - name: useSystemLibrariesArgument + ${{ if eq(parameters.useSystemLibraries, 'true') }}: + # Do not use all (or include libunwind) because of the issues described in https://github.com/dotnet/source-build/issues/5288 + value: $(commandPrefix)with-system-libs brotli+rapidjson+zlib + ${{ else }}: + value: '' + + - name: baseArguments + value: $(commandPrefix)ci $(cleanArgument) $(commandPrefix)prepareMachine -c ${{ parameters.configuration }} $(brandingArgument) $(useSystemLibrariesArgument) $(officialBuildArgument) + + - name: baseProperties + value: $(officialBuildProperties) /p:VerticalName=$(Agent.JobName) + + - name: targetProperties + ${{ if and(ne(parameters.targetOS, ''), ne(parameters.targetArchitecture, '')) }}: + value: -os ${{ parameters.targetOS }} -arch ${{ parameters.targetArchitecture }} + ${{ elseif ne(parameters.targetOS, '') }}: + value: -os ${{ parameters.targetOS }} + ${{ else }}: + value: -arch ${{ parameters.targetArchitecture }} + + ### Signing + - name: _SignDiagnosticFilesArgs + value: '' + - ${{ if eq(parameters.sign, 'True') }}: + - name: signArguments + value: $(commandPrefix)sign + # MB test signing is not supported on non-Windows platforms for now. + # In those cases, force to dryrun. + - ${{ if or(eq(parameters.signType, 'DryRun'), and(ne(parameters.targetOS, 'windows'), eq(parameters.signType, 'Test'))) }}: + # The _SignType variable is used by microbuild installation + - name: _SignType + value: '' + - name: signProperties + value: /p:ForceDryRunSigning=true + - ${{ else }}: + - name: _SignType + value: ${{ parameters.signType }} + - name: signProperties + value: /p:DotNetSignType=${{ parameters.signType }} /p:TeamName=$(_TeamName) + - ${{ else }}: + - name: signArguments + value: '' + - name: signProperties + value: '' + - name: _SignType + value: '' + + ### Build Pass + - ${{ if ne(parameters.buildPass, '') }}: + - name: buildPassProperties + value: /p:DotNetBuildPass=${{ parameters.buildPass }} + - ${{ else }}: + - name: buildPassProperties + value: '' + + ### Additional properties + - ${{ if eq(parameters.enableIBCOptimization, 'true') }}: + - name: ibcProperties + value: '/p:EnableIBCOptimization=true /p:IBCDropAccessToken=$(dn-bot-devdiv-drop-r-code-r)' + - ${{ else }}: + - name: ibcProperties + value: '' + + # Timeout + ## Real signing takes a while - increase the timeout to allow for that + ${{ if eq(variables['_SignType'], 'real') }}: + timeoutInMinutes: 720 + ## Currently, CodeQL slows the build down too much + ## https://github.com/dotnet/source-build/issues/4276 + ${{ elseif and(parameters.isBuiltFromVmr, eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: + timeoutInMinutes: 720 + ${{ elseif and(startswith(parameters.buildName, 'OSX'), ne(variables['System.TeamProject'], 'internal')) }}: + timeoutInMinutes: 360 + ${{ else }}: + timeoutInMinutes: 240 + + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + displayName: Publish BuildLogs + condition: succeededOrFailed() + targetPath: $(Build.ArtifactStagingDirectory)/BuildLogs + artifactName: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt) + sbomEnabled: false + + # Both publishing steps are necessary to ensure artifacts are published on both success and failure. + # This prevents overwrite conflicts in the event of a failed build followed by a rerun. + # Additionally, the 'Download Previous Build *' steps depend on a fixed name to acquire specific assets in multi-stage builds. + - output: pipelineArtifact + path: $(Build.ArtifactStagingDirectory)/artifacts + artifact: $(successfulJobArtifactName) + displayName: Publish Artifacts (On Success) + condition: succeeded() + sbomEnabled: true + + - output: pipelineArtifact + path: $(artifactsPrepublishDir) + artifact: $(failedJobArtifactName) + displayName: Publish Artifacts (On Failure) + condition: failed() + continueOnError: true + sbomEnabled: true + + # Using build artifacts to enable publishing the vertical manifests to a single artifact from different jobs + - ${{ if ne(parameters.buildSourceOnly, true) }}: + - output: buildArtifacts + PathtoPublish: $(Build.ArtifactStagingDirectory)/artifacts/manifests/${{ parameters.configuration }}/$(Agent.JobName).xml + ArtifactName: VerticalManifests + displayName: Publish Vertical Manifest + sbomEnabled: false + + steps: + - checkout: self + fetchDepth: 1 + + - ${{ if not(parameters.isBuiltFromVmr) }}: + # Synchronize new content in the VMR during PRs + - template: /eng/common/templates/steps/vmr-sync.yml@self + parameters: + vmrPath: $(vmrPath) + targetRef: $(Build.SourceVersion) # Synchronize the current repo commit + + - ${{ if parameters.buildFromArchive }}: + - script: | + set -ex + cp -r "$(vmrPath)" "$(sourcesPath)" + rm -rf "$(sourcesPath)/.git" + displayName: Export VMR sources + workingDirectory: $(Build.ArtifactStagingDirectory) + + - ${{ if ne(parameters.reuseBuildArtifactsFrom,'') }}: + - ${{ each reuseBuildArtifacts in parameters.reuseBuildArtifactsFrom }}: + - ${{ if eq(parameters.buildSourceOnly, true) }}: + - template: ../steps/download-artifacts.yml + parameters: + artifactDescription: Previous Build (${{ reuseBuildArtifacts }} - Source Build artifacts) + artifactName: ${{ reuseBuildArtifacts }}_Artifacts + downloadFilePatterns: | + assets/*/Private.SourceBuilt.Artifacts.*.tar.gz + assets/*/dotnet-sdk-*.tar.gz + copyDestination: $(sourcesPath)/prereqs/packages/archive/ + flattenDirs: true + + - ${{ else }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: ${{ reuseBuildArtifacts }}_Artifacts + targetPath: $(sourcesPath)/artifacts/ + displayName: Download Previous Build (${{ reuseBuildArtifacts }}) + + - ${{ if eq(parameters.withPreviousSDK, 'true') }}: + - script: | + source "$(sourcesPath)/eng/download-source-built-archive.sh" + DownloadArchive "source-built SDK" "PrivateSourceBuiltSdkVersion" false "${{ parameters.artifactsRid }}" "$(sourcesPath)/prereqs/packages/archive/" + displayName: Download Previously Source-Built SDK + + - template: /eng/common/templates/steps/enable-internal-runtimes.yml + + - ${{ if and(parameters.excludeRuntimeDependentJobs, eq(parameters.reuseBuildArtifactsFrom, ''), not(parameters.withPreviousSDK)) }}: + - script: | + source "$(sourcesPath)/eng/download-source-built-archive.sh" + if [[ $SYSTEM_TEAMPROJECT == "internal" ]]; then + storageKey="$(dotnetbuilds-internal-container-read-token-base64)" + else + storageKey="" + fi + # Download the 1xx source-built SDK + DownloadArchive "1xx source-built SDK" "PrivateSourceBuiltSdkVersion" false "${{ parameters.artifactsRid }}" "$(sourcesPath)/prereqs/packages/archive/" "" "" $storageKey + displayName: Download 1xx SDK + + - ${{ if parameters.excludeRuntimeDependentJobs }}: + - script: | + source "$(sourcesPath)/eng/download-source-built-archive.sh" + if [[ $SYSTEM_TEAMPROJECT == "internal" ]]; then + storageKey="$(dotnetbuilds-internal-container-read-token-base64)" + else + storageKey="" + fi + mkdir -p "$(sharedComponentsDir)" + DownloadArchive "shared component artifacts" "PrivateSourceBuiltArtifactsVersion" false "${{ parameters.artifactsRid }}" "$(sharedComponentsDir)" "Private.SourceBuilt.SharedComponents" "MicrosoftNETSdkPackageVersion" $storageKey + tar -xzf $(sharedComponentsDir)/Private.SourceBuilt.SharedComponents.*.tar.gz -C "$(sharedComponentsDir)" + rm -f $(sharedComponentsDir)/Private.SourceBuilt.SharedComponents.*.tar.gz + displayName: Download Shared Component Artifacts + + - ${{ if and(eq(parameters.sign, 'True'), ne(parameters.signType, 'DryRun')) }}: + - template: ${{ variables['Build.SourcesDirectory'] }}/eng/common/core-templates/steps/install-microbuild.yml + parameters: + enableMicrobuild: true + enableMicrobuildForMacAndLinux: true + ${{ if ne(parameters.signType, 'Real') }}: + microbuildUseESRP: false + + # When building internal, authenticate to internal feeds that may be in use + # We do not need these for source-only builds + - ${{ if and(ne(variables['System.TeamProject'], 'public'), ne(parameters.buildSourceOnly, 'True')) }}: + # Authenticate with service connections to be able to pull packages to external nuget feeds. + - task: NuGetAuthenticate@1 + inputs: + nuGetServiceConnections: devdiv/engineering, devdiv/dotnet-core-internal-tooling + + - ${{ if eq(parameters.targetOS, 'windows') }}: + # Node 20.x is a toolset dependency to build aspnetcore + # Keep in sync with aspnetcore: https://github.com/dotnet/aspnetcore/blob/7d5309210d8f7bae8fa074da495e9d009d67f1b4/.azure/pipelines/ci.yml#L719-L722 + - task: NodeTool@0 + displayName: Install Node 20.x + inputs: + versionSpec: 20.x + + - ${{ if eq(parameters.signDac, 'true') }}: + # TODO: Once we turn off the dotnet/runtime official build, move these templates into the VMR's eng folder. + - template: ../steps/setup-diagnostics-esrp.yml@self + parameters: + isOfficialBuild: true + serviceConnectionGuid: 0a3b81f8-02bd-434b-960a-1e45cfcca801 + serviceConnectionName: 'diagnostics-esrp-kvcertuser-pme' + scriptRoot: '${{ variables.sourcesPath }}/src/runtime/eng/native/signing' + + + - script: build.cmd + $(baseArguments) + $(signArguments) + $(baseProperties) + /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) + $(targetProperties) + $(signProperties) + $(buildPassProperties) + $(ibcProperties) + $(_SignDiagnosticFilesArgs) + ${{ parameters.extraProperties }} + displayName: Build + workingDirectory: ${{ variables.sourcesPath }} + ${{ if eq(parameters.sign, 'true') }}: + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + ${{ if eq(parameters.signDac, 'true') }}: + ESRP_TOKEN: $(System.AccessToken) + + - ${{ if eq(parameters.runTests, 'True') }}: + - script: build.cmd + $(baseArguments) + $(targetProperties) + $(buildPassProperties) + ${{ parameters.extraProperties }} + -test + -excludeCIBinarylog + /bl:artifacts/log/Release/Test.binlog + displayName: Run Tests + workingDirectory: ${{ variables.sourcesPath }} + timeoutInMinutes: ${{ variables.runTestsTimeout }} + + - ${{ else }}: + - ${{ if or(eq(parameters.targetOS, 'osx'), eq(parameters.targetOS, 'ios'), eq(parameters.targetOS, 'iossimulator'), eq(parameters.targetOS, 'tvos'), eq(parameters.targetOS, 'tvossimulator'), eq(parameters.targetOS, 'maccatalyst')) }}: + - script: | + xcrun simctl runtime delete all + displayName: Reclaim disk space from unused simulator runtimes + - ${{ if eq(parameters.targetOS, 'osx') }}: + - script: | + $(sourcesPath)/eng/common/native/install-dependencies.sh osx + displayName: Install dependencies + - ${{ if eq(parameters.buildSourceOnly, 'true') }}: + - script: | + set -ex + + customPrepArgs="-c ${{ parameters.configuration }}" + prepSdk=true + + if [[ '${{ parameters.excludeRuntimeDependentJobs }}' == 'True' ]]; then + prepSdk=false + fi + + if [[ '${{ parameters.withPreviousSDK }}' == 'True' ]]; then + # Using source-built SDK implies we do not want to bootstrap. + customPrepArgs="${customPrepArgs} --no-bootstrap" + prepSdk=false + elif [[ '${{ length(parameters.reuseBuildArtifactsFrom) }}' -gt '0' ]]; then + customPrepArgs="${customPrepArgs} --no-artifacts" + prepSdk=false + fi + + if [[ "$prepSdk" == "false" ]]; then + customPrepArgs="${customPrepArgs} --no-sdk" + rm -rf $(sourcesPath)/.dotnet + mkdir $(sourcesPath)/.dotnet + previousSdkPath="$(sourcesPath)/prereqs/packages/archive/dotnet-sdk-*.tar.gz" + eval tar -ozxf "$previousSdkPath" -C "$(sourcesPath)/.dotnet" + eval rm -f "$previousSdkPath" + + echo "##vso[task.setvariable variable=additionalBuildArgs]--with-sdk $(sourcesPath)/.dotnet" + fi + + ./prep-source-build.sh $customPrepArgs + displayName: Prep the Build + workingDirectory: $(sourcesPath) + + - ${{ if ne(parameters.skipBuild, 'true') }}: + + - script: | + set -ex + df -h + + customEnvVars="" + customPreBuildArgs="" + customBuildArgs="$(baseArguments)" + extraBuildProperties="$(baseProperties) /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) $(targetProperties) $(buildPassProperties) ${{ parameters.extraProperties }}" + + if [[ '${{ parameters.buildSourceOnly }}' != 'True' ]]; then + # We shouldn't pass signing arguments to the build script when building source-only + # because signing source-built artifacts happens post-build. + customBuildArgs="$customBuildArgs $(signArguments) $(_SignDiagnosticFilesArgs)" + extraBuildProperties="$extraBuildProperties $(signProperties)" + else + customBuildArgs="$customBuildArgs --source-only" + extraBuildProperties="$extraBuildProperties /p:ReportSbrpUsage=true" + if [[ '${{ parameters.sign }}' == 'True' ]]; then + # Set up for signing source-built artifacts post-build. + extraBuildProperties="$extraBuildProperties /p:DotNetSourceOnlyPostBuildSign=true" + fi + + if [[ '${{ parameters.excludeRuntimeDependentJobs }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --with-shared-components $(sharedComponentsDir)" + fi + fi + + if [[ '${{ parameters.runOnline }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --online" + else + customPreBuildArgs="$customPreBuildArgs sudo -E unshare -n" + fi + + if [[ '${{ parameters.enablePoison }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --poison" + fi + + if [[ '${{ parameters.buildFromArchive }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --source-repository https://github.com/dotnet/dotnet" + customBuildArgs="$customBuildArgs --source-version $(git -C "$(vmrPath)" rev-parse HEAD)" + if [[ '${{ parameters.createSourceArtifact }}' == 'True' ]]; then + # Do not enable source artifact creation when not building from a git repo. + # We strip the .git/ directory when building from archive, + # so it's not possible to create the artifact in that mode. + echo "Cannot create source artifact when building from archive." + exit 1 + fi + fi + + if [[ '${{ parameters.createSourceArtifact }}' == 'True' ]]; then + extraBuildProperties="$extraBuildProperties /p:CreateSourceArtifact=true" + fi + + if [[ '${{ parameters.useMonoRuntime }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --use-mono-runtime" + fi + + if [[ -n "${{ parameters.targetRid }}" ]]; then + customBuildArgs="$customBuildArgs --target-rid ${{ parameters.targetRid }}" + fi + + if [[ -n "${{ parameters.crossRootFs }}" ]]; then + customEnvVars="$customEnvVars ROOTFS_DIR=${{ parameters.crossRootFs}}" + if [[ '${{ parameters.targetArchitecture }}' != 'wasm' ]]; then + extraBuildProperties="$extraBuildProperties /p:CrossBuild=true" + fi + fi + + buildArgs="$(additionalBuildArgs) $customBuildArgs $extraBuildProperties" + + for envVar in $customEnvVars; do + customEnvVarsWithBashSyntax="$customEnvVarsWithBashSyntax export $envVar;" + done + + eval $customEnvVarsWithBashSyntax + $customPreBuildArgs ./build.sh $buildArgs + displayName: Build + workingDirectory: $(sourcesPath) + ${{ if eq(parameters.sign, 'true') }}: + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if ne(parameters.runOnline, 'True' )}}: + - script: | + set -ex + # Update the owner of the staging directory to the current user + sudo chown -R $(whoami) $(Build.ArtifactStagingDirectory) + displayName: Update owner of artifacts staging directory + + # Only run tests if enabled + - ${{ if eq(parameters.runTests, 'True') }}: + - ${{ parameters.testInitSteps }} + + # Setup the NuGet sources used by the tests to use private feeds. This is necessary when testing internal-only product + # builds where the packages are only available in the private feeds. This allows the tests to restore from those feeds. + - ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - task: Bash@3 + displayName: Setup Private Feeds Credentials + inputs: + filePath: $(sourcesPath)/src/sdk/eng/common/SetupNugetSources.sh + arguments: $(sourcesPath)/src/sdk/NuGet.config $Token + env: + Token: $(dn-bot-dnceng-artifact-feeds-rw) + + - script: cp "$(sourcesPath)/src/sdk/NuGet.config" "$(sourcesPath)/test/Microsoft.DotNet.SourceBuild.Tests/assets/online.NuGet.Config" + displayName: Copy Test NuGet Config for Smoke Tests + + - script: | + set -ex + + customPreBuildArgs='' + customBuildArgs="--test --excludeCIBinarylog /bl:artifacts/log/Release/Test.binlog $(baseArguments)" + extraBuildProperties="$(baseProperties) /p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory) $(targetProperties) ${{ parameters.extraProperties }}" + + if [[ '${{ parameters.runOnline }}' == 'False' ]]; then + customPreBuildArgs="$customPreBuildArgs sudo" + fi + + if [[ '${{ parameters.buildSourceOnly }}' == 'True' ]]; then + if [[ '${{ parameters.enablePoison }}' == 'True' ]]; then + customBuildArgs="$customBuildArgs --poison" + fi + customBuildArgs="$customBuildArgs --source-only" + fi + + if [[ -n "${{ parameters.targetRid }}" ]]; then + customBuildArgs="$customBuildArgs --target-rid ${{ parameters.targetRid }}" + fi + + buildArgs="$(additionalBuildArgs) $customBuildArgs $extraBuildProperties" + buildArgs=$(echo $buildArgs | xargs) # Remove extra spaces + + cd $(sourcesPath) + $customPreBuildArgs ./build.sh $buildArgs + + displayName: Run Tests + timeoutInMinutes: ${{ variables.runTestsTimeout }} + + - ${{ if eq(parameters.sign, 'True') }}: + - ${{ if eq(parameters.buildSourceOnly, 'true') }}: + - script: | + sign_properties="$(signProperties) $(_SignDiagnosticFilesArgs)" + + customPreBuildArgs="" + if [[ '${{ parameters.runOnline }}' == 'False' ]]; then + customPreBuildArgs="sudo -E" + export MICROBUILDOUTPUTFOLDEROVERRIDE + fi + + $customPreBuildArgs bash -c ' + set -euo pipefail + . eng/common/tools.sh + MSBuild eng/sign-source-built-artifacts.proj -restore \ + "/p:SignProperties='"$sign_properties"'" \ + "/p:ArtifactsStagingDir=$(Build.ArtifactStagingDirectory)" \ + "/p:OfficialBuildId=$(Build.BuildNumber)" \ + "/bl:artifacts/log/outer-sign-source-built-artifacts.binlog" + ' + displayName: Sign Source-Built Artifacts + workingDirectory: $(Build.SourcesDirectory) + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - template: ${{ variables['Build.SourcesDirectory'] }}/eng/common/core-templates/steps/cleanup-microbuild.yml + parameters: + enableMicrobuild: true + enableMicrobuildForMacAndLinux: true + + - task: CopyFiles@2 + displayName: Prepare BuildLogs staging directory + inputs: + SourceFolder: '$(sourcesPath)' + Contents: | + artifacts/log/** + artifacts/TestResults/**/*.binlog + artifacts/TestResults/**/*.diff + artifacts/TestResults/**/Updated*.txt + artifacts/TestResults/**/*.trx + artifacts/TestResults/**/*.xml + TargetFolder: '$(Build.ArtifactStagingDirectory)/BuildLogs' + CleanTargetFolder: true + continueOnError: true + condition: succeededOrFailed() + + - task: CopyFiles@2 + displayName: Copy unmerged artifacts to staging directory + inputs: + SourceFolder: '$(sourcesPath)/artifacts' + Contents: | + packages/**/* + assets/**/* + obj/manifests/**/* + TargetFolder: '$(artifactsPrepublishDir)' + CleanTargetFolder: true + condition: failed() + continueOnError: true + + - ${{ if or(ne(variables['System.TeamProject'], 'internal'), eq(variables['Build.Reason'], 'PullRequest')) }}: + - publish: $(Build.ArtifactStagingDirectory)/BuildLogs + artifact: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt) + displayName: Publish BuildLogs + continueOnError: true + condition: always() + + # Only upload test results if enabled + - ${{ if eq(parameters.runTests, 'True') }}: + - task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + continueOnError: true + inputs: + testRunner: VSTest + testResultsFiles: 'artifacts/TestResults/Release/*.trx' + searchFolder: $(sourcesPath) + mergeTestResults: true + publishRunAttachments: true + testRunTitle: Tests_$(Agent.JobName) + + - task: PublishTestResults@2 + displayName: Publish Scenario Test Results + condition: and(eq(variables['hasScenarioTestResults'], 'true'), succeededOrFailed()) + continueOnError: true + inputs: + testRunner: xUnit + testResultsFiles: 'artifacts/TestResults/**/scenario-tests/*.xml' + searchFolder: $(sourcesPath) + mergeTestResults: true + publishRunAttachments: true + testRunTitle: ScenarioTests_$(Agent.JobName) + + - ${{ if or(ne(variables['System.TeamProject'], 'internal'), eq(variables['Build.Reason'], 'PullRequest')) }}: + # Both publishing steps are necessary to ensure artifacts are published on both success and failure. + # This prevents overwrite conflicts in the event of a failed build followed by a rerun. + # Additionally, the 'Download Previous Build *' steps depend on a fixed name to acquire specific assets in multi-stage builds. + - publish: $(Build.ArtifactStagingDirectory)/artifacts + artifact: $(successfulJobArtifactName) + displayName: Publish Artifacts (On Success) + condition: succeeded() + continueOnError: true + + - publish: $(artifactsPrepublishDir) + artifact: $(failedJobArtifactName) + displayName: Publish Artifacts (On Failure) + condition: failed() + continueOnError: true + + # Using build artifacts to enable publishing the vertical manifests to a single artifact from different jobs + - ${{ if ne(parameters.buildSourceOnly, true) }}: + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/artifacts/manifests/${{ parameters.configuration }}/$(Agent.JobName).xml + ArtifactName: VerticalManifests + displayName: Publish Vertical Manifest \ No newline at end of file diff --git a/eng/pipelines/templates/stages/source-build-and-validate.yml b/eng/pipelines/templates/stages/source-build-and-validate.yml index 6d46acfce8f3..758931c0ee98 100644 --- a/eng/pipelines/templates/stages/source-build-and-validate.yml +++ b/eng/pipelines/templates/stages/source-build-and-validate.yml @@ -16,10 +16,28 @@ parameters: type: boolean default: true +# True when the build is a build run from the production pipeline +- name: isOfficialBuild + type: boolean + default: false + +- name: desiredSigning + type: string + default: '' + - name: desiredFinalVersionKind type: string default: '' +# Indicates whether runtime dependent jobs are excluded. +- name: excludeRuntimeDependentJobs + type: boolean + default: false + +- name: extraProperties + type: string + default: '' + stages: - stage: VMR_SourceOnly_Build displayName: VMR Source-Only Build @@ -28,6 +46,8 @@ stages: - template: ../variables/vmr-build.yml parameters: buildSourceOnly: true + isOfficialBuild: ${{ parameters.isOfficialBuild }} + desiredSigning: ${{ parameters.desiredSigning }} desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} jobs: - ${{ each leg in parameters.legs }}: @@ -39,21 +59,20 @@ stages: container: name: ${{ variables.centOSStreamContainerName }} image: ${{ variables.centOSStreamContainerImage }} + artifactsRid: ${{ variables.centOSStreamX64Rid }} ${{ if ne(leg.reuseBuildArtifactsFrom, '') }}: reuseBuildArtifactsFrom: - ${{ format(leg.reuseBuildArtifactsFrom, variables.centOSStreamName) }} - ${{ if and(contains(leg.buildNameFormat, 'Previous'), eq(leg.targetArchitecture, 'x64')) }}: - artifactsRid: ${{ variables.centOSStreamX64Rid }} # Fedora ${{ elseif eq(leg.distro, 'fedora') }}: buildName: ${{ format(leg.buildNameFormat, variables.fedoraName) }} container: name: ${{ variables.fedoraContainerName }} image: ${{ variables.fedoraContainerImage }} + artifactsRid: ${{ variables.fedoraX64Rid }} ${{ if ne(leg.reuseBuildArtifactsFrom, '') }}: reuseBuildArtifactsFrom: - ${{ format(leg.reuseBuildArtifactsFrom, variables.fedoraName) }} - enableCoreDumpGeneration: ${{ leg.enableCoreDumpGeneration }} # Ubuntu ${{ elseif eq(leg.distro, 'ubuntu') }}: buildName: ${{ format(leg.buildNameFormat, variables.ubuntuName) }} @@ -65,6 +84,10 @@ stages: container: name: ${{ variables.ubuntuContainerName }} image: ${{ variables.ubuntuContainerImage }} + ${{ if eq(leg.targetArchitecture, 'arm64') }}: + artifactsRid: ${{ variables.ubuntuArm64Rid }} + ${{ else }}: + artifactsRid: ${{ variables.ubuntuX64Rid }} ${{ if ne(leg.reuseBuildArtifactsFrom, '') }}: reuseBuildArtifactsFrom: - ${{ format(leg.reuseBuildArtifactsFrom, variables.centOSStreamName) }} @@ -74,20 +97,22 @@ stages: container: name: ${{ variables.almaLinuxContainerName }} image: ${{ variables.almaLinuxContainerImage }} + artifactsRid: ${{ variables.almaLinuxX64Rid }} + targetRid: ${{ variables.almaLinuxX64Rid }} ${{ if ne(leg.reuseBuildArtifactsFrom, '') }}: reuseBuildArtifactsFrom: - ${{ format(leg.reuseBuildArtifactsFrom, variables.centOSStreamName) }} - targetRid: ${{ variables.almaLinuxX64Rid }} # Alpine ${{ elseif eq(leg.distro, 'alpine') }}: buildName: ${{ format(leg.buildNameFormat, variables.alpineName) }} container: name: ${{ variables.alpineContainerName }} image: ${{ variables.alpineContainerImage }} + artifactsRid: ${{ variables.alpineX64Rid }} + targetRid: ${{ variables.alpineX64Rid }} ${{ if ne(leg.reuseBuildArtifactsFrom, '') }}: reuseBuildArtifactsFrom: - ${{ format(leg.reuseBuildArtifactsFrom, variables.centOSStreamName) }} - targetRid: ${{ variables.alpineX64Rid }} targetArchitecture: ${{ leg.targetArchitecture }} isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} @@ -104,10 +129,17 @@ stages: useMonoRuntime: ${{ leg.useMonoRuntime }} useSystemLibraries: ${{ leg.useSystemLibraries }} withPreviousSDK: ${{ leg.withPreviousSDK }} + createSourceArtifact: ${{ leg.createSourceArtifact }} + ${{ if eq(leg.disableSigning, false) }}: + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} # We only want to publish SB artifacts for each distinct distro/version/arch combination ${{ if and(endsWith(leg.buildNameFormat, 'Offline_MsftSdk'), not(contains(leg.buildNameFormat, '_Mono_'))) }}: - extraProperties: /p:DotNetSourceOnlyPublishArtifacts=true + extraProperties: ${{ leg.extraProperties }} /p:DotNetSourceOnlyPublishArtifacts=true + ${{ else }}: + extraProperties: ${{ leg.extraProperties }} - ${{ if and(parameters.isBuiltFromVmr, ne(variables['Build.Reason'], 'PullRequest'), eq(parameters.useMicrosoftBuildAssetsForTests, true)) }}: - stage: VMR_SourceOnly_Validation @@ -130,12 +162,14 @@ stages: container: name: ${{ variables.centOSStreamContainerName }} image: ${{ variables.centOSStreamContainerImage }} + artifactsRid: ${{ variables.centOSStreamX64Rid }} # Fedora ${{ elseif eq(leg.distro, 'fedora') }}: buildName: ${{ format(leg.buildNameFormat, variables.fedoraName) }}_Validation container: name: ${{ variables.fedoraContainerName }} image: ${{ variables.fedoraContainerImage }} + artifactsRid: ${{ variables.fedoraX64Rid }} # Ubuntu ${{ elseif eq(leg.distro, 'ubuntu') }}: buildName: ${{ format(leg.buildNameFormat, variables.ubuntuName) }}_Validation @@ -143,16 +177,19 @@ stages: container: name: ${{ variables.ubuntuArmContainerName }} image: ${{ variables.ubuntuArmContainerImage }} + artifactsRid: ${{ variables.ubuntuArm64Rid }} ${{ else }}: container: name: ${{ variables.ubuntuContainerName }} image: ${{ variables.ubuntuContainerImage }} + artifactsRid: ${{ variables.ubuntuX64Rid }} # AlmaLinux ${{ elseif eq(leg.distro, 'almalinux') }}: buildName: ${{ format(leg.buildNameFormat, variables.almaLinuxName) }}_Validation container: name: ${{ variables.almaLinuxContainerName }} image: ${{ variables.almaLinuxContainerImage }} + artifactsRid: ${{ variables.almaLinuxX64Rid }} targetRid: ${{ variables.almaLinuxX64Rid }} # Alpine ${{ elseif eq(leg.distro, 'alpine') }}: @@ -160,6 +197,7 @@ stages: container: name: ${{ variables.alpineContainerName }} image: ${{ variables.alpineContainerImage }} + artifactsRid: ${{ variables.alpineX64Rid }} targetRid: ${{ variables.alpineX64Rid }} targetArchitecture: ${{ leg.targetArchitecture }} @@ -174,9 +212,11 @@ stages: enablePoison: ${{ leg.enablePoison }} skipBuild: true extraProperties: > + ${{ leg.extraProperties }} /p:DotNetSourceOnlyTestOnly=true /p:ExtraRestoreSourcePath=$(Pipeline.Workspace)/msft-pkgs /p:RestoreAdditionalProjectSources=$(Pipeline.Workspace)/msft-pkgs + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} testInitSteps: # Ensure the artifacts staging directory exists so that even if no files get placed there, it won't fail @@ -199,13 +239,14 @@ stages: downloadFilePatterns: packages/Release/Shipping/** copyDestination: $(Pipeline.Workspace)/msft-pkgs flattenDirs: true - - template: ../steps/download-artifacts.yml - parameters: - artifactDescription: Microsoft WASM Packages - artifactName: Browser_Shortstack_wasm_Artifacts - downloadFilePatterns: packages/Release/Shipping/** - copyDestination: $(Pipeline.Workspace)/msft-pkgs - flattenDirs: true + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + - template: ../steps/download-artifacts.yml + parameters: + artifactDescription: Microsoft WASM Packages + artifactName: Browser_Shortstack_wasm_Artifacts + downloadFilePatterns: packages/Release/Shipping/** + copyDestination: $(Pipeline.Workspace)/msft-pkgs + flattenDirs: true - template: ../steps/download-artifacts.yml parameters: artifactDescription: Microsoft Windows Packages diff --git a/eng/pipelines/templates/stages/source-build-sdk-diff-tests.yml b/eng/pipelines/templates/stages/source-build-sdk-diff-tests.yml index ed0c074c3e14..1f89f15fbfad 100644 --- a/eng/pipelines/templates/stages/source-build-sdk-diff-tests.yml +++ b/eng/pipelines/templates/stages/source-build-sdk-diff-tests.yml @@ -13,7 +13,7 @@ stages: jobs: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}_Offline_MsftSdk', variables.centOSStreamName) }} + sbJobName: ${{ format('SB_{0}_Offline_MsftSdk', variables.centOSStreamName) }} msftJobName: AzureLinux_x64_Cross sbTargetRid: ${{ variables.centOSStreamX64Rid }} msftTargetRid: ${{ variables.linuxX64Rid }} @@ -23,7 +23,7 @@ stages: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}_Offline_MsftSdk', variables.almaLinuxName) }} + sbJobName: ${{ format('SB_{0}_Offline_MsftSdk', variables.almaLinuxName) }} msftJobName: AzureLinux_x64_Cross sbTargetRid: ${{ variables.almaLinuxX64Rid }} msftTargetRid: ${{ variables.linuxX64Rid }} @@ -32,7 +32,7 @@ stages: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}_Offline_MsftSdk', variables.alpineName) }} + sbJobName: ${{ format('SB_{0}_Offline_MsftSdk', variables.alpineName) }} msftJobName: AzureLinux_x64_Cross_Alpine sbTargetRid: ${{ variables.alpineX64Rid }} msftTargetRid: ${{ variables.linuxMuslX64Rid }} @@ -41,7 +41,7 @@ stages: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}_Offline_MsftSdk', variables.fedoraName) }} + sbJobName: ${{ format('SB_{0}_Offline_MsftSdk', variables.fedoraName) }} msftJobName: AzureLinux_x64_Cross sbTargetRid: ${{ variables.fedoraX64Rid }} msftTargetRid: ${{ variables.linuxX64Rid }} @@ -50,7 +50,7 @@ stages: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}_Offline_MsftSdk', variables.ubuntuName) }} + sbJobName: ${{ format('SB_{0}_Offline_MsftSdk', variables.ubuntuName) }} msftJobName: AzureLinux_x64_Cross sbTargetRid: ${{ variables.ubuntuX64Rid }} msftTargetRid: ${{ variables.linuxX64Rid }} @@ -59,7 +59,7 @@ stages: - template: /eng/pipelines/templates/jobs/sdk-diff-tests.yml@self parameters: - sbJobName: ${{ format('{0}Arm64_Offline_MsftSdk', variables.ubuntuName) }} + sbJobName: ${{ format('SB_{0}Arm64_Offline_MsftSdk', variables.ubuntuName) }} msftJobName: AzureLinux_x64_Cross sbTargetRid: ${{ variables.ubuntuArm64Rid }} msftTargetRid: ${{ variables.linuxArm64Rid }} diff --git a/eng/pipelines/templates/stages/source-build-stages.yml b/eng/pipelines/templates/stages/source-build-stages.yml index c4a22870fd8c..70141d02aad7 100644 --- a/eng/pipelines/templates/stages/source-build-stages.yml +++ b/eng/pipelines/templates/stages/source-build-stages.yml @@ -16,6 +16,8 @@ parameters: - lite # run everything - full + # run source build outer loop (Mono jobs) + - source-build-outer-loop - name: useMicrosoftBuildAssetsForTests type: boolean @@ -26,6 +28,15 @@ parameters: type: boolean default: true +# True when the build is a build run from the production pipeline +- name: isOfficialBuild + type: boolean + default: false + +- name: desiredSigning + type: string + default: '' + - name: desiredFinalVersionKind type: string default: '' @@ -35,6 +46,11 @@ parameters: type: object default: [] +# Indicates whether runtime dependent jobs are excluded. +- name: excludeRuntimeDependentJobs + type: boolean + default: false + stages: - ${{ if or(in(parameters.scope, 'full') , contains(join(';', parameters.verifications), 'source-build-')) }}: - template: source-build-and-validate.yml @@ -43,7 +59,10 @@ stages: pool_LinuxArm64: ${{ parameters.pool_LinuxArm64 }} useMicrosoftBuildAssetsForTests: ${{ parameters.useMicrosoftBuildAssetsForTests }} isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + isOfficialBuild: ${{ parameters.isOfficialBuild }} + desiredSigning: ${{ parameters.desiredSigning }} desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} # Description of the source build legs to run. # This is described here as a parameter to allow two separate stages to be produced from this list (one for building @@ -53,99 +72,28 @@ stages: # to determine which variable to use for determining the various parameter values which are based on variables. # # Changing the build name requires updating the referenced name in the source-build-sdk-diff-tests.yml pipeline + # Changing the `disableSigning` parameter requires adding or removing the build name and source-built artifact + # patterns in the ../steps/vmr-validate-signing.yml file. legs: ### Source-build verification legs ### - # Stage 1 leg is built in full scope, or lite when any of the `source-build-` verifications are requested. - - buildNameFormat: '{0}_Online_MsftSdk' - distro: centos - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: true # ✅ - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 - - - ${{ if containsValue(parameters.verifications, 'source-build-stage2') }}: - - - buildNameFormat: '{0}_Offline_CurrentSourceBuiltSdk' - distro: centos - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 - reuseBuildArtifactsFrom: '{0}_Online_MsftSdk_x64' - - ### Additional legs for full build ### - - ${{ if in(parameters.scope, 'full') }}: - - - buildNameFormat: '{0}_Offline_MsftSdk' - distro: ubuntu - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - excludeOmniSharpTests: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 - - - buildNameFormat: '{0}_Offline_PreviousSourceBuiltSdk' - distro: centos - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: true # ✅ - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: true # ✅ - - - buildNameFormat: '{0}_Offline_MsftSdk' - distro: almalinux - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 - - - buildNameFormat: '{0}_Offline_MsftSdk' - distro: alpine - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 + - ${{ if in(parameters.scope, 'source-build-outer-loop') }}: - - buildNameFormat: '{0}_Offline_MsftSdk' + - buildNameFormat: 'SB_{0}_Mono_Offline_MsftSdk' distro: centos targetArchitecture: x64 buildFromArchive: true # ✅ enablePoison: false # 🚫 runOnline: false # 🚫 - useMonoRuntime: false # 🚫 + useMonoRuntime: true # ✅ useSystemLibraries: false # 🚫 withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true - - buildNameFormat: '{0}_Online_PreviousSourceBuiltSdk' - distro: centos - targetArchitecture: x64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: true # ✅ - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: true # ✅ - - - buildNameFormat: '{0}_Mono_Offline_MsftSdk' + - buildNameFormat: 'SB_{0}_Mono_Offline_CurrentSourceBuiltSdk' distro: centos targetArchitecture: x64 buildFromArchive: true # ✅ @@ -154,47 +102,154 @@ stages: useMonoRuntime: true # ✅ useSystemLibraries: false # 🚫 withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + reuseBuildArtifactsFrom: 'SB_{0}_Mono_Offline_MsftSdk_x64' + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true - - buildNameFormat: '{0}_Offline_MsftSdk' - distro: fedora - targetArchitecture: x64 - buildFromArchive: true # ✅ - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: true # ✅ - withPreviousSDK: false # 🚫 - enableCoreDumpGeneration: true # ✅ - - - buildNameFormat: '{0}Arm64_Offline_MsftSdk' - distro: ubuntu - targetArchitecture: arm64 - buildFromArchive: false # 🚫 - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: false # 🚫 - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 + - ${{ else }}: - - buildNameFormat: '{0}_Offline_CurrentSourceBuiltSdk' - distro: fedora + # Stage 1 leg is built in full scope, or lite when any of the `source-build-` verifications are requested. + - buildNameFormat: 'SB_{0}_Online_MsftSdk' + distro: centos targetArchitecture: x64 buildFromArchive: false # 🚫 enablePoison: false # 🚫 - runOnline: false # 🚫 + runOnline: true # ✅ useMonoRuntime: false # 🚫 useSystemLibraries: false # 🚫 withPreviousSDK: false # 🚫 - enableCoreDumpGeneration: true # ✅ - reuseBuildArtifactsFrom: '{0}_Offline_MsftSdk_x64' - - - buildNameFormat: '{0}_Mono_Offline_CurrentSourceBuiltSdk' - distro: centos - targetArchitecture: x64 - buildFromArchive: true # ✅ - enablePoison: false # 🚫 - runOnline: false # 🚫 - useMonoRuntime: true # ✅ - useSystemLibraries: false # 🚫 - withPreviousSDK: false # 🚫 - reuseBuildArtifactsFrom: '{0}_Mono_Offline_MsftSdk_x64' + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + ${{ if in(parameters.scope, 'lite') }}: + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true + + - ${{ if containsValue(parameters.verifications, 'source-build-stage2') }}: + + - buildNameFormat: 'SB_{0}_Offline_CurrentSourceBuiltSdk' + distro: centos + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + reuseBuildArtifactsFrom: 'SB_{0}_Online_MsftSdk_x64' + ${{ if in(parameters.scope, 'lite') }}: + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true + + ### Additional legs for full build ### + - ${{ if in(parameters.scope, 'full') }}: + + - buildNameFormat: 'SB_{0}_Offline_MsftSdk' + distro: ubuntu + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + excludeOmniSharpTests: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_PreviousSourceBuiltSdk' + distro: centos + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: true # ✅ + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: true # ✅ + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_MsftSdk' + distro: almalinux + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_MsftSdk' + distro: alpine + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_MsftSdk' + distro: centos + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: false # 🚫 + createSourceArtifact: true # ✅ + + - buildNameFormat: 'SB_{0}_Online_PreviousSourceBuiltSdk' + distro: centos + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: true # ✅ + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: true # ✅ + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_MsftSdk' + distro: fedora + targetArchitecture: x64 + buildFromArchive: true # ✅ + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: true # ✅ + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}Arm64_Offline_MsftSdk' + distro: ubuntu + targetArchitecture: arm64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + + - buildNameFormat: 'SB_{0}_Offline_CurrentSourceBuiltSdk' + distro: fedora + targetArchitecture: x64 + buildFromArchive: false # 🚫 + enablePoison: false # 🚫 + runOnline: false # 🚫 + useMonoRuntime: false # 🚫 + useSystemLibraries: false # 🚫 + withPreviousSDK: false # 🚫 + disableSigning: true # ✅ + createSourceArtifact: false # 🚫 + reuseBuildArtifactsFrom: 'SB_{0}_Offline_MsftSdk_x64' diff --git a/eng/pipelines/templates/stages/vmr-build.yml b/eng/pipelines/templates/stages/vmr-build.yml index 166fabe3437e..04f5b6c3a514 100644 --- a/eng/pipelines/templates/stages/vmr-build.yml +++ b/eng/pipelines/templates/stages/vmr-build.yml @@ -24,12 +24,15 @@ parameters: - lite # run everything - full + # run source build outer loop (Mono jobs) + - source-build-outer-loop # True when build is running from dotnet/dotnet directly - name: isBuiltFromVmr type: boolean default: true +# True when the build is a build run from the production pipeline - name: isOfficialBuild type: boolean default: false @@ -90,16 +93,24 @@ parameters: demands: ImageOverride -equals $(poolImage_Linux) os: linux +# Indicates whether to exclude runtime dependent jobs. +# This is used to skip building the cross-OS DACs and other runtime dependent jobs in non-1xx branches. +- name: excludeRuntimeDependentJobs + type: boolean + default: false + stages: -- template: vmr-verticals.yml - parameters: - desiredSigning: ${{ parameters.desiredSigning }} - isOfficialBuild: ${{ parameters.isOfficialBuild }} - desiredIbc: ${{ parameters.desiredIbc }} - desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - scope: ${{ parameters.scope }} - verifications: ${{ parameters.verifications }} +- ${{ if notin(parameters.scope, 'source-build-outer-loop') }}: + - template: vmr-verticals.yml + parameters: + desiredSigning: ${{ parameters.desiredSigning }} + isOfficialBuild: ${{ parameters.isOfficialBuild }} + desiredIbc: ${{ parameters.desiredIbc }} + desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + scope: ${{ parameters.scope }} + verifications: ${{ parameters.verifications }} + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} - ${{ if and(parameters.isBuiltFromVmr, eq(parameters.isOfficialBuild, true)) }}: - stage: Publish_Build_Assets @@ -120,6 +131,7 @@ stages: **/manifests/**/*.xml !*Sdk_*_Artifacts/**/VerticalManifest.xml +- ${{ if and(ne(variables['System.TeamProject'], 'public'), in(parameters.scope, 'full')) }}: - template: vmr-validation.yml parameters: desiredSigning: ${{ parameters.desiredSigning }} @@ -132,6 +144,8 @@ stages: pool_LinuxArm64: ${{ parameters.pool_LinuxArm64 }} scope: ${{ parameters.scope }} isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + isOfficialBuild: ${{ parameters.isOfficialBuild }} + desiredSigning: ${{ parameters.desiredSigning }} desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} verifications: ${{ parameters.verifications }} ${{ if ne(parameters.scope, 'full') }}: @@ -139,3 +153,4 @@ stages: # are available from the subset of jobs that get build in PRs compared to the full build. Instead, we run # the tests in the same job as the build and filter out some of the tests that can't be executed in this state. useMicrosoftBuildAssetsForTests: false + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} diff --git a/eng/pipelines/templates/stages/vmr-license-scan.yml b/eng/pipelines/templates/stages/vmr-license-scan.yml index 692144409142..9373404d2689 100644 --- a/eng/pipelines/templates/stages/vmr-license-scan.yml +++ b/eng/pipelines/templates/stages/vmr-license-scan.yml @@ -118,6 +118,7 @@ stages: - script: | source ./eng/common/tools.sh InitializeDotNetCli true + echo "##vso[task.setvariable variable=DotNetPath]$_InitializeDotNetCli" displayName: Install .NET SDK workingDirectory: $(Build.SourcesDirectory) @@ -135,6 +136,7 @@ stages: - template: /eng/pipelines/templates/steps/create-baseline-update-pr.yml@self parameters: + dotnetPath: $(DotNetPath) pipeline: license repo: dotnet/dotnet originalFilesDirectory: test/Microsoft.DotNet.SourceBuild.Tests/assets/LicenseScanTests diff --git a/eng/pipelines/templates/stages/vmr-scan.yml b/eng/pipelines/templates/stages/vmr-scan.yml index 92c676432cbf..bd5ee5ff91c1 100644 --- a/eng/pipelines/templates/stages/vmr-scan.yml +++ b/eng/pipelines/templates/stages/vmr-scan.yml @@ -7,7 +7,7 @@ stages: displayName: Tag & Scan pool: name: $(DncEngInternalBuildPool) - image: 1es-ubuntu-2004 + image: 1es-ubuntu-2204 os: linux steps: @@ -16,11 +16,15 @@ stages: - script: | source ./eng/common/tools.sh InitializeDotNetCli true - .dotnet/dotnet tool restore - displayName: Initialize tooling + echo "##vso[task.setvariable variable=DotNetPath]$_InitializeDotNetCli" + displayName: Install .NET CLI + + - script: | + $(DotNetPath)/dotnet tool restore + displayName: Restore tools - script: > - .dotnet/dotnet darc vmr scan-cloaked-files + $(DotNetPath)/dotnet darc vmr scan-cloaked-files --vmr "$(Build.SourcesDirectory)" --tmp "$(Agent.TempDirectory)" || (echo '##[error]Found cloaked files in the VMR' && exit 1) diff --git a/eng/pipelines/templates/stages/vmr-validation.yml b/eng/pipelines/templates/stages/vmr-validation.yml index 31e683a25fa2..83aa408574a3 100644 --- a/eng/pipelines/templates/stages/vmr-validation.yml +++ b/eng/pipelines/templates/stages/vmr-validation.yml @@ -53,6 +53,7 @@ stages: displayName: VMR Validation dependsOn: - VMR_Vertical_Build + - VMR_SourceOnly_Build variables: - template: ../variables/vmr-build.yml parameters: @@ -65,7 +66,7 @@ stages: jobs: - job: ValidateInstallers_Linux_x64 pool: ${{ parameters.pool_Linux }} - timeoutInMinutes: 30 + timeoutInMinutes: 60 steps: - template: ../steps/vmr-validate-installers.yml parameters: @@ -85,7 +86,7 @@ stages: reuseBuildArtifactsFrom: - Windows_x64 - AzureLinux_x64_Cross_arm64 - - ${{ if eq(variables.signEnabled, 'true') }}: + - ${{ if eq(variables.signType, 'real') }}: - job: ValidateSigning_Windows displayName: Validate Signing - Windows pool: ${{ parameters.pool_Windows }} diff --git a/eng/pipelines/templates/stages/vmr-verticals.yml b/eng/pipelines/templates/stages/vmr-verticals.yml index 0687b344b6d1..4a77a2091ce7 100644 --- a/eng/pipelines/templates/stages/vmr-verticals.yml +++ b/eng/pipelines/templates/stages/vmr-verticals.yml @@ -82,6 +82,12 @@ parameters: demands: ImageOverride -equals $(poolImage_Linux) os: linux +# Indicates whether to exclude runtime dependent jobs. +# This is used to skip building the cross-OS DACs and other runtime dependent jobs in non-1xx branches. +- name: excludeRuntimeDependentJobs + type: boolean + default: false + stages: #### VERTICAL BUILD (Validation) #### @@ -162,225 +168,229 @@ stages: targetOS: windows targetArchitecture: x86 - - ${{ if containsValue(parameters.verifications, 'unified-build-android-arm64') }}: - - template: ../jobs/vmr-build.yml - parameters: - buildName: Android_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.androidCrossContainerName }} - image: ${{ variables.androidCrossContainerImage }} - targetOS: android - targetArchitecture: arm64 - - - ${{ if containsValue(parameters.verifications, 'unified-build-browser-wasm') }}: - - template: ../jobs/vmr-build.yml - parameters: - buildName: Browser_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.browserCrossContainerName }} - image: ${{ variables.browserCrossContainerImage }} - crossRootFs: '/crossrootfs/x64' - targetOS: browser - targetArchitecture: wasm - - - ${{ if containsValue(parameters.verifications, 'unified-build-iossimulator-arm64') }}: - - template: ../jobs/vmr-build.yml - parameters: - buildName: iOSSimulator_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: iossimulator - targetArchitecture: arm64 + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + + - ${{ if containsValue(parameters.verifications, 'unified-build-android-arm64') }}: + - template: ../jobs/vmr-build.yml + parameters: + buildName: Android_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.androidCrossContainerName }} + image: ${{ variables.androidCrossContainerImage }} + targetOS: android + targetArchitecture: arm64 + + - ${{ if containsValue(parameters.verifications, 'unified-build-browser-wasm') }}: + - template: ../jobs/vmr-build.yml + parameters: + buildName: Browser_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.browserCrossContainerName }} + image: ${{ variables.browserCrossContainerImage }} + crossRootFs: '/crossrootfs/x64' + targetOS: browser + targetArchitecture: wasm + + - ${{ if containsValue(parameters.verifications, 'unified-build-iossimulator-arm64') }}: + - template: ../jobs/vmr-build.yml + parameters: + buildName: iOSSimulator_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: iossimulator + targetArchitecture: arm64 ### Additional jobs for full build ### - ${{ if in(parameters.scope, 'full') }}: - - template: ../jobs/vmr-build.yml - parameters: - buildName: Android_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.androidCrossContainerName }} - image: ${{ variables.androidCrossContainerImage }} - targetOS: android - targetArchitecture: arm - - - template: ../jobs/vmr-build.yml - parameters: - buildName: Android_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.androidCrossContainerName }} - image: ${{ variables.androidCrossContainerImage }} - targetOS: android - targetArchitecture: x64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: Android_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.androidCrossContainerName }} - image: ${{ variables.androidCrossContainerImage }} - targetOS: android - targetArchitecture: x86 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: Browser_Multithreaded_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.browserCrossContainerName }} - image: ${{ variables.browserCrossContainerImage }} - crossRootFs: '/crossrootfs/x64' - targetOS: browser - targetArchitecture: wasm - extraProperties: /p:WasmEnableThreads=true - - - template: ../jobs/vmr-build.yml - parameters: - buildName: LinuxBionic_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.linuxBionicCrossContainerName }} - image: ${{ variables.linuxBionicCrossContainerImage }} - targetOS: linux-bionic - targetArchitecture: arm64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: LinuxBionic_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.linuxBionicCrossContainerName }} - image: ${{ variables.linuxBionicCrossContainerImage }} - targetOS: linux-bionic - targetArchitecture: arm - - - template: ../jobs/vmr-build.yml - parameters: - buildName: LinuxBionic_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.linuxBionicCrossContainerName }} - image: ${{ variables.linuxBionicCrossContainerImage }} - targetOS: linux-bionic - targetArchitecture: x64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: iOS_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: ios - targetArchitecture: arm64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: iOSSimulator_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: iossimulator - targetArchitecture: x64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: MacCatalyst_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: maccatalyst - targetArchitecture: arm64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: MacCatalyst_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: maccatalyst - targetArchitecture: x64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: tvOS_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: tvos - targetArchitecture: arm64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: tvOSSimulator_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: tvossimulator - targetArchitecture: arm64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: tvOSSimulator_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - targetOS: tvossimulator - targetArchitecture: x64 - - - template: ../jobs/vmr-build.yml - parameters: - buildName: Wasi_Shortstack - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.wasiCrossContainerName }} - image: ${{ variables.wasiCrossContainerImage }} - crossRootFs: '/crossrootfs/x64' - targetOS: wasi - targetArchitecture: wasm + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + + - template: ../jobs/vmr-build.yml + parameters: + buildName: Android_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.androidCrossContainerName }} + image: ${{ variables.androidCrossContainerImage }} + targetOS: android + targetArchitecture: arm + + - template: ../jobs/vmr-build.yml + parameters: + buildName: Android_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.androidCrossContainerName }} + image: ${{ variables.androidCrossContainerImage }} + targetOS: android + targetArchitecture: x64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: Android_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.androidCrossContainerName }} + image: ${{ variables.androidCrossContainerImage }} + targetOS: android + targetArchitecture: x86 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: Browser_Multithreaded_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.browserCrossContainerName }} + image: ${{ variables.browserCrossContainerImage }} + crossRootFs: '/crossrootfs/x64' + targetOS: browser + targetArchitecture: wasm + extraProperties: /p:WasmEnableThreads=true + + - template: ../jobs/vmr-build.yml + parameters: + buildName: LinuxBionic_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.linuxBionicCrossContainerName }} + image: ${{ variables.linuxBionicCrossContainerImage }} + targetOS: linux-bionic + targetArchitecture: arm64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: LinuxBionic_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.linuxBionicCrossContainerName }} + image: ${{ variables.linuxBionicCrossContainerImage }} + targetOS: linux-bionic + targetArchitecture: arm + + - template: ../jobs/vmr-build.yml + parameters: + buildName: LinuxBionic_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.linuxBionicCrossContainerName }} + image: ${{ variables.linuxBionicCrossContainerImage }} + targetOS: linux-bionic + targetArchitecture: x64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: iOS_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: ios + targetArchitecture: arm64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: iOSSimulator_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: iossimulator + targetArchitecture: x64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: MacCatalyst_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: maccatalyst + targetArchitecture: arm64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: MacCatalyst_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: maccatalyst + targetArchitecture: x64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: tvOS_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: tvos + targetArchitecture: arm64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: tvOSSimulator_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: tvossimulator + targetArchitecture: arm64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: tvOSSimulator_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + targetOS: tvossimulator + targetArchitecture: x64 + + - template: ../jobs/vmr-build.yml + parameters: + buildName: Wasi_Shortstack + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.wasiCrossContainerName }} + image: ${{ variables.wasiCrossContainerImage }} + crossRootFs: '/crossrootfs/x64' + targetOS: wasi + targetArchitecture: wasm - template: ../jobs/vmr-build.yml parameters: @@ -392,17 +402,19 @@ stages: targetOS: osx targetArchitecture: x64 - - template: ../jobs/vmr-build.yml - parameters: - buildName: OSX_Shortstack_Mono_LLVMAOT - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Mac }} - useMonoRuntime: true - targetOS: osx - targetArchitecture: x64 - extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + + - template: ../jobs/vmr-build.yml + parameters: + buildName: OSX_Shortstack_Mono_LLVMAOT + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Mac }} + useMonoRuntime: true + targetOS: osx + targetArchitecture: x64 + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: @@ -432,21 +444,23 @@ stages: targetArchitecture: x64 extraProperties: /p:PgoInstrument=true - - template: ../jobs/vmr-build.yml - parameters: - buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.azurelinuxX64CrossContainerName }} - image: ${{ variables.azurelinuxX64CrossContainerImage }} - crossRootFs: '/crossrootfs/x64' - useMonoRuntime: true - targetOS: linux - targetArchitecture: x64 - extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + + - template: ../jobs/vmr-build.yml + parameters: + buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.azurelinuxX64CrossContainerName }} + image: ${{ variables.azurelinuxX64CrossContainerImage }} + crossRootFs: '/crossrootfs/x64' + useMonoRuntime: true + targetOS: linux + targetArchitecture: x64 + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: @@ -535,21 +549,22 @@ stages: targetArchitecture: arm64 targetRid: ${{ variables.linuxMuslArm64Rid }} - - template: ../jobs/vmr-build.yml - parameters: - buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - pool: ${{ parameters.pool_Linux_Shortstack }} - container: - name: ${{ variables.azurelinuxArm64CrossContainerName }} - image: ${{ variables.azurelinuxArm64CrossContainerImage }} - crossRootFs: '/crossrootfs/arm64' - useMonoRuntime: true - targetOS: linux - targetArchitecture: arm64 - extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + - template: ../jobs/vmr-build.yml + parameters: + buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + pool: ${{ parameters.pool_Linux_Shortstack }} + container: + name: ${{ variables.azurelinuxArm64CrossContainerName }} + image: ${{ variables.azurelinuxArm64CrossContainerImage }} + crossRootFs: '/crossrootfs/arm64' + useMonoRuntime: true + targetOS: linux + targetArchitecture: arm64 + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: @@ -606,25 +621,27 @@ stages: ## Build Pass 2 verticals - # build the cross-OS DACs - - template: ../jobs/vmr-build.yml - parameters: - buildName: Windows - isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} - sign: ${{ variables.signEnabled }} - signType: ${{ variables.signType }} - signDac: ${{ variables.signDacEnabled }} - pool: ${{ parameters.pool_Windows }} - targetOS: windows - targetArchitecture: x86 - buildPass: 2 - reuseBuildArtifactsFrom: - - AzureLinux_x64_Cross_x64 - - AzureLinux_x64_Cross_Alpine_x64 - - AzureLinux_x64_Cross_arm64 - - AzureLinux_x64_Cross_Alpine_arm64 - - AzureLinux_x64_Cross_arm - - AzureLinux_x64_Cross_Alpine_arm + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + + # build the cross-OS DACs + - template: ../jobs/vmr-build.yml + parameters: + buildName: Windows + isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }} + sign: ${{ variables.signEnabled }} + signType: ${{ variables.signType }} + signDac: ${{ variables.signDacEnabled }} + pool: ${{ parameters.pool_Windows }} + targetOS: windows + targetArchitecture: x86 + buildPass: 2 + reuseBuildArtifactsFrom: + - AzureLinux_x64_Cross_x64 + - AzureLinux_x64_Cross_Alpine_x64 + - AzureLinux_x64_Cross_arm64 + - AzureLinux_x64_Cross_Alpine_arm64 + - AzureLinux_x64_Cross_arm + - AzureLinux_x64_Cross_Alpine_arm # build the ASP.NET Core hosting bundle and VS components - template: ../jobs/vmr-build.yml @@ -658,21 +675,22 @@ stages: - Windows_x64 - Windows_x86 - Windows_arm64 - - Browser_Shortstack_wasm - - Browser_Multithreaded_Shortstack_wasm - - Wasi_Shortstack_wasm - - Android_Shortstack_arm64 - - Android_Shortstack_arm - - Android_Shortstack_x64 - - Android_Shortstack_x86 - - iOS_Shortstack_arm64 - - iOSSimulator_Shortstack_arm64 - - iOSSimulator_Shortstack_x64 - - tvOS_Shortstack_arm64 - - tvOSSimulator_Shortstack_arm64 - - tvOSSimulator_Shortstack_x64 - - MacCatalyst_Shortstack_arm64 - - MacCatalyst_Shortstack_x64 + - ${{ if not(parameters.excludeRuntimeDependentJobs) }}: + - Browser_Shortstack_wasm + - Browser_Multithreaded_Shortstack_wasm + - Wasi_Shortstack_wasm + - Android_Shortstack_arm64 + - Android_Shortstack_arm + - Android_Shortstack_x64 + - Android_Shortstack_x86 + - iOS_Shortstack_arm64 + - iOSSimulator_Shortstack_arm64 + - iOSSimulator_Shortstack_x64 + - tvOS_Shortstack_arm64 + - tvOSSimulator_Shortstack_arm64 + - tvOSSimulator_Shortstack_x64 + - MacCatalyst_Shortstack_arm64 + - MacCatalyst_Shortstack_x64 ## Build Pass 3 verticals diff --git a/eng/pipelines/templates/steps/create-baseline-update-pr.yml b/eng/pipelines/templates/steps/create-baseline-update-pr.yml index 442c59013d4c..0332a9fbc45b 100644 --- a/eng/pipelines/templates/steps/create-baseline-update-pr.yml +++ b/eng/pipelines/templates/steps/create-baseline-update-pr.yml @@ -1,4 +1,8 @@ parameters: +- name: dotnetPath + type: string + default: '$(Build.SourcesDirectory)/.dotnet' + # The pipeline that is being run # Used to determine the correct baseline update tool to run # Currently only supports "sdk" and "license" @@ -31,7 +35,7 @@ steps: branchName=$(echo "$(Build.SourceBranch)" | sed 's/refs\/heads\///g') - .dotnet/dotnet run \ + ${{ parameters.dotnetPath }}/dotnet run \ --project eng/tools/CreateBaselineUpdatePR/ \ --property:RestoreSources="$restoreSources" \ "${{ parameters.pipeline }}" \ diff --git a/eng/pipelines/templates/steps/vmr-validate-installers.yml b/eng/pipelines/templates/steps/vmr-validate-installers.yml index ed2bdff78a20..8e46f54d9694 100644 --- a/eng/pipelines/templates/steps/vmr-validate-installers.yml +++ b/eng/pipelines/templates/steps/vmr-validate-installers.yml @@ -15,6 +15,11 @@ parameters: default: '' steps: +- ${{ if eq(parameters.OS, 'Darwin') }}: + - script: | + xcrun simctl runtime delete all + displayName: Reclaim disk space from unused simulator runtimes + - ${{ if ne(parameters.reuseBuildArtifactsFrom,'') }}: - ${{ each reuseBuildArtifacts in parameters.reuseBuildArtifactsFrom }}: - task: DownloadPipelineArtifact@2 diff --git a/eng/pipelines/templates/steps/vmr-validate-signing.yml b/eng/pipelines/templates/steps/vmr-validate-signing.yml index 0d6b3cb14261..dd6f4991e729 100644 --- a/eng/pipelines/templates/steps/vmr-validate-signing.yml +++ b/eng/pipelines/templates/steps/vmr-validate-signing.yml @@ -28,6 +28,13 @@ parameters: default: '$(Build.ArtifactStagingDirectory)/SigningValidation' steps: +- ${{ if eq(parameters.OS, 'Darwin') }}: + - script: | + xcrun simctl runtime delete all + displayName: Reclaim disk space from unused simulator runtimes + +# Include "**SB_CentOSStream*_Offline_MsftSdk_x64_Artifacts/**/Release/source-build/dotnet-source-*" +# once https://github.com/dotnet/arcade/issues/16034 has been resolved. - task: DownloadPipelineArtifact@2 displayName: Download Vertical Build Artifacts continueOnError: ${{ parameters.continueOnError }} @@ -38,6 +45,8 @@ steps: **AzureLinux*_Artifacts/** **OSX_*_Artifacts/** **Windows_*_Artifacts/** + **SB_CentOSStream*_Offline_MsftSdk_x64_Artifacts/**/Release/source-build/dotnet-sdk-*.tar.gz + **SB_CentOSStream*_Offline_MsftSdk_x64_Artifacts/**/Release/source-build/Private.SourceBuilt.Artifacts.*.tar.gz downloadPath: '${{ parameters.signCheckFilesDirectory }}' # This is necessary whenever we want to publish/restore to an AzDO private feed @@ -58,7 +67,8 @@ steps: /p:ArtifactDownloadDirectory=${{ parameters.signCheckFilesDirectory }} ` /p:SourceBranch='$(Build.SourceBranch)' ` /p:DotNetRootDirectory=$(Build.SourcesDirectory) ` - /p:OutputLogsDirectory=${{ parameters.outputDirectory }} + /p:OutputLogsDirectory=${{ parameters.outputDirectory }} ` + /bl:${{ parameters.outputDirectory }}/outer-signing-validation.binlog displayName: Validate Signing continueOnError: ${{ parameters.continueOnError }} @@ -75,7 +85,8 @@ steps: /p:ArtifactDownloadDirectory=${{ parameters.signCheckFilesDirectory }} \ /p:SourceBranch='$(Build.SourceBranch)' \ /p:DotNetRootDirectory=$(Build.SourcesDirectory) \ - /p:OutputLogsDirectory=${{ parameters.outputDirectory }} + /p:OutputLogsDirectory=${{ parameters.outputDirectory }} \ + /bl:${{ parameters.outputDirectory }}/outer-signing-validation.binlog displayName: Validate Signing continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/pipelines/templates/variables/vmr-build.yml b/eng/pipelines/templates/variables/vmr-build.yml index f570a7579f0c..13f3fb918837 100644 --- a/eng/pipelines/templates/variables/vmr-build.yml +++ b/eng/pipelines/templates/variables/vmr-build.yml @@ -41,8 +41,8 @@ variables: - name: ibcEnabled value: false - ${{ elseif or(startsWith(parameters.desiredIBC, 'Default'), eq(parameters.desiredIBC, '')) }}: - # Enable IBC on the internal project if the, branch is a release branch - - ${{ if and(eq(parameters.isOfficialBuild, true), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: + # Enable IBC on the internal project if this is an official build of a release branch and is not a source-only build. + - ${{ if and(eq(parameters.isOfficialBuild, true), ne(parameters.buildSourceOnly, true), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: - name: ibcEnabled value: true # No IBC otherwise. @@ -53,18 +53,21 @@ variables: - name: ibcEnabled value: false -- ${{ if eq(parameters.desiredFinalVersionKind, 'Default (repo specifies)') }}: +- ${{ if eq(parameters.desiredFinalVersionKind, 'Repo default (uses the repo branding defaults)') }}: - name: brandingType - value: 'default' -- ${{ elseif eq(parameters.desiredFinalVersionKind, 'Stable Preview') }}: + value: 'repodefault' +- ${{ elseif eq(parameters.desiredFinalVersionKind, 'Unstable (produces assets with the repo specified branding suffix)') }}: + - name: brandingType + value: 'unstable' +- ${{ elseif eq(parameters.desiredFinalVersionKind, 'Preview (produces assets with a .final branding suffix)') }}: - name: brandingType value: 'preview' -- ${{ elseif eq(parameters.desiredFinalVersionKind, 'Stable Final') }}: +- ${{ elseif eq(parameters.desiredFinalVersionKind, 'Release (produces assets without a branding suffix)') }}: - name: brandingType - value: 'rtm' + value: 'release' - ${{ else }}: - name: brandingType - value: 'default' + value: '' ## Do not set this when building source only to avoid including telemetry in the bootstrap artifacts ## handed off to distro partners. @@ -75,15 +78,13 @@ variables: - name: officialBuildProperties value: '' -- ${{ if eq(parameters.buildSourceOnly, true) }}: - - name: signEnabled - value: false -- ${{ elseif eq(parameters.desiredSigning, 'Signed (Real)') }}: +- ${{ if eq(parameters.desiredSigning, 'Signed (Real)') }}: - name: signEnabled value: true - name: signType - value: Real - - ${{ if and(eq(parameters.isOfficialBuild, true), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: + value: real + # Only sign DACs if this is an official build of a release branch and is not a source-only build. + - ${{ if and(eq(parameters.isOfficialBuild, true), ne(parameters.buildSourceOnly, true), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: - name: signDacEnabled value: true - ${{ else }}: @@ -93,7 +94,7 @@ variables: - name: signEnabled value: true - name: signType - value: Test + value: test - name: signDacEnabled value: false - ${{ elseif eq(parameters.desiredSigning, 'Signed (Dry Run)') }}: @@ -123,8 +124,13 @@ variables: value: true - name: signType value: real - - name: signDacEnabled - value: true + # Only sign DACs if this is not a source-only build. + - ${{ if ne(parameters.buildSourceOnly, true) }}: + - name: signDacEnabled + value: true + - ${{ else }}: + - name: signDacEnabled + value: false # Do not sign otherwise. - ${{ else }}: - name: signEnabled @@ -145,7 +151,7 @@ variables: - name: alpineContainerName value: alpineContainer - name: alpineContainerImage - value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-amd64 + value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.23-amd64 - name: centOSStreamContainerName value: centOSStreamContainer @@ -155,7 +161,7 @@ variables: - name: fedoraContainerName value: fedoraContainer - name: fedoraContainerImage - value: mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-41 + value: mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-43-amd64 - name: ubuntuContainerName value: ubuntuContainer @@ -220,11 +226,11 @@ variables: - name: almaLinuxName value: AlmaLinux8 - name: alpineName - value: Alpine321 + value: Alpine323 - name: centOSStreamName value: CentOSStream10 - name: fedoraName - value: Fedora41 + value: Fedora43 - name: ubuntuName value: Ubuntu2404 @@ -241,11 +247,11 @@ variables: - name: linuxMuslArm64Rid value: linux-musl-arm64 - name: alpineX64Rid - value: alpine.3.21-x64 + value: alpine.3.23-x64 - name: centOSStreamX64Rid value: centos.10-x64 - name: fedoraX64Rid - value: fedora.41-x64 + value: fedora.43-x64 - name: ubuntux64Rid value: ubuntu.24.04-x64 - name: ubuntuArm64Rid @@ -263,13 +269,13 @@ variables: - name: poolName_LinuxArm64 value: Docker-Linux-Arm-Public - name: poolImage_Mac - value: macos-13 + value: macos-14 - ${{ if eq(parameters.isScoutingJob, true) }}: - name: poolImage_Windows - value: windows.vs2022preview.scout.amd64.open + value: windows.vs2022.scout.amd64.open - ${{ else }}: - name: poolImage_Windows - value: windows.vs2022preview.amd64.open + value: windows.vs2022.amd64.open - ${{ else }}: - name: defaultPoolName value: NetCore1ESPool-Internal @@ -278,11 +284,10 @@ variables: - name: poolImage_Linux value: build.ubuntu.2204.amd64 - name: poolImage_LinuxArm64 - value: Mariner-2-Docker-ARM64 + value: Azure-Linux-3-Arm64 - name: poolName_LinuxArm64 value: Docker-Linux-Arm-Internal - name: poolImage_Mac value: macos-latest-internal - name: poolImage_Windows - value: windows.vs2022preview.amd64 - + value: windows.vs2022.amd64 diff --git a/eng/pipelines/unofficial.yml b/eng/pipelines/unofficial.yml index 32bb1d509042..d25e87cdd39e 100644 --- a/eng/pipelines/unofficial.yml +++ b/eng/pipelines/unofficial.yml @@ -133,3 +133,6 @@ extends: desiredFinalVersionKind: ${{ parameters.desiredFinalVersionKind }} scope: ${{ parameters.buildScope }} isOfficialBuild: false + # Exclude runtime dependent jobs for any branch not producing a 1xx version since runtime-related repos aren't built in that context + # This is enabled for all branches except main and 1xx + excludeRuntimeDependentJobs: false diff --git a/eng/pipelines/vmr-license-scan.yml b/eng/pipelines/vmr-license-scan.yml index d0dda14d0db3..350dcb203439 100644 --- a/eng/pipelines/vmr-license-scan.yml +++ b/eng/pipelines/vmr-license-scan.yml @@ -6,9 +6,14 @@ schedules: branches: # For releases branches only run on internal/release branches because that's where dependencies flow. # Previews don't have internal/release branches so they must be run from non-internal release branches. + # The 2xx, 3xx, 4xx triggers exist to cover the feature band pre-release time periods. + # They can be removed once released as the triggers below will handle the branches after release. include: - main - release/*.0.1xx-preview* + - release/*.0.2xx* + - release/*.0.3xx* + - release/*.0.4xx* - internal/release/*.0.1xx* pr: none @@ -16,10 +21,15 @@ pr: none # Always trigger a run when changes are made to the license test implementation or baselines trigger: branches: + # The 2xx, 3xx, 4xx triggers exist to cover the feature band pre-release time periods. + # They can be removed once released as the internal/release trigger will handle the branches after release. include: - main - release/*.0.1xx-preview* - - internal/release/*.0.1xx* + - release/*.0.2xx* + - release/*.0.3xx* + - release/*.0.4xx* + - internal/release/*.0.?xx* paths: include: - test/Microsoft.DotNet.SourceBuild.Tests/LicenseScanTests.cs diff --git a/eng/shared-source-only.targets b/eng/shared-source-only.targets new file mode 100644 index 000000000000..18271f472fe3 --- /dev/null +++ b/eng/shared-source-only.targets @@ -0,0 +1,84 @@ + + + + + + + BootstrapOnly + BootstrapRepoDepsOnly + NonToolingOnly + NonToolingAndBootstrapOnly + ToolingOnly + + + + + + <_VersionDetailsXml Condition="'$(PackageVersionPropsFlowType)' == 'DependenciesOnly'">$(ProjectDirectory)eng/Version.Details.xml + + + + + + + ;@(SharedRepositoryReference); + <_BootstrapRepoDepsString>;@(_BootstrapRepoDeps); + $(SharedComponentFilter_NonToolingAndBootstrapOnly) + + + + + + + + + <_ItemsToRemove Include="@(SharedComponentFilteredPackages)" + Condition="!$(SharedRepositoryReferenceString.Contains(';%(RepoOrigin);')) and !$([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';%(RepoOrigin);'))" /> + + + + + <_ItemsToRemove Include="@(SharedComponentFilteredPackages)" + Condition="!$(SharedRepositoryReferenceString.Contains(';%(RepoOrigin);'))" /> + + + + + <_ItemsToRemove Include="@(SharedComponentFilteredPackages)" + Condition="$(SharedRepositoryReferenceString.Contains(';%(RepoOrigin);')) or $([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';%(RepoOrigin);'))" /> + + + + + <_ItemsToRemove Include="@(SharedComponentFilteredPackages)" + Condition="!$([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';%(RepoOrigin);'))" /> + + + + + <_ItemsToRemove Include="@(SharedComponentFilteredPackages)" + Condition="!$([System.String]::new(';$(_BootstrapRepoDepsString);').Contains(';%(Identity);'))" /> + + + + + <_ItemsToRemoveString>@(_ItemsToRemove->'%(Identity)::%(Version)', ';') + + + + + + + $(SharedComponentsArtifactsPath)$([System.IO.Path]::GetFileName('%(PipelineArtifactPath)')) + + + + + + diff --git a/eng/sign-source-built-artifacts.proj b/eng/sign-source-built-artifacts.proj new file mode 100644 index 000000000000..001cc2a96a0f --- /dev/null +++ b/eng/sign-source-built-artifacts.proj @@ -0,0 +1,63 @@ + + + + $(NetCurrent) + + + + + + + + + + + + + + + + + + + $(ArtifactsDir)/staging/ + $(ArtifactsStagingDir)/artifacts + $(ArtifactsPublishStagingDir)/assets/$(Configuration) + $(SourceBuiltAssetsDir)/source-build + + + + dotnet-sdk + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/source-build-toolset-init.sh b/eng/source-build-toolset-init.sh new file mode 100755 index 000000000000..dd5a1eb4d4c6 --- /dev/null +++ b/eng/source-build-toolset-init.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +### This script provides shared functionality for initializing the source build toolset. +### It handles custom SDK setup, MSBuild SDK resolver initialization, and toolset preparation. + +set -euo pipefail + +function source_only_toolset_init() { + local custom_sdk_dir="$1" + local custom_packages_dir="$2" + local binary_log="$3" + local is_running_tests="${4:-false}" + local properties=("${@:5}") # All remaining arguments are properties + + echo "Initializing source-only toolset..." + + # Determine the script directory and navigate up to the repository root + local script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + local repo_root="$( cd "${script_dir}/.." && pwd )" + + # Set up custom SDK if specified + if [ -n "$custom_sdk_dir" ]; then + if [ ! -d "$custom_sdk_dir" ]; then + echo "ERROR: Custom SDK directory '$custom_sdk_dir' does not exist" + exit 1 + fi + if [ ! -x "$custom_sdk_dir/dotnet" ]; then + echo "ERROR: Custom SDK '$custom_sdk_dir/dotnet' does not exist or is not executable" + exit 1 + fi + export SDK_VERSION=$("$custom_sdk_dir/dotnet" --version) + export CLI_ROOT="$custom_sdk_dir" + echo "Using custom bootstrap SDK from '$CLI_ROOT', version '$SDK_VERSION'" + else + sdkLine=$(grep -m 1 'dotnet' "$repo_root/global.json") + sdkPattern="\"dotnet\" *: *\"(.*)\"" + if [[ $sdkLine =~ $sdkPattern ]]; then + export SDK_VERSION=${BASH_REMATCH[1]} + export CLI_ROOT="$repo_root/.dotnet" + fi + fi + + # Set _InitializeDotNetCli & DOTNET_INSTALL_DIR so that eng/common/tools.sh doesn't attempt to restore the SDK. + export _InitializeDotNetCli="$CLI_ROOT" + export DOTNET_INSTALL_DIR="$CLI_ROOT" + + # Don't use the global nuget cache folder when building source-only which + # restores prebuilt packages that should never get into the global nuget cache. + export NUGET_PACKAGES="$repo_root/.packages/" + + if [[ "$is_running_tests" == true ]]; then + # Use a custom package cache for tests to make prebuilt detection work. + export NUGET_PACKAGES="${NUGET_PACKAGES}tests/" + fi + + echo "NuGet packages cache: '${NUGET_PACKAGES}'" + + # Find the Arcade SDK version and set env vars for the msbuild sdk resolver + local packageVersionsPath='' + local packagesDir="$repo_root/prereqs/packages/" + local packagesArchiveDir="${packagesDir}archive/" + local packagesPreviouslySourceBuiltDir="${packagesDir}previously-source-built/" + local tempDir="" + + if [[ "$custom_packages_dir" != "" && -f "$custom_packages_dir/PackageVersions.props" ]]; then + packageVersionsPath="$custom_packages_dir/PackageVersions.props" + elif [ -d "$packagesArchiveDir" ]; then + sourceBuiltArchive=$(find "$packagesArchiveDir" -maxdepth 1 -name 'Private.SourceBuilt.Artifacts*.tar.gz') + if [ -f "${packagesPreviouslySourceBuiltDir}PackageVersions.props" ]; then + packageVersionsPath=${packagesPreviouslySourceBuiltDir}PackageVersions.props + elif [ -f "$sourceBuiltArchive" ]; then + # Extract safely into a private temporary directory and ensure cleanup. + tempDir="$(mktemp -d)" + trap "rm -rf '${tempDir}'" EXIT # tempDir is local, use double quotes to expand. + tar -xzf "$sourceBuiltArchive" -C "$tempDir" PackageVersions.props + packageVersionsPath="$tempDir/PackageVersions.props" + fi + fi + + if [ ! -f "$packageVersionsPath" ]; then + echo "Cannot find PackagesVersions.props. Debugging info:" + echo " Attempted custom PVP path: $custom_packages_dir/PackageVersions.props" + echo " Attempted previously-source-built path: ${packagesPreviouslySourceBuiltDir}PackageVersions.props" + echo " Attempted archive path: $packagesArchiveDir" + exit 1 + fi + + # Extract toolset packages + + # Ensure that by default, the bootstrap version of the toolset SDK is used. Source-build infra + # projects use bootstrap toolset SDKs, and would fail to find it in the build. The repo + # projects overwrite this so that they use the source-built toolset SDK instad. + + # 1. Microsoft.DotNet.Arcade.Sdk + arcadeSdkLine=$(grep -m 1 'MicrosoftDotNetArcadeSdkVersion' "$packageVersionsPath") + arcadeSdkPattern="(.*)" + if [[ $arcadeSdkLine =~ $arcadeSdkPattern ]]; then + export ARCADE_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} + + export SOURCE_BUILT_SDK_ID_ARCADE=Microsoft.DotNet.Arcade.Sdk + export SOURCE_BUILT_SDK_VERSION_ARCADE=$ARCADE_BOOTSTRAP_VERSION + export SOURCE_BUILT_SDK_DIR_ARCADE=${NUGET_PACKAGES}BootstrapPackages/microsoft.dotnet.arcade.sdk/$ARCADE_BOOTSTRAP_VERSION + fi + + # 2. Microsoft.Build.NoTargets + notargetsSdkLine=$(grep -m 1 'Microsoft.Build.NoTargets' "$repo_root/global.json") + notargetsSdkPattern="\"Microsoft\\.Build\\.NoTargets\" *: *\"(.*)\"" + if [[ $notargetsSdkLine =~ $notargetsSdkPattern ]]; then + export NOTARGETS_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} + + export SOURCE_BUILT_SDK_ID_NOTARGETS=Microsoft.Build.NoTargets + export SOURCE_BUILT_SDK_VERSION_NOTARGETS=$NOTARGETS_BOOTSTRAP_VERSION + export SOURCE_BUILT_SDK_DIR_NOTARGETS=${NUGET_PACKAGES}BootstrapPackages/microsoft.build.notargets/$NOTARGETS_BOOTSTRAP_VERSION + fi + + # 3. Microsoft.Build.Traversal + traversalSdkLine=$(grep -m 1 'Microsoft.Build.Traversal' "$repo_root/global.json") + traversalSdkPattern="\"Microsoft\\.Build\\.Traversal\" *: *\"(.*)\"" + if [[ $traversalSdkLine =~ $traversalSdkPattern ]]; then + export TRAVERSAL_BOOTSTRAP_VERSION=${BASH_REMATCH[1]} + + export SOURCE_BUILT_SDK_ID_TRAVERSAL=Microsoft.Build.Traversal + export SOURCE_BUILT_SDK_VERSION_TRAVERSAL=$TRAVERSAL_BOOTSTRAP_VERSION + export SOURCE_BUILT_SDK_DIR_TRAVERSAL=${NUGET_PACKAGES}BootstrapPackages/microsoft.build.traversal/$TRAVERSAL_BOOTSTRAP_VERSION + fi + + echo "Found bootstrap versions: SDK $SDK_VERSION, Arcade $ARCADE_BOOTSTRAP_VERSION, NoTargets $NOTARGETS_BOOTSTRAP_VERSION and Traversal $TRAVERSAL_BOOTSTRAP_VERSION" + + # toolset prep steps + source "$repo_root/eng/common/tools.sh" + InitializeBuildTool + + initSourceOnlyBinaryLog="" + if [[ "$binary_log" == true ]]; then + initSourceOnlyBinaryLog="/bl:\"$log_dir/init-source-only.binlog\"" + fi + + "$_InitializeBuildTool" build-server shutdown --msbuild + MSBuild-Core "$repo_root/eng/init-source-only.proj" $initSourceOnlyBinaryLog "${properties[@]}" + # kill off the MSBuild server so that on future invocations we pick up our custom SDK Resolver + "$_InitializeBuildTool" build-server shutdown --msbuild + + local bootstrapArcadeDir=$(cat "$repo_root/artifacts/toolset/bootstrap-sdks.txt" | grep "microsoft.dotnet.arcade.sdk") + local arcadeBuildStepsDir="$bootstrapArcadeDir/tools/" + + # Point MSBuild to the custom SDK resolvers folder, so it will pick up our custom SDK Resolver + export MSBUILDADDITIONALSDKRESOLVERSFOLDER="$repo_root/artifacts/toolset/VSSdkResolvers/" + + # Set _InitializeToolset so that eng/common/tools.sh doesn't attempt to restore the arcade toolset again. + _InitializeToolset="${arcadeBuildStepsDir}/Build.proj" + + echo "Source-only toolset initialization complete" +} diff --git a/eng/tools/BinaryToolKit/BinaryTool.cs b/eng/tools/BinaryToolKit/BinaryTool.cs index a9343581883c..c17a0405d1b4 100644 --- a/eng/tools/BinaryToolKit/BinaryTool.cs +++ b/eng/tools/BinaryToolKit/BinaryTool.cs @@ -9,7 +9,7 @@ namespace BinaryToolKit; public static class BinaryTool { - public static async Task ExecuteAsync( + public static void Execute( TaskLoggingHelper log, string targetDirectory, string outputReportDirectory, @@ -21,7 +21,7 @@ public static async Task ExecuteAsync( log.LogMessage(MessageImportance.High, $"Starting binary tool at {startTime} in {mode} mode"); // Run the tooling - var detectedBinaries = await DetectBinaries.ExecuteAsync(log, targetDirectory, outputReportDirectory, allowedBinariesFile); + var detectedBinaries = DetectBinaries.Execute(log, targetDirectory, outputReportDirectory, allowedBinariesFile); if (mode == Modes.Validate) { diff --git a/eng/tools/BinaryToolKit/BinaryToolTask.cs b/eng/tools/BinaryToolKit/BinaryToolTask.cs index e4ac9e2b11fc..df9d4ca81a87 100644 --- a/eng/tools/BinaryToolKit/BinaryToolTask.cs +++ b/eng/tools/BinaryToolKit/BinaryToolTask.cs @@ -37,14 +37,12 @@ public string Mode private Modes _mode; - public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult(); - - private async Task ExecuteAsync() + public override bool Execute() { try { ParseArgs(); - await BinaryTool.ExecuteAsync(Log, TargetDirectory, OutputReportDirectory, AllowedBinariesFile, _mode); + BinaryTool.Execute(Log, TargetDirectory, OutputReportDirectory, AllowedBinariesFile, _mode); } catch (Exception ex) { diff --git a/eng/tools/BinaryToolKit/DetectBinaries.cs b/eng/tools/BinaryToolKit/DetectBinaries.cs index 4037a79ebb34..45b966bef12f 100644 --- a/eng/tools/BinaryToolKit/DetectBinaries.cs +++ b/eng/tools/BinaryToolKit/DetectBinaries.cs @@ -17,11 +17,9 @@ namespace BinaryToolKit; public static class DetectBinaries { - private const string Utf16Marker = "UTF-16"; - private const int ChunkSize = 4096; private static readonly Regex GitCleanRegex = new Regex(@"Would (remove|skip)( repository)? (.*)"); - public static async Task> ExecuteAsync( + public static IList Execute( TaskLoggingHelper log, string targetDirectory, string outputReportDirectory, @@ -40,33 +38,30 @@ public static async Task> ExecuteAsync( return (pattern: p, matcher: m); }).ToList(); - var parallelOptions = new ParallelOptions + foreach (var file in Directory.EnumerateFiles(targetDirectory, "*", new EnumerationOptions() { AttributesToSkip = FileAttributes.ReparsePoint, RecurseSubdirectories = true })) { - MaxDegreeOfParallelism = Environment.ProcessorCount * 2 - }; - - await Parallel.ForEachAsync(Directory.EnumerateFiles(targetDirectory, "*", SearchOption.AllDirectories), parallelOptions, async (file, _) => - { - bool matched = false; - - foreach (var (pattern, matcher) in patternMatchers) + // This code is meant for finding binaries in source code repositories. + // Most files will be non-binary. We want to avoid checking each of those against the patternMatchers [O(N*M)], so we first check if the file is binary. + if (IsBinary(file)) { - if (matcher.Match(targetDirectory, file).HasMatches) + bool matched = false; + + foreach (var (pattern, matcher) in patternMatchers) { - usedPatterns.Add(pattern); - matched = true; - break; + if (matcher.Match(targetDirectory, file).HasMatches) + { + usedPatterns.Add(pattern); + matched = true; + break; + } } - } - if (!matched) - { - if (await IsBinaryAsync(log, file)) + if (!matched) { newBinaries.Add(file.Substring(targetDirectory.Length + 1)); } } - }); + } var unusedPatterns = new HashSet(patterns.Except(usedPatterns)); UpdateAllowedBinariesFile(log, allowedBinariesFile, outputReportDirectory, unusedPatterns); @@ -76,69 +71,23 @@ await Parallel.ForEachAsync(Directory.EnumerateFiles(targetDirectory, "*", Searc return newBinaries.ToList(); } - private static async Task IsBinaryAsync(TaskLoggingHelper log, string filePath) + private static bool IsBinary(string filePath) { // Using the GNU diff heuristic to determine if a file is binary or not. // For more details, refer to the GNU diff manual: // https://www.gnu.org/software/diffutils/manual/html_node/Binary.html using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - using (BinaryReader br = new BinaryReader(fs)) { - byte[] buffer = new byte[ChunkSize]; - int bytesRead = br.Read(buffer, 0, ChunkSize); - for (int i = 0; i < bytesRead; i++) - { - if (buffer[i] == 0) - { - // Need to check that the file is not UTF-16 encoded - // because heuristic can return false positives - return await IsNotUTF16Async(log, filePath); - } - } - } - return false; - } + Span buffer = stackalloc byte[4096]; + int bytesRead = fs.Read(buffer); + buffer = buffer[..bytesRead]; - private static async Task IsNotUTF16Async(TaskLoggingHelper log, string file) - { - if (Environment.OSVersion.Platform == PlatformID.Unix) - { - string output = await ExecuteProcessAsync(log, "file", $"\"{file}\""); - output = output.Split(":")[1].Trim(); - - if (output.Contains(Utf16Marker)) - { - return false; - } - } - return true; - } - - private static async Task ExecuteProcessAsync(TaskLoggingHelper log, string executable, string arguments) - { - ProcessStartInfo psi = new() - { - FileName = executable, - Arguments = arguments, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - var proc = Process.Start(psi)!; - - string output = await proc.StandardOutput.ReadToEndAsync(); - string error = await proc.StandardError.ReadToEndAsync(); + bool hasZeroByte = buffer.IndexOf((byte)0) != -1; + bool hasUTF16ByteOrderMarker = buffer.StartsWith((ReadOnlySpan)[0xFE, 0xFF]) || buffer.StartsWith((ReadOnlySpan)[0xFF, 0xFE]); - await proc.WaitForExitAsync(); - - if (!string.IsNullOrEmpty(error)) - { - log.LogError(error); + return hasZeroByte && !hasUTF16ByteOrderMarker; } - - return output; } private static IEnumerable ParseAllowedBinariesFile(TaskLoggingHelper log, string file, List? knownFiles = null) diff --git a/eng/tools/BuildComparer/BuildComparer.cs b/eng/tools/BuildComparer/BuildComparer.cs index 6dad7fddd5cc..efc0e0ad2e12 100644 --- a/eng/tools/BuildComparer/BuildComparer.cs +++ b/eng/tools/BuildComparer/BuildComparer.cs @@ -3,54 +3,60 @@ using Microsoft.Arcade.Common; using Microsoft.DotNet.Build.Manifest; -using NuGet.Packaging; using System.Collections.Concurrent; using System.Collections.Immutable; -using System.CommandLine; -using System.Formats.Tar; -using System.IO.Compression; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; using System.Xml.Linq; public abstract class BuildComparer { + private readonly HashSet _includedRepositories = + [ + "arcade", + "deployment-tools", + "diagnostics", + "fsharp", + "msbuild", + "nuget-client", + "razor", + "roslyn", + "vstest" + ]; + /// /// Type of asset being processed in the build comparison tool. /// protected AssetType? _assetType; - + /// /// Base path for VMR build assets. /// protected string _vmrBuildAssetBasePath; - + /// /// Base path for Microsoft build assets. /// protected string _baseBuildAssetBasePath; - + /// /// Path where the comparison report for issues will be saved. /// private string _issuesReportPath; - + /// /// Path where the comparison report for no issues will be saved. /// private string _noIssuesReportPath; - + /// /// Semaphore used to control parallel processing. /// protected SemaphoreSlim _throttle; - + /// /// Report containing the results of the comparison. /// protected ComparisonReport _comparisonReport = new ComparisonReport(); - + /// /// List of all asset mappings between base and VMR builds. /// @@ -168,33 +174,19 @@ protected void GenerateAssetMappings() // Walk the top-level directories of the asset base path, and find the MergedManifest under each // one. The MergedManifest.xml contains the list of outputs produced by the repo. - - foreach (var baseDirectory in Directory.GetDirectories(_baseBuildAssetBasePath, "*", SearchOption.TopDirectoryOnly)) + foreach (string includedRepository in _includedRepositories) { // Find the merged manifest underneath this directory - // (e.g. /arcade/nonshipping/>/MergedManifest.xml) + // (e.g. /arcade/nonshipping//MergedManifest.xml) + string repoBaseAssetsDirectory = Path.Combine(_baseBuildAssetBasePath, includedRepository); - string repoMergedManifestPath = Directory.GetFiles(baseDirectory, + string repoMergedManifestPath = Directory.GetFiles(repoBaseAssetsDirectory, "MergedManifest.xml", SearchOption.AllDirectories) - .FirstOrDefault(); - - if (repoMergedManifestPath == null) - { - // These repos don't publish assets, so we skip them - if (baseDirectory.EndsWith("scenario-tests") - || baseDirectory.EndsWith("source-build-reference-packages") - || baseDirectory.EndsWith("runtime") - || baseDirectory.EndsWith("sdk")) - { - continue; - } - - throw new FileNotFoundException($"MergedManifest.xml not found in {baseDirectory}"); - } + .FirstOrDefault() ?? throw new FileNotFoundException($"MergedManifest.xml not found in {repoBaseAssetsDirectory}"); _assetMappings.AddRange(MapFilesForRepoManifest( vmrManifests, - baseDirectory, + repoBaseAssetsDirectory, _vmrBuildAssetBasePath, repoMergedManifestPath)); } diff --git a/eng/tools/BuildComparer/BuildComparer.csproj b/eng/tools/BuildComparer/BuildComparer.csproj index 0dbe36eb7ee6..8874541b8068 100644 --- a/eng/tools/BuildComparer/BuildComparer.csproj +++ b/eng/tools/BuildComparer/BuildComparer.csproj @@ -1,7 +1,8 @@  + - Exe $(NetCurrent) + Exe enable disable @@ -10,4 +11,5 @@ + diff --git a/eng/tools/ChangeValidation/ChangeValidation.csproj b/eng/tools/ChangeValidation/ChangeValidation.csproj new file mode 100644 index 000000000000..f9975de051f7 --- /dev/null +++ b/eng/tools/ChangeValidation/ChangeValidation.csproj @@ -0,0 +1,19 @@ + + + + Exe + $(NetCurrent) + enable + enable + + $(NoWarn);CS8002 + + + + + + + + + + diff --git a/eng/tools/ChangeValidation/ExclusionFileValidation.cs b/eng/tools/ChangeValidation/ExclusionFileValidation.cs new file mode 100644 index 000000000000..390a2cd9026e --- /dev/null +++ b/eng/tools/ChangeValidation/ExclusionFileValidation.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.FileSystemGlobbing; +using Microsoft.Extensions.FileSystemGlobbing.Abstractions; +using Microsoft.DotNet.DarcLib; +using Microsoft.DotNet.DarcLib.VirtualMonoRepo; +using Microsoft.DotNet.DarcLib.Models.VirtualMonoRepo; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Microsoft.DotNet.DarcLib.Helpers; +using static ChangeValidation.Validation; + +namespace ChangeValidation; + +internal class ExclusionFileValidation : IValidationStep +{ + private static readonly int maxDisplayedFiles = 20; + private readonly IVmrDependencyTracker _dependencyTracker; + private readonly IProcessManager _processManager; + private readonly IVmrInfo _vmrInfo; + private readonly ILocalGitRepoFactory _localGitRepoFactory; + + public ExclusionFileValidation( + IVmrDependencyTracker dependencyTracker, + IProcessManager processManager, + ILocalGitRepoFactory localGitRepoFactory, + IVmrInfo vmrInfo) + { + _dependencyTracker = dependencyTracker; + _processManager = processManager; + _localGitRepoFactory = localGitRepoFactory; + _vmrInfo = vmrInfo; + } + + public string DisplayName => "Exclusion File Validation"; + + public async Task Validate(PrInfo prInfo) + { + ILocalGitRepo vmr = _localGitRepoFactory.Create(_vmrInfo.VmrPath); + + var newExclusionRules = await GetExclusionPatterns(); // We are already checked to the PR Head commit, just parse exclusions from file + var originalExclusionRules = await GetExclusionPatternsFromBranch(vmr, prInfo.TargetBranch); + + var newExcludedFiles = FindMatchingFiles(newExclusionRules); + var originalExcludedFiles = FindMatchingFiles(originalExclusionRules); + + var excludedFilesInPr = prInfo.ChangedFiles + .Where(file => newExcludedFiles.Contains(NormalizePath(file))) + .ToList(); + + var filesMatchingNewExclusionRules = newExcludedFiles + .Where(file => !originalExcludedFiles.Contains(NormalizePath(file)) && !prInfo.ChangedFiles.Contains(NormalizePath(file))) + .ToList(); + + foreach (var file in excludedFilesInPr.Take(maxDisplayedFiles).ToList()) + { + LogError($"This PR modifies the file `{file}`, which is part of the excluded files" + + $" defined in {VmrInfo.DefaultRelativeSourceMappingsPath}. If these changes are" + + $" necessary, please contact @dotnet/product-construction."); + } + + if (excludedFilesInPr.Count > maxDisplayedFiles) + { + LogError($"... {excludedFilesInPr.Count - maxDisplayedFiles} more excluded files detected" + + $" in the PR (only showing the first {maxDisplayedFiles} files)."); + } + + foreach (var file in filesMatchingNewExclusionRules.Take(maxDisplayedFiles).ToList()) + { + LogError($"The new exclusion rules defined in {VmrInfo.DefaultRelativeSourceMappingsPath} " + + $"include the VMR file `{file}`. If this file is not needed in the VMR, consider " + + $"deleting it as part of the PR."); + } + + if (filesMatchingNewExclusionRules.Count > maxDisplayedFiles) + { + LogError($"... {filesMatchingNewExclusionRules.Count - maxDisplayedFiles} more VMR files " + + $"match the new exclusion rules (only showing the first {maxDisplayedFiles} files). " + + "Please review the modifications made to exclusion rules"); + } + + return !excludedFilesInPr.Any() && !filesMatchingNewExclusionRules.Any(); + } + + private async Task> GetExclusionPatternsFromBranch(ILocalGitRepo vmr, string branchName) + { + string originalRef = await vmr.GetCheckedOutBranchAsync(); + try + { + LogInfo($"Checking out branch {branchName} to get exclusion patterns."); + await vmr.CheckoutAsync(branchName); + return await GetExclusionPatterns(); + } + finally + { + await vmr.CheckoutAsync(originalRef); + } + } + + private async Task> GetExclusionPatterns() + { + await _dependencyTracker.RefreshMetadataAsync(); + + var sourceMappings = _dependencyTracker.Mappings; + + var exclusionPatterns = sourceMappings.SelectMany(mapping => GetVmrGlobFromSourceMapping(mapping)) + .Distinct() + .ToList(); + + LogInfo("Successfully parsed exclusion patterns..."); + LogInfo(string.Join(Environment.NewLine, exclusionPatterns)); + + return exclusionPatterns; + } + + private List GetVmrGlobFromSourceMapping(SourceMapping mapping) + { + return mapping.Exclude.Select(exclusion => "src/" + mapping.Name + "/" + NormalizePath(exclusion)).ToList(); + } + + private HashSet FindMatchingFiles(List globPatterns) + { + var matcher = new Matcher(); + + foreach (var pattern in globPatterns) + { + matcher.AddInclude(pattern); + } + + var directoryInfo = new DirectoryInfo(_vmrInfo.VmrPath); + var directoryWrapper = new DirectoryInfoWrapper(directoryInfo); + var result = matcher.Execute(directoryWrapper); + + var res = result.Files + .Select(file => file.Path) + .ToHashSet(); + + return res; + } + + internal static string NormalizePath(string path) + { + return path.Replace('\\', '/'); + } +} diff --git a/eng/tools/ChangeValidation/IValidationStep.cs b/eng/tools/ChangeValidation/IValidationStep.cs new file mode 100644 index 000000000000..6a09f03d3f83 --- /dev/null +++ b/eng/tools/ChangeValidation/IValidationStep.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ChangeValidation; + +internal interface IValidationStep +{ + /// + /// Executes the validation step with the provided file names. + /// + /// List of file names to validate. + /// boolean determining the success of the validation step + Task Validate(PrInfo prInfo); + + /// + /// Gets the display name of the validation step. + /// + string DisplayName { get; } +} diff --git a/eng/tools/ChangeValidation/PrInfo.cs b/eng/tools/ChangeValidation/PrInfo.cs new file mode 100644 index 000000000000..c8407edfe55c --- /dev/null +++ b/eng/tools/ChangeValidation/PrInfo.cs @@ -0,0 +1,12 @@ +// 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.Collections.Immutable; +using System.Linq; + +namespace ChangeValidation; + +internal record PrInfo( + string TargetBranch, + ImmutableList ChangedFiles); diff --git a/eng/tools/ChangeValidation/SubmoduleValidation.cs b/eng/tools/ChangeValidation/SubmoduleValidation.cs new file mode 100644 index 000000000000..5431093ac9d7 --- /dev/null +++ b/eng/tools/ChangeValidation/SubmoduleValidation.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Text; +using Microsoft.Extensions.FileSystemGlobbing; +using Microsoft.Extensions.FileSystemGlobbing.Abstractions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.DotNet.DarcLib; +using Microsoft.DotNet.DarcLib.Helpers; +using Microsoft.DotNet.DarcLib.VirtualMonoRepo; +using Microsoft.DotNet.DarcLib.Models.VirtualMonoRepo; +using static ChangeValidation.Validation; + +namespace ChangeValidation; + +internal class SubmoduleValidation : IValidationStep +{ + private static readonly int maxDisplayedFiles = 20; + private readonly IVmrDependencyTracker _dependencyTracker; + private readonly IProcessManager _processManager; + + public string DisplayName => "Submodule Validation"; + + private static string ConvertSubmodulePathToVmrGlob(string submodulePath) => + "src/" + submodulePath.Replace('\\', '/').TrimStart('/').TrimEnd('/') + "/**"; + + public SubmoduleValidation( + IVmrDependencyTracker vmrDependencyTracker, + IProcessManager processManager) + { + _dependencyTracker = vmrDependencyTracker; + _processManager = processManager; + } + + public async Task Validate(PrInfo prInfo) + { + var repoRoot = _processManager.FindGitRoot(AppContext.BaseDirectory); + var sourceManifest = await GetSourceManifestFromBranch("HEAD", repoRoot); + + LogInfo("Reading submodule paths and converting to glob patterns..."); + + var submoduleGlobPatterns = sourceManifest.Submodules.Select(submodule => submodule.Path) + .Select(pattern => ConvertSubmodulePathToVmrGlob(pattern)) + .ToList(); + + LogInfo(string.Join(Environment.NewLine, submoduleGlobPatterns)); + + var matcher = new Matcher(); + foreach (var pattern in submoduleGlobPatterns) + { + matcher.AddInclude(pattern); + } + + var directory = new InMemoryDirectoryInfo("/", prInfo.ChangedFiles); + + var result = matcher.Execute(directory); + + var modifiedSubmoduleFiles = result.Files.Select(f => f.Path).ToList(); + + foreach(var file in modifiedSubmoduleFiles.Take(maxDisplayedFiles).ToList()) + { + LogError($"This PR modifies the submodule file `{file}`, which is not allowed. Submodule files are managed by the repository maintainers and should not be modified directly."); + } + + if (modifiedSubmoduleFiles.Count > maxDisplayedFiles) + { + LogError($"... {modifiedSubmoduleFiles.Count - maxDisplayedFiles} more submodule files detected in the PR. Only showing the first {maxDisplayedFiles} files."); + } + + return !modifiedSubmoduleFiles.Any(); + } + + private async Task GetSourceManifestFromBranch(string branchName, string repoRoot) + { + var sourceManifestContent = await _processManager.ExecuteGit(repoRoot, ["show", $"{branchName}:{VmrInfo.DefaultRelativeSourceManifestPath}"]); + var sourceManifest = SourceManifest.FromJson(sourceManifestContent.StandardOutput); + return sourceManifest; + } +} diff --git a/eng/tools/ChangeValidation/ToolingFilesValidation.cs b/eng/tools/ChangeValidation/ToolingFilesValidation.cs new file mode 100644 index 000000000000..6a3c250a8265 --- /dev/null +++ b/eng/tools/ChangeValidation/ToolingFilesValidation.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Text.RegularExpressions; +using static ChangeValidation.Validation; + +namespace ChangeValidation; + +internal class ToolingFilesValidation : IValidationStep +{ + public string DisplayName => "Tooling Files Validation"; + + private static readonly string[] ToolingFilePatterns = new[] + { + "^eng/Version\\.Details\\.xml$", + "^src/source-manifest\\.json$", + "^eng/Version\\.props$", + "^eng/Version\\.Details\\.props$", + "^global\\.json$", + "^eng/common/.*" + }; + + private static readonly List ToolingFilesRegexes = ToolingFilePatterns + .Select(pattern => new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled)) + .ToList(); + + public Task Validate(PrInfo prInfo) + { + var syncToolingChanges = prInfo.ChangedFiles + .Where(f => ToolingFilesRegexes.Any(regex => regex.IsMatch(f))) + .ToList(); + + if (syncToolingChanges.Any()) + { + foreach (var file in syncToolingChanges) + { + LogError($"The file `{file}` is a tooling file reserved for automated processes. Modifying this file is not permitted."); + } + return Task.FromResult(false); + } + else + { + return Task.FromResult(true); + } + } +} diff --git a/eng/tools/ChangeValidation/Validation.cs b/eng/tools/ChangeValidation/Validation.cs new file mode 100644 index 000000000000..1b5a01551202 --- /dev/null +++ b/eng/tools/ChangeValidation/Validation.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using Maestro.Common; +using Microsoft.DotNet.DarcLib.Helpers; +using Microsoft.DotNet.DarcLib.VirtualMonoRepo; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Abstractions; + + +namespace ChangeValidation; + +internal static class Validation +{ + internal static void LogInfo(string message) => Console.WriteLine(message); + internal static void LogWarning(string message) => Console.WriteLine($"##vso[task.logissue type=warning] {message}"); + internal static void LogError(string message) => Console.WriteLine($"##vso[task.logissue type=error] {message}"); + + internal static async Task Main(string[] args) + { + var pm = new ProcessManager(NullLogger.Instance, "git"); + + var repoRoot = pm.FindGitRoot(AppContext.BaseDirectory); + + var serviceProvider = RegisterServices(repoRoot); + + List validationSteps = CreateValidationSteps(serviceProvider); + + PrInfo prInfo = await SetupPrInfo(pm, repoRoot); + + return await RunValidationSteps(validationSteps, prInfo) ? 0 : 1; + } + + private static async Task RunValidationSteps(List validationSteps, PrInfo prInfo) + { + int stepCounter = 0; + int failedSteps = 0; + + foreach (var validationStep in validationSteps) + { + stepCounter++; + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine($"{stepCounter}. Starting {validationStep.DisplayName}"); + bool validationSuccess = false; + try + { + validationSuccess = await validationStep.Validate(prInfo); + } + catch (Exception) + { + LogError($"{validationStep.DisplayName} was interrupted with an unexpected error."); + } + if (validationSuccess) + { + LogInfo($"{validationStep.DisplayName} succeeded."); + } + else + { + failedSteps++; + Console.WriteLine($"{validationStep.DisplayName} failed."); + } + } + if (failedSteps > 0) + { + Console.WriteLine(); + Console.WriteLine(); + LogError($"{failedSteps} out of {validationSteps.Count} validation steps have failed."); + return false; + } + else + { + LogInfo("All validation steps succeeded!"); + return true; + } + } + + private static async Task SetupPrInfo(IProcessManager pm, string repoPath) + { + string? targetBranch = $"origin/{Environment.GetEnvironmentVariable("SYSTEM_PULLREQUEST_TARGETBRANCH")}"; + string? prNumber = Environment.GetEnvironmentVariable("SYSTEM_PULLREQUEST_PULLREQUESTNUMBER"); + + if (string.IsNullOrEmpty(targetBranch)) + { + throw new ArgumentException("Cannot determine PR target branch."); + } + + // Create a git reference to the original branching-off point of the PR branch from the target branch + await pm.ExecuteGit(repoPath, "fetch", "origin", $"refs/pull/{prNumber}/merge:pr-merge"); + + var diffOutput = (await pm.ExecuteGit(repoPath, ["diff", "--name-only", targetBranch, "pr-merge"])).StandardOutput.Trim(); + + var changedFiles = diffOutput + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries) + .Select(f => f.Replace('\\', '/').Trim()) + .ToImmutableList(); + + Console.WriteLine($"Found modifications to {changedFiles.Count} file(s) in PR head branch"); + + return new PrInfo(targetBranch, changedFiles); + } + + private static ServiceProvider RegisterServices(string repoRoot) + { + return new ServiceCollection() + .AddCodeflow("tmp", vmrPath: repoRoot) + .AddSingleton() + .BuildServiceProvider(); + } + + private static List CreateValidationSteps(IServiceProvider serviceProvider) + { + return [ + ActivatorUtilities.CreateInstance(serviceProvider), + ActivatorUtilities.CreateInstance(serviceProvider), + ActivatorUtilities.CreateInstance(serviceProvider) + ]; + } + + private class NullRemoteTokenProvider : IRemoteTokenProvider + { + public string? GetTokenForRepository(string repoUri) => null; + public Task GetTokenForRepositoryAsync(string repoUri) => Task.FromResult(null); + } +} diff --git a/eng/tools/CreateBaselineUpdatePR/PRCreator.cs b/eng/tools/CreateBaselineUpdatePR/PRCreator.cs index 1189d8084868..83b29c8bd0ee 100644 --- a/eng/tools/CreateBaselineUpdatePR/PRCreator.cs +++ b/eng/tools/CreateBaselineUpdatePR/PRCreator.cs @@ -17,6 +17,7 @@ public class PRCreator private const string BuildLink = "https://dev.azure.com/dnceng/internal/_build/results?buildId="; private const string DefaultLicenseBaselineContent = "{\n \"files\": []\n}"; private const string TreeMode = "040000"; + private const string UpdatedFilePrefix = "updated"; private const int MaxRetries = 10; public PRCreator(string repo, string gitHubToken) { @@ -105,10 +106,11 @@ await Parallel.ForEachAsync(treeResponse.Tree, async (item, cancellationToken) = } // Return a dictionary using the filename without the - // "Updated" prefix and anything after the first '.' as the key + // "updated" prefix (case-insensitive) and anything after the first '.' as the key private Dictionary> GetUpdatedFiles(string updatedFilesDirectory) => Directory - .GetFiles(updatedFilesDirectory, "Updated*", SearchOption.AllDirectories) + .GetFiles(updatedFilesDirectory, "*", SearchOption.AllDirectories) + .Where(file => Path.GetFileName(file).StartsWith(UpdatedFilePrefix, StringComparison.OrdinalIgnoreCase)) .GroupBy(updatedTestsFile => ParseUpdatedFileName(updatedTestsFile).Split('.')[0]) .ToDictionary( group => group.Key, @@ -255,7 +257,15 @@ private async Task CreateBlobAsync(string content) return await ApiRequestWithRetries(() => _client.Git.Blob.Create(_repoOwner, _repoName, blob)); } - private string ParseUpdatedFileName(string updatedFile) => updatedFile.Split("Updated")[1]; + private string ParseUpdatedFileName(string updatedFile) + { + string fileName = Path.GetFileName(updatedFile); + if (fileName.StartsWith(UpdatedFilePrefix, StringComparison.OrdinalIgnoreCase)) + { + return fileName.Substring(UpdatedFilePrefix.Length); + } + throw new ArgumentException($"File name '{fileName}' does not start with '{UpdatedFilePrefix}' prefix.", nameof(updatedFile)); + } private async Task CreateTreeFromItemsAsync(List items, string path = "") { diff --git a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs index 67fb52b29221..aa66622dc613 100644 --- a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs +++ b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs @@ -15,12 +15,10 @@ internal class CatalogFileEntry const string ElementName = "File"; internal string Path { get; set; } - internal byte[] OriginalHash { get; set; } internal byte[] PoisonedHash { get; set; } public XElement ToXml() => new XElement(ElementName, new XAttribute(nameof(Path), Path), - new XAttribute(nameof(OriginalHash), OriginalHash.ToHexString()), PoisonedHash == null ? null : new XAttribute(nameof(PoisonedHash), PoisonedHash.ToHexString()) ); } diff --git a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs index 99e334a0012b..654762f2eb8e 100644 --- a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs +++ b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs @@ -18,7 +18,6 @@ internal class CatalogPackageEntry internal string Path { get; set; } internal string Id { get; set; } internal string Version { get; set; } - internal byte[] OriginalHash { get; set; } internal byte[] PoisonedHash { get; set; } internal List Files { get; } @@ -31,7 +30,6 @@ public CatalogPackageEntry() new XAttribute(nameof(Path), Path), new XAttribute(nameof(Id), Id), new XAttribute(nameof(Version), Version), - new XAttribute(nameof(OriginalHash), OriginalHash.ToHexString()), PoisonedHash == null ? null : new XAttribute(nameof(PoisonedHash), PoisonedHash.ToHexString()), Files.Select(f => f.ToXml()) ); diff --git a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs index 61e21fec9125..cbf852f1b31b 100644 --- a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs +++ b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs @@ -47,16 +47,16 @@ public class CheckForPoison : Task public string PoisonReportOutputFilePath { get; set; } /// - /// The path of a previously-generated file hash catalog, if + /// The paths of previously-generated file hash catalogs, if /// hash checked is desired. If not, only assembly attributes - /// and package marker file checked will be done. + /// and package marker files checks will be done. /// - public string HashCatalogFilePath { get; set; } + public ITaskItem[] HashCatalogFilePaths { get; set; } /// - /// The marker file name to check for in poisoned nupkg files. + /// The marker file names to check for in poisoned nupkg files. /// - public string MarkerFileName { get; set; } + public ITaskItem[] MarkerFileNames { get; set; } /// /// If true, fails the build if any poisoned files are found. @@ -86,66 +86,6 @@ public class CheckForPoison : Task ".tar.gz", }; - private static readonly string[] FileNamesToSkip = - { - "_._", - "-.-", - ".bowerrc", - ".gitignore", - ".gitkeep", - ".rels", - "LICENSE", - "prefercliruntime", - "RunCsc", - "RunVbc", - }; - - private static readonly string[] FileExtensionsToSkip = - { - ".config", - ".cs", - ".cshtml", - ".csproj", - ".css", - ".db", - ".editorconfig", - ".eot", - ".fs", - ".fsproj", - ".h", - ".html", - ".ico", - ".js", - ".json", - ".map", - ".md", - ".nuspec", - ".otf", - ".png", - ".props", - ".proto", - ".proj", - ".psmdcp", - ".pubxml", - ".razor", - ".rtf", - ".scss", - ".sln", - ".svg", - ".targets", - ".transform", - ".ts", - ".ttf", - ".txt", - ".vb", - ".vbproj", - ".win32manifest", - ".woff", - ".woff2", - ".xaml", - ".xml", - }; - private const string PoisonMarker = "POISONED"; private const string SbrpAttributeType = "System.Reflection.AssemblyMetadataAttribute"; @@ -156,7 +96,7 @@ private record CandidateFileEntry(string ExtractedPath, string DisplayPath, bool public override bool Execute() { - IEnumerable poisons = GetPoisonedFiles(FilesToCheck, HashCatalogFilePath, MarkerFileName); + IEnumerable poisons = GetPoisonedFiles(FilesToCheck, HashCatalogFilePaths, MarkerFileNames); // if we should write out the poison report, do that if (!string.IsNullOrWhiteSpace(PoisonReportOutputFilePath)) @@ -181,12 +121,12 @@ public override bool Execute() /// Internal helper to allow other tasks to check for poisoned files. /// /// Initial queue of candidate files (will be cleared when done) - /// File path to the file hash catalog - /// Marker file name to check for in poisoned nupkgs + /// File paths to the file hash catalog + /// Marker file names to check for in poisoned nupkgs /// List of poisoned packages and files found and reasons for each - internal IEnumerable GetPoisonedFiles(IEnumerable initialCandidates, string catalogedPackagesFilePath, string markerFileName) + internal IEnumerable GetPoisonedFiles(IEnumerable initialCandidates, IEnumerable catalogedPackagesFilePaths, IEnumerable markerFileNames) { - IEnumerable catalogedPackages = ReadCatalog(catalogedPackagesFilePath); + IEnumerable catalogedPackages = catalogedPackagesFilePaths.SelectMany(file => ReadCatalog(file.ItemSpec)); var poisons = new List(); var candidateQueue = new Queue(initialCandidates.Select(candidate => new CandidateFileEntry(candidate.ItemSpec, @@ -211,7 +151,7 @@ internal IEnumerable GetPoisonedFiles(IEnumerable Log.LogMessage($"Zip or NuPkg file to check: {candidate.ExtractedPath}"); var tempCheckingDir = Path.Combine(tempDir.FullName, Path.GetFileNameWithoutExtension(candidate.ExtractedPath)); - PoisonedFileEntry result = ExtractAndCheckZipFileOnly(catalogedPackages, candidate, markerFileName, tempCheckingDir, candidateQueue); + PoisonedFileEntry result = ExtractAndCheckZipFileOnly(catalogedPackages, candidate, markerFileNames, tempCheckingDir, candidateQueue); if (result != null) { poisons.Add(result); @@ -232,19 +172,12 @@ internal IEnumerable GetPoisonedFiles(IEnumerable return poisons; } - private static PoisonedFileEntry CheckSingleFile(IEnumerable catalogedPackages, CandidateFileEntry candidate) + private PoisonedFileEntry CheckSingleFile(IEnumerable catalogedPackages, CandidateFileEntry candidate) { // skip some common files that get copied verbatim from nupkgs - LICENSE, _._, etc as well as // file types that we never care about - text files, .gitconfig, etc. var fileToCheck = candidate.ExtractedPath; - if (FileNamesToSkip.Any(f => Path.GetFileName(fileToCheck).ToLowerInvariant() == f.ToLowerInvariant()) || - FileExtensionsToSkip.Any(e => Path.GetExtension(fileToCheck).ToLowerInvariant() == e.ToLowerInvariant()) || - (new FileInfo(fileToCheck).Length == 0)) - { - return null; - } - var poisonEntry = new PoisonedFileEntry(); poisonEntry.Path = candidate.DisplayPath; @@ -264,15 +197,13 @@ private static PoisonedFileEntry CheckSingleFile(IEnumerable f.OriginalHash.SequenceEqual(poisonEntry.Hash) || (f.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false))) + foreach (var matchingCatalogedFile in p.Files.Where(f => f.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)) { poisonEntry.Type |= PoisonType.Hash; var match = new PoisonMatch { File = matchingCatalogedFile.Path, - Package = p.Path, + Package = Utility.MakeRelativePath(p.Path, ProjectDirPath), PackageId = p.Id, PackageVersion = p.Version, }; @@ -373,7 +304,7 @@ private static bool IsAttributeSbrp(MetadataReader reader, CustomAttribute attr) return false; } - private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable catalogedPackages, CandidateFileEntry candidate, string markerFileName, string tempDir, Queue futureFilesToCheck) + private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable catalogedPackages, CandidateFileEntry candidate, IEnumerable markerFileNames, string tempDir, Queue futureFilesToCheck) { var poisonEntry = new PoisonedFileEntry(); var zipToCheck = candidate.ExtractedPath; @@ -385,10 +316,8 @@ private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable c.OriginalHash.SequenceEqual(poisonEntry.Hash) || (c.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false))) + // first check for a matching hash match + foreach (var matchingCatalogedPackage in catalogedPackages.Where(c => c.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)) { poisonEntry.Type |= PoisonType.Hash; var match = new PoisonMatch @@ -422,7 +351,7 @@ private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable HasMarkerFile(tempDir, markerFileName.ItemSpec))) { poisonEntry.Type |= PoisonType.NupkgFile; } @@ -458,7 +387,6 @@ private static IEnumerable ReadCatalog(string hashCatalogFi { Id = p.Attributes["Id"].Value, Version = p.Attributes["Version"].Value, - OriginalHash = p.Attributes["OriginalHash"].Value.ToBytes(), PoisonedHash = p.Attributes["PoisonedHash"]?.Value?.ToBytes(), Path = p.Attributes["Path"].Value, }; @@ -467,7 +395,6 @@ private static IEnumerable ReadCatalog(string hashCatalogFi { var fEntry = new CatalogFileEntry { - OriginalHash = f.Attributes["OriginalHash"].Value.ToBytes(), PoisonedHash = f.Attributes["PoisonedHash"]?.Value?.ToBytes(), Path = f.Attributes["Path"].Value, }; @@ -476,5 +403,8 @@ private static IEnumerable ReadCatalog(string hashCatalogFi } return packages; } + + private static bool HasMarkerFile(string nugetPackageDir, string markerFileName) + => !string.IsNullOrWhiteSpace(markerFileName) && File.Exists(Path.Combine(nugetPackageDir, markerFileName)); } } diff --git a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs index 598bbd43661b..874269256fcd 100644 --- a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs +++ b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs @@ -77,10 +77,6 @@ public override bool Execute() var packageEntry = new CatalogPackageEntry(); packageEntries.Add(packageEntry); packageEntry.Path = p.ItemSpec; - using (var stream = File.OpenRead(p.ItemSpec)) - { - packageEntry.OriginalHash = sha.ComputeHash(stream); - } var packageIdentity = ReadNuGetPackageInfos.ReadIdentity(p.ItemSpec); packageEntry.Id = packageIdentity.Id; packageEntry.Version = packageIdentity.Version.OriginalVersion; @@ -96,29 +92,16 @@ public override bool Execute() continue; } - var catalogFileEntry = new CatalogFileEntry(); - packageEntry.Files.Add(catalogFileEntry); - catalogFileEntry.Path = Utility.MakeRelativePath(file, packageTempPath); - - // There seem to be some weird issues with using a file stream both for hashing and - // assembly loading, even closing it in between. Use a MemoryStream to avoid issues. - var memStream = new MemoryStream(); - using (var stream = File.OpenRead(file)) - { - stream.CopyTo(memStream); - } - - // First get the original hash of the file - memStream.Seek(0, SeekOrigin.Begin); - catalogFileEntry.OriginalHash = sha.ComputeHash(memStream); - // Add poison marker to assemblies try { AssemblyName asm = AssemblyName.GetAssemblyName(file); Poison(file); - // then get the hash of the now-poisoned file + var catalogFileEntry = new CatalogFileEntry(); + packageEntry.Files.Add(catalogFileEntry); + catalogFileEntry.Path = Utility.MakeRelativePath(file, packageTempPath); + using (var stream = File.OpenRead(file)) { catalogFileEntry.PoisonedHash = sha.ComputeHash(stream); diff --git a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs index 374105e3d1a7..09b06cab0561 100644 --- a/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs +++ b/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs @@ -31,7 +31,11 @@ internal PoisonedFileEntry() new XAttribute(nameof(Path), Path), new XElement(nameof(Hash), Hash.ToHexString()), new XElement(nameof(Type), Type.ToString()), - Matches.Select(m => m.ToXml()) + // Order matches to ensure the output is deterministic across runs + Matches + .OrderBy(m => m.Package) + .ThenBy(m => m.File) + .Select(m => m.ToXml()) ); } } diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Config.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Config.cs new file mode 100644 index 000000000000..1c83da016fb2 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Config.cs @@ -0,0 +1,24 @@ +// 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.Reflection; +using System.Runtime.Versioning; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks; + +internal static class Config +{ + private static readonly string _taskAssemblyLocation = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? + throw new InvalidOperationException("Unable to determine task assembly location."); + + private static readonly string _staticDirectory = Path.Combine(_taskAssemblyLocation, "static"); + + private const string EmptyProjectFileName = "empty.proj"; + public const string SigningPropsFileName = "Signing.props"; + + public static string EmptyProjectPath => Path.Combine(_staticDirectory, EmptyProjectFileName); + public static string SigningPropsPath => Path.Combine(_staticDirectory, SigningPropsFileName); +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSourceArtifact.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSourceArtifact.cs new file mode 100644 index 000000000000..ad07a66b565d --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSourceArtifact.cs @@ -0,0 +1,92 @@ +// 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.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.DotNet.UnifiedBuild.Tasks.Services; +using BuildTask = Microsoft.Build.Utilities.Task; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks; + +public class CreateSourceArtifact : BuildTask +{ + /// + /// Commit SHA of the VMR source to download + /// + [Required] + public required string SourceCommit { get; init; } + + /// + /// Name of the artifact to generate + /// + [Required] + public required string ArtifactName { get; init; } + + /// + /// The stable sdk version + /// Should match the sdk tag associated with a release + /// Used for artifact prefixing + /// + [Required] + public required string SdkStableVersion { get; init; } + + /// + /// The build/non-stable sdk version + /// Used for artifact versioning + /// + [Required] + public required string SdkNonStableVersion { get; init; } + + /// + /// Path to the root of the repo + /// + [Required] + public required string RepoRoot { get; init; } + + /// + /// Path to the directory to place the source artifacts in + /// + [Required] + public required string OutputDirectory { get; init; } + + private const string GitHubRepoName = "dotnet"; + private const string GitHubTimezone = "America/Los_Angeles"; // GitHub uses this timezone for commit timestamps in zip metadata + private const int GitArchiveTimeout = 5 * 60 * 1000; // 5 minutes + private const string TarExtension = "tar.gz"; + + public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult(); + + private async Task ExecuteAsync() + { + var processService = new ProcessService(Log, GitArchiveTimeout); + + string artifactFilePath = Path.Combine(OutputDirectory, $"{ArtifactName}-{SdkNonStableVersion}.{TarExtension}"); + + Log.LogMessage(MessageImportance.High, $"Creating {TarExtension} source artifact at: {artifactFilePath}"); + + try + { + ProcessResult result = await processService.RunProcessAsync( + "git", + $"-c \"tar.tar.gz.command=gzip -cn\" archive --format={TarExtension} --output \"{artifactFilePath}\" --prefix \"{GitHubRepoName}-{SdkStableVersion}/\" {SourceCommit}", + environmentVariables: new List { new ProcessEnvironmentVariable("TZ", GitHubTimezone) }, + workingDirectory: RepoRoot + ); + + if (result.ExitCode != 0) + { + Log.LogError($"[{TarExtension}] Git exited with code {result.ExitCode}: {result.Error}"); + return false; + } + } + catch (Exception ex) + { + Log.LogError($"[{TarExtension}] Exception during artifact creation: {ex.Message}"); + return false; + } + return true; + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/GetDependencyNamesFromVersionDetails.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/GetDependencyNamesFromVersionDetails.cs new file mode 100644 index 000000000000..3f7d02bbc6b8 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/GetDependencyNamesFromVersionDetails.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks +{ + /// + /// Reads a Version.Details.xml file and returns the list of non-pinned dependency package names. + /// + public class GetDependencyNamesFromVersionDetails : Microsoft.Build.Utilities.Task + { + private const string PinnedAttributeName = "Pinned"; + private const string DependencyAttributeName = "Dependency"; + private const string NameAttributeName = "Name"; + + /// + /// Path to the Version.Details.xml file. + /// + [Required] + public string VersionDetailsPath { get; set; } + + /// + /// Output: Array of dependency package names found in the Version.Details.xml file. + /// Excludes pinned dependencies. + /// + [Output] + public string[] DependencyNames { get; set; } + + public override bool Execute() + { + if (string.IsNullOrEmpty(VersionDetailsPath) || !File.Exists(VersionDetailsPath)) + { + Log.LogError($"The VersionDetailsPath must point to a valid Version.Details.xml file. " + + $"Provided file path '{VersionDetailsPath}' does not exist."); + return false; + } + + HashSet dependencies = VersionDetailsHelper.GetDependencies(VersionDetailsPath, Log); + + if (Log.HasLoggedErrors) + { + return false; + } + + DependencyNames = dependencies + .OrderBy(name => name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + return true; + } + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj index a974280c841e..746c6282d69b 100644 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Services/ProcessService.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Services/ProcessService.cs new file mode 100644 index 000000000000..6e2be154f290 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Services/ProcessService.cs @@ -0,0 +1,117 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Task = System.Threading.Tasks.Task; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks.Services; + +public record ProcessEnvironmentVariable(string Name, string Value); +public record ProcessResult(string Output, string Error, int ExitCode); + +public class ProcessService(TaskLoggingHelper log, int timeout = 10 * 1000) +{ + private TaskLoggingHelper Log { get; } = log; + private TimeSpan Timeout { get; } = TimeSpan.FromMilliseconds(timeout); + + public async Task RunProcessAsync( + string command, + string arguments, + List? environmentVariables = null, + string workingDirectory = "", + bool printOutput = false) + { + var processInfo = new ProcessStartInfo + { + FileName = command, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = workingDirectory + }; + + if (environmentVariables != null) + { + foreach (ProcessEnvironmentVariable env in environmentVariables) + { + processInfo.EnvironmentVariables[env.Name] = env.Value; + } + } + + using var process = new Process { StartInfo = processInfo, EnableRaisingEvents = true }; + + var outputBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + + var outputTcs = new TaskCompletionSource(); + var errorTcs = new TaskCompletionSource(); + + process.OutputDataReceived += (s, e) => + { + if (e.Data == null) + { + outputTcs.TrySetResult(); + return; + } + + outputBuilder.AppendLine(e.Data); + if (printOutput) + { + Log.LogMessage(MessageImportance.High, e.Data); + } + }; + + process.ErrorDataReceived += (s, e) => + { + if (e.Data == null) + { + errorTcs.TrySetResult(); + return; + } + + errorBuilder.AppendLine(e.Data); + if (printOutput) + { + Log.LogMessage(MessageImportance.High, e.Data); + } + }; + + if (!process.Start()) + { + throw new InvalidOperationException($"Failed to start process: {command} {arguments}"); + } + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + using var timeoutCts = new CancellationTokenSource(Timeout); + + var exitedTask = Task.Run(() => process.WaitForExit(), timeoutCts.Token); + + try + { + await exitedTask; + await Task.WhenAll(outputTcs.Task, errorTcs.Task); + } + catch (OperationCanceledException) + { + try { process.Kill(entireProcessTree: true); } catch { } + throw new TimeoutException($"Process timed out after {Timeout.TotalMilliseconds} ms"); + } + + return new ProcessResult( + Output: outputBuilder.ToString(), + Error: errorBuilder.ToString(), + ExitCode: process.ExitCode + ); + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SignSourceBuiltArtifacts.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SignSourceBuiltArtifacts.cs new file mode 100644 index 000000000000..ba21f1489870 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SignSourceBuiltArtifacts.cs @@ -0,0 +1,143 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.DotNet.UnifiedBuild.Tasks.Services; +using BuildTask = Microsoft.Build.Utilities.Task; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks; + +public class SignSourceBuiltArtifacts : BuildTask +{ + /// + /// List of source-built assets to sign + /// + [Required] + public required ITaskItem[] SignableSourceBuiltAssets { get; init; } + + /// + /// Path to the eng directory + /// + [Required] + public required string DotNetEngDirectory { get; init; } + + /// + /// Path to the logs directory + /// + [Required] + public required string LogsDirectory { get; init; } + + /// + /// Sign-specific properties to pass to the signing process + /// + public string SignProperties { get; init; } = string.Empty; + + /// + /// The official build ID + /// + public string OfficialBuildId { get; init; } = string.Empty; + + private string SigningPropsTargetPath => Path.Combine(DotNetEngDirectory, Config.SigningPropsFileName); + private const string SignBinlogFileName = "sign-source-built-artifacts.binlog"; + private const int SigningTimeout = 60 * 60 * 1000 * 2; // 2 hours + + public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult(); + + private async Task ExecuteAsync() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Log.LogError("Windows is not supported for signing."); + return false; + } + + try + { + PrepareForSigning(); + await RunSigningAsync(); + Log.LogMessage(MessageImportance.High, "Signing complete."); + } + catch (Exception ex) + { + Log.LogError($"Signing failed: {ex.Message}"); + } + finally + { + CleanupSigning(); + } + + return !Log.HasLoggedErrors; + } + + private void PrepareForSigning() + { + if (!Directory.Exists(LogsDirectory)) + { + Directory.CreateDirectory(LogsDirectory); + } + + // Place the Signing.props file into the repo's eng directory for + // Arcade's signing infra to find + File.Copy(Config.SigningPropsPath, SigningPropsTargetPath, overwrite: true); + } + + private void CleanupSigning() + { + if (File.Exists(SigningPropsTargetPath)) + { + File.Delete(SigningPropsTargetPath); + } + } + + /// + /// Runs the signing process + /// + private async Task RunSigningAsync() + { + (string command, string arguments) = GetSigningCommandAndArguments(); + Log.LogMessage(MessageImportance.High, $"Running signing command: {command} {arguments}"); + + var processService = new ProcessService(Log, timeout: SigningTimeout); + ProcessResult result = await processService.RunProcessAsync( + command, + arguments, + environmentVariables: new List { new ProcessEnvironmentVariable("OPENSSL_ENABLE_SHA1_SIGNATURES", "1") }, + printOutput: true); + + if (result.ExitCode != 0) + { + Log.LogError($"Signing failed with exit code {result.ExitCode}"); + } + } + + /// + /// Gets the command and arguments to run signing + /// + private (string command, string arguments) GetSigningCommandAndArguments() + { + string buildScript = Path.Combine(DotNetEngDirectory, "common", "build.sh"); + string binlog = Path.Combine(LogsDirectory, SignBinlogFileName); + string signableAssets = string.Join(';', SignableSourceBuiltAssets.Select(a => a.ItemSpec)); + + string arguments = + $"--ci " + + $"--configuration Release " + + $"--restore " + + $"--projects {Config.EmptyProjectPath} " + + $"--sign " + + $"--excludecibinarylog " + + $"/bl:{binlog} " + + $"/p:OfficialBuildId={OfficialBuildId} " + + $"/p:SignableSourceBuiltAssets=\\\"{signableAssets}\\\" " + + $"{SignProperties}"; + + return (buildScript, arguments); + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SigningValidation.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SigningValidation.cs index 84b1e4a1acd1..cc1ee51326e1 100644 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SigningValidation.cs +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/SigningValidation.cs @@ -12,12 +12,14 @@ using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; -using Task = System.Threading.Tasks.Task; +using Microsoft.DotNet.UnifiedBuild.Tasks.Services; +using BuildTask = Microsoft.Build.Utilities.Task; namespace Microsoft.DotNet.UnifiedBuild.Tasks; -public class SigningValidation : Microsoft.Build.Utilities.Task +public class SigningValidation : BuildTask { /// /// Directory where the blobs and packages were downloaded to @@ -58,13 +60,16 @@ public class SigningValidation : Microsoft.Build.Utilities.Task /// private static readonly string _signCheckFilesDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName(), "SignCheckFiles"); + private const string _signCheckBinLogFileName = "signing-validation.binlog"; private const string _signCheckExclusionsFileName = "SignCheckExclusionsFile.txt"; private const string _signCheckStdoutLogFileName = "signcheck.log"; private const string _signCheckStderrLogFileName = "signcheck.error.log"; private const string _signCheckResultsXmlFileName = "signcheck.xml"; private const int _signCheckTimeout = 60 * 60 * 1000 * 2; // 2 hours - public override bool Execute() + public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult(); + + private async Task ExecuteAsync() { try { @@ -72,7 +77,7 @@ public override bool Execute() PrepareFilesToSignCheck(); - RunSignCheck(); + await RunSignCheckAsync(); ProcessSignCheckResults(); @@ -108,23 +113,33 @@ private void PrepareFilesToSignCheck() var manifestsDir = Path.Combine(artifactDirectory, "manifests"); if (!Directory.Exists(manifestsDir)) { - continue; + Log.LogMessage(MessageImportance.High, + $" No manifests in '{artifactDirectory}' to parse for files to sign check. " + + $"All files defined in the directory will be included..."); + filesToSignCheck.AddRange(Directory.EnumerateFiles(artifactDirectory, "*", SearchOption.AllDirectories) + .Select(file => (artifactDirectory, Path.GetRelativePath(artifactDirectory, file)))); } - foreach (string manifest in Directory.EnumerateFiles(manifestsDir, "*.xml", SearchOption.AllDirectories)) + else { - using (Stream xmlStream = File.OpenRead(manifest)) + Log.LogMessage(MessageImportance.High, + $" Parsing manifests in '{manifestsDir}' for files to sign check. " + + $"All blobs and packages defined in the manifests will be included..."); + foreach (string manifest in Directory.EnumerateFiles(manifestsDir, "*.xml", SearchOption.AllDirectories)) { - XDocument doc = XDocument.Load(xmlStream); - - // Extract blobs - filesToSignCheck.AddRange(doc.Descendants("Blob") - .Where(blob => IsReleaseShipping(blob) && IsExternallyVisible(blob)) - .Select(blob => (artifactDirectory, ExtractAttribute(blob, "PipelineArtifactPath")))); - - // Extract packages - filesToSignCheck.AddRange(doc.Descendants("Package") - .Where(pkg => IsReleaseShipping(pkg) && IsExternallyVisible(pkg)) - .Select(pkg => (artifactDirectory, ExtractAttribute(pkg, "PipelineArtifactPath")))); + using (Stream xmlStream = File.OpenRead(manifest)) + { + XDocument doc = XDocument.Load(xmlStream); + + // Extract blobs + filesToSignCheck.AddRange(doc.Descendants("Blob") + .Where(blob => IsReleaseShipping(blob) && IsExternallyVisible(blob)) + .Select(blob => (artifactDirectory, ExtractAttribute(blob, "PipelineArtifactPath")))); + + // Extract packages + filesToSignCheck.AddRange(doc.Descendants("Package") + .Where(pkg => IsReleaseShipping(pkg) && IsExternallyVisible(pkg)) + .Select(pkg => (artifactDirectory, ExtractAttribute(pkg, "PipelineArtifactPath")))); + } } } } @@ -174,56 +189,33 @@ private void PrepareFilesToSignCheck() /// /// Runs the signcheck task on the specified package base path /// - private void RunSignCheck() + private async Task RunSignCheckAsync() { - using (var process = new Process()) + (string command, string arguments) = GetSignCheckCommandAndArguments(); + Log.LogMessage(MessageImportance.High, $"Running SignCheck..."); + + var processService = new ProcessService(Log, timeout: _signCheckTimeout); + ProcessResult result = await processService.RunProcessAsync( + command, + arguments, + printOutput: false); + + string errorLog = GetLogPath(_signCheckStderrLogFileName); + string errorLogContent = File.Exists(errorLog) ? File.ReadAllText(errorLog).Trim() : string.Empty; + if (result.ExitCode != 0 || !string.IsNullOrWhiteSpace(errorLogContent)) { - (string command, string arguments) = GetSignCheckCommandAndArguments(); - - process.StartInfo = new ProcessStartInfo() - { - FileName = command, - Arguments = arguments, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - }; - - // SignCheck writes console output to log files and the output stream. - // To avoid cluttering the console, redirect the output to empty handlers. - process.OutputDataReceived += (sender, args) => { }; - process.ErrorDataReceived += (sender, args) => { }; - - Log.LogMessage(MessageImportance.High, $"Running SignCheck..."); - - process.Start(); - - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - - bool hasExited = process.WaitForExit(_signCheckTimeout); - if (!hasExited) - { - throw new TimeoutException($"SignCheck timed out after {_signCheckTimeout / 1000} seconds."); - } - - string errorLog = GetLogPath(_signCheckStderrLogFileName); - string errorLogContent = File.Exists(errorLog) ? File.ReadAllText(errorLog).Trim() : string.Empty; - if (process.ExitCode != 0 || !string.IsNullOrWhiteSpace(errorLogContent)) - { - // We don't want to throw here because SignCheck will fail for unsigned files - Log.LogError($"SignCheck failed with exit code {process.ExitCode}: {errorLogContent}"); - } - - string stdoutLog = GetLogPath(_signCheckStdoutLogFileName); - string stdoutLogContent = File.Exists(stdoutLog) ? File.ReadAllText(stdoutLog).Trim() : string.Empty; - if (!string.IsNullOrWhiteSpace(stdoutLogContent) && stdoutLogContent.Contains("No files were processed")) - { - Log.LogError("SignCheck did not process any files."); - } + // We don't want to throw here because SignCheck will fail for unsigned files + Log.LogError($"SignCheck failed with exit code {result.ExitCode}: {errorLogContent}"); + } - Log.LogMessage(MessageImportance.High, $"SignCheck completed."); + string stdoutLog = GetLogPath(_signCheckStdoutLogFileName); + string stdoutLogContent = File.Exists(stdoutLog) ? File.ReadAllText(stdoutLog).Trim() : string.Empty; + if (!string.IsNullOrWhiteSpace(stdoutLogContent) && stdoutLogContent.Contains("No files were processed")) + { + Log.LogError("SignCheck did not process any files."); } + + Log.LogMessage(MessageImportance.High, $"SignCheck completed."); } private void ProcessSignCheckResults() @@ -299,7 +291,9 @@ private bool LogAndCheckResults(IEnumerable results, string issueType) /// private (string command, string arguments) GetSignCheckCommandAndArguments() { - string sdkTaskScript = Path.Combine(DotNetRootDirectory, "eng", "common", "sdk-task"); + // Target sdk-task scripts from Arcade to catch and fix issues with the scripts between releases + // This reduces our exposure to known issues in the local VMR copy and improves build reliability during the VMR bootstrap window. + string sdkTaskScript = Path.Combine(DotNetRootDirectory, "src", "arcade", "eng", "common", "sdk-task"); string argumentsTemplate = $"'{sdkTaskScript}.$scriptExtension$' " + @@ -312,6 +306,7 @@ private bool LogAndCheckResults(IEnumerable results, string issueType) $"/p:SignCheckErrorLog='{GetLogPath(_signCheckStderrLogFileName)}' " + $"/p:SignCheckResultsXmlFile='{GetLogPath(_signCheckResultsXmlFileName)}' " + $"/p:SignCheckExclusionsFile='{GetSignCheckExclusionsFile()}' " + + $"/bl:{GetLogPath(_signCheckBinLogFileName)} " + $"$additionalArgs$"; string command = string.Empty; diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs deleted file mode 100644 index ea30a676c37b..000000000000 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using NuGet.Packaging.Core; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml.Linq; - -namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport -{ - public class ValidateUsageAgainstBaseline : Task - { - [Required] - public string DataFile { get; set; } - - [Required] - public string OutputBaselineFile { get; set; } - - [Required] - public string OutputReportFile { get; set; } - - public bool AllowTestProjectUsage { get; set; } - - public string BaselineDataFile { get; set; } - - public override bool Execute() - { - var used = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile))); - - IEnumerable baselineUsages; - if (File.Exists(BaselineDataFile)) - { - baselineUsages = UsageData.Parse(XElement.Parse(File.ReadAllText(BaselineDataFile))).Usages; - } - else - { - baselineUsages = Enumerable.Empty(); - } - - Comparison diff = Compare( - used.Usages.Select(u => u.GetIdentityWithoutRid()).Distinct(), - baselineUsages.Select(u => u.GetIdentityWithoutRid()).Distinct()); - - var report = new XElement("BaselineComparison"); - - bool tellUserToUpdateBaseline = false; - - if (diff.Added.Any()) - { - tellUserToUpdateBaseline = true; - Log.LogError( - $"{diff.Added.Length} new packages used not in baseline! See report " + - $"at {OutputReportFile} for more information. Package IDs are:\n" + - string.Join("\n", diff.Added.Select(u => u.ToString()))); - - // In the report, list full usage info, not only identity. - report.Add( - new XElement( - "New", - used.Usages - .Where(u => diff.Added.Contains(u.GetIdentityWithoutRid())) - .Select(u => u.ToXml()))); - } - if (diff.Removed.Any()) - { - tellUserToUpdateBaseline = true; - Log.LogMessage( - MessageImportance.High, - $"{diff.Removed.Length} packages in baseline weren't used!"); - - report.Add(new XElement("Removed", diff.Removed.Select(id => id.ToXElement()))); - } - if (diff.Unchanged.Any()) - { - Log.LogMessage( - MessageImportance.High, - $"{diff.Unchanged.Length} packages used as expected in the baseline."); - } - - if (!AllowTestProjectUsage) - { - Usage[] testProjectUsages = used.Usages - .Where(WriteUsageReports.IsTestUsageByHeuristic) - .ToArray(); - - if (testProjectUsages.Any()) - { - string[] projects = testProjectUsages - .Select(u => u.AssetsFile) - .Distinct() - .ToArray(); - - Log.LogError( - $"{testProjectUsages.Length} forbidden test usages found in " + - $"{projects.Length} projects:\n" + - string.Join("\n", projects)); - } - } - - // Simplify the used data to what is necessary for a baseline, to reduce file size. - foreach (var usage in used.Usages) - { - usage.AssetsFile = null; - } - used.Usages = used.Usages.Distinct().ToArray(); - - Directory.CreateDirectory(Path.GetDirectoryName(OutputBaselineFile)); - File.WriteAllText(OutputBaselineFile, used.ToXml().ToString()); - - Directory.CreateDirectory(Path.GetDirectoryName(OutputReportFile)); - File.WriteAllText(OutputReportFile, report.ToString()); - - if (tellUserToUpdateBaseline) - { - Log.LogWarning( - "Prebuilt usages are different from the baseline. If detected changes are " + - "acceptable, update baseline with:\n" + - $"cp '{OutputBaselineFile}' '{BaselineDataFile}'"); - } - - return !Log.HasLoggedErrors; - } - - private static Comparison Compare(IEnumerable actual, IEnumerable baseline) - { - return new Comparison(actual.ToArray(), baseline.ToArray()); - } - - private class Comparison - { - public T[] Added { get; } - public T[] Removed { get; } - public T[] Unchanged { get; } - - public Comparison( - IEnumerable actual, - IEnumerable baseline) - { - Added = actual.Except(baseline).ToArray(); - Removed = baseline.Except(actual).ToArray(); - Unchanged = actual.Intersect(baseline).ToArray(); - } - } - } -} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteAnnotatedUsageReport.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteAnnotatedUsageReport.cs new file mode 100644 index 000000000000..f9396eb0df1b --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteAnnotatedUsageReport.cs @@ -0,0 +1,256 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport +{ + public class WriteAnnotatedUsageReport : Task + { + private const string SnapshotPrefix = "PackageVersions."; + private const string SnapshotSuffix = ".Snapshot.props"; + + /// + /// Source usage data JSON file. + /// + [Required] + public string DataFile { get; set; } + + /// + /// Suffix to use for the version property name. + /// + [Required] + public string VersionPropertySuffix { get; set; } + + /// + /// A set of "PackageVersions.{repo}.Snapshot.props" files. They are analyzed to find + /// packages built during source-build, and which repo built them. This info is added to the + /// report. New packages are associated to a repo by going through each PVP in ascending + /// file modification order. + /// + public ITaskItem[] PackageVersionPropsSnapshots { get; set; } + + /// + /// File containing the results of poisoning the prebuilts. Example format: + /// + /// MATCH: output built\dotnet-sdk-...\System.Collections.dll(hash 4b...31) matches one of: + /// intermediate\netstandard.library.2.0.1.nupkg\build\...\System.Collections.dll + /// + /// The usage report reads this file, looking for 'intermediate\*.nupkg' to annotate. + /// + public string PoisonedReportFile { get; set; } + + /// + /// Path to write the prebuilt annotated usage report file. + /// + [Required] + public string PrebuiltAnnotatedUsageReportFile { get; set; } + + public override bool Execute() + { + UsageData data = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile))); + IEnumerable sourceBuildRepoOutputs = GetSourceBuildRepoOutputs(); + + var poisonNupkgFilenames = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (File.Exists(PoisonedReportFile)) + { + foreach (string line in File.ReadAllLines(PoisonedReportFile)) + { + string[] segments = line.Split('\\'); + if (segments.Length > 2 && + segments[0].Trim() == "intermediate" && + segments[1].EndsWith(".nupkg")) + { + poisonNupkgFilenames.Add(Path.GetFileNameWithoutExtension(segments[1])); + } + } + } + + var report = new XElement("AnnotatedUsages"); + + var annotatedUsages = data.Usages.NullAsEmpty() + .Select(usage => + { + string id = usage.PackageIdentity.Id; + string version = usage.PackageIdentity.Version.OriginalVersion; + + string pvpIdent = WritePackageVersionsProps.GetPropertyName(id, VersionPropertySuffix); + + var sourceBuildCreator = new StringBuilder(); + foreach (RepoOutput output in sourceBuildRepoOutputs) + { + foreach (PackageVersionPropsElement p in output.Built) + { + if (p.Name.Equals(pvpIdent, StringComparison.OrdinalIgnoreCase)) + { + if (sourceBuildCreator.Length != 0) + { + sourceBuildCreator.Append(" "); + } + sourceBuildCreator.Append(output.Repo); + sourceBuildCreator.Append(" "); + sourceBuildCreator.Append(p.Name); + sourceBuildCreator.Append("/"); + sourceBuildCreator.Append(p.Version); + } + } + } + + return new AnnotatedUsage + { + Usage = usage, + + Project = data.ProjectDirectories + ?.FirstOrDefault(p => usage.AssetsFile?.StartsWith(p) ?? false), + + SourceBuildPackageIdCreator = sourceBuildCreator.Length == 0 + ? null + : sourceBuildCreator.ToString(), + + TestProjectByHeuristic = IsTestUsageByHeuristic(usage), + + EndsUpInOutput = poisonNupkgFilenames.Contains($"{id}.{version}") + }; + }) + .ToArray(); + + foreach (var onlyTestProjectUsage in annotatedUsages + .GroupBy(u => u.Usage.PackageIdentity) + .Where(g => g.All(u => u.TestProjectByHeuristic)) + .SelectMany(g => g)) + { + onlyTestProjectUsage.TestProjectOnlyByHeuristic = true; + } + + report.Add(annotatedUsages.Select(u => u.ToXml())); + + Directory.CreateDirectory(Path.GetDirectoryName(PrebuiltAnnotatedUsageReportFile)); + File.WriteAllText(PrebuiltAnnotatedUsageReportFile, report.ToString()); + + return !Log.HasLoggedErrors; + } + + private RepoOutput[] GetSourceBuildRepoOutputs() + { + var pvpSnapshotFiles = PackageVersionPropsSnapshots.NullAsEmpty() + .Select(item => + { + var content = File.ReadAllText(item.ItemSpec); + return new + { + Path = item.ItemSpec, + Content = content, + Xml = XElement.Parse(content) + }; + }) + .OrderBy(snapshot => + { + // Get the embedded creation time if possible: the file's original metadata may + // have been destroyed by copying, zipping, etc. + string creationTime = snapshot.Xml + // Get all elements + .Elements() + // Select all the subelements + .SelectMany(e => e.Elements()) + // Find all that match the creation time property name + .Where(e => e.Name == snapshot.Xml.GetDefaultNamespace().GetName(WritePackageVersionsProps.CreationTimePropertyName)) + // There should be only one or zero + .SingleOrDefault()?.Value; + + if (string.IsNullOrEmpty(creationTime)) + { + Log.LogError($"No creation time property found in snapshot {snapshot.Path}"); + return default(DateTime); + } + + return new DateTime(long.Parse(creationTime)); + }) + .Select(snapshot => + { + string filename = Path.GetFileName(snapshot.Path); + return new + { + Repo = filename.Substring( + SnapshotPrefix.Length, + filename.Length - SnapshotPrefix.Length - SnapshotSuffix.Length), + PackageVersionProp = PackageVersionPropsElement.Parse(snapshot.Xml) + }; + }) + .ToArray(); + + return pvpSnapshotFiles.Skip(1) + .Zip(pvpSnapshotFiles, (pvp, prev) => new RepoOutput + { + Repo = prev.Repo, + Built = pvp.PackageVersionProp.Except(prev.PackageVersionProp).ToArray() + }) + .ToArray(); + } + + public static bool IsTestUsageByHeuristic(Usage usage) + { + string[] assetsFileParts = usage.AssetsFile?.Split('/', '\\'); + + // If the dir name ends in Test(s), it's probably a test project. + // Ignore the first two segments to avoid classifying everything in "src/vstest". + // This also catches "test" dirs that contain many test projects. + if (assetsFileParts?.Skip(2).Any(p => + p.EndsWith("Tests", StringComparison.OrdinalIgnoreCase) || + p.EndsWith("Test", StringComparison.OrdinalIgnoreCase)) == true) + { + return true; + } + + // CoreFX restores test dependencies during this sync project. + if (assetsFileParts?.Contains("XUnit.Runtime") == true) + { + return true; + } + + return false; + } + + private class RepoOutput + { + public string Repo { get; set; } + public PackageVersionPropsElement[] Built { get; set; } + } + + private struct PackageVersionPropsElement + { + public static PackageVersionPropsElement[] Parse(XElement xml) + { + return xml + // Get the first PropertyGroup. The second PropertyGroup is 'extra properties', and the third group is the creation time. + // Only select the first because the extra properties are not built packages. + .Elements() + .First() + // Get all *PackageVersion property elements. + .Elements() + .Select(x => new PackageVersionPropsElement( + x.Name.LocalName, + x.Nodes().OfType().First().Value)) + .ToArray(); + } + + public string Name { get; } + public string Version { get; } + + public PackageVersionPropsElement(string name, string version) + { + Name = name; + Version = version; + } + } + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs index 36b317f380d3..b5d44e139e4e 100644 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs @@ -83,6 +83,13 @@ public class WritePackageUsageData : Task /// public string ProjectAssetsJsonArchiveFile { get; set; } + /// + /// Output: List of prebuilt packages detected. + /// Each item has PackageId and PackageVersion metadata. + /// + [Output] + public ITaskItem[] PrebuiltPackages { get; set; } + public override bool Execute() { DateTime startTime = DateTime.Now; @@ -256,6 +263,16 @@ public override bool Execute() Directory.CreateDirectory(Path.GetDirectoryName(DataFile)); File.WriteAllText(DataFile, data.ToXml().ToString()); + PrebuiltPackages = prebuilt + .Select(p => new Microsoft.Build.Utilities.TaskItem( + p.ToString(), + new Dictionary + { + { "PackageId", p.Id }, + { "PackageVersion", p.Version.ToNormalizedString() } + })) + .ToArray(); + Log.LogMessage(MessageImportance.Normal, $"Writing package usage data... done. Took {DateTime.Now - startTime}"); return !Log.HasLoggedErrors; diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs deleted file mode 100644 index 6fc172538a96..000000000000 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -using Microsoft.Build.Framework; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NuGet.Packaging.Core; -using NuGet.Versioning; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using System.Xml.Linq; -using Task = Microsoft.Build.Utilities.Task; - -namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport -{ - public class WriteUsageBurndownData : Task - { - /// - /// Specifies the root directory for git. - /// Note: Requires a trailing "/" when specifying the directory. - /// - [Required] - public string RootDirectory { get; set; } - - /// - /// Specifies the path to the prebuilt baseline file - /// to be used to generate the burndown. - /// - [Required] - public string PrebuiltBaselineFile { get; set; } - - /// - /// Output data CSV file. - /// - [Required] - public string OutputFilePath { get; set; } - - /// - /// Sends HTTP requests and receives HTTP responses. - /// - private readonly HttpClient client = new(); - - public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult(); - - private async Task ExecuteAsync() - { - string baselineRelativeFileName = PrebuiltBaselineFile.Replace(RootDirectory, ""); - string gitLogCommand = $"log --first-parent --pretty=format:%H,%f,%ci -- {PrebuiltBaselineFile}"; - - DateTime startTime = DateTime.Now; - Log.LogMessage(MessageImportance.High, "Generating summary usage burndown data..."); - - - IEnumerable> getCommitTasks = ExecuteGitCommand(RootDirectory, gitLogCommand) - .Select(async commitLine => - { - var splitLine = commitLine.Split(','); - var commit = new Commit() - { - Sha = splitLine[0], - Title = splitLine[1], - CommitDate = DateTime.Parse(splitLine[2]) - }; - string fileContents = await GetFileContentsAsync(baselineRelativeFileName, commit.Sha); - Usage[] usages = UsageData.Parse(XElement.Parse(fileContents)).Usages.NullAsEmpty().ToArray(); - commit.PackageVersionCount = usages.Count(); - commit.PackageCount = usages.GroupBy(i => i.PackageIdentity.Id).Select(grp => grp.First()).Count(); - return commit; - }); - - Commit[] commits = await System.Threading.Tasks.Task.WhenAll(getCommitTasks); - IEnumerable data = commits.Select(c => c.ToString()); - - Directory.CreateDirectory(Path.GetDirectoryName(OutputFilePath)); - - File.WriteAllLines(OutputFilePath, data); - - Log.LogMessage( - MessageImportance.High, - $"Generating summary usage burndown data at {OutputFilePath} done. Took {DateTime.Now - startTime}"); - - return !Log.HasLoggedErrors; - } - - /// - /// Get the contents of a git file based on the commit sha. - /// - /// The relative path (from the git root) to the file. - /// The commit sha for the version of the file to get. - /// The contents of the specified file. - private Task GetFileContentsAsync(string relativeFilePath, string commitSha) => - client.GetStringAsync($"https://raw.githubusercontent.com/dotnet/source-build/{commitSha}/{relativeFilePath.Replace('\\', '/')}"); - - /// - /// Executes a git command and returns the result. - /// - /// The working directory for the git command. - /// The git command to execute. - /// An array of the output lines of the git command. - private string[] ExecuteGitCommand(string workingDirectory, string command) - { - string[] returnData; - Process _process = new Process(); - _process.StartInfo.FileName = "git"; - _process.StartInfo.Arguments = command; - _process.StartInfo.WorkingDirectory = workingDirectory; - _process.StartInfo.RedirectStandardOutput = true; - _process.StartInfo.UseShellExecute = false; - _process.Start(); - returnData = _process.StandardOutput.ReadToEnd().Split('\n'); - _process.WaitForExit(); - return returnData; - } - - private class Commit - { - public string Sha { get; set; } - public string Title { get; set; } - public DateTime CommitDate { get; set; } - public int PackageVersionCount { get; set; } - public int PackageCount { get; set; } - - public override string ToString() - { - return $"{Sha}, {Title}, {CommitDate}, {PackageVersionCount}, {PackageCount}"; - } - } - } -} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs deleted file mode 100644 index 1d0f53f7e91a..000000000000 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs +++ /dev/null @@ -1,256 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; - -namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport -{ - public class WriteUsageReports : Task - { - private const string SnapshotPrefix = "PackageVersions."; - private const string SnapshotSuffix = ".Snapshot.props"; - - /// - /// Source usage data JSON file. - /// - [Required] - public string DataFile { get; set; } - - /// - /// Suffix to use for the version property name. - /// - [Required] - public string VersionPropertySuffix { get; set; } - - /// - /// A set of "PackageVersions.{repo}.Snapshot.props" files. They are analyzed to find - /// packages built during source-build, and which repo built them. This info is added to the - /// report. New packages are associated to a repo by going through each PVP in ascending - /// file modification order. - /// - public ITaskItem[] PackageVersionPropsSnapshots { get; set; } - - /// - /// File containing the results of poisoning the prebuilts. Example format: - /// - /// MATCH: output built\dotnet-sdk-...\System.Collections.dll(hash 4b...31) matches one of: - /// intermediate\netstandard.library.2.0.1.nupkg\build\...\System.Collections.dll - /// - /// The usage report reads this file, looking for 'intermediate\*.nupkg' to annotate. - /// - public string PoisonedReportFile { get; set; } - - [Required] - public string OutputDirectory { get; set; } - - public override bool Execute() - { - UsageData data = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile))); - IEnumerable sourceBuildRepoOutputs = GetSourceBuildRepoOutputs(); - - var poisonNupkgFilenames = new HashSet(StringComparer.OrdinalIgnoreCase); - - if (File.Exists(PoisonedReportFile)) - { - foreach (string line in File.ReadAllLines(PoisonedReportFile)) - { - string[] segments = line.Split('\\'); - if (segments.Length > 2 && - segments[0].Trim() == "intermediate" && - segments[1].EndsWith(".nupkg")) - { - poisonNupkgFilenames.Add(Path.GetFileNameWithoutExtension(segments[1])); - } - } - } - - var report = new XElement("AnnotatedUsages"); - - var annotatedUsages = data.Usages.NullAsEmpty() - .Select(usage => - { - string id = usage.PackageIdentity.Id; - string version = usage.PackageIdentity.Version.OriginalVersion; - - string pvpIdent = WritePackageVersionsProps.GetPropertyName(id, VersionPropertySuffix); - - var sourceBuildCreator = new StringBuilder(); - foreach (RepoOutput output in sourceBuildRepoOutputs) - { - foreach (PackageVersionPropsElement p in output.Built) - { - if (p.Name.Equals(pvpIdent, StringComparison.OrdinalIgnoreCase)) - { - if (sourceBuildCreator.Length != 0) - { - sourceBuildCreator.Append(" "); - } - sourceBuildCreator.Append(output.Repo); - sourceBuildCreator.Append(" "); - sourceBuildCreator.Append(p.Name); - sourceBuildCreator.Append("/"); - sourceBuildCreator.Append(p.Version); - } - } - } - - return new AnnotatedUsage - { - Usage = usage, - - Project = data.ProjectDirectories - ?.FirstOrDefault(p => usage.AssetsFile?.StartsWith(p) ?? false), - - SourceBuildPackageIdCreator = sourceBuildCreator.Length == 0 - ? null - : sourceBuildCreator.ToString(), - - TestProjectByHeuristic = IsTestUsageByHeuristic(usage), - - EndsUpInOutput = poisonNupkgFilenames.Contains($"{id}.{version}") - }; - }) - .ToArray(); - - foreach (var onlyTestProjectUsage in annotatedUsages - .GroupBy(u => u.Usage.PackageIdentity) - .Where(g => g.All(u => u.TestProjectByHeuristic)) - .SelectMany(g => g)) - { - onlyTestProjectUsage.TestProjectOnlyByHeuristic = true; - } - - report.Add(annotatedUsages.Select(u => u.ToXml())); - - Directory.CreateDirectory(OutputDirectory); - - File.WriteAllText( - Path.Combine(OutputDirectory, "annotated-usage.xml"), - report.ToString()); - - return !Log.HasLoggedErrors; - } - - private RepoOutput[] GetSourceBuildRepoOutputs() - { - var pvpSnapshotFiles = PackageVersionPropsSnapshots.NullAsEmpty() - .Select(item => - { - var content = File.ReadAllText(item.ItemSpec); - return new - { - Path = item.ItemSpec, - Content = content, - Xml = XElement.Parse(content) - }; - }) - .OrderBy(snapshot => - { - // Get the embedded creation time if possible: the file's original metadata may - // have been destroyed by copying, zipping, etc. - string creationTime = snapshot.Xml - // Get all elements - .Elements() - // Select all the subelements - .SelectMany(e => e.Elements()) - // Find all that match the creation time property name - .Where(e => e.Name == snapshot.Xml.GetDefaultNamespace().GetName(WritePackageVersionsProps.CreationTimePropertyName)) - // There should be only one or zero - .SingleOrDefault()?.Value; - - if (string.IsNullOrEmpty(creationTime)) - { - Log.LogError($"No creation time property found in snapshot {snapshot.Path}"); - return default(DateTime); - } - - return new DateTime(long.Parse(creationTime)); - }) - .Select(snapshot => - { - string filename = Path.GetFileName(snapshot.Path); - return new - { - Repo = filename.Substring( - SnapshotPrefix.Length, - filename.Length - SnapshotPrefix.Length - SnapshotSuffix.Length), - PackageVersionProp = PackageVersionPropsElement.Parse(snapshot.Xml) - }; - }) - .ToArray(); - - return pvpSnapshotFiles.Skip(1) - .Zip(pvpSnapshotFiles, (pvp, prev) => new RepoOutput - { - Repo = prev.Repo, - Built = pvp.PackageVersionProp.Except(prev.PackageVersionProp).ToArray() - }) - .ToArray(); - } - - public static bool IsTestUsageByHeuristic(Usage usage) - { - string[] assetsFileParts = usage.AssetsFile?.Split('/', '\\'); - - // If the dir name ends in Test(s), it's probably a test project. - // Ignore the first two segments to avoid classifying everything in "src/vstest". - // This also catches "test" dirs that contain many test projects. - if (assetsFileParts?.Skip(2).Any(p => - p.EndsWith("Tests", StringComparison.OrdinalIgnoreCase) || - p.EndsWith("Test", StringComparison.OrdinalIgnoreCase)) == true) - { - return true; - } - - // CoreFX restores test dependencies during this sync project. - if (assetsFileParts?.Contains("XUnit.Runtime") == true) - { - return true; - } - - return false; - } - - private class RepoOutput - { - public string Repo { get; set; } - public PackageVersionPropsElement[] Built { get; set; } - } - - private struct PackageVersionPropsElement - { - public static PackageVersionPropsElement[] Parse(XElement xml) - { - return xml - // Get the first PropertyGroup. The second PropertyGroup is 'extra properties', and the third group is the creation time. - // Only select the first because the extra properties are not built packages. - .Elements() - .First() - // Get all *PackageVersion property elements. - .Elements() - .Select(x => new PackageVersionPropsElement( - x.Name.LocalName, - x.Nodes().OfType().First().Value)) - .ToArray(); - } - - public string Name { get; } - public string Version { get; } - - public PackageVersionPropsElement(string name, string version) - { - Name = name; - Version = version; - } - } - } -} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/VersionDetailsHelper.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/VersionDetailsHelper.cs new file mode 100644 index 000000000000..37d68bed2ee4 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/VersionDetailsHelper.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Xml; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.UnifiedBuild.Tasks +{ + /// + /// Helper class for reading dependency information from Version.Details.xml files. + /// + internal static class VersionDetailsHelper + { + private const string PinnedAttributeName = "Pinned"; + private const string DependencyAttributeName = "Dependency"; + private const string NameAttributeName = "Name"; + + /// + /// Retrieve the set of non-pinned dependencies from a Version.Details.xml file. + /// + /// Path to the Version.Details.xml file. + /// TaskLoggingHelper for logging errors. + /// Hash set of dependency names, or null if an error occurred. + public static HashSet GetDependencies(string versionDetailsPath, TaskLoggingHelper log) + { + XmlDocument document = new XmlDocument(); + + try + { + document.Load(versionDetailsPath); + } + catch (Exception e) + { + log.LogErrorFromException(e); + return null; + } + + HashSet dependencyNames = new HashSet(StringComparer.OrdinalIgnoreCase); + + // Load the nodes and filter those that are pinned + XmlNodeList dependencyNodes = document.DocumentElement.SelectNodes($"//{DependencyAttributeName}"); + + foreach (XmlNode dependency in dependencyNodes) + { + if (dependency.NodeType == XmlNodeType.Comment || dependency.NodeType == XmlNodeType.Whitespace) + { + continue; + } + + bool isPinned = false; + XmlAttribute pinnedAttribute = dependency.Attributes[PinnedAttributeName]; + if (pinnedAttribute != null && !bool.TryParse(pinnedAttribute.Value, out isPinned)) + { + log.LogError($"The '{PinnedAttributeName}' attribute is set but the value " + + $"'{pinnedAttribute.Value}' is not a valid boolean."); + return null; + } + + if (isPinned) + { + continue; + } + + var name = dependency.Attributes[NameAttributeName]?.Value?.Trim(); + + if (string.IsNullOrEmpty(name)) + { + log.LogError($"The '{NameAttributeName}' attribute must be specified."); + return null; + } + + dependencyNames.Add(name); + } + + return dependencyNames; + } + } +} diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs index 89be96e23fc4..27eb63530804 100644 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs @@ -98,56 +98,7 @@ public class WritePackageVersionsProps : Microsoft.Build.Utilities.Task /// Hash set of dependency names. private HashSet GetDependences() { - XmlDocument document = new XmlDocument(); - - try - { - document.Load(VersionDetails); - } - catch (Exception e) - { - Log.LogErrorFromException(e); - return null; - } - - HashSet dependencyNames = new HashSet(StringComparer.OrdinalIgnoreCase); - - // Load the nodes, filter those that are not pinned, and - XmlNodeList dependencyNodes = document.DocumentElement.SelectNodes($"//{DependencyAttributeName}"); - - foreach (XmlNode dependency in dependencyNodes) - { - if (dependency.NodeType == XmlNodeType.Comment || dependency.NodeType == XmlNodeType.Whitespace) - { - continue; - } - - bool isPinned = false; - XmlAttribute pinnedAttribute = dependency.Attributes[PinnedAttributeName]; - if (pinnedAttribute != null && !bool.TryParse(pinnedAttribute.Value, out isPinned)) - { - Log.LogError($"The '{PinnedAttributeName}' attribute is set but the value " + - $"'{pinnedAttribute.Value}' is not a valid boolean..."); - return null; - } - - if (isPinned) - { - continue; - } - - var name = dependency.Attributes[NameAttributeName]?.Value?.Trim(); - - if (string.IsNullOrEmpty(name)) - { - Log.LogError($"The '{NameAttributeName}' attribute must be specified."); - return null; - } - - dependencyNames.Add(name); - } - - return dependencyNames; + return VersionDetailsHelper.GetDependencies(VersionDetails, Log); } /// diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WriteSbrpUsageReport.cs b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WriteSbrpUsageReport.cs index ea02dd5118d6..31aa6df459b9 100644 --- a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WriteSbrpUsageReport.cs +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WriteSbrpUsageReport.cs @@ -215,6 +215,25 @@ private void ScanProjectReferences() TrackPackageReference(lockFile.Path, downloadDep.Name, downloadDep.VersionRange.MinVersion?.ToString(), Enumerable.Empty()); } + // Track framework references (e.g., Microsoft.NETCore.App, Microsoft.AspNetCore.App) + // These correspond to targeting packs like Microsoft.NETCore.App.Ref.6.0.0 + foreach (var targetFramework in lockFile.PackageSpec.TargetFrameworks) + { + foreach (var frameworkRef in targetFramework.FrameworkReferences) + { + string? targetingPackVersion = InferTargetingPackVersion(targetFramework.FrameworkName.GetShortFolderName()); + if (targetingPackVersion != null) + { + string targetingPackName = $"{frameworkRef.Name}.Ref"; + TrackPackageReference( + lockFile.Path, + targetingPackName, + targetingPackVersion, + [ targetFramework.FrameworkName.GetShortFolderName() ]); + } + } + } + if (lockFile.PackageSpec.RestoreMetadata.ProjectPath.Contains(SbrpRepoName)) { // For SBRP projects, we need to track the project references as well. While project references are included in the targets @@ -241,6 +260,25 @@ private void ScanProjectReferences() } } + // Match patterns like net6.0, net7.0, net8.0, net9.0, net10.0, etc. + [GeneratedRegex(@"^net(\d+\.\d+)$")] + private static partial Regex GetTargetingPackVersionRegex(); + + /// + /// Infers the targeting pack version from a target framework moniker. + /// For source-build, we map net6.0 -> 6.0.0, net7.0 -> 7.0.0, etc. + /// + private static string? InferTargetingPackVersion(string tfm) + { + var match = GetTargetingPackVersionRegex().Match(tfm); + if (match.Success) + { + return $"{match.Groups[1].Value}.0"; + } + + return null; + } + private void TrackPackageReference(string lockFilePath, string? name, string? version, IEnumerable tfms) { string id = PackageInfo.GetId(name, version); diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/Signing.props b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/Signing.props new file mode 100644 index 000000000000..70fd9258a16f --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/Signing.props @@ -0,0 +1,45 @@ + + + + 3PartySHA2 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/empty.proj b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/empty.proj new file mode 100644 index 000000000000..0d2eaa148745 --- /dev/null +++ b/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/static/empty.proj @@ -0,0 +1,4 @@ + + + + diff --git a/eng/tools/tools.proj b/eng/tools/tools.proj new file mode 100644 index 000000000000..1cf3fdcd1a46 --- /dev/null +++ b/eng/tools/tools.proj @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/eng/vs-workaround.targets b/eng/vs-workaround.targets new file mode 100644 index 000000000000..e53cb5e68895 --- /dev/null +++ b/eng/vs-workaround.targets @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/es-metadata.yml b/es-metadata.yml new file mode 100644 index 000000000000..972dbca33d9e --- /dev/null +++ b/es-metadata.yml @@ -0,0 +1,8 @@ +schemaVersion: 0.0.1 +isProduction: true +accountableOwners: + service: 8d20dc36-68f2-41f9-94fe-c3cd15ce04bd +routing: + defaultAreaPath: + org: devdiv + path: DevDiv\.NET Shared\VMR diff --git a/global.json b/global.json index b00fc4e0f22a..d33e0de0c8d8 100644 --- a/global.json +++ b/global.json @@ -1,10 +1,10 @@ { "tools": { - "dotnet": "10.0.100-rc.1.25411.109" + "dotnet": "11.0.100-alpha.1.25618.104" }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25411.109" + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.25618.104" } } diff --git a/prep-source-build.sh b/prep-source-build.sh index 1a52284bb54c..125fb9ea9548 100755 --- a/prep-source-build.sh +++ b/prep-source-build.sh @@ -7,6 +7,8 @@ ### and detecting binaries and removing any non-SB allowed binaries. ### ### Options: +### --configuration Build configuration: 'Debug' or 'Release' (short: -c) +### Default is Release ### --no-artifacts Exclude the download of the previously source-built artifacts archive ### --no-bootstrap Don't replace portable packages in the download source-built artifacts ### --no-prebuilts Exclude the download of the prebuilts archive @@ -32,17 +34,23 @@ IFS=$'\n\t' source="${BASH_SOURCE[0]}" REPO_ROOT="$( cd -P "$( dirname "$0" )" && pwd )" +# Load common helper functions +source "$REPO_ROOT/eng/download-source-built-archive.sh" +source "$REPO_ROOT/eng/source-build-toolset-init.sh" + function print_help () { sed -n '/^### /,/^$/p' "$source" | cut -b 5- } packagesDir="$REPO_ROOT/prereqs/packages" +# Common settings +configuration='Release' + # SB prep default arguments defaultArtifactsRid='centos.10-x64' # Binary Tooling default arguments -defaultDotnetSdk="$REPO_ROOT/.dotnet" defaultPsbDir="$packagesDir/previously-source-built" # SB prep arguments @@ -57,15 +65,12 @@ runtime_source_feed='' # IBM requested these to support s390x scenarios runtime_source_feed_key='' # IBM requested these to support s390x scenarios # Binary Tooling arguments -dotnetSdk=$defaultDotnetSdk +customSdkDir='' psbDir=$defaultPsbDir artifactsBaseFileName="Private.SourceBuilt.Artifacts" artifactsTarballPattern="$artifactsBaseFileName.*.tar.gz" -sharedComponentsBaseFileName="Private.SourceBuilt.SharedComponents" -sharedComponentsTarballPattern="$sharedComponentsBaseFileName.*.tar.gz" - prebuiltsBaseFileName="Private.SourceBuilt.Prebuilts" prebuiltsTarballPattern="$prebuiltsBaseFileName.*.tar.gz" @@ -80,6 +85,10 @@ while :; do print_help exit 0 ;; + --configuration|-c) + configuration=$2 + shift + ;; --no-bootstrap) buildBootstrap=false ;; @@ -112,11 +121,15 @@ while :; do removeBinaries=false ;; --with-sdk) - dotnetSdk=$2 + customSdkDir="$(cd -P "$2" && pwd)" shift ;; --with-packages) - psbDir=$2 + psbDir="$(cd -P "$2" && pwd)" + if [ ! -d "$psbDir" ]; then + echo "Custom previously built packages directory '$psbDir' does not exist" + exit 1 + fi shift ;; *) @@ -127,137 +140,11 @@ while :; do shift done -# Attempting to bootstrap without an SDK will fail. So either the --no-sdk flag must be passed -# or a pre-existing .dotnet SDK directory must exist. -if [ "$buildBootstrap" == true ] && [ "$installDotnet" == false ] && [ ! -d "$REPO_ROOT/.dotnet" ]; then - echo " ERROR: --no-sdk requires --no-bootstrap or a pre-existing .dotnet SDK directory. Exiting..." - exit 1 -fi - -# Check to make sure curl exists to download the archive files -if ! command -v curl &> /dev/null -then - echo " ERROR: curl not found. Exiting..." - exit 1 -fi - -# Check if Private.SourceBuilt artifacts archive exists -downloadPsbArtifacts=$downloadArtifacts -packagesArchiveDir="$packagesDir/archive/" -if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${artifactsTarballPattern} ]; then - echo " $artifactsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" - downloadPsbArtifacts=false -fi - -# Check if shared components archive exists -downloadSharedComponentsArtifacts=$downloadArtifacts -if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${sharedComponentsTarballPattern} ]; then - echo " $sharedComponentsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" - downloadSharedComponentsArtifacts=false -fi +source "$REPO_ROOT/eng/common/tools.sh" -# Check if Private.SourceBuilt prebuilts archive exists -if [ "$downloadPrebuilts" == true ] && [ -f ${packagesArchiveDir}${prebuiltsTarballPattern} ]; then - echo " $prebuiltsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" - downloadPrebuilts=false -fi - -# Check if dotnet is installed -if [ "$installDotnet" == true ] && [ -d "$REPO_ROOT/.dotnet" ]; then - echo " ./.dotnet SDK directory exists...it will not be installed" - installDotnet=false; -fi - -# Helper to extract a property value from an XML file -function GetXmlPropertyValue { - local propName="$1" - local filePath="$2" - local value="" - local line pattern - line=$(grep -m 1 "<$propName>" "$filePath" || :) - pattern="<$propName>(.*)" - if [[ $line =~ $pattern ]]; then - value="${BASH_REMATCH[1]}" - fi - echo "$value" -} - -# Helper to download a file with retries -function DownloadWithRetries { - local url="$1" - local targetDir="$2" - ( - cd "$targetDir" && - for i in {1..5}; do - if curl -fL --retry 5 -O "$url"; then - return 0 - else - case $? in - 18) - sleep 3 - ;; - *) - return 1 - ;; - esac - fi - done - return 1 - ) -} - -function DownloadArchive { - local label="$1" - local propertyName="$2" - local isRequired="$3" - local artifactsRid="$4" - local outputDir="$5" - local destinationFilenamePrefix="${6:-}" - - local packageVersionsPath="$REPO_ROOT/eng/Versions.props" - local notFoundMessage="No $label found to download..." - - local archiveVersion - archiveVersion=$(GetXmlPropertyValue "$propertyName" "$packageVersionsPath") - if [[ -z "$archiveVersion" ]]; then - if [ "$isRequired" == true ]; then - echo " ERROR: $notFoundMessage" - exit 1 - else - echo " $notFoundMessage" - return - fi - fi - - local archiveUrl - if [[ "$propertyName" == "MicrosoftNETSdkVersion" ]]; then - archiveUrl="https://ci.dot.net/public/source-build/$artifactsBaseFileName.$archiveVersion.$artifactsRid.tar.gz" - elif [[ "$propertyName" == *Prebuilts* ]]; then - archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/$prebuiltsBaseFileName.$archiveVersion.$defaultArtifactsRid.tar.gz" - elif [[ "$propertyName" == *Artifacts* ]]; then - archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/$artifactsBaseFileName.$archiveVersion.$artifactsRid.tar.gz" - else - echo " ERROR: Unknown archive property name: $propertyName" - exit 1 - fi - - echo " Downloading $label from $archiveUrl..." - if ! DownloadWithRetries "$archiveUrl" "$outputDir"; then - echo " ERROR: Failed to download $archiveUrl" - exit 1 - fi - - # Rename the file if a destination filename prefix is provided - if [[ -n "$destinationFilenamePrefix" ]]; then - local downloadedFilename - downloadedFilename=$(basename "$archiveUrl") - # Extract the suffix from the downloaded filename - local suffix="${downloadedFilename#$artifactsBaseFileName}" - local newFilename="$destinationFilenamePrefix$suffix" - mv "$outputDir/$downloadedFilename" "$outputDir/$newFilename" - echo " Renamed $downloadedFilename to $newFilename" - fi -} +# Default properties +properties=( "/p:Configuration=$configuration" ) +properties+=( "/p:DotNetBuildSourceOnly=true" ) function BootstrapArtifacts { DOTNET_SDK_PATH="$REPO_ROOT/.dotnet" @@ -283,21 +170,60 @@ function BootstrapArtifacts { # Copy NuGet.config from the sdk repo to have the right feeds cp "$REPO_ROOT/src/sdk/NuGet.config" "$workingDir" - properties=( "/p:ArchiveDir=$packagesArchiveDir" ) + local bootstrapProperties=( "${properties[@]}" ) + bootstrapProperties+=( "/p:ArchiveDir=$packagesArchiveDir" ) if [[ -n "$bootstrap_rid" ]]; then - properties+=( "/p:PortableTargetRid=$bootstrap_rid" ) + bootstrapProperties+=( "/p:PortableTargetRid=$bootstrap_rid" ) fi # Run restore on project to initiate download of bootstrap packages - "$DOTNET_SDK_PATH/dotnet" restore "$workingDir/buildBootstrapPreviouslySB.csproj" /bl:artifacts/log/prep-bootstrap.binlog /fileLoggerParameters:LogFile=artifacts/log/prep-bootstrap.log "${properties[@]}" + "$DOTNET_SDK_PATH/dotnet" restore "$workingDir/buildBootstrapPreviouslySB.csproj" /bl:$log_dir/prep-bootstrap.binlog /fileLoggerParameters:LogFile=$log_dir/prep-bootstrap.log "${bootstrapProperties[@]}" # Remove working directory rm -rf "$workingDir" } +# Attempting to bootstrap without an SDK will fail. So either the --no-sdk flag must be passed +# or a pre-existing .dotnet SDK directory must exist. +if [ "$buildBootstrap" == true ] && [ "$installDotnet" == false ] && [ ! -d "$REPO_ROOT/.dotnet" ]; then + echo " ERROR: --no-sdk requires --no-bootstrap or a pre-existing .dotnet SDK directory. Exiting..." + exit 1 +fi + +# Check to make sure curl exists to download the archive files +if ! command -v curl &> /dev/null +then + echo " ERROR: curl not found. Exiting..." + exit 1 +fi + +# Check if Private.SourceBuilt artifacts archive exists +downloadPsbArtifacts=$downloadArtifacts +packagesArchiveDir="$packagesDir/archive/" +if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${artifactsTarballPattern} ]; then + echo " $artifactsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" + downloadPsbArtifacts=false +fi + +# Check if Private.SourceBuilt prebuilts archive exists +if [ "$downloadPrebuilts" == true ] && [ -f ${packagesArchiveDir}${prebuiltsTarballPattern} ]; then + echo " $prebuiltsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" + downloadPrebuilts=false +fi + +# Check if dotnet is installed +expectedSdkVersion=$(GetXmlPropertyValue "PrivateSourceBuiltSdkVersion" "$REPO_ROOT/eng/Versions.props") +if [ "$installDotnet" == true ] && [ -d "$REPO_ROOT/.dotnet" ]; then + installedVersions=$("$REPO_ROOT/.dotnet/dotnet" --list-sdks | awk '{print $1}') + if grep -qx "$expectedSdkVersion" <<< "${installedVersions[*]}"; then + echo " Skipping SDK installation - version $expectedSdkVersion detected" + installDotnet=false + fi +fi + # Check for the version of dotnet to install if [ "$installDotnet" == true ]; then - echo " Installing dotnet..." + echo " Installing .NET SDK $expectedSdkVersion" use_installed_dotnet_cli=false (source ./eng/common/tools.sh && InitializeDotNetCli true) fi @@ -311,25 +237,19 @@ if [ "$downloadPsbArtifacts" == true ]; then fi fi -if [ "$downloadSharedComponentsArtifacts" == true ]; then - source $REPO_ROOT/eng/common/native/init-os-and-arch.sh - source $REPO_ROOT/eng/common/native/init-distro-rid.sh - initDistroRidGlobal "$os" "$arch" "" - - DownloadArchive "shared component artifacts" "MicrosoftNETSdkVersion" false "$__DistroRid" "$packagesArchiveDir" "$sharedComponentsBaseFileName" -fi - if [ "$downloadPrebuilts" == true ]; then - DownloadArchive "prebuilts" "PrivateSourceBuiltPrebuiltsVersion" false "$artifactsRid" "$packagesArchiveDir" fi if [ "$removeBinaries" == true ]; then originalPackagesDir=$packagesDir - # Create working directory for extracking packages + # Create working directory for extracting packages workingDir=$(mktemp -d) + toolsetInitProperties=( "${properties[@]}" ) + toolsetInitProperties+=( "/p:DisableSharedComponentValidation=true" ) + # If --with-packages is not passed, unpack PSB artifacts if [[ $psbDir == $defaultPsbDir ]]; then echo " Extracting previously source-built to $workingDir" @@ -345,15 +265,23 @@ if [ "$removeBinaries" == true ]; then tar -xzf "$sourceBuiltArchive" -C "$workingDir" psbDir=$workingDir + else + echo " Using previously source-built packages from $psbDir" + toolsetInitProperties+=( "/p:CustomPreviouslySourceBuiltPackagesPath=$psbDir" ) fi - "$dotnetSdk/dotnet" build \ + + # Initialize source-only toolset for binary detection (includes custom SDK setup, MSBuild resolver, and source-built resolver) + source_only_toolset_init "$customSdkDir" "$psbDir" "true" "" "${toolsetInitProperties[@]}" + + "$_InitializeBuildTool" build \ "$REPO_ROOT/eng/init-detect-binaries.proj" \ + "${properties[@]}" \ "/p:BinariesMode=Clean" \ "/p:AllowedBinariesFile=$REPO_ROOT/eng/allowed-sb-binaries.txt" \ "/p:BinariesPackagesDir=$psbDir" \ - "/bl:artifacts/log/prep-remove-binaries.binlog" \ - "/fileLoggerParameters:LogFile=artifacts/log/prep-remove-binaries.log" \ + "/bl:$log_dir/prep-remove-binaries.binlog" \ + "/fileLoggerParameters:LogFile=$log_dir/prep-remove-binaries.log" \ "${positional_args[@]}" rm -rf "$workingDir" diff --git a/repo-projects/Directory.Build.props b/repo-projects/Directory.Build.props index b41783a5080b..aa112e54e2af 100644 --- a/repo-projects/Directory.Build.props +++ b/repo-projects/Directory.Build.props @@ -118,6 +118,8 @@ $(CommonArgs) /p:OfficialBuildId=$(OfficialBuildId) $(CommonArgs) /p:OfficialBuilder="$(OfficialBuilder)" $(CommonArgs) /p:ForceDryRunSigning=$(ForceDryRunSigning) + $(CommonArgs) /p:DotNetSignType=$(DotNetSignType) + $(CommonArgs) /p:KeepNativeSymbols=$(KeepNativeSymbols) $(CommonArgs) /p:DotNetPackageVersionPropsPath=$(PackageVersionPropsPath) @@ -219,7 +221,7 @@ Group="ARCADE" Version="$(ArcadeBootstrapVersion)" Location="$(BootstrapPackagesDir)microsoft.dotnet.arcade.sdk/$(ArcadeBootstrapVersion)" /> - + diff --git a/repo-projects/Directory.Build.targets b/repo-projects/Directory.Build.targets index 53b9e9a78883..96a2e2392c62 100644 --- a/repo-projects/Directory.Build.targets +++ b/repo-projects/Directory.Build.targets @@ -28,7 +28,13 @@ $(CommonArgs) /p:PortableBuild=$(PortableBuild) $(CommonArgs) /p:PortableTargetRid=$(PortableTargetRid) $(CommonArgs) /p:DefaultArtifactVisibility=$(DefaultArtifactVisibility) - $(CommonArgs) /p:DotNetFinalVersionKind=$(DotNetFinalVersionKind) + + + + /p:DotNetFinalVersionKind="" + /p:DotNetFinalVersionKind=prerelease + /p:DotNetFinalVersionKind=release + $(CommonArgs) $(RepoDotNetFinalVersionKindArg) $(CommonArgs) /p:DotNetBuildTests=true @@ -36,7 +42,6 @@ $(BuildArgs) /p:EnableDefaultRidSpecificArtifacts=$(EnableDefaultRidSpecificArtifacts) $(BuildArgs) /p:AllowEmptySignList=true - @@ -145,8 +150,14 @@ - + + + + - - - - - ExtraSources @@ -174,6 +176,7 @@ previously-source-built shared-components reference-packages + extra-test-dependencies true false @@ -182,6 +185,7 @@ <_CommonBuildSources Include="@(DependentRepoSourceName)" /> <_CommonBuildSources Include="$(ExtraSourcesNuGetSourceName)" Condition="'$(ExtraRestoreSourcePath)' != ''" /> + <_CommonBuildSources Include="$(ExtraTestDependenciesNuGetSourceName)" Condition="Exists('$(ExtraTestDependenciesRestoreSourcePath)')" /> @@ -241,6 +245,11 @@ SourcePath="$(ExtraRestoreSourcePath)" Condition="'$(ExtraRestoreSourcePath)' != ''" /> + + + + <_SharedComponentFilterMode>$(SharedComponentFilter_NonToolingOnly) + <_SharedComponentFilterMode Condition="$([System.String]::new(';$(BootstrapArcadeRepos);').Contains(';$(RepositoryName);'))">$(SharedComponentFilter_BootstrapRepoDepsOnly) + + + Properties="SharedComponentFilterMode=$(_SharedComponentFilterMode)"> @@ -535,6 +549,17 @@ + + + @(_DependencyProducedPackage->WithMetadataValue('Identity', 'Microsoft.NETCore.App.Ref')->Metadata('Version')) + + + - $(BuildArgs) /p:EnablePackageValidation=false + + $(BuildArgs) /p:AfterMicrosoftNETSdkTargets=$(RepositoryEngineeringDir)vs-workaround.targets + true @@ -56,6 +59,9 @@ + + - + - + + diff --git a/repo-projects/nuget-client.proj b/repo-projects/nuget-client.proj index b66a1c13b3e4..4f90e9049807 100644 --- a/repo-projects/nuget-client.proj +++ b/repo-projects/nuget-client.proj @@ -7,6 +7,11 @@ $([MSBuild]::NormalizePath('$(ProjectDirectory)', 'eng', 'dotnet-build', 'build$(ShellExtension)')) $(BuildArgs) /p:GenerateResourceUsePreserializedResources=true + + + $(BuildArgs) /p:NetCurrent=$(NetCurrent) + $(BuildArgs) /p:NetPrevious=$(NetPrevious) + $(BuildArgs) /p:NetMinimum=$(NetMinimum) diff --git a/repo-projects/roslyn-analyzers.proj b/repo-projects/roslyn-analyzers.proj deleted file mode 100644 index 3bae3c296103..000000000000 --- a/repo-projects/roslyn-analyzers.proj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg) - - - true - - - - - - - - - - - - diff --git a/repo-projects/roslyn.proj b/repo-projects/roslyn.proj index be68563daa5e..30d118be522b 100644 --- a/repo-projects/roslyn.proj +++ b/repo-projects/roslyn.proj @@ -33,7 +33,6 @@ - diff --git a/repo-projects/runtime.proj b/repo-projects/runtime.proj index e295703ab58c..c7ca094258ce 100644 --- a/repo-projects/runtime.proj +++ b/repo-projects/runtime.proj @@ -27,10 +27,17 @@ true - $(BuildArgs) $(FlagParameterPrefix)cross + $(BuildArgs) $(FlagParameterPrefix)cross - - $(BuildArgs) --bootstrap + + true + false + false + $(BuildArgs) $(FlagParameterPrefix)bootstrap $(BuildArgs) /p:DotNetBuildAllRuntimePacks=true @@ -49,7 +56,6 @@ $(BuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_BROTLI=true $(BuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_LIBUNWIND=true - $(BuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_RAPIDJSON=true $(BuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_ZLIB=true $(BuildArgs) /p:FeatureXplatEventSource=false diff --git a/repo-projects/scenario-tests.proj b/repo-projects/scenario-tests.proj index f272b133313b..9c0c6c1b7187 100644 --- a/repo-projects/scenario-tests.proj +++ b/repo-projects/scenario-tests.proj @@ -47,7 +47,9 @@ $(ScenarioTestsResultsDir)$([System.DateTime]::Now.ToString("yyyy-MM-dd_HH_mm_ss")).xml --xml $(TestXmlOutputPath) --target-rid $(TargetRid) --portable-rid $(PortableTargetRid) --no-cleanup --no-traits Category=MultiTFM $(ScenarioTestsAdditionalArgs) --no-traits Category=RequiresNonTargetRidPackages + $(ScenarioTestsAdditionalArgs) --no-traits Category=RequiresPortableAssets $(ScenarioTestsAdditionalArgs) --no-traits SkipIfBuild=SourceOnlyUnofficialBuild + $(ScenarioTestsAdditionalArgs) --no-traits SkipIfBuild=SourceOnly diff --git a/repo-projects/sdk.proj b/repo-projects/sdk.proj index 6e2e0db05e70..741cde9370aa 100644 --- a/repo-projects/sdk.proj +++ b/repo-projects/sdk.proj @@ -33,14 +33,13 @@ - - + diff --git a/repo-projects/source-build-reference-packages.proj b/repo-projects/source-build-reference-packages.proj index ce6da2771436..2ab299a1c826 100644 --- a/repo-projects/source-build-reference-packages.proj +++ b/repo-projects/source-build-reference-packages.proj @@ -12,9 +12,9 @@ + Returns="@(ReferenceOnlyPackage)"> - <_ReferenceOnlyPackages Include="$(RepoArtifactsDir)packages\$(Configuration)\ReferenceOnly\*" /> + @@ -22,18 +22,20 @@ - + - - - + - + + + + + + diff --git a/repo-projects/sourcelink.proj b/repo-projects/sourcelink.proj index 82a553312e8e..2ddfa6a13dd7 100644 --- a/repo-projects/sourcelink.proj +++ b/repo-projects/sourcelink.proj @@ -1,8 +1,14 @@ + + + true + + + diff --git a/repo-projects/symreader.proj b/repo-projects/symreader.proj index 9deac728bc91..23a52690490f 100644 --- a/repo-projects/symreader.proj +++ b/repo-projects/symreader.proj @@ -1,5 +1,10 @@ + + + true + + diff --git a/repo-projects/winforms.proj b/repo-projects/winforms.proj index 93a79216bb8d..b66ed44eec11 100644 --- a/repo-projects/winforms.proj +++ b/repo-projects/winforms.proj @@ -1,7 +1,7 @@ - $(BuildArgs) $(FlagParameterPrefix)NativeToolsOnMachine + $(BuildArgs) $(FlagParameterPrefix)NativeToolsOnMachine true @@ -9,7 +9,7 @@ - + diff --git a/repo-projects/wpf.proj b/repo-projects/wpf.proj index f77b34f28978..0d8e37d25c2a 100644 --- a/repo-projects/wpf.proj +++ b/repo-projects/wpf.proj @@ -6,6 +6,9 @@ $(BuildArgs) /p:Platform=$(TargetArchitecture) $(BuildArgs) /p:BuildWithNetFrameworkHostedCompiler=true + + + $(BuildArgs) /p:AfterMicrosoftNETSdkTargets=$(RepositoryEngineeringDir)vs-workaround.targets @@ -13,4 +16,8 @@ + + + + diff --git a/repo-projects/xdt.proj b/repo-projects/xdt.proj index b17ec80adf3d..0a7edcedbfe1 100644 --- a/repo-projects/xdt.proj +++ b/repo-projects/xdt.proj @@ -1,5 +1,10 @@ + + + true + + diff --git a/src/arcade/.azuredevops/dependabot.yml b/src/arcade/.azuredevops/dependabot.yml new file mode 100644 index 000000000000..f18e60565a42 --- /dev/null +++ b/src/arcade/.azuredevops/dependabot.yml @@ -0,0 +1,5 @@ +version: 2 + +# Disabling dependabot on Azure DevOps as this is a mirrored repo. Updates should go through github. +enable-campaigned-updates: false +enable-security-updates: false diff --git a/src/arcade/.config/dotnet-tools.json b/src/arcade/.config/dotnet-tools.json index 9feb133fc414..170a9e381796 100644 --- a/src/arcade/.config/dotnet-tools.json +++ b/src/arcade/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "microsoft.dnceng.secretmanager": { - "version": "1.1.0-beta.25381.1", + "version": "1.1.0-beta.25564.1", "commands": [ "secret-manager" ] diff --git a/src/arcade/.github/workflows/backport-base.yml b/src/arcade/.github/workflows/backport-base.yml index 22b1aaa16091..913b646e0fa4 100644 --- a/src/arcade/.github/workflows/backport-base.yml +++ b/src/arcade/.github/workflows/backport-base.yml @@ -19,6 +19,19 @@ on: required: false type: string default: 'dotnet,microsoft' + additional_git_am_switches: + description: 'Additional switches to pass to git am command (e.g., "--exclude=docs/release-notes/* --whitespace=fix"). Useful for excluding files that differ between branches or fixing whitespace conflicts.' + required: false + type: string + default: '' + conflict_resolution_command: + description: >- + Optional shell command to attempt automatic conflict resolution after a failed git am. + If the command eliminates merge conflicts, the backport proceeds automatically. + Do not run git commands from this parameter as they might have unintended side effects. + DO NOT PASS UNTRUSTED INPUT TO THIS PARAMETER. + required: false + type: string jobs: cleanup: @@ -64,7 +77,8 @@ jobs: with: script: | const target_branch = '${{ steps.target-branch-extractor.outputs.result }}'; - const backport_start_body = `Started backporting to _${target_branch}_: https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const workflow_run_url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const backport_start_body = `Started backporting to \`${target_branch}\` ([link to workflow run](${workflow_run_url}))`; await github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, @@ -80,6 +94,8 @@ jobs: env: BACKPORT_PR_TITLE_TEMPLATE: ${{ inputs.pr_title_template }} BACKPORT_PR_DESCRIPTION_TEMPLATE: ${{ inputs.pr_description_template }} + ADDITIONAL_GIT_AM_SWITCHES: ${{ inputs.additional_git_am_switches }} + CONFLICT_RESOLUTION_COMMAND: ${{ inputs.conflict_resolution_command }} with: script: | const target_branch = '${{ steps.target-branch-extractor.outputs.result }}'; @@ -87,19 +103,40 @@ jobs: const repo_name = context.payload.repository.name; const pr_number = context.payload.issue.number; const comment_user = context.payload.comment.user.login; + const workflow_run_url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const wrap_in_code_block = (language, content) => `\`\`\`${language}\n${content}\n\`\`\``; + const wrap_in_details_block = (summary, content) => `
\n${summary}\n\n${content}\n
`; + + // Post a comment on the PR and return the comment URL + async function postComment(body) { + const { data: comment } = await github.rest.issues.createComment({ + owner: repo_owner, + repo: repo_name, + issue_number: pr_number, + body + }); + return comment.html_url; + } try { - // verify the comment user is a repo collaborator + // verify the comment user has write access to the repo try { - await github.rest.repos.checkCollaborator({ + const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({ owner: repo_owner, repo: repo_name, username: comment_user }); - console.log(`Verified ${comment_user} is a repo collaborator.`); + + const writePermissions = ['admin', 'write']; + if (!writePermissions.includes(permission.permission)) { + throw new Error(`Insufficient permissions: ${permission.permission}`); + } + + console.log(`Verified ${comment_user} has ${permission.permission} access to the repo.`); } catch (error) { console.log(error); - throw new Error(`Error: @${comment_user} is not a repo collaborator, backporting is not allowed. If you're a collaborator please make sure your ${repo_owner} team membership visibility is set to Public on https://github.com/orgs/${repo_owner}/people?query=${comment_user}`); + throw new Error(`Error: @${comment_user} does not have write access to this repo, backporting is not allowed. Required permissions: write or admin.`); } try { await exec.exec(`git ls-remote --exit-code --heads origin ${target_branch}`) } catch { throw new Error(`Error: The specified backport target branch "${target_branch}" wasn't found in the repo.`); } @@ -127,9 +164,14 @@ jobs: } catch { } // download and apply patch - await exec.exec(`curl -sSL "${context.payload.issue.pull_request.patch_url}" --output changes.patch`); + const patch_file = 'changes.patch'; + await exec.exec(`curl -sSL "${context.payload.issue.pull_request.patch_url}" --output ${patch_file}`); - const git_am_command = "git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch"; + const additional_switches = process.env.ADDITIONAL_GIT_AM_SWITCHES?.trim() || ''; + const base_switches = '--3way --empty=keep --ignore-whitespace --keep-non-patch'; + const git_am_command = additional_switches + ? `git am ${base_switches} ${additional_switches} ${patch_file}` + : `git am ${base_switches} ${patch_file}`; let git_am_output = `$ ${git_am_command}\n\n`; let git_am_failed = false; try { @@ -145,21 +187,52 @@ jobs: } if (git_am_failed) { - const git_am_failed_body = `@${context.payload.comment.user.login} backporting to "${target_branch}" failed, the patch most likely resulted in conflicts:\n\n\`\`\`shell\n${git_am_output}\n\`\`\`\n\nPlease backport manually!`; - await github.rest.issues.createComment({ - owner: repo_owner, - repo: repo_name, - issue_number: pr_number, - body: git_am_failed_body - }); - core.setFailed("Error: git am failed, most likely due to a merge conflict."); - return; - } - else { - // push the temp branch to the repository - await exec.exec(`git push --force --set-upstream origin HEAD:${temp_branch}`); + const resolution_command = process.env.CONFLICT_RESOLUTION_COMMAND || ''; + + // If no resolution command supplied, fail immediately + if (resolution_command.trim().length === 0) { + const details = `${wrap_in_code_block('console', git_am_output)}\n[Link to workflow output](${workflow_run_url})`; + const git_am_failed_body = `@${comment_user} backporting to \`${target_branch}\` failed, the patch most likely resulted in conflicts. Please backport manually!\n${wrap_in_details_block('git am output', details)}`; + postComment(git_am_failed_body); + core.setFailed("git am failed, most likely due to a merge conflict."); + return; + } + + console.log(`git am failed; attempting in-session conflict resolution via provided command: ${resolution_command}`); + + // Run user-provided resolution command + // Ignore return code to capture stdout/stderr + const resolution_result = await exec.getExecOutput(`bash -c "${resolution_command}"`, [], { ignoreReturnCode: true }); + if (resolution_result.exitCode !== 0) { + const details = `\`${resolution_command}\` stderr:\n${wrap_in_code_block('console', resolution_result.stderr)}\n[Link to workflow output](${workflow_run_url})`; + const resolution_failed_body = `@${comment_user} backporting to \`${target_branch}\` failed during automated conflict resolution. Please backport manually!\n${wrap_in_details_block('Error details', details)}`; + postComment(resolution_failed_body); + core.setFailed(`Automated conflict resolution command exited with code ${resolution_result.exitCode}`); + return; + } + + // Stage changes (excluding patch file) + await exec.exec(`git add -A`); + await exec.exec(`git reset HEAD ${patch_file}`); + + // Check for remaining conflicts + const diff_command = 'git diff --name-only --diff-filter=U'; + const diff_result = await exec.getExecOutput(diff_command); + if (diff_result.stdout.trim().length !== 0) { + const details = `${wrap_in_code_block('console', diff_result.stdout)}\n[Link to workflow output](${workflow_run_url})`; + const conflicts_body = `@${comment_user} backporting to \`${target_branch}\` failed. Automated conflict resolution did not resolve all conflicts. Please backport manually!\n${wrap_in_details_block(`${diff_command} output`, details)}`; + postComment(conflicts_body); + core.setFailed(`Automated conflict resolution did not resolve all conflicts.`); + return; + } + + console.log('Automated conflict resolution resolved all merge conflicts. Continuing.'); + await exec.exec('git am --continue'); } + // push the temp branch to the repository + await exec.exec(`git push --force --set-upstream origin HEAD:${temp_branch}`); + if (!should_open_pull_request) { console.log("Backport temp branch already exists, skipping opening a PR."); return; @@ -200,19 +273,12 @@ jobs: console.log("Successfully opened the GitHub PR."); } catch (error) { - + const body = `@${comment_user} an error occurred while backporting to \`${target_branch}\`. See the [workflow output](${workflow_run_url}) for details.`; + const comment_url = await postComment(body); + console.log(`Posted comment: ${comment_url}`); core.setFailed(error); - - // post failure to GitHub comment - const unknown_error_body = `@${comment_user} an error occurred while backporting to "${target_branch}", please check the run log for details!\n\n${error.message}`; - await github.rest.issues.createComment({ - owner: repo_owner, - repo: repo_name, - issue_number: pr_number, - body: unknown_error_body - }); } - + - name: Re-lock PR comments uses: actions/github-script@v7 if: ${{ github.event.issue.locked == true && (success() || failure()) }} diff --git a/src/arcade/.github/workflows/inter-branch-merge-base.yml b/src/arcade/.github/workflows/inter-branch-merge-base.yml index 19ada5b48883..dee843a51b86 100644 --- a/src/arcade/.github/workflows/inter-branch-merge-base.yml +++ b/src/arcade/.github/workflows/inter-branch-merge-base.yml @@ -71,7 +71,7 @@ jobs: if: steps.extract-configuration-values.outputs.configurationFound != 'true' name: Read configuration status - - run: ${{ github.workspace }}\arcade-repository\.github\workflows\scripts\inter-branch-merge.ps1 -RepoName ${{ steps.fetch-repo-name.outputs.repository_name }} -RepoOwner ${{ github.repository_owner }} -MergeFromBranch $env:GITHUB_REF_NAME -MergeToBranch ${{ steps.extract-configuration-values.outputs.mergeToBranch }} ${{ steps.extract-configuration-values.outputs.mergeSwitchArguments }} + - run: ${{ github.workspace }}\arcade-repository\.github\workflows\scripts\inter-branch-merge.ps1 -RepoName ${{ steps.fetch-repo-name.outputs.repository_name }} -RepoOwner ${{ github.repository_owner }} -MergeFromBranch $env:GITHUB_REF_NAME -MergeToBranch ${{ steps.extract-configuration-values.outputs.mergeToBranch }} -ResetToTargetPaths "${{ steps.extract-configuration-values.outputs.resetToTargetPaths }}" ${{ steps.extract-configuration-values.outputs.mergeSwitchArguments }} if: steps.extract-configuration-values.outputs.configurationFound == 'true' name: Merge branches working-directory: ${{ github.workspace }}/repository diff --git a/src/arcade/.github/workflows/scripts/inter-branch-merge.ps1 b/src/arcade/.github/workflows/scripts/inter-branch-merge.ps1 index d1e251e09d64..c05c2c87895d 100644 --- a/src/arcade/.github/workflows/scripts/inter-branch-merge.ps1 +++ b/src/arcade/.github/workflows/scripts/inter-branch-merge.ps1 @@ -15,6 +15,10 @@ The current branch Create a PR even if the only commits are from dotnet-maestro[bot] .PARAMETER QuietComments Do not tag commiters, do not comment on PR updates. Reduces GitHub notifications +.PARAMETER ResetToTargetPaths +Semicolon-separated list of glob patterns for files to reset to the target branch version. +After the merge branch is created, files matching these patterns will be checked out from +the target branch and committed, resolving potential merge conflicts for these files. #> [CmdletBinding(SupportsShouldProcess = $true)] param( @@ -36,7 +40,10 @@ param( [switch]$AllowAutomatedCommits, - [switch]$QuietComments + [switch]$QuietComments, + + [Alias('r')] + [string]$ResetToTargetPaths = "" ) $ErrorActionPreference = 'stop' @@ -105,6 +112,70 @@ function GetCommitterGitHubName($sha) { return $null } +function ResetFilesToTargetBranch($patterns, $targetBranch) { + if (-not $patterns -or $patterns.Count -eq 0) { + return + } + + Write-Host "Resetting files to $targetBranch for patterns: $($patterns -join ', ')" + + # Verify the target branch exists + $branchExists = & git rev-parse --verify "origin/$targetBranch" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Warning "Target branch 'origin/$targetBranch' does not exist. Skipping file reset." + return + } + + # Configure git user for the commit + # Use GitHub Actions bot identity + Invoke-Block { & git config user.name "github-actions[bot]" } + Invoke-Block { & git config user.email "41898282+github-actions[bot]@users.noreply.github.com" } + + # Track which patterns had changes + $processedPatterns = @() + + foreach ($pattern in $patterns) { + $pattern = $pattern.Trim() + if (-not $pattern) { + continue + } + + Write-Host "Processing pattern: $pattern" + + # Use git checkout to reset files matching the pattern to the target branch + # The -- is needed to separate the revision from the pathspec + # Just attempt to checkout the pattern directly - git will handle whether files exist + try { + & git checkout "origin/$targetBranch" -- $pattern 2>&1 | Write-Host + if ($LASTEXITCODE -eq 0) { + Write-Host -f Green "Checked out pattern '$pattern' from $targetBranch" + $processedPatterns += $pattern + } else { + Write-Host -f Yellow "Pattern '$pattern' did not match any files in $targetBranch" + } + } + catch { + Write-Warning "Failed to checkout pattern '$pattern' from $targetBranch. Error: $_" + } + } + + # Check if there are any changes to commit after processing all patterns + $status = & git status --porcelain + if ($status -and $processedPatterns.Count -gt 0) { + # Add all changes (the checkout already modified the specific files) + Invoke-Block { & git add -A } + + # Create a commit message listing all patterns that were reset + $patternsList = $processedPatterns -join "`n- " + $commitMessage = "Reset files to $targetBranch`n`nReset patterns:`n- $patternsList" + + Invoke-Block { & git commit -m $commitMessage } + Write-Host -f Green "Successfully reset files to $targetBranch for patterns: $patternsList" + } else { + Write-Host "No changes to commit after processing all patterns" + } +} + # see https://git-scm.com/docs/pretty-formats $formatString = '%h %cn <%ce>: %s (%cr)' @@ -151,6 +222,12 @@ try { $mergeBranchName = "merge/$MergeFromBranch-to-$MergeToBranch" Invoke-Block { & git checkout -B $mergeBranchName } + # Reset specified files to target branch if ResetToTargetPaths is configured + if ($ResetToTargetPaths) { + $patterns = $ResetToTargetPaths -split ";" + ResetFilesToTargetBranch $patterns $MergeToBranch + } + $remoteName = 'origin' $prOwnerName = $RepoOwner $prRepoName = $RepoName diff --git a/src/arcade/.github/workflows/scripts/read-configuration.ps1 b/src/arcade/.github/workflows/scripts/read-configuration.ps1 index 1aeae31238e8..73e72d7c37bd 100644 --- a/src/arcade/.github/workflows/scripts/read-configuration.ps1 +++ b/src/arcade/.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 @@ -98,8 +98,15 @@ if ($configuration -ne $null) { $ExtraSwitches = $configuration['ExtraSwitches'] } + $ResetToTargetPaths = ""; + if($configuration.ContainsKey('ResetToTargetPaths')){ + # Convert array to semicolon-separated string for output + $ResetToTargetPaths = $configuration['ResetToTargetPaths'] -join ";" + } + "mergeSwitchArguments=$ExtraSwitches" | Out-File -FilePath $env:GITHUB_OUTPUT -Append "mergeToBranch=$MergeToBranch" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + "resetToTargetPaths=$ResetToTargetPaths" | Out-File -FilePath $env:GITHUB_OUTPUT -Append "configurationFound=$true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append } diff --git a/src/arcade/Arcade.slnx b/src/arcade/Arcade.slnx index cc381104afc9..337ee9c22791 100644 --- a/src/arcade/Arcade.slnx +++ b/src/arcade/Arcade.slnx @@ -29,6 +29,7 @@ + @@ -45,6 +46,7 @@ + @@ -71,7 +73,6 @@ - diff --git a/src/arcade/Directory.Build.targets b/src/arcade/Directory.Build.targets index 994499260938..1b8d3519ede2 100644 --- a/src/arcade/Directory.Build.targets +++ b/src/arcade/Directory.Build.targets @@ -4,4 +4,9 @@ + + + true + + diff --git a/src/arcade/Directory.Packages.props b/src/arcade/Directory.Packages.props index 3dc1678af592..a093ae1c0a59 100644 --- a/src/arcade/Directory.Packages.props +++ b/src/arcade/Directory.Packages.props @@ -11,11 +11,11 @@ 5.8.4 3.14.1-9323.2545153 - 5.0.2-dotnet.2737382 + 5.0.2-dotnet.2811440 2.9.3 - 3.0.0 + 3.1.0 1.22.0 $(XUnitVersion) 3.1.3 @@ -77,11 +77,11 @@ - - + + - + @@ -91,7 +91,7 @@ - + diff --git a/src/arcade/Documentation/ArcadeSdk.md b/src/arcade/Documentation/ArcadeSdk.md index 5ff86313be40..a5df38e66b25 100644 --- a/src/arcade/Documentation/ArcadeSdk.md +++ b/src/arcade/Documentation/ArcadeSdk.md @@ -702,7 +702,7 @@ The following task restores tools that are only available from internal feeds. feedsToUse: config restoreSolution: 'eng\common\internal\Tools.csproj' nugetConfigPath: 'eng\common\internal\NuGet.config' - restoreDirectory: '$(Build.SourcesDirectory)\.packages' + restoreDirectory: '$(System.DefaultWorkingDirectory)\.packages' ``` [The tools](https://github.com/dotnet/arcade/blob/master/eng/common/internal/Tools.csproj) are restored conditionally based on which Arcade SDK features the repository uses (these are specified via `UsingToolXxx` properties). @@ -745,7 +745,7 @@ The Build Pipeline needs to link the following variable group: - task: PublishBuildArtifacts@1 displayName: Publish Logs inputs: - PathtoPublish: '$(Build.SourcesDirectory)\artifacts\log\$(BuildConfiguration)' + PathtoPublish: '$(System.DefaultWorkingDirectory)\artifacts\log\$(BuildConfiguration)' ArtifactName: '$(OperatingSystemName) $(BuildConfiguration)' continueOnError: true condition: not(succeeded()) @@ -887,7 +887,7 @@ The following build definition steps are required for successful generation of a inputs: dropServiceURI: 'https://devdiv.artifacts.visualstudio.com' buildNumber: 'ProfilingInputs/DevDiv/$(Build.Repository.Name)/$(Build.SourceBranchName)/$(Build.BuildNumber)' - sourcePath: '$(Build.SourcesDirectory)\artifacts\OptProf\$(BuildConfiguration)\Data' + sourcePath: '$(System.DefaultWorkingDirectory)\artifacts\OptProf\$(BuildConfiguration)\Data' toLowerCase: false usePat: false displayName: 'OptProf - Publish to Artifact Services - ProfilingInputs' @@ -900,7 +900,7 @@ The following build definition steps are required for successful generation of a vsMajorVersion: $(VisualStudio.MajorVersion) channelName: $(VisualStudio.ChannelName) manifests: $(VisualStudio.SetupManifestList) - outputFolder: '$(Build.SourcesDirectory)\artifacts\VSSetup\$(BuildConfiguration)\Insertion' + outputFolder: '$(System.DefaultWorkingDirectory)\artifacts\VSSetup\$(BuildConfiguration)\Insertion' displayName: 'OptProf - Build VS bootstrapper' condition: succeeded() diff --git a/src/arcade/Documentation/AzureDevOps/PhaseToJobSchemaChange.md b/src/arcade/Documentation/AzureDevOps/PhaseToJobSchemaChange.md index 39cf0a9dde47..fbaec6a0c024 100644 --- a/src/arcade/Documentation/AzureDevOps/PhaseToJobSchemaChange.md +++ b/src/arcade/Documentation/AzureDevOps/PhaseToJobSchemaChange.md @@ -188,7 +188,7 @@ phases: - task: PublishBuildArtifacts@1 displayName: Publish Logs to VSTS inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' + PathtoPublish: '$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)' PublishLocation: Container ArtifactName: $(Agent.Os)_$(Agent.JobName) continueOnError: true @@ -276,7 +276,7 @@ phases: - task: PublishBuildArtifacts@1 displayName: Publish Logs to VSTS inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' + PathtoPublish: '$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)' PublishLocation: Container ArtifactName: $(Agent.Os)_$(Agent.JobName) continueOnError: true diff --git a/src/arcade/Documentation/AzureDevOps/SendingJobsToHelix.md b/src/arcade/Documentation/AzureDevOps/SendingJobsToHelix.md index bb39c2a9dc51..6b93e46321ac 100644 --- a/src/arcade/Documentation/AzureDevOps/SendingJobsToHelix.md +++ b/src/arcade/Documentation/AzureDevOps/SendingJobsToHelix.md @@ -100,7 +100,7 @@ The list of available Helix queues can be found on the [Helix homepage](https:// # HelixConfiguration: '' -- any property that you would like to attached to a job # HelixPreCommands: '' -- any commands that you would like to run prior to running your job # HelixPostCommands: '' -- any commands that you would like to run after running your job - XUnitProjects: $(Build.SourcesDirectory)/HelloTests/HelloTests.csproj # specify your xUnit projects (semicolon delimited) here! + XUnitProjects: $(System.DefaultWorkingDirectory)/HelloTests/HelloTests.csproj # specify your xUnit projects (semicolon delimited) here! # XUnitWorkItemTimeout: '00:05:00' -- a timeout (specified as a System.TimeSpan string) for all work items created from XUnitProjects XUnitPublishTargetFramework: netcoreapp3.1 # specify your publish target framework here XUnitRuntimeTargetFramework: netcoreapp2.0 # specify the framework you want to use for the xUnit runner diff --git a/src/arcade/Documentation/DependencyFlowOnboardingWithoutArcade.md b/src/arcade/Documentation/DependencyFlowOnboardingWithoutArcade.md index 1f31150624d0..7212c4ea9e37 100644 --- a/src/arcade/Documentation/DependencyFlowOnboardingWithoutArcade.md +++ b/src/arcade/Documentation/DependencyFlowOnboardingWithoutArcade.md @@ -78,7 +78,7 @@ If you only have one Azure DevOps job that publishes assets, then you can add th azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet diff --git a/src/arcade/Documentation/HowToAddPerfTestingToPipeline.md b/src/arcade/Documentation/HowToAddPerfTestingToPipeline.md index ec8c55009f0e..cb3155519e8d 100644 --- a/src/arcade/Documentation/HowToAddPerfTestingToPipeline.md +++ b/src/arcade/Documentation/HowToAddPerfTestingToPipeline.md @@ -103,7 +103,7 @@ Performance testing has been fully tested in coreclr. Coreclr, corefx and other # Test job depends on the corresponding build job dependsOn: build_Windows_NT_x64_Release - extraSetupParameters: -CoreRootDirectory $(Build.SourcesDirectory)\bin\tests\Windows_NT.x64.Release\Tests\Core_Root -Architecture x64 + extraSetupParameters: -CoreRootDirectory $(System.DefaultWorkingDirectory)\bin\tests\Windows_NT.x64.Release\Tests\Core_Root -Architecture x64 steps: # Download product build @@ -121,7 +121,7 @@ Performance testing has been fully tested in coreclr. Coreclr, corefx and other inputs: sourceFolder: $(System.ArtifactsDirectory)/Windows_NT_x64_Release_build contents: '**' - targetFolder: $(Build.SourcesDirectory)/bin/Product/Windows_NT.x64.Release + targetFolder: $(System.DefaultWorkingDirectory)/bin/Product/Windows_NT.x64.Release # Create Core_Root - script: build-test.cmd Release x64 skipmanaged skipnative diff --git a/src/arcade/Documentation/OneLocBuild.md b/src/arcade/Documentation/OneLocBuild.md index 404e47f2ae9d..c217dd48f1b3 100644 --- a/src/arcade/Documentation/OneLocBuild.md +++ b/src/arcade/Documentation/OneLocBuild.md @@ -182,7 +182,7 @@ The parameters that can be passed to the template are as follows: | **Parameter** | **Default Value** | **Notes** | |:-:|:-:|-| | `RepoType` | `'gitHub'` | Should be set to `'gitHub'` for GitHub-based repositories and `'azureDevOps'` for Azure DevOps-based ones. | -| `SourcesDirectory` | `$(Build.SourcesDirectory)` | This is the root directory for your repository source code. | +| `SourcesDirectory` | `$(System.DefaultWorkingDirectory)` | This is the root directory for your repository source code. | | `CreatePr` | `true` | When set to `true`, instructs the OneLocBuild task to make a PR back to the source repository containing the localized files. | | `AutoCompletePr` | `false` | When set to `true`, instructs the OneLocBuild task to autocomplete the created PR. Requires permissions to bypass any checks on the main branch. | | `ReusePr` | `true` | When set to `true`, instructs the OneLocBuild task to update an existing PR (if one exists) rather than open a new one to reduce PR noise. | diff --git a/src/arcade/Documentation/Policy/PowershellBestPractices.md b/src/arcade/Documentation/Policy/PowershellBestPractices.md index ff25d5a91ecc..27a9c9abe059 100644 --- a/src/arcade/Documentation/Policy/PowershellBestPractices.md +++ b/src/arcade/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/src/arcade/Documentation/Projects/Build Analysis/BuildRetryOnboard.md b/src/arcade/Documentation/Projects/Build Analysis/BuildRetryOnboard.md index 906873208140..8ed320140698 100644 --- a/src/arcade/Documentation/Projects/Build Analysis/BuildRetryOnboard.md +++ b/src/arcade/Documentation/Projects/Build Analysis/BuildRetryOnboard.md @@ -21,7 +21,7 @@ Ex. \eng\BuildConfiguration\build-configuration.json ``` - task: PublishPipelineArtifact@1 inputs: - targetPath: $(Build.SourcesDirectory)\eng\BuildConfiguration + targetPath: $(System.DefaultWorkingDirectory)\eng\BuildConfiguration artifactName: BuildConfiguration ``` diff --git a/src/arcade/Documentation/SBOMGenerationGuidance.md b/src/arcade/Documentation/SBOMGenerationGuidance.md index 9e9b5e28057b..f65f816cd3d2 100644 --- a/src/arcade/Documentation/SBOMGenerationGuidance.md +++ b/src/arcade/Documentation/SBOMGenerationGuidance.md @@ -78,7 +78,7 @@ Arcade configurations. The template allows customization of behavior via the fol - `ManifestDirPath`: Determines where in the build agent the SBOM will be generated to, defaults to `$(Build.ArtifactStagingDirectory)/sbom` - `BuildDropPath` : Determines the directory that the SBOM tooling will use to find build outputs. - Defaults to $`(Build.SourcesDirectory)/artifacts` to match Arcade's convention. + Defaults to $`(System.DefaultWorkingDirectory)/artifacts` to match Arcade's convention. - `sbomContinueOnError`: By default the tasks are set up to not break the build and instead continue on error if anything goes wrong in the generation process. @@ -246,7 +246,7 @@ for your release builds: ``` It means that your build outputs might not match with the expected location for Arcade: - `$(Build.SourcesDirectory)/Artifacts`. In this case you should modify the `BuildDropPath` + `$(System.DefaultWorkingDirectory)/Artifacts`. In this case you should modify the `BuildDropPath` parameter of the template to point to your build's output directory. - For any other problems with the tasks or templates, you can reach out to the [.NET Engineering diff --git a/src/arcade/NuGet.config b/src/arcade/NuGet.config index 98d5bfbc2460..f0da823560cb 100644 --- a/src/arcade/NuGet.config +++ b/src/arcade/NuGet.config @@ -14,6 +14,8 @@ + + @@ -50,12 +52,15 @@ + + + + + + + - - - - diff --git a/src/arcade/azure-pipelines-codeql.yml b/src/arcade/azure-pipelines-codeql.yml index 75f9c91e35d5..6e609a45668c 100644 --- a/src/arcade/azure-pipelines-codeql.yml +++ b/src/arcade/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/src/arcade/azure-pipelines-daily.yaml b/src/arcade/azure-pipelines-daily.yaml index a537ee89627c..357cc81d4882 100644 --- a/src/arcade/azure-pipelines-daily.yaml +++ b/src/arcade/azure-pipelines-daily.yaml @@ -27,9 +27,9 @@ stages: inputs: packageType: runtime version: 8.x - installationPath: $(Build.SourcesDirectory)/.dotnet + installationPath: $(System.DefaultWorkingDirectory)/.dotnet - - script: $(Build.SourcesDirectory)/.dotnet/dotnet.exe tool restore + - script: dotnet tool restore displayName: Restore dotnet tools - task: AzureCLI@2 diff --git a/src/arcade/azure-pipelines-pr.yml b/src/arcade/azure-pipelines-pr.yml index 8b20b6d280ba..4f2a75102bfb 100644 --- a/src/arcade/azure-pipelines-pr.yml +++ b/src/arcade/azure-pipelines-pr.yml @@ -3,9 +3,7 @@ trigger: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 + - release/*.0 exclude: - .github/* - Documentation/* @@ -20,9 +18,7 @@ pr: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 + - release/*.0 - templates paths: include: @@ -164,7 +160,7 @@ stages: /p:DotNetSymbolServerTokenSymWeb=DryRunPTA /p:PDBArtifactsDirectory='$(Build.ArtifactStagingDirectory)/PDBArtifacts/' /p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/' - /p:SymbolPublishingExclusionsFile='$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' + /p:SymbolPublishingExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SymbolPublishingExclusionsFile.txt' /p:Configuration=Release /p:PublishToMSDL=false - powershell: eng\common\build.ps1 @@ -174,8 +170,8 @@ stages: -restore -test -warnAsError $false - -projects $(Build.SourcesDirectory)\tests\UnitTests.proj - /bl:$(Build.SourcesDirectory)\artifacts\log\$(_BuildConfig)\Helix.binlog + -projects $(System.DefaultWorkingDirectory)\tests\UnitTests.proj + /bl:$(System.DefaultWorkingDirectory)\artifacts\log\$(_BuildConfig)\Helix.binlog /p:RestoreUsingNuGetTargets=false displayName: Run Helix Tests env: @@ -204,8 +200,8 @@ stages: --restore --test --warnAsError false - --projects $(Build.SourcesDirectory)/tests/UnitTests.proj - /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/Helix.binlog + --projects $(System.DefaultWorkingDirectory)/tests/UnitTests.proj + /bl:$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)/Helix.binlog /p:RestoreUsingNuGetTargets=false displayName: Run Helix Tests env: @@ -249,8 +245,8 @@ stages: -restore -test -warnAsError false - -projects $(Build.SourcesDirectory)/tests/XHarness.Apple.SimulatorTests.proj - /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/XHarness.Apple.Simulator.Tests.binlog + -projects $(System.DefaultWorkingDirectory)/tests/XHarness.Apple.SimulatorTests.proj + /bl:$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)/XHarness.Apple.Simulator.Tests.binlog /p:RestoreUsingNuGetTargets=false displayName: XHarness Apple Simulator Helix Testing env: @@ -279,8 +275,8 @@ stages: -restore -test -warnAsError false - -projects $(Build.SourcesDirectory)/tests/XHarness.Apple.DeviceTests.proj - /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/Helix.XHarness.Apple.Device.Tests.binlog + -projects $(System.DefaultWorkingDirectory)/tests/XHarness.Apple.DeviceTests.proj + /bl:$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)/Helix.XHarness.Apple.Device.Tests.binlog /p:RestoreUsingNuGetTargets=false displayName: XHarness Apple Device Helix Testing env: @@ -309,8 +305,8 @@ stages: -restore -test -warnAsError false - -projects $(Build.SourcesDirectory)/tests/XHarness.Android.SimulatorTests.proj - /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/Helix.XHarness.Android.Simulator.Tests.binlog + -projects $(System.DefaultWorkingDirectory)/tests/XHarness.Android.SimulatorTests.proj + /bl:$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)/Helix.XHarness.Android.Simulator.Tests.binlog /p:RestoreUsingNuGetTargets=false displayName: XHarness Android Helix Testing (Linux) env: @@ -338,8 +334,8 @@ stages: -restore -test -warnAsError $false - -projects $(Build.SourcesDirectory)\tests\XHarness.Android.DeviceTests.proj - /bl:$(Build.SourcesDirectory)\artifacts\log\$(_BuildConfig)\Helix.XHarness.Android.Device.Tests.binlog + -projects $(System.DefaultWorkingDirectory)\tests\XHarness.Android.DeviceTests.proj + /bl:$(System.DefaultWorkingDirectory)\artifacts\log\$(_BuildConfig)\Helix.XHarness.Android.Device.Tests.binlog /p:RestoreUsingNuGetTargets=false displayName: XHarness Android Helix Testing (Windows) env: diff --git a/src/arcade/azure-pipelines-unofficial.yml b/src/arcade/azure-pipelines-unofficial.yml new file mode 100644 index 000000000000..887f46d621d6 --- /dev/null +++ b/src/arcade/azure-pipelines-unofficial.yml @@ -0,0 +1,27 @@ +trigger: none + +variables: +- template: /eng/common-variables.yml@self + parameters: + signType: test +- template: /eng/common/templates-official/variables/pool-providers.yml@self + +resources: + containers: + - container: LinuxContainer + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-fpm-amd64 + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +extends: + template: v1/1ES.Unofficial.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: + name: $(DncEngInternalBuildPool) + image: windows.vs2019.amd64 + os: windows + + stages: + - template: /eng/build.yml@self diff --git a/src/arcade/azure-pipelines.yml b/src/arcade/azure-pipelines.yml index ce915aaac72c..373ee74dd6b2 100644 --- a/src/arcade/azure-pipelines.yml +++ b/src/arcade/azure-pipelines.yml @@ -3,9 +3,7 @@ trigger: branches: include: - main - - release/6.0 - - release/8.0 - - release/9.0 + - release/*.0 paths: include: - '*' @@ -24,6 +22,8 @@ pr: none variables: - template: /eng/common-variables.yml@self + parameters: + signType: real - template: /eng/common/templates-official/variables/pool-providers.yml@self resources: @@ -47,67 +47,13 @@ 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: - - stage: build - displayName: Build - jobs: - - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) }}: - - template: /eng/common/templates-official/job/onelocbuild.yml@self - parameters: - MirrorRepo: arcade - LclSource: lclFilesFromPackage - LclPackageId: 'LCL-JUNO-PROD-ARCADE' - - template: /eng/common/templates-official/jobs/jobs.yml@self - parameters: - artifacts: - publish: - artifacts: true - logs: true - manifests: true - enableMicrobuild: true - enableSourceIndex: true - enableSourceBuild: true - workspace: - clean: all - jobs: - - job: Windows_NT - timeoutInMinutes: 90 - strategy: - matrix: - Build_Release: - _BuildConfig: Release - preSteps: - - checkout: self - fetchDepth: 0 - clean: true - steps: - - script: eng\common\cibuild.cmd - -configuration $(_BuildConfig) - -prepareMachine - $(_InternalBuildArgs) - /p:Test=false - displayName: Windows Build / Publish - - - stage: ValidateSdk - displayName: Validate Arcade SDK - dependsOn: build - jobs: - - template: /eng/validate-sdk.yml@self - parameters: - buildArgs: -configuration $(_BuildConfig) - -prepareMachine - $(_InternalBuildArgs) - /p:Test=false - - - template: /eng/common/templates-official/post-build/post-build.yml@self + - template: /eng/build.yml@self parameters: - publishingInfraVersion: 3 - # 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 + ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: + oneLocEnabled: true + microbuildUseESRP: true diff --git a/src/arcade/eng/BuildTask.Packages.props b/src/arcade/eng/BuildTask.Packages.props index fd82c1e09415..d8f9f7900665 100644 --- a/src/arcade/eng/BuildTask.Packages.props +++ b/src/arcade/eng/BuildTask.Packages.props @@ -2,14 +2,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/arcade/eng/Version.Details.props b/src/arcade/eng/Version.Details.props index b1b6069ef9b8..bbe1689181ee 100644 --- a/src/arcade/eng/Version.Details.props +++ b/src/arcade/eng/Version.Details.props @@ -1,4 +1,3 @@ - 10.0.100-preview.4.25220.1 - 10.0.0-beta.25401.2 - 10.0.0-beta.25401.2 + 11.0.0-beta.25570.6 + 11.0.0-beta.25570.6 - 1.1.0-beta.25404.2 - 1.1.0-beta.25404.2 + 1.1.0-beta.25564.1 + 1.1.0-beta.25564.1 - 10.0.0-prerelease.25381.1 + 11.0.0-prerelease.25603.1 1.1.0-beta2-19575-01 1.1.0-beta2-19575-01 8.0.0-preview.24461.2 - - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 - 9.0.0-rc.2.24473.5 + + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 2.0.0-preview.1.24305.1 @@ -51,12 +50,16 @@ This file should be imported by eng/Versions.props 2.0.0-beta5.25210.1 - 1.1.0-beta.25381.1 + 1.1.0-beta.25564.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 + + 2.23.0 + + 13.0.3 @@ -81,7 +84,7 @@ This file should be imported by eng/Versions.props $(MicrosoftDiaSymReaderPdb2PdbPackageVersion) $(MicrosoftSymbolManifestGeneratorPackageVersion) - + $(MicrosoftBclAsyncInterfacesPackageVersion) $(MicrosoftExtensionsDependencyInjectionPackageVersion) $(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion) @@ -111,5 +114,9 @@ This file should be imported by eng/Versions.props $(MicrosoftBuildFrameworkPackageVersion) $(MicrosoftBuildTasksCorePackageVersion) $(MicrosoftBuildUtilitiesCorePackageVersion) + + $(MicrosoftApplicationInsightsPackageVersion) + + $(NewtonsoftJsonPackageVersion) diff --git a/src/arcade/eng/Version.Details.xml b/src/arcade/eng/Version.Details.xml index d4781abff02a..e43556c0c4a9 100644 --- a/src/arcade/eng/Version.Details.xml +++ b/src/arcade/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + @@ -20,25 +20,25 @@ https://github.com/dotnet/templating 43b5827697e501c442eb75ffff832cd4df2514fe - + https://github.com/dotnet/arcade - 40693ae2ee51e447f6ca96d07bc1ba779dcb9b9c + c1926cf647dd684194c2914f47f61ff534f030ac - + https://github.com/dotnet/arcade - 40693ae2ee51e447f6ca96d07bc1ba779dcb9b9c + c1926cf647dd684194c2914f47f61ff534f030ac - + https://github.com/dotnet/arcade-services - 412c9802fe722908764810f1b097dcc8307fc8ec + 526aa00e2a2533e0263e50f299d5525572c48c18 - + https://github.com/dotnet/arcade-services - 412c9802fe722908764810f1b097dcc8307fc8ec + 526aa00e2a2533e0263e50f299d5525572c48c18 - + https://github.com/dotnet/xharness - 889949a0c734be07795cedf04690f959b0cf00bc + 3df2923500447ee925d59f026c81720c7a9b4e4b https://github.com/dotnet/roslyn @@ -58,57 +58,57 @@ - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 https://github.com/dotnet/deployment-tools @@ -119,51 +119,59 @@ ef4c24166691977558e5312758df4313ab310dc0 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 https://github.com/dotnet/command-line-api e9b0511d7f1128e2bc3be7a658a2a4ea977e602d - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - - https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 990ebf52fc408ca45929fd176d2740675a67fab8 + + https://github.com/dotnet/runtime + d3981726bc8b0e179db50301daf9f22d42393096 - + https://github.com/dotnet/dnceng - 9f9162ee769829503e3efb7b7ec688de558ff63b + 79d5ba9594a98c05483843e8bc018e312bc5817b - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 + + https://github.com/microsoft/ApplicationInsights-dotnet + 2faa7e8b157a431daa2e71785d68abd5fa817b53 + + + https://github.com/JamesNK/Newtonsoft.Json + 0a2e291c0d9c0c7675d445703e51750363a549ef + diff --git a/src/arcade/eng/Versions.props b/src/arcade/eng/Versions.props index 50fe1932967b..fff57b61fc41 100644 --- a/src/arcade/eng/Versions.props +++ b/src/arcade/eng/Versions.props @@ -1,14 +1,15 @@ - + + + - - 10.0.0 + 11.0.0 beta - + + false - - true + 6.0.0 @@ -20,21 +21,20 @@ 4.5.5 6.0.1 + 9.0.0-beta.24223.1 4.3.0 - - 2.0.3 - 6.13.2 - 6.13.2 - 6.13.2 - 6.13.2 - 6.13.2 + 6.14.0 + 6.14.0 + 6.14.0 + 6.14.0 + 6.14.0 5.0.0 6.0.4 @@ -53,4 +53,5 @@ 17.5.0 + diff --git a/src/arcade/eng/build.yml b/src/arcade/eng/build.yml new file mode 100644 index 000000000000..2c7cb0452de5 --- /dev/null +++ b/src/arcade/eng/build.yml @@ -0,0 +1,73 @@ +parameters: +- name: oneLocEnabled + default: false + type: boolean +- name: microbuildUseESRP + default: false + type: boolean + +stages: +- stage: build + displayName: Build + jobs: + - ${{ if eq(parameters.oneLocEnabled, true) }}: + - template: /eng/common/templates-official/job/onelocbuild.yml@self + parameters: + MirrorRepo: arcade + LclSource: lclFilesFromPackage + LclPackageId: 'LCL-JUNO-PROD-ARCADE' + - template: /eng/common/templates-official/jobs/jobs.yml@self + parameters: + artifacts: + publish: + artifacts: true + logs: true + manifests: true + enableMicrobuild: true + microbuildUseESRP: ${{ parameters.microbuildUseESRP }} + enableSourceIndex: true + enableSourceBuild: true + workspace: + clean: all + jobs: + - job: Windows_NT + timeoutInMinutes: 90 + strategy: + matrix: + Build_Release: + _BuildConfig: Release + preSteps: + - checkout: self + fetchDepth: 0 + clean: true + steps: + - script: eng\common\cibuild.cmd + -configuration $(_BuildConfig) + -prepareMachine + $(_InternalBuildArgs) + /p:Test=false + displayName: Windows Build / Publish + +- stage: ValidateSdk + displayName: Validate Arcade SDK + dependsOn: build + jobs: + - template: /eng/validate-sdk.yml@self + parameters: + microbuildUseESRP: ${{ parameters.microbuildUseESRP }} + buildArgs: -configuration $(_BuildConfig) + -prepareMachine + $(_InternalBuildArgs) + /p:Test=false + +- template: /eng/common/templates-official/post-build/post-build.yml@self + parameters: + publishingInfraVersion: 3 + # 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/src/arcade/eng/common-variables.yml b/src/arcade/eng/common-variables.yml index 32cbb325ac39..4892a24959cd 100644 --- a/src/arcade/eng/common-variables.yml +++ b/src/arcade/eng/common-variables.yml @@ -1,3 +1,8 @@ +parameters: +- name: signType + default: Test + type: string + variables: # Cannot use key:value syntax in root defined variables - name: _TeamName @@ -17,7 +22,7 @@ variables: - name: _RunAsInternal value: True - name: _SignType - value: real + value: ${{ parameters.signType }} # Publish-Build-Assets provides: BotAccount-dotnet-maestro-bot-PAT # DotNet-HelixApi-Access provides: HelixApiAccessToken - group: Publish-Build-Assets @@ -25,7 +30,7 @@ variables: - group: SDL_Settings # DotNetPublishUsingPipelines can be removed when Arcade itself consumes a new Arcade version. - name: _InternalBuildArgs - value: /p:DotNetSignType=$(_SignType) + value: /p:DotNetSignType=${{ parameters.signType }} /p:TeamName=$(_TeamName) /p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER) diff --git a/src/arcade/eng/common/SetupNugetSources.ps1 b/src/arcade/eng/common/SetupNugetSources.ps1 index 5db4ad71ee2f..fc8d618014e0 100644 --- a/src/arcade/eng/common/SetupNugetSources.ps1 +++ b/src/arcade/eng/common/SetupNugetSources.ps1 @@ -7,11 +7,11 @@ # 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: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 +# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token # env: # Token: $(dn-bot-dnceng-artifact-feeds-rw) # @@ -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') +$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/src/arcade/eng/common/SetupNugetSources.sh b/src/arcade/eng/common/SetupNugetSources.sh index 4604b61b0323..b97cc536379d 100755 --- a/src/arcade/eng/common/SetupNugetSources.sh +++ b/src/arcade/eng/common/SetupNugetSources.sh @@ -11,8 +11,8 @@ # - task: Bash@3 # displayName: Setup Internal Feeds # inputs: -# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh -# arguments: $(Build.SourcesDirectory)/NuGet.config +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh +# arguments: $(System.DefaultWorkingDirectory)/NuGet.config # condition: ne(variables['Agent.OS'], 'Windows_NT') # - task: NuGetAuthenticate@1 # @@ -52,78 +52,124 @@ 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 (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") + 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 -DotNetVersions=('5' '6' '7' '8' '9') +# 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 +185,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/arcade/eng/common/build.sh b/src/arcade/eng/common/build.sh index 9767bb411a4f..ec3e80d189ea 100755 --- a/src/arcade/eng/common/build.sh +++ b/src/arcade/eng/common/build.sh @@ -92,7 +92,7 @@ runtime_source_feed='' runtime_source_feed_key='' properties=() -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -help|-h) diff --git a/src/arcade/eng/common/core-templates/job/job.yml b/src/arcade/eng/common/core-templates/job/job.yml index d9013251542c..748c4f07a64d 100644 --- a/src/arcade/eng/common/core-templates/job/job.yml +++ b/src/arcade/eng/common/core-templates/job/job.yml @@ -19,6 +19,8 @@ parameters: # publishing defaults artifacts: '' enableMicrobuild: false + enablePreviewMicrobuild: false + microbuildPluginVersion: 'latest' enableMicrobuildForMacAndLinux: false microbuildUseESRP: true enablePublishBuildArtifacts: false @@ -71,6 +73,8 @@ jobs: templateContext: ${{ parameters.templateContext }} variables: + - name: AllowPtrToDetectTestRunRetryFiles + value: true - ${{ if ne(parameters.enableTelemetry, 'false') }}: - name: DOTNET_CLI_TELEMETRY_PROFILE value: '$(Build.Repository.Uri)' @@ -128,6 +132,8 @@ jobs: - template: /eng/common/core-templates/steps/install-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} microbuildUseESRP: ${{ parameters.microbuildUseESRP }} continueOnError: ${{ parameters.continueOnError }} @@ -153,6 +159,8 @@ jobs: - template: /eng/common/core-templates/steps/cleanup-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} continueOnError: ${{ parameters.continueOnError }} @@ -163,7 +171,7 @@ jobs: inputs: testResultsFormat: 'xUnit' testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -174,7 +182,7 @@ jobs: inputs: testResultsFormat: 'VSTest' testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -218,7 +226,7 @@ jobs: - task: CopyFiles@2 displayName: Gather buildconfiguration for build retry inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/common/BuildConfiguration' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' continueOnError: true diff --git a/src/arcade/eng/common/core-templates/job/onelocbuild.yml b/src/arcade/eng/common/core-templates/job/onelocbuild.yml index 8bf7d23355bc..c5788829a872 100644 --- a/src/arcade/eng/common/core-templates/job/onelocbuild.yml +++ b/src/arcade/eng/common/core-templates/job/onelocbuild.yml @@ -8,7 +8,7 @@ parameters: CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - SourcesDirectory: $(Build.SourcesDirectory) + SourcesDirectory: $(System.DefaultWorkingDirectory) CreatePr: true AutoCompletePr: false ReusePr: true @@ -68,7 +68,7 @@ jobs: - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - task: Powershell@2 inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/generate-locproject.ps1 arguments: $(_GenerateLocProjectArguments) displayName: Generate LocProject.json condition: ${{ parameters.condition }} @@ -103,7 +103,7 @@ jobs: - task: CopyFiles@2 displayName: Copy LocProject.json inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/Localize/' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/Localize/' Contents: 'LocProject.json' TargetFolder: '$(Build.ArtifactStagingDirectory)/loc' condition: ${{ parameters.condition }} diff --git a/src/arcade/eng/common/core-templates/job/publish-build-assets.yml b/src/arcade/eng/common/core-templates/job/publish-build-assets.yml index d5303229c97e..8b5c635fe807 100644 --- a/src/arcade/eng/common/core-templates/job/publish-build-assets.yml +++ b/src/arcade/eng/common/core-templates/job/publish-build-assets.yml @@ -38,6 +38,10 @@ parameters: # Optional: A minimatch pattern for the asset manifests to publish to BAR assetManifestsPattern: '*/manifests/**/*.xml' + repositoryAlias: self + + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -60,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) @@ -78,12 +87,12 @@ jobs: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - checkout: self + - checkout: ${{ parameters.repositoryAlias }} fetchDepth: 3 clean: true - - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: - - ${{ if eq(parameters.publishingVersion, 3) }}: + - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: + - ${{ if eq(parameters.publishingVersion, 3) }}: - task: DownloadPipelineArtifact@2 displayName: Download Asset Manifests inputs: @@ -108,24 +117,35 @@ jobs: flattenFolders: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - 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: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' + condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: powershell@2 displayName: Create ReleaseConfigs Artifact inputs: @@ -137,7 +157,7 @@ jobs: Add-Content -Path $filePath -Value "$(DefaultChannels)" Add-Content -Path $filePath -Value $(IsStableBuild) - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + $symbolExclusionfile = "$(System.DefaultWorkingDirectory)/eng/SymbolPublishingExclusionsFile.txt" if (Test-Path -Path $symbolExclusionfile) { Write-Host "SymbolExclusionFile exists" @@ -153,7 +173,7 @@ jobs: artifactName: AssetManifests displayName: 'Publish Merged Manifest' retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + sbomEnabled: false # we don't need SBOM for logs - template: /eng/common/core-templates/steps/publish-build-artifacts.yml parameters: @@ -170,6 +190,11 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} 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 @@ -177,7 +202,7 @@ jobs: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion 3 @@ -186,9 +211,11 @@ 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 parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - JobLabel: 'Publish_Artifacts_Logs' + JobLabel: 'Publish_Artifacts_Logs' diff --git a/src/arcade/eng/common/core-templates/job/source-build.yml b/src/arcade/eng/common/core-templates/job/source-build.yml index d805d5faeb94..9d820f974211 100644 --- a/src/arcade/eng/common/core-templates/job/source-build.yml +++ b/src/arcade/eng/common/core-templates/job/source-build.yml @@ -60,10 +60,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.2004.amd64 + demands: ImageOverride -equals build.ubuntu.2204.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: 1es-azurelinux-3 os: linux ${{ else }}: pool: diff --git a/src/arcade/eng/common/core-templates/job/source-index-stage1.yml b/src/arcade/eng/common/core-templates/job/source-index-stage1.yml index 30530359a5d6..76baf5c27258 100644 --- a/src/arcade/eng/common/core-templates/job/source-index-stage1.yml +++ b/src/arcade/eng/common/core-templates/job/source-index-stage1.yml @@ -3,7 +3,7 @@ parameters: sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] binlogPath: artifacts/log/Debug/Build.binlog - condition: '' + condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') dependsOn: '' pool: '' is1ESPipeline: '' @@ -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, '') }}: @@ -41,4 +41,4 @@ jobs: - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml parameters: - binLogPath: ${{ parameters.binLogPath }} \ No newline at end of file + binLogPath: ${{ parameters.binLogPath }} diff --git a/src/arcade/eng/common/core-templates/jobs/codeql-build.yml b/src/arcade/eng/common/core-templates/jobs/codeql-build.yml index 693b00b37044..dbc14ac580a2 100644 --- a/src/arcade/eng/common/core-templates/jobs/codeql-build.yml +++ b/src/arcade/eng/common/core-templates/jobs/codeql-build.yml @@ -24,7 +24,7 @@ jobs: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config - name: GuardianVersion value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} diff --git a/src/arcade/eng/common/core-templates/jobs/jobs.yml b/src/arcade/eng/common/core-templates/jobs/jobs.yml index 2f992b2c6ecc..01ada7476651 100644 --- a/src/arcade/eng/common/core-templates/jobs/jobs.yml +++ b/src/arcade/eng/common/core-templates/jobs/jobs.yml @@ -43,6 +43,8 @@ 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. @@ -114,3 +116,5 @@ jobs: enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/src/arcade/eng/common/core-templates/post-build/post-build.yml b/src/arcade/eng/common/core-templates/post-build/post-build.yml index a151fd811e3e..06864cd1feb8 100644 --- a/src/arcade/eng/common/core-templates/post-build/post-build.yml +++ b/src/arcade/eng/common/core-templates/post-build/post-build.yml @@ -1,106 +1,106 @@ parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: requireDefaultChannels - displayName: Fail the build if there are no default channel(s) registrations for the current build - type: boolean - default: false - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - - name: isAssetlessBuild - type: boolean - displayName: Is Assetless Build - default: false - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - - - name: is1ESPipeline - type: boolean - default: false +# Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. +# Publishing V1 is no longer supported +# Publishing V2 is no longer supported +# Publishing V3 is the default +- name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + +- name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + +- name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + +- name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + +- name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + +- name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + +- name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + +- name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + +- name: requireDefaultChannels + displayName: Fail the build if there are no default channel(s) registrations for the current build + type: boolean + default: false + +- name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + +- name: isAssetlessBuild + type: boolean + displayName: Is Assetless Build + default: false + +# These parameters let the user customize the call to sdk-task.ps1 for publishing +# symbols & general artifacts as well as for signing validation +- name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + +- name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + +- name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + +# Which stages should finish execution before post-build stages start +- name: validateDependsOn + type: object + default: + - build + +- name: publishDependsOn + type: object + default: + - Validate + +# Optional: Call asset publishing rather than running in a separate stage +- name: publishAssetsImmediately + type: boolean + default: false + +- name: is1ESPipeline + type: boolean + default: false stages: - ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: @@ -108,10 +108,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Validate Build Assets variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: NuGet Validation @@ -127,35 +127,35 @@ 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 - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - job: displayName: Signing Validation @@ -169,54 +169,54 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - # 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 - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: /eng/common/core-templates/steps/publish-logs.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + # 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 + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) - job: displayName: SourceLink Validation @@ -230,41 +230,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/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 + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + 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 - ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - stage: publish_using_darc @@ -274,10 +274,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Publish using Darc variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: Publish Using Darc @@ -291,30 +291,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal image: windows.vs2019.amd64 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - task: NuGetAuthenticate@1 - - - task: AzureCLI@2 - displayName: Publish Using Darc - inputs: - azureSubscription: "Darc: Maestro Production" - scriptType: ps - scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: > + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - 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 + + - task: UseDotNet@2 + inputs: + version: 8.0.x + + - task: AzureCLI@2 + displayName: Publish Using Darc + inputs: + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(System.AccessToken)' @@ -323,3 +334,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/src/arcade/eng/common/core-templates/post-build/setup-maestro-vars.yml b/src/arcade/eng/common/core-templates/post-build/setup-maestro-vars.yml index f7602980dbe7..a7abd58c4bb6 100644 --- a/src/arcade/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/src/arcade/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -36,7 +36,7 @@ steps: $AzureDevOpsBuildId = $Env:Build_BuildId } else { - . $(Build.SourcesDirectory)\eng\common\tools.ps1 + . $(System.DefaultWorkingDirectory)\eng\common\tools.ps1 $darc = Get-Darc $buildInfo = & $darc get-build ` --id ${{ parameters.BARBuildId }} ` diff --git a/src/arcade/eng/common/core-templates/steps/enable-internal-sources.yml b/src/arcade/eng/common/core-templates/steps/enable-internal-sources.yml index 64f881bffc3c..4085512b6909 100644 --- a/src/arcade/eng/common/core-templates/steps/enable-internal-sources.yml +++ b/src/arcade/eng/common/core-templates/steps/enable-internal-sources.yml @@ -17,8 +17,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token env: Token: ${{ parameters.legacyCredential }} # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. @@ -29,8 +29,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config - ${{ else }}: - template: /eng/common/templates/steps/get-federated-access-token.yml parameters: @@ -39,8 +39,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) # This is required in certain scenarios to install the ADO credential provider. # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others # (e.g. dotnet msbuild). diff --git a/src/arcade/eng/common/core-templates/steps/generate-sbom.yml b/src/arcade/eng/common/core-templates/steps/generate-sbom.yml index 44a9636cdff9..003f7eae0fa5 100644 --- a/src/arcade/eng/common/core-templates/steps/generate-sbom.yml +++ b/src/arcade/eng/common/core-templates/steps/generate-sbom.yml @@ -5,8 +5,8 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageVersion: 11.0.0 + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom IgnoreDirectories: '' diff --git a/src/arcade/eng/common/core-templates/steps/install-microbuild-impl.yml b/src/arcade/eng/common/core-templates/steps/install-microbuild-impl.yml new file mode 100644 index 000000000000..da22beb3f60c --- /dev/null +++ b/src/arcade/eng/common/core-templates/steps/install-microbuild-impl.yml @@ -0,0 +1,34 @@ +parameters: + - name: microbuildTaskInputs + type: object + default: {} + + - name: microbuildEnv + type: object + default: {} + + - name: enablePreviewMicrobuild + type: boolean + default: false + + - name: condition + type: string + + - name: continueOnError + type: boolean + +steps: +- ${{ if eq(parameters.enablePreviewMicrobuild, true) }}: + - task: MicroBuildSigningPluginPreview@4 + displayName: Install Preview MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} +- ${{ else }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} diff --git a/src/arcade/eng/common/core-templates/steps/install-microbuild.yml b/src/arcade/eng/common/core-templates/steps/install-microbuild.yml index da30e67bc34c..4f4b56ed2a6b 100644 --- a/src/arcade/eng/common/core-templates/steps/install-microbuild.yml +++ b/src/arcade/eng/common/core-templates/steps/install-microbuild.yml @@ -4,6 +4,8 @@ parameters: # Enable install tasks for MicroBuild on Mac and Linux # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT' enableMicrobuildForMacAndLinux: false + # Enable preview version of MB signing plugin + enablePreviewMicrobuild: false # Determines whether the ESRP service connection information should be passed to the signing plugin. # This overlaps with _SignType to some degree. We only need the service connection for real signing. # It's important that the service connection not be passed to the MicroBuildSigningPlugin task in this place. @@ -11,8 +13,10 @@ 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 - microBuildOutputFolder: '$(Build.SourcesDirectory)' + # Microbuild installation directory + microBuildOutputFolder: $(Agent.TempDirectory)/MicroBuild + # Microbuild version + microbuildPluginVersion: 'latest' continueOnError: false @@ -25,8 +29,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: | @@ -46,35 +69,50 @@ steps: displayName: 'Validate ESRP usage (Non-Windows)' condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - ${{ if eq(parameters.microbuildUseESRP, true) }}: - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}: - azureSubscription: 'MicroBuild Signing Task (DevDiv)' - useEsrpCli: true - ${{ elseif eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea - ${{ else }}: - ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - continueOnError: ${{ parameters.continueOnError }} - condition: and( - succeeded(), - or( - and( - eq(variables['Agent.Os'], 'Windows_NT'), - in(variables['_SignType'], 'real', 'test') - ), - and( - ${{ eq(parameters.enableMicrobuildForMacAndLinux, true) }}, - ne(variables['Agent.Os'], 'Windows_NT'), - eq(variables['_SignType'], 'real') - ) - )) + # Two different MB install steps. This is due to not being able to use the agent OS during + # YAML expansion, and Windows vs. Linux/Mac uses different service connections. However, + # we can avoid including the MB install step if not enabled at all. This avoids a bunch of + # extra pipeline authorizations, since most pipelines do not sign on non-Windows. + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea + ${{ else }}: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca + microbuildEnv: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) + + - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + workingDirectory: ${{ parameters.microBuildOutputFolder }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ${{ else }}: + ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc + microbuildEnv: + TeamName: $(_TeamName) + 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/arcade/eng/common/core-templates/steps/publish-logs.yml b/src/arcade/eng/common/core-templates/steps/publish-logs.yml index de24d0087c58..5a927b4c7bcb 100644 --- a/src/arcade/eng/common/core-templates/steps/publish-logs.yml +++ b/src/arcade/eng/common/core-templates/steps/publish-logs.yml @@ -12,22 +12,24 @@ steps: inputs: targetType: inline script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + New-Item -ItemType Directory $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(System.DefaultWorkingDirectory)/artifacts/log/Debug/* $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ continueOnError: true condition: always() - task: PowerShell@2 displayName: Redact Logs inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/redact-logs.ps1 # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml - # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # Sensitive data can as well be added to $(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' # 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 '$(Build.SourcesDirectory)/PostBuildLogs' - -BinlogToolVersion ${{parameters.BinlogToolVersion}} - -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + arguments: -InputPath '$(System.DefaultWorkingDirectory)/PostBuildLogs' + -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)' @@ -44,7 +46,7 @@ steps: - task: CopyFiles@2 displayName: Gather post build logs inputs: - SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + SourceFolder: '$(System.DefaultWorkingDirectory)/PostBuildLogs' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' condition: always() diff --git a/src/arcade/eng/common/core-templates/steps/source-index-stage1-publish.yml b/src/arcade/eng/common/core-templates/steps/source-index-stage1-publish.yml index c2917c1efc1c..3ad83b8c3075 100644 --- a/src/arcade/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/src/arcade/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250425.2 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250515.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog @@ -14,13 +14,13 @@ steps: workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: "Source Index: Download netsourceindex Tools" # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) -- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output +- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(System.DefaultWorkingDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output displayName: "Source Index: Process Binlog into indexable sln" - ${{ if and(ne(parameters.runAsPublic, 'true'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: diff --git a/src/arcade/eng/common/cross/build-rootfs.sh b/src/arcade/eng/common/cross/build-rootfs.sh index 8abfb71f7275..9b7eede4e50f 100755 --- a/src/arcade/eng/common/cross/build-rootfs.sh +++ b/src/arcade/eng/common/cross/build-rootfs.sh @@ -72,7 +72,7 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.4-RELEASE" +__FreeBSDBase="13.5-RELEASE" __FreeBSDPkg="1.21.3" __FreeBSDABI="13" __FreeBSDPackages="libunwind" @@ -383,7 +383,7 @@ while :; do ;; freebsd14) __CodeName=freebsd - __FreeBSDBase="14.2-RELEASE" + __FreeBSDBase="14.3-RELEASE" __FreeBSDABI="14" __SkipUnmount=1 ;; diff --git a/src/arcade/eng/common/darc-init.sh b/src/arcade/eng/common/darc-init.sh index e889f439b8dc..9f5ad6b763b5 100755 --- a/src/arcade/eng/common/darc-init.sh +++ b/src/arcade/eng/common/darc-init.sh @@ -5,7 +5,7 @@ darcVersion='' versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2020-02-20' verbosity='minimal' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --darcversion) diff --git a/src/arcade/eng/common/dotnet-install.sh b/src/arcade/eng/common/dotnet-install.sh index 7b9d97e3bd4d..61f302bb6775 100755 --- a/src/arcade/eng/common/dotnet-install.sh +++ b/src/arcade/eng/common/dotnet-install.sh @@ -18,7 +18,7 @@ architecture='' runtime='dotnet' runtimeSourceFeed='' runtimeSourceFeedKey='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in -version|-v) diff --git a/src/arcade/eng/common/dotnet.sh b/src/arcade/eng/common/dotnet.sh index 2ef68235675f..f6d24871c1d4 100755 --- a/src/arcade/eng/common/dotnet.sh +++ b/src/arcade/eng/common/dotnet.sh @@ -19,7 +19,7 @@ source $scriptroot/tools.sh InitializeDotNetCli true # install # Invoke acquired SDK with args if they are provided -if [[ $# > 0 ]]; then +if [[ $# -gt 0 ]]; then __dotnetDir=${_InitializeDotNetCli} dotnetPath=${__dotnetDir}/dotnet ${dotnetPath} "$@" diff --git a/src/arcade/eng/common/generate-locproject.ps1 b/src/arcade/eng/common/generate-locproject.ps1 index 524aaa57f2b7..fa1cdc2b3007 100644 --- a/src/arcade/eng/common/generate-locproject.ps1 +++ b/src/arcade/eng/common/generate-locproject.ps1 @@ -33,15 +33,27 @@ $jsonTemplateFiles | ForEach-Object { $jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$wxlFilesV3 = @() +$wxlFilesV5 = @() $wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them if (-not $wxlFiles) { $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files if ($wxlEnFiles) { - $wxlFiles = @() - $wxlEnFiles | ForEach-Object { - $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" - $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru - } + $wxlFiles = @() + $wxlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $content = Get-Content $_.FullName -Raw + + # Split files on schema to select different parser settings in the generated project. + if ($content -like "*http://wixtoolset.org/schemas/v4/wxl*") + { + $wxlFilesV5 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + elseif ($content -like "*http://schemas.microsoft.com/wix/2006/localization*") + { + $wxlFilesV3 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + } } } @@ -114,7 +126,32 @@ $locJson = @{ CloneLanguageSet = "WiX_CloneLanguages" LssFiles = @( "wxl_loc.lss" ) LocItems = @( - $wxlFiles | ForEach-Object { + $wxlFilesV3 | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "WiX_CloneLanguages" + LssFiles = @( "P210WxlSchemaV4.lss" ) + LocItems = @( + $wxlFilesV5 | ForEach-Object { $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" $continue = $true foreach ($exclusion in $exclusions.Exclusions) { diff --git a/src/arcade/eng/common/generate-sbom-prep.sh b/src/arcade/eng/common/generate-sbom-prep.sh old mode 100755 new mode 100644 diff --git a/src/arcade/eng/common/internal-feed-operations.ps1 b/src/arcade/eng/common/internal-feed-operations.ps1 index 92b77347d990..c282d3ae403a 100644 --- a/src/arcade/eng/common/internal-feed-operations.ps1 +++ b/src/arcade/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/src/arcade/eng/common/internal-feed-operations.sh b/src/arcade/eng/common/internal-feed-operations.sh index 9378223ba095..6299e7effd4c 100755 --- a/src/arcade/eng/common/internal-feed-operations.sh +++ b/src/arcade/eng/common/internal-feed-operations.sh @@ -100,7 +100,7 @@ operation='' authToken='' repoName='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --operation) diff --git a/src/arcade/eng/common/native/install-dependencies.sh b/src/arcade/eng/common/native/install-dependencies.sh index 477a44f335be..11f81cbd40d4 100755 --- a/src/arcade/eng/common/native/install-dependencies.sh +++ b/src/arcade/eng/common/native/install-dependencies.sh @@ -27,9 +27,11 @@ case "$os" in libssl-dev libkrb5-dev pigz cpio localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 - elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then + elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ] || [ "$ID" = "centos" ]; then pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)" $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio + elif [ "$ID" = "amzn" ]; then + dnf install -y cmake llvm lld lldb clang python libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio elif [ "$ID" = "alpine" ]; then apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio else diff --git a/src/arcade/eng/common/post-build/nuget-verification.ps1 b/src/arcade/eng/common/post-build/nuget-verification.ps1 index a365194a9389..eea88e653c91 100644 --- a/src/arcade/eng/common/post-build/nuget-verification.ps1 +++ b/src/arcade/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 @@ -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/src/arcade/eng/common/post-build/publish-using-darc.ps1 b/src/arcade/eng/common/post-build/publish-using-darc.ps1 index 1eda208a3bbf..48e55598bdd2 100644 --- a/src/arcade/eng/common/post-build/publish-using-darc.ps1 +++ b/src/arcade/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/src/arcade/eng/common/post-build/redact-logs.ps1 b/src/arcade/eng/common/post-build/redact-logs.ps1 index b7fc19591507..fc0218a013d1 100644 --- a/src/arcade/eng/common/post-build/redact-logs.ps1 +++ b/src/arcade/eng/common/post-build/redact-logs.ps1 @@ -7,7 +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 { diff --git a/src/arcade/eng/common/sdk-task.ps1 b/src/arcade/eng/common/sdk-task.ps1 index a9d2a2d26996..b64b66a6275b 100644 --- a/src/arcade/eng/common/sdk-task.ps1 +++ b/src/arcade/eng/common/sdk-task.ps1 @@ -7,13 +7,16 @@ Param( [switch] $restore, [switch] $prepareMachine, [switch][Alias('nobl')]$excludeCIBinaryLog, + [switch]$noWarnAsError, [switch] $help, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) $ci = $true $binaryLog = if ($excludeCIBinaryLog) { $false } else { $true } -$warnAsError = $true +$warnAsError = if ($noWarnAsError) { $false } else { $true } . $PSScriptRoot\tools.ps1 @@ -67,7 +70,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.13.0" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "18.0.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/src/arcade/eng/common/sdk-task.sh b/src/arcade/eng/common/sdk-task.sh index 2f83adc0269f..3270f83fa9a7 100755 --- a/src/arcade/eng/common/sdk-task.sh +++ b/src/arcade/eng/common/sdk-task.sh @@ -10,6 +10,7 @@ show_usage() { echo "Advanced settings:" echo " --excludeCIBinarylog Don't output binary log (short: -nobl)" + echo " --noWarnAsError Do not warn as error" echo "" echo "Command line arguments not listed above are passed thru to msbuild." } @@ -52,6 +53,7 @@ exclude_ci_binary_log=false restore=false help=false properties='' +warnAsError=true while (($# > 0)); do lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" @@ -73,6 +75,10 @@ while (($# > 0)); do exclude_ci_binary_log=true shift 1 ;; + --noWarnAsError) + warnAsError=false + shift 1 + ;; --help) help=true shift 1 @@ -85,7 +91,6 @@ while (($# > 0)); do done ci=true -warnAsError=true if $help; then show_usage diff --git a/src/arcade/eng/common/template-guidance.md b/src/arcade/eng/common/template-guidance.md index 98bbc1ded0ba..4bf4cf41bd7c 100644 --- a/src/arcade/eng/common/template-guidance.md +++ b/src/arcade/eng/common/template-guidance.md @@ -50,7 +50,7 @@ extends: - task: CopyFiles@2 displayName: Gather build output inputs: - SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + SourceFolder: '$(System.DefaultWorkingDirectory)/artifacts/marvel' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' ``` diff --git a/src/arcade/eng/common/templates-official/job/job.yml b/src/arcade/eng/common/templates-official/job/job.yml index a8a943287458..92a0664f5647 100644 --- a/src/arcade/eng/common/templates-official/job/job.yml +++ b/src/arcade/eng/common/templates-official/job/job.yml @@ -3,7 +3,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml diff --git a/src/arcade/eng/common/templates-official/variables/sdl-variables.yml b/src/arcade/eng/common/templates-official/variables/sdl-variables.yml index dbdd66d4a4b3..f1311bbb1b33 100644 --- a/src/arcade/eng/common/templates-official/variables/sdl-variables.yml +++ b/src/arcade/eng/common/templates-official/variables/sdl-variables.yml @@ -4,4 +4,4 @@ variables: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/src/arcade/eng/common/templates/job/job.yml b/src/arcade/eng/common/templates/job/job.yml index 7cbf668c22bc..238fa0818f7b 100644 --- a/src/arcade/eng/common/templates/job/job.yml +++ b/src/arcade/eng/common/templates/job/job.yml @@ -6,7 +6,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml @@ -77,7 +77,7 @@ jobs: parameters: is1ESPipeline: false args: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + targetPath: '$(System.DefaultWorkingDirectory)\eng\common\BuildConfiguration' artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true diff --git a/src/arcade/eng/common/tools.ps1 b/src/arcade/eng/common/tools.ps1 index d4cfd9ccd806..f6bde2683794 100644 --- a/src/arcade/eng/common/tools.ps1 +++ b/src/arcade/eng/common/tools.ps1 @@ -157,9 +157,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { return $global:_DotNetInstallDir } - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - $env:DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we do not need all ASP.NET packages restored. $env:DOTNET_NOLOGO=1 @@ -225,7 +222,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build Write-PipelinePrependPath -Path $dotnetRoot - Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' return $global:_DotNetInstallDir = $dotnetRoot @@ -277,7 +273,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { Retry({ Write-Host "GET $uri" - Invoke-WebRequest $uri -OutFile $installScript + Invoke-WebRequest $uri -UseBasicParsing -OutFile $installScript }) } @@ -394,8 +390,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.13.0 - $defaultXCopyMSBuildVersion = '17.13.0' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/18.0.0 + $defaultXCopyMSBuildVersion = '18.0.0' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -510,7 +506,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)) { @@ -544,7 +540,8 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') { $vswhereVersion = $GlobalJson.tools.vswhere } else { - $vswhereVersion = '2.5.2' + # keep this in sync with the VSWhereVersion in DefaultVersions.props + $vswhereVersion = '3.1.7' } $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion" @@ -552,25 +549,33 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (!(Test-Path $vsWhereExe)) { Create-Directory $vsWhereDir - Write-Host 'Downloading vswhere' + 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 }) } - 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 diff --git a/src/arcade/eng/common/tools.sh b/src/arcade/eng/common/tools.sh index c1841c9dfd0f..6c121300ac7d 100755 --- a/src/arcade/eng/common/tools.sh +++ b/src/arcade/eng/common/tools.sh @@ -115,9 +115,6 @@ function InitializeDotNetCli { local install=$1 - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - export DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we want to control all package sources export DOTNET_NOLOGO=1 @@ -166,7 +163,6 @@ function InitializeDotNetCli { # build steps from using anything other than what we've downloaded. Write-PipelinePrependPath -path "$dotnet_root" - Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" # return value diff --git a/src/arcade/eng/publishing/v3/publish.yml b/src/arcade/eng/publishing/v3/publish.yml index 2df2c31d0317..383ac1b8e5de 100644 --- a/src/arcade/eng/publishing/v3/publish.yml +++ b/src/arcade/eng/publishing/v3/publish.yml @@ -25,7 +25,7 @@ stages: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/publishing/v3/validate-and-locate-build.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/publishing/v3/validate-and-locate-build.ps1 arguments: > -BuildId ${{ parameters.BARBuildId }} -PromoteToChannelIds ${{ parameters.PromoteToChannelIds }} @@ -102,7 +102,7 @@ stages: - task: PowerShell@2 displayName: Enable cross-org publishing inputs: - filePath: $(Build.SourcesDirectory)/eng/common/enable-cross-org-publishing.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/enable-cross-org-publishing.ps1 arguments: -token $(dn-bot-all-orgs-artifact-feeds-rw) - task: AzureCLI@2 @@ -136,9 +136,9 @@ stages: azureSubscription: maestro-build-promotion scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: > - -task PublishArtifactsInManifest -restore -msbuildEngine dotnet + -task PublishArtifactsInManifest -restore -msbuildEngine dotnet -noWarnAsError /p:PublishingInfraVersion=3 /p:BARBuildId=${{ parameters.BARBuildId }} /p:TargetChannels='${{ parameters.PromoteToChannelIds }}' diff --git a/src/arcade/eng/validate-sdk.yml b/src/arcade/eng/validate-sdk.yml index 30c12dea6954..5dd1a0e4d081 100644 --- a/src/arcade/eng/validate-sdk.yml +++ b/src/arcade/eng/validate-sdk.yml @@ -2,6 +2,8 @@ parameters: buildArgs: '' validateBlobFeedUrl: https://dotnetfeed.blob.core.windows.net/dotnet-core-test/index.json buildConfig: Release + microbuildUseESRP: false + jobs: - template: /eng/common/templates-official/job/job.yml@self @@ -9,6 +11,7 @@ jobs: name: ValidateArcadeSDK displayName: Validate Arcade SDK enableMicrobuild: true + microbuildUseESRP: ${{ parameters.microbuildUseESRP }} artifacts: download: path: build_stage_artifacts @@ -40,7 +43,7 @@ jobs: inlineScript: > .\eng\update-packagesource.ps1 -gitHubPat $(BotAccount-dotnet-maestro-bot-PAT) - -packagesSource $(Build.SourcesDirectory)/build_stage_artifacts + -packagesSource $(System.DefaultWorkingDirectory)/build_stage_artifacts - script: eng\common\cibuild.cmd $(_BuildArgs) displayName: Build / Validate diff --git a/src/arcade/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 b/src/arcade/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 index 91bc94cf866e..766b683ed42f 100644 --- a/src/arcade/eng/xcopy-msbuild/install-visualstudiobuildtools.ps1 +++ b/src/arcade/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/src/arcade/es-metadata.yml b/src/arcade/es-metadata.yml new file mode 100644 index 000000000000..76c257c77efd --- /dev/null +++ b/src/arcade/es-metadata.yml @@ -0,0 +1,8 @@ +schemaVersion: 0.0.1 +isProduction: true +accountableOwners: + service: b3bbd815-183a-4142-8056-3a676d687f71 +routing: + defaultAreaPath: + org: dnceng + path: internal\Dotnet-Core-Engineering diff --git a/src/arcade/global.json b/src/arcade/global.json index a5dd3101dde8..1bb09a938a29 100644 --- a/src/arcade/global.json +++ b/src/arcade/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100-preview.7.25372.107", + "version": "10.0.100", "rollForward": "latestFeature", "paths": [ ".dotnet", @@ -9,11 +9,11 @@ "errorMessage": "The required .NET SDK wasn't found. Please run ./eng/common/dotnet.cmd/sh to install it." }, "tools": { - "dotnet": "10.0.100-preview.7.25372.107" + "dotnet": "10.0.100" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25401.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25401.2", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.25570.6", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.25570.6", "Microsoft.Build.NoTargets": "3.7.0" } } diff --git a/src/arcade/scripts/add-build-variables.ps1 b/src/arcade/scripts/add-build-variables.ps1 index cb0137303b0c..de1fcf2084a5 100644 --- a/src/arcade/scripts/add-build-variables.ps1 +++ b/src/arcade/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/src/arcade/scripts/cqb-utilities.ps1 b/src/arcade/scripts/cqb-utilities.ps1 index a792c7271bc6..c3e4ad35b969 100644 --- a/src/arcade/scripts/cqb-utilities.ps1 +++ b/src/arcade/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/src/arcade/scripts/create-preview-flow.ps1 b/src/arcade/scripts/create-preview-flow.ps1 index 593562719bd8..cb122c6635a8 100644 --- a/src/arcade/scripts/create-preview-flow.ps1 +++ b/src/arcade/scripts/create-preview-flow.ps1 @@ -162,12 +162,11 @@ if ($AddInternalFlow) { Write-Host "Making default channels for SDK repos" MakeDefaultChannel https://github.com/dotnet/sdk $SdkBranch $SdkChannel -MakeDefaultChannel https://github.com/dotnet/roslyn-analyzers $SdkBranch $SdkChannel MakeDefaultChannel https://github.com/dotnet/templating $SdkBranch $SdkChannel if ($AddInternalFlow) { # Because of where internal fixes tend to be, we eliminate some leaves in the sdk graph - # and flow them through the normal public channels: templating, roslyn-analyzers + # and flow them through the normal public channels: templating Write-Host "Making default channels for SDK repos" MakeDefaultChannel https://dev.azure.com/dnceng/internal/_git/dotnet-sdk $InternalSdkBranch $InternalSdkChannel MakeDefaultChannel https://dev.azure.com/dnceng/internal/_git/dotnet-templating $InternalSdkBranch $InternalSdkChannel @@ -190,7 +189,6 @@ if ($AddInternalFlow) { Write-Host "Adding arcade flow to repos not building in the VMR" AddArcadeFlow https://dev.azure.com/dnceng/internal/_git/dotnet-wpf-int $RuntimeBranch -AddArcadeFlow https://github.com/dotnet/icu "dotnet/$RuntimeBranch" Write-Host "Adding non-VMR runtime flow" AddPackageOnlyFlow https://dev.azure.com/dnceng/internal/_git/dotnet-wpf-int $RuntimeChannel https://github.com/dotnet/wpf $RuntimeBranch EveryBuild @@ -223,14 +221,12 @@ if ($AddInternalFlow) { } Write-Host "Add VMR sdk repo forward flow" -AddForwardFlow https://github.com/dotnet/roslyn-analyzers $SdkChannel $publicVMR roslyn-analyzers $SdkBranch EveryBuild AddForwardFlow https://github.com/dotnet/templating $SdkChannel $publicVMR templating $SdkBranch EveryBuild # SDK is batched so that it batches alongside NuGet for a cohesive set of changes. AddBatchedForwardFlow https://github.com/dotnet/sdk $SdkChannel $publicVMR sdk $SdkBranch EveryBuild if ($AddInternalFlow) { Write-Host "Adding internal VMR sdk repo forward flow" - AddForwardFlow https://dev.azure.com/dnceng/internal/_git/dotnet-roslyn-analyzers $InternalSdkChannel $internalVMR roslyn-analyzers $InternalSdkBranch EveryBuild AddForwardFlow https://dev.azure.com/dnceng/internal/_git/dotnet-sdk $InternalSdkChannel $internalVMR sdk $InternalSdkBranch EveryBuild AddForwardFlow https://dev.azure.com/dnceng/internal/_git/dotnet-templating $InternalSdkChannel $internalVMR templating $InternalSdkBranch EveryBuild @@ -239,15 +235,12 @@ if ($AddInternalFlow) { } Write-Host "Adding tooling repo VMR forward flow" -# NuGet is special in that it flows into the SDK and then batched source into the VMR -# Change to traditional flow when https://github.com/dotnet/arcade-services/issues/4665 is resolved -AddPackageOnlyFlow https://github.com/nuget/nuget.client $VSChannel https://github.com/dotnet/sdk $SdkBranch EveryBuild -AddBatchedForwardFlow https://github.com/nuget/nuget.client $VSChannel $publicVMR nuget-client $SdkBranch EveryBuild +AddForwardFlow https://github.com/nuget/nuget.client $VSChannel $publicVMR nuget-client $SdkBranch EveryBuild AddForwardFlow https://github.com/dotnet/roslyn $VSChannel $publicVMR roslyn $SdkBranch EveryBuild AddForwardFlow https://github.com/dotnet/fsharp $VSChannel $publicVMR fsharp $SdkBranch EveryBuild AddForwardFlow https://github.com/dotnet/msbuild $VSChannel $publicVMR msbuild $SdkBranch EveryBuild AddForwardFlow https://github.com/dotnet/razor $VSChannel $publicVMR razor $SdkBranch EveryBuild -AddForwardFlow https://github.com/dotnet/vstest $VSChannel $publicVMR vstest $SdkBranch EveryBuild +AddForwardFlow https://github.com/microsoft/vstest $VSChannel $publicVMR vstest $SdkBranch EveryBuild if ($AddInternalFlow) { Write-Host "Adding internal VMR sdk repo forward flow" @@ -255,6 +248,9 @@ if ($AddInternalFlow) { throw "NYI" } +Write-Host "Adding VMR->repo package flow" +AddPackageOnlyFlow https://github.com/dotnet/dotnet $SdkBranch https://github.com/dotnet/icu "dotnet/$RuntimeBranch" None + Write-Host "Adding VMR->repo backflow." # Only repos that branch for a release get backflow AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/runtime runtime $RuntimeBranch EveryBuild @@ -264,7 +260,6 @@ AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/emsdk emsdk $R AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/windowsdesktop windowsdesktop $RuntimeBranch EveryBuild AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/winforms winforms $RuntimeBranch EveryBuild AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/wpf wpf $RuntimeBranch EveryBuild -AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/roslyn-analyzers templating $SdkBranch EveryBuild AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/sdk sdk $SdkBranch EveryBuild AddBackwardsFlow $publicVMR $SdkChannel https://github.com/dotnet/templating templating $SdkBranch EveryBuild diff --git a/src/arcade/scripts/duplicate-feed.ps1 b/src/arcade/scripts/duplicate-feed.ps1 index 7a27caa6b7c6..fa67529d6ecd 100644 --- a/src/arcade/scripts/duplicate-feed.ps1 +++ b/src/arcade/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/src/arcade/scripts/launch-mirrors.ps1 b/src/arcade/scripts/launch-mirrors.ps1 index 71c4ea59d8e4..4bc09eb66fd5 100644 --- a/src/arcade/scripts/launch-mirrors.ps1 +++ b/src/arcade/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/src/arcade/scripts/list-dotnet-install-versions.ps1 b/src/arcade/scripts/list-dotnet-install-versions.ps1 index b12ce62e1002..ea15a791e92b 100644 --- a/src/arcade/scripts/list-dotnet-install-versions.ps1 +++ b/src/arcade/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/src/arcade/scripts/m2m-dotnet.ps1 b/src/arcade/scripts/m2m-dotnet.ps1 deleted file mode 100644 index b8f8bb6562e6..000000000000 --- a/src/arcade/scripts/m2m-dotnet.ps1 +++ /dev/null @@ -1,404 +0,0 @@ -<# -.SYNOPSIS -Prepares data for migration, disables subscriptions, migrates and verifies -DARC subcriptions and default channels. - -.DESCRIPTION -This script runs in 3 modes: - 1. GenerateDataFile - creates json file which describes DARC migration. - 2. DisableSubscriptions - disables targeting subscriptions for your repository and old branch. - 3. Migration - using json file generated during Initializatin, this script removes default channels - and targeting subscriptions for your repository and branch. Then it recreates them under - a new branch. - 4. Verification - Compares default channels and targeting subscriptions from json file against current - state in DARC. - -.PARAMETER Repository -Mandatory short name of GitHub repository (e.g. dotnet/runtime or dotnet/wpf). This short name is transformed to -public and internal repository (e.g. for dotnet/runtime https://github.com/dotnet/runtime and -https://dev.azure.com/dnceng/internal/_git/dotnet-runtime). - -.PARAMETER NewBranch -Optional new name of branch, defaults to 'main'. - -.PARAMETER OldBranch -Optional old name of branch, defaults to 'master'. - -.PARAMETER GenerateDataFile -Switch to run in generate data file mode. Repository parameter is required and NewBranch, OldBranch are optional. - -.PARAMETER DisableSubscriptions -Switch to run in disable subscriptions on old branch mode. DataFile parameter is required. - -.PARAMETER Migrate -Switch to run in migration mode. DataFile parameter is required. - -.PARAMETER Verify -Switch to run in verification mode. DataFile parameter is required. - -.PARAMETER DataFile -json file path used for DARC validation. - -.PARAMETER DryRun -When specified then no DARC updates are executed, but only logged. - -.EXAMPLE -1. For initilization execute: -./m2m-dotnet.ps1 -GenerateDataFile -Repository dotnet/m2m-renaming-test-1 -or you can additionaly specify branch names: -./m2m-dotnet.ps1 -GenerateDataFile -Repository dotnet/m2m-renaming-test-1 -OldBranch master -NewBranch main - -This generates data file m2m-dotnet_[timestamp].json and disables all targeting subscriptions. - -2. To disable targeting subscriptions for your repository and old branch execute: -.\m2m-dotnet.ps1 -DisableSubscriptions -DataFile m2m-dotnet_[timestamp].json - -3. For migration execute: -.\m2m-dotnet.ps1 -Migrate -DataFile m2m-dotnet_[timestamp].json - -4. For verification execute: -.\m2m-dotnet.ps1 -Verify -DataFile m2m-dotnet_[timestamp].json - -#> - -[CmdletBinding()] -param ( - - [Parameter(ParameterSetName = 'GenerateDataFile', Mandatory = $true)] - [switch]$GenerateDataFile, - - [Parameter(ParameterSetName = 'DisableSubscriptions', Mandatory = $true)] - [switch]$DisableSubscriptions, - - [Parameter(ParameterSetName = 'Migrate', Mandatory = $true)] - [switch]$Migrate, - - [Parameter(ParameterSetName = 'Verify', Mandatory = $true)] - [switch]$Verify, - - [Parameter(ParameterSetName = 'GenerateDataFile', Mandatory = $true)] - [string]$Repository, - [Parameter(ParameterSetName = 'GenerateDataFile')] - [string]$NewBranch = "main", - [Parameter(ParameterSetName = 'GenerateDataFile')] - [string]$OldBranch = "master", - - [Parameter(ParameterSetName = 'Verify', Mandatory = $true)] - [Parameter(ParameterSetName = 'Migrate', Mandatory = $true)] - [Parameter(ParameterSetName = 'DisableSubscriptions', Mandatory = $true)] - [string]$DataFile, - - [switch]$DryRun = $false -) - - -Class DarcExecutor { - [bool]$DryRun = $false - - [string[]] ParseIgnoreChecks([string] $line) { - $ignoreChecks = @() - # Matches fragment like : ignoreChecks = [ "WIP", "license/cla" ] - if ($line -match "ignoreChecks\s*=\s*\[\s*([^\]]+)\s*\]") { - $ignoreChecksValuesMatches = [regex]::matches($matches[1], "`"([^`"]+)`"") - ForEach ($check in $ignoreChecksValuesMatches) { - $ignoreChecks += $check.Groups[1].Value - } - } - - return $ignoreChecks - } - - [object[]] ParseMergePolicies([string] $line) { - $line = $line -replace "ignoreChecks\s*=\s*\[\s*[^\]]*\s*\]", "" - $policies = $line -split "\s+" | Where-Object { $_ } - return $policies - } - - [object[]] ParseSubscriptions([string] $output) { - $darcOutputLines = $output.Split([Environment]::NewLine) - $list = @() - $processingMergePolicies = $false - $batchable = $fromRepo = $fromChannel = $updateFrequency = $enabled = $mergePolicies = $null - For ($i = 0; $i -le $darcOutputLines.Length; $i++) { - $line = $darcOutputLines[$i] - # Matches header like: https://github.com/dotnet/arcade (.NET Eng - Latest) ==> 'https://github.com/dotnet/m2m-renaming-test-1' ('main') - if ($line -match "([^\s]+)\s+\(([^\)]+)\)\s+==>\s+'([^']+)'\s+\('([^\)]+)'\)") { - if ($i -ne 0) { - $list += @{fromRepo = $fromRepo; fromChannel = $fromChannel; updateFrequency = $updateFrequency; enabled = $enabled; batchable = $batchable; ignoreChecks = @($this.ParseIgnoreChecks($mergePolicies)); mergePolicies = @($this.ParseMergePolicies($mergePolicies)) }; - } - - $updateFrequency = $enabled = $batchable = $mergePolicies = "" - - $fromRepo = $matches[1] - $fromChannel = $matches[2] - continue - } - # Matches field like: - Update Frequency: EveryWeek - if ($line -match "^\s+\-\s+([^:]+):\s*(.*)") { - $processingMergePolicies = $false - if ($matches[1] -eq "Update Frequency") { - $updateFrequency = $matches[2] - continue - } - if ($matches[1] -eq "Enabled") { - $enabled = $matches[2] - continue - } - if ($matches[1] -eq "Batchable") { - $batchable = $matches[2] - continue - } - if ($matches[1] -eq "Merge Policies") { - $mergePolicies = $matches[2] - $processingMergePolicies = $true - continue - } - } - if ($processingMergePolicies) { - $mergePolicies += $line - continue - } - } - - if ($null -ne $fromRepo) { - $list += @{fromRepo = $fromRepo; fromChannel = $fromChannel; updateFrequency = $updateFrequency; enabled = $enabled; batchable = $batchable; ignoreChecks = @($this.ParseIgnoreChecks($mergePolicies)); mergePolicies = @($this.ParseMergePolicies($mergePolicies)) }; - } - - return $list - } - - [object[]] GetSubscriptions([string]$repo, [string]$branch) { - $arguments = @("get-subscriptions", "--exact", "--target-repo", $repo, "--target-branch", $branch) - $output = $this.Execute($arguments, $false) - $subscriptions = @($this.ParseSubscriptions($output)) - return $subscriptions - } - - - [void]AddSubscription($repo, $branch, $item) { - $arguments = @("add-subscription", "--channel", $item.fromChannel, "--source-repo", $item.fromRepo, "--target-repo", $repo, "--update-frequency", $item.updateFrequency, "--target-branch", $branch, "--no-trigger", "-q") - $policiesArguments = @("set-repository-policies", "--repo", $repo, "--branch", $branch, "-q") - $targetArgumentsRef = [ref]$arguments - if ($item.batchable -eq "True") { - $arguments += "--batchable" - $targetArgumentsRef = [ref]$policiesArguments - } - if ($item.mergePolicies -contains "Standard") { - $targetArgumentsRef.value += "--standard-automerge" - } - if ($item.mergePolicies -like "NoRequestedChanges") { - $targetArgumentsRef.value += "--no-requested-changes" - } - if ($item.mergePolicies -like "NoExtraCommits") { - $targetArgumentsRef.value += "--no-extra-commits" - } - if ($item.mergePolicies -like "AllChecksSuccessful") { - $targetArgumentsRef.value += "--all-checks-passed" - } - if ($item.ignoreChecks.length -gt 0) { - $targetArgumentsRef.value += "--ignore-checks" - $targetArgumentsRef.value += $item.ignoreChecks -join "," - } - - if ($item.batchable -eq "True") { - $this.Execute($policiesArguments, $true) - } - - $output = $this.Execute($arguments, $true) - - if ($output -match "Successfully created new subscription with id '([^']+)'.") { - $id = $matches[1] - if ($item.enabled -eq [bool]::FalseString) { - $this.DisableSubscription($id) - } - } - else { - Write-Error(" WARNING: {0}" -f $output) - } - } - - [void]DeleteSubscriptions($repo, $branch) { - $arguments = @("get-subscriptions", "--exact", "--target-repo", $repo, "--target-branch", $branch) - $output = $this.Execute($arguments, $false) - if (-not ($output -match "^No subscriptions found matching the specified criteria.")) { - Write-Host ("Deleting subscriptions for {0} {1}" -f $repo, $branch) - $arguments = @("delete-subscriptions", "--exact", "--target-repo", $repo, "--target-branch", $branch, "-q") - $this.Execute($arguments, $true) - } - } - - [void]CreateDefaultChannel($repo, $branch, $channel) { - Write-Host ("Creating default channel {2} for branch {0} {1}" -f $repo, $branch, $channel) - $arguments = @("add-default-channel", "--repo", $repo, "--branch", $branch, "--channel", $channel, "-q") - $this.Execute($arguments, $true) - } - - [void]DisableSubscription ([string] $id) { - Write-Host ("Disabling subscription {0}" -f $id) - $arguments = @("subscription-status", "--id", $id, "-d", "-q") - $this.Execute($arguments, $true) - } - - [string[]]GetTargetSubscriptionIds ([string] $repo, [string] $branch) { - $arguments = @("get-subscriptions", "--exact", "--target-repo", $repo, "--target-branch", $branch) - $ids = $this.Execute($arguments, $false) | Select-String -AllMatches -Pattern "\s+-\s+Id:\s+([^\s]+)" | ForEach-Object { $_.Matches } | Foreach-Object { $_.Groups[1].Value } - return $ids - } - - [void]DisableTargetSubscriptions ([string] $repo, [string] $branch) { - Write-Host "Disabling targeting subscriptions for $repo ($branch)" - - $ids = $this.GetTargetSubscriptionIds($repo, $branch) - ForEach ($id in $ids) { - $this.DisableSubscription($id) - } - } - - [Hashtable[]]GetDefaultChannels ([string] $repo, [string] $branch) { - $arguments = @("get-default-channels", "--source-repo", $repo, "--branch", $branch) - $output = $this.Execute($arguments, $true) - $records = @($output | Select-String -AllMatches -Pattern "\((\d+)\)\s+$repo\s+@\s+$branch\s+->\s+(.*)\b" | ForEach-Object { $_.Matches } | ForEach-Object { @{id = $_.Groups[1].Value; channel = $_.Groups[2].Value } }) - return $records - } - - [void]DeleteDefaultChannel([string] $id) { - Write-Host ("Deleting default channel {0}" -f $id) - $arguments = @("delete-default-channel", "--id", $id) - $this.Execute($arguments, $true) - } - - [void]DeleteDefaultChannels([string] $repo, [string] $branch) { - $channels = @($this.GetDefaultChannels($repo, $branch)) - ForEach ($item in $channels) { - $this.DeleteDefaultChannel($item.id) - } - } - - [Hashtable]GetRepoConfig([string] $repo, [string] $newBranch, [string] $oldBranch) { - $defaultChannels = @($this.GetDefaultChannels($repo, $oldBranch) | ForEach-Object { $_.channel }) - $subscriptions = @($this.GetSubscriptions($repo, $oldBranch)) - $config = @{repo = $repo; newBranch = $newBranch; oldBranch = $oldBranch; defaultChannels = $defaultChannels; subscriptions = $subscriptions; } - return $config - } - - [void]MigrateRepo([PSCustomObject]$config) { - Write-Host (">>>Migrating repository {0} {1} ==> {2}..." -f $config.repo, $config.oldBranch, $config.newBranch) - - $this.DeleteDefaultChannels($config.repo, $config.oldBranch) - ForEach ($channel in $config.defaultChannels) { - $this.CreateDefaultChannel($config.repo, $config.newBranch, $channel) - } - - $this.DeleteSubscriptions($config.repo, $config.oldBranch) - $this.DeleteSubscriptions($config.repo, $config.newBranch) - - Write-Host ("Adding subscriptions") - ForEach ($item in $config.subscriptions) { - $this.AddSubscription($config.repo, $config.newBranch, $item) - } - } - - [void]VerifyRepo([PSCustomObject]$config) { - Write-Host (">>>Verifying repository {0} {1} ==> {2}..." -f $config.repo, $config.oldBranch, $config.newBranch) - if ($this.GetDefaultChannels($config.repo, $config.oldBranch).length -ne 0) { - throw("Default channels for old branch haven't been removed.") - } - if ($this.GetTargetSubscriptionIds($config.repo, $config.oldBranch).length -ne 0) { - throw("Subscriptions for old branch haven't been removed.") - } - - $actualConfig = $this.GetRepoConfig($config.repo, $config.oldBranch, $config.newBranch) - if ($actualConfig.defaultChannels.length -ne $config.defaultChannels.length) { - throw("Subscriptions for old branch haven't been removed.") - } - - $expectedDefaultChannels = ConvertTo-Json($actualConfig.defaultChannels | Sort-Object) - $actualDefaultChannels = ConvertTo-Json($config.defaultChannels | Sort-Object) - if ($expectedDefaultChannels -ne $actualDefaultChannels) { - throw("Expected default channels {0} don't match actual {1}." -f $actualDefaultChannels, $actualDefaultChannels) - } - - $actualSubscriptions = ConvertTo-Json($actualConfig.subscriptions | Sort-Object { $_.fromRepo }) - $expectedSubscriptions = ConvertTo-Json($config.subscriptions | Sort-Object { $_.fromRepo }) - - if ($expectedSubscriptions -ne $actualSubscriptions) { - throw("Expected subscriptions {0} don't match actual {1}." -f $expectedSubscriptions, $actualSubscriptions) - } - - Write-Host ("Validation of {0} passed" -f $config.repo) - } - - [string]Execute ([string[]] $arguments, [bool]$exitCodeCheck) { - if ($this.DryRun -and ($arguments[0] -ne "get-default-channels") -and ($arguments[0] -ne "get-subscriptions")) { - Write-Host (">>> darc {0}" -f ($arguments -join " ")) - return "Successfully created new subscription with id 'TEST_ID'." - } - else { - $output = (&"darc" $arguments | Out-String) - if ($exitCodeCheck -and $LASTEXITCODE -ne 0) { - throw (" Error executing command ""darc {0}"" with status code {1}: {2}" -f ($arguments -join " "), $LASTEXITCODE, $output) - } - return $output - } - } -} -function InitializeDarc { - param ( - [DarcExecutor] $darc - ) - $configFile = "m2m-dotnet_{0:yyyyMMdd_HHmmss}.json" -f (get-date) - $internalRepo = "https://dev.azure.com/dnceng/internal/_git/{0}" -f ($Repository -replace "/", "-") - $publicRepo = "https://github.com/{0}" -f $Repository - - Write-Host ("Creating configuration for repository {0} {1} ==> {2}..." -f $publicRepo, $OldBranch, $NewBranch) - $configPublic = $darc.GetRepoConfig($publicRepo, $NewBranch, $OldBranch) - - Write-Host ("Creating configuration for repository {0} {1} ==> {2}..." -f $internalRepo, $OldBranch, $NewBranch) - $configInternal = $darc.GetRepoConfig($internalRepo, $NewBranch, $OldBranch) - - $configs = @($configPublic, $configInternal) - ConvertTo-Json $configs -Depth 4 | Out-File -FilePath $configFile - Write-Host ("Configuration has been saved as {0}" -f $configFile) -} - -function DisableDarcSubscriptions { - param ( - [DarcExecutor] $darc - ) - - $configs = Get-Content -Raw -Path $DataFile | ConvertFrom-Json - ForEach ($config in $configs) { - $darc.DisableTargetSubscriptions($config.repo, $config.oldBranch) - } -} -function MigrateDarc { - param ( - [DarcExecutor]$darc - ) - - $configs = Get-Content -Raw -Path $DataFile | ConvertFrom-Json - ForEach ($config in $configs) { - $darc.MigrateRepo($config) - } -} -function VerifyDarc { - param ( - [DarcExecutor]$darc - ) - - $configs = Get-Content -Raw -Path $DataFile | ConvertFrom-Json - ForEach ($config in $configs) { - $darc.VerifyRepo($config) - } -} - -$ErrorActionPreference = 'Stop' -$darc = [DarcExecutor]::new() -$darc.DryRun = $DryRun - -switch ($PSCmdlet.ParameterSetName) { - "GenerateDataFile" { InitializeDarc -darc $darc } - "DisableSubscriptions" { DisableDarcSubscriptions -darc $darc } - "Migrate" { MigrateDarc -darc $darc } - "Verify" { VerifyDarc -darc $darc } -} diff --git a/src/arcade/scripts/pause-all-pipelines.ps1 b/src/arcade/scripts/pause-all-pipelines.ps1 index e109d06e079c..47c0de52c6ad 100644 --- a/src/arcade/scripts/pause-all-pipelines.ps1 +++ b/src/arcade/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" diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/src/DownloadFile.cs b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/src/DownloadFile.cs index cd1a18098d22..9fe6fb49651e 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/src/DownloadFile.cs +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/src/DownloadFile.cs @@ -215,11 +215,11 @@ private async Tasks.Task DownloadWithRetriesAsync(HttpClient httpClient, s if (attempt > Retries) { Log.LogMessage($"Failed to download '{uri}' to '{DestinationPath}': {e.Message}"); + Log.LogErrorFromException(e, true, true, null); return false; } Log.LogMessage($"Retrying download of '{uri}' to '{DestinationPath}' due to failure: '{e.Message}' ({attempt}/{Retries})"); - Log.LogErrorFromException(e, true, true, null); await Tasks.Task.Delay(RetryDelayMilliseconds).ConfigureAwait(false); continue; diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props index 3dd23f15f1c5..ed05aaf6bcd8 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props +++ b/src/arcade/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 @@ -80,17 +80,18 @@ $(XUnitVersion) 3.1.3 - 3.0.0 - 1.7.3 + 3.1.0 + 1.8.5 - 3.9.3 + 4.0.2 $(MSTestVersion) $(MSTestVersion) $(ArcadeSdkVersion) $(ArcadeSdkVersion) 3.12.0 3.15.1 - 2.6.7 + + 3.1.7 1.0.0 $(ArcadeSdkVersion) $(ArcadeSdkVersion) @@ -98,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/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Imports.targets b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Imports.targets index 2bbcc1458651..1f53ba09b3f7 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Imports.targets +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Imports.targets @@ -9,8 +9,11 @@ - + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props index 409f25e07e28..e443c179b2aa 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.props @@ -20,6 +20,9 @@ Icon.png $(MSBuildThisFileDirectory)Assets\DotNetPackageIcon.png + + true + true diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets index 1dff8890440d..ff765c311416 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/ProjectDefaults.targets @@ -1,6 +1,11 @@ + + + false + + $(__DeployProjectOutput) diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/SdkTasks/PublishArtifactsInManifest.proj b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/SdkTasks/PublishArtifactsInManifest.proj index 603e337c4a4a..d0c77effc663 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/SdkTasks/PublishArtifactsInManifest.proj +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/SdkTasks/PublishArtifactsInManifest.proj @@ -77,6 +77,8 @@ $(NetToolCurrent) Publish + + false diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj index 9997e86b16a7..cf2a163077a6 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.proj @@ -6,7 +6,7 @@ - https://github.com/dotnet/arcade/blob/master/Documentation/CorePackages/Signing.md Optional variables: - EnableDefaultArtifacts Includes *.nupkg, *.vsix and *.wixpack.zip under "/artifacts/packages/**" for sigining. + EnableDefaultArtifacts Includes *.nupkg, *.vsix and *.wixpack.zip under "/artifacts/packages/**" for signing. Defaults to true. EnableDefaultRidSpecificArtifacts Do not sign *.nupkg packages under "/artifacts/packages/**" that do not contain the current TargetRid. DefaultArtifactVisibility The default visibility for Artifact items. Defaults to External. diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props index ee62c8cf6fcd..d9f48837efb7 100644 --- a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Sign.props @@ -30,6 +30,8 @@ + + + + + + - netstandard2.0%3bnetstandard2.1%3bnetcoreapp2.1%3bnetcoreapp3.1%3bnet5.0%3bnet6.0%3bnet7.0%3bnet8.0%3bnet9.0%3bnet10.0 + netstandard2.0%3bnetstandard2.1%3bnetcoreapp2.1%3bnetcoreapp3.1%3bnet5.0%3bnet6.0%3bnet7.0%3bnet8.0%3bnet9.0%3bnet10.0%3b$(NetCurrent) <_EnableTargetFrameworkFiltering>false <_EnableTargetFrameworkFiltering Condition="'$(NoTargetFrameworkFiltering)' != 'true' and '$(DotNetTargetFrameworkFilter)' != ''">true diff --git a/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/TargetingPacks.targets b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/TargetingPacks.targets new file mode 100644 index 000000000000..7d4ce9b10d2a --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/TargetingPacks.targets @@ -0,0 +1,109 @@ + + + + + + true + true + + $(NetCurrent) + $(NetPrevious) + $(NetMinimum) + + + + + + + + + + + + + + + + + + + + + + + $(KnownFrameworkReferenceMicrosoftNETCoreAppCurrentDefaultRuntimeFrameworkVersion) + $(KnownFrameworkReferenceMicrosoftNETCoreAppCurrentLatestRuntimeFrameworkVersion) + $(KnownFrameworkReferenceMicrosoftNETCoreAppCurrentTargetingPackVersion) + + + + $(KnownRuntimePackMicrosoftNETCoreAppCurrentMonoLatestRuntimeFrameworkVersion) + + + + $(KnownRuntimePackMicrosoftNETCoreAppCurrentNativeAOTLatestRuntimeFrameworkVersion) + + + + $(KnownILCompilerPackCurrentVersion) + + + + $(KnownCrossgen2PackCurrentVersion) + + + + $(KnownAppHostPackCurrentVersion) + + + + $(KnownILLinkPackCurrentVersion) + + + + + + + %(RuntimePackRuntimeIdentifiers);$(NETCoreSdkRuntimeIdentifier) + + + %(Crossgen2RuntimeIdentifiers);$(NETCoreSdkRuntimeIdentifier) + + + + \ No newline at end of file diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/GeneralTests.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/GeneralTests.cs index afa9d0642c17..c07a42262cda 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/GeneralTests.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/GeneralTests.cs @@ -264,5 +264,28 @@ public void TargetChannelConfig_TargetFeeds_UnequalTest() actualResult.Should().BeFalse(); } + + [Theory] + [InlineData("https://dotnetcli.blob.core.windows.net/test", "https://builds.dotnet.microsoft.com/test")] + [InlineData("https://dotnetbuilds.blob.core.windows.net/internal", "https://ci.dot.net/internal")] + [InlineData("https://dotnetbuilds.blob.core.windows.net/public?sv=token", "https://ci.dot.net/public")] + [InlineData("https://unknown.blob.core.windows.net/test", "https://unknown.blob.core.windows.net/test")] + [InlineData("https://pkgs.dev.azure.com/dnceng/public/_packaging/feed/nuget/v3/index.json", "https://pkgs.dev.azure.com/dnceng/public/_packaging/feed/nuget/v3/index.json")] + public void TargetFeedConfig_SafeTargetURL_AppliesCdnSubstitution(string targetUrl, string expectedSafeUrl) + { + // Arrange + var feedConfig = new TargetFeedConfig( + contentType: TargetFeedContentType.Installer, + targetURL: targetUrl, + type: FeedType.AzureStorageContainer, + token: "dummyToken" + ); + + // Act + var actualSafeUrl = feedConfig.SafeTargetURL; + + // Assert + actualSafeUrl.Should().Be(expectedSafeUrl); + } } } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/ProductionChannelValidatorTests.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/ProductionChannelValidatorTests.cs new file mode 100644 index 000000000000..ece57d4bd3d7 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/ProductionChannelValidatorTests.cs @@ -0,0 +1,704 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.Build.Tasks.Feed.Model; +using Microsoft.DotNet.Build.Manifest; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Microsoft.DotNet.Build.Tasks.Feed.Tests +{ + public class ProductionChannelValidatorTests + { + private readonly Mock _mockAzureDevOpsService; + private readonly Mock _mockBranchClassificationService; + private readonly Mock> _mockLogger; + + public ProductionChannelValidatorTests() + { + _mockAzureDevOpsService = new Mock(); + _mockBranchClassificationService = new Mock(); + _mockLogger = new Mock>(); + } + + private ProductionChannelValidator CreateValidator(ValidationMode validationMode) + { + return new ProductionChannelValidator( + _mockAzureDevOpsService.Object, + _mockBranchClassificationService.Object, + _mockLogger.Object, + validationMode); + } + + private static ProductConstructionService.Client.Models.Build CreateTestBuild( + int id = 12345, + DateTimeOffset? dateProduced = null, + int staleness = 0, + bool released = false, + bool stable = false, + string commit = "abc123", + string azureDevOpsAccount = "dnceng", + string azureDevOpsProject = "internal", + int? azureDevOpsBuildId = 123456, + string azureDevOpsRepository = "https://dev.azure.com/dnceng/internal/_git/dotnet-runtime", + string azureDevOpsBranch = "refs/heads/main", + List channels = null, + List assets = null, + List dependencies = null, + List incoherencies = null) + { + var build = new ProductConstructionService.Client.Models.Build( + id: id, + dateProduced: dateProduced ?? DateTimeOffset.UtcNow, + staleness: staleness, + released: released, + stable: stable, + commit: commit, + channels: channels ?? new List(), + assets: assets ?? new List(), + dependencies: dependencies ?? new List(), + incoherencies: incoherencies ?? new List() + ); + + // Set the Azure DevOps properties using reflection since they might be read-only + // This is a test scenario, so it's acceptable to use reflection + build.AzureDevOpsAccount = azureDevOpsAccount; + build.AzureDevOpsProject = azureDevOpsProject; + build.AzureDevOpsBuildId = azureDevOpsBuildId; + build.AzureDevOpsRepository = azureDevOpsRepository; + build.AzureDevOpsBranch = azureDevOpsBranch; + + return build; + } + + private static TargetChannelConfig CreateProductionChannelConfig(int id = 1) + { + return new TargetChannelConfig( + id: id, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: null, + akaMSCreateLinkPatterns: null, + akaMSDoNotCreateLinkPatterns: null, + targetFeeds: new TargetFeedSpecification[0], + symbolTargetType: SymbolPublishVisibility.None, + flatten: true, + isProduction: true); + } + + private static TargetChannelConfig CreateNonProductionChannelConfig(int id = 2) + { + return new TargetChannelConfig( + id: id, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: null, + akaMSCreateLinkPatterns: null, + akaMSDoNotCreateLinkPatterns: null, + targetFeeds: new TargetFeedSpecification[0], + symbolTargetType: SymbolPublishVisibility.None, + flatten: true, + isProduction: false); + } + + [Theory] + [InlineData(ValidationMode.Enforce)] + [InlineData(ValidationMode.Audit)] + public async Task ValidateAsync_NullBuild_ThrowsArgumentNullException(ValidationMode validationMode) + { + // Arrange + var validator = CreateValidator(validationMode); + var targetChannel = CreateProductionChannelConfig(); + + // Act & Assert + await Assert.ThrowsAsync(() => + validator.ValidateAsync(null, targetChannel)); + } + + [Theory] + [InlineData(ValidationMode.Enforce)] + [InlineData(ValidationMode.Audit)] + public async Task ValidateAsync_NonProductionChannel_ReturnsSuccess(ValidationMode validationMode) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateNonProductionChannelConfig(); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(TargetChannelValidationResult.Success); + _mockAzureDevOpsService.Verify(x => x.GetBuildInfoAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + _mockBranchClassificationService.Verify(x => x.GetBranchClassificationsAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData(ValidationMode.Enforce)] + [InlineData(ValidationMode.Audit)] + public async Task ValidateAsync_ProductionChannel_ValidBuild_ReturnsSuccess(ValidationMode validationMode) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official", "other-tag" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = "main", Classification = "production" }, + new BranchClassification { BranchName = "release/*", Classification = "production" } + } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(TargetChannelValidationResult.Success); + _mockAzureDevOpsService.Verify(x => x.GetBuildInfoAsync("dnceng", "internal", 123456), Times.Once); + _mockBranchClassificationService.Verify(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456"), Times.Once); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_ProductionChannel_MissingRequiredTag_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "other-tag", "another-tag" } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + _mockAzureDevOpsService.Verify(x => x.GetBuildInfoAsync("dnceng", "internal", 123456), Times.Once); + _mockBranchClassificationService.Verify(x => x.GetBranchClassificationsAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_ProductionChannel_NonProductionBranch_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(azureDevOpsBranch: "refs/heads/feature/test"); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = "main", Classification = "production" }, + new BranchClassification { BranchName = "release/*", Classification = "production" } + } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_NonAzureDevOpsRepository_WithValidTags_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild( + azureDevOpsRepository: "https://github.com/dotnet/runtime", + azureDevOpsAccount: "dnceng", + azureDevOpsProject: "internal", + azureDevOpsBuildId: 123456 + ); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "https://github.com/dotnet/runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + _mockAzureDevOpsService.Verify(x => x.GetBuildInfoAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + _mockBranchClassificationService.Verify(x => x.GetBranchClassificationsAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_ProductionChannel_MissingAzureDevOpsInfo_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange - Azure DevOps repository but missing Azure DevOps build metadata + var validator = CreateValidator(validationMode); + var build = CreateTestBuild( + azureDevOpsRepository: "https://dev.azure.com/dnceng/internal/_git/dotnet-runtime", // Azure DevOps repo + azureDevOpsAccount: null, // But missing metadata + azureDevOpsProject: null, + azureDevOpsBuildId: null + ); + var targetChannel = CreateProductionChannelConfig(); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + _mockAzureDevOpsService.Verify(x => x.GetBuildInfoAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData(ValidationMode.Enforce, "main", "main", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "master", "master", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "release/6.0", "release/*", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "release/6.0.1", "release/*", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "main", "~default", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "master", "~default", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Enforce, "feature/test", "main", TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Enforce, "develop", "release/*", TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Enforce, "feature/test", "~default", TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, "main", "main", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "master", "master", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "release/6.0", "release/*", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "release/6.0.1", "release/*", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "main", "~default", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "master", "~default", TargetChannelValidationResult.Success)] + [InlineData(ValidationMode.Audit, "feature/test", "main", TargetChannelValidationResult.AuditOnlyFailure)] + [InlineData(ValidationMode.Audit, "develop", "release/*", TargetChannelValidationResult.AuditOnlyFailure)] + [InlineData(ValidationMode.Audit, "feature/test", "~default", TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_BranchPatternMatching_WorksCorrectly(ValidationMode validationMode, string branchName, string pattern, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(azureDevOpsBranch: $"refs/heads/{branchName}"); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = pattern, Classification = "production" } + } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_AzureDevOpsServiceThrows_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ThrowsAsync(new InvalidOperationException("API Error")); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_BranchClassificationServiceThrows_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ThrowsAsync(new InvalidOperationException("API Error")); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, LogLevel.Debug, LogLevel.Error)] + [InlineData(ValidationMode.Audit, LogLevel.Debug, LogLevel.Warning)] + public async Task ValidateAsync_BranchClassificationServiceThrows_LogsCorrectly(ValidationMode validationMode, LogLevel expectedDebugLevel, LogLevel expectedValidationLevel) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ThrowsAsync(new InvalidOperationException("API Error")); + + // Act + await validator.ValidateAsync(build, targetChannel); + + // Assert + // Verify that debug logging is called for branch classification failures + _mockLogger.Verify( + x => x.Log( + expectedDebugLevel, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Failed to fetch branch classifications for build")), + It.IsAny(), + It.IsAny>()), + Times.Once); + + // Verify that validation mode appropriate logging is called + _mockLogger.Verify( + x => x.Log( + expectedValidationLevel, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Error validating branch classification for build")), + It.IsAny(), + It.IsAny>()), + Times.Once); + } + + [Theory] + [InlineData(ValidationMode.Enforce)] + [InlineData(ValidationMode.Audit)] + public async Task ValidateAsync_RepositoryUrlExtraction_WorksCorrectly(ValidationMode validationMode) + { + // Arrange + var validator = CreateValidator(validationMode); + var repositoryUrl = "https://dev.azure.com/dnceng/internal/_git/dotnet-runtime"; + var expectedRepoId = "dotnet-runtime"; + + var build = CreateTestBuild(azureDevOpsRepository: repositoryUrl); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = expectedRepoId }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = "main", Classification = "production" } + } + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(TargetChannelValidationResult.Success); + } + + [Theory] + [InlineData("refs/heads/main", "main")] + [InlineData("refs/heads/release/6.0", "release/6.0")] + [InlineData("main", "main")] + [InlineData("release/6.0", "release/6.0")] + public async Task ValidateAsync_BranchNameNormalization_WorksCorrectly(string inputBranch, string expectedNormalizedBranch) + { + // Arrange + var enforceValidator = CreateValidator(ValidationMode.Enforce); + var auditValidator = CreateValidator(ValidationMode.Audit); + var build = CreateTestBuild(azureDevOpsBranch: inputBranch); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = expectedNormalizedBranch, Classification = "production" } + } + }); + + // Act - Test both validators for successful case + var enforceResult = await enforceValidator.ValidateAsync(build, targetChannel); + var auditResult = await auditValidator.ValidateAsync(build, targetChannel); + + // Assert + enforceResult.Should().Be(TargetChannelValidationResult.Success); + auditResult.Should().Be(TargetChannelValidationResult.Success); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_EmptyBranchClassifications_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List() + }); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_NullBranchClassificationResponse_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync((BranchClassificationResponse)null); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Theory] + [InlineData(ValidationMode.Enforce, TargetChannelValidationResult.Fail)] + [InlineData(ValidationMode.Audit, TargetChannelValidationResult.AuditOnlyFailure)] + public async Task ValidateAsync_UnexpectedExceptionInValidation_ReturnsExpectedResult(ValidationMode validationMode, TargetChannelValidationResult expectedResult) + { + // Arrange + var validator = CreateValidator(validationMode); + var build = CreateTestBuild(); + var targetChannel = CreateProductionChannelConfig(); + + // Mock the Azure DevOps service to throw when fetching build info + // This simulates an unexpected exception during validation + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ThrowsAsync(new HttpRequestException("Network error")); + + // Act + var result = await validator.ValidateAsync(build, targetChannel); + + // Assert + result.Should().Be(expectedResult); + } + + [Fact] + public void AzureDevOpsService_GetBuildInfoAsync_CallsCorrectEndpoint() + { + // Arrange + var mockHttpClient = new Mock(); + var mockLogger = new Mock>(); + var service = new AzureDevOpsService(mockHttpClient.Object, mockLogger.Object); + + // Note: This test would need to be expanded with proper HttpClient mocking + // which is more complex. For now, we're just verifying the interface structure. + + // Act & Assert - Just verifying method signature exists + Assert.True(typeof(IProductionChannelValidatorBuildInfoService).GetMethod(nameof(IProductionChannelValidatorBuildInfoService.GetBuildInfoAsync)) != null); + } + + [Fact] + public async Task ValidateAsync_ValidationMode_DefaultsToEnforce() + { + // Arrange - Create validator without specifying ValidationMode (should default to Enforce) + var defaultValidator = new ProductionChannelValidator( + _mockAzureDevOpsService.Object, + _mockBranchClassificationService.Object, + _mockLogger.Object); + + var build = CreateTestBuild(azureDevOpsBranch: "refs/heads/feature/test"); + var targetChannel = CreateProductionChannelConfig(); + + _mockAzureDevOpsService + .Setup(x => x.GetBuildInfoAsync("dnceng", "internal", 123456)) + .ReturnsAsync(new AzureDevOpsBuildInfo + { + Project = new AzureDevOpsProject { Id = "project-guid-123", Name = "internal" }, + Repository = new AzureDevOpsRepository { Id = "repo-guid-456", Name = "dotnet-runtime" }, + Tags = new List { "1ES.PT.Official" } + }); + + _mockBranchClassificationService + .Setup(x => x.GetBranchClassificationsAsync("dnceng", "project-guid-123", "repo-guid-456")) + .ReturnsAsync(new BranchClassificationResponse + { + Status = "Success", + BranchClassifications = new List + { + new BranchClassification { BranchName = "main", Classification = "production" } + } + }); + + // Act + var result = await defaultValidator.ValidateAsync(build, targetChannel); + + // Assert - Default should be Enforce mode, so non-production branch should return Fail + result.Should().Be(TargetChannelValidationResult.Fail); + } + + [Fact] + public void PublishArtifactsInManifest_EnforceProduction_DefaultsToFalse() + { + // Arrange & Act + var task = new PublishArtifactsInManifest(); + + // Assert + task.EnforceProduction.Should().BeFalse(); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void PublishArtifactsInManifest_EnforceProduction_AcceptsValidValues(bool enforceProduction) + { + // Arrange & Act + var task = new PublishArtifactsInManifest(); + task.EnforceProduction = enforceProduction; + + // Assert + task.EnforceProduction.Should().Be(enforceProduction); + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishArtifactsInManifestTests.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishArtifactsInManifestTests.cs index 05c776ff704a..4b150806a7dc 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishArtifactsInManifestTests.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishArtifactsInManifestTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; @@ -16,6 +17,7 @@ using Microsoft.DotNet.Build.Tasks.Feed.Model; using Microsoft.DotNet.Internal.DependencyInjection.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Xunit; using static Microsoft.DotNet.Build.CloudTestTasks.AzureStorageUtils; using static Microsoft.DotNet.Build.Tasks.Feed.PublishArtifactsInManifestBase; @@ -30,6 +32,88 @@ public class PublishArtifactsInManifestTests private const string AzDOFeedUrl = "https://pkgs.dev.azure.com/dnceng/public/_packaging/a-dotnet-feed/nuget/v3/index.json"; private const string StorageUrl = "https://dotnetstorageaccount.blob.core.windows.net/placewherethingsarepublished/index.json"; + /// + /// Mock implementation of ITargetChannelValidator for testing negative scenarios. + /// + private class MockTargetChannelValidator : ITargetChannelValidator + { + private readonly TargetChannelValidationResult _validationResult; + private readonly bool _shouldValidate; + + public MockTargetChannelValidator(TargetChannelValidationResult validationResult, bool shouldValidate = true) + { + _validationResult = validationResult; + _shouldValidate = shouldValidate; + } + + public int ValidateCallCount { get; private set; } + public ProductConstructionService.Client.Models.Build LastBuild { get; private set; } + public TargetChannelConfig LastTargetChannel { get; private set; } + + public Task ValidateAsync(ProductConstructionService.Client.Models.Build build, TargetChannelConfig targetChannel) + { + if (_shouldValidate) + { + ValidateCallCount++; + LastBuild = build; + LastTargetChannel = targetChannel; + } + return Task.FromResult(_validationResult); + } + } + + /// + /// Test publishing task that exposes the ValidateTargetChannelAsync method for testing. + /// + private class TestablePublishArtifactsTask : PublishArtifactsInManifestBase + { + public TestablePublishArtifactsTask(ITargetChannelValidator validator = null) + : base(null, validator) + { + } + + public override Task ExecuteAsync() + { + throw new NotImplementedException(); + } + + public new async Task ValidateTargetChannelAsync( + ProductConstructionService.Client.Models.Build build, + TargetChannelConfig targetChannel) + { + return await base.ValidateTargetChannelAsync(build, targetChannel); + } + } + + /// + /// Creates a test Build object with the required constructor parameters. + /// + private static ProductConstructionService.Client.Models.Build CreateTestBuild( + int id = 12345, + DateTimeOffset? dateProduced = null, + int staleness = 0, + bool released = false, + bool stable = false, + string commit = "abc123", + List channels = null, + List assets = null, + List dependencies = null, + List incoherencies = null) + { + return new ProductConstructionService.Client.Models.Build( + id: id, + dateProduced: dateProduced ?? DateTimeOffset.UtcNow, + staleness: staleness, + released: released, + stable: stable, + commit: commit, + channels: channels ?? new List(), + assets: assets ?? new List(), + dependencies: dependencies ?? new List(), + incoherencies: incoherencies ?? new List() + ); + } + // This test should be refactored: https://github.com/dotnet/arcade/issues/6715 [Fact] public void ConstructV3PublishingTask() @@ -66,13 +150,31 @@ public void ConstructV4PublishingTask() var task = new PublishArtifactsInManifest() { BuildEngine = buildEngine, - TargetChannels = GeneralTestingChannelId + TargetChannels = GeneralTestingChannelId, + AzdoApiToken = "test-token" // Add test token for DI }; // Dependency Injection setup var collection = new ServiceCollection() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton() + .AddSingleton(provider => + { + var httpClient = provider.GetRequiredService(); + var loggerFactory = provider.GetRequiredService(); + var logger = loggerFactory.CreateLogger(); + return new AzureDevOpsService(httpClient, logger, "test-token"); + }) + .AddSingleton(provider => + { + var httpClient = provider.GetRequiredService(); + var loggerFactory = provider.GetRequiredService(); + var logger = loggerFactory.CreateLogger(); + return new BranchClassificationService(httpClient, logger, "test-token"); + }) + .AddSingleton() + .AddLogging(); // Add logging services task.ConfigureServices(collection); using var provider = collection.BuildServiceProvider(); @@ -83,6 +185,68 @@ public void ConstructV4PublishingTask() which.Should().BeOfType(); } + [Theory] + [InlineData(TargetChannelValidationResult.Success)] + [InlineData(TargetChannelValidationResult.Fail)] + public async Task ValidateTargetChannelAsync_ProductionChannelValidation_Works(TargetChannelValidationResult validationResult) + { + // Arrange + var mockValidator = new MockTargetChannelValidator(validationResult: validationResult); + var task = new TestablePublishArtifactsTask(mockValidator); + var buildEngine = new MockBuildEngine(); + task.BuildEngine = buildEngine; + + var build = CreateTestBuild(id: 12345, commit: "abc123"); + + var productionChannel = new TargetChannelConfig( + id: 1, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: null, + akaMSCreateLinkPatterns: null, + akaMSDoNotCreateLinkPatterns: null, + targetFeeds: new TargetFeedSpecification[0], + symbolTargetType: SymbolPublishVisibility.None, + flatten: true, + isProduction: true); + + // Act + var result = await task.ValidateTargetChannelAsync(build, productionChannel); + + // Assert + // For both Success and AuditOnlyFailure, the method returns true (allows publishing) + // Only Fail should return false + bool expectedResult = validationResult != TargetChannelValidationResult.Fail; + result.Should().Be(expectedResult); + + mockValidator.ValidateCallCount.Should().Be(1); + mockValidator.LastBuild.Should().Be(build); + mockValidator.LastTargetChannel.Should().Be(productionChannel); + + // Check that validation log message was written + buildEngine.BuildMessageEvents.Should().Contain(m => + m.Importance == Microsoft.Build.Framework.MessageImportance.Normal && + m.Message.Contains("Validating production channel 1")); + + if (validationResult == TargetChannelValidationResult.Fail) + { + // Check that error was logged + buildEngine.BuildErrorEvents.Should().Contain(error => + error.Message.Contains("Build validation failed for production channel 1")); + } + else if (validationResult == TargetChannelValidationResult.AuditOnlyFailure) + { + // Check that warning was logged for audit-only failure + buildEngine.BuildWarningEvents.Should().Contain(warning => + warning.Message.Contains("Build validation audit failure for production channel 1")); + } + else + { + // Check that no error was logged for success + buildEngine.BuildErrorEvents.Should().BeEmpty(); + } + } + [Theory] [InlineData("https://pkgs.dev.azure.com/dnceng/public/_packaging/mmitche-test-transport/nuget/v3/index.json", "dnceng", "public/", "mmitche-test-transport")] [InlineData("https://pkgs.dev.azure.com/DevDiv/public/_packaging/1234.5/nuget/v3/index.json", "DevDiv", "public/", "1234.5")] diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj index ff2898086e26..f3cb15a12eb7 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/Microsoft.DotNet.Build.Tasks.Feed.csproj @@ -11,6 +11,7 @@ + true $(DefineConstants);DOTNET_BUILD_SOURCE_ONLY @@ -54,44 +55,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AssetPublisherFactory.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AssetPublisherFactory.cs index f0af6a336cf0..7efed918fac1 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AssetPublisherFactory.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AssetPublisherFactory.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if !NET472_OR_GREATER using Azure; using System; using Microsoft.Build.Utilities; @@ -53,4 +52,3 @@ private TokenCredential GetAzureTokenCredential(string managedIdentityClientId) } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureDevOpsNugetFeedAssetPublisher.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureDevOpsNugetFeedAssetPublisher.cs index 41fab2058271..235dd3a3a106 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureDevOpsNugetFeedAssetPublisher.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureDevOpsNugetFeedAssetPublisher.cs @@ -9,7 +9,6 @@ using System.Threading; using Microsoft.Build.Utilities; using Microsoft.DotNet.Build.Tasks.Feed.Model; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client.Models; using NuGet.Packaging; using NuGet.Packaging.Core; @@ -94,9 +93,3 @@ public async Task PublishAssetAsync(string file, string blobPath, PushOptions op } } } -#else -public class AzureDevOpsNugetFeedAssetPublisher : Task -{ - public override bool Execute() => throw new NotSupportedException("AzureDevOpsNugetFeedAssetPublisher depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); -} -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureStorageAssetPublisher.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureStorageAssetPublisher.cs index b90c72114dac..686ec25d0127 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureStorageAssetPublisher.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/AzureStorageAssetPublisher.cs @@ -7,14 +7,11 @@ using Azure.Storage.Blobs.Models; using Microsoft.Build.Utilities; using Microsoft.DotNet.Build.CloudTestTasks; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client.Models; -#endif using Task = System.Threading.Tasks.Task; namespace Microsoft.DotNet.Build.Tasks.Feed { -#if !NET472_OR_GREATER public abstract class AzureStorageAssetPublisher : IAssetPublisher { private readonly TaskLoggingHelper _log; @@ -67,19 +64,4 @@ public async Task PublishAssetAsync(string file, string blobPath, PushOptions op } } } -#else - public abstract class AzureStorageAssetPublisher : IAssetPublisher - { - private readonly TaskLoggingHelper _log; - - protected AzureStorageAssetPublisher(TaskLoggingHelper log) - { - _log = log; - } - - public abstract BlobClient CreateBlobClient(string blobPath); - - public Task PublishAssetAsync(string file, string blobPath, PushOptions options, SemaphoreSlim clientThrottle = null) => throw new NotImplementedException(); - } -#endif } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/IAssetPublisher.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/IAssetPublisher.cs index 4c74e001bb48..0d21950b3329 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/IAssetPublisher.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/IAssetPublisher.cs @@ -2,18 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client.Models; -#endif using Task = System.Threading.Tasks.Task; namespace Microsoft.DotNet.Build.Tasks.Feed { public interface IAssetPublisher { -#if !NET472_OR_GREATER LocationType LocationType { get; } -#endif Task PublishAssetAsync(string file, string blobPath, PushOptions options, SemaphoreSlim clientThrottle = null); } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ITargetChannelValidator.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ITargetChannelValidator.cs new file mode 100644 index 000000000000..7f2901c7c555 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ITargetChannelValidator.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.DotNet.Build.Tasks.Feed.Model; + +namespace Microsoft.DotNet.Build.Tasks.Feed +{ + public enum TargetChannelValidationResult + { + Success, + AuditOnlyFailure, + Fail + } + + public enum ValidationMode + { + /// + /// In audit mode, validation failures are reported as AuditOnlyFailure instead of Fail. + /// This allows builds to be published but with warnings about validation issues. + /// + Audit, + + /// + /// In enforce mode, validation failures are reported as Fail, preventing builds from being published. + /// + Enforce + } + + /// + /// Interface for validating whether a build can be published to production channels. + /// + public interface ITargetChannelValidator + { + /// + /// Validates whether the build can be published to the specified target channel. + /// + /// The build information from BAR + /// The target channel the build will be published to + /// ValidationResult indicating the outcome of the validation + Task ValidateAsync(ProductConstructionService.Client.Models.Build build, TargetChannelConfig targetChannel); + } +} diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ProductionChannelValidator.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ProductionChannelValidator.cs new file mode 100644 index 000000000000..55770683772a --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/ProductionChannelValidator.cs @@ -0,0 +1,477 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.DotNet.Build.Tasks.Feed.Model; +using Microsoft.Extensions.Logging; + +namespace Microsoft.DotNet.Build.Tasks.Feed +{ + public class ProductionChannelValidator : ITargetChannelValidator + { + private const string RequiredAzureDevOpsTag = "1ES.PT.Official"; + + private readonly IProductionChannelValidatorBuildInfoService _productionChannelValidatorBuildInfoService; + private readonly IBranchClassificationService _branchClassificationService; + private readonly ILogger _logger; + private readonly ValidationMode _validationMode; + + public ProductionChannelValidator( + IProductionChannelValidatorBuildInfoService productionChannelValidatorBuildInfoService, + IBranchClassificationService branchClassificationService, + ILogger logger, + ValidationMode validationMode = ValidationMode.Enforce) + { + _productionChannelValidatorBuildInfoService = productionChannelValidatorBuildInfoService ?? throw new ArgumentNullException(nameof(productionChannelValidatorBuildInfoService)); + _branchClassificationService = branchClassificationService ?? throw new ArgumentNullException(nameof(branchClassificationService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _validationMode = validationMode; + } + + public async Task ValidateAsync(ProductConstructionService.Client.Models.Build build, TargetChannelConfig targetChannel) + { + if (build == null) + { + throw new ArgumentNullException(nameof(build)); + } + + // If the target channel is not a production channel, no validation is needed + if (!targetChannel.IsProduction) + { + _logger.LogInformation($"Target channel {targetChannel.Id} is not a production channel, skipping validation"); + return TargetChannelValidationResult.Success; + } + + _logger.LogInformation($"Validating build {build.Id} for production channel {targetChannel.Id}"); + + try + { + // Get build information from Azure DevOps which includes tags, project, and repository info + var buildInfo = await GetAzureDevOpsBuildInfoAsync(build); + if (buildInfo == null) + { + return ApplyValidationMode(TargetChannelValidationResult.Fail); + } + + // Step 1: Validate Azure DevOps build has the required tag - always return Fail if tags don't validate + var tagValidationResult = ValidateAzureDevOpsTags(build, buildInfo.Tags); + if (tagValidationResult != TargetChannelValidationResult.Success) + { + return ApplyValidationMode(tagValidationResult); // This will be Fail for missing/invalid tags + } + + // Step 2: Now that tags are validated, check if this is an Azure DevOps repository + if (!IsAzureDevOpsRepository(build.AzureDevOpsRepository)) + { + LogValidationFailure($"Build {build.Id} repository '{build.AzureDevOpsRepository}' is not an Azure DevOps repository."); + return ApplyValidationMode(TargetChannelValidationResult.Fail); + } + + // Step 3: Validate that the branch is a production branch + var branchValidationResult = await ValidateBranchIsProductionAsync(build, buildInfo); + if (branchValidationResult != TargetChannelValidationResult.Success) + { + return ApplyValidationMode(branchValidationResult); + } + + _logger.LogInformation($"Build {build.Id} passed all production channel validations"); + return TargetChannelValidationResult.Success; + } + catch (Exception ex) + { + LogValidationFailure(ex, $"Error validating build {build.Id} for production channel {targetChannel.Id}"); + return ApplyValidationMode(TargetChannelValidationResult.Fail); + } + } + + /// + /// Applies the validation mode to convert Fail results to AuditOnlyFailure when in Audit mode + /// + private TargetChannelValidationResult ApplyValidationMode(TargetChannelValidationResult result) + { + if (_validationMode == ValidationMode.Audit && result == TargetChannelValidationResult.Fail) + { + return TargetChannelValidationResult.AuditOnlyFailure; + } + return result; + } + + /// + /// Logs a validation failure message based on the current validation mode. + /// In Audit mode, logs as a warning. In Enforce mode, logs as an error. + /// + private void LogValidationFailure(string message) + { + if (_validationMode == ValidationMode.Audit) + { + _logger.LogWarning(message); + } + else + { + _logger.LogError(message); + } + } + + /// + /// Logs a validation failure message with exception based on the current validation mode. + /// In Audit mode, logs as a warning. In Enforce mode, logs as an error. + /// + private void LogValidationFailure(Exception exception, string message) + { + if (_validationMode == ValidationMode.Audit) + { + _logger.LogWarning(exception, message); + } + else + { + _logger.LogError(exception, message); + } + } + + private static bool IsAzureDevOpsRepository(string repositoryUrl) + { + if (string.IsNullOrEmpty(repositoryUrl)) + return false; + + return repositoryUrl.Contains("dev.azure.com", StringComparison.OrdinalIgnoreCase) || + repositoryUrl.Contains("visualstudio.com", StringComparison.OrdinalIgnoreCase); + } + + private async Task GetAzureDevOpsBuildInfoAsync(ProductConstructionService.Client.Models.Build build) + { + // Extract Azure DevOps information from build + var azureDevOpsAccount = build.AzureDevOpsAccount; + var azureDevOpsProject = build.AzureDevOpsProject; + var azureDevOpsBuildId = build.AzureDevOpsBuildId; + + if (string.IsNullOrEmpty(azureDevOpsAccount) || + string.IsNullOrEmpty(azureDevOpsProject) || + !azureDevOpsBuildId.HasValue) + { + LogValidationFailure($"Build {build.Id} missing Azure DevOps information for validation"); + return null; + } + + // Don't catch exceptions here - let them bubble up to the main catch block + // which will treat service communication failures as AuditOnlyFailure + return await _productionChannelValidatorBuildInfoService.GetBuildInfoAsync(azureDevOpsAccount, azureDevOpsProject, azureDevOpsBuildId.Value); + } + + private TargetChannelValidationResult ValidateAzureDevOpsTags(ProductConstructionService.Client.Models.Build build, IReadOnlyList tags) + { + try + { + _logger.LogDebug($"Validating Azure DevOps tags for build {build.Id}"); + + if (tags == null) + { + LogValidationFailure($"Build {build.Id} has no tag information available"); + return TargetChannelValidationResult.Fail; + } + + bool hasRequiredTag = tags.Contains(RequiredAzureDevOpsTag, StringComparer.OrdinalIgnoreCase); + + if (hasRequiredTag) + { + _logger.LogDebug($"Build {build.Id} has required tag '{RequiredAzureDevOpsTag}'"); + return TargetChannelValidationResult.Success; + } + else + { + LogValidationFailure($"Build {build.Id} does not have required tag '{RequiredAzureDevOpsTag}'. Found tags: {string.Join(", ", tags)}"); + return TargetChannelValidationResult.Fail; + } + } + catch (Exception ex) + { + LogValidationFailure(ex, $"Error validating Azure DevOps tags for build {build.Id}"); + return TargetChannelValidationResult.Fail; + } + } + + private async Task ValidateBranchIsProductionAsync(ProductConstructionService.Client.Models.Build build, AzureDevOpsBuildInfo buildInfo) + { + // Extract repository and branch information + var azureDevOpsAccount = build.AzureDevOpsAccount; + var azureDevOpsBranch = build.AzureDevOpsBranch; + + if (string.IsNullOrEmpty(azureDevOpsAccount) || + string.IsNullOrEmpty(azureDevOpsBranch)) + { + LogValidationFailure($"Build {build.Id} missing repository information for branch validation"); + return TargetChannelValidationResult.Fail; + } + + string projectId = buildInfo?.Project?.Id; + string repositoryId = buildInfo?.Repository?.Id; + + if (string.IsNullOrEmpty(projectId)) + { + LogValidationFailure($"Build {build.Id}: Could not find project GUID from build info"); + return TargetChannelValidationResult.Fail; + } + + if (string.IsNullOrEmpty(repositoryId)) + { + LogValidationFailure($"Build {build.Id}: Could not find repository GUID from build info"); + return TargetChannelValidationResult.Fail; + } + + // Normalize branch name (remove refs/heads/ prefix if present) + var branchName = NormalizeBranchName(azureDevOpsBranch); + + _logger.LogDebug($"Checking branch classification for {azureDevOpsAccount}/{projectId}/{repositoryId}, branch: {branchName}"); + + try + { + var branchClassifications = await _branchClassificationService.GetBranchClassificationsAsync( + azureDevOpsAccount, projectId, repositoryId); + + bool isProductionBranch = IsProductionBranch(branchName, branchClassifications); + + if (isProductionBranch) + { + _logger.LogDebug($"Branch '{branchName}' is classified as a production branch"); + return TargetChannelValidationResult.Success; + } + else + { + LogValidationFailure($"Branch '{branchName}' is not classified as a production branch"); + return TargetChannelValidationResult.Fail; + } + } + catch (Exception ex) + { + // For branch classification service failures, always log as debug first + _logger.LogDebug(ex, $"Failed to fetch branch classifications for build {build.Id}"); + + // Then apply validation mode specific logging + LogValidationFailure(ex, $"Error validating branch classification for build {build.Id}"); + return TargetChannelValidationResult.Fail; + } + } + + private static string NormalizeBranchName(string branchName) + { + if (string.IsNullOrEmpty(branchName)) + return branchName; + + // Remove refs/heads/ prefix if present + if (branchName.StartsWith("refs/heads/", StringComparison.OrdinalIgnoreCase)) + { + return branchName.Substring("refs/heads/".Length); + } + + return branchName; + } + + private static bool IsProductionBranch(string branchName, BranchClassificationResponse branchClassifications) + { + if (branchClassifications?.BranchClassifications == null) + return false; + + foreach (var classification in branchClassifications.BranchClassifications) + { + if (classification.Classification.Equals("production", StringComparison.OrdinalIgnoreCase)) + { + if (MatchesBranchPattern(branchName, classification.BranchName)) + { + return true; + } + } + } + + return false; + } + + private static bool MatchesBranchPattern(string branchName, string pattern) + { + if (string.IsNullOrEmpty(branchName) || string.IsNullOrEmpty(pattern)) + return false; + + // Handle exact match + if (pattern.Equals(branchName, StringComparison.OrdinalIgnoreCase)) + return true; + + // Handle wildcard patterns (e.g., "release/*") + if (pattern.EndsWith("/*")) + { + var prefix = pattern.Substring(0, pattern.Length - 2); + return branchName.StartsWith(prefix + "/", StringComparison.OrdinalIgnoreCase); + } + + // Handle special ~default pattern + if (pattern.Equals("~default", StringComparison.OrdinalIgnoreCase)) + { + // This would require additional logic to determine the default branch + // For now, treat common default branch names as matching + return branchName.Equals("main", StringComparison.OrdinalIgnoreCase) || + branchName.Equals("master", StringComparison.OrdinalIgnoreCase); + } + + return false; + } + } + + // Interface definitions for dependency injection and testability + public interface IProductionChannelValidatorBuildInfoService + { + Task GetBuildInfoAsync(string account, string project, int buildId); + } + + public interface IBranchClassificationService + { + Task GetBranchClassificationsAsync(string organizationName, string projectId, string repositoryId); + } + + // Response models for Azure DevOps API + public class AzureDevOpsBuildInfo + { + public AzureDevOpsProject Project { get; set; } + public AzureDevOpsRepository Repository { get; set; } + public IReadOnlyList Tags { get; set; } + } + + public class AzureDevOpsProject + { + public string Id { get; set; } + public string Name { get; set; } + } + + public class AzureDevOpsRepository + { + public string Id { get; set; } + public string Name { get; set; } + } + + // Response models for the branch classification API + public class BranchClassificationResponse + { + public IReadOnlyList BranchClassifications { get; set; } + public string Status { get; set; } + } + + public class BranchClassification + { + public string BranchName { get; set; } + public string Classification { get; set; } + } + + // Implementation classes + public class AzureDevOpsService : IProductionChannelValidatorBuildInfoService + { + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private readonly string _token; + + public AzureDevOpsService(HttpClient httpClient, ILogger logger, string token = null) + { + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _token = token; + + // Configure authentication for Azure DevOps API using Basic authentication + if (!string.IsNullOrEmpty(_token)) + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Basic", + Convert.ToBase64String(Encoding.ASCII.GetBytes($":{_token}"))); + } + } + + public async Task GetBuildInfoAsync(string account, string project, int buildId) + { + try + { + var url = $"https://dev.azure.com/{account}/{project}/_apis/build/builds/{buildId}?api-version=6.0"; + _logger.LogDebug($"Fetching build info from: {url}"); + + var response = await _httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + var buildResponse = JsonSerializer.Deserialize(content, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + return new AzureDevOpsBuildInfo + { + Project = buildResponse?.Project, + Repository = buildResponse?.Repository, + Tags = buildResponse?.Tags ?? new List() + }; + } + catch (Exception ex) + { + _logger.LogDebug(ex, $"Error fetching build info for {account}/{project}/{buildId}"); + throw; + } + } + + private class AzureDevOpsBuildInfoResponse + { + public AzureDevOpsProject Project { get; set; } + public AzureDevOpsRepository Repository { get; set; } + public List Tags { get; set; } + } + } + + public class BranchClassificationService : IBranchClassificationService + { + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private readonly string _token; + + public BranchClassificationService(HttpClient httpClient, ILogger logger, string token = null) + { + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _token = token; + } + + public async Task GetBranchClassificationsAsync(string organizationName, string projectId, string repositoryId) + { + try + { + var url = string.Format(CultureInfo.InvariantCulture, + "https://BranchClassification.app.prod.gitops.startclean.microsoft.com/api/getBranchClassifications/{0}/{1}/{2}", + organizationName, projectId, repositoryId); + _logger.LogDebug($"Fetching branch classifications from: {url}"); + + using var request = new HttpRequestMessage(HttpMethod.Get, url); + + // Add Bearer token authentication for branch classification service + if (!string.IsNullOrEmpty(_token)) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); + } + + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + var classification = JsonSerializer.Deserialize(content, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + return classification; + } + catch (Exception ex) + { + _logger.LogDebug(ex, $"Error fetching branch classifications for {organizationName}/{projectId}/{repositoryId}"); + throw; + } + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifest.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifest.cs index c50fd0e6a941..480d59853956 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifest.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifest.cs @@ -5,13 +5,14 @@ using Microsoft.Build.Framework; using Microsoft.DotNet.Build.Tasks.Feed.Model; using Microsoft.DotNet.Build.Manifest; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using System; using System.IO; using System.Linq; +using System.Net.Http; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; @@ -213,8 +214,17 @@ public string BuildQuality public int NonStreamingPublishingMaxClients {get; set;} + /// + /// Whether to enforce production channel validation rules. + /// If true, validation failures prevent builds from being published (Fail). + /// If false, validation failures are reported as warnings but builds are allowed to be published (AuditOnlyFailure). + /// Default: false (audit mode) + /// + public bool EnforceProduction { get; set; } = false; + private IBuildModelFactory _buildModelFactory; private IFileSystem _fileSystem; + private ITargetChannelValidator _targetChannelValidator; private PublishingConstants.BuildQuality _buildQuality; @@ -227,14 +237,53 @@ public override void ConfigureServices(IServiceCollection collection) collection.TryAddSingleton(); collection.TryAddSingleton(); collection.TryAddSingleton(); + + // Register ProductionChannelValidator with the specified validation mode + collection.TryAddSingleton(provider => + { + var buildInfoService = provider.GetRequiredService(); + var branchClassificationService = provider.GetRequiredService(); + var loggerFactory = provider.GetRequiredService(); + var logger = loggerFactory.CreateLogger(); + + // Convert EnforceProduction boolean to ValidationMode enum + var validationMode = EnforceProduction ? ValidationMode.Enforce : ValidationMode.Audit; + + return new ProductionChannelValidator(buildInfoService, branchClassificationService, logger, validationMode); + }); + + // Add logging services + collection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug)); + + // Register AzureDevOpsService with proper authentication + collection.TryAddSingleton(provider => + { + var httpClient = provider.GetRequiredService(); + var loggerFactory = provider.GetRequiredService(); + var logger = loggerFactory.CreateLogger(); + return new AzureDevOpsService(httpClient, logger, AzdoApiToken); + }); + + // Register BranchClassificationService with proper authentication + collection.TryAddSingleton(provider => + { + var httpClient = provider.GetRequiredService(); + var loggerFactory = provider.GetRequiredService(); + var logger = loggerFactory.CreateLogger(); + return new BranchClassificationService(httpClient, logger, AzdoApiToken); + }); + + collection.TryAddSingleton(); collection.TryAddSingleton(Log); } public bool ExecuteTask(IBuildModelFactory buildModelFactory, - IFileSystem fileSystem) + IFileSystem fileSystem, + ITargetChannelValidator targetChannelValidator) { _buildModelFactory = buildModelFactory; _fileSystem = fileSystem; + _targetChannelValidator = targetChannelValidator; return ExecuteAsync().GetAwaiter().GetResult(); } @@ -358,7 +407,7 @@ internal PublishArtifactsInManifestBase ConstructPublishingV3Task(BuildModel bui SymbolPublishingExclusionsFile = this.SymbolPublishingExclusionsFile, PublishSpecialClrFiles = this.PublishSpecialClrFiles, BuildQuality = this.BuildQuality, - ArtifactsBasePath = this.ArtifactsBasePath, + ArtifactsBasePath = this.ArtifactsBasePath, AzdoApiToken = this.AzdoApiToken, BuildId = this.BuildId, AzureDevOpsProject = this.AzureProject, @@ -368,7 +417,9 @@ internal PublishArtifactsInManifestBase ConstructPublishingV3Task(BuildModel bui SymbolRequestProject = this.SymbolRequestProject, UseStreamingPublishing = this.UseStreamingPublishing, StreamingPublishingMaxClients = this.StreamingPublishingMaxClients, - NonStreamingPublishingMaxClients = this.NonStreamingPublishingMaxClients + NonStreamingPublishingMaxClients = this.NonStreamingPublishingMaxClients, + EnforceProduction = this.EnforceProduction, + TargetChannelValidator = _targetChannelValidator }; } @@ -416,14 +467,10 @@ internal PublishArtifactsInManifestBase ConstructPublishingV4Task(BuildModel bui SymbolRequestProject = this.SymbolRequestProject, UseStreamingPublishing = this.UseStreamingPublishing, StreamingPublishingMaxClients = this.StreamingPublishingMaxClients, - NonStreamingPublishingMaxClients = this.NonStreamingPublishingMaxClients + NonStreamingPublishingMaxClients = this.NonStreamingPublishingMaxClients, + EnforceProduction = this.EnforceProduction, + TargetChannelValidator = _targetChannelValidator }; } } } -#else -public class PublishArtifactsInManifest : Microsoft.Build.Utilities.Task -{ - public override bool Execute() => throw new System.NotSupportedException("PublishArtifactsInManifest depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); -} -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs index cc9f553f60f6..90f94d2cee92 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs @@ -22,13 +22,11 @@ using Microsoft.Arcade.Common; using Microsoft.Build.Framework; using Microsoft.DotNet.Build.Tasks.Feed.Model; -#if !NET472_OR_GREATER using Azure.Identity; using Microsoft.DotNet.ProductConstructionService.Client; using Microsoft.DotNet.ProductConstructionService.Client.Models; using Microsoft.DotNet.Internal.SymbolHelper; using Microsoft.DotNet.ArcadeAzureIntegration; -#endif using Microsoft.DotNet.Build.Manifest; using Newtonsoft.Json; using NuGet.Versioning; @@ -39,7 +37,6 @@ namespace Microsoft.DotNet.Build.Tasks.Feed { -#if NET /// /// The intended use of this task is to push artifacts described in /// a build manifest to a static package feed. @@ -48,6 +45,11 @@ public abstract class PublishArtifactsInManifestBase : Microsoft.Build.Utilities { public AssetPublisherFactory AssetPublisherFactory { get; } + /// + /// Production channel validator for validating builds before publishing to production channels. + /// + public ITargetChannelValidator TargetChannelValidator { get; set; } + /// /// Full path to the folder containing blob assets. /// @@ -109,6 +111,14 @@ public abstract class PublishArtifactsInManifestBase : Microsoft.Build.Utilities /// public int MaxClients { get { return UseStreamingPublishing ? StreamingPublishingMaxClients : NonStreamingPublishingMaxClients; } } + /// + /// Whether to enforce production channel validation rules. + /// If true, validation failures prevent builds from being published (Fail). + /// If false, validation failures are reported as warnings but builds are allowed to be published (AuditOnlyFailure). + /// Default: false (audit mode) + /// + public bool EnforceProduction { get; set; } = false; + /// /// Whether this build is internal or not. If true, extra checks are done to avoid accidental /// publishing of assets to public feeds or storage accounts. @@ -192,7 +202,7 @@ public abstract class PublishArtifactsInManifestBase : Microsoft.Build.Utilities private ConcurrentDictionary _artifactUrlHelpers = new ConcurrentDictionary(); // Matches versions such as 1.0.0.1234 - private static readonly string FourPartVersionPattern = @"\d+\.\d+\.\d+\.\d+"; + private static readonly string FourPartVersionPattern = @"\d+\.\d+\.\d+\.\d+"; private static Regex FourPartVersionRegex = new Regex(FourPartVersionPattern); @@ -227,9 +237,11 @@ public abstract class PublishArtifactsInManifestBase : Microsoft.Build.Utilities private int TimeoutInSeconds = 300; - protected PublishArtifactsInManifestBase(AssetPublisherFactory assetPublisherFactory = null) + protected PublishArtifactsInManifestBase(AssetPublisherFactory assetPublisherFactory = null, + ITargetChannelValidator targetChannelValidator = null) { AssetPublisherFactory = assetPublisherFactory ?? new AssetPublisherFactory(Log); + TargetChannelValidator = targetChannelValidator; } public override bool Execute() @@ -239,6 +251,47 @@ public override bool Execute() public abstract Task ExecuteAsync(); + /// + /// Validates whether the build can be published to a production channel. + /// This method uses the injected ITargetChannelValidator to perform the validation. + /// + /// The build information from BAR + /// The target channel the build will be published to + /// True if the build is allowed to be published to the target channel, false otherwise + protected async Task ValidateTargetChannelAsync(ProductConstructionService.Client.Models.Build build, TargetChannelConfig targetChannel) + { + if (targetChannel.IsProduction) + { + Log.LogMessage(MessageImportance.Normal, $"Validating production channel {targetChannel.Id}"); + var validationResult = await TargetChannelValidator.ValidateAsync(build, targetChannel); + + switch (validationResult) + { + case TargetChannelValidationResult.Success: + Log.LogMessage(MessageImportance.Normal, $"Build validation succeeded for production channel {targetChannel.Id}"); + return true; + + case TargetChannelValidationResult.AuditOnlyFailure: + Log.LogWarning($"Build validation audit failure for production channel {targetChannel.Id}. This is currently treated as a warning."); + return true; // For now, treat audit-only failures as success but log warning + + case TargetChannelValidationResult.Fail: + Log.LogError($"Build validation failed for production channel {targetChannel.Id}"); + return false; + + default: + Log.LogError($"Unknown validation result '{validationResult}' for production channel {targetChannel.Id}"); + return false; + } + } + else + { + Log.LogMessage(MessageImportance.Normal, $"Skipping validation for non-production channel {targetChannel.Id}"); + } + + return true; + } + /// /// Lookup an asset in the build asset dictionary by name and version /// @@ -313,8 +366,8 @@ protected ReadOnlyDictionary CreateBuildAssetDictionary(ProductCo /// True if that asset didn't have the informed location recorded already. private bool TryAddAssetLocation(string assetId, string assetVersion, ReadOnlyDictionary buildAssets, TargetFeedConfig feedConfig, LocationType assetLocationType) { - Asset assetRecord = string.IsNullOrEmpty(assetVersion) ? - LookupAsset(assetId, buildAssets) : + Asset assetRecord = string.IsNullOrEmpty(assetVersion) ? + LookupAsset(assetId, buildAssets) : LookupAsset(assetId, assetVersion, buildAssets); if (assetRecord == null) @@ -701,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) { @@ -1312,7 +1365,7 @@ public async Task PushNugetPackagesAsync( HashSet packagesToPublish, TargetFeedConfig feedConfig, int maxClients, - Func packagePublishAction) + Func packagePublishAction) { if (!packagesToPublish.Any()) { @@ -1443,6 +1496,7 @@ public async Task PushNugetPackageAsync( // Using these callbacks we can mock up functionality when testing. CompareLocalPackageToFeedPackageCallBack ??= CompareLocalPackageToFeedPackage; AttemptPushPackageCallback ??= NuGetFeedUploadPackageAsync; + var packageStatus = PackageFeedStatus.Unknown; try @@ -1761,7 +1815,7 @@ await PushNugetPackageAsync( } /// - /// Create Temporary directory if it does not exists. + /// Creates a temporary directory if it does not exists. /// /// public void EnsureTemporaryDirectoryExists(string temporaryLocation) @@ -1883,32 +1937,3 @@ protected bool AnyMissingRequiredBaseProperties() } } } -#else - public abstract class PublishArtifactsInManifestBase : Microsoft.Build.Utilities.Task - { - public override bool Execute() => throw new NotSupportedException("PublishArtifactsInManifestBase depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); - - public abstract Task ExecuteAsync(); - - public Task PushNugetPackagesAsync( - HashSet packagesToPublish, - TargetFeedConfig feedConfig, - int maxClients, - Func packagePublishAction) - => throw new NotSupportedException("PublishArtifactsInManifestBase depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); - - public Task PushNugetPackageAsync( - TargetFeedConfig feedConfig, - HttpClient client, - string localPackageLocation, - string id, - string version, - string feedAccount, - string feedVisibility, - string feedName, - Func> CompareLocalPackageToFeedPackageCallBack = null, - Func> RunProcessAndGetOutputsCallBack = null - ) => throw new NotSupportedException("PublishArtifactsInManifestBase depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); - } -} -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV3.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV3.cs index 3fb6f47440d1..71f032dddf54 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV3.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV3.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.Build.Framework; using Microsoft.DotNet.Build.Tasks.Feed.Model; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client; using Microsoft.DotNet.ProductConstructionService.Client.Models; using Microsoft.DotNet.Build.Manifest; @@ -126,6 +125,13 @@ public override async Task ExecuteAsync() return false; } + // Validate that the channel can be be used for this build + if (!await ValidateTargetChannelAsync(buildInformation, targetChannelConfig)) + { + Log.LogError($"Channel with ID '{targetChannelId}' is not valid for this build."); + return false; + } + Log.LogMessage(MessageImportance.High, $"Publishing to this target channel: {targetChannelConfig}"); List shortLinkUrls = new List(); @@ -229,9 +235,3 @@ public PublishArtifactsInManifestV3(AssetPublisherFactory assetPublisherFactory } } } -#else -public class PublishArtifactsInManifestV3 : Microsoft.Build.Utilities.Task -{ - public override bool Execute() => throw new NotSupportedException("PublishArtifactsInManifestV3 depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); -} -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV4.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV4.cs index a0ff7f1b6ab4..7fee2b5e242a 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV4.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestV4.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.Build.Framework; using Microsoft.DotNet.Build.Tasks.Feed.Model; -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client; using Microsoft.DotNet.ProductConstructionService.Client.Models; using Microsoft.DotNet.Build.Manifest; @@ -122,6 +121,13 @@ public override async Task ExecuteAsync() return false; } + // Validate that the channel can be be used for this build + if (!await ValidateTargetChannelAsync(buildInformation, targetChannelConfig)) + { + Log.LogError($"Channel with ID '{targetChannelId}' is not valid for this build."); + return false; + } + Log.LogMessage(MessageImportance.High, $"Publishing to this target channel: {targetChannelConfig}"); List shortLinkUrls = new List(); @@ -239,9 +245,3 @@ public PublishArtifactsInManifestV4(AssetPublisherFactory assetPublisherFactory } } } -#else -public class PublishArtifactsInManifestV4 : Microsoft.Build.Utilities.Task -{ - public override bool Execute() => throw new NotSupportedException("PublishArtifactsInManifestV4 depends on ProductConstructionService.Client, which has discontinued support for desktop frameworks."); -} -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs index 7b284481ff26..80e05cb284a5 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs +++ b/src/arcade/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); diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/TaskTracer.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/TaskTracer.cs index 7ef4554334a0..bd5af0eb5662 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/TaskTracer.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/TaskTracer.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if NET using Microsoft.Build.Framework; using MsBuildUtils = Microsoft.Build.Utilities; @@ -68,4 +67,3 @@ public void Verbose(string format, params object[] arguments) _log.LogMessage(_verbose ? MessageImportance.Normal : MessageImportance.Low, format, arguments); } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AssetComparer.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AssetComparer.cs index dc743d5e12fb..fef8e5d3c756 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AssetComparer.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AssetComparer.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if !NET472_OR_GREATER using Microsoft.DotNet.ProductConstructionService.Client.Models; using System.Collections.Generic; @@ -39,4 +38,3 @@ public int GetHashCode(Asset asset) } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/LatestLinksManager.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/LatestLinksManager.cs index d2e634874858..cf5a509ac587 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/LatestLinksManager.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/LatestLinksManager.cs @@ -22,12 +22,6 @@ public class LatestLinksManager private string _akaMSCreatedBy { get; } private string _akaMSGroupOwner { get; } - private static Dictionary AccountsWithCdns { get; } = new() - { - {"dotnetcli.blob.core.windows.net", "builds.dotnet.microsoft.com" }, - {"dotnetbuilds.blob.core.windows.net", "ci.dot.net" } - }; - public LatestLinksManager( string akaMSClientId, X509Certificate2 certificate, @@ -106,14 +100,6 @@ public static string ComputeLatestLinkBase(TargetFeedConfig feedConfig) { feedBaseUrl += "/"; } - var authority = new Uri(feedBaseUrl).Authority; - if (AccountsWithCdns.TryGetValue(authority, out var replacementAuthority)) - { - // The storage accounts are in a single datacenter in the US and thus download - // times can be painful elsewhere. The CDN helps with this therefore we point the target - // of the aka.ms links to the CDN. - feedBaseUrl = feedBaseUrl.Replace(authority, replacementAuthority); - } return feedBaseUrl; } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/PublishingConstants.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/PublishingConstants.cs index 7811173aead4..b6c2fce2d68f 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/PublishingConstants.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/PublishingConstants.cs @@ -13,13 +13,24 @@ public class PublishingConstants { public static readonly string ExpectedFeedUrlSuffix = "index.json"; + /// + /// Mapping of Azure storage accounts to their corresponding CDN URLs. + /// Used to replace blob storage URLs with CDN URLs for both aka.ms links and asset locations. + /// + public static readonly Dictionary AccountsWithCdns = new() + { + {"dotnetcli.blob.core.windows.net", "builds.dotnet.microsoft.com" }, + {"dotnetbuilds.blob.core.windows.net", "ci.dot.net" } + }; + // Matches package feeds like // https://pkgs.dev.azure.com/dnceng/public/_packaging/public-feed-name/nuget/v3/index.json // or https://pkgs.dev.azure.com/dnceng/_packaging/internal-feed-name/nuget/v3/index.json public static readonly string AzDoNuGetFeedPattern = @"https://pkgs.dev.azure.com/(?[a-zA-Z0-9-]+)/(?[a-zA-Z0-9-]+/)?_packaging/(?.+)/nuget/v3/index.json"; - public static readonly TargetFeedContentType[] InstallersAndSymbols = { + public static readonly TargetFeedContentType[] InstallersAndSymbols = + [ TargetFeedContentType.OSX, TargetFeedContentType.Deb, TargetFeedContentType.Rpm, @@ -31,9 +42,10 @@ public class PublishingConstants TargetFeedContentType.Badge, TargetFeedContentType.Symbols, TargetFeedContentType.Other - }; + ]; - public static readonly TargetFeedContentType[] InstallersAndChecksums = { + public static readonly TargetFeedContentType[] InstallersAndChecksums = + [ TargetFeedContentType.OSX, TargetFeedContentType.Deb, TargetFeedContentType.Rpm, @@ -45,15 +57,16 @@ public class PublishingConstants TargetFeedContentType.Badge, TargetFeedContentType.Checksum, TargetFeedContentType.Other - }; + ]; - public static readonly TargetFeedContentType[] Packages = { + public static readonly TargetFeedContentType[] Packages = + [ TargetFeedContentType.Package, TargetFeedContentType.CorePackage, TargetFeedContentType.ToolingPackage, TargetFeedContentType.InfrastructurePackage, TargetFeedContentType.LibraryPackage, - }; + ]; public enum BuildQuality { @@ -80,7 +93,10 @@ public enum BuildQuality public const string FeedStagingInternalForInstallers = "https://dotnetbuilds.blob.core.windows.net/internal"; public const string FeedStagingInternalForChecksums = "https://dotnetbuilds.blob.core.windows.net/internal-checksums"; - private const string FeedGeneralTesting = "https://pkgs.dev.azure.com/dnceng/public/_packaging/general-testing/nuget/v3/index.json"; + public const string FeedDevForInstallers = "https://dotnetbuilds.blob.core.windows.net/dev"; + public const string FeedDevInternalForInstallers = "https://dotnetbuilds.blob.core.windows.net/dev-internal"; + + private const string FeedDev = "https://pkgs.dev.azure.com/dnceng/public/_packaging/general-testing/nuget/v3/index.json"; private const string FeedDotNetExperimental = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json"; @@ -88,6 +104,8 @@ public enum BuildQuality public const string FeedDotNetEng = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json"; + private const string FeedDotNetEngInternal = "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/v3/index.json"; + private const string FeedDotNetTools = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json"; private const string FeedDotNetToolsInternal = "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/v3/index.json"; @@ -125,6 +143,10 @@ public enum BuildQuality private const string FeedDotNet10InternalShipping = "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet10-internal/nuget/v3/index.json"; private const string FeedDotNet10InternalTransport = "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet10-internal-transport/nuget/v3/index.json"; + private const string FeedDotNet11Shipping = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet11/nuget/v3/index.json"; + private const string FeedDotNet11Transport = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet11-transport/nuget/v3/index.json"; + private const string FeedDotNet11Workloads = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet11-workloads/nuget/v3/index.json"; + private const string FeedDotNetLibrariesShipping = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json"; private const string FeedDotNetLibrariesTransport = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries-transport/nuget/v3/index.json"; @@ -149,22 +171,6 @@ public enum BuildQuality (TargetFeedContentType.Checksum, FeedStagingInternalForChecksums), }; - private static TargetFeedSpecification[] DotNet7Feeds = - { - (Packages, FeedDotNet7Shipping, AssetSelection.ShippingOnly), - (Packages, FeedDotNet7Transport, AssetSelection.NonShippingOnly), - (InstallersAndSymbols, FeedStagingForInstallers), - (TargetFeedContentType.Checksum, FeedStagingForChecksums), - }; - - private static TargetFeedSpecification[] DotNet7InternalFeeds = - { - (Packages, FeedDotNet7InternalShipping, AssetSelection.ShippingOnly), - (Packages, FeedDotNet7InternalTransport, AssetSelection.NonShippingOnly), - (InstallersAndSymbols, FeedStagingInternalForInstallers), - (TargetFeedContentType.Checksum, FeedStagingInternalForChecksums), - }; - private static TargetFeedSpecification[] DotNet8Feeds = { (Packages, FeedDotNet8Shipping, AssetSelection.ShippingOnly), @@ -231,8 +237,16 @@ public enum BuildQuality private static TargetFeedSpecification[] DotNet10InternalFeeds = { - (Packages, FeedDotNet10InternalShipping, AssetSelection.ShippingOnly), - (Packages, FeedDotNet10InternalTransport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.Package, FeedDotNet10InternalShipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.Package, FeedDotNet10InternalTransport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.InfrastructurePackage, FeedDotNetEngInternal, AssetSelection.ShippingOnly), + (TargetFeedContentType.InfrastructurePackage, FeedDotNetEngInternal, AssetSelection.NonShippingOnly), + (TargetFeedContentType.CorePackage, FeedDotNet10InternalShipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.CorePackage, FeedDotNet10InternalTransport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.LibraryPackage, FeedDotNetLibrariesInternalShipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.LibraryPackage, FeedDotNetLibrariesInternalTransport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.ToolingPackage, FeedDotNetToolsInternal, AssetSelection.ShippingOnly), + (TargetFeedContentType.ToolingPackage, FeedDotNetToolsInternal, AssetSelection.NonShippingOnly), (InstallersAndSymbols, FeedStagingInternalForInstallers), (TargetFeedContentType.Checksum, FeedStagingInternalForChecksums), }; @@ -245,6 +259,30 @@ public enum BuildQuality (TargetFeedContentType.Checksum, FeedStagingForChecksums), }; + private static TargetFeedSpecification[] DotNet11Feeds = + { + (TargetFeedContentType.Package, FeedDotNet11Shipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.Package, FeedDotNet11Transport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.InfrastructurePackage, FeedDotNetEng, AssetSelection.ShippingOnly), + (TargetFeedContentType.InfrastructurePackage, FeedDotNetEng, AssetSelection.NonShippingOnly), + (TargetFeedContentType.CorePackage, FeedDotNet11Shipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.CorePackage, FeedDotNet11Transport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.LibraryPackage, FeedDotNetLibrariesShipping, AssetSelection.ShippingOnly), + (TargetFeedContentType.LibraryPackage, FeedDotNetLibrariesTransport, AssetSelection.NonShippingOnly), + (TargetFeedContentType.ToolingPackage, FeedDotNetTools, AssetSelection.ShippingOnly), + (TargetFeedContentType.ToolingPackage, FeedDotNetTools, AssetSelection.NonShippingOnly), + (InstallersAndSymbols, FeedStagingForInstallers), + (TargetFeedContentType.Checksum, FeedStagingForChecksums), + }; + + private static TargetFeedSpecification[] DotNet11WorkloadFeeds = + { + (Packages, FeedDotNet11Workloads, AssetSelection.ShippingOnly), + (Packages, FeedDotNet11Workloads, AssetSelection.NonShippingOnly), + (InstallersAndSymbols, FeedStagingForInstallers), + (TargetFeedContentType.Checksum, FeedStagingForChecksums), + }; + private static TargetFeedSpecification[] DotNetEngFeeds = { (Packages, FeedDotNetEng, AssetSelection.ShippingOnly), @@ -303,22 +341,23 @@ public enum BuildQuality private static TargetFeedSpecification[] GeneralTestingFeeds = { - (Packages, FeedGeneralTesting, AssetSelection.ShippingOnly), - (Packages, FeedGeneralTesting, AssetSelection.NonShippingOnly), - (InstallersAndSymbols, FeedStagingForInstallers), - (TargetFeedContentType.Checksum, FeedStagingForChecksums), + (Packages, FeedDev, AssetSelection.ShippingOnly), + (Packages, FeedDev, AssetSelection.NonShippingOnly), + (InstallersAndSymbols, FeedDevForInstallers), + (TargetFeedContentType.Checksum, FeedDevForInstallers), }; private static TargetFeedSpecification[] GeneralTestingInternalFeeds = { (Packages, FeedGeneralTestingInternal, AssetSelection.ShippingOnly), (Packages, FeedGeneralTestingInternal, AssetSelection.NonShippingOnly), - (InstallersAndSymbols, FeedStagingInternalForInstallers), - (TargetFeedContentType.Checksum, FeedStagingInternalForChecksums), + (InstallersAndSymbols, FeedDevInternalForInstallers), + (TargetFeedContentType.Checksum, FeedDevInternalForInstallers), }; #endregion - public static readonly ImmutableList DefaultAkaMSCreateLinkPatterns = [ + public static readonly ImmutableList DefaultAkaMSCreateLinkPatterns = + [ new Regex(@"\.rpm(\.sha512)?$", RegexOptions.IgnoreCase), new Regex(@"\.zip(\.sha512)?$", RegexOptions.IgnoreCase), new Regex(@"\.version(\.sha512)?$", RegexOptions.IgnoreCase), @@ -335,17 +374,18 @@ public enum BuildQuality new Regex(@"productversion", RegexOptions.IgnoreCase) ]; - public static readonly ImmutableList DefaultAkaMSDoNotCreateLinkPatterns = [ + public static readonly ImmutableList DefaultAkaMSDoNotCreateLinkPatterns = + [ new Regex(@"wixpack", RegexOptions.IgnoreCase), ]; - private static readonly ImmutableList DotNet10AkaMSDoNotCreateLinkPatterns = [ + private static readonly ImmutableList UnifiedBuildAkaMSDoNotCreateLinkPatterns = + [ ..DefaultAkaMSDoNotCreateLinkPatterns, new Regex(@"productversion", RegexOptions.IgnoreCase) ]; - #region Target Channel Configs - public static readonly List ChannelInfos = new List() { + public static readonly List ChannelInfos = [ // How TO: Adding publishing for a new channel: // 1. If not already complete, add desired using `darc add-channel`. Please using follow naming conventions from @@ -366,6 +406,8 @@ public enum BuildQuality // - symbolTargetType: List of symbol targets. Internal channels should use SymbolPublishVisibility.Internal and public channels should use SymbolPublishVisibility.Public // - filenamesToExclude: Usually left as FilenamesToExclude. + #region .NET 6 Channels + // .NET 6, new TargetChannelConfig( id: 1296, @@ -521,6 +563,10 @@ public enum BuildQuality targetFeeds: DotNet6InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + #endregion + + #region .NET 8 Channels + // .NET 8, new TargetChannelConfig( id: 3073, @@ -588,6 +634,17 @@ public enum BuildQuality targetFeeds: DotNet8InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8 HotFix Internal, + new TargetChannelConfig( + id: 8624, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/8.0-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet8InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.1xx SDK, new TargetChannelConfig( id: 3074, @@ -610,6 +667,17 @@ public enum BuildQuality targetFeeds: DotNet8InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.1xx SDK HotFix Internal, + new TargetChannelConfig( + id: 8625, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/8.0.1xx-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet8InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.2xx SDK, new TargetChannelConfig( id: 4036, @@ -654,6 +722,17 @@ public enum BuildQuality targetFeeds: DotNet8InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.3xx SDK HotFix Internal, + new TargetChannelConfig( + id: 8627, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/8.0.3xx-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet8InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.4xx SDK, new TargetChannelConfig( id: 4586, @@ -676,6 +755,21 @@ public enum BuildQuality targetFeeds: DotNet8InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 8.0.4xx SDK HotFix Internal, + new TargetChannelConfig( + id: 8628, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/8.0.4xx-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet8InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + + #endregion + + #region .NET 9 Channels + // .NET 9, new TargetChannelConfig( id: 3883, @@ -732,6 +826,17 @@ public enum BuildQuality targetFeeds: DotNet9InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 9 HotFix Internal, + new TargetChannelConfig( + id: 8629, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/9.0-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet9InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 9 Workload Release, new TargetChannelConfig( id: 4611, @@ -765,6 +870,17 @@ public enum BuildQuality targetFeeds: DotNet9InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 9.0.1xx SDK HotFix Internal, + new TargetChannelConfig( + id: 8630, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/9.0.1xx-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet9InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 9.0.2xx SDK, new TargetChannelConfig( id: 5286, @@ -809,6 +925,21 @@ public enum BuildQuality targetFeeds: DotNet9InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), + // .NET 9.0.3xx SDK HotFix Internal, + new TargetChannelConfig( + id: 8632, + isInternal: true, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: ["internal/9.0.3xx-hotfix"], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet9InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), + + #endregion + + #region .NET 10 Channels + // .NET 10, new TargetChannelConfig( id: 5172, @@ -816,183 +947,173 @@ public enum BuildQuality publishingInfraVersion: PublishingInfraVersion.Latest, akaMSChannelNames: ["10.0"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 Workload Release, + // .NET 10 Internal, new TargetChannelConfig( - id: 5174, - isInternal: false, + id: 5177, + isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: [ "10.0-workloads" ], + akaMSChannelNames: ["internal/10.0"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10WorkloadFeeds, - symbolTargetType: SymbolPublishVisibility.Public), + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet10InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10.0.1xx SDK, + // .NET 10 Private, new TargetChannelConfig( - id: 5173, - isInternal: false, + id: 8710, + isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: [ "10.0.1xx", "10.0" ], + akaMSChannelNames: ["internal/10.0-private"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet10InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10 UB, + // .NET 10 Eng, new TargetChannelConfig( - id: 5708, + id: 8394, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-ub", "10.0-ub"], + akaMSChannelNames: ["eng/net10"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetEngFeeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 Preview 1, + // .NET 10 Eng - Validation, new TargetChannelConfig( - id: 6545, + id: 8395, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview1"], + akaMSChannelNames: ["eng/net10validation"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetEngFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), - // .NET 10 Preview 2, + // .NET 10 Workload Release, new TargetChannelConfig( - id: 6547, + id: 5174, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview2"], + akaMSChannelNames: [ "10.0-workloads" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet10WorkloadFeeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 Preview 3, + // .NET 10.0.1xx SDK, new TargetChannelConfig( - id: 6549, + id: 5173, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview3"], + akaMSChannelNames: [ "10.0.1xx", "10.0" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 Preview 4, + // .NET 10.0.1xx SDK Release, new TargetChannelConfig( - id: 6551, + id: 8859, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview4"], + akaMSChannelNames: [ "10.0.1xx-release", "10.0-release" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 Preview 5, + // .NET 10.0.1xx SDK Internal, new TargetChannelConfig( - id: 6553, - isInternal: false, + id: 5178, + isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview5"], + akaMSChannelNames: ["internal/10.0.1xx", "internal/10.0"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet10InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10 Preview 6, + // .NET 10.0.1xx SDK Release Internal, new TargetChannelConfig( - id: 6555, - isInternal: false, + id: 8858, + isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview6"], + akaMSChannelNames: ["internal/10.0.1xx-release", "internal/10.0-release"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet10InternalFeeds, + symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10 Preview 7, + // .NET 10.0.2xx SDK, new TargetChannelConfig( - id: 6557, + id: 8856, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-preview7"], + akaMSChannelNames: [ "10.0.2xx", "10.0" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 RC 1, + // .NET 10.0.2xx SDK Release, new TargetChannelConfig( - id: 6494, + id: 8860, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-rc1"], + akaMSChannelNames: [ "10.0.2xx-release" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10 RC 1 Internal, + // .NET 10.0.2xx SDK Internal, new TargetChannelConfig( - id: 6496, + id: 8857, isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["internal/10.0-rc1"], + akaMSChannelNames: ["internal/10.0.2xx"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10 RC 2, + // .NET 10.0.2xx SDK Internal, new TargetChannelConfig( - id: 6498, - isInternal: false, - publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0-rc2"], - akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), - - // .NET 10 RC 2 Internal, - new TargetChannelConfig( - id: 6500, + id: 8861, isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["internal/10.0-rc2"], + akaMSChannelNames: ["internal/10.0.2xx-release"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10.0.1xx RC 1, + // .NET 10 RC 2, new TargetChannelConfig( - id: 6573, + id: 6498, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-rc1"], + akaMSChannelNames: ["10.0-rc2"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10.0.1xx RC 1 Internal, + // .NET 10 RC 2 Internal, new TargetChannelConfig( - id: 6575, + id: 6500, isInternal: true, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["internal/10.0.1xx-rc1"], + akaMSChannelNames: ["internal/10.0-rc2"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), @@ -1003,7 +1124,7 @@ public enum BuildQuality publishingInfraVersion: PublishingInfraVersion.Latest, akaMSChannelNames: ["10.0.1xx-rc2"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10Feeds, symbolTargetType: SymbolPublishVisibility.Public), @@ -1014,86 +1135,50 @@ public enum BuildQuality publishingInfraVersion: PublishingInfraVersion.Latest, akaMSChannelNames: ["internal/10.0.1xx-rc2"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, targetFeeds: DotNet10InternalFeeds, symbolTargetType: SymbolPublishVisibility.Internal), - // .NET 10.0.1xx SDK Preview 1, - new TargetChannelConfig( - id: 6476, - isInternal: false, - publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview1"], - akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + #endregion - // .NET 10.0.1xx SDK Preview 2, - new TargetChannelConfig( - id: 6478, - isInternal: false, - publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview2"], - akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + #region .NET 11 Channels - // .NET 10.0.1xx SDK Preview 3, + // .NET 11, new TargetChannelConfig( - id: 6484, + id: 8297, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview3"], + akaMSChannelNames: ["11.0"], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet11Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10.0.1xx SDK Preview 4, + // .NET 11 Workload Release, new TargetChannelConfig( - id: 6486, + id: 8299, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview4"], + akaMSChannelNames: [ "11.0-workloads" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet11WorkloadFeeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10.0.1xx SDK Preview 5, + // .NET 11.0.1xx SDK, new TargetChannelConfig( - id: 6488, + id: 8298, isInternal: false, publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview5"], + akaMSChannelNames: [ "11.0.1xx", "11.0" ], akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, + akaMSDoNotCreateLinkPatterns: UnifiedBuildAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNet11Feeds, symbolTargetType: SymbolPublishVisibility.Public), - // .NET 10.0.1xx SDK Preview 6, - new TargetChannelConfig( - id: 6490, - isInternal: false, - publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview6"], - akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + #endregion - // .NET 10.0.1xx SDK Preview 7, - new TargetChannelConfig( - id: 6492, - isInternal: false, - publishingInfraVersion: PublishingInfraVersion.Latest, - akaMSChannelNames: ["10.0.1xx-preview7"], - akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, - akaMSDoNotCreateLinkPatterns: DotNet10AkaMSDoNotCreateLinkPatterns, - targetFeeds: DotNet10Feeds, - symbolTargetType: SymbolPublishVisibility.Public), + #region Other .NET Channels // .NET Core Experimental, new TargetChannelConfig( @@ -1305,7 +1390,8 @@ public enum BuildQuality akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, targetFeeds: GeneralTestingFeeds, - symbolTargetType: SymbolPublishVisibility.Public), + symbolTargetType: SymbolPublishVisibility.Public, + isProduction: false), // General Testing Internal, new TargetChannelConfig( @@ -1316,7 +1402,12 @@ public enum BuildQuality akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, targetFeeds: GeneralTestingInternalFeeds, - symbolTargetType: SymbolPublishVisibility.Internal), + symbolTargetType: SymbolPublishVisibility.Internal, + isProduction: false), + + #endregion + + #region VS Channels // VS 16.6, new TargetChannelConfig( @@ -1473,6 +1564,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.7, new TargetChannelConfig( id: 3581, @@ -1484,6 +1576,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.8, new TargetChannelConfig( id: 3582, @@ -1495,6 +1588,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.9, new TargetChannelConfig( id: 4015, @@ -1506,6 +1600,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.10 new TargetChannelConfig( id: 4165, @@ -1517,6 +1612,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.11 new TargetChannelConfig( id: 4544, @@ -1528,6 +1624,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.12 new TargetChannelConfig( id: 4906, @@ -1539,6 +1636,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.13 new TargetChannelConfig( id: 5288, @@ -1550,6 +1648,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.14 new TargetChannelConfig( id: 6136, @@ -1561,6 +1660,7 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), + // VS 17.15 new TargetChannelConfig( id: 6989, @@ -1583,7 +1683,79 @@ public enum BuildQuality targetFeeds: DotNetToolsFeeds, symbolTargetType: SymbolPublishVisibility.Public, flatten: false), - }; - #endregion + + // 18.1 + new TargetChannelConfig( + id: 8703, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + + // 18.2 + new TargetChannelConfig( + id: 8704, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + + // 18.3 + new TargetChannelConfig( + id: 8705, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + + // 18.4 + new TargetChannelConfig( + id: 8706, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + + // 18.5 + new TargetChannelConfig( + id: 8707, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + + // 18.6 + new TargetChannelConfig( + id: 8708, + isInternal: false, + publishingInfraVersion: PublishingInfraVersion.Latest, + akaMSChannelNames: [], + akaMSCreateLinkPatterns: DefaultAkaMSCreateLinkPatterns, + akaMSDoNotCreateLinkPatterns: DefaultAkaMSDoNotCreateLinkPatterns, + targetFeeds: DotNetToolsFeeds, + symbolTargetType: SymbolPublishVisibility.Public, + flatten: false), + #endregion + ]; } } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetChannelConfig.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetChannelConfig.cs index 2942b8e1aff3..239261e471d2 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetChannelConfig.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetChannelConfig.cs @@ -47,6 +47,12 @@ public struct TargetChannelConfig public bool IsInternal { get; } + /// + /// Whether this channel is a production channel vs. a non-production channel. + /// Non-production channels are typically used for testing purposes. + /// + public bool IsProduction { get; } + public bool Flatten { get; } public TargetChannelConfig( @@ -58,7 +64,8 @@ public TargetChannelConfig( ImmutableList akaMSDoNotCreateLinkPatterns, IEnumerable targetFeeds, SymbolPublishVisibility symbolTargetType, - bool flatten = true) + bool flatten = true, + bool isProduction = true) { Id = id; @@ -68,6 +75,7 @@ public TargetChannelConfig( TargetFeeds = targetFeeds.ToImmutableList(); SymbolTargetType = symbolTargetType; Flatten = flatten; + IsProduction = isProduction; AkaMSCreateLinkPatterns = akaMSCreateLinkPatterns ?? ImmutableList.Empty; AkaMSDoNotCreateLinkPatterns = akaMSDoNotCreateLinkPatterns ?? ImmutableList.Empty; } @@ -84,6 +92,7 @@ public override string ToString() $"\n {string.Join("\n ", TargetFeeds.Select(f => $"{string.Join(", ", f.ContentTypes)} -> {f.FeedUrl}"))}" + $"\n SymbolTargetType: '{SymbolTargetType}' " + $"\n IsInternal: '{IsInternal}'" + + $"\n IsProduction: '{IsProduction}'" + $"\n Flatten: '{Flatten}'"; } @@ -97,6 +106,7 @@ public override bool Equals(object other) PublishingInfraVersion == config.PublishingInfraVersion && Id == config.Id && IsInternal == config.IsInternal && + IsProduction == config.IsProduction && Flatten == config.Flatten) { return true; @@ -129,6 +139,7 @@ public override int GetHashCode() hash.Add(PublishingInfraVersion); hash.Add(Id); hash.Add(IsInternal); + hash.Add(IsProduction); foreach(var akaMSChannelName in AkaMSChannelNames) { hash.Add(akaMSChannelName); diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetFeedConfig.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetFeedConfig.cs index b2f25c8fb4f2..fe3220c37395 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetFeedConfig.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Feed/src/model/TargetFeedConfig.cs @@ -16,9 +16,25 @@ namespace Microsoft.DotNet.Build.Tasks.Feed.Model public class TargetFeedConfig { /// - /// Returns the TargetURL stripped of SAS token so it can be used for logging purposes. + /// Returns the TargetURL stripped of SAS token and with CDN substitution applied + /// for known blob storage accounts. This is used for both logging purposes and + /// for storing asset locations in BAR that point to publicly accessible CDN URLs. /// - public string SafeTargetURL => new UriBuilder(TargetURL) {Query = "", Fragment = ""}.Uri.AbsoluteUri; + public string SafeTargetURL + { + get + { + var uriBuilder = new UriBuilder(TargetURL) { Query = "", Fragment = "" }; + + // Apply CDN substitution for known storage accounts + if (PublishingConstants.AccountsWithCdns.TryGetValue(uriBuilder.Host, out var replacementHost)) + { + uriBuilder.Host = replacementHost; + } + + return uriBuilder.Uri.AbsoluteUri; + } + } public TargetFeedContentType ContentType { get; } diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/acquisition/acquire-nuget-exe/acquire-nuget-exe.proj b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/acquisition/acquire-nuget-exe/acquire-nuget-exe.proj deleted file mode 100644 index c283a08255ed..000000000000 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/acquisition/acquire-nuget-exe/acquire-nuget-exe.proj +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - https://dist.nuget.org/win-x86-commandline/v3.5.0/nuget.exe - $(BaseIntermediateOutputPath)nuget\ - $(NuGetExeToolDir)NuGet.exe - - - - - - - - - - diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/bundle.targets b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/bundle.targets index 7c6d934178df..b88ff9497152 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/bundle.targets +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/bundle.targets @@ -14,7 +14,8 @@ - + + @@ -95,6 +96,29 @@ + + + <_BundledComponentsPackageGroupWxsPath>$(IntermediateOutputPath)dotnetPackageGroup.wxs + + + + + + + @(_BundledComponents->'', ' + ') + + + +]]> + + + + - - true - - false - - - - <_AzureLinuxVersionSuffix>azl - <_InstallerBuildPartAzureLinux>$(Version)-$(_AzureLinuxVersionSuffix)-$(_InstallerArchSuffix) - <_InstallerFileNameWithoutExtensionAzureLinux>$(InstallerName)-$(_InstallerBuildPartAzureLinux)$(CrossArchContentsBuildPart) - <_InstallerFileAzureLinux>$(PackageOutputPath)$(_InstallerFileNameWithoutExtensionAzureLinux)$(InstallerExtension) - @@ -344,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) + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/DotNetLogo_256x.png b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/DotNetLogo_256x.png new file mode 100644 index 000000000000..3ff052f8302a Binary files /dev/null and b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/DotNetLogo_256x.png differ diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/dotnet.ico b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/dotnet.ico new file mode 100644 index 000000000000..16c9148e8609 Binary files /dev/null and b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/resources/dotnet.ico differ diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/wix.targets b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/wix.targets index b5006eb81308..08f5414e8969 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/wix.targets +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/wix.targets @@ -452,24 +452,10 @@ ComponentMsiFile=$(CrossArchMsiFile)" /> - - - - - - - @@ -480,25 +466,12 @@ $(MSBuildThisFileDirectory)vs\VS.Redist.Common.Component.nuspec.txt $(IntermediateOutputPath)vs\VS.Redist.Common.Component.nuspec - - $(PackProperties)COMPONENT_MSI=$(ComponentMsiFile); - $(PackProperties)ARCH=$(MsiArch); - $(PackProperties)COMPONENT_NAME=$(VSInsertionComponentName); - $(PackProperties)FRIENDLY_NAME=$(ProductBrandName); - $(PackProperties)PROJECT_URL=$(RepositoryUrl); - - - $(PackArgs) $(VsInsertionNuspecFile) - $(PackArgs) -Version $(Version) - $(PackArgs) -OutputDirectory $(ArtifactsNonShippingPackagesDir) - $(PackArgs) -NoDefaultExcludes - $(PackArgs) -NoPackageAnalysis - $(PackArgs) -Properties "$(PackProperties)" + COMPONENT_MSI=$(ComponentMsiFile);ARCH=$(MsiArch);COMPONENT_NAME=$(VSInsertionComponentName);FRIENDLY_NAME=$(ProductBrandName);PROJECT_URL=$(RepositoryUrl) - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/dummyEula.rtf b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/dummyEula.rtf new file mode 100644 index 000000000000..ebcd5ac373f4 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/bundle/dummyEula.rtf @@ -0,0 +1,237 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} +{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} +{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f376\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f377\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\f379\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f380\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f381\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f382\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\f383\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f384\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f376\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f377\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\f379\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f380\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f381\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f382\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\f383\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f384\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f746\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f747\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\f749\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f750\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f751\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f752\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);} +{\f753\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f754\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;} +{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);} +{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;} +{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} +{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; +\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 +\ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused +Normal Table;}{\s15\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext15 \slink16 \sunhideused \styrsid10564401 header;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 +\sbasedon10 \slink15 \slocked \styrsid10564401 Header Char;}{\s17\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext17 \slink18 \sunhideused \styrsid10564401 footer;}{\*\cs18 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 +\sbasedon10 \slink17 \slocked \styrsid10564401 Footer Char;}}{\*\rsidtbl \rsid2163051\rsid10564401}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim0}{\info +{\operator Rakesh Ranjan Singh}{\creatim\yr2017\mo5\dy30\hr17\min38}{\revtim\yr2017\mo5\dy30\hr17\min39}{\version2}{\edmins1}{\nofpages1}{\nofwords28}{\nofchars160}{\nofcharsws187}{\vern37}}{\*\userprops {\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85 +995028c_Enabled}\proptype30{\staticval True}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_SiteId}\proptype30{\staticval 72f988bf-86f1-41af-91ab-2d7cd011db47}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_Ref}\proptype30 +{\staticval https://api.informationprotection.azure.com/api/72f988bf-86f1-41af-91ab-2d7cd011db47}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_SetBy}\proptype30{\staticval raksingh@microsoft.com}{\propname MSIP_Label_f42aa342-8706-4288-bd11-e +bb85995028c_SetDate}\proptype30{\staticval 2017-05-30T17:39:57.6592568-07:00}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_Name}\proptype30{\staticval General}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_Application}\proptype30 +{\staticval Microsoft Azure Information Protection}{\propname MSIP_Label_f42aa342-8706-4288-bd11-ebb85995028c_Extended_MSFT_Method}\proptype30{\staticval Automatic}{\propname Sensitivity}\proptype30{\staticval General}}{\*\xmlnstbl {\xmlns1 http://schemas +.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot10564401 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0{\*\ftnsep \ltrpar \pard\plain \ltrpar +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10564401 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2163051 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10564401 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2163051 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10564401 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2163051 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10564401 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2163051 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\headerl \ltrpar \pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\headerr \ltrpar \pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\footerl \ltrpar \pard\plain \ltrpar\s17\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\footerr \ltrpar \pard\plain \ltrpar\s17\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\headerf \ltrpar \pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\footerf \ltrpar \pard\plain \ltrpar\s17\ql \li0\ri0\widctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid10564401 +\par }}{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}} +{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10564401 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 +\af31507 \ltrch\fcs0 \insrsid10564401 \hich\af31506\dbch\af31505\loch\f31506 This is a dummy file for Eula as required by \hich\af31506\dbch\af31505\loch\f31506 B\hich\af31506\dbch\af31505\loch\f31506 urn bundle \hich\af31506\dbch\af31505\loch\f31506 +. The bal.WixStandardBootstrapperApplication element must hav a value for exactly one of the LicenseFile or LicenseUrl attributes.}{\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2163051\charrsid10564401 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f +7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd +ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d +7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b +d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 +fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 +b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b +fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 +9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd +79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf +5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 +d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 +738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 +2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac +5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a +b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 +493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 +be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f +f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 +7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e +b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 +6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd +f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d +7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 +4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf +1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a +faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 +67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 +416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 +1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b +8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 +8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 +2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 +3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e +3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 +0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 +0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 +000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 +7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 +000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 +000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; +\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; +\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; +\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; +\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; +\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; +\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; +\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; +\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; +\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; +\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; +\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; +\lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000b00a +ec6da6d9d201feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/eula.rtf new file mode 100644 index 000000000000..7f40e11a8ab6 --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/breadcrumbstorefolder.wxs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/breadcrumbstorefolder.wxs new file mode 100644 index 000000000000..fe71303fb322 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/breadcrumbstorefolder.wxs @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi new file mode 100644 index 000000000000..24a6a6c75a5f --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.common.wxi @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.wxs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.wxs new file mode 100644 index 000000000000..3ffe84a15ed8 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/product.wxs @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/provider.wxs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/provider.wxs new file mode 100644 index 000000000000..51f6bf4ba6b2 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/provider.wxs @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/registrykeys.wxs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/registrykeys.wxs new file mode 100644 index 000000000000..ea54f57ddff1 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/registrykeys.wxs @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/werrelatedkeys.wxs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/werrelatedkeys.wxs new file mode 100644 index 000000000000..d783a3012f9f --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/product/werrelatedkeys.wxs @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi new file mode 100644 index 000000000000..e446d14c1b8d --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/variables.wxi @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/vs/VS.Redist.Common.Component.nuspec.txt b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/vs/VS.Redist.Common.Component.nuspec.txt new file mode 100644 index 000000000000..e9bb74c1aa46 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/vs/VS.Redist.Common.Component.nuspec.txt @@ -0,0 +1,18 @@ + + + + $COMPONENT_NAME$ + 1.0.0 + $COMPONENT_NAME$ + Microsoft + Microsoft + https://www.microsoft.com/net/dotnet_library_license.htm + $PROJECT_URL$ + true + $FRIENDLY_NAME$ ($ARCH$) Windows Installer MSI as a .nupkg for internal Visual Studio build consumption + © Microsoft Corporation. All rights reserved. + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets new file mode 100644 index 000000000000..131b6db1556d --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix5/wix.targets @@ -0,0 +1,523 @@ + + + + + + + + + + + $(IntermediateOutputPath)/wixpackcontents/ + + + + <_WixIntermediateOutputPath>$(BaseIntermediateOutputPath)wix + + + + $(IntermediateOutputPath)/wix/ + $(IntermediateOutputPath)/$(InstallerRuntimeIdentifier)/wix/ + x86 + $(InstallerTargetArchitecture) + + + + + + + + + + + + + + + + $(PkgMicrosoft_Wix)\tools\net6.0\any\ + wixext5 + $(PkgMicrosoft_WixToolset_UI_wixext)\$(WixExtensionsDir)\ + $(PkgMicrosoft_WixToolset_Dependency_wixext)\$(WixExtensionsDir)\ + $(PkgMicrosoft_WixToolset_Util_wixext)\$(WixExtensionsDir)\ + $(PkgMicrosoft_WixToolset_Bal_wixext)\$(WixExtensionsDir)\ + + + + + + 5 + + 1996-04-01 + + + + + + + + + + + + + + + + + + $(BundleInstallerUpgradeCodeSeed) $(MajorVersion).$(MinorVersion) $(RuntimeIdentifier) + + + + + + + + + + + + $(_OutInstallerFile) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_WixThemeIncludeLine Include="<?xml version="1.0"?>" /> + <_WixThemeIncludeLine Include="<Include>" /> + <_WixThemeIncludeLine Include="@(WixThemeVariable->'<Variable Name="%(Identity)" Value="%(Value)" />')"/> + <_WixThemeIncludeLine Include="</Include>" /> + + + + <_WixThemeFile>$(IntermediateOutputPath)wix/wixtheme.wxi + + + + + + + $(IntermediateOutputPath)o/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)..\resources\dotnet.ico + $(MSBuildThisFileDirectory)..\resources\DotNetLogo_256x.png + $(MSBuildThisFileDirectory)..\resources\dotnetlogo.bmp + Foundation + + + + + + + + + + + + + + + + + + + + + + <_OutInstallerFile>$(_InstallerFile) + + + + + + + <_OutInstallerFile Condition="'$(_OutInstallerFile)' == ''">$(_InstallerFile) + + + + 500 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(InstallerName.Replace('-', '_')) + + -bcgg + + + + + + + + + + + + + + + + + + + + + + + + + + <_wixArgs> + <_wixArgs>$(_wixArgs) -wx + <_wixArgs>$(_wixArgs) -nologo + <_wixArgs>$(_wixArgs) -culture en-us + <_wixArgs>$(_wixArgs) -arch $(MsiArch) + <_wixArgs>$(_wixArgs) -out "$(_OutInstallerFile)" + + <_wixArgs>$(_wixArgs) @(WixExtensions -> '-ext %(Identity)', ' ') + <_wixArgs>$(_wixArgs) @(CandleVariables -> '-d %(Identity)="%(Value)"', ' ') + <_wixArgs>$(_wixArgs) @(WixSrcFile -> '"%(FullPath)"', ' ') + + <_wixArgs>$(_wixArgs) $(AdditionalCandleArgs) + <_wixArgs>$(_wixArgs) $(AdditionalLightArgs) + <_wixArgs>$(_wixArgs) $(WixAdditionalOptions) + + <_wixCommand>wix.exe build $(_wixArgs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VS.Redist.Common.$(VSInsertionShortComponentName).$(InstallerTargetArchitecture)$(CrossArchContentsBuildPart).$(MajorVersion).$(MinorVersion) + $(ArtifactsNonShippingPackagesDir)$(VSInsertionComponentName).$(Version).nupkg + + + $(MSBuildThisFileDirectory)vs\VS.Redist.Common.Component.nuspec.txt + $(IntermediateOutputPath)vs\VS.Redist.Common.Component.nuspec + + COMPONENT_MSI=$(ComponentMsiFile);ARCH=$(MsiArch);COMPONENT_NAME=$(VSInsertionComponentName);FRIENDLY_NAME=$(ProductBrandName);PROJECT_URL=$(RepositoryUrl) + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs index e7ba349668f2..93dfab25875e 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/ArEntry.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs index aed27483c654..19377abf9ee6 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs index a454a7c61d2c..b877d72e75ce 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateMD5SumsFile.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs index c05d9c5d4981..c86b95e36842 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateWixBuildWixpack.cs @@ -28,6 +28,8 @@ namespace Microsoft.DotNet.Build.Tasks.Installers */ public class CreateWixBuildWixpack : Task { + public string AdditionalOptions { get; set; } + public ITaskItem BindTrackingFile { get; set; } public ITaskItem[] BindPaths { get; set; } @@ -71,6 +73,8 @@ public class CreateWixBuildWixpack : Task [Required] public ITaskItem[] SourceFiles { get; set; } + public string[] SuppressSpecificWarnings { get; set; } + [Required] public string WixpackWorkingDir { get; set; } @@ -94,12 +98,6 @@ public override bool Execute() WixpackWorkingDir = Path.Combine(Path.GetTempPath(), "WixpackTemp", Guid.NewGuid().ToString().Split('-')[0]); } - _wixprojDir = string.Empty; - if (!_defineConstantsDictionary.TryGetValue("ProjectDir", out _wixprojDir)) - { - throw new InvalidOperationException("ProjectDir not defined in DefineConstants. Task cannot proceed."); - } - _installerFilename = Path.GetFileName(InstallerFile); if (Directory.Exists(WixpackWorkingDir)) @@ -108,15 +106,22 @@ public override bool Execute() } Directory.CreateDirectory(WixpackWorkingDir); - // Copy wixproj file - fail if ProjectPath is not defined - if (_defineConstantsDictionary.TryGetValue("ProjectPath", out var projectPath)) + if (_defineConstantsDictionary.TryGetValue("ProjectDir", out _wixprojDir)) { - string destPath = Path.Combine(WixpackWorkingDir, Path.GetFileName(projectPath)); - File.Copy(projectPath, destPath, overwrite: true); + // Copy wixproj file - fail if ProjectPath is not defined + if (_defineConstantsDictionary.TryGetValue("ProjectPath", out var projectPath)) + { + string destPath = Path.Combine(WixpackWorkingDir, Path.GetFileName(projectPath)); + File.Copy(projectPath, destPath, overwrite: true); + } + else + { + throw new InvalidOperationException("ProjectPath not defined in DefineConstants. Task cannot proceed."); + } } else { - throw new InvalidOperationException("ProjectPath not defined in DefineConstants. Task cannot proceed."); + _wixprojDir = string.Empty; } CopyIncludeSearchPathsContents(); @@ -193,7 +198,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 @@ -333,6 +338,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) { @@ -395,6 +409,12 @@ private void GenerateWixBuildCommandLineFile() commandLineArgs.Add($"-trackingfile {BindTrackingFile.ItemSpec}"); } + // Add AdditionalOptions if specified + if (!string.IsNullOrEmpty(AdditionalOptions)) + { + commandLineArgs.Add(AdditionalOptions); + } + commandLineArgs.Add($"-nologo"); commandLineArgs.Add($"-wx"); @@ -407,7 +427,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"); @@ -493,6 +517,7 @@ private void CopySourceFilesAndContent() // Copy the sourceFile to WixpackWorkingDir var copiedXmlPath = Path.Combine(WixpackWorkingDir, Path.GetFileName(xmlPath)); File.Copy(xmlPath, copiedXmlPath, overwrite: true); + string sourceFileFolder = Path.GetDirectoryName(xmlPath); // First preprocess the source file to remove non-applicable include files. // We defer ingestion of variables, until all variables from include files @@ -501,7 +526,7 @@ private void CopySourceFilesAndContent() PreprocessWixSourceFile(copiedXmlPath); // Ingest variables after file preprocessing - ProcessAllReferencedIncludeFiles(copiedXmlPath); + ProcessAllReferencedIncludeFiles(copiedXmlPath, sourceFileFolder); IngestDefineVariablesFromWixFile(copiedXmlPath); try @@ -515,7 +540,7 @@ private void CopySourceFilesAndContent() ("MsiPackage", "Id", ["SourceFile"]), ("ExePackage", "Id", ["SourceFile"]), ("Payload", "Id", ["SourceFile"]), - ("WixStandardBootstrapperApplication", "Id", ["LicenseFile", "LocalizationFile", "ThemeFile"]), + ("WixStandardBootstrapperApplication", "Id", ["LicenseFile", "LocalizationFile", "ThemeFile", "LogoFile"]), ("WixVariable", "Id", ["Value"]), ("Icon", "Id", ["SourceFile"]) }; @@ -550,7 +575,7 @@ private void CopySourceFilesAndContent() string pattern = source.Substring(startIdx, source.IndexOf(')', startIdx + 2) - startIdx + 1); - source = GetAbsoluteSourcePath(source); + source = GetAbsoluteSourcePath(source, sourceFileFolder); var parts = source.Split([$"\\{pattern}\\"], StringSplitOptions.None); if (parts.Length < 2) @@ -584,7 +609,7 @@ private void CopySourceFilesAndContent() id = Path.GetFileName(source); } - CopySourceFile(id, source); + CopySourceFile(id, source, sourceFileFolder); // Update the original attribute to "\\" var newSourceValue = $"{id}\\{Path.GetFileName(source)}"; @@ -609,7 +634,7 @@ private void CopySourceFilesAndContent() /// in the wixpack working directory. /// /// - private void ProcessAllReferencedIncludeFiles(string file) + private void ProcessAllReferencedIncludeFiles(string file, string relativeRoot) { string content = File.ReadAllText(file); @@ -620,13 +645,13 @@ private void ProcessAllReferencedIncludeFiles(string file) if (match.Groups.Count > 1) { string filename = match.Groups[1].Value.Trim('\"'); - string includeFilePath = GetAbsoluteSourcePath(filename); + string includeFilePath = GetAbsoluteSourcePath(ResolvePath(filename), relativeRoot); if (File.Exists(includeFilePath)) { // Copy the include file, update the source file with new path // and ingest the variables. string id = Path.GetFileName(includeFilePath); - string path = CopySourceFile(id, includeFilePath); + string path = CopySourceFile(id, includeFilePath, relativeRoot); ProcessIncludeFile(path); content = content.Replace(filename, $"{id}\\{id}"); } @@ -634,13 +659,16 @@ private void ProcessAllReferencedIncludeFiles(string file) { // Include file could be in IncludeSearchPaths and already copied and processed. bool foundInSearchPath = false; - foreach (var searchPath in IncludeSearchPaths) + if (IncludeSearchPaths != null) { - var potentialPath = Path.Combine(WixpackWorkingDir, searchPath, Path.GetFileName(includeFilePath)); - if (File.Exists(potentialPath)) + foreach (var searchPath in IncludeSearchPaths) { - foundInSearchPath = true; - break; + var potentialPath = Path.Combine(WixpackWorkingDir, searchPath, Path.GetFileName(includeFilePath)); + if (File.Exists(potentialPath)) + { + foundInSearchPath = true; + break; + } } } @@ -891,7 +919,12 @@ private void PreprocessWixSourceFile(string sourceFile) if (keepBlock) { - output.Append(input.Substring(blockStart, blockEnd - blockStart)); + string selectedContent = input.Substring(blockStart, blockEnd - blockStart); + output.Append(selectedContent); + + // Ingest variables from the selected content asap as + // variables could be referenced in subsequent blocks or conditions. + IngestDefineVariablesFromString(selectedContent); } pos = endifStart + 9; // Move past } @@ -950,9 +983,17 @@ private bool EvaluateCondition(string condition) if (varName.StartsWith("var.", StringComparison.OrdinalIgnoreCase)) { - _defineVariablesDictionary.TryGetValue(varName.Substring(4), out actualValue); + varName = varName.Substring(4); + _defineVariablesDictionary.TryGetValue(varName, out actualValue); } - else + else if (varName.StartsWith("sys.", StringComparison.OrdinalIgnoreCase)) + { + varName = varName.Substring(4); + _systemVariablesDictionary.TryGetValue(varName, out actualValue); + } + + // Fallback to _defineConstantsDictionary if not found in variables + if (actualValue == null || actualValue == "") { _defineConstantsDictionary.TryGetValue(varName, out actualValue); } @@ -970,12 +1011,12 @@ private bool EvaluateCondition(string condition) } } - private string CopySourceFile(string fileId, string source) + private string CopySourceFile(string fileId, string source, string relativeRoot = "") { var destDir = Path.Combine(WixpackWorkingDir, fileId); Directory.CreateDirectory(destDir); - source = GetAbsoluteSourcePath(source); + source = GetAbsoluteSourcePath(source, relativeRoot); if (File.Exists(source)) { @@ -1010,15 +1051,23 @@ private void CopyBindPathContents() string bindName = BindPaths[i].GetMetadata("BindName"); if (!string.IsNullOrEmpty(bindName)) { + string wixpackSubfolder = Path.GetRandomFileName(); string bindPath = BindPaths[i].ItemSpec; - if (!bindName.EndsWith(".dll")) + + foreach (string file in Directory.GetFiles(bindPath, "*", SearchOption.TopDirectoryOnly)) { - bindName += ".dll"; // Ensure the bind name ends with .dll + // Copy known usable files only + // .dll, .exe, .msi + if (file.EndsWith(".dll") || + file.EndsWith(".exe") || + file.EndsWith(".msi")) + { + CopySourceFile(wixpackSubfolder, file); + } } - CopySourceFile(bindName, Path.Combine(bindPath, bindName)); // Update the bind path item spec to the new relative folder - BindPaths[i].ItemSpec = bindName; + BindPaths[i].ItemSpec = wixpackSubfolder; continue; } } @@ -1037,12 +1086,14 @@ private void CopyLocalizationFiles() } } - private string GetAbsoluteSourcePath(string source) + private string GetAbsoluteSourcePath(string source, string relativeRoot = "") { // If the source is relative, resolve it against the project directory if (!Path.IsPathRooted(source)) { - return Path.Combine(_wixprojDir, source); + return string.IsNullOrEmpty(relativeRoot) ? + Path.Combine(_wixprojDir, source) : + Path.Combine(relativeRoot, source); } return source; diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs index 35afda693df9..8bbdd7ff4dd9 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeader.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs index a51257b92eb7..3b5558aa8a23 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderEntryType.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs index d91e2e930aa6..6b4546c854fb 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmLead.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs index e10381eea135..21f28eab24a3 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmPackage.cs +++ b/src/arcade/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; diff --git a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs index 3a77b797d74d..a102acb64dcd 100644 --- a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAndroidWorkItems.cs b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAndroidWorkItems.cs index fa0544c68a53..e8f00390894c 100644 --- a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAndroidWorkItems.cs +++ b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAndroidWorkItems.cs @@ -157,7 +157,9 @@ private string GetDefaultCommand(ITaskItem appPackage, int expectedExitCode) "--timeout \"$timeout\" " + "--package-name \"$package_name\" " + " -v " + - $"{ devOutArg } { instrumentationArg } { exitCodeArg } { extraArguments } { passthroughArgs }"; + $"{ devOutArg } { instrumentationArg } { exitCodeArg } { extraArguments } " + + "--arg env:DOTNET_CI=true " + + $"{ passthroughArgs }"; } private string GetHelixCommand( diff --git a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAppleWorkItems.cs b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAppleWorkItems.cs index 0eb88de121b1..a1d7c943d8cd 100644 --- a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAppleWorkItems.cs +++ b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/CreateXHarnessAppleWorkItems.cs @@ -213,6 +213,7 @@ private string GetDefaultCommand(bool includesTestRunner, bool resetSimulator) = "-v " + (!includesTestRunner ? "--expected-exit-code $expected_exit_code " : string.Empty) + (resetSimulator ? $"--reset-simulator " : string.Empty) + + "--set-env=DOTNET_CI=true " + (!string.IsNullOrEmpty(AppArguments) ? "-- " + AppArguments : string.Empty); private string GetHelixCommand( diff --git a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs index 671215d884ad..76a6faa1848e 100644 --- a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs +++ b/src/arcade/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); } @@ -81,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) { diff --git a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/tools/Microsoft.DotNet.Helix.Sdk.MonoQueue.targets b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/tools/Microsoft.DotNet.Helix.Sdk.MonoQueue.targets index f1e7cd0a4033..90456cf23943 100644 --- a/src/arcade/src/Microsoft.DotNet.Helix/Sdk/tools/Microsoft.DotNet.Helix.Sdk.MonoQueue.targets +++ b/src/arcade/src/Microsoft.DotNet.Helix/Sdk/tools/Microsoft.DotNet.Helix.Sdk.MonoQueue.targets @@ -71,9 +71,13 @@ <_AccessTokenSuffix /> <_AccessTokenSuffix Condition=" '$(HelixAccessToken)' != '' ">&access_token={Get this from helix.dot.net} + <_HelixBaseUri Condition="'$(HelixBaseUri)' == ''">https://helix.dot.net + <_HelixBaseUri Condition="'$(HelixBaseUri)' != ''">$(HelixBaseUri.TrimEnd('/')) + <_HelixJobName /> + <_HelixJobName Condition="'$(HelixJobName)' != ''"> '$(HelixJobName)' - - + + , 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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs new file mode 100644 index 000000000000..a9cf811728b2 --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs new file mode 100644 index 000000000000..d38cab4887b0 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs @@ -0,0 +1,214 @@ +// 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"); + } + + [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"); + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs new file mode 100644 index 000000000000..ca42421c4010 --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj new file mode 100644 index 000000000000..83d868c3bdc2 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj @@ -0,0 +1,25 @@ + + + + $(NetToolCurrent) + true + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs new file mode 100644 index 000000000000..632511d34411 --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs new file mode 100644 index 000000000000..0bd0f5b5dfdc --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs new file mode 100644 index 000000000000..37dd8cb9d378 --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs new file mode 100644 index 000000000000..7e25ccd1b88e --- /dev/null +++ b/src/arcade/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. + } + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/sharedfx.targets b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/sharedfx.targets index ba0448127660..3b2dbd56552d 100644 --- a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/sharedfx.targets +++ b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/sharedfx.targets @@ -7,7 +7,7 @@ true true - true + true true $(AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder);.map;.r2rmap;.dbg;.debug;.dwarf <_DefaultHostJsonTargetPath>runtimes/$(RuntimeIdentifier)/lib/$(TargetFramework) @@ -62,11 +62,10 @@ - + diff --git a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix.targets b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix.targets index a35b20c83847..09ee4c34a614 100644 --- a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix.targets +++ b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix.targets @@ -17,7 +17,8 @@ ComponentGroupName="InstallFiles" DirectoryRef="SHARED" /> - + + diff --git a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/product/sharedfxdir.wxs b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix/product/sharedfxdir.wxs similarity index 100% rename from src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/product/sharedfxdir.wxs rename to src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix/product/sharedfxdir.wxs diff --git a/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix5/product/sharedfxdir.wxs b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix5/product/sharedfxdir.wxs new file mode 100644 index 000000000000..86059da1ecd6 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SharedFramework.Sdk/targets/windows/wix5/product/sharedfxdir.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip new file mode 100644 index 000000000000..333b5199c4e4 Binary files /dev/null and b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/Resources/InnerZipFile.zip differ diff --git a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 47bcb2283818..879174e31497 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/arcade/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, @@ -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(rpmPackage, layout); + var fakeBuildEngine = new FakeBuildEngine(_output); + var fakeLog = new TaskLoggingHelper(fakeBuildEngine, "TestLog"); + ZipData.ExtractRpmPayloadContents(fakeLog, rpmPackage, layout); // Checks: // Expected files are present @@ -526,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(); @@ -593,7 +598,7 @@ public void OnlyContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -628,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[] @@ -661,7 +666,7 @@ public void SkipStrongNamingForAlreadyStrongNamedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, Array.Empty()); } @@ -682,7 +687,7 @@ public void DoNotSkipStrongNamingForDelaySignedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -706,7 +711,7 @@ public void SkipStrongNamingForCrossGennedBinary() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -730,7 +735,7 @@ public void SkipStrongNamingBinaryButDontSkipAuthenticode() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -758,7 +763,7 @@ public void OnlyAuthenticodeSignByPKT() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, new Dictionary>(), new[] { @@ -791,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[] @@ -833,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[] @@ -873,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[] @@ -902,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[] @@ -938,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[] { @@ -960,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[] @@ -981,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[] { @@ -1003,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[] @@ -1030,7 +1035,7 @@ public void DoubleNestedContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -1081,7 +1086,7 @@ public void NestedContainer() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -1160,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[] @@ -1242,7 +1247,7 @@ public void SignZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1278,6 +1283,93 @@ public void SignZipFile() }); } + [Fact] + public void SignArchivesUsingDetachedSignature() + { + // List of files to be considered for signing + var itemsToSign = new List() + { + 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 ExplicitSignInfoKey("test.zip"), new FileSignInfoEntry("ArchiveCert") }, + { new ExplicitSignInfoKey("test.tgz"), new FileSignInfoEntry("ArchiveCert") }, + { new ExplicitSignInfoKey("InnerZipFile.zip"), new FileSignInfoEntry("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. @@ -1298,7 +1390,7 @@ public void SignJustPkgWithoutUnpack() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1332,7 +1424,7 @@ public void UnpackAndSignPkg() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1399,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[] @@ -1465,7 +1557,7 @@ public void SignNestedPkgFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1517,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[] @@ -1563,7 +1655,7 @@ public void SignTarGZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1615,7 +1707,7 @@ public void SymbolsNupkg() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -1667,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[] @@ -1718,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[] { @@ -1744,7 +1836,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); } @@ -1762,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[] { @@ -1790,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[] { @@ -1833,7 +1925,7 @@ public void VerifyDebIntegrity() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var expectedFilesToBeSigned = new List { @@ -1864,7 +1956,7 @@ public void VerifyRpmIntegrity() var strongNameSignInfo = new Dictionary>(); // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); var expectedFilesToBeSigned = new List { @@ -1896,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[] { @@ -1920,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'" }); } @@ -1939,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[] { @@ -1967,7 +2059,7 @@ public void SignMsiEngine() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2016,7 +2108,7 @@ public void SignBundleDoubleNested() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2068,7 +2160,7 @@ public void MsiWithWixpack() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2135,7 +2227,7 @@ public void MPackFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2170,7 +2262,7 @@ public void VsixPackage_DuplicateVsixAfter() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2231,7 +2323,7 @@ public void VsixPackage_WithSpaces() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2292,7 +2384,7 @@ public void VsixPackage_DuplicateVsixBefore() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2350,7 +2442,7 @@ public void VsixPackage_DuplicateVsixBeforeAndAfter() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2410,7 +2502,7 @@ public void VsixPackageWithRelationships() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2444,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()); } @@ -2469,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[] { @@ -2555,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'", @@ -2587,6 +2679,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 +2715,11 @@ public void ValidateSignToolTaskParsing() { "CertificateName", "DualSignCertificate" }, { "PublicKeyToken", "31bf3856ad364e35" }, { "CollisionPriorityId", "123" } + }), + new TaskItem("test.zip", new Dictionary + { + { "CertificateName", "DetachedArchiveCert" }, + { "CollisionPriorityId", "123" } }) }; @@ -2634,7 +2736,11 @@ public void ValidateSignToolTaskParsing() { "MacCertificate", "MacDeveloperHarden" }, { "MacNotarizationAppName", "com.microsoft.dotnet" }, { "CollisionPriorityId", "123" } - }) + }), + new TaskItem("DetachedArchiveCert", new Dictionary + { + { "SupportsDetachedSignature", "true" } + }), }; var task = new SignToolTask @@ -2667,7 +2773,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); } @@ -2711,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[] { @@ -2744,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); } @@ -2765,7 +2875,7 @@ public void PackageWithZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfoWithCollisionId, new[] { @@ -2797,7 +2907,7 @@ public void NestedZipFile() }; // Overriding information - var fileSignInfo = new Dictionary(); + var fileSignInfo = new Dictionary(); ValidateFileSignInfos(itemsToSign, strongNameSignInfo, fileSignInfo, s_fileExtensionSignInfo, new[] { @@ -2848,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. @@ -2931,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[] @@ -3015,7 +3125,7 @@ public void MissingCertificateName(string extension) new Configuration(_tmpDir, itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), new Dictionary>(), new(), null, @@ -3066,7 +3176,7 @@ public void MissingCertificateNameButExtensionIsIgnored(string extension) new Configuration(_tmpDir, itemsToSign, new Dictionary>(), - new Dictionary(), + new Dictionary(), extensionSignInfo, new(), null, @@ -3090,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]); } @@ -3449,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/arcade/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs index e7e2ed2f4a24..06f87035dee4 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/AdditionalCertificateInformation.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs index 1eaca00b2739..ecd63cf0009b 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/BatchSignUtil.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SignTool/src/Configuration.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/Configuration.cs index 439d09d35ee3..84d85749c7f8 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/Configuration.cs +++ b/src/arcade/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, @@ -224,45 +224,63 @@ 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; } + // 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. 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(); @@ -298,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; @@ -418,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; @@ -454,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 { @@ -472,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? @@ -485,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) { @@ -509,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, @@ -529,6 +563,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); } @@ -542,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) { @@ -814,20 +854,26 @@ 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)); entry.WriteToFile(tempPath); + ZipData.SetUnixFileMode(_log, entry.UnixFileMode, tempPath); _hashToCollisionIdMap.TryGetValue(fileUniqueKey, out string collisionPriorityId); 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) { @@ -863,5 +909,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/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs deleted file mode 100644 index b8c775ddb36c..000000000000 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitCertificateKey.cs +++ /dev/null @@ -1,61 +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.Diagnostics; - -namespace Microsoft.DotNet.SignTool -{ - internal readonly struct ExplicitCertificateKey : IEquatable - { - public readonly string FileName; - public readonly string PublicKeyToken; - public readonly string TargetFramework; - 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) - { - Debug.Assert(fileName != null); - - FileName = fileName; - PublicKeyToken = publicKeyToken ?? ""; - TargetFramework = targetFramework ?? ""; - CollisionPriorityId = collisionPriorityId ?? ""; - 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); - - public override int GetHashCode() - => Hash.Combine(Hash.Combine(FileName.GetHashCode(), PublicKeyToken.GetHashCode()), Hash.Combine(TargetFramework.GetHashCode(), ExecutableType.GetHashCode())); - - bool IEquatable.Equals(ExplicitCertificateKey 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) - => key1.Equals(key2); - - public static bool operator !=(ExplicitCertificateKey key1, ExplicitCertificateKey key2) - => !(key1 == key2); - } -} diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs new file mode 100644 index 000000000000..66cf9e8ad9cd --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SignTool/src/ExplicitSignInfoKey.cs @@ -0,0 +1,52 @@ +// 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; + +namespace Microsoft.DotNet.SignTool +{ + /// + /// 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; + public readonly string TargetFramework; + public readonly string CollisionPriorityId; + public readonly ExecutableType ExecutableType; + + public ExplicitSignInfoKey(string fileName, string publicKeyToken = null, string targetFramework = null, string collisionPriorityId = null, ExecutableType executableType = ExecutableType.None) + { + Debug.Assert(fileName != null); + + FileName = fileName; + PublicKeyToken = publicKeyToken ?? ""; + TargetFramework = targetFramework ?? ""; + CollisionPriorityId = collisionPriorityId ?? ""; + ExecutableType = executableType; + } + + public override bool Equals(object obj) + => 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(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 ==(ExplicitSignInfoKey key1, ExplicitSignInfoKey key2) + => key1.Equals(key2); + + public static bool operator !=(ExplicitSignInfoKey key1, ExplicitSignInfoKey key2) + => !(key1 == key2); + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs index 62e5c8636f67..efe8825d81f7 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/FileSignInfo.cs +++ b/src/arcade/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: @@ -124,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; @@ -147,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/arcade/src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/FileSignInfoEntry.cs new file mode 100644 index 000000000000..7b0baef84e5b --- /dev/null +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SignTool/src/SignInfo.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/SignInfo.cs index a6bb1087218a..6b9d0cf73e02 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/SignInfo.cs +++ b/src/arcade/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 @@ -49,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; @@ -57,7 +73,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, bool doNotUnpack = false) { ShouldIgnore = shouldIgnore; IsAlreadySigned = isAlreadySigned; @@ -66,33 +82,41 @@ public SignInfo(string certificate, string strongName, string notarizationAppNam CollisionPriorityId = collisionPriorityId; IsAlreadyStrongNamed = isAlreadyStrongNamed; NotarizationAppName = notarizationAppName; + GeneratesDetachedSignature = generatesDetachedSignature; + DoNotUnpack = doNotUnpack; } - 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, 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); + => 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); + => 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); + => 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) : - new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, true, value, IsAlreadyStrongNamed); + 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); + 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, DoNotUnpack); + + internal SignInfo WithDoNotUnpack(bool doNotUnpack) + => new SignInfo(Certificate, StrongName, NotarizationAppName, CollisionPriorityId, ShouldIgnore, IsAlreadySigned, IsAlreadyStrongNamed, GeneratesDetachedSignature, doNotUnpack); } } diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/SignTool.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/SignTool.cs index 6aa74d2ccc12..6b88e6d156bc 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/SignTool.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs index f0b8b879869b..a3be92ae9329 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/SignToolConstants.cs +++ b/src/arcade/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/arcade/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs index b999bbd6502b..a5d2cea441a2 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/SignToolTask.cs +++ b/src/arcade/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("SupportsDetachedSignature"); + 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 }; @@ -438,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)) @@ -446,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)) { @@ -530,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) { @@ -544,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) { @@ -557,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; } @@ -576,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)); } } diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs index 4d0acc311a96..da4b1ae2610c 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs +++ b/src/arcade/src/Microsoft.DotNet.SignTool/src/VerifySignatures.cs @@ -262,7 +262,9 @@ 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-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 }; foreach (string keyUrl in keyUrls) diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 65681e36a196..8f8a0ffb0cbb 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/arcade/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 @@ -347,6 +353,15 @@ private static IEnumerable ReadPkgOrAppBundleEntries(string archiv private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, 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 { @@ -366,8 +381,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", pkgToolPath)) @@ -382,6 +401,7 @@ private void RepackPkgOrAppBundles(TaskLoggingHelper log, string tempDir, string Directory.Delete(extractDir, recursive: true); } } +#endif } #if NETFRAMEWORK @@ -413,7 +433,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); } } @@ -481,8 +501,7 @@ private void RepackTarGZip(TaskLoggingHelper log, string tempDir, string tarTool 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; } @@ -523,7 +542,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 +572,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]; @@ -565,12 +584,12 @@ private string GetUpdatedControlArchive(string debianPackage, string dataArchive 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(dataArchive, dataLayout); - ExtractTarballContents(controlArchive, controlLayout); + ExtractTarballContents(log, dataArchive, dataLayout); + ExtractTarballContents(log, controlArchive, controlLayout); string sumsFile = Path.Combine(workingDir, "md5sums"); CreateMD5SumsFile createMD5SumsFileTask = new() @@ -600,17 +619,39 @@ private string GetUpdatedControlArchive(string debianPackage, string dataArchive 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)) + { + 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 var gzip = new GZipStream(dstStream, CompressionMode.Compress); - TarFile.CreateFromDirectory(controlLayout, gzip, includeBaseDirectory: false); + using GZipStream compressor = new(outputStream, CompressionMode.Compress); + streamToCompress.CopyTo(compressor); } 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 +664,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); } } @@ -636,7 +680,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("/")) { @@ -645,7 +689,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, + }; } } } @@ -658,7 +705,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 +719,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 +730,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 +794,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 +804,21 @@ internal static void ExtractRpmPayloadContents(string rpmPackage, string layout) if (entry != null) { entry.WriteToFile(outputPath); + SetUnixFileMode(log, entry.UnixFileMode, outputPath); } } } - 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,8 +828,40 @@ 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 + + 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/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs index e2d34d86cc1b..0bdac8c92e22 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs +++ b/src/arcade/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); diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalFactAttribute.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalFactAttribute.cs index a036ab78989a..f3cd644dda0a 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalFactAttribute.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalFactAttribute.cs @@ -1,14 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - -// Not adding the support for xunit.v3. -// Still keeping the logic inside compatible with xunit.v3 in case we decided to add it. -// For cases that used to do [ConditionalFact] in xunit.v2, they can now call Assert.Skip instead of throwing SkipTestException -// In this case, [Fact] will just work because Assert.Skip is natively supported in xunit.v3 -// TODO: Evaluate whether or not we want to still expose this attribute in xunit.v3 for usages of CalleeType and ConditionMemberNames? -#if !USES_XUNIT_3 - using System; using System.Diagnostics.CodeAnalysis; using Microsoft.DotNet.XUnitExtensions; @@ -43,4 +35,3 @@ public ConditionalFactAttribute(params string[] conditionMemberNames) } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalTheoryAttribute.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalTheoryAttribute.cs index 38d8d359d2bb..afd862c97141 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalTheoryAttribute.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/ConditionalTheoryAttribute.cs @@ -1,13 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Not adding the support for xunit.v3. -// Still keeping the logic inside compatible with xunit.v3 in case we decided to add it. -// For cases that used to do [ConditionalTheory] in xunit.v2, they can now call Assert.Skip instead of throwing SkipTestException -// In this case, [Fact] will just work because Assert.Skip is natively supported in xunit.v3 -// TODO: Evaluate whether or not we want to still expose this attribute in xunit.v3 for usages of CalleeType and ConditionMemberNames? -#if !USES_XUNIT_3 - using System; using System.Diagnostics.CodeAnalysis; using Microsoft.DotNet.XUnitExtensions; @@ -42,4 +35,3 @@ public ConditionalTheoryAttribute(params string[] conditionMemberNames) } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/SkipOnCoreClrAttribute.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/SkipOnCoreClrAttribute.cs index 22c432f7fd54..19d16508350f 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/SkipOnCoreClrAttribute.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Attributes/SkipOnCoreClrAttribute.cs @@ -26,6 +26,7 @@ public class SkipOnCoreClrAttribute : Attribute, ITraitAttribute private static readonly Lazy s_isReleaseRuntime = new Lazy(() => CoreClrConfigurationDetection.IsReleaseRuntime); private static readonly Lazy s_isDebugRuntime = new Lazy(() => CoreClrConfigurationDetection.IsDebugRuntime); private static readonly Lazy s_isStressTest = new Lazy(() => CoreClrConfigurationDetection.IsStressTest); + private static readonly Lazy s_isCoreClrInterpreter = new Lazy(() => CoreClrConfigurationDetection.IsCoreClrInterpreter); private readonly TestPlatforms _testPlatforms = TestPlatforms.Any; private readonly RuntimeTestModes _testMode = RuntimeTestModes.Any; @@ -58,7 +59,8 @@ private static bool StressModeApplies(RuntimeTestModes stressMode) => (stressMode.HasFlag(RuntimeTestModes.TailcallStress) && s_isTailCallStress.Value) || (stressMode.HasFlag(RuntimeTestModes.JitStressRegs) && s_isJitStressRegs.Value) || (stressMode.HasFlag(RuntimeTestModes.JitStress) && s_isJitStress.Value) || - (stressMode.HasFlag(RuntimeTestModes.JitMinOpts) && s_isJitMinOpts.Value); + (stressMode.HasFlag(RuntimeTestModes.JitMinOpts) && s_isJitMinOpts.Value) || + (stressMode.HasFlag(RuntimeTestModes.InterpreterActive) && s_isCoreClrInterpreter.Value); #endif internal SkipOnCoreClrAttribute() { } diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/CoreClrConfigurationDetection.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/CoreClrConfigurationDetection.cs index 027c1495e737..22fa19997178 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/CoreClrConfigurationDetection.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/CoreClrConfigurationDetection.cs @@ -22,8 +22,20 @@ public static class CoreClrConfigurationDetection public static bool IsTieredCompilation => string.Equals(GetEnvironmentVariableValue("TieredCompilation", "1"), "1", StringComparison.InvariantCulture); public static bool IsHeapVerify => string.Equals(GetEnvironmentVariableValue("HeapVerify"), "1", StringComparison.InvariantCulture); + public static bool IsCoreClrInterpreter + { + get + { + if (!string.IsNullOrWhiteSpace(GetEnvironmentVariableValue("Interpreter", ""))) + return true; + if (int.TryParse(GetEnvironmentVariableValue("InterpMode", "0"), out int mode) && (mode > 0)) + return true; + return false; + } + } + public static bool IsGCStress => !string.Equals(GetEnvironmentVariableValue("GCStress"), "0", StringComparison.InvariantCulture); - + public static bool IsAnyJitStress => IsJitStress || IsJitStressRegs || IsJitMinOpts || IsTailCallStress; public static bool IsAnyJitOptimizationStress => IsAnyJitStress || IsTieredCompilation; diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalFactDiscoverer.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalFactDiscoverer.cs index 60380f2a26cc..b03d8327ac35 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalFactDiscoverer.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalFactDiscoverer.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Not adding the support for xunit.v3. -// This is used by ConditionalFact and ConditionalTheory which we no longer support in xunit.v3. -// Still keeping the logic inside supporting xunit.v3 in case we decided to add it. -#if !USES_XUNIT_3 - using System; using System.Collections.Generic; using System.Linq; @@ -47,7 +42,7 @@ protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions var details = TestIntrospectionHelper.GetTestCaseDetails(discoveryOptions, testMethod, factAttribute); return skipReason != null - ? (IXunitTestCase)new SkippedTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, details.SkipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout) + ? (IXunitTestCase)new SkippedTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, skipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout) : new SkippedFactTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, details.SkipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout); // Test case skippable at runtime. #else return skipReason != null @@ -60,4 +55,3 @@ protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalTheoryDiscoverer.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalTheoryDiscoverer.cs index e12e60325190..35881aa65a86 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalTheoryDiscoverer.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/ConditionalTheoryDiscoverer.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Not adding the support for xunit.v3. -// This is used by ConditionalFact and ConditionalTheory which we no longer support in xunit.v3. -// Still keeping the logic inside supporting xunit.v3 in case we decided to add it. -#if !USES_XUNIT_3 - using System; using System.Collections.Generic; using System.Linq; @@ -57,7 +52,7 @@ protected override IEnumerable CreateTestCasesForTheory(ITestFra var details = TestIntrospectionHelper.GetTestCaseDetails(discoveryOptions, testMethod, theoryAttribute); var testCases = skipReason != null - ? new[] { new SkippedTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, details.SkipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout) } + ? new[] { new SkippedTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, skipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout) } : new IXunitTestCase[] { new SkippedFactTestCase(details.ResolvedTestMethod, details.TestCaseDisplayName, details.UniqueID, details.Explicit, details.SkipExceptions, details.SkipReason, details.SkipType, details.SkipUnless, details.SkipWhen, testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), timeout: details.Timeout) }; // Theory skippable at runtime. return new ValueTask>(Task.FromResult>(testCases)); @@ -138,4 +133,3 @@ protected override IEnumerable CreateTestCasesForDataRow(ITestFr } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/SkipOnCoreClrDiscoverer.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/SkipOnCoreClrDiscoverer.cs index 6476b08071d1..63dce43b2987 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/SkipOnCoreClrDiscoverer.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/Discoverers/SkipOnCoreClrDiscoverer.cs @@ -25,6 +25,7 @@ public class SkipOnCoreClrDiscoverer : ITraitDiscoverer private static readonly Lazy s_isReleaseRuntime = new Lazy(() => CoreClrConfigurationDetection.IsReleaseRuntime); private static readonly Lazy s_isDebugRuntime = new Lazy(() => CoreClrConfigurationDetection.IsDebugRuntime); private static readonly Lazy s_isStressTest = new Lazy(() => CoreClrConfigurationDetection.IsStressTest); + private static readonly Lazy s_isCoreClrInterpreter = new Lazy(() => CoreClrConfigurationDetection.IsCoreClrInterpreter); public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { @@ -72,7 +73,8 @@ private static bool StressModeApplies(RuntimeTestModes stressMode) => (stressMode.HasFlag(RuntimeTestModes.TailcallStress) && s_isTailCallStress.Value) || (stressMode.HasFlag(RuntimeTestModes.JitStressRegs) && s_isJitStressRegs.Value) || (stressMode.HasFlag(RuntimeTestModes.JitStress) && s_isJitStress.Value) || - (stressMode.HasFlag(RuntimeTestModes.JitMinOpts) && s_isJitMinOpts.Value); + (stressMode.HasFlag(RuntimeTestModes.JitMinOpts) && s_isJitMinOpts.Value) || + (stressMode.HasFlag(RuntimeTestModes.InterpreterActive) && s_isCoreClrInterpreter.Value); } } #endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/RuntimeTestModes.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/RuntimeTestModes.cs index 24e25026a288..4fed720a782f 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/RuntimeTestModes.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/RuntimeTestModes.cs @@ -44,5 +44,7 @@ public enum RuntimeTestModes AnyJitOptimizationStress = AnyJitStress | TieredCompilation, // Disable when any JIT non-full optimization stress mode is exercised. HeapVerify = 1 << 9, // DOTNET_HeapVerify (or COMPlus_HeapVerify) is set. + + InterpreterActive = 1 << 10, // DOTNET_Interpreter != "" or DOTNET_InterpMode != 0 } } diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedFactTestCase.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedFactTestCase.cs index 785f36cde4e3..65af69705fa1 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedFactTestCase.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedFactTestCase.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Not adding the support for xunit.v3. -// This is used by ConditionalFact and ConditionalTheory which we no longer support in xunit.v3. -// Still keeping the logic inside supporting xunit.v3 in case we decided to add it. -#if !USES_XUNIT_3 - using System; using System.Collections.Generic; using System.Linq; @@ -79,4 +74,3 @@ public override async Task RunAsync(IMessageSink diagnosticMessageSi } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedTestMessageBus.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedTestMessageBus.cs index 1339daf8a8f3..328f5835ee32 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedTestMessageBus.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/SkippedTestMessageBus.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Not adding the support for xunit.v3. -// This is used by ConditionalFact and ConditionalTheory which we no longer support in xunit.v3. -// Still keeping the logic inside supporting xunit.v3 in case we decided to add it. -#if !USES_XUNIT_3 - using System; using System.Linq; #if !USES_XUNIT_3 @@ -72,4 +67,3 @@ public bool QueueMessage(IMessageSinkMessage message) } } } -#endif diff --git a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/TestPlatforms.cs b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/TestPlatforms.cs index 57f2b6ed0f89..c3115e50d853 100644 --- a/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/TestPlatforms.cs +++ b/src/arcade/src/Microsoft.DotNet.XUnitExtensions.Shared/TestPlatforms.cs @@ -13,7 +13,7 @@ public enum TestPlatforms OSX = 4, FreeBSD = 8, NetBSD = 16, - illumos= 32, + illumos = 32, Solaris = 64, iOS = 128, tvOS = 256, @@ -24,6 +24,7 @@ public enum TestPlatforms Wasi = 8192, Haiku = 16384, AnyUnix = FreeBSD | Linux | NetBSD | OSX | illumos | Solaris | iOS | tvOS | MacCatalyst | Android | Browser | LinuxBionic | Wasi | Haiku, + AnyApple = OSX | iOS | tvOS | MacCatalyst, Any = ~0 } } diff --git a/src/arcade/src/SignCheck/Microsoft.SignCheck/Utils.cs b/src/arcade/src/SignCheck/Microsoft.SignCheck/Utils.cs index 570bc46df23b..a93733604c49 100644 --- a/src/arcade/src/SignCheck/Microsoft.SignCheck/Utils.cs +++ b/src/arcade/src/SignCheck/Microsoft.SignCheck/Utils.cs @@ -200,6 +200,8 @@ 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 }; foreach (string keyUrl in keyUrls) diff --git a/src/arcade/tests/UnitTests.proj b/src/arcade/tests/UnitTests.proj index 7dcbd830e9ed..baaa1b68e4f1 100644 --- a/src/arcade/tests/UnitTests.proj +++ b/src/arcade/tests/UnitTests.proj @@ -9,6 +9,7 @@ test/product/ + Arcade Job Name true sdk diff --git a/src/aspnetcore/.azure/pipelines/ci-public.yml b/src/aspnetcore/.azure/pipelines/ci-public.yml index e0d7b2fae27e..bfc4bb6cfacb 100644 --- a/src/aspnetcore/.azure/pipelines/ci-public.yml +++ b/src/aspnetcore/.azure/pipelines/ci-public.yml @@ -76,8 +76,6 @@ variables: value: /bl:artifacts/log/Release/Build.CodeSign.binlog - name: WindowsInstallersLogArgs value: /bl:artifacts/log/Release/Build.Installers.binlog -- name: WindowsArm64InstallersLogArgs - value: /bl:artifacts/log/Release/Build.Installers.Arm64.binlog - name: _SignType value: '' - name: _InternalRuntimeDownloadArgs @@ -100,6 +98,8 @@ stages: jobName: Code_check jobDisplayName: Code check agentOs: Windows + # Need full history for git log comparison in CodeCheck.ps1 + fetchDepth: 0 steps: - powershell: ./eng/scripts/CodeCheck.ps1 -ci $(_InternalRuntimeDownloadArgs) displayName: Run eng/scripts/CodeCheck.ps1 @@ -205,29 +205,14 @@ stages: -sign -buildInstallers -noBuildNative - /p:DotNetSignType=$(_SignType) - $(_BuildArgs) - $(_InternalRuntimeDownloadArgs) - $(WindowsInstallersLogArgs) - displayName: Build Installers - - # Windows installers bundle and sharedfx msi for arm64 - - script: ./eng/build.cmd - -ci - -prepareMachine - -noBuildRepoTasks - -arch arm64 - -sign - -buildInstallers - -noBuildNative -publish /p:DotNetSignType=$(_SignType) /p:AssetManifestFileName=aspnetcore-win.xml $(_BuildArgs) $(_PublishArgs) $(_InternalRuntimeDownloadArgs) - $(WindowsArm64InstallersLogArgs) - displayName: Build ARM64 Installers + $(WindowsInstallersLogArgs) + displayName: Build Installers artifacts: - name: Windows_Logs_Attempt_$(System.JobAttempt) @@ -337,164 +322,170 @@ stages: inputName: Linux_x64 # Build Linux ARM - - template: jobs/default-build.yml - parameters: - jobName: Linux_arm_build - jobDisplayName: "Build: Linux ARM" - agentOs: Linux - buildArgs: - --arch arm - --pack - --all - --no-build-java - --publish - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_arm.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_arm_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_arm_Packages - path: artifacts/packages/ - - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: jobs/codesign-xplat.yml + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: jobs/default-build.yml parameters: - inputName: Linux_arm + jobName: Linux_arm_build + jobDisplayName: "Build: Linux ARM" + agentOs: Linux + buildArgs: + --arch arm + --pack + --all + --no-build-java + --publish + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm_Packages + path: artifacts/packages/ - # Build Linux ARM64 - - template: jobs/default-build.yml - parameters: - jobName: Linux_arm64_build - jobDisplayName: "Build: Linux ARM64" - agentOs: Linux - steps: - - script: ./eng/build.sh - --ci - --arch arm64 - --pack - --build-installers - --all - --no-build-java - -p:OnlyPackPlatformSpecificPackages=true - $(_BuildArgs) - $(_InternalRuntimeDownloadArgs) - displayName: Run build.sh - artifacts: - - name: Linux_arm64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_arm64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: jobs/codesign-xplat.yml + parameters: + inputName: Linux_arm - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: jobs/codesign-xplat.yml + # Build Linux ARM64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: jobs/default-build.yml parameters: - inputName: Linux_arm64 + jobName: Linux_arm64_build + jobDisplayName: "Build: Linux ARM64" + agentOs: Linux + steps: + - script: ./eng/build.sh + --ci + --arch arm64 + --pack + --build-installers + --all + --no-build-java + -p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + $(_InternalRuntimeDownloadArgs) + displayName: Run build.sh + artifacts: + - name: Linux_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm64_Packages + path: artifacts/packages/ - # Build Linux Musl x64 - - template: jobs/default-build.yml - parameters: - jobName: Linux_musl_x64_build - jobDisplayName: "Build: Linux Musl x64" - agentOs: Linux - container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 - buildArgs: - --arch x64 - --os-name linux-musl - --pack - --all - --no-build-java - --publish - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_x64.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - disableComponentGovernance: true - artifacts: - - name: Linux_musl_x64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_x64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: jobs/codesign-xplat.yml + parameters: + inputName: Linux_arm64 - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: jobs/codesign-xplat.yml + # Build Linux Musl x64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: jobs/default-build.yml parameters: - inputName: Linux_musl_x64 + jobName: Linux_musl_x64_build + jobDisplayName: "Build: Linux Musl x64" + agentOs: Linux + useHostedUbuntu: false + container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 + buildArgs: + --arch x64 + --os-name linux-musl + --pack + --all + --no-build-java + --publish + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_x64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + disableComponentGovernance: true + artifacts: + - name: Linux_musl_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_x64_Packages + path: artifacts/packages/ - # Build Linux Musl ARM - - template: jobs/default-build.yml - parameters: - jobName: Linux_musl_arm_build - jobDisplayName: "Build: Linux Musl ARM" - agentOs: Linux - useHostedUbuntu: false - container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 - buildArgs: - --arch arm - --os-name linux-musl - --pack - --all - --no-build-java - --publish - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_arm.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_musl_arm_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_arm_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: jobs/codesign-xplat.yml + parameters: + inputName: Linux_musl_x64 - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: jobs/codesign-xplat.yml + # Build Linux Musl ARM + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: jobs/default-build.yml parameters: - inputName: Linux_musl_arm + jobName: Linux_musl_arm_build + jobDisplayName: "Build: Linux Musl ARM" + agentOs: Linux + useHostedUbuntu: false + container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 + buildArgs: + --arch arm + --os-name linux-musl + --pack + --all + --no-build-java + --publish + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm_Packages + path: artifacts/packages/ - # Build Linux Musl ARM64 - - template: jobs/default-build.yml - parameters: - jobName: Linux_musl_arm64_build - jobDisplayName: "Build: Linux Musl ARM64" - agentOs: Linux - useHostedUbuntu: false - container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 - buildArgs: - --arch arm64 - --os-name linux-musl - --pack - --all - --no-build-java - --publish - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_arm64.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_musl_arm64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_arm64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: jobs/codesign-xplat.yml + parameters: + inputName: Linux_musl_arm - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: jobs/codesign-xplat.yml + # Build Linux Musl ARM64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: jobs/default-build.yml parameters: - inputName: Linux_musl_arm64 + jobName: Linux_musl_arm64_build + jobDisplayName: "Build: Linux Musl ARM64" + agentOs: Linux + useHostedUbuntu: false + container: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 + buildArgs: + --arch arm64 + --os-name linux-musl + --pack + --all + --no-build-java + --publish + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: jobs/codesign-xplat.yml + parameters: + inputName: Linux_musl_arm64 - ${{ if ne(parameters.skipTests, 'true') }}: # Test jobs @@ -535,7 +526,7 @@ stages: isAzDOTestingJob: true buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) beforeBuild: - - bash: "./eng/scripts/install-nginx-mac.sh" + - bash: "./eng/scripts/install-nginx.sh" displayName: Installing Nginx artifacts: - name: MacOS_Test_Logs_Attempt_$(System.JobAttempt) @@ -556,7 +547,7 @@ stages: useHostedUbuntu: false buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) beforeBuild: - - bash: "./eng/scripts/install-nginx-linux.sh" + - bash: "./eng/scripts/install-nginx.sh" displayName: Installing Nginx - bash: "echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p" displayName: Increase inotify limit @@ -636,8 +627,6 @@ stages: agentOs: Windows isAzDOTestingJob: true timeoutInMinutes: 240 - # Temporarily disabled due to https://github.com/dotnet/aspnetcore/issues/63140 - condition: 'false' steps: - script: git submodule update --init displayName: Update submodules @@ -680,4 +669,4 @@ stages: timeoutInMinutes: 120 variables: # Log environment variables in binary logs to ease debugging - MSBUILDLOGALLENVIRONMENTVARIABLES: true + MSBUILDLOGALLENVIRONMENTVARIABLES: true \ No newline at end of file diff --git a/src/aspnetcore/.azure/pipelines/ci-unofficial.yml b/src/aspnetcore/.azure/pipelines/ci-unofficial.yml new file mode 100644 index 000000000000..eb6e293c07e3 --- /dev/null +++ b/src/aspnetcore/.azure/pipelines/ci-unofficial.yml @@ -0,0 +1,700 @@ +# +# See https://learn.microsoft.com/azure/devops/pipelines/yaml-schema for details on this file. +# + +# Configure which branches trigger builds +trigger: none + +pr: none + +parameters: +# Choose whether to skip tests when running pipeline manually. +- name: skipTests + default: false + displayName: Skip tests? + type: boolean + +variables: +- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE + value: true +- name: GRADLE_USER_HOME + value: $(Build.SourcesDirectory)/.gradle +- name: _TeamName + value: AspNetCore +- name: _PublishUsingPipelines + value: true +- name: PostBuildSign + value: false +- name: _ArcadePublishNonWindowsArg + value: '' +- name: _UseHelixOpenQueues + value: ${{ ne(variables['System.TeamProject'], 'internal') }} +- ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - name: _BuildArgs + value: /p:TeamName=$(_TeamName) + /p:OfficialBuildId=$(Build.BuildNumber) + /p:SkipTestBuild=true + /p:PostBuildSign=$(PostBuildSign) + # Publish-Build-Assets provides: MaestroAccessToken, BotAccount-dotnet-maestro-bot-PAT + - group: Publish-Build-Assets + # The following extra properties are not set when testing. Use with final build.[cmd,sh] of asset-producing jobs. + - name: _PublishArgs + value: /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) +- ${{ if in(variables['Build.Reason'], 'PullRequest') }}: + - name: _BuildArgs + value: '/p:SkipTestBuild=true /p:PostBuildSign=$(PostBuildSign)' + - name: _PublishArgs + value: '' +- name: WindowsArm64LogArgs + value: /bl:artifacts/log/Release/Build.arm64.binlog +- name: Windows64LogArgs + value: /bl:artifacts/log/Release/Build.x64.binlog +- name: Windows86LogArgs + value: -ExcludeCIBinaryLog +- name: WindowsSignLogArgs + value: /bl:artifacts/log/Release/Build.CodeSign.binlog +- name: WindowsInstallersLogArgs + value: /bl:artifacts/log/Release/Build.Installers.binlog +- name: _InternalRuntimeDownloadArgs + value: -RuntimeSourceFeed https://ci.dot.net/internal + -RuntimeSourceFeedKey $(dotnetbuilds-internal-container-read-token-base64) + /p:DotNetAssetRootAccessTokenSuffix='$(dotnetbuilds-internal-container-read-token-base64)' +# The code signing doesn't use the aspnet build scripts, so the msbuild parameters have to be passed directly. This +# is awkward but necessary because the eng/common/ build scripts don't add the msbuild properties automatically. +- name: _InternalRuntimeDownloadCodeSignArgs + value: $(_InternalRuntimeDownloadArgs) + /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal + /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) +- group: DotNet-HelixApi-Access +- name: _SignType + value: test +# Exclude artifacts & .packages folders from CodeQL scans +- name: LGTM_INDEX_FILTERS + value: | + exclude:.packages + exclude:artifacts +- template: /eng/common/templates-official/variables/pool-providers.yml@self + +resources: + repositories: + # Repo: 1ESPipelineTemplates/1ESPipelineTemplates + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines + parameters: + sdl: + sourceAnalysisPool: + name: NetCore1ESPool-Svc-Internal + image: windows.vs2022.amd64 + os: windows + spotBugs: + enabled: false + policheck: + enabled: true + tsa: + enabled: true + containers: + azureLinux30Net10BuildAmd64: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64 + stages: + - stage: build + displayName: Build + jobs: + # Code check + - ${{ if in(variables['Build.Reason'], 'PullRequest', 'Manual') }}: + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Code_check + jobDisplayName: Code check + agentOs: Windows + # Need full history for git log comparison in CodeCheck.ps1 + fetchDepth: 0 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + steps: + - powershell: ./eng/scripts/CodeCheck.ps1 -ci $(_InternalRuntimeDownloadArgs) + displayName: Run eng/scripts/CodeCheck.ps1 + artifacts: + - name: Code_Check_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + + # Build Windows (x64/x86/arm64) + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + codeSign: true + jobName: Windows_build + jobDisplayName: "Build: Windows x64/x86/arm64" + agentOs: Windows + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + steps: + - ${{ if notIn(variables['Build.Reason'], 'PullRequest') }}: + - script: echo "##vso[build.addbuildtag]daily-build" + displayName: 'Set CI daily-build tag' + + # !!! NOTE !!! Some of these steps have disabled code signing. + # This is intentional to workaround https://github.com/dotnet/arcade/issues/1957 which always re-submits for code-signing, even + # if they have already been signed. This results in slower builds due to re-submitting the same .nupkg many times for signing. + # The sign settings have been configured to + - script: ./eng/build.cmd + -ci + -prepareMachine + -nativeToolsOnMachine + -arch x64 + -pack + -all + $(_BuildArgs) + $(_InternalRuntimeDownloadArgs) + $(Windows64LogArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build x64 + + # Build the x86 shared framework + # This is going to actually build x86 native assets. + - script: ./eng/build.cmd + -ci + -prepareMachine + -noBuildRepoTasks + -arch x86 + -pack + -all + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + $(_InternalRuntimeDownloadArgs) + $(Windows86LogArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build x86 + + # Build the arm64 shared framework + - script: ./eng/build.cmd + -ci + -prepareMachine + -noBuildRepoTasks + -arch arm64 + -pack + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + $(_InternalRuntimeDownloadArgs) + $(WindowsArm64LogArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build ARM64 + + - script: .\src\SiteExtensions\build.cmd + -ci + -prepareMachine + -noBuildRepoTasks + -pack + -noBuildDeps + -noBuildNative + $(_BuildArgs) + $(_InternalRuntimeDownloadArgs) + /p:_DevBuild=true + env: + MSBUILDUSESERVER: "1" + displayName: Build SiteExtension + + # Windows installers bundle x86/x64/arm64 assets + - script: ./eng/build.cmd + -ci + -prepareMachine + -noBuildRepoTasks + -sign + -buildInstallers + -noBuildNative + -publish + /p:DotNetSignType=$(_SignType) + /p:AssetManifestFileName=aspnetcore-win.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + $(WindowsInstallersLogArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build Installers + + artifacts: + - name: Windows_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Windows_Packages + path: artifacts/packages/ + - name: Windows_HostingBundle + path: artifacts/bin/WindowsHostingBundle + - name: Windows_ANCM_Msi + path: artifacts/bin/ANCMv2 + - name: Windows_ANCMIISExpress_Msi + path: artifacts/bin/AncmIISExpressV2 + + # Build MacOS arm64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: MacOs_arm64_build + jobDisplayName: "Build: macOS arm64" + agentOs: macOs + timeoutInMinutes: 90 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch arm64 + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-MacOS_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: MacOS_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: MacOS_arm64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: MacOS_arm64 + + # Build MacOS x64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: MacOs_x64_build + jobDisplayName: "Build: macOS x64" + agentOs: macOs + timeoutInMinutes: 90 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-MacOS_x64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: MacOS_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: MacOS_x64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: MacOS_x64 + + # Build Linux x64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_x64_build + jobDisplayName: "Build: Linux x64" + agentOs: Linux + useHostedUbuntu: false + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch x64 + --pack + --all + --build-installers + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_x64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_x64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_x64 + + # Build Linux ARM + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_arm_build + jobDisplayName: "Build: Linux ARM" + agentOs: Linux + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch arm + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_arm + + # Build Linux ARM64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_arm64_build + jobDisplayName: "Build: Linux ARM64" + agentOs: Linux + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch arm64 + --pack + --all + --build-installers + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_arm64 + + # Build Linux Musl x64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_musl_x64_build + jobDisplayName: "Build: Linux Musl x64" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch x64 + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_x64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + disableComponentGovernance: true + artifacts: + - name: Linux_musl_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_x64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_x64 + + # Build Linux Musl ARM + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_musl_arm_build + jobDisplayName: "Build: Linux Musl ARM" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch arm + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_arm + + # Build Linux Musl ARM64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_musl_arm64_build + jobDisplayName: "Build: Linux Musl ARM64" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + buildArgs: + --arch arm64 + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_arm64 + + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Windows_Test + jobDisplayName: "Test: Windows Server x64" + agentOs: Windows + isAzDOTestingJob: true + continueOnError: true + # Just uploading artifacts/logs/ files can take 15 minutes. Doubling the cancel timeout for this job. + cancelTimeoutInMinutes: 30 + buildArgs: -all -pack -test -binaryLog /p:SkipHelixReadyTests=true /p:SkipIISNewHandlerTests=true /p:SkipIISTests=true + /p:SkipIISExpressTests=true /p:SkipIISNewShimTests=true /p:RunBlazorPlaywrightTemplateTests=true /p:DoNotCleanUpTemplates=true + $(_InternalRuntimeDownloadArgs) + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + - powershell: "& ./src/Servers/IIS/tools/UpdateIISExpressCertificate.ps1; & ./src/Servers/IIS/tools/update_schema.ps1" + displayName: Setup IISExpress test certificates and schema + artifacts: + - name: Windows_Test_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Windows_Test_Results_Attempt_$(System.JobAttempt) + path: artifacts/TestResults/ + publishOnError: true + includeForks: true + + - ${{ if and(ne(parameters.skipTests, 'true'), in(variables['Build.Reason'], 'PullRequest', 'Manual')) }}: + # Test jobs + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: MacOS_Test + jobDisplayName: "Test: macOS" + agentOs: macOS + timeoutInMinutes: 90 + isAzDOTestingJob: true + buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + - bash: "./eng/scripts/install-nginx.sh" + displayName: Installing Nginx + artifacts: + - name: MacOS_Test_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: MacOS_Test_Results_Attempt_$(System.JobAttempt) + path: artifacts/TestResults/ + publishOnError: true + includeForks: true + + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Linux_Test + jobDisplayName: "Test: Ubuntu x64" + agentOs: Linux + isAzDOTestingJob: true + useHostedUbuntu: false + buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + - bash: "./eng/scripts/install-nginx.sh" + displayName: Installing Nginx + - bash: "echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p" + displayName: Increase inotify limit + artifacts: + - name: Linux_Test_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_Test_Results_Attempt_$(System.JobAttempt) + path: artifacts/TestResults/ + publishOnError: true + includeForks: true + + # Helix x64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Helix_x64_Subset_1 + jobDisplayName: 'Tests: Helix x64 Subset 1' + agentOs: Windows + timeoutInMinutes: 240 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + steps: + # Build the shared framework + - script: ./eng/build.cmd -ci -prepareMachine -nativeToolsOnMachine -nobl -all -pack -arch x64 + /p:CrossgenOutput=false /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log $(_InternalRuntimeDownloadArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build shared fx + # -noBuildRepoTasks -noBuildNative -noBuild to avoid repeating work done in the previous step. + - script: ./eng/build.cmd -ci -prepareMachine -nativeToolsOnMachine -all -noBuildRepoTasks -noBuildNative -noBuild -test + -projects eng\helix\helix.proj /p:IsHelixPRCheck=true /p:IsHelixJob=true /p:HelixSubset=1 + /p:CrossgenOutput=false /p:RunTemplateTests=false /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log $(_InternalRuntimeDownloadArgs) + displayName: Run build.cmd helix target + env: + HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues + SYSTEM_ACCESSTOKEN: $(System.AccessToken) # We need to set this env var to publish helix results to Azure Dev Ops + + artifacts: + - name: Helix_Logs_Subset_1_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + + # Helix x64 + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Helix_x64_Subset_2 + jobDisplayName: 'Tests: Helix x64 Subset 2' + agentOs: Windows + timeoutInMinutes: 240 + beforeBuild: + - script: git submodule update --init + displayName: Update submodules + steps: + # Build the shared framework + - script: ./eng/build.cmd -ci -prepareMachine -nativeToolsOnMachine -nobl -all -pack -arch x64 + /p:CrossgenOutput=false /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log $(_InternalRuntimeDownloadArgs) + env: + MSBUILDUSESERVER: "1" + displayName: Build shared fx + # -noBuildRepoTasks -noBuildNative -noBuild to avoid repeating work done in the previous step. + - script: ./eng/build.cmd -ci -prepareMachine -nativeToolsOnMachine -all -noBuildRepoTasks -noBuildNative -noBuild -test + -projects eng\helix\helix.proj /p:IsHelixPRCheck=true /p:IsHelixJob=true /p:HelixSubset=2 + /p:CrossgenOutput=false /p:RunTemplateTests=false /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log $(_InternalRuntimeDownloadArgs) + displayName: Run build.cmd helix target + env: + HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues + SYSTEM_ACCESSTOKEN: $(System.AccessToken) # We need to set this env var to publish helix results to Azure Dev Ops + + artifacts: + - name: Helix_Logs_Subset_2_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + + # Local development validation + - ${{ if in(variables['Build.Reason'], 'PullRequest', 'Manual') }}: + - template: .azure/pipelines/jobs/default-build.yml@self + parameters: + jobName: Local_Windows + jobDisplayName: 'Test: Windows local development validation' + agentOs: Windows + isAzDOTestingJob: true + timeoutInMinutes: 240 + steps: + - script: git submodule update --init + displayName: Update submodules + - script: ./restore.cmd + displayName: Run restore.cmd + - script: ./eng/build.cmd -all -noBuildJava -noBuildNodeJS + displayName: Build (No NodeJS) + - script: npm run build + displayName: Build JS + - script: ./eng/build.cmd -all -noBuildJava -pack -c Debug + displayName: Build (Debug) + - script: ./eng/build.cmd -all -noBuildJava -pack -c Release + displayName: Build (Release) + - script: ./src/ProjectTemplates/build.cmd + -test + -NoRestore + -NoBuild + -NoBuildDeps + -configuration Release + displayName: Run project template tests + - powershell: . ./activate.ps1; ./src/ProjectTemplates/scripts/Run-BlazorWeb-Locally.ps1 -Verbose + displayName: Run Blazor web app test script + + artifacts: + - name: Local_Windows_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + diff --git a/src/aspnetcore/.azure/pipelines/ci.yml b/src/aspnetcore/.azure/pipelines/ci.yml index ab0d36e1bedf..8d65fcf1c396 100644 --- a/src/aspnetcore/.azure/pipelines/ci.yml +++ b/src/aspnetcore/.azure/pipelines/ci.yml @@ -78,8 +78,6 @@ variables: value: /bl:artifacts/log/Release/Build.CodeSign.binlog - name: WindowsInstallersLogArgs value: /bl:artifacts/log/Release/Build.Installers.binlog -- name: WindowsArm64InstallersLogArgs - value: /bl:artifacts/log/Release/Build.Installers.Arm64.binlog - name: _InternalRuntimeDownloadArgs value: -RuntimeSourceFeed https://ci.dot.net/internal -RuntimeSourceFeedKey $(dotnetbuilds-internal-container-read-token-base64) @@ -140,6 +138,8 @@ extends: jobName: Code_check jobDisplayName: Code check agentOs: Windows + # Need full history for git log comparison in CodeCheck.ps1 + fetchDepth: 0 steps: - powershell: ./eng/scripts/CodeCheck.ps1 -ci $(_InternalRuntimeDownloadArgs) displayName: Run eng/scripts/CodeCheck.ps1 @@ -204,11 +204,9 @@ extends: -prepareMachine -noBuildRepoTasks -arch arm64 - -sign -pack -noBuildJava -noBuildNative - /p:DotNetSignType=$(_SignType) /p:OnlyPackPlatformSpecificPackages=true $(_BuildArgs) $(_InternalRuntimeDownloadArgs) @@ -230,23 +228,6 @@ extends: MSBUILDUSESERVER: "1" displayName: Build SiteExtension - # This runs code-signing on all packages, zips, and jar files as defined in build/CodeSign.targets. If - # https://github.com/dotnet/arcade/issues/1957 is resolved, consider running code-signing inline with the other - # previous steps. Sign check is disabled because it is run in a separate step below, after installers are built. - - script: ./eng/build.cmd - -ci - -prepareMachine - -noBuildRepoTasks - -noBuildNative - -noBuild - -sign - /p:DotNetSignType=$(_SignType) - $(_BuildArgs) - $(WindowsSignLogArgs) - env: - MSBUILDUSESERVER: "1" - displayName: Code sign packages - # Windows installers bundle x86/x64/arm64 assets - script: ./eng/build.cmd -ci @@ -255,33 +236,16 @@ extends: -sign -buildInstallers -noBuildNative - /p:DotNetSignType=$(_SignType) - $(_BuildArgs) - $(_InternalRuntimeDownloadArgs) - $(WindowsInstallersLogArgs) - env: - MSBUILDUSESERVER: "1" - displayName: Build Installers - - # Windows installers bundle and sharedfx msi for arm64 - - script: ./eng/build.cmd - -ci - -prepareMachine - -noBuildRepoTasks - -arch arm64 - -sign - -buildInstallers - -noBuildNative -publish /p:DotNetSignType=$(_SignType) /p:AssetManifestFileName=aspnetcore-win.xml $(_BuildArgs) $(_PublishArgs) $(_InternalRuntimeDownloadArgs) - $(WindowsArm64InstallersLogArgs) + $(WindowsInstallersLogArgs) env: MSBUILDUSESERVER: "1" - displayName: Build ARM64 Installers + displayName: Build Installers artifacts: - name: Windows_Logs_Attempt_$(System.JobAttempt) @@ -391,164 +355,170 @@ extends: inputName: Linux_x64 # Build Linux ARM - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - jobName: Linux_arm_build - jobDisplayName: "Build: Linux ARM" - agentOs: Linux - buildArgs: - --arch arm - --pack - --all - --no-build-java - $(_ArcadePublishNonWindowsArg) - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_arm.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_arm_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_arm_Packages - path: artifacts/packages/ - - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: .azure/pipelines/jobs/codesign-xplat.yml@self + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: .azure/pipelines/jobs/default-build.yml@self parameters: - inputName: Linux_arm + jobName: Linux_arm_build + jobDisplayName: "Build: Linux ARM" + agentOs: Linux + buildArgs: + --arch arm + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm_Packages + path: artifacts/packages/ - # Build Linux ARM64 - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - jobName: Linux_arm64_build - jobDisplayName: "Build: Linux ARM64" - agentOs: Linux - buildArgs: - --arch arm64 - --pack - --all - --build-installers - --no-build-java - $(_ArcadePublishNonWindowsArg) - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_arm64.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_arm64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_arm64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_arm - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: .azure/pipelines/jobs/codesign-xplat.yml@self + # Build Linux ARM64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: .azure/pipelines/jobs/default-build.yml@self parameters: - inputName: Linux_arm64 + jobName: Linux_arm64_build + jobDisplayName: "Build: Linux ARM64" + agentOs: Linux + buildArgs: + --arch arm64 + --pack + --all + --build-installers + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_arm64_Packages + path: artifacts/packages/ - # Build Linux Musl x64 - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - jobName: Linux_musl_x64_build - jobDisplayName: "Build: Linux Musl x64" - agentOs: Linux - container: azureLinux30Net10BuildAmd64 - buildArgs: - --arch x64 - --os-name linux-musl - --pack - --all - --no-build-java - $(_ArcadePublishNonWindowsArg) - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_x64.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - disableComponentGovernance: true - artifacts: - - name: Linux_musl_x64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_x64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_arm64 - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: .azure/pipelines/jobs/codesign-xplat.yml@self + # Build Linux Musl x64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: .azure/pipelines/jobs/default-build.yml@self parameters: - inputName: Linux_musl_x64 + jobName: Linux_musl_x64_build + jobDisplayName: "Build: Linux Musl x64" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + buildArgs: + --arch x64 + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_x64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + disableComponentGovernance: true + artifacts: + - name: Linux_musl_x64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_x64_Packages + path: artifacts/packages/ - # Build Linux Musl ARM - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - jobName: Linux_musl_arm_build - jobDisplayName: "Build: Linux Musl ARM" - agentOs: Linux - useHostedUbuntu: false - container: azureLinux30Net10BuildAmd64 - buildArgs: - --arch arm - --os-name linux-musl - --pack - --all - --no-build-java - $(_ArcadePublishNonWindowsArg) - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_arm.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_musl_arm_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_arm_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_x64 - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: .azure/pipelines/jobs/codesign-xplat.yml@self + # Build Linux Musl ARM + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: .azure/pipelines/jobs/default-build.yml@self parameters: - inputName: Linux_musl_arm + jobName: Linux_musl_arm_build + jobDisplayName: "Build: Linux Musl ARM" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + buildArgs: + --arch arm + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm_Packages + path: artifacts/packages/ - # Build Linux Musl ARM64 - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - jobName: Linux_musl_arm64_build - jobDisplayName: "Build: Linux Musl ARM64" - agentOs: Linux - useHostedUbuntu: false - container: azureLinux30Net10BuildAmd64 - buildArgs: - --arch arm64 - --os-name linux-musl - --pack - --all - --no-build-java - $(_ArcadePublishNonWindowsArg) - -p:OnlyPackPlatformSpecificPackages=true - -p:AssetManifestFileName=aspnetcore-Linux_musl_arm64.xml - $(_BuildArgs) - $(_PublishArgs) - $(_InternalRuntimeDownloadArgs) - artifacts: - - name: Linux_musl_arm64_Logs_Attempt_$(System.JobAttempt) - path: artifacts/log/ - publishOnError: true - includeForks: true - - name: Linux_musl_arm64_Packages - path: artifacts/packages/ + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_arm - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - template: .azure/pipelines/jobs/codesign-xplat.yml@self + # Build Linux Musl ARM64 + - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: + - template: .azure/pipelines/jobs/default-build.yml@self parameters: - inputName: Linux_musl_arm64 + jobName: Linux_musl_arm64_build + jobDisplayName: "Build: Linux Musl ARM64" + agentOs: Linux + useHostedUbuntu: false + container: azureLinux30Net10BuildAmd64 + buildArgs: + --arch arm64 + --os-name linux-musl + --pack + --all + --no-build-java + $(_ArcadePublishNonWindowsArg) + -p:OnlyPackPlatformSpecificPackages=true + -p:AssetManifestFileName=aspnetcore-Linux_musl_arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + artifacts: + - name: Linux_musl_arm64_Logs_Attempt_$(System.JobAttempt) + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Linux_musl_arm64_Packages + path: artifacts/packages/ + + - ${{ if ne(variables.PostBuildSign, 'true') }}: + - template: .azure/pipelines/jobs/codesign-xplat.yml@self + parameters: + inputName: Linux_musl_arm64 - template: .azure/pipelines/jobs/default-build.yml@self parameters: @@ -586,7 +556,7 @@ extends: isAzDOTestingJob: true buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) beforeBuild: - - bash: "./eng/scripts/install-nginx-mac.sh" + - bash: "./eng/scripts/install-nginx.sh" displayName: Installing Nginx artifacts: - name: MacOS_Test_Logs_Attempt_$(System.JobAttempt) @@ -607,7 +577,7 @@ extends: useHostedUbuntu: false buildArgs: --all --test --binaryLog /p:RunTemplateTests=false /p:SkipHelixReadyTests=true $(_InternalRuntimeDownloadArgs) beforeBuild: - - bash: "./eng/scripts/install-nginx-linux.sh" + - bash: "./eng/scripts/install-nginx.sh" displayName: Installing Nginx - bash: "echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p" displayName: Increase inotify limit @@ -722,7 +692,7 @@ extends: enableInternalSources: true platform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-amd64' + container: azureLinux30Net10BuildAmd64 buildScript: './eng/build.sh' buildArguments: '--source-build $(_InternalRuntimeDownloadArgs)' jobProperties: @@ -749,44 +719,14 @@ extends: - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - template: /eng/common/templates-official/job/publish-build-assets.yml@self parameters: - dependsOn: - - Windows_build - - ${{ if ne(variables.PostBuildSign, 'true') }}: - - CodeSign_Xplat_MacOS_arm64 - - CodeSign_Xplat_MacOS_x64 - - CodeSign_Xplat_Linux_x64 - - CodeSign_Xplat_Linux_arm - - CodeSign_Xplat_Linux_arm64 - - CodeSign_Xplat_Linux_musl_x64 - - CodeSign_Xplat_Linux_musl_arm - - CodeSign_Xplat_Linux_musl_arm64 - - ${{ if eq(variables.PostBuildSign, 'true') }}: - - MacOs_arm64_build - - MacOs_x64_build - - Linux_x64_build - - Linux_arm_build - - Linux_arm64_build - - Linux_musl_x64_build - - Linux_musl_arm_build - - Linux_musl_arm64_build - # In addition to the dependencies above that provide assets, ensure the build was successful overall. - - ${{ if in(variables['Build.Reason'], 'Manual') }}: - - Code_check - - ${{ if ne(parameters.skipTests, 'true') }}: - - Windows_Test - - MacOS_Test - - Linux_Test - - Helix_x64_Subset_1 - - Helix_x64_Subset_2 - - ${{ if eq(variables.enableSourceIndex, 'true') }}: - - SourceIndexStage1 - - Source_Build_Managed + dependsOn: [] pool: name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 publishUsingPipelines: ${{ variables._PublishUsingPipelines }} enablePublishBuildArtifacts: true # publish artifacts/log files publishAssetsImmediately: true # Don't use a separate stage for darc publishing. + isAssetlessBuild: true - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - template: /eng/common/templates-official/post-build/post-build.yml@self @@ -796,4 +736,4 @@ extends: enableSigningValidation: false enableNugetValidation: false publishInstallersAndChecksums: true - publishAssetsImmediately: true + publishAssetsImmediately: true \ No newline at end of file diff --git a/src/aspnetcore/.azure/pipelines/components-e2e-tests.yml b/src/aspnetcore/.azure/pipelines/components-e2e-tests.yml index 2386635fa813..fc0acb70ba41 100644 --- a/src/aspnetcore/.azure/pipelines/components-e2e-tests.yml +++ b/src/aspnetcore/.azure/pipelines/components-e2e-tests.yml @@ -98,8 +98,6 @@ jobs: exit 1 fi displayName: Run E2E tests - env: - DOTNET_EnableAVX512: 0 - script: .dotnet/dotnet test ./src/Components/test/E2ETest -c $(BuildConfiguration) --no-build --filter 'Quarantined=true' -p:RunQuarantinedTests=true -p:VsTestUseMSBuildOutput=false --logger:"trx%3BLogFileName=Microsoft.AspNetCore.Components.E2ETests.trx" @@ -107,8 +105,6 @@ jobs: --results-directory $(Build.SourcesDirectory)/artifacts/TestResults/$(BuildConfiguration)/Quarantined displayName: Run Quarantined E2E tests continueOnError: true - env: - DOTNET_EnableAVX512: 0 - task: PublishTestResults@2 displayName: Publish E2E Test Results inputs: diff --git a/src/aspnetcore/.azure/pipelines/identitymodel-helix-matrix.yml b/src/aspnetcore/.azure/pipelines/identitymodel-helix-matrix.yml index 8329ee4d11f9..11e9cf0a37d8 100644 --- a/src/aspnetcore/.azure/pipelines/identitymodel-helix-matrix.yml +++ b/src/aspnetcore/.azure/pipelines/identitymodel-helix-matrix.yml @@ -26,7 +26,7 @@ resources: ref: refs/tags/release extends: - template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines parameters: sdl: sourceAnalysisPool: diff --git a/src/aspnetcore/.azure/pipelines/jobs/default-build.yml b/src/aspnetcore/.azure/pipelines/jobs/default-build.yml index 0283be806cf1..a2e24e0560eb 100644 --- a/src/aspnetcore/.azure/pipelines/jobs/default-build.yml +++ b/src/aspnetcore/.azure/pipelines/jobs/default-build.yml @@ -71,6 +71,9 @@ parameters: timeoutInMinutes: 180 testRunTitle: $(AgentOsName)-$(BuildConfiguration) useHostedUbuntu: true + # Checkout optimization parameters + fetchDepth: 1 + fetchTags: false # We need longer than the default amount of 5 minutes to upload our logs/artifacts. (We currently take around 5 mins in the best case). # This makes sure we have time to upload everything in the case of a build timeout - really important for investigating a build @@ -103,7 +106,7 @@ jobs: # See https://github.com/dotnet/arcade/blob/master/Documentation/ChoosingAMachinePool.md pool: ${{ if eq(parameters.agentOs, 'macOS') }}: - vmImage: macOS-13 + vmImage: macOS-15 ${{ if eq(parameters.agentOs, 'Linux') }}: ${{ if eq(parameters.useHostedUbuntu, true) }}: vmImage: ubuntu-22.04 @@ -159,12 +162,15 @@ jobs: - name: ${{ pair.key }} value: ${{ pair.value }} steps: + - checkout: self + fetchDepth: ${{ parameters.fetchDepth }} + fetchTags: ${{ parameters.fetchTags }} - ${{ if ne(parameters.agentOs, 'Windows') }}: - script: df -h displayName: Disk size - ${{ if eq(parameters.agentOs, 'macOS') }}: - - script: sudo xcode-select -s /Applications/Xcode_15.2.0.app/Contents/Developer - displayName: Use XCode 15.2.0 + - script: sudo xcode-select -s /Applications/Xcode_16.4.0.app/Contents/Developer + displayName: Use XCode 16.4.0 - ${{ if and(eq(parameters.agentOs, 'Windows'), eq(parameters.isAzDOTestingJob, true)) }}: - powershell: ./eng/scripts/InstallProcDump.ps1 displayName: Install ProcDump @@ -319,7 +325,7 @@ jobs: pool: ${{ if eq(parameters.agentOs, 'macOS') }}: name: Azure Pipelines - image: macOS-13 + image: macOS-15 os: macOS ${{ if eq(parameters.agentOs, 'Linux') }}: name: $(DncEngInternalBuildPool) @@ -385,12 +391,15 @@ jobs: - name: ${{ pair.key }} value: ${{ pair.value }} steps: + - checkout: self + fetchDepth: ${{ parameters.fetchDepth }} + fetchTags: ${{ parameters.fetchTags }} - ${{ if ne(parameters.agentOs, 'Windows') }}: - script: df -h displayName: Disk size - ${{ if eq(parameters.agentOs, 'macOS') }}: - - script: sudo xcode-select -s /Applications/Xcode_15.2.0.app/Contents/Developer - displayName: Use XCode 15.2.0 + - script: sudo xcode-select -s /Applications/Xcode_16.4.0.app/Contents/Developer + displayName: Use XCode 16.4.0 - ${{ if and(eq(parameters.agentOs, 'Windows'), eq(parameters.isAzDOTestingJob, true)) }}: - powershell: ./eng/scripts/InstallProcDump.ps1 displayName: Install ProcDump diff --git a/src/aspnetcore/.azure/pipelines/signalr-daily-tests.yml b/src/aspnetcore/.azure/pipelines/signalr-daily-tests.yml deleted file mode 100644 index 85a88a460790..000000000000 --- a/src/aspnetcore/.azure/pipelines/signalr-daily-tests.yml +++ /dev/null @@ -1,48 +0,0 @@ -# Uses Scheduled Triggers, which aren't supported in YAML yet. -# https://learn.microsoft.com/azure/devops/pipelines/build/triggers?view=vsts&tabs=yaml#scheduled - -# Daily Tests for ASP.NET Core SignalR -# These use Sauce Labs resources, hence they run daily rather than per-commit. - -variables: - - ${{ if ne(variables['System.TeamProject'], 'public') }}: - - group: AzureDevOps-Artifact-Feeds-Pats - - template: /eng/common/templates-official/variables/pool-providers.yml@self - -resources: - repositories: - # Repo: 1ESPipelineTemplates/1ESPipelineTemplates - - repository: 1esPipelines - type: git - name: 1ESPipelineTemplates/1ESPipelineTemplates - ref: refs/tags/release - -extends: - template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines - parameters: - sdl: - sourceAnalysisPool: - name: NetCore1ESPool-Svc-Internal - image: 1es-windows-2022 - os: windows - codeql: - compiled: - enabled: false - justificationForDisabling: 'This is a test-only pipeline. The same product code is already scanned in the main pipeline (aspnetcore-ci)' - stages: - - stage: build - displayName: Build - # The only Daily Tests we have run in Sauce Labs and only need to run on one machine (because they just trigger SauceLabs) - # Hence we use the 'default-build.yml' template because it represents a single phase - jobs: - - template: .azure/pipelines/jobs/default-build.yml@self - parameters: - buildDirectory: src/SignalR - buildArgs: "/p:DailyTests=true /p:SauceUser='$(asplab-sauce-labs-username)' /p:SauceKey='$(asplab-sauce-labs-access-key)' -t" - agentOs: Windows - jobName: SignalRDailyTests - jobDisplayName: "SignalR Daily Tests" - artifacts: - - name: Windows_Logs - path: artifacts/log/ - publishOnError: true diff --git a/src/aspnetcore/.azuredevops/dependabot.yml b/src/aspnetcore/.azuredevops/dependabot.yml new file mode 100644 index 000000000000..f18e60565a42 --- /dev/null +++ b/src/aspnetcore/.azuredevops/dependabot.yml @@ -0,0 +1,5 @@ +version: 2 + +# Disabling dependabot on Azure DevOps as this is a mirrored repo. Updates should go through github. +enable-campaigned-updates: false +enable-security-updates: false diff --git a/src/aspnetcore/.azuredevops/pull_request_template/branches/internal.md b/src/aspnetcore/.azuredevops/pull_request_template/branches/internal.md index 59ad82ee7ad9..199bdce27062 100644 --- a/src/aspnetcore/.azuredevops/pull_request_template/branches/internal.md +++ b/src/aspnetcore/.azuredevops/pull_request_template/branches/internal.md @@ -40,6 +40,6 @@ Fixes #{bug number} (in this specific format) ---- -## When servicing release/2.1 +## When servicing release/2.3 - [ ] Make necessary changes in eng/PatchConfig.props diff --git a/src/aspnetcore/.azuredevops/pull_request_template/branches/release.md b/src/aspnetcore/.azuredevops/pull_request_template/branches/release.md index a0689c5c7a86..dd84172cb188 100644 --- a/src/aspnetcore/.azuredevops/pull_request_template/branches/release.md +++ b/src/aspnetcore/.azuredevops/pull_request_template/branches/release.md @@ -46,6 +46,6 @@ Fixes #{bug number} (in this specific format) ---- -## When servicing release/2.1 +## When servicing release/2.3 - [ ] Make necessary changes in eng/PatchConfig.props diff --git a/src/aspnetcore/.devcontainer/devcontainer.json b/src/aspnetcore/.devcontainer/devcontainer.json index a30ea9f0a474..6dda64455e23 100644 --- a/src/aspnetcore/.devcontainer/devcontainer.json +++ b/src/aspnetcore/.devcontainer/devcontainer.json @@ -31,7 +31,6 @@ // This is needed so that things like the C# extension can resolve the correct SDK version "remoteEnv": { "PATH": "${containerWorkspaceFolder}/.dotnet:${containerEnv:PATH}", - "DOTNET_MULTILEVEL_LOOKUP": "0", "TARGET": "net10.0", "DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER": "true" }, diff --git a/src/aspnetcore/.github/PULL_REQUEST_TEMPLATE/servicing.md b/src/aspnetcore/.github/PULL_REQUEST_TEMPLATE/servicing.md index 59ad82ee7ad9..199bdce27062 100644 --- a/src/aspnetcore/.github/PULL_REQUEST_TEMPLATE/servicing.md +++ b/src/aspnetcore/.github/PULL_REQUEST_TEMPLATE/servicing.md @@ -40,6 +40,6 @@ Fixes #{bug number} (in this specific format) ---- -## When servicing release/2.1 +## When servicing release/2.3 - [ ] Make necessary changes in eng/PatchConfig.props diff --git a/src/aspnetcore/.github/copilot-instructions.md b/src/aspnetcore/.github/copilot-instructions.md index a0b2a2e577fb..f3e40b4145bb 100644 --- a/src/aspnetcore/.github/copilot-instructions.md +++ b/src/aspnetcore/.github/copilot-instructions.md @@ -40,3 +40,6 @@ * On Linux/Mac: `source activate.sh` (from repository root) * If not in the repository root, navigate there first or use the full path to the activation script. * This ensures that the correct version of .NET SDK is used for the repository. + +## ASP.NET Core Components Area +* When working on issues under the src/Components area, follow the instructions in [./instructions/components.instructions.md](./instructions/components.instructions.md). diff --git a/src/aspnetcore/.github/instructions/components.instructions.md b/src/aspnetcore/.github/instructions/components.instructions.md new file mode 100644 index 000000000000..619cb523397f --- /dev/null +++ b/src/aspnetcore/.github/instructions/components.instructions.md @@ -0,0 +1,6 @@ +--- +description: Instructions for folder 'src\Components' +applyTo: "src/Components/**" +--- + +Follow the instructions in [src/Components/AGENTS.md](../../src/Components/AGENTS.md) when working on issues in the Components area. diff --git a/src/aspnetcore/.github/policies/resourceManagement.yml b/src/aspnetcore/.github/policies/resourceManagement.yml index 4a9c0e892785..b49980a7e59e 100644 --- a/src/aspnetcore/.github/policies/resourceManagement.yml +++ b/src/aspnetcore/.github/policies/resourceManagement.yml @@ -1,11 +1,11 @@ # Policybot docs live at https://eng.ms/docs/more/github-inside-microsoft/policies/resource-management -id: +id: name: GitOps.PullRequestIssueManagement description: GitOps.PullRequestIssueManagement primitive -owner: +owner: resource: repository disabled: false -where: +where: configuration: resourceManagementConfiguration: scheduledSearches: @@ -347,13 +347,12 @@ configuration: - payloadType: Pull_Request - isAction: action: Opened - - or: - - includesModifiedFiles: - files: - - src/Shared/Runtime + - includesModifiedFiles: + files: + - src/Shared/Runtime then: - addReply: - reply: Greetings human! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo! + reply: Greetings! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo! - addLabel: label: 'Attention: Shared Code Modified' description: '[Shared Code PRs] Flag PRs that affect shared code src/Shared/Runtime' @@ -441,19 +440,8 @@ configuration: branch: main then: - addMilestone: - milestone: 10.0-rc1 + milestone: 11.0-preview1 description: '[Milestone Assignments] Assign Milestone to PRs merged to the `main` branch' - - if: - - payloadType: Pull_Request - - isAction: - action: Closed - - targetsBranch: - branch: release/10.0-preview7 - then: - - removeMilestone - - addMilestone: - milestone: 10.0-preview7 - description: '[Milestone Assignments] Assign Milestone to PRs merged to release/10.0-preview1 branch' - if: - payloadType: Issues - isAction: @@ -566,6 +554,25 @@ configuration: Otherwise, please add `tell-mode` label. description: Add release/2.3 targeting PRs to the servicing project + - if: + - payloadType: Pull_Request + - isAction: + action: Opened + - targetsBranch: + branch: release/10.0 + - activitySenderHasPermission: + permission: Read + - not: + isActivitySender: + user: dotnet-maestro + issueAuthor: False + - not: + isActivitySender: + user: dotnet-maestro-bot + issueAuthor: False + then: + - addLabel: + label: servicing-consider - if: - payloadType: Pull_Request - labelAdded: @@ -605,6 +612,17 @@ configuration: - addReply: reply: Hi @${issueAuthor}. This PR was just approved to be included in the upcoming servicing release. Somebody from the @dotnet/aspnet-build team will get it merged when the branches are open. Until then, please make sure all the CI checks pass and the PR is reviewed. description: '[Servicing PR Approved] Let the author know that the PR will be merged by the build team' + - if: + - payloadType: Pull_Request + - isAction: + action: Closed + - targetsBranch: + branch: release/10.0 + then: + - removeMilestone + - addMilestone: + milestone: 10.0.3 + description: '[Milestone Assignments] Assign Milestone to PRs merged to release/10.0 branch' - if: - payloadType: Pull_Request - isAction: @@ -614,7 +632,7 @@ configuration: then: - removeMilestone - addMilestone: - milestone: 9.0.9 + milestone: 9.0.13 description: '[Milestone Assignments] Assign Milestone to PRs merged to release/9.0 branch' - if: - payloadType: Pull_Request @@ -625,7 +643,7 @@ configuration: then: - removeMilestone - addMilestone: - milestone: 8.0.20 + milestone: 8.0.24 description: '[Milestone Assignments] Assign Milestone to PRs merged to release/8.0 branch' - if: - payloadType: Issues @@ -685,16 +703,6 @@ configuration: - addLabel: label: NativeAOT description: Label AOT related issues appropriately - - if: - - payloadType: Issues - - labelAdded: - label: help wanted - - isAction: - action: Labeled - then: - - addReply: - reply: Looks like this issue has been identified as a candidate for community contribution. If you're considering sending a PR for this issue, look for the `Summary Comment` link in the issue description. That comment has been left by an engineer on our team to help you get started with handling this issue. You can learn more about our Help Wanted process [here](https://aka.ms/aspnet/processes/help-wanted) - description: Drop a comment when a help candidate issue is identified - if: - payloadType: Pull_Request - isAction: @@ -740,6 +748,14 @@ configuration: - enableAutoMerge: mergeMethod: "squash" description: '[Infrastructure PRs] Add area-infrastructure label to dependabot update Pull Requests & enable auto-merge' -onFailure: -onSuccess: +onFailure: +onSuccess: + + + + + + + + diff --git a/src/aspnetcore/.github/workflows/backport.yml b/src/aspnetcore/.github/workflows/backport.yml index c4fbcf43aace..9708c1248680 100644 --- a/src/aspnetcore/.github/workflows/backport.yml +++ b/src/aspnetcore/.github/workflows/backport.yml @@ -14,7 +14,7 @@ permissions: jobs: backport: - uses: dotnet/arcade/.github/workflows/backport-base.yml@7c0434b26153965459a8d7968f18cf7c0a2cf463 # 2025-01-13 + uses: dotnet/arcade/.github/workflows/backport-base.yml@e43d0e93ea2e7b1b3107abb67684db8ee76b2589 # 2025-01-13 with: pr_description_template: | Backport of #%source_pr_number% to %target_branch% @@ -63,6 +63,6 @@ jobs: ---- - ## When servicing release/2.1 + ## When servicing release/2.3 - - [ ] Make necessary changes in eng/PatchConfig.props + - [ ] Make necessary changes in eng/PatchConfig.props \ No newline at end of file diff --git a/src/aspnetcore/.github/workflows/browsertesting-open-issue.yml b/src/aspnetcore/.github/workflows/browsertesting-open-issue.yml index 64c966577dcb..9dc6b21b35ed 100644 --- a/src/aspnetcore/.github/workflows/browsertesting-open-issue.yml +++ b/src/aspnetcore/.github/workflows/browsertesting-open-issue.yml @@ -14,7 +14,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.1 # copilot can be assigned to the issue # https://cli.github.com/manual/gh_issue_create diff --git a/src/aspnetcore/.github/workflows/copilot-setup-steps.yml b/src/aspnetcore/.github/workflows/copilot-setup-steps.yml index 7ee5ddd2d5b9..3e8e567ec592 100644 --- a/src/aspnetcore/.github/workflows/copilot-setup-steps.yml +++ b/src/aspnetcore/.github/workflows/copilot-setup-steps.yml @@ -14,11 +14,32 @@ jobs: # You can define any steps you want, and they will run before the agent starts. # If you do not check out your code, Copilot will do this for you. steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 + with: + submodules: 'true' - # Include PrepareForHelix to maximise what is downloaded here + # Include PrepareForHelix to maximise what is downloaded here - name: Build solution env: # prevent GitInfo errors CI: false run: ./restore.sh + + # For MCP servers like nuget's + - name: Install .NET 10.x + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 10.x + dotnet-quality: preview + + # for MCP servers + - name: Install .NET 8.x + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 8.x + + # Diagnostics in the log + - name: dotnet --info + run: dotnet --info diff --git a/src/aspnetcore/.github/workflows/inter-branch-merge-flow.yml b/src/aspnetcore/.github/workflows/inter-branch-merge-flow.yml index df6fcb03ec8d..543e0953bb9b 100644 --- a/src/aspnetcore/.github/workflows/inter-branch-merge-flow.yml +++ b/src/aspnetcore/.github/workflows/inter-branch-merge-flow.yml @@ -7,7 +7,8 @@ on: permissions: contents: write pull-requests: write + issues: write jobs: Merge: - uses: dotnet/arcade/.github/workflows/backport-base.yml@7c0434b26153965459a8d7968f18cf7c0a2cf463 # 2024-06-24 + uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@e43d0e93ea2e7b1b3107abb67684db8ee76b2589 # 2024-06-24 \ No newline at end of file diff --git a/src/aspnetcore/.github/workflows/locker.yml b/src/aspnetcore/.github/workflows/locker.yml index 3c8fa04b9200..4866829dfa4c 100644 --- a/src/aspnetcore/.github/workflows/locker.yml +++ b/src/aspnetcore/.github/workflows/locker.yml @@ -23,7 +23,7 @@ jobs: if: ${{ github.repository_owner == 'dotnet' }} steps: - name: Checkout Actions - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 with: repository: "microsoft/vscode-github-triage-actions" path: ./actions diff --git a/src/aspnetcore/.github/workflows/markdownlint.yml b/src/aspnetcore/.github/workflows/markdownlint.yml index 659a2ee6d2f9..fcb3b7338895 100644 --- a/src/aspnetcore/.github/workflows/markdownlint.yml +++ b/src/aspnetcore/.github/workflows/markdownlint.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 - name: Use Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: 16.x - name: Run Markdownlint diff --git a/src/aspnetcore/.github/workflows/runtime-sync.yml b/src/aspnetcore/.github/workflows/runtime-sync.yml index 0600fd4b5c24..641b456bc243 100644 --- a/src/aspnetcore/.github/workflows/runtime-sync.yml +++ b/src/aspnetcore/.github/workflows/runtime-sync.yml @@ -21,14 +21,14 @@ jobs: runs-on: windows-latest steps: - name: Checkout aspnetcore - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 with: # Test this script using changes in a fork repository: 'dotnet/aspnetcore' path: aspnetcore ref: main - name: Checkout runtime - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 with: # Test this script using changes in a fork repository: 'dotnet/runtime' @@ -47,7 +47,7 @@ jobs: mkdir ..\artifacts git status > ..\artifacts\status.txt git diff > ..\artifacts\diff.txt - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: results path: artifacts diff --git a/src/aspnetcore/.github/workflows/update-jquery-validate.yml b/src/aspnetcore/.github/workflows/update-jquery-validate.yml index 11f84794457a..f9d609789719 100644 --- a/src/aspnetcore/.github/workflows/update-jquery-validate.yml +++ b/src/aspnetcore/.github/workflows/update-jquery-validate.yml @@ -16,10 +16,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: '20.x' diff --git a/src/aspnetcore/.github/workflows/update-sdk.yml b/src/aspnetcore/.github/workflows/update-sdk.yml index bb18b8b4ed79..aec501eafc06 100644 --- a/src/aspnetcore/.github/workflows/update-sdk.yml +++ b/src/aspnetcore/.github/workflows/update-sdk.yml @@ -19,7 +19,7 @@ jobs: name: Update .NET SDK runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 - uses: martincostello/update-dotnet-sdk@76e2c0df2303d4f6a404228105ebb7d60ace0556 # v3.4.0 with: quality: 'daily' diff --git a/src/aspnetcore/.github/workflows/update-selenium-and-playwright-dependencies.yml b/src/aspnetcore/.github/workflows/update-selenium-and-playwright-dependencies.yml index 6e25e08b9400..54a5502ec4cc 100644 --- a/src/aspnetcore/.github/workflows/update-selenium-and-playwright-dependencies.yml +++ b/src/aspnetcore/.github/workflows/update-selenium-and-playwright-dependencies.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 - name: Update dependencies run: pwsh eng/scripts/update-selenium-and-playwright-versions.ps1 diff --git a/src/aspnetcore/.github/workflows/validate-npm-package-lock-json.yml b/src/aspnetcore/.github/workflows/validate-npm-package-lock-json.yml index 34cf0e9b30db..e41ee53aad21 100644 --- a/src/aspnetcore/.github/workflows/validate-npm-package-lock-json.yml +++ b/src/aspnetcore/.github/workflows/validate-npm-package-lock-json.yml @@ -14,13 +14,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.2.2 with: fetch-depth: 0 submodules: false - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: 20.x diff --git a/src/aspnetcore/.vscode/mcp.json b/src/aspnetcore/.vscode/mcp.json new file mode 100644 index 000000000000..3812a04e2bbb --- /dev/null +++ b/src/aspnetcore/.vscode/mcp.json @@ -0,0 +1,15 @@ +{ + "servers": { + "playwright": { + "type": "stdio", + "command": "npx", + "args": [ + "@playwright/mcp@latest" + ] + }, + "microsoft.docs.mcp": { + "type": "http", + "url": "https://learn.microsoft.com/api/mcp" + } + } +} diff --git a/src/aspnetcore/AspNetCore.slnx b/src/aspnetcore/AspNetCore.slnx index 60f5ca0d74fc..cf6f14abe556 100644 --- a/src/aspnetcore/AspNetCore.slnx +++ b/src/aspnetcore/AspNetCore.slnx @@ -7,7 +7,6 @@ - @@ -161,6 +160,9 @@ + + + @@ -469,14 +471,6 @@ - - - - - - - - @@ -1143,6 +1137,7 @@ + @@ -1190,8 +1185,8 @@ - + diff --git a/src/aspnetcore/Directory.Build.props b/src/aspnetcore/Directory.Build.props index 2754f04d06c3..010dead7aff3 100644 --- a/src/aspnetcore/Directory.Build.props +++ b/src/aspnetcore/Directory.Build.props @@ -136,6 +136,8 @@ $(NoWarn);TBD + + $(NoWarn);NU3027 diff --git a/src/aspnetcore/Directory.Build.targets b/src/aspnetcore/Directory.Build.targets index a2245ab43544..ae356cd2d0ed 100644 --- a/src/aspnetcore/Directory.Build.targets +++ b/src/aspnetcore/Directory.Build.targets @@ -53,8 +53,6 @@ '$(IsBenchmarkProject)' == 'true' OR '$(IsSampleProject)' == 'true' OR '$(IsMicrobenchmarksProject)' == 'true') ">false - - true @@ -73,24 +71,6 @@ $(PackageVersion) - - - $(BaselinePackageVersion.Substring(0, $(BaselinePackageVersion.IndexOf('-')))).0 - $(BaselinePackageVersion).0 - - $(BaselinePackageVersion) - $(BaselinePackageVersion) - - - $(BaselinePackageVersion) - - + + diff --git a/src/aspnetcore/README.md b/src/aspnetcore/README.md index 5af63de029e3..6e918232f153 100644 --- a/src/aspnetcore/README.md +++ b/src/aspnetcore/README.md @@ -29,7 +29,9 @@ and make pull-requests. ## Reporting security issues and bugs -Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). +Security issues and bugs should be reported privately to the Microsoft Security Response Center (MSRC) via the [MSRC Researcher Portal](https://msrc.microsoft.com/report/vulnerability/new). You should receive a response within 24 hours. Further information can be found in the [MSRC Report an Issue FAQ](https://www.microsoft.com/msrc/faqs-report-an-issue). You can also find these instructions in this repo's [Security doc](SECURITY.md). + +Also see info about related [Microsoft .NET Bounty Program](https://www.microsoft.com/msrc/bounty-dot-net-core). ## Related projects @@ -46,7 +48,7 @@ See [CODE-OF-CONDUCT](./CODE-OF-CONDUCT.md) ## Nightly builds -This table includes links to download the latest builds of the ASP.NET Core Shared Framework. Also included are links to download the Windows Hosting Bundle, which includes the ASP.NET Core Shared Framework, the .NET Runtime Shared Framework, and the IIS plugin (ASP.NET Core Module). You can download the latest .NET Runtime builds [here](https://github.com/dotnet/runtime/blob/main/docs/project/dogfooding.md#nightly-builds-table), and the latest .NET SDK builds [here](https://github.com/dotnet/installer#table). **If you're unsure what you need, then install the SDK; it has everything except the IIS plugin.** +This table includes links to download the latest builds of the ASP.NET Core Shared Framework. Also included are links to download the Windows Hosting Bundle, which includes the ASP.NET Core Shared Framework, the .NET Runtime Shared Framework, and the IIS plugin (ASP.NET Core Module). You can download the latest .NET Runtime builds [here](https://github.com/dotnet/runtime/blob/main/docs/project/dogfooding.md#nightly-builds-table), and the latest .NET SDK builds [here](https://github.com/dotnet/dotnet/blob/main/docs/builds-table.md). **If you're unsure what you need, then install the SDK; it has everything except the IIS plugin.** | Platform | Shared Framework (Installer) | Shared Framework (Binaries) | Hosting Bundle (Installer) | | :--------- | :----------: | :----------: | :----------: | diff --git a/src/aspnetcore/SECURITY.md b/src/aspnetcore/SECURITY.md index 5a9569ce1f58..3b0663100fd4 100644 --- a/src/aspnetcore/SECURITY.md +++ b/src/aspnetcore/SECURITY.md @@ -6,9 +6,9 @@ The .NET Core and ASP.NET Core support policy, including supported versions can ## Reporting a Vulnerability -Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) through https://msrc.microsoft.com or by emailing secure@microsoft.com. -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your -original message. Further information, including the MSRC PGP key, can be found in the [MSRC Report an Issue FAQ](https://www.microsoft.com/en-us/msrc/faqs-report-an-issue). +Security issues and bugs should be reported privately to the Microsoft Security Response Center (MSRC), via the [MSRC Researcher Portal](https://msrc.microsoft.com/report/vulnerability/new). +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your +original message. Further information can be found in the [MSRC Report an Issue FAQ](https://www.microsoft.com/msrc/faqs-report-an-issue). Reports via MSRC may qualify for the .NET Core Bug Bounty. Details of the .NET Core Bug Bounty including terms and conditions are at [https://aka.ms/corebounty](https://aka.ms/corebounty). diff --git a/src/aspnetcore/activate.ps1 b/src/aspnetcore/activate.ps1 index c780fca16fff..72e950eafc87 100644 --- a/src/aspnetcore/activate.ps1 +++ b/src/aspnetcore/activate.ps1 @@ -26,7 +26,6 @@ function deactivate ([switch]$init) { Remove-Item env:DOTNET_ROOT -ea ignore Remove-Item 'env:DOTNET_ROOT(x86)' -ea Ignore - Remove-Item env:DOTNET_MULTILEVEL_LOOKUP -ea ignore if (-not $init) { # Remove the deactivate function Remove-Item function:deactivate @@ -40,8 +39,6 @@ $_OLD_PATH = $env:PATH # Tell dotnet where to find itself $env:DOTNET_ROOT = "$PSScriptRoot\.dotnet" ${env:DOTNET_ROOT(x86)} = "$PSScriptRoot\.dotnet\x86" -# Tell dotnet not to look beyond the DOTNET_ROOT folder for more dotnet things -$env:DOTNET_MULTILEVEL_LOOKUP = 0 # Put dotnet first on PATH $env:PATH = "${env:DOTNET_ROOT};${env:PATH}" diff --git a/src/aspnetcore/activate.sh b/src/aspnetcore/activate.sh index 4986e1b297f5..8108b9849db2 100755 --- a/src/aspnetcore/activate.sh +++ b/src/aspnetcore/activate.sh @@ -53,7 +53,6 @@ deactivate () { fi unset DOTNET_ROOT - unset DOTNET_MULTILEVEL_LOOKUP if [ ! "${1:-}" = "init" ] ; then # Remove the deactivate function unset -f deactivate @@ -67,8 +66,6 @@ DIR="$( cd "$( dirname "$THIS_SCRIPT" )" && pwd )" _OLD_PATH="$PATH" # Tell dotnet where to find itself export DOTNET_ROOT="$DIR/.dotnet" -# Tell dotnet not to look beyond the DOTNET_ROOT folder for more dotnet things -export DOTNET_MULTILEVEL_LOOKUP=0 # Put dotnet first on PATH export PATH="$DOTNET_ROOT:$PATH" diff --git a/src/aspnetcore/docs/BuildFromSource.md b/src/aspnetcore/docs/BuildFromSource.md index b4f15930027a..027388773422 100644 --- a/src/aspnetcore/docs/BuildFromSource.md +++ b/src/aspnetcore/docs/BuildFromSource.md @@ -214,7 +214,7 @@ While it's typically better to use the project-specific build scripts, the repo- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | | `.\eng\build.cmd -all -pack -arch x64` | Build development packages for all the shipping projects in the repo. Must be run from the root of the repo. | | `.\eng\build.cmd -test -projects .\src\Framework\test\Microsoft.AspNetCore.App.UnitTests.csproj` | Run all the unit tests in the `Microsoft.AspNetCore.App.UnitTests` project. | -| `.\eng\build.cmd -noBuildNative -noBuildManage` | Builds the repo and skips native and managed projects, a quicker alternative to `./restore.cmd`. Must be run from the root of the repo. | +| `.\eng\build.cmd -noBuildNative -noBuildManaged` | Builds the repo and skips native and managed projects, a quicker alternative to `./restore.cmd`. Must be run from the root of the repo. | ## Complete list of repo dependencies diff --git a/src/aspnetcore/docs/HelpWantedProcess.md b/src/aspnetcore/docs/HelpWantedProcess.md deleted file mode 100644 index a71209be959b..000000000000 --- a/src/aspnetcore/docs/HelpWantedProcess.md +++ /dev/null @@ -1,23 +0,0 @@ -# Help Wanted Process - -This document describes the process that the team uses for identifying candidate issues for community members to tackle. - -## Process - -This process (we'll refer to it as `help-wanted` process going forward) triggers when the engineering team decides that a particular issue may be a good fit for a community member to tackle. Below are the stages that issue will go through, as part of this process: -1. The triage team will **assign the `help candidate` label** to the issue, as well as **assign an engineer to the issue**. -2. The task for the assigned engineer will be to understand the ask based on all the comments in the issue and **summarize the problem in a comment** using the [Help Wanted Template](/docs/HelpWantedIssueSummaryCommentTemplate.md). -3. As part of filling out the template, the engineer will also need to **describe a high-level design for how to approach the problem and how it can be solved**. -4. When all the sections of the template are filled in, the engineer will then need to **apply one of the `Complexity: ` labels**, to indicate how easy / hard will it be to tackle the issue. - This will help community members to find the right type of issue to contribute to. -5. After posting this comment the assigned engineer should: - - Unassign themself from the issue. - - **Replace the `help candidate` label with `help wanted` label**, as an indicator, that the issue is ready for the community to pick up. - - Copy the direct link to the summary comment and add the link to the bottom of the description of the issue to help with discoverability, as there can be too many comments in the issue and the summary comment may be hard to find. - Here is an example comment to use: - - ```text - **Summary Comment** : https://github.com/dotnet/aspnetcore/issues/51912#issuecomment-1801246403 - ``` - -This will conclude the process. diff --git a/src/aspnetcore/docs/PreparingPatchUpdates.md b/src/aspnetcore/docs/PreparingPatchUpdates.md index 5b069cb4b04d..5716ac2f3bdd 100644 --- a/src/aspnetcore/docs/PreparingPatchUpdates.md +++ b/src/aspnetcore/docs/PreparingPatchUpdates.md @@ -9,6 +9,3 @@ In order to prepare this repo to build a new servicing update, the following cha - 7 + 8 ``` - -* Update the package baselines. This is used to ensure packages keep a consistent set of dependencies between releases. - See [eng/tools/BaselineGenerator/](/eng/tools/BaselineGenerator/README.md) for instructions on how to run this tool. diff --git a/src/aspnetcore/docs/ReferenceResolution.md b/src/aspnetcore/docs/ReferenceResolution.md index dd9bc10124e8..c33b8c852e31 100644 --- a/src/aspnetcore/docs/ReferenceResolution.md +++ b/src/aspnetcore/docs/ReferenceResolution.md @@ -12,7 +12,6 @@ The requirements that led to this system are: * Versions of external dependencies should be consistent and easily discovered. * Newer versions of packages should not have lower dependency versions than previous releases. -* Minimize the cascading effect of servicing updates where possible by keeping a consistent baseline of dependencies. * Servicing releases should not add or remove dependencies in existing packages. As a minor point, the current system also makes our project files somewhat less verbose. @@ -26,13 +25,9 @@ As a minor point, the current system also makes our project files somewhat less * Only use `` in test projects. * Name the .csproj file to match the assembly name. * Run `eng/scripts/GenerateProjectList.ps1` (or `build.cmd /t:GenerateProjectList`) when adding new projects -* Use [eng/tools/BaseLineGenerator/](/eng/tools/BaselineGenerator/README.md) if you need to update baselines. -* If you need to make a breaking change to dependencies, you may need to add ``. ## Important files -* [eng/Baseline.xml](/eng/Baseline.xml) - this contains the 'baseline' of the latest servicing release for this branch. - It should be modified and used to update the generated file, [eng/Baseline.Designer.props](eng/Baseline.Designer.props). * [eng/Dependencies.props](/eng/Dependencies.props) - contains a list of all package references that might be used in the repo. * [eng/ProjectReferences.props](/eng/ProjectReferences.props) - lists which assemblies or packages might be available to be referenced as a local project. * [eng/Versions.props](/eng/Versions.props) - contains a list of versions which may be updated by automation. This is used by MSBuild to restore and build. @@ -86,20 +81,6 @@ Steps for adding a new package dependency to an existing project. Let's say I'm The attribute value should be `"Microsoft.CodeAnalysis.Razor"` for dotnet/runtime dependencies in dotnet/aspnetcore-tooling. -## Example: make a breaking change to references - -If Microsoft.AspNetCore.Banana in 2.1 had a reference to `Microsoft.AspNetCore.Orange`, but in 3.1 or 5.0 this reference -is changing to `Microsoft.AspNetCore.BetterThanOrange`, you would need to make these changes to the .csproj file - -```diff - - -- -+ -+ - -``` - ## A darc cheatsheet `darc` is a command-line tool that is used for dependency management in the dotnet ecosystem of repos. `darc` can be installed using the `darc-init` scripts located inside the `eng/common` directory. Once `darc` is installed, you'll need to set up the appropriate access tokens as outlined [in the official Darc docs](https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#setting-up-your-darc-client). diff --git a/src/aspnetcore/docs/UpdatingMajorVersionAndTFM.md b/src/aspnetcore/docs/UpdatingMajorVersionAndTFM.md index 6fde9d8f98a8..76ed7a15346e 100644 --- a/src/aspnetcore/docs/UpdatingMajorVersionAndTFM.md +++ b/src/aspnetcore/docs/UpdatingMajorVersionAndTFM.md @@ -14,14 +14,13 @@ Typically, we will update the Major Version before updating the TFM. This is bec 1. Increment `AspNetCoreMajorVersion` by 1. 2. Change `PreReleaseVersionIteration` to `1`. 3. Change `PreReleaseVersionLabel` to `alpha`. - 4. Change `PreReleaseBrandingLabel` to `Alpha $(PreReleaseVersionIteration)`. + Note: `PreReleaseBrandingLabel` is automatically calculated based on `PreReleaseVersionLabel` and does not need to be manually updated. * Add entries to [NuGet.config](/NuGet.config) for the new Major Version's feed. This just means copying the current feeds (e.g. `dotnet8` and `dotnet8-transport`) and adding entries for the new feeds (`dotnet9` and `dotnet9-transport`). Make an effort to remove old feeds here at the same time. * In [src/ProjectTemplates/Shared/TemplatePackageInstaller.cs](/src/ProjectTemplates/Shared/TemplatePackageInstaller.cs), add an entry to `_templatePackages` for `Microsoft.DotNet.Web.ProjectTemplates` matching the new version. * In [eng/targets/CSharp.Common.props](/eng/targets/CSharp.Common.props) for the previous release branch, modify the `` to be a hardcoded version instead of `preview`. (e.g. If main is being updated to 8.0.0 modify the `` in the release/7.0 branch). See https://learn.microsoft.com/dotnet/csharp/language-reference/configure-language-version#defaults to find what language version to use. * Mark APIs from the previous release as Shipped by running `.\eng\scripts\mark-shipped.cmd`. **Note that it's best to do this as early as possible after the API surface is finalized from the previous release** - make sure to be careful that any new API in `main` that isn't shipped as part of the previous release, stays in `PublicAPI.Unshipped.txt` files. * One way to ensure this is to check out the release branch shipping the previous release (**after API surface area has been finalized**), run `.\eng\scripts\mark-shipped.cmd` there, copy over all of the `PublicAPI.Unshipped.txt` and `PublicAPI.Shipped.txt` files into a new branch based off of `main`, and build the repo. Any failures there will tell you whether or not there are new APIs in main that need to be put back into the `PublicAPI.Unshipped.txt` files. * The result of `.\eng\scripts\mark-shipped.cmd` should be checked in to the release branch as well, as part of the RTM release. -* Update `.\eng\Baseline.xml` to reflect the set of RTM packages that were just shipped. Then, `dotnet run` `.\eng\tools\BaselineGenerator\BaselineGenerator.csproj`, which will update `.\eng\Baseline.Designer.props`. If RTM hasn't shipped yet, do this in a separate PR once it has. See https://github.com/dotnet/aspnetcore/pull/49269. * **In the new release branch**, add files named `.\eng\PlatformManifest.txt` and `.\eng\PackageOverrides.txt`. These files should be found by downloading the just released RTM version of the `Microsoft.AspNetCore.App.Ref` package, and copying over the files from the `data` folder. * Update [helix-matrix.yml](https://github.com/dotnet/aspnetcore/blob/436556163a671259c8b14ae1c90d72767af62d18/.azure/pipelines/helix-matrix.yml#L12-L16) to list the currently active release branches. * This should be done in `main` as well as the relevant release branch. diff --git a/src/aspnetcore/docs/area-owners.md b/src/aspnetcore/docs/area-owners.md index 618af640082d..cc1d36f0bd0b 100644 --- a/src/aspnetcore/docs/area-owners.md +++ b/src/aspnetcore/docs/area-owners.md @@ -2,35 +2,35 @@ The below table lists all the `area-`labels used in the **dotnet/aspnetcore** repository and their owners. -| area label | Owners | Doc Owners | Description | -| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **[area-auth](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-auth)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) [@tdykstra](https://github.com/tdykstra) | Authn, Authz, OAuth, OIDC, Bearer | -| **[area-blazor](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-blazor)** | [@lewing](https://github.com/lewing) [@danroth27](https://github.com/danroth27) [@aspnet-blazor-eng](https://github.com/aspnet-blazor-eng) | [@guardrex](https://github.com/guardrex) | Blazor, Razor Components (WASM issues may be moved to dotnet/runtime repo) | -| **[area-commandlinetools](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-commandlinetools)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@captainsafia](https://github.com/captainsafia) | [@wadepickett](https://github.com/wadepickett) | Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPI | +| area label | Owners | Doc Owners | Description | +| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **[area-auth](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-auth)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) [@tdykstra](https://github.com/tdykstra) | Authn, Authz, OAuth, OIDC, Bearer | +| **[area-blazor](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-blazor)** | [@lewing](https://github.com/lewing) [@danroth27](https://github.com/danroth27) [@aspnet-blazor-eng](https://github.com/aspnet-blazor-eng) | [@guardrex](https://github.com/guardrex) | Blazor, Razor Components (WASM issues may be moved to dotnet/runtime repo) | +| **[area-commandlinetools](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-commandlinetools)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@captainsafia](https://github.com/captainsafia) | [@wadepickett](https://github.com/wadepickett) | Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPI | | **[area-dataprotection](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-dataprotection)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@DeagleGross](https://github.com/DeagleGross) | [@tdykstra](https://github.com/tdykstra) | DataProtection | -| **[area-grpc](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-grpc)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@jamesnk](https://github.com/jamesnk) [@mgravell](https://github.com/mgravell) | [@wadepickett](https://github.com/wadepickett) | GRPC wire-up, templates (library itself is  https://github.com/grpc/grpc-dotnet) | -| **[area-healthchecks](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-healthchecks)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@samsp-msft](https://github.com/samsp-msft) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | Healthchecks (some bugs also in Extensions repo) | +| **[area-grpc](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-grpc)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@jamesnk](https://github.com/jamesnk) | [@wadepickett](https://github.com/wadepickett) | GRPC wire-up, templates (library itself is  https://github.com/grpc/grpc-dotnet) | +| **[area-healthchecks](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-healthchecks)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | Healthchecks (some bugs also in Extensions repo) | | **[area-hosting](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-hosting)** | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) [@halter73](https://github.com/halter73) | [@tdykstra](https://github.com/tdykstra) | Includes Hosting | -| **[area-identity](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-identity)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@tdykstra](https://github.com/tdykstra) [@wadepickett](https://github.com/wadepickett) | Identity and providers | +| **[area-identity](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-identity)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@tdykstra](https://github.com/tdykstra) [@wadepickett](https://github.com/wadepickett) | Identity and providers | | **[area-infrastructure](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-infrastructure)** | [@joperezr](https://github.com/joperezr) [@wtgodbe](https://github.com/wtgodbe) | [@tdykstra](https://github.com/tdykstra) | MSBuild projects/targets, build scripts, CI, Installers and shared framework | -| **[area-middleware](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-middleware)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@samsp-msft](https://github.com/samsp-msft) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | URL rewrite, redirect, response cache/compression, session, caching, and other general middlewares | -| **[area-minimal](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-minimal)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@captainsafia](https://github.com/captainsafia) [@mitchdenny](https://github.com/mitchdenny) | [@wadepickett](https://github.com/wadepickett) | Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc | +| **[area-middleware](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-middleware)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | URL rewrite, redirect, response cache/compression, session, caching, and other general middlewares | +| **[area-minimal](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-minimal)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@captainsafia](https://github.com/captainsafia) [@mitchdenny](https://github.com/mitchdenny) | [@wadepickett](https://github.com/wadepickett) | Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc | | **[area-mvc](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-mvc)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@captainsafia](https://github.com/captainsafia) | [@tdykstra](https://github.com/tdykstra) | MVC, Actions and Controllers, Localization, CORS, most templates | -| **[area-networking](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-networking)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@samsp-msft](https://github.com/samsp-msft) [@brennanconroy](https://github.com/brennanconroy) [@DeagleGross](https://github.com/DeagleGross) | [@tdykstra](https://github.com/tdykstra) | Includes Kestrel/servers, protocols such as HTTP/2, HTTP3, YARP, jsonpatch, bedrock, websockets, http client factory, http abstractions, networking aspects of caching | +| **[area-networking](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-networking)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@brennanconroy](https://github.com/brennanconroy) [@DeagleGross](https://github.com/DeagleGross) | [@tdykstra](https://github.com/tdykstra) | Includes Kestrel/servers, protocols such as HTTP/2, HTTP3, YARP, jsonpatch, bedrock, websockets, http client factory, http abstractions, networking aspects of caching | | **[area-perf](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-perf)** | [@joperezr](https://github.com/joperezr) [@sebastienros](https://github.com/sebastienros) | [@tdykstra](https://github.com/tdykstra) | Autofiled performance bugs, perf infra. | | **[area-routing](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-routing)** | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) [@halter73](https://github.com/halter73) [@JamesNK](https://github.com/JamesNK) ([@javiercn](https://github.com/javiercn) consulting) | [@tdykstra](https://github.com/tdykstra) | Routing | -| **[area-security](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-security)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) [@tdykstra](https://github.com/tdykstra) | security features/work | -| **[area-signalr](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-signalr)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | SignalR clients and servers | -| **[area-ui-rendering](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-ui-rendering)** | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) | MVC Views/Pages, Razor Views/Page | +| **[area-security](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-security)** | [@joperezr](https://github.com/joperezr) [@mikekistler](https://github.com/mikekistler) [@halter73](https://github.com/halter73) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) [@tdykstra](https://github.com/tdykstra) | security features/work | +| **[area-signalr](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-signalr)** | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) [@brennanconroy](https://github.com/brennanconroy) | [@wadepickett](https://github.com/wadepickett) | SignalR clients and servers | +| **[area-ui-rendering](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-ui-rendering)** | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) [@mackinnonbuck](https://github.com/mackinnonbuck) | [@wadepickett](https://github.com/wadepickett) | MVC Views/Pages, Razor Views/Page | | **[area-unified-build](https://github.com/dotnet/aspnetcore/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-unified-build)** | [@joperezr](https://github.com/joperezr) [@wtgodbe](https://github.com/wtgodbe) | [@tdykstra](https://github.com/tdykstra) | Work related to https://github.com/dotnet/dotnet | -| other repo | Owners | Doc Owners | Description | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------- | +| other repo | Owners | Doc Owners | Description | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------- | | {aspire} | [@joperezr](https://github.com/joperezr) [@maddymontaquila](https://github.com/maddymontaquila) | [@IEvangelist](https://github.com/IEvangelist) | [https://github.com/dotnet/aspire](https://github.com/dotnet/aspire) | | {extensions} | [@joperezr](https://github.com/joperezr) [@russkie](https://github.com/russkie) | [@wadepickett](https://github.com/wadepickett) | [https://github.com/dotnet/extensions](https://github.com/dotnet/extensions) | | {orleans} | [@adityamandaleeka](https://github.com/adityamandaleeka) [@mikekistler](https://github.com/mikekistler) | [@IEvangelist](https://github.com/IEvangelist) | [https://github.com/dotnet/orleans](https://github.com/dotnet/orleans) | -| {systemwebadapters} | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) | [@tdykstra](https://github.com/tdykstra) | [https://github.com/dotnet/systemweb-adapters](https://github.com/dotnet/systemweb-adapters) | -| {yarp} | [@adityamandaleeka](https://github.com/adityamandaleeka) [@samsp-msft](https://github.com/samsp-msft) [@benjaminpetit](https://github.com/benjaminpetit) | [@wadepickett](https://github.com/wadepickett) | [https://github.com/dotnet/yarp](https://github.com/dotnet/yarp) | +| {systemwebadapters} | [@joperezr](https://github.com/joperezr) [@danroth27](https://github.com/danroth27) | [@tdykstra](https://github.com/tdykstra) | [https://github.com/dotnet/systemweb-adapters](https://github.com/dotnet/systemweb-adapters) | +| {yarp} | [@adityamandaleeka](https://github.com/adityamandaleeka) [@benjaminpetit](https://github.com/benjaminpetit) | [@wadepickett](https://github.com/wadepickett) | [https://github.com/dotnet/yarp](https://github.com/dotnet/yarp) | The **dotnet/runtime** repo has [its own list](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md). diff --git a/src/aspnetcore/eng/AfterSolutionBuild.targets b/src/aspnetcore/eng/AfterSolutionBuild.targets index c629174994f3..6a27ca63dade 100644 --- a/src/aspnetcore/eng/AfterSolutionBuild.targets +++ b/src/aspnetcore/eng/AfterSolutionBuild.targets @@ -1,21 +1,10 @@ - + - - - - - <_BuildOutput Include="$(ArtifactsShippingPackagesDir)*.nupkg" - Exclude="$(ArtifactsShippingPackagesDir)*.symbols.nupkg" /> - - - - - diff --git a/src/aspnetcore/eng/Baseline.Designer.props b/src/aspnetcore/eng/Baseline.Designer.props deleted file mode 100644 index f566358b9acb..000000000000 --- a/src/aspnetcore/eng/Baseline.Designer.props +++ /dev/null @@ -1,1002 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - - - - - 9.0.0 - - - - - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - 9.0.0 - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - - - - - - - - - - - - 9.0.0 - - - - 9.0.0 - - - - - - - 9.0.0 - - - - - \ No newline at end of file diff --git a/src/aspnetcore/eng/Baseline.xml b/src/aspnetcore/eng/Baseline.xml deleted file mode 100644 index 1b4ce55e8e33..000000000000 --- a/src/aspnetcore/eng/Baseline.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/aspnetcore/eng/Build.props b/src/aspnetcore/eng/Build.props index 33d09a7a1017..764f8847521d 100644 --- a/src/aspnetcore/eng/Build.props +++ b/src/aspnetcore/eng/Build.props @@ -162,7 +162,6 @@ $(RepoRoot)src\Framework\App.Ref\src\Microsoft.AspNetCore.App.Ref.sfxproj; $(RepoRoot)src\Framework\App.Ref.Internal\src\Microsoft.AspNetCore.App.Ref.Internal.csproj; $(RepoRoot)src\Framework\App.Runtime\src\aspnetcore-runtime.proj; - $(RepoRoot)src\Framework\App.Runtime\src\aspnetcore-runtime-composite.proj; $(RepoRoot)src\Framework\App.Runtime\src\aspnetcore-base-runtime.proj; $(RepoRoot)src\Framework\App.Runtime\bundle\aspnetcore-runtime-bundle.bundleproj; $(RepoRoot)eng\tools\HelixTestRunner\HelixTestRunner.csproj; @@ -175,6 +174,9 @@ $(RepoRoot)**\bin\**\*; $(RepoRoot)**\obj\**\*;" Condition=" '$(BuildMainlyReferenceProviders)' != 'true' " /> + + - diff --git a/src/aspnetcore/eng/ShippingAssemblies.props b/src/aspnetcore/eng/ShippingAssemblies.props index 0acec8474d50..6b969841ea81 100644 --- a/src/aspnetcore/eng/ShippingAssemblies.props +++ b/src/aspnetcore/eng/ShippingAssemblies.props @@ -132,7 +132,6 @@ - diff --git a/src/aspnetcore/eng/SignCheckExclusionsFile.txt b/src/aspnetcore/eng/SignCheckExclusionsFile.txt deleted file mode 100644 index a65b9f27f9b0..000000000000 --- a/src/aspnetcore/eng/SignCheckExclusionsFile.txt +++ /dev/null @@ -1,5 +0,0 @@ -*apphost.exe;; Exclude the apphost because this is expected to be code-signed by customers after the SDK modifies it. -*.binlog;; MSBuild binary logs are not signed though they are sometimes placed where validation thinks they should be. -*.js;; We do not sign JavaScript files. -*netfxca|*wixca|*wixdepca|*wixuiwixca;*.msi; We do not sign WiX content in our installers. -*wixstdba.dll;*.exe; diff --git a/src/aspnetcore/eng/Version.Details.props b/src/aspnetcore/eng/Version.Details.props index 845abb2fb5cb..1a28b716bd64 100644 --- a/src/aspnetcore/eng/Version.Details.props +++ b/src/aspnetcore/eng/Version.Details.props @@ -1,4 +1,3 @@ - - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-beta.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 3.2.0-preview.25414.101 - 7.0.0-preview.1.42001 - 7.0.0-preview.1.42001 - 7.0.0-preview.1.42001 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 - 10.0.0-rc.1.25414.101 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-beta.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 3.3.0-preview.26057.107 + 7.3.0-preview.1.5807 + 7.3.0-preview.1.5807 + 7.3.0-preview.1.5807 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 + 11.0.0-alpha.1.26057.107 4.13.0-3.24613.7 4.13.0-3.24613.7 4.13.0-3.24613.7 4.13.0-3.24613.7 - 9.9.0-preview.1.25409.1 - 9.9.0-preview.1.25409.1 - 9.9.0-preview.1.25409.1 + 10.3.0-preview.1.26058.2 + 10.3.0-preview.1.26058.2 + 10.3.0-preview.1.26058.2 - 1.0.0-prerelease.25405.1 - 1.0.0-prerelease.25405.1 - 1.0.0-prerelease.25405.1 - 1.0.0-prerelease.25405.1 - 1.0.0-prerelease.25405.1 + 1.0.0-prerelease.25502.1 + 1.0.0-prerelease.25502.1 + 1.0.0-prerelease.25502.1 + 1.0.0-prerelease.25502.1 + 1.0.0-prerelease.25502.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/src/aspnetcore/eng/Version.Details.xml b/src/aspnetcore/eng/Version.Details.xml index c1466860a359..0c285db6769e 100644 --- a/src/aspnetcore/eng/Version.Details.xml +++ b/src/aspnetcore/eng/Version.Details.xml @@ -8,333 +8,333 @@ See https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md for instructions on using darc. --> - + - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c @@ -358,100 +358,100 @@ - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/extensions - ed336d147b46d36edad3e9441398de636b67cf5d + 99383237b2b86d4d85b4f5908b938e9e43c97db7 - + https://github.com/dotnet/extensions - ed336d147b46d36edad3e9441398de636b67cf5d + 99383237b2b86d4d85b4f5908b938e9e43c97db7 - + https://github.com/dotnet/extensions - ed336d147b46d36edad3e9441398de636b67cf5d + 99383237b2b86d4d85b4f5908b938e9e43c97db7 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 48d2ef546dc00d3f3eeda4d083fda7aeaf66a546 + 71ce9774e9875270b80faaac1d6b60568a80e1fa - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 48d2ef546dc00d3f3eeda4d083fda7aeaf66a546 + 71ce9774e9875270b80faaac1d6b60568a80e1fa - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 48d2ef546dc00d3f3eeda4d083fda7aeaf66a546 + 71ce9774e9875270b80faaac1d6b60568a80e1fa - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 48d2ef546dc00d3f3eeda4d083fda7aeaf66a546 + 71ce9774e9875270b80faaac1d6b60568a80e1fa - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 48d2ef546dc00d3f3eeda4d083fda7aeaf66a546 + 71ce9774e9875270b80faaac1d6b60568a80e1fa - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + https://github.com/dotnet/dotnet - 992b076e9bc005baa97d3de57dccfa739b50f82e + 896160ec9eb0fdb1c019f698b39fa0923c9a316c - + \ No newline at end of file diff --git a/src/aspnetcore/eng/Versions.props b/src/aspnetcore/eng/Versions.props index d57f9aad228c..dea1ec205d7b 100644 --- a/src/aspnetcore/eng/Versions.props +++ b/src/aspnetcore/eng/Versions.props @@ -1,26 +1,29 @@ - + + + - 10 + 11 0 0 1 - true 8.0.1 *-* - - false - release - rc - RC $(PreReleaseVersionIteration) + alpha + + + + + Alpha $(PreReleaseVersionIteration) + Preview $(PreReleaseVersionIteration) + RC $(PreReleaseVersionIteration) + RTM + Servicing true false $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) @@ -50,12 +53,14 @@ new features specific to OOB --> net10.0 + true false + @@ -91,11 +96,6 @@ 1.2.0 1.2.6 - - 17.12.36 - 17.12.36 - 17.12.36 - 17.12.36 8.0.100-alpha.1.22607.1 - - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 4.13.0-3.24613.7 - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 - 4.13.0-3.24613.7 3.3.3 1.1.2 1.1.2 @@ -165,10 +156,10 @@ 2.64.0 2.64.0 2.5.187 - 3.0.0 - 3.0.0 - 3.0.0 - 3.0.0 + 3.14.1 + 3.14.1 + 3.14.1 + 3.14.1 0.3.46-beta $(MessagePackVersion) 4.10.0 @@ -178,11 +169,11 @@ 13.0.3 13.0.4 2.5.2 - 1.54.0 + 1.56.0 3.0.0 7.2.4 - 4.34.0 - 4.34.0 + 4.38.0 + 4.38.0 1.4.0 4.0.0 2.7.27 @@ -205,4 +196,5 @@ 1.10.93 1.2.3 + diff --git a/src/aspnetcore/eng/common/SetupNugetSources.ps1 b/src/aspnetcore/eng/common/SetupNugetSources.ps1 index 5db4ad71ee2f..fc8d618014e0 100644 --- a/src/aspnetcore/eng/common/SetupNugetSources.ps1 +++ b/src/aspnetcore/eng/common/SetupNugetSources.ps1 @@ -7,11 +7,11 @@ # 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: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 +# arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token # env: # Token: $(dn-bot-dnceng-artifact-feeds-rw) # @@ -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') +$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/src/aspnetcore/eng/common/SetupNugetSources.sh b/src/aspnetcore/eng/common/SetupNugetSources.sh index 4604b61b0323..b97cc536379d 100755 --- a/src/aspnetcore/eng/common/SetupNugetSources.sh +++ b/src/aspnetcore/eng/common/SetupNugetSources.sh @@ -11,8 +11,8 @@ # - task: Bash@3 # displayName: Setup Internal Feeds # inputs: -# filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh -# arguments: $(Build.SourcesDirectory)/NuGet.config +# filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.sh +# arguments: $(System.DefaultWorkingDirectory)/NuGet.config # condition: ne(variables['Agent.OS'], 'Windows_NT') # - task: NuGetAuthenticate@1 # @@ -52,78 +52,124 @@ 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 (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") + 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 -DotNetVersions=('5' '6' '7' '8' '9') +# 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 +185,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/aspnetcore/eng/common/build.sh b/src/aspnetcore/eng/common/build.sh index 9767bb411a4f..ec3e80d189ea 100755 --- a/src/aspnetcore/eng/common/build.sh +++ b/src/aspnetcore/eng/common/build.sh @@ -92,7 +92,7 @@ runtime_source_feed='' runtime_source_feed_key='' properties=() -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -help|-h) diff --git a/src/aspnetcore/eng/common/core-templates/job/job.yml b/src/aspnetcore/eng/common/core-templates/job/job.yml index d9013251542c..748c4f07a64d 100644 --- a/src/aspnetcore/eng/common/core-templates/job/job.yml +++ b/src/aspnetcore/eng/common/core-templates/job/job.yml @@ -19,6 +19,8 @@ parameters: # publishing defaults artifacts: '' enableMicrobuild: false + enablePreviewMicrobuild: false + microbuildPluginVersion: 'latest' enableMicrobuildForMacAndLinux: false microbuildUseESRP: true enablePublishBuildArtifacts: false @@ -71,6 +73,8 @@ jobs: templateContext: ${{ parameters.templateContext }} variables: + - name: AllowPtrToDetectTestRunRetryFiles + value: true - ${{ if ne(parameters.enableTelemetry, 'false') }}: - name: DOTNET_CLI_TELEMETRY_PROFILE value: '$(Build.Repository.Uri)' @@ -128,6 +132,8 @@ jobs: - template: /eng/common/core-templates/steps/install-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} microbuildUseESRP: ${{ parameters.microbuildUseESRP }} continueOnError: ${{ parameters.continueOnError }} @@ -153,6 +159,8 @@ jobs: - template: /eng/common/core-templates/steps/cleanup-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} continueOnError: ${{ parameters.continueOnError }} @@ -163,7 +171,7 @@ jobs: inputs: testResultsFormat: 'xUnit' testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -174,7 +182,7 @@ jobs: inputs: testResultsFormat: 'VSTest' testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + searchFolder: '$(System.DefaultWorkingDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true @@ -218,7 +226,7 @@ jobs: - task: CopyFiles@2 displayName: Gather buildconfiguration for build retry inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/common/BuildConfiguration' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' continueOnError: true diff --git a/src/aspnetcore/eng/common/core-templates/job/onelocbuild.yml b/src/aspnetcore/eng/common/core-templates/job/onelocbuild.yml index 8bf7d23355bc..c5788829a872 100644 --- a/src/aspnetcore/eng/common/core-templates/job/onelocbuild.yml +++ b/src/aspnetcore/eng/common/core-templates/job/onelocbuild.yml @@ -8,7 +8,7 @@ parameters: CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - SourcesDirectory: $(Build.SourcesDirectory) + SourcesDirectory: $(System.DefaultWorkingDirectory) CreatePr: true AutoCompletePr: false ReusePr: true @@ -68,7 +68,7 @@ jobs: - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - task: Powershell@2 inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/generate-locproject.ps1 arguments: $(_GenerateLocProjectArguments) displayName: Generate LocProject.json condition: ${{ parameters.condition }} @@ -103,7 +103,7 @@ jobs: - task: CopyFiles@2 displayName: Copy LocProject.json inputs: - SourceFolder: '$(Build.SourcesDirectory)/eng/Localize/' + SourceFolder: '$(System.DefaultWorkingDirectory)/eng/Localize/' Contents: 'LocProject.json' TargetFolder: '$(Build.ArtifactStagingDirectory)/loc' condition: ${{ parameters.condition }} diff --git a/src/aspnetcore/eng/common/core-templates/job/publish-build-assets.yml b/src/aspnetcore/eng/common/core-templates/job/publish-build-assets.yml index d5303229c97e..8b5c635fe807 100644 --- a/src/aspnetcore/eng/common/core-templates/job/publish-build-assets.yml +++ b/src/aspnetcore/eng/common/core-templates/job/publish-build-assets.yml @@ -38,6 +38,10 @@ parameters: # Optional: A minimatch pattern for the asset manifests to publish to BAR assetManifestsPattern: '*/manifests/**/*.xml' + repositoryAlias: self + + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -60,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) @@ -78,12 +87,12 @@ jobs: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - checkout: self + - checkout: ${{ parameters.repositoryAlias }} fetchDepth: 3 clean: true - - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: - - ${{ if eq(parameters.publishingVersion, 3) }}: + - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: + - ${{ if eq(parameters.publishingVersion, 3) }}: - task: DownloadPipelineArtifact@2 displayName: Download Asset Manifests inputs: @@ -108,24 +117,35 @@ jobs: flattenFolders: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - 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: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/sdk-task.ps1 arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) + -runtimeSourceFeed https://ci.dot.net/internal + -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' + condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: powershell@2 displayName: Create ReleaseConfigs Artifact inputs: @@ -137,7 +157,7 @@ jobs: Add-Content -Path $filePath -Value "$(DefaultChannels)" Add-Content -Path $filePath -Value $(IsStableBuild) - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + $symbolExclusionfile = "$(System.DefaultWorkingDirectory)/eng/SymbolPublishingExclusionsFile.txt" if (Test-Path -Path $symbolExclusionfile) { Write-Host "SymbolExclusionFile exists" @@ -153,7 +173,7 @@ jobs: artifactName: AssetManifests displayName: 'Publish Merged Manifest' retryCountOnTaskFailure: 10 # for any logs being locked - sbomEnabled: false # we don't need SBOM for logs + sbomEnabled: false # we don't need SBOM for logs - template: /eng/common/core-templates/steps/publish-build-artifacts.yml parameters: @@ -170,6 +190,11 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} 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 @@ -177,7 +202,7 @@ jobs: azureSubscription: "Darc: Maestro Production" scriptType: ps scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion 3 @@ -186,9 +211,11 @@ 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 parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} - JobLabel: 'Publish_Artifacts_Logs' + JobLabel: 'Publish_Artifacts_Logs' diff --git a/src/aspnetcore/eng/common/core-templates/job/source-build.yml b/src/aspnetcore/eng/common/core-templates/job/source-build.yml index d805d5faeb94..9d820f974211 100644 --- a/src/aspnetcore/eng/common/core-templates/job/source-build.yml +++ b/src/aspnetcore/eng/common/core-templates/job/source-build.yml @@ -60,10 +60,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.2004.amd64 + demands: ImageOverride -equals build.ubuntu.2204.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: 1es-azurelinux-3 os: linux ${{ else }}: pool: diff --git a/src/aspnetcore/eng/common/core-templates/job/source-index-stage1.yml b/src/aspnetcore/eng/common/core-templates/job/source-index-stage1.yml index 30530359a5d6..76baf5c27258 100644 --- a/src/aspnetcore/eng/common/core-templates/job/source-index-stage1.yml +++ b/src/aspnetcore/eng/common/core-templates/job/source-index-stage1.yml @@ -3,7 +3,7 @@ parameters: sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] binlogPath: artifacts/log/Debug/Build.binlog - condition: '' + condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') dependsOn: '' pool: '' is1ESPipeline: '' @@ -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, '') }}: @@ -41,4 +41,4 @@ jobs: - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml parameters: - binLogPath: ${{ parameters.binLogPath }} \ No newline at end of file + binLogPath: ${{ parameters.binLogPath }} diff --git a/src/aspnetcore/eng/common/core-templates/jobs/codeql-build.yml b/src/aspnetcore/eng/common/core-templates/jobs/codeql-build.yml index 693b00b37044..dbc14ac580a2 100644 --- a/src/aspnetcore/eng/common/core-templates/jobs/codeql-build.yml +++ b/src/aspnetcore/eng/common/core-templates/jobs/codeql-build.yml @@ -24,7 +24,7 @@ jobs: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config - name: GuardianVersion value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} diff --git a/src/aspnetcore/eng/common/core-templates/jobs/jobs.yml b/src/aspnetcore/eng/common/core-templates/jobs/jobs.yml index 2f992b2c6ecc..01ada7476651 100644 --- a/src/aspnetcore/eng/common/core-templates/jobs/jobs.yml +++ b/src/aspnetcore/eng/common/core-templates/jobs/jobs.yml @@ -43,6 +43,8 @@ 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. @@ -114,3 +116,5 @@ jobs: enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/src/aspnetcore/eng/common/core-templates/post-build/post-build.yml b/src/aspnetcore/eng/common/core-templates/post-build/post-build.yml index a151fd811e3e..06864cd1feb8 100644 --- a/src/aspnetcore/eng/common/core-templates/post-build/post-build.yml +++ b/src/aspnetcore/eng/common/core-templates/post-build/post-build.yml @@ -1,106 +1,106 @@ parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: requireDefaultChannels - displayName: Fail the build if there are no default channel(s) registrations for the current build - type: boolean - default: false - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - - name: isAssetlessBuild - type: boolean - displayName: Is Assetless Build - default: false - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - - - name: is1ESPipeline - type: boolean - default: false +# Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. +# Publishing V1 is no longer supported +# Publishing V2 is no longer supported +# Publishing V3 is the default +- name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + +- name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + +- name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + +- name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + +- name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + +- name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + +- name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + +- name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + +- name: requireDefaultChannels + displayName: Fail the build if there are no default channel(s) registrations for the current build + type: boolean + default: false + +- name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + +- name: isAssetlessBuild + type: boolean + displayName: Is Assetless Build + default: false + +# These parameters let the user customize the call to sdk-task.ps1 for publishing +# symbols & general artifacts as well as for signing validation +- name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + +- name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + +- name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + +# Which stages should finish execution before post-build stages start +- name: validateDependsOn + type: object + default: + - build + +- name: publishDependsOn + type: object + default: + - Validate + +# Optional: Call asset publishing rather than running in a separate stage +- name: publishAssetsImmediately + type: boolean + default: false + +- name: is1ESPipeline + type: boolean + default: false stages: - ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: @@ -108,10 +108,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Validate Build Assets variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: NuGet Validation @@ -127,35 +127,35 @@ 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 - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - job: displayName: Signing Validation @@ -169,54 +169,54 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - # 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 - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: /eng/common/core-templates/steps/publish-logs.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + # 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 + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) - job: displayName: SourceLink Validation @@ -230,41 +230,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 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: - BARBuildId: ${{ parameters.BARBuildId }} - 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 - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/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 + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + 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 + + - task: PowerShell@2 + displayName: Validate + 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 - ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - stage: publish_using_darc @@ -274,10 +274,10 @@ stages: dependsOn: ${{ parameters.validateDependsOn }} displayName: Publish using Darc variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: Publish Using Darc @@ -291,30 +291,41 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal image: windows.vs2019.amd64 os: windows ${{ else }}: name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2019.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - task: NuGetAuthenticate@1 - - - task: AzureCLI@2 - displayName: Publish Using Darc - inputs: - azureSubscription: "Darc: Maestro Production" - scriptType: ps - scriptLocation: scriptPath - scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: > + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - 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 + + - task: UseDotNet@2 + inputs: + version: 8.0.x + + - task: AzureCLI@2 + displayName: Publish Using Darc + inputs: + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(System.AccessToken)' @@ -323,3 +334,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/src/aspnetcore/eng/common/core-templates/post-build/setup-maestro-vars.yml b/src/aspnetcore/eng/common/core-templates/post-build/setup-maestro-vars.yml index f7602980dbe7..a7abd58c4bb6 100644 --- a/src/aspnetcore/eng/common/core-templates/post-build/setup-maestro-vars.yml +++ b/src/aspnetcore/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -36,7 +36,7 @@ steps: $AzureDevOpsBuildId = $Env:Build_BuildId } else { - . $(Build.SourcesDirectory)\eng\common\tools.ps1 + . $(System.DefaultWorkingDirectory)\eng\common\tools.ps1 $darc = Get-Darc $buildInfo = & $darc get-build ` --id ${{ parameters.BARBuildId }} ` diff --git a/src/aspnetcore/eng/common/core-templates/steps/enable-internal-sources.yml b/src/aspnetcore/eng/common/core-templates/steps/enable-internal-sources.yml index 64f881bffc3c..4085512b6909 100644 --- a/src/aspnetcore/eng/common/core-templates/steps/enable-internal-sources.yml +++ b/src/aspnetcore/eng/common/core-templates/steps/enable-internal-sources.yml @@ -17,8 +17,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $Env:Token env: Token: ${{ parameters.legacyCredential }} # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. @@ -29,8 +29,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config - ${{ else }}: - template: /eng/common/templates/steps/get-federated-access-token.yml parameters: @@ -39,8 +39,8 @@ steps: - task: PowerShell@2 displayName: Setup Internal Feeds inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(System.DefaultWorkingDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) # This is required in certain scenarios to install the ADO credential provider. # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others # (e.g. dotnet msbuild). diff --git a/src/aspnetcore/eng/common/core-templates/steps/generate-sbom.yml b/src/aspnetcore/eng/common/core-templates/steps/generate-sbom.yml index 44a9636cdff9..003f7eae0fa5 100644 --- a/src/aspnetcore/eng/common/core-templates/steps/generate-sbom.yml +++ b/src/aspnetcore/eng/common/core-templates/steps/generate-sbom.yml @@ -5,8 +5,8 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageVersion: 11.0.0 + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom IgnoreDirectories: '' diff --git a/src/aspnetcore/eng/common/core-templates/steps/install-microbuild-impl.yml b/src/aspnetcore/eng/common/core-templates/steps/install-microbuild-impl.yml new file mode 100644 index 000000000000..da22beb3f60c --- /dev/null +++ b/src/aspnetcore/eng/common/core-templates/steps/install-microbuild-impl.yml @@ -0,0 +1,34 @@ +parameters: + - name: microbuildTaskInputs + type: object + default: {} + + - name: microbuildEnv + type: object + default: {} + + - name: enablePreviewMicrobuild + type: boolean + default: false + + - name: condition + type: string + + - name: continueOnError + type: boolean + +steps: +- ${{ if eq(parameters.enablePreviewMicrobuild, true) }}: + - task: MicroBuildSigningPluginPreview@4 + displayName: Install Preview MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} +- ${{ else }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} diff --git a/src/aspnetcore/eng/common/core-templates/steps/install-microbuild.yml b/src/aspnetcore/eng/common/core-templates/steps/install-microbuild.yml index da30e67bc34c..4f4b56ed2a6b 100644 --- a/src/aspnetcore/eng/common/core-templates/steps/install-microbuild.yml +++ b/src/aspnetcore/eng/common/core-templates/steps/install-microbuild.yml @@ -4,6 +4,8 @@ parameters: # Enable install tasks for MicroBuild on Mac and Linux # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT' enableMicrobuildForMacAndLinux: false + # Enable preview version of MB signing plugin + enablePreviewMicrobuild: false # Determines whether the ESRP service connection information should be passed to the signing plugin. # This overlaps with _SignType to some degree. We only need the service connection for real signing. # It's important that the service connection not be passed to the MicroBuildSigningPlugin task in this place. @@ -11,8 +13,10 @@ 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 - microBuildOutputFolder: '$(Build.SourcesDirectory)' + # Microbuild installation directory + microBuildOutputFolder: $(Agent.TempDirectory)/MicroBuild + # Microbuild version + microbuildPluginVersion: 'latest' continueOnError: false @@ -25,8 +29,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: | @@ -46,35 +69,50 @@ steps: displayName: 'Validate ESRP usage (Non-Windows)' condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - ${{ if eq(parameters.microbuildUseESRP, true) }}: - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}: - azureSubscription: 'MicroBuild Signing Task (DevDiv)' - useEsrpCli: true - ${{ elseif eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea - ${{ else }}: - ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - continueOnError: ${{ parameters.continueOnError }} - condition: and( - succeeded(), - or( - and( - eq(variables['Agent.Os'], 'Windows_NT'), - in(variables['_SignType'], 'real', 'test') - ), - and( - ${{ eq(parameters.enableMicrobuildForMacAndLinux, true) }}, - ne(variables['Agent.Os'], 'Windows_NT'), - eq(variables['_SignType'], 'real') - ) - )) + # Two different MB install steps. This is due to not being able to use the agent OS during + # YAML expansion, and Windows vs. Linux/Mac uses different service connections. However, + # we can avoid including the MB install step if not enabled at all. This avoids a bunch of + # extra pipeline authorizations, since most pipelines do not sign on non-Windows. + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea + ${{ else }}: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca + microbuildEnv: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) + + - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml@self + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + workingDirectory: ${{ parameters.microBuildOutputFolder }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ${{ else }}: + ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc + microbuildEnv: + TeamName: $(_TeamName) + 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/aspnetcore/eng/common/core-templates/steps/publish-logs.yml b/src/aspnetcore/eng/common/core-templates/steps/publish-logs.yml index de24d0087c58..5a927b4c7bcb 100644 --- a/src/aspnetcore/eng/common/core-templates/steps/publish-logs.yml +++ b/src/aspnetcore/eng/common/core-templates/steps/publish-logs.yml @@ -12,22 +12,24 @@ steps: inputs: targetType: inline script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + New-Item -ItemType Directory $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(System.DefaultWorkingDirectory)/artifacts/log/Debug/* $(System.DefaultWorkingDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ continueOnError: true condition: always() - task: PowerShell@2 displayName: Redact Logs inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/redact-logs.ps1 # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml - # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # Sensitive data can as well be added to $(System.DefaultWorkingDirectory)/eng/BinlogSecretsRedactionFile.txt' # 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 '$(Build.SourcesDirectory)/PostBuildLogs' - -BinlogToolVersion ${{parameters.BinlogToolVersion}} - -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + arguments: -InputPath '$(System.DefaultWorkingDirectory)/PostBuildLogs' + -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)' @@ -44,7 +46,7 @@ steps: - task: CopyFiles@2 displayName: Gather post build logs inputs: - SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + SourceFolder: '$(System.DefaultWorkingDirectory)/PostBuildLogs' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' condition: always() diff --git a/src/aspnetcore/eng/common/core-templates/steps/source-index-stage1-publish.yml b/src/aspnetcore/eng/common/core-templates/steps/source-index-stage1-publish.yml index c2917c1efc1c..3ad83b8c3075 100644 --- a/src/aspnetcore/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/src/aspnetcore/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250425.2 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250515.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog @@ -14,13 +14,13 @@ steps: workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: "Source Index: Download netsourceindex Tools" # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) -- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output +- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(System.DefaultWorkingDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output displayName: "Source Index: Process Binlog into indexable sln" - ${{ if and(ne(parameters.runAsPublic, 'true'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: diff --git a/src/aspnetcore/eng/common/cross/build-rootfs.sh b/src/aspnetcore/eng/common/cross/build-rootfs.sh index 8abfb71f7275..9b7eede4e50f 100755 --- a/src/aspnetcore/eng/common/cross/build-rootfs.sh +++ b/src/aspnetcore/eng/common/cross/build-rootfs.sh @@ -72,7 +72,7 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.4-RELEASE" +__FreeBSDBase="13.5-RELEASE" __FreeBSDPkg="1.21.3" __FreeBSDABI="13" __FreeBSDPackages="libunwind" @@ -383,7 +383,7 @@ while :; do ;; freebsd14) __CodeName=freebsd - __FreeBSDBase="14.2-RELEASE" + __FreeBSDBase="14.3-RELEASE" __FreeBSDABI="14" __SkipUnmount=1 ;; diff --git a/src/aspnetcore/eng/common/darc-init.sh b/src/aspnetcore/eng/common/darc-init.sh index e889f439b8dc..9f5ad6b763b5 100755 --- a/src/aspnetcore/eng/common/darc-init.sh +++ b/src/aspnetcore/eng/common/darc-init.sh @@ -5,7 +5,7 @@ darcVersion='' versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2020-02-20' verbosity='minimal' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --darcversion) diff --git a/src/aspnetcore/eng/common/dotnet-install.sh b/src/aspnetcore/eng/common/dotnet-install.sh index 7b9d97e3bd4d..61f302bb6775 100755 --- a/src/aspnetcore/eng/common/dotnet-install.sh +++ b/src/aspnetcore/eng/common/dotnet-install.sh @@ -18,7 +18,7 @@ architecture='' runtime='dotnet' runtimeSourceFeed='' runtimeSourceFeedKey='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in -version|-v) diff --git a/src/aspnetcore/eng/common/dotnet.sh b/src/aspnetcore/eng/common/dotnet.sh index 2ef68235675f..f6d24871c1d4 100755 --- a/src/aspnetcore/eng/common/dotnet.sh +++ b/src/aspnetcore/eng/common/dotnet.sh @@ -19,7 +19,7 @@ source $scriptroot/tools.sh InitializeDotNetCli true # install # Invoke acquired SDK with args if they are provided -if [[ $# > 0 ]]; then +if [[ $# -gt 0 ]]; then __dotnetDir=${_InitializeDotNetCli} dotnetPath=${__dotnetDir}/dotnet ${dotnetPath} "$@" diff --git a/src/aspnetcore/eng/common/generate-locproject.ps1 b/src/aspnetcore/eng/common/generate-locproject.ps1 index 524aaa57f2b7..fa1cdc2b3007 100644 --- a/src/aspnetcore/eng/common/generate-locproject.ps1 +++ b/src/aspnetcore/eng/common/generate-locproject.ps1 @@ -33,15 +33,27 @@ $jsonTemplateFiles | ForEach-Object { $jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern +$wxlFilesV3 = @() +$wxlFilesV5 = @() $wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them if (-not $wxlFiles) { $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files if ($wxlEnFiles) { - $wxlFiles = @() - $wxlEnFiles | ForEach-Object { - $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" - $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru - } + $wxlFiles = @() + $wxlEnFiles | ForEach-Object { + $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)" + $content = Get-Content $_.FullName -Raw + + # Split files on schema to select different parser settings in the generated project. + if ($content -like "*http://wixtoolset.org/schemas/v4/wxl*") + { + $wxlFilesV5 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + elseif ($content -like "*http://schemas.microsoft.com/wix/2006/localization*") + { + $wxlFilesV3 += Copy-Item $_.FullName -Destination $destinationFile -PassThru + } + } } } @@ -114,7 +126,32 @@ $locJson = @{ CloneLanguageSet = "WiX_CloneLanguages" LssFiles = @( "wxl_loc.lss" ) LocItems = @( - $wxlFiles | ForEach-Object { + $wxlFilesV3 | ForEach-Object { + $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($_.FullName.Contains($exclusion)) { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnPath" + OutputPath = $outputPath + } + } + } + ) + }, + @{ + LanguageSet = $LanguageSet + CloneLanguageSet = "WiX_CloneLanguages" + LssFiles = @( "P210WxlSchemaV4.lss" ) + LocItems = @( + $wxlFilesV5 | ForEach-Object { $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" $continue = $true foreach ($exclusion in $exclusions.Exclusions) { diff --git a/src/aspnetcore/eng/common/generate-sbom-prep.sh b/src/aspnetcore/eng/common/generate-sbom-prep.sh old mode 100755 new mode 100644 diff --git a/src/aspnetcore/eng/common/internal-feed-operations.ps1 b/src/aspnetcore/eng/common/internal-feed-operations.ps1 index 92b77347d990..c282d3ae403a 100644 --- a/src/aspnetcore/eng/common/internal-feed-operations.ps1 +++ b/src/aspnetcore/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/src/aspnetcore/eng/common/internal-feed-operations.sh b/src/aspnetcore/eng/common/internal-feed-operations.sh index 9378223ba095..6299e7effd4c 100755 --- a/src/aspnetcore/eng/common/internal-feed-operations.sh +++ b/src/aspnetcore/eng/common/internal-feed-operations.sh @@ -100,7 +100,7 @@ operation='' authToken='' repoName='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --operation) diff --git a/src/aspnetcore/eng/common/native/install-dependencies.sh b/src/aspnetcore/eng/common/native/install-dependencies.sh index 477a44f335be..11f81cbd40d4 100755 --- a/src/aspnetcore/eng/common/native/install-dependencies.sh +++ b/src/aspnetcore/eng/common/native/install-dependencies.sh @@ -27,9 +27,11 @@ case "$os" in libssl-dev libkrb5-dev pigz cpio localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 - elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then + elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ] || [ "$ID" = "centos" ]; then pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)" $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio + elif [ "$ID" = "amzn" ]; then + dnf install -y cmake llvm lld lldb clang python libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio elif [ "$ID" = "alpine" ]; then apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio else diff --git a/src/aspnetcore/eng/common/post-build/nuget-verification.ps1 b/src/aspnetcore/eng/common/post-build/nuget-verification.ps1 index a365194a9389..eea88e653c91 100644 --- a/src/aspnetcore/eng/common/post-build/nuget-verification.ps1 +++ b/src/aspnetcore/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 @@ -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/src/aspnetcore/eng/common/post-build/publish-using-darc.ps1 b/src/aspnetcore/eng/common/post-build/publish-using-darc.ps1 index 1eda208a3bbf..48e55598bdd2 100644 --- a/src/aspnetcore/eng/common/post-build/publish-using-darc.ps1 +++ b/src/aspnetcore/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/src/aspnetcore/eng/common/post-build/redact-logs.ps1 b/src/aspnetcore/eng/common/post-build/redact-logs.ps1 index b7fc19591507..fc0218a013d1 100644 --- a/src/aspnetcore/eng/common/post-build/redact-logs.ps1 +++ b/src/aspnetcore/eng/common/post-build/redact-logs.ps1 @@ -7,7 +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 { diff --git a/src/aspnetcore/eng/common/sdk-task.ps1 b/src/aspnetcore/eng/common/sdk-task.ps1 index a9d2a2d26996..b64b66a6275b 100644 --- a/src/aspnetcore/eng/common/sdk-task.ps1 +++ b/src/aspnetcore/eng/common/sdk-task.ps1 @@ -7,13 +7,16 @@ Param( [switch] $restore, [switch] $prepareMachine, [switch][Alias('nobl')]$excludeCIBinaryLog, + [switch]$noWarnAsError, [switch] $help, + [string] $runtimeSourceFeed = '', + [string] $runtimeSourceFeedKey = '', [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) $ci = $true $binaryLog = if ($excludeCIBinaryLog) { $false } else { $true } -$warnAsError = $true +$warnAsError = if ($noWarnAsError) { $false } else { $true } . $PSScriptRoot\tools.ps1 @@ -67,7 +70,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.13.0" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "18.0.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/src/aspnetcore/eng/common/sdk-task.sh b/src/aspnetcore/eng/common/sdk-task.sh index 2f83adc0269f..3270f83fa9a7 100755 --- a/src/aspnetcore/eng/common/sdk-task.sh +++ b/src/aspnetcore/eng/common/sdk-task.sh @@ -10,6 +10,7 @@ show_usage() { echo "Advanced settings:" echo " --excludeCIBinarylog Don't output binary log (short: -nobl)" + echo " --noWarnAsError Do not warn as error" echo "" echo "Command line arguments not listed above are passed thru to msbuild." } @@ -52,6 +53,7 @@ exclude_ci_binary_log=false restore=false help=false properties='' +warnAsError=true while (($# > 0)); do lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" @@ -73,6 +75,10 @@ while (($# > 0)); do exclude_ci_binary_log=true shift 1 ;; + --noWarnAsError) + warnAsError=false + shift 1 + ;; --help) help=true shift 1 @@ -85,7 +91,6 @@ while (($# > 0)); do done ci=true -warnAsError=true if $help; then show_usage diff --git a/src/aspnetcore/eng/common/template-guidance.md b/src/aspnetcore/eng/common/template-guidance.md index 98bbc1ded0ba..4bf4cf41bd7c 100644 --- a/src/aspnetcore/eng/common/template-guidance.md +++ b/src/aspnetcore/eng/common/template-guidance.md @@ -50,7 +50,7 @@ extends: - task: CopyFiles@2 displayName: Gather build output inputs: - SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + SourceFolder: '$(System.DefaultWorkingDirectory)/artifacts/marvel' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' ``` diff --git a/src/aspnetcore/eng/common/templates-official/job/job.yml b/src/aspnetcore/eng/common/templates-official/job/job.yml index a8a943287458..92a0664f5647 100644 --- a/src/aspnetcore/eng/common/templates-official/job/job.yml +++ b/src/aspnetcore/eng/common/templates-official/job/job.yml @@ -3,7 +3,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml diff --git a/src/aspnetcore/eng/common/templates-official/variables/sdl-variables.yml b/src/aspnetcore/eng/common/templates-official/variables/sdl-variables.yml index dbdd66d4a4b3..f1311bbb1b33 100644 --- a/src/aspnetcore/eng/common/templates-official/variables/sdl-variables.yml +++ b/src/aspnetcore/eng/common/templates-official/variables/sdl-variables.yml @@ -4,4 +4,4 @@ variables: - name: DefaultGuardianVersion value: 0.109.0 - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file + value: $(System.DefaultWorkingDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/src/aspnetcore/eng/common/templates/job/job.yml b/src/aspnetcore/eng/common/templates/job/job.yml index 7cbf668c22bc..238fa0818f7b 100644 --- a/src/aspnetcore/eng/common/templates/job/job.yml +++ b/src/aspnetcore/eng/common/templates/job/job.yml @@ -6,7 +6,7 @@ parameters: enableSbom: true runAsPublic: false PackageVersion: 9.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' jobs: - template: /eng/common/core-templates/job/job.yml @@ -77,7 +77,7 @@ jobs: parameters: is1ESPipeline: false args: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + targetPath: '$(System.DefaultWorkingDirectory)\eng\common\BuildConfiguration' artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true diff --git a/src/aspnetcore/eng/common/tools.ps1 b/src/aspnetcore/eng/common/tools.ps1 index d4cfd9ccd806..f6bde2683794 100644 --- a/src/aspnetcore/eng/common/tools.ps1 +++ b/src/aspnetcore/eng/common/tools.ps1 @@ -157,9 +157,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { return $global:_DotNetInstallDir } - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - $env:DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we do not need all ASP.NET packages restored. $env:DOTNET_NOLOGO=1 @@ -225,7 +222,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build Write-PipelinePrependPath -Path $dotnetRoot - Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' return $global:_DotNetInstallDir = $dotnetRoot @@ -277,7 +273,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { Retry({ Write-Host "GET $uri" - Invoke-WebRequest $uri -OutFile $installScript + Invoke-WebRequest $uri -UseBasicParsing -OutFile $installScript }) } @@ -394,8 +390,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/17.13.0 - $defaultXCopyMSBuildVersion = '17.13.0' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/18.0.0 + $defaultXCopyMSBuildVersion = '18.0.0' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -510,7 +506,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)) { @@ -544,7 +540,8 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') { $vswhereVersion = $GlobalJson.tools.vswhere } else { - $vswhereVersion = '2.5.2' + # keep this in sync with the VSWhereVersion in DefaultVersions.props + $vswhereVersion = '3.1.7' } $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion" @@ -552,25 +549,33 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (!(Test-Path $vsWhereExe)) { Create-Directory $vsWhereDir - Write-Host 'Downloading vswhere' + 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 }) } - 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 diff --git a/src/aspnetcore/eng/common/tools.sh b/src/aspnetcore/eng/common/tools.sh index c1841c9dfd0f..6c121300ac7d 100755 --- a/src/aspnetcore/eng/common/tools.sh +++ b/src/aspnetcore/eng/common/tools.sh @@ -115,9 +115,6 @@ function InitializeDotNetCli { local install=$1 - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - export DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we want to control all package sources export DOTNET_NOLOGO=1 @@ -166,7 +163,6 @@ function InitializeDotNetCli { # build steps from using anything other than what we've downloaded. Write-PipelinePrependPath -path "$dotnet_root" - Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" # return value diff --git a/src/aspnetcore/eng/helix/content/InstallJdk.ps1 b/src/aspnetcore/eng/helix/content/InstallJdk.ps1 index ac47e2005cd2..36d57d31b909 100644 --- a/src/aspnetcore/eng/helix/content/InstallJdk.ps1 +++ b/src/aspnetcore/eng/helix/content/InstallJdk.ps1 @@ -45,7 +45,7 @@ Remove-Item -Force -Recurse $tempDir -ErrorAction Ignore | out-null mkdir $tempDir -ea Ignore | out-null mkdir $installDir -ea Ignore | out-null Write-Host "Starting download of JDK ${JdkVersion}" -& $PSScriptRoot\Download.ps1 "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/java/jdk-${JdkVersion}_windows-x64_bin.zip" $tempDir/jdk.zip +& $PSScriptRoot\Download.ps1 "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/java/microsoft-jdk-${JdkVersion}-windows-x64.zip" $tempDir/jdk.zip Write-Host "Done downloading JDK ${JdkVersion}" Add-Type -assembly "System.IO.Compression.FileSystem" @@ -53,7 +53,8 @@ Add-Type -assembly "System.IO.Compression.FileSystem" Write-Host "Expanded JDK to $tempDir" Write-Host "Installing JDK to $installDir" -Move-Item "$tempDir/jdk/jdk-${JdkVersion}/*" $installDir +# The name of the file directory within the zip is based on the version, but may contain a +N for build number. +Move-Item "$(Get-ChildItem -Path "$tempDir/jdk" | Select-Object -First 1 -ExpandProperty FullName)/*" $installDir Write-Host "Done installing JDK to $installDir" Remove-Item -Force -Recurse $tempDir -ErrorAction Ignore | out-null diff --git a/src/aspnetcore/eng/helix/content/installjdk.sh b/src/aspnetcore/eng/helix/content/installjdk.sh index ad1794322efe..28780ab315d0 100755 --- a/src/aspnetcore/eng/helix/content/installjdk.sh +++ b/src/aspnetcore/eng/helix/content/installjdk.sh @@ -22,7 +22,7 @@ fi echo "PlatformArch: $platformarch" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" output_dir="$DIR/java" -url="https://netcorenativeassets.blob.core.windows.net/resource-packages/external/linux/java/jdk-${java_version}_${platformarch}_bin.tar.gz" +url="https://netcorenativeassets.blob.core.windows.net/resource-packages/external/linux/java/microsoft-jdk-${java_version}-${platformarch}.tar.gz" echo "Downloading from: $url" tmp="$(mktemp -d -t install-jdk.XXXXXX)" @@ -41,6 +41,6 @@ curl -Lsfo $(basename $url) "$url" --retry 5 echo "Installing java from $(basename $url) $url" mkdir $output_dir echo "Unpacking to $output_dir" -tar --strip-components 1 -xzf "jdk-${java_version}_${platformarch}_bin.tar.gz" --no-same-owner --directory "$output_dir" +tar --strip-components 1 -xzf "microsoft-jdk-${java_version}-${platformarch}.tar.gz" --no-same-owner --directory "$output_dir" popd diff --git a/src/aspnetcore/eng/helix/content/runtests.cmd b/src/aspnetcore/eng/helix/content/runtests.cmd index 8fd9e85596ce..6dc33efc3011 100644 --- a/src/aspnetcore/eng/helix/content/runtests.cmd +++ b/src/aspnetcore/eng/helix/content/runtests.cmd @@ -12,7 +12,6 @@ set $installPlaywright=%7 REM Batch only supports up to 9 arguments using the %# syntax, need to shift to get more set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -set DOTNET_MULTILEVEL_LOOKUP=0 set PLAYWRIGHT_BROWSERS_PATH=%CD%\ms-playwright REM Avoid https://github.com/dotnet/aspnetcore/issues/41937 in current session. @@ -22,12 +21,9 @@ set "PATH=%HELIX_WORKITEM_ROOT%;%PATH%;%HELIX_WORKITEM_ROOT%\node\bin" echo Set path to: "%PATH%" echo. -set exit_code=0 - echo "Running tests: dotnet %HELIX_CORRELATION_PAYLOAD%/HelixTestRunner/HelixTestRunner.dll --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --helixTimeout %$helixTimeout% --playwright %$installPlaywright%" dotnet %HELIX_CORRELATION_PAYLOAD%/HelixTestRunner/HelixTestRunner.dll --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --helixTimeout %$helixTimeout% --playwright %$installPlaywright% -if not errorlevel 0 ( - set exit_code=%errorlevel% -) +set exit_code=%errorlevel% + echo "Finished running tests: exit_code=%exit_code%" EXIT /b %exit_code% diff --git a/src/aspnetcore/eng/helix/content/runtests.sh b/src/aspnetcore/eng/helix/content/runtests.sh index ba9ce79418ef..db9c42884862 100644 --- a/src/aspnetcore/eng/helix/content/runtests.sh +++ b/src/aspnetcore/eng/helix/content/runtests.sh @@ -9,8 +9,6 @@ YELLOW="\033[0;33m" MAGENTA="\033[0;95m" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -# Prevent fallback to global .NET locations. This ensures our tests use the shared frameworks we specify and don't rollforward to something else that might be installed on the machine -export DOTNET_MULTILEVEL_LOOKUP=0 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 # Avoid https://github.com/dotnet/aspnetcore/issues/41937 in current session. diff --git a/src/aspnetcore/eng/scripts/CodeCheck.ps1 b/src/aspnetcore/eng/scripts/CodeCheck.ps1 index 90e04810f19f..f3ef5d320f6a 100644 --- a/src/aspnetcore/eng/scripts/CodeCheck.ps1 +++ b/src/aspnetcore/eng/scripts/CodeCheck.ps1 @@ -149,11 +149,6 @@ try { & $PSScriptRoot\GenerateProjectList.ps1 -ci:$ci } - Write-Host " Re-generating package baselines" - Invoke-Block { - & dotnet run --project "$repoRoot/eng/tools/BaselineGenerator/" - } - Write-Host "Running git diff to check for pending changes" # Redirect stderr to stdout because PowerShell does not consistently handle output to stderr @@ -213,6 +208,25 @@ try { LogError $file } } + + # Check for relevant changes to SignalR typescript files + $tsChanges = $changedFilesFromTarget | Where-Object { $_ -like "src/SignalR/clients/ts/*" -and $_ -ne "src/SignalR/clients/ts/CHANGELOG.md" } + $changelogChanged = $changedFilesFromTarget -contains "src/SignalR/clients/ts/CHANGELOG.md" + $signalrChangelogOverrideMarker = "[no changelog]" + + # Only enforce changelog rule if there are relevant TS changes + if ($tsChanges.Count -gt 0 -and -not $changelogChanged) { + # Check if the override marker exists in recent commit messages + $hasOverride = git log origin/$targetBranch..HEAD --pretty=%B | Select-String -Pattern $signalrChangelogOverrideMarker -Quiet + + if (-not $hasOverride) { + LogError "Changes were made to 'src/SignalR/clients/ts/', but no update to 'CHANGELOG.md' was found." + LogError "Either update 'src/SignalR/clients/ts/CHANGELOG.md' or include '$signalrChangelogOverrideMarker' in your commit message." + exit 1 + } else { + Write-Host "SignalR Changelog update skipped due to override marker in commit message." + } + } } } finally { diff --git a/src/aspnetcore/eng/scripts/InstallJdk.ps1 b/src/aspnetcore/eng/scripts/InstallJdk.ps1 index 35dc6c8d9e0f..f601797e21c9 100644 --- a/src/aspnetcore/eng/scripts/InstallJdk.ps1 +++ b/src/aspnetcore/eng/scripts/InstallJdk.ps1 @@ -22,7 +22,7 @@ $installDir = "$repoRoot\.tools\jdk\win-x64\" $javacExe = "$installDir\bin\javac.exe" $tempDir = "$repoRoot\obj" if (-not $JdkVersion) { - $JdkVersion = "11.0.24" + $JdkVersion = "21.0.5" } if (Test-Path $javacExe) { diff --git a/src/aspnetcore/eng/scripts/InstallVisualStudio.ps1 b/src/aspnetcore/eng/scripts/InstallVisualStudio.ps1 index 164e86a7918f..bed4e987491d 100644 --- a/src/aspnetcore/eng/scripts/InstallVisualStudio.ps1 +++ b/src/aspnetcore/eng/scripts/InstallVisualStudio.ps1 @@ -11,11 +11,12 @@ Enterprise .PARAMETER Channel Selects which channel of Visual Studio to install. Must be one of these values: - Release (the default) - Preview + Release/Stable (the default) + Preview/Insiders .PARAMETER Version Selects which version of Visual Studio to install. Must be one of these values: 2022 + 2026 .PARAMETER InstallPath The location on disk where Visual Studio should be installed or updated. Default path is location of latest existing installation of the specified edition, if any. If that VS edition is not currently installed, default @@ -35,9 +36,9 @@ param( [ValidateSet('BuildTools','Community', 'Professional', 'Enterprise')] [string]$Edition = 'Community', - [ValidateSet('Release', 'Preview', 'IntPreview', 'Dogfood')] + [ValidateSet('Release', 'Stable', 'Preview', 'Insiders', 'IntPreview', 'Dogfood')] [string]$Channel = 'Release', - [ValidateSet('2022')] + [ValidateSet('2022', '2026')] [string]$Version = '2022', [string]$InstallPath, [switch]$Passive, @@ -67,20 +68,42 @@ $ProgressPreference = 'SilentlyContinue' # Workaround PowerShell/PowerShell#2138 if ("$Version" -eq "2022") { $vsversion = 17; } -$channelUri = "https://aka.ms/vs/$vsversion/release" +elseif ("$Version" -eq "2026") { + $vsversion = 18; +} + +# Normalize channel names (Stable=Release, Insiders=Preview, Dogfood=IntPreview) +switch ($Channel) { + 'Stable' { $Channel = 'Release' } + 'Insiders' { $Channel = 'Preview' } + 'Dogfood' { $Channel = 'IntPreview' } +} + $responseFileName = "vs.$vsversion" if ("$Edition" -eq "BuildTools") { $responseFileName += ".buildtools" } -if ("$Channel" -eq "Dogfood") { - $Channel = "IntPreview" -} + +# Channel URIs differ between VS 2022 and VS 2026+ +# VS 2022: release, pre, intpreview +# VS 2026+: stable, insiders, intpreview (canary) if ("$Channel" -eq "Preview") { $responseFileName += ".preview" - $channelUri = "https://aka.ms/vs/$vsversion/pre" + if ($vsversion -ge 18) { + $channelUri = "https://aka.ms/vs/$vsversion/insiders" + } else { + $channelUri = "https://aka.ms/vs/$vsversion/pre" + } } elseif ("$Channel" -eq "IntPreview") { $responseFileName += ".intpreview" $channelUri = "https://aka.ms/vs/$vsversion/intpreview" +} else { + # Release channel + if ($vsversion -ge 18) { + $channelUri = "https://aka.ms/vs/$vsversion/stable" + } else { + $channelUri = "https://aka.ms/vs/$vsversion/release" + } } $responseFile = "$PSScriptRoot\$responseFileName.json" @@ -107,11 +130,15 @@ if (-not $InstallPath) { } if (-not $InstallPath) { - if ($vsversion -eq "17") { + if (($vsversion -eq 17) -or ($vsversion -eq 18)) { $pathPrefix = "${env:ProgramFiles}"; } if ("$Channel" -eq "Preview") { - $InstallPath = "$pathPrefix\Microsoft Visual Studio\$Version\${Edition}_Pre" + if ($vsversion -ge 18) { + $InstallPath = "$pathPrefix\Microsoft Visual Studio\$Version\${Edition}_Insiders" + } else { + $InstallPath = "$pathPrefix\Microsoft Visual Studio\$Version\${Edition}_Pre" + } } elseif ("$Channel" -eq "IntPreview") { $InstallPath = "$pathPrefix\Microsoft Visual Studio\$Version\${Edition}_IntPre" } else { diff --git a/src/aspnetcore/eng/scripts/install-nginx-linux.sh b/src/aspnetcore/eng/scripts/install-nginx-linux.sh deleted file mode 100755 index f075a899d1cf..000000000000 --- a/src/aspnetcore/eng/scripts/install-nginx-linux.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -scriptroot="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -reporoot="$(dirname "$(dirname "$scriptroot")")" -nginxinstall="$reporoot/.tools/nginx" - -curl -sSL http://nginx.org/download/nginx-1.26.3.tar.gz --retry 5 | tar zxfv - -C /tmp && cd /tmp/nginx-1.26.3/ -./configure --prefix=$nginxinstall --with-http_ssl_module --without-http_rewrite_module -make -make install -echo "##vso[task.prependpath]$nginxinstall/sbin" diff --git a/src/aspnetcore/eng/scripts/install-nginx-mac.sh b/src/aspnetcore/eng/scripts/install-nginx-mac.sh deleted file mode 100755 index e7df86f57c0a..000000000000 --- a/src/aspnetcore/eng/scripts/install-nginx-mac.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -brew update -brew list openssl || brew install openssl -brew list nginx || brew install nginx diff --git a/src/aspnetcore/eng/scripts/install-nginx.sh b/src/aspnetcore/eng/scripts/install-nginx.sh new file mode 100755 index 000000000000..23d71043ed19 --- /dev/null +++ b/src/aspnetcore/eng/scripts/install-nginx.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail + +scriptroot="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +reporoot="$(dirname "$(dirname "$scriptroot")")" +nginxinstall="$reporoot/.tools/nginx" + +curl -sSL http://nginx.org/download/nginx-1.29.1.tar.gz --retry 5 | tar zxfv - -C /tmp && cd /tmp/nginx-1.29.1/ +./configure --prefix=$nginxinstall --with-http_ssl_module --without-http_rewrite_module +make +make install +echo "##vso[task.prependpath]$nginxinstall/sbin" diff --git a/src/aspnetcore/eng/scripts/vs.17.buildtools.intpreview.json b/src/aspnetcore/eng/scripts/vs.17.buildtools.intpreview.json index 8a7c5ac445cb..5e27f5b52ab5 100644 --- a/src/aspnetcore/eng/scripts/vs.17.buildtools.intpreview.json +++ b/src/aspnetcore/eng/scripts/vs.17.buildtools.intpreview.json @@ -20,7 +20,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", "Microsoft.VisualStudio.Workload.MSBuildTools", "Microsoft.VisualStudio.Workload.NetCoreBuildTools", diff --git a/src/aspnetcore/eng/scripts/vs.17.buildtools.json b/src/aspnetcore/eng/scripts/vs.17.buildtools.json index 3a125c9d35eb..a00df84f9373 100644 --- a/src/aspnetcore/eng/scripts/vs.17.buildtools.json +++ b/src/aspnetcore/eng/scripts/vs.17.buildtools.json @@ -20,7 +20,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", "Microsoft.VisualStudio.Workload.MSBuildTools", "Microsoft.VisualStudio.Workload.NetCoreBuildTools", diff --git a/src/aspnetcore/eng/scripts/vs.17.buildtools.preview.json b/src/aspnetcore/eng/scripts/vs.17.buildtools.preview.json index 571a49978103..9b1c3ac6aa0d 100644 --- a/src/aspnetcore/eng/scripts/vs.17.buildtools.preview.json +++ b/src/aspnetcore/eng/scripts/vs.17.buildtools.preview.json @@ -20,7 +20,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", "Microsoft.VisualStudio.Workload.MSBuildTools", "Microsoft.VisualStudio.Workload.NetCoreBuildTools", diff --git a/src/aspnetcore/eng/scripts/vs.17.intpreview.json b/src/aspnetcore/eng/scripts/vs.17.intpreview.json index 67b7a1f8e90d..a4d195acc76b 100644 --- a/src/aspnetcore/eng/scripts/vs.17.intpreview.json +++ b/src/aspnetcore/eng/scripts/vs.17.intpreview.json @@ -17,7 +17,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Workload.NativeDesktop", "Microsoft.VisualStudio.Workload.NetWeb", diff --git a/src/aspnetcore/eng/scripts/vs.17.json b/src/aspnetcore/eng/scripts/vs.17.json index 86acfd28fdbe..ac50b0374bf5 100644 --- a/src/aspnetcore/eng/scripts/vs.17.json +++ b/src/aspnetcore/eng/scripts/vs.17.json @@ -17,7 +17,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Workload.NativeDesktop", "Microsoft.VisualStudio.Workload.NetWeb", diff --git a/src/aspnetcore/eng/scripts/vs.17.preview.json b/src/aspnetcore/eng/scripts/vs.17.preview.json index 39f8297f1f96..3cc22c12d5fb 100644 --- a/src/aspnetcore/eng/scripts/vs.17.preview.json +++ b/src/aspnetcore/eng/scripts/vs.17.preview.json @@ -17,7 +17,7 @@ "Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64", "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL", - "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Workload.NativeDesktop", "Microsoft.VisualStudio.Workload.NetWeb", diff --git a/src/aspnetcore/eng/scripts/vs.18.buildtools.intpreview.json b/src/aspnetcore/eng/scripts/vs.18.buildtools.intpreview.json new file mode 100644 index 000000000000..7c32d3cf404d --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.buildtools.intpreview.json @@ -0,0 +1,31 @@ +{ + "channelUri": "https://aka.ms/vs/18/intpreview/channel", + "channelId": "VisualStudio.18.IntPreview", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.FSharp.MSBuild", + "Microsoft.VisualStudio.Component.NuGet", + "Microsoft.VisualStudio.Component.NuGet.BuildTools", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", + "Microsoft.VisualStudio.Workload.MSBuildTools", + "Microsoft.VisualStudio.Workload.NetCoreBuildTools", + "Microsoft.VisualStudio.Workload.VCTools", + "Microsoft.VisualStudio.Workload.VisualStudioExtensionBuildTools", + "Microsoft.VisualStudio.Workload.WebBuildTools" + ] +} diff --git a/src/aspnetcore/eng/scripts/vs.18.buildtools.json b/src/aspnetcore/eng/scripts/vs.18.buildtools.json new file mode 100644 index 000000000000..4307d3705bc0 --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.buildtools.json @@ -0,0 +1,31 @@ +{ + "channelUri": "https://aka.ms/vs/18/stable/channel", + "channelId": "VisualStudio.18.Release", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.FSharp.MSBuild", + "Microsoft.VisualStudio.Component.NuGet", + "Microsoft.VisualStudio.Component.NuGet.BuildTools", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", + "Microsoft.VisualStudio.Workload.MSBuildTools", + "Microsoft.VisualStudio.Workload.NetCoreBuildTools", + "Microsoft.VisualStudio.Workload.VCTools", + "Microsoft.VisualStudio.Workload.VisualStudioExtensionBuildTools", + "Microsoft.VisualStudio.Workload.WebBuildTools" + ] +} diff --git a/src/aspnetcore/eng/scripts/vs.18.buildtools.preview.json b/src/aspnetcore/eng/scripts/vs.18.buildtools.preview.json new file mode 100644 index 000000000000..e43598698d36 --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.buildtools.preview.json @@ -0,0 +1,31 @@ +{ + "channelUri": "https://aka.ms/vs/18/insiders/channel", + "channelId": "VisualStudio.18.Preview", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.FSharp.MSBuild", + "Microsoft.VisualStudio.Component.NuGet", + "Microsoft.VisualStudio.Component.NuGet.BuildTools", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools", + "Microsoft.VisualStudio.Workload.MSBuildTools", + "Microsoft.VisualStudio.Workload.NetCoreBuildTools", + "Microsoft.VisualStudio.Workload.VCTools", + "Microsoft.VisualStudio.Workload.VisualStudioExtensionBuildTools", + "Microsoft.VisualStudio.Workload.WebBuildTools" + ] +} diff --git a/src/aspnetcore/eng/scripts/vs.18.intpreview.json b/src/aspnetcore/eng/scripts/vs.18.intpreview.json new file mode 100644 index 000000000000..2fd07fb719d5 --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.intpreview.json @@ -0,0 +1,26 @@ +{ + "channelUri": "https://aka.ms/vs/18/intpreview/channel", + "channelId": "VisualStudio.18.IntPreview", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.VisualStudioExtension" + ] +} diff --git a/src/aspnetcore/eng/scripts/vs.18.json b/src/aspnetcore/eng/scripts/vs.18.json new file mode 100644 index 000000000000..65393cf4a014 --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.json @@ -0,0 +1,26 @@ +{ + "channelUri": "https://aka.ms/vs/18/stable/channel", + "channelId": "VisualStudio.18.Release", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.VisualStudioExtension" + ] +} diff --git a/src/aspnetcore/eng/scripts/vs.18.preview.json b/src/aspnetcore/eng/scripts/vs.18.preview.json new file mode 100644 index 000000000000..ad23cf27b40a --- /dev/null +++ b/src/aspnetcore/eng/scripts/vs.18.preview.json @@ -0,0 +1,26 @@ +{ + "channelUri": "https://aka.ms/vs/18/insiders/channel", + "channelId": "VisualStudio.18.Preview", + "includeRecommended": false, + "addProductLang": [ + "en-US" + ], + "add": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.VisualStudioExtension" + ] +} diff --git a/src/aspnetcore/eng/targets/BeforeWix.Common.targets b/src/aspnetcore/eng/targets/BeforeWix.Common.targets new file mode 100644 index 000000000000..99c95b6f503d --- /dev/null +++ b/src/aspnetcore/eng/targets/BeforeWix.Common.targets @@ -0,0 +1,26 @@ + + + + yes + $(ProductName) + + $(DefineConstants);Debug + $(DefineConstants);EmbedCab=$(EmbedCab) + + + + $(OutputName.Replace('-', '_')).cab + $(OutputName.Replace('_win', '')).cab + + + + en-US + $(Culture) + $(Platform) + $(OutputPath) + + $(DefineConstants);Cabinet=$(Cabinet) + $(DefineConstants);BinPath=$(OutputPath)$(Culture)\ + $(WixVariables);$(DefineConstants) + + \ No newline at end of file diff --git a/src/aspnetcore/eng/targets/Cpp.Common.props b/src/aspnetcore/eng/targets/Cpp.Common.props index ca1335ef2c85..4f2f7662b977 100644 --- a/src/aspnetcore/eng/targets/Cpp.Common.props +++ b/src/aspnetcore/eng/targets/Cpp.Common.props @@ -22,7 +22,7 @@ x64 v143 $(PlatformToolsetVersion) - - 10.0.19041.0 + + 10.0.22621.0 diff --git a/src/aspnetcore/eng/targets/Helix.Common.props b/src/aspnetcore/eng/targets/Helix.Common.props index febe906600ce..c99dbcc4fee2 100644 --- a/src/aspnetcore/eng/targets/Helix.Common.props +++ b/src/aspnetcore/eng/targets/Helix.Common.props @@ -29,7 +29,7 @@ - + diff --git a/src/aspnetcore/eng/targets/Helix.targets b/src/aspnetcore/eng/targets/Helix.targets index d58bc8feedde..3f3eccd45d77 100644 --- a/src/aspnetcore/eng/targets/Helix.targets +++ b/src/aspnetcore/eng/targets/Helix.targets @@ -1,7 +1,7 @@ - - + + @@ -21,7 +21,7 @@ $(HelixQueueAzureLinux); $(HelixQueueDebian); $(HelixQueueFedora40); - Ubuntu.2004.Amd64.Open; + Ubuntu.2204.Amd64.Open; true diff --git a/src/aspnetcore/eng/targets/Packaging.targets b/src/aspnetcore/eng/targets/Packaging.targets index cbf2aece03f5..b9425e6ca60c 100644 --- a/src/aspnetcore/eng/targets/Packaging.targets +++ b/src/aspnetcore/eng/targets/Packaging.targets @@ -1,16 +1,5 @@ - - - - diff --git a/src/aspnetcore/eng/targets/ResolveReferences.targets b/src/aspnetcore/eng/targets/ResolveReferences.targets index 6dd4f551a26e..39bfaa0f8d4c 100644 --- a/src/aspnetcore/eng/targets/ResolveReferences.targets +++ b/src/aspnetcore/eng/targets/ResolveReferences.targets @@ -10,8 +10,6 @@ Items used by the resolution strategy: - * BaselinePackageReference = a list of packages that were referenced in the last release of the project currently building - - mainly used to ensure references do not change in servicing builds unless $(UseLatestPackageReferences) is not true. * LatestPackageReference = a list of the latest versions of packages * Reference = a list of the references which are needed for compilation or runtime * ProjectReferenceProvider = a list which maps of assembly names to the project file that produces it @@ -46,7 +44,7 @@ Condition=" '$(UseLatestPackageReferences)' == '' AND '$(IsPackageInThisPatch)' == 'true' ">true false - + true @@ -104,6 +102,29 @@ + + + <_ProjectReferenceWithOriginalIdentity Include="@(ProjectReference)" + OriginalIdentity="%(Identity)" /> + <_ProjectReferenceWithFilename Include="@(_ProjectReferenceWithOriginalIdentity->Metadata('Filename'))" /> + + <_ProjectReferenceExcludedWithFilename Include="@(_ProjectReferenceWithFilename)" + Exclude="@(AspNetCoreAppReference)" /> + <_FrameworkProjectReference Include="@(_ProjectReferenceWithFilename)" + Exclude="@(_ProjectReferenceExcludedWithFilename)" /> + + + all + false + + + - - - - - - - - - - - - - - - - - - <_LatestPackageReferenceWithVersion Remove="@(_LatestPackageReferenceWithVersion)" /> - <_BaselinePackageReferenceWithVersion Remove="@(_BaselinePackageReferenceWithVersion)" /> <_PrivatePackageReferenceWithVersion Remove="@(_PrivatePackageReferenceWithVersion)" /> <_ImplicitPackageReference Remove="@(_ImplicitPackageReference)" /> @@ -207,15 +203,10 @@ <_ExplicitPackageReference Remove="@(_ExplicitPackageReference)" /> - - + Text="Could not resolve this reference. Could not locate the package or project for "%(Reference.Identity)". Did you update dependencies lists? See docs/ReferenceResolution.md for more details." /> + + + + + + + diff --git a/src/aspnetcore/eng/targets/Wix.Common.targets b/src/aspnetcore/eng/targets/Wix.Common.targets index e159100ea531..f4e4866c5f33 100644 --- a/src/aspnetcore/eng/targets/Wix.Common.targets +++ b/src/aspnetcore/eng/targets/Wix.Common.targets @@ -1,57 +1,4 @@ - - - - - - Microsoft400 - - - Microsoft400 - - - Microsoft400 - - - Microsoft400 - - - - - - - <_GeneratedPackageVersion>$(PackageVersion) - <_GeneratedPackageVersion - Condition="! $(PackageVersion.Contains('$(_BuildNumberLabels)'))">$(VersionPrefix)-$(_PreReleaseLabel)$(_BuildNumberLabels) - - $(OutputNamePrefix)$(_GeneratedPackageVersion)$(OutputNameSuffix) - - yes - $(ProductName) - - $(DefineConstants);Debug - $(DefineConstants);EmbedCab=$(EmbedCab) - - - - $(OutputName.Replace('-', '_')).cab - $(OutputName.Replace('_win', '')).cab - - - - en-US - $(Culture) - $(Platform) - $(OutputPath) - - $(DefineConstants);Cabinet=$(Cabinet) - $(DefineConstants);BinPath=$(OutputPath)$(Culture)\ - $(WixVariables);$(DefineConstants) - - - - - diff --git a/src/aspnetcore/eng/test-configuration.json b/src/aspnetcore/eng/test-configuration.json index ae0eae533528..bb564eda340e 100644 --- a/src/aspnetcore/eng/test-configuration.json +++ b/src/aspnetcore/eng/test-configuration.json @@ -10,10 +10,9 @@ {"testName": {"contains": "CanLaunchPhotinoWebViewAndClickButton"}}, {"testName": {"contains": "CheckInvalidHostingModelParameter"}}, {"testName": {"contains": "CheckNewShimIsUsed"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/57538 - {"testName": {"contains": "ComponentLifecycleMethodThrowsExceptionTerminatesTheCircuit"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/57551 - {"testName": {"contains": "ComponentDisposeMethodThrowsExceptionTerminatesTheCircuit"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/57551 {"testName": {"contains": "PhoneFactorFailsAfterSecurityStampChangeTest"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/58231 {"testName": {"contains": "AbortSendsFinalGOAWAY"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/59358 + {"testName": {"contains": "Http2_RequestWithDataAndContentLength_Success"}}, // Issue: https://github.com/dotnet/aspnetcore/issues/63266 {"testAssembly": {"contains": "IIS"}}, {"testAssembly": {"contains": "Template"}}, {"failureMessage": {"contains":"(Site is started but no worker process found)"}}, diff --git a/src/aspnetcore/eng/testing/linker/SupportFiles/Directory.Build.targets b/src/aspnetcore/eng/testing/linker/SupportFiles/Directory.Build.targets index 26dae763a703..3d1b3c8b268e 100644 --- a/src/aspnetcore/eng/testing/linker/SupportFiles/Directory.Build.targets +++ b/src/aspnetcore/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -69,8 +69,39 @@ --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/eng/tools/BaselineGenerator/BaselineGenerator.csproj b/src/aspnetcore/eng/tools/BaselineGenerator/BaselineGenerator.csproj deleted file mode 100644 index aa2c6e9905e9..000000000000 --- a/src/aspnetcore/eng/tools/BaselineGenerator/BaselineGenerator.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - $(DefaultNetCoreTargetFramework) - $(MSBuildThisFileDirectory)../../ - - - - - - - - diff --git a/src/aspnetcore/eng/tools/BaselineGenerator/Program.cs b/src/aspnetcore/eng/tools/BaselineGenerator/Program.cs deleted file mode 100644 index c599c77ac460..000000000000 --- a/src/aspnetcore/eng/tools/BaselineGenerator/Program.cs +++ /dev/null @@ -1,398 +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.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using Microsoft.Extensions.CommandLineUtils; -using NuGet.Common; -using NuGet.Configuration; -using NuGet.Packaging; -using NuGet.Protocol; -using NuGet.Protocol.Core.Types; -using NuGet.Versioning; - -namespace PackageBaselineGenerator; - -/// -/// This generates Baseline.props with information about the last RTM release. -/// -class Program : CommandLineApplication -{ - static void Main(string[] args) - { - new Program().Execute(args); - } - - private readonly CommandOption _sources; - private readonly CommandOption _output; - private readonly CommandOption _update; - - private static readonly string[] _defaultSources = new string[] { "https://api.nuget.org/v3/index.json" }; - - public Program() - { - _sources = Option( - "-s|--package-sources ", - "The NuGet source(s) of packages to fetch", - CommandOptionType.MultipleValue); - _output = Option("-o|--output ", "The generated file output path", CommandOptionType.SingleValue); - _update = Option("-u|--update", "Regenerate the input (Baseline.xml) file.", CommandOptionType.NoValue); - - Invoke = () => Run().GetAwaiter().GetResult(); - } - - private async Task Run() - { - if (_output.HasValue() && _update.HasValue()) - { - await Error.WriteLineAsync("'--output' and '--update' options must not be used together."); - return 1; - } - - var inputPath = Path.Combine(Directory.GetCurrentDirectory(), "Baseline.xml"); - var input = XDocument.Load(inputPath); - var sources = _sources.HasValue() ? _sources.Values.Select(s => s.TrimEnd('/')) : _defaultSources; - var packageSources = sources.Select(s => new PackageSource(s)); - var providers = Repository.Provider.GetCoreV3(); // Get v2 and v3 API support - var sourceRepositories = packageSources.Select(ps => new SourceRepository(ps, providers)); - if (_update.HasValue()) - { - var updateResult = await RunUpdateAsync(inputPath, input, sourceRepositories); - if (updateResult != 0) - { - return updateResult; - } - } - - List<(string packageBase, bool feedV3)> packageBases = new List<(string, bool)>(); - foreach (var sourceRepository in sourceRepositories) - { - var feedType = await sourceRepository.GetFeedType(CancellationToken.None); - var feedV3 = feedType == FeedType.HttpV3; - var packageBase = sourceRepository.PackageSource + "/package"; - if (feedV3) - { - var resources = await sourceRepository.GetResourceAsync(); - packageBase = resources.GetServiceEntryUri(ServiceTypes.PackageBaseAddress).ToString().TrimEnd('/'); - } - - packageBases.Add((packageBase, feedV3)); - } - - var output = _output.HasValue() - ? _output.Value() - : Path.Combine(Directory.GetCurrentDirectory(), "Baseline.Designer.props"); - - var packageCache = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); - - var tempDir = Path.Combine(Directory.GetCurrentDirectory(), "obj", "tmp"); - Directory.CreateDirectory(tempDir); - - var baselineVersion = input.Root.Attribute("Version").Value; - - // Baseline and .NET Core versions always align in non-preview releases. - var parsedVersion = Version.Parse(baselineVersion); - var defaultTarget = ((parsedVersion.Major < 5) ? "netcoreapp" : "net") + - $"{parsedVersion.Major}.{parsedVersion.Minor}"; - - var doc = new XDocument( - new XComment(" Auto generated. Do not edit manually, use eng/tools/BaselineGenerator/ to recreate. "), - new XElement("Project", - new XElement("PropertyGroup", - new XElement("MSBuildAllProjects", "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)"), - new XElement("AspNetCoreBaselineVersion", baselineVersion)))); - - var client = new HttpClient(); - foreach (var pkg in input.Root.Descendants("Package")) - { - var id = pkg.Attribute("Id").Value; - var version = pkg.Attribute("Version").Value; - var packageFileName = $"{id}.{version}.nupkg"; - var nupkgPath = Path.Combine(packageCache, id.ToLowerInvariant(), version, packageFileName); - if (!File.Exists(nupkgPath)) - { - nupkgPath = Path.Combine(tempDir, packageFileName); - } - - if (!File.Exists(nupkgPath)) - { - foreach ((string packageBase, bool feedV3) in packageBases) - { - var url = feedV3 ? - $"{packageBase}/{id.ToLowerInvariant()}/{version}/{id.ToLowerInvariant()}.{version}.nupkg" : - $"{packageBase}/{id}/{version}"; - - Console.WriteLine($"Downloading {url}"); - try - { - using (var response = await client.GetStreamAsync(url)) - { - using (var file = File.Create(nupkgPath)) - { - await response.CopyToAsync(file); - } - } - } - catch (HttpRequestException e) when (e.StatusCode == System.Net.HttpStatusCode.NotFound) - { - // If it's not found, continue onto the next one. - continue; - } - } - - if (!File.Exists(nupkgPath)) - { - throw new Exception($"Could not download package {id} @ {version} using any input feed"); - } - } - - using (var reader = new PackageArchiveReader(nupkgPath)) - { - doc.Root.Add(new XComment($" Package: {id}")); - - var propertyGroup = new XElement( - "PropertyGroup", - new XAttribute("Condition", $" '$(PackageId)' == '{id}' "), - new XElement("BaselinePackageVersion", version)); - doc.Root.Add(propertyGroup); - - foreach (var group in reader.NuspecReader.GetDependencyGroups()) - { - // Don't bother generating empty ItemGroup elements. - if (!group.Packages.Any()) - { - continue; - } - - // Handle changes to $(DefaultNetCoreTargetFramework) even if some projects are held back. - var targetCondition = $"'$(TargetFramework)' == '{group.TargetFramework.GetShortFolderName()}'"; - if (string.Equals( - group.TargetFramework.GetShortFolderName(), - defaultTarget, - StringComparison.OrdinalIgnoreCase)) - { - targetCondition = - $"('$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' OR '$(TargetFramework)' == '{defaultTarget}')"; - } - - var itemGroup = new XElement( - "ItemGroup", - new XAttribute("Condition", $" '$(PackageId)' == '{id}' AND {targetCondition} ")); - doc.Root.Add(itemGroup); - - foreach (var dependency in group.Packages) - { - itemGroup.Add( - new XElement("BaselinePackageReference", - new XAttribute("Include", dependency.Id), - new XAttribute("Version", dependency.VersionRange.ToString()))); - } - } - } - } - - var settings = new XmlWriterSettings - { - OmitXmlDeclaration = true, - Encoding = Encoding.UTF8, - Indent = true, - }; - - using (var writer = XmlWriter.Create(output, settings)) - { - doc.Save(writer); - } - - Console.WriteLine($"Generated file in {output}"); - - return 0; - } - - private async Task RunUpdateAsync( - string documentPath, - XDocument document, - IEnumerable sourceRepositories) - { - var packageMetadataResources = await Task.WhenAll(sourceRepositories.Select(async sr => - await sr.GetResourceAsync())); - - var logger = new Logger(Error, Out); - var hasChanged = false; - using (var cacheContext = new SourceCacheContext { NoCache = true }) - { - var versionAttribute = document.Root.Attribute("Version"); - hasChanged = await TryUpdateVersionAsync( - versionAttribute, - "Microsoft.AspNetCore.App.Runtime.win-x64", - packageMetadataResources, - logger, - cacheContext); - - foreach (var package in document.Root.Descendants("Package")) - { - var id = package.Attribute("Id").Value; - versionAttribute = package.Attribute("Version"); - var attributeChanged = await TryUpdateVersionAsync( - versionAttribute, - id, - packageMetadataResources, - logger, - cacheContext); - - hasChanged |= attributeChanged; - } - } - - if (hasChanged) - { - await Out.WriteLineAsync($"Updating {documentPath}."); - - var settings = new XmlWriterSettings - { - Async = true, - CheckCharacters = true, - CloseOutput = false, - Encoding = Encoding.UTF8, - Indent = true, - IndentChars = " ", - NewLineOnAttributes = false, - OmitXmlDeclaration = true, - WriteEndDocumentOnClose = true, - }; - - using (var stream = File.OpenWrite(documentPath)) - { - using (var writer = XmlWriter.Create(stream, settings)) - { - await document.SaveAsync(writer, CancellationToken.None); - } - } - } - else - { - await Out.WriteLineAsync("No new versions found"); - } - - return 0; - } - - private static async Task TryUpdateVersionAsync( - XAttribute versionAttribute, - string packageId, - IEnumerable packageMetadataResources, - ILogger logger, - SourceCacheContext cacheContext) - { - var currentVersion = NuGetVersion.Parse(versionAttribute.Value); - var versionRange = new VersionRange( - currentVersion, - new FloatRange(NuGetVersionFloatBehavior.Patch, currentVersion)); - - var searchMetadatas = await Task.WhenAll( - packageMetadataResources.Select(async pmr => await pmr.GetMetadataAsync( - packageId, - includePrerelease: false, - includeUnlisted: true, // Microsoft.AspNetCore.DataOrotection.Redis package is not listed. - sourceCacheContext: cacheContext, - log: logger, - token: CancellationToken.None))); - - // Find the latest version among each search metadata - NuGetVersion latestVersion = null; - foreach (var searchMetadata in searchMetadatas) - { - var potentialLatestVersion = versionRange.FindBestMatch( - searchMetadata.Select(metadata => metadata.Identity.Version)); - if (latestVersion == null || - (potentialLatestVersion != null && potentialLatestVersion.CompareTo(latestVersion) > 0)) - { - latestVersion = potentialLatestVersion; - } - } - - if (latestVersion == null) - { - logger.LogWarning($"Unable to find latest version of '{packageId}'."); - return false; - } - - var hasChanged = false; - if (latestVersion != currentVersion) - { - hasChanged = true; - versionAttribute.Value = latestVersion.ToNormalizedString(); - } - - return hasChanged; - } - - private class Logger : ILogger - { - private readonly TextWriter _error; - private readonly TextWriter _out; - - public Logger(TextWriter error, TextWriter @out) - { - _error = error; - _out = @out; - } - - public void Log(LogLevel level, string data) - { - switch (level) - { - case LogLevel.Debug: - LogDebug(data); - break; - case LogLevel.Error: - LogError(data); - break; - case LogLevel.Information: - LogInformation(data); - break; - case LogLevel.Minimal: - LogMinimal(data); - break; - case LogLevel.Verbose: - LogVerbose(data); - break; - case LogLevel.Warning: - LogWarning(data); - break; - } - } - - public void Log(ILogMessage message) => Log(message.Level, message.Message); - - public Task LogAsync(LogLevel level, string data) - { - Log(level, data); - return Task.CompletedTask; - } - - public Task LogAsync(ILogMessage message) => LogAsync(message.Level, message.Message); - - public void LogDebug(string data) => _out.WriteLine($"Debug: {data}"); - - public void LogError(string data) => _error.WriteLine($"Error: {data}"); - - public void LogInformation(string data) => _out.WriteLine($"Information: {data}"); - - public void LogInformationSummary(string data) => _out.WriteLine($"Summary: {data}"); - - public void LogMinimal(string data) => _out.WriteLine($"Minimal: {data}"); - - public void LogVerbose(string data) => _out.WriteLine($"Verbose: {data}"); - - public void LogWarning(string data) => _out.WriteLine($"Warning: {data}"); - } -} diff --git a/src/aspnetcore/eng/tools/BaselineGenerator/README.md b/src/aspnetcore/eng/tools/BaselineGenerator/README.md deleted file mode 100644 index 28904c40b5a9..000000000000 --- a/src/aspnetcore/eng/tools/BaselineGenerator/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# BaselineGenerator - -This tool is used to generate an MSBuild file which sets the "baseline" against which servicing updates are built. - -## Usage - -Add `--package-sources {source}` to the commands below if the packages of interest are not all hosted on NuGet.org. - -### Auto-update - -Run `dotnet run --update` in this project folder. This will attempt to find the latest patch version of packages and -update the baseline file. - -### Manual update - -1. Add to the [Baseline.xml](/eng/Baseline.xml) a list of package ID's and their latest released versions. The source of -this information can typically be found in the build.xml file generated during ProdCon builds. See - for example. -Update the version at the top of baseline.xml to match prior release (even if no packages changed in the prior release). -2. Run `dotnet run` on this project. diff --git a/src/aspnetcore/eng/tools/GenerateFiles/Directory.Build.props.in b/src/aspnetcore/eng/tools/GenerateFiles/Directory.Build.props.in index 13205d2f4961..49747cadbe74 100644 --- a/src/aspnetcore/eng/tools/GenerateFiles/Directory.Build.props.in +++ b/src/aspnetcore/eng/tools/GenerateFiles/Directory.Build.props.in @@ -4,5 +4,6 @@ ${ArtifactsShippingPackagesDir} true ${LibNetHostAppPackVersion} + <_ProductVersionForInstallers>${MicrosoftAspNetCoreAppRuntimeVersion} diff --git a/src/aspnetcore/eng/tools/RepoTasks/RemoveSharedFrameworkDependencies.cs b/src/aspnetcore/eng/tools/RepoTasks/RemoveSharedFrameworkDependencies.cs deleted file mode 100644 index e29e25581607..000000000000 --- a/src/aspnetcore/eng/tools/RepoTasks/RemoveSharedFrameworkDependencies.cs +++ /dev/null @@ -1,110 +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.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using NuGet.Frameworks; -using NuGet.Packaging; -using NuGet.Packaging.Core; - -namespace RepoTasks; - -// This is temporary until we can use FrameworkReference to build our own packages -public class RemoveSharedFrameworkDependencies : Microsoft.Build.Utilities.Task -{ - [Required] - public ITaskItem[] Files { get; set; } - - [Required] - public ITaskItem[] FrameworkOnlyPackages { get; set; } - - [Required] - public string SharedFrameworkTargetFramework { get; set; } - - public override bool Execute() - { - Log.LogMessage("NuGet version = " + typeof(PackageArchiveReader).Assembly.GetName().Version); - var dependencyToRemove = new HashSet(FrameworkOnlyPackages.Select(p => p.ItemSpec), StringComparer.OrdinalIgnoreCase); - - foreach (var file in Files) - { - FilterDependencies(file.ItemSpec, dependencyToRemove); - } - return !Log.HasLoggedErrors; - } - - private void FilterDependencies(string targetPath, ISet dependencyToRemove) - { - var fileName = Path.GetFileName(targetPath); - Log.LogMessage($"Updating {fileName}"); - - using (var fileStream = File.Open(targetPath, FileMode.Open)) - using (var package = new ZipArchive(fileStream, ZipArchiveMode.Update)) - using (var packageReader = new PackageArchiveReader(fileStream, leaveStreamOpen: true)) - { - var referencesFrameworkOnlyAssembly = false; - var nuspecFile = packageReader.GetNuspecFile(); - using (var stream = package.OpenFile(nuspecFile)) - { - var reader = Manifest.ReadFrom(stream, validateSchema: true); - stream.Position = 0; - var packageBuilder = new PackageBuilder(stream, basePath: null); - var updatedGroups = new List(); - - foreach (var group in packageBuilder.DependencyGroups) - { - var packages = new List(); - var updatedGroup = new PackageDependencyGroup(group.TargetFramework, packages); - foreach (var dependency in group.Packages) - { - if (dependencyToRemove.Contains(dependency.Id)) - { - referencesFrameworkOnlyAssembly = true; - Log.LogMessage($" Remove dependency on '{dependency.Id}'"); - continue; - } - - packages.Add(dependency); - } - - updatedGroups.Add(updatedGroup); - } - - if (referencesFrameworkOnlyAssembly) - { - packageBuilder.DependencyGroups.Clear(); - packageBuilder.DependencyGroups.AddRange(updatedGroups); - - var updatedManifest = Manifest.Create(packageBuilder); - var inMemory = new MemoryStream(); - updatedManifest.Save(inMemory); - inMemory.Position = 0; - // Hack the raw nuspec to add the dependency - var rawNuspec = XDocument.Load(inMemory, LoadOptions.PreserveWhitespace); - var ns = rawNuspec.Root.GetDefaultNamespace(); - var metadata = rawNuspec.Root.Descendants(ns + "metadata").Single(); - metadata.Add( - new XElement(ns + "frameworkReferences", - new XElement(ns + "group", - new XAttribute("targetFramework", NuGetFramework.Parse(SharedFrameworkTargetFramework).GetFrameworkString()), - new XElement(ns + "frameworkReference", new XAttribute("name", "Microsoft.AspNetCore.App"))))); - stream.Position = 0; - stream.SetLength(0); - rawNuspec.Save(stream); - Log.LogMessage(MessageImportance.High, "Added to {0}", fileName); - } - else - { - Log.LogMessage($"No changes made to {fileName}"); - } - } - } - } -} diff --git a/src/aspnetcore/eng/tools/RepoTasks/RepoTasks.tasks b/src/aspnetcore/eng/tools/RepoTasks/RepoTasks.tasks index b6cd9a820d1e..b4bb38411ac3 100644 --- a/src/aspnetcore/eng/tools/RepoTasks/RepoTasks.tasks +++ b/src/aspnetcore/eng/tools/RepoTasks/RepoTasks.tasks @@ -9,7 +9,6 @@ - diff --git a/src/aspnetcore/eng/tools/tools.slnf b/src/aspnetcore/eng/tools/tools.slnf index b7524a84eccd..ee113b425cb9 100644 --- a/src/aspnetcore/eng/tools/tools.slnf +++ b/src/aspnetcore/eng/tools/tools.slnf @@ -2,7 +2,6 @@ "solution": { "path": "..\\..\\AspNetCore.slnx", "projects": [ - "eng\\tools\\BaselineGenerator\\BaselineGenerator.csproj", "eng\\tools\\RepoTasks\\RepoTasks.csproj", "eng\\tools\\HelixTestRunner\\HelixTestRunner.csproj" ] diff --git a/src/aspnetcore/es-metadata.yml b/src/aspnetcore/es-metadata.yml new file mode 100644 index 000000000000..a9fe05cc4ca9 --- /dev/null +++ b/src/aspnetcore/es-metadata.yml @@ -0,0 +1,8 @@ +schemaVersion: 0.0.1 +isProduction: true +accountableOwners: + service: 4db45fa9-fb0f-43ce-b523-ad1da773dfbc +routing: + defaultAreaPath: + org: devdiv + path: DevDiv\ASP.NET Core\Policy Violations diff --git a/src/aspnetcore/global.json b/src/aspnetcore/global.json index ede979be68ba..6d7e4db7680d 100644 --- a/src/aspnetcore/global.json +++ b/src/aspnetcore/global.json @@ -1,9 +1,14 @@ { "sdk": { - "version": "10.0.100-preview.7.25380.108" + "version": "10.0.100", + "paths": [ + ".dotnet", + "$host$" + ], + "errorMessage": "The .NET SDK could not be found, run ./restore.cmd or ./restore.sh first." }, "tools": { - "dotnet": "10.0.100-preview.7.25380.108", + "dotnet": "10.0.100", "runtimes": { "dotnet/x86": [ "$(MicrosoftInternalRuntimeAspNetCoreTransportVersion)" @@ -12,25 +17,17 @@ "$(MicrosoftInternalRuntimeAspNetCoreTransportVersion)" ] }, - "vs": { - "version": "17.13", - "components": [ - "Microsoft.VisualStudio.Component.VC.ATL", - "Microsoft.VisualStudio.Component.VC.ATL.ARM64", - "Microsoft.VisualStudio.Component.VC.Tools.ARM64", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" - ] - }, - "xcopy-msbuild": "17.13.0" + "vs": {} }, "native-tools": { "jdk": "latest" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25414.101", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25414.101", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25414.101", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26057.107", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26057.107", + "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26057.107", "Microsoft.Build.NoTargets": "3.7.0", - "Microsoft.Build.Traversal": "3.4.0" + "Microsoft.Build.Traversal": "3.4.0", + "Microsoft.WixToolset.Sdk": "5.0.2-dotnet.2811440" } } diff --git a/src/aspnetcore/package-lock.json b/src/aspnetcore/package-lock.json index 95e6c7158c66..f98819db8210 100644 --- a/src/aspnetcore/package-lock.json +++ b/src/aspnetcore/package-lock.json @@ -38,20 +38,6 @@ "npm": ">=9.3.1" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha1-7UQbb6YAByUgzhi0PSyMyMrsx/Q=", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@azure/msal-browser": { "version": "2.39.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@azure/msal-browser/-/msal-browser-2.39.0.tgz", @@ -74,24 +60,24 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha1-S1+rl9MzOO/5FiNQVfDrwh5XOoU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha1-IA9xXmbVKiOyIalDVTSpHME61b4=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha1-ghwdNWQcNVKE1KhwuKSnsMFB42c=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha1-qKSWLhVnEhrAs7SH9SEHRDtFXH8=", "dev": true, "license": "MIT", "engines": { @@ -99,22 +85,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha1-XIdvg8jE3LIz7ktnDAYG8qwwAPk=", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha1-TIGzXlHhtzT1EMmbB9+8e7u0j34=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -130,9 +116,9 @@ } }, "node_modules/@babel/core/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -148,16 +134,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/generator/-/generator-7.26.10.tgz", - "integrity": "sha1-pg2d5JysoWdE5jQMNljf72E4w/c=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha1-cSci1eUPRNB7x6yf6EQ4dC3WEpg=", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.10", - "@babel/types": "^7.26.10", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -165,27 +151,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha1-2OrE0twNe24R+m5TUzLg0xhPBrQ=", + "version": "7.27.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha1-8x/Ya5FfxNrx86xpdsWb5whO2cU=", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha1-ddkruNjVEwHA1J5Splyaf+lFFNg=", + "version": "7.27.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha1-RqD276uAjVHSnOloWN0Qzocycz0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -195,18 +181,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.26.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", - "integrity": "sha1-1vg+MDlUf7s5ln54BDzTyLeCDHE=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha1-Ry0MKAKIUJaJea2J8XNZSmmV2kY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.26.9", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "engines": { @@ -217,14 +203,14 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.26.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", - "integrity": "sha1-UWl1bsvh2V94ZrkLtVWwIllTAqA=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha1-fB3dZLIGXH94A0sltDNGp+Ge2Zc=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -235,26 +221,26 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", - "integrity": "sha1-9PJ5L64u84IHS8LXE1Is8k5t2yE=", + "version": "0.6.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha1-dCzPHLADwHtIhZ/J+iwbvkDl91M=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -269,44 +255,54 @@ } } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha1-uUMN8qpOF7woZl6t6uiqHZheZnQ=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha1-nf/+RvcnAFpeopBRrINftzXkwaM=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha1-8+B6EL437XpjRhxj5pKVdZRaYVA=", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha1-5/jSBgLr2/nrvqCgdR+w8qQUFxU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha1-fvdpoyPiZV4SZnO7bS1pE7vq0gQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha1-jOVOydWSaV5Y2EzYhLe1xqL97q4=", + "version": "7.28.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha1-orN9PaOyNE/ghdqyNEJvK5ovpfY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -316,22 +312,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha1-MySuULrn4qs8M/YMmod7agFGtU4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha1-xlIhthpkPz5icF5d0rXxFeNfkgA=", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha1-GFgNAMmTQRetcZOSxPZYXJMzzDU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha1-3bL4dlNP+AE+bCspm/TTmzxR1Ew=", "dev": true, "license": "MIT", "engines": { @@ -339,15 +335,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha1-5TlWqz1bn7iL4Es+LzG1I6/TS5I=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha1-RgHVx84usq6lgyjUNyVSP802LOY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -357,15 +353,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.26.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", - "integrity": "sha1-bLBOgq4pHa6OcjNd/kOLByXxTI0=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha1-se0tY0zjvbcw5LUt4w+MzP1pK8A=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.26.5" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -375,23 +371,23 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha1-Cy4bYtVg1rGVSJP9K3BdwXyR8Mk=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha1-YruRs6u6jH8f7AJS2dvqEbPuelY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha1-Gqu3Lucu01eJtLvK08ooYs5hTow=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha1-VNp5YJerGc5n7Z+ItHuy7Ek2doc=", "dev": true, "license": "MIT", "engines": { @@ -399,9 +395,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha1-JLZOLD7HzTs8VHcpuNFocfIsvcc=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha1-AQtpOPq3y333SqK7wGqlA7j+X7Q=", "dev": true, "license": "MIT", "engines": { @@ -409,9 +405,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha1-huRb2KSat+A/J2V3+WF5ZT1B2nI=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha1-+lL1sefbGrBJRFtCHERxMDiXcC8=", "dev": true, "license": "MIT", "engines": { @@ -419,42 +415,42 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha1-2Z39WVMS5siUvX0jdHACXIXuqdA=", + "version": "7.28.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha1-/khyCSvBQ4/9DOV55vaZYJ+dCno=", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha1-a66jzWLsLQwQaHeNY8sTFPZjc4Q=", + "version": "7.28.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha1-/gcnR0LpW9988UQ1k+64kmq2OCc=", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha1-6b24LxS5ffZWmwsDjt1DaDnFd0k=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha1-CwIl7pA2LwMO/WROgDTJlGiJOwg=", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -464,14 +460,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha1-zC5T6/CgNAd3//XtUhlD4lO02P4=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha1-+95Xl0cHu/oDdtNNQl/0+mxzJCE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -481,13 +477,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha1-r55PtjzLiry5I3Wy/P42tgx3TTA=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha1-Q/cKbX79UjcO7731WuA9kbKThW0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -497,13 +493,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha1-6Nwm/NYW5sW/K9DVosFR1PkqkTc=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha1-vrYjvVc7i28wR70EwyUGrcPlinI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -513,15 +509,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha1-gHpmf5FYrKxvYWS0vrha2evJ4dE=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha1-4TSlR56yupwCcU6MHr8eyQdhJP0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -531,14 +527,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha1-3nCT8efer2jq3XzGsH8quCVDJp4=", + "version": "7.28.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha1-Nz9uLeABb3PK+PJwBPYdFndDdCo=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -616,13 +612,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha1-YgQSQFBY76VuSlZJA7eTVQIPRF8=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha1-iIlK79KwO17mrRVip8jhWHSWrs0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -632,13 +628,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha1-OxQShHaZ7qc5tPJgLHTONvawsPc=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha1-NMAX1USW+bEbYUdOfqPf1VY//gc=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -674,13 +670,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha1-o0MToXjqVvGVFZm5KcHOrO5xkpA=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha1-L5vrXv8w+lB8VTLRB9qse4iPo0w=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -800,13 +796,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha1-Z92it02kNyfPIdRs+a/vI/Q2U5k=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha1-UUfSkGank0UPIgxj+jqUMbfm3Rg=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -833,13 +829,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha1-eCHUQQvuXaqtu0zdmmZJcE4XaEU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha1-biBhBnujqwJm2DSp+UgRGW8qupo=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -849,15 +845,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha1-XjmRE147nG6q9e/1bRrloR30X/g=", + "version": "7.28.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha1-Enbmxyhass0ezLC8c1a3pp/4QsI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -867,15 +863,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha1-yAAI2srlFIJ5PlqcCLOaW+fhLXE=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha1-mpOJO5N5s5Rmx0R09VrwPeeMZuc=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -885,13 +881,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.26.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", - "integrity": "sha1-PcRAXTGtHL5FKTqlcgWm47AJ1T4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha1-VYqdbiTPcoAt07YqS1Hg1iwPV/k=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -901,13 +897,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", - "integrity": "sha1-wzZl5GsGdZyTaHyg+EOVuAwEc6E=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", + "integrity": "sha1-4NOvY72MgN4uVn5pClToTYXrFvY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -917,14 +913,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha1-qM6E/tua1RJUmYQQH6hAgKn19R8=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha1-3UCmo3Df1J0yNiriBt2vK7CCqSU=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -934,14 +930,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha1-bI2iGfTrFcrpg07ENI/46eCWZKA=", + "version": "7.28.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha1-0bjmm1TJmTvFWCA+H0m/yXm/2FI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -951,18 +947,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha1-cVJFf3iAtZOmOt6Khh5uJqRGn1I=", + "version": "7.28.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha1-ddZhdUhniMVnKKc0JNZ8vHRzSVw=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -972,14 +968,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha1-2zZJLHhGDlNLiFKx1b7+PJI+8Qs=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha1-gWYueL9ec0qXmCwrfwp5MojvPKo=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -989,13 +985,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha1-lm6iWVxJgiQ0CINgLTz9egx5zqE=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha1-uEAnZN+WF5ogcLt7UBoVhs+K16c=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1005,14 +1002,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha1-uteUXdB3NMpS/jrU6HK0DtCbsJo=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha1-qmgh3oZMUosf7PKG8KF0446Cb00=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1022,13 +1019,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha1-iFDd9X3OKuu0OUu0NKdZgDEFnm0=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha1-8fv2KOzhjhLnsysXWUDmg1j1RtE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1038,14 +1035,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha1-b3JZtN4SdyGgjx5RZbhS/KppbTE=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha1-UEOFTKYgqUFJNy5pAw/4y2qesOw=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1055,13 +1052,30 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha1-I+kX3mPtI8ZgDF3QbZRmnc5597g=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha1-THjzVVKsDgaqH248Vz1naV6K9aQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha1-Rb5iEbd42/S51UxOiitC+nLgmho=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1071,13 +1085,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.26.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", - "integrity": "sha1-4p8Btt4wLHwseUJ3pI8Eqcp/A7w=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", + "integrity": "sha1-fMkKgXDoNTJnbPpQUnjhRwVulP4=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1087,13 +1101,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha1-kHRf5VBTOU9VTkBYTNqB8sikAqI=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha1-ccpp00ce3W2qcRz038NABBXfnCM=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1103,14 +1117,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.26.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", - "integrity": "sha1-JyMfedUXDvM7URHwf+XK/rLJalY=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha1-vCT3CA6f9yG2OnCseyVkyhW2xAo=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1120,15 +1134,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha1-k52VbmimBmYQBb/VUMT8Lvlfe5c=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha1-TQvzB3IOTc5tfDD8sf1sp3ves6c=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1138,13 +1152,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha1-yG20B8uCfN7ZAqkMcH0ngaqolmA=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha1-ouDObvJWN2vVJ/KQ2gI5g1J6T0w=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1154,13 +1168,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha1-GhxrTUqlm8TK1bazoiOgq9aFyd4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha1-uq76TRCh1CBvnc3aUNfVgnu3CyQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1170,13 +1184,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha1-sZRBqMOaL9oJApALMG6gWuEFXbc=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", + "integrity": "sha1-0Cj9bbjAgd7kq+vIEsIyXiSoWw4=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1186,13 +1200,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha1-Y9/xl2PqZKMfXmwglX5qJeQe1d4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha1-N7iLpZTYUkGOmVNvVhL3lfI66vk=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1202,14 +1216,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha1-SbpHjyKVEBVEq9eUSGzTCI3dtsU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha1-pBRfnYfCKR/i0F+ZS2XbpOPnGW8=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1219,14 +1233,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha1-jwEdRLINAsPeRNiFDZcdhJf5gfs=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha1-jkTtN8J4fswjvcNn9Jl3R2YU6DI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1236,16 +1250,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha1-i9G0ODYmnj0zMHFRoRS887pnk/g=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", + "integrity": "sha1-dDnlkqktdnDfy5XQy8BL0+ZIAdI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1255,14 +1269,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha1-ZxAHnN18aU2zZSmh6EEeSfy/FMk=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha1-Y/LPT23BXevBL2lORHFIY9NM0zQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1272,14 +1286,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha1-RUmQrmzCL9Kg+mCzosb2OjgGTmo=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha1-8yuPeBjY/AzEbuIKjvdfBxr5duE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1289,13 +1303,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha1-QuYXESlLEFwkgzbcsEt3BU6ovs0=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha1-JZxDk5coytFwasFzUbfmp76hq+s=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1305,13 +1319,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.26.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", - "integrity": "sha1-+/azySy1CeezGe5G49qJxb7dMf4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha1-T50xU79ngtc91CeFqdItAxl7yR0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1321,13 +1335,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha1-v+11hmJhqLZDRosMz9J18gMyFKE=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha1-YU4LFcyADlmX2t2b1upSTtbIGcY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1337,15 +1351,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha1-AgNyUCUHQWSAi88aLPqQxlLJnxg=", + "version": "7.28.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", + "integrity": "sha1-nuHOyoCz5sS6ySR7IUnjaVj3+Y0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -1355,14 +1371,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha1-OF1d4TUWKTO+tKPSJ6K35Su0zwM=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha1-HJMs0nvzh0xDpcrE9D6/lwyYcbU=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1372,13 +1388,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha1-EOcNltUrsfEMXKqsWaxUXqK6f/M=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha1-hMc0Hr3jXM02sTfp5FhmglByoww=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1388,14 +1404,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha1-4ULriZ0m73FUNfIBq24TlUHu590=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", + "integrity": "sha1-gjjHhfnVwcUVqQvxlu+1DQdaSyY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1405,13 +1421,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha1-uFaEIgWz534Yt6ehuUlYBpx7olc=", + "version": "7.27.7", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha1-H9L+u3x059Ic87BfeuvJB5QK9To=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1421,14 +1437,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha1-hH9BOSY1d1JkVdfTIjzYvaUeO1c=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha1-/ay6scXtgexw39u4shPWXaFItq8=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1438,15 +1454,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha1-nItz5k5sw8uydDYziFp90sOF/jM=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha1-TbvvKDtbLwGiHoHimfduNfkA+xE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1456,13 +1472,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha1-1y1Yi9iLDeyLYuNvb9qRzt/ijj8=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha1-B+r9YYgAWR6IBzoK8blA2aQsZCQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1472,14 +1488,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", - "integrity": "sha1-A6ikZw1s666VMFrG3vrIHs53dAs=", + "version": "7.28.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha1-nT+jvrtI3dAJHOVykTnNmcZ86lE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1489,14 +1504,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha1-L1g3pbXNOEKpGdgUfpkDzHRVuFA=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha1-35ulV3yXTj8USYiLcLdhaZmKbQk=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1506,13 +1521,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha1-A5iu0vHxC6P3ipPbIZsn70F/uc4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha1-QPukh4zL0cVmBaRHmjqJGsAnS7Q=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1522,13 +1537,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha1-u3heYJH5n4JqlfmJT8Fv3mHBY/I=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha1-Uyq9rN7Ie/7h4O+OL83uVD/jK5A=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1538,14 +1553,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha1-JKNRU5MbS6PRPOxKd0jCGrVRTvk=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha1-GiZNX8EnUJGPUOP+PiTkNxeKuwg=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1555,13 +1570,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha1-x/ArlE6YakF4F7ILosUE38FFPTI=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha1-GJhJNdnSKWhDpJHXigFJOffc0oA=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1571,13 +1586,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.26.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", - "integrity": "sha1-lmsV0VOpkRcqVApprV4YRc7ZkLU=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha1-Gg6zXYuz5u/AbJ/UDrC871SDKLg=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1587,13 +1602,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.26.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", - "integrity": "sha1-0OM6zZIjdEwehX29b6F70KN4aTc=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha1-cOlmu0kuA1Cc836vptzDBR+EQ2k=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1603,17 +1618,17 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.26.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", - "integrity": "sha1-LpyqhwqhAvUNcSUkDZ2/kTNLCVA=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha1-RBxfmkoTFQOVFsbGEvxm1fRZTnI=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-syntax-typescript": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1623,13 +1638,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha1-p17zlHzhU2P8yqOOLdm8cLJ4i4I=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha1-PjFD+EOK74Qt4ogW7OWHgBkM+AY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1639,14 +1654,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha1-qQHpbywdBxsNG7XcDTyIDOj1PdM=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha1-vf4tMXDHjFaRo8O+k0yMAIdSWVY=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1656,14 +1671,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha1-Xq50f+OerPE6i9AGpPsLXR+l6bE=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha1-JZSPXDldsV9gkCjjcGZ+2Lrpr5c=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1673,14 +1688,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha1-ZRFMF7T/wg+lsWPGPHDA0lYh+r4=", + "version": "7.27.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha1-arcG0Q+AG1xy2ouyVIVh+gQZPNE=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1690,80 +1705,81 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha1-LsZOkD0O/nQ2mfd6EL33lVwhI8M=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/preset-env/-/preset-env-7.28.5.tgz", + "integrity": "sha1-gt0VnRVj8hmhzpQySzBx64nigLA=", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/compat-data": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.5", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.4", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.28.5", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.28.5", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.4", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.4", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.0", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "engines": { @@ -1789,17 +1805,17 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.26.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", - "integrity": "sha1-SlcPG40QSiQtkjlX/6Hq/xQqEG0=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha1-VANZ76MCgjaVhGY0KWdSL9jypgw=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", - "@babel/plugin-transform-typescript": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1809,56 +1825,53 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha1-oHtNj6J68TGmM9ezUk24A+tHZMI=", + "version": "7.28.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha1-pwImAW+r4lxXg7LyLT4cm8XKMyY=", "dev": true, "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha1-RXetPd9D0ZRSjP9OH6ayMvpgm7I=", + "version": "7.27.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha1-+njO7TxOe2Pr9ss55YUvykX2gJ0=", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/traverse/-/traverse-7.26.10.tgz", - "integrity": "sha1-Q8yjPXYAXbqpMCT65TbMGUakw4A=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha1-RQyrkTXSGnosqdLTWqBcIOaMNgs=", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -1874,14 +1887,14 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha1-OWOC9jNb1P62V0Hqz8gIIY+Fklk=", + "version": "7.28.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha1-EPxAX2CJfDXwfoVJPJMse1ygWSs=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1965,9 +1978,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz", - "integrity": "sha1-cWY39QiordWBTMZMVuWMzle9vpM=", + "version": "4.9.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha1-TpCvZ7xR3e5s3vUoTt9XLsN2tZU=", "dev": true, "license": "MIT", "dependencies": { @@ -1984,9 +1997,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha1-z8bP/jnfOQo4Qc3iq8z5Lqp64OA=", + "version": "4.12.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha1-vM32Fbz3tujbgw7AuNIcmiXeWXs=", "dev": true, "license": "MIT", "engines": { @@ -2025,9 +2038,9 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -2042,26 +2055,10 @@ } } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/globals/-/globals-13.24.0.tgz", - "integrity": "sha1-hDKhnXjODB6DOUnDats0VAC7EXE=", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha1-wftl+PUBeQHN0slRhkuhhFihBgI=", + "version": "4.1.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha1-hUwpJGdwW2mUduGi3swMijRYgGs=", "dev": true, "license": "MIT", "dependencies": { @@ -2071,19 +2068,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@eslint/js/-/js-8.57.1.tgz", @@ -2117,9 +2101,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -2174,9 +2158,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha1-lexAnGlhnWyxuLNPFLZg7yjr1lQ=", + "version": "6.2.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha1-YCFu6kZNhkWXzigyAAc4oFiWUME=", "dev": true, "license": "MIT", "engines": { @@ -2187,9 +2171,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha1-DmIyDPmcIa//OzASGSVGqsv7BcU=", + "version": "6.2.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha1-wETV3MUhoHZBNHJZehrLHxA8QEE=", "dev": true, "license": "MIT", "engines": { @@ -2225,9 +2209,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha1-1bZWjKaJ2FYTcLBwdoXSJDT6/0U=", + "version": "7.1.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha1-Eyh1q95njH6o1pFTPy5+Irt0Tbo=", "dev": true, "license": "MIT", "dependencies": { @@ -2546,9 +2530,9 @@ } }, "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -2664,34 +2648,31 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha1-Tw4GNi4BNi+CPTSPGHKwj2ZtgUI=", + "version": "0.3.13", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha1-Y0Khn0Q0dRjJPkOxrGnes8Rlah8=", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha1-eg7mAfYPmaIMfHxf8MgDiMEYm9Y=", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha1-N1xHbRlylHhRuh4Vro8SMEdEWqE=", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.0.0" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha1-VY+2Ry7RakyFC4iVMOazZDjEkoA=", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha1-eg7mAfYPmaIMfHxf8MgDiMEYm9Y=", "dev": true, "license": "MIT", "engines": { @@ -2699,9 +2680,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha1-nXHKiG4yUC65NiyadKRnh8Nt+Bo=", + "version": "0.3.11", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha1-shg1y9Nttla4V8KtAuvUE8wTqbo=", "dev": true, "license": "MIT", "dependencies": { @@ -2710,16 +2691,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha1-MYi8snOkFLDSFf0ipYVAuYm5QJo=", + "version": "1.5.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha1-aRKwDSxjHA0Vzhp6tXzWV/Ko+Lo=", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha1-FfGQ6YiV8/wjJ27hS8drZ1wuUPA=", + "version": "0.3.31", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha1-2xXWeByTHzolGj2sOVAcmKYIL9A=", "dev": true, "license": "MIT", "dependencies": { @@ -2832,9 +2813,9 @@ } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -2875,9 +2856,9 @@ } }, "node_modules/@npmcli/git/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -3055,18 +3036,18 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.8.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@puppeteer/browsers/-/browsers-2.8.0.tgz", - "integrity": "sha1-nVkpM8vvxmw3gjdwhEuMusUmB90=", + "version": "2.11.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@puppeteer/browsers/-/browsers-2.11.0.tgz", + "integrity": "sha1-stzXywLdLeWQlTHQDnF6BL1h3nM=", "dev": true, "license": "Apache-2.0", "dependencies": { - "debug": "^4.4.0", + "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", - "semver": "^7.7.1", - "tar-fs": "^3.0.8", + "semver": "^7.7.3", + "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { @@ -3092,9 +3073,9 @@ } }, "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -3110,9 +3091,9 @@ } }, "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -3265,9 +3246,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha1-u5Tx+eqqyUTaI3dnzf7mxbImLUo=", + "version": "5.3.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha1-V7obDL2o56PFl6SFPIB7FW4hp7Q=", "dev": true, "license": "MIT", "dependencies": { @@ -3288,9 +3269,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", - "integrity": "sha1-4ddwBzX36N5WHvfR+gNiCCoYDEM=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha1-duD+9lM7POMT+WmHnmHo8h8O6yg=", "cpu": [ "arm" ], @@ -3302,9 +3283,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz", - "integrity": "sha1-+mzfsfyeLI4ien811STY9/kM9Ns=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha1-08/GdaQLveyXvabX/js7BfDhzZM=", "cpu": [ "arm64" ], @@ -3316,9 +3297,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz", - "integrity": "sha1-baWh3cTxHUp66Fq0Q4JMtr9hTjA=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha1-65Erj1ndR8d7PFCnhIkBOx1ncrQ=", "cpu": [ "arm64" ], @@ -3330,9 +3311,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz", - "integrity": "sha1-JbdM4tjT+eqOEZsBOE1EocCg064=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha1-59CDn9/RJ2odNLxeu70N/X0LgaA=", "cpu": [ "x64" ], @@ -3344,9 +3325,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz", - "integrity": "sha1-vj0540Qd9dbhh8g9FYxgZWyC4gM=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha1-f/gRh2D3NR5I/QzTcX/4BUPWqsg=", "cpu": [ "arm64" ], @@ -3358,9 +3339,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz", - "integrity": "sha1-zZMtPsZ5cR79Zcolgh+zGOJbfOQ=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha1-SdMw2tvaHU6bhrSjlRtZkoqUiak=", "cpu": [ "x64" ], @@ -3372,9 +3353,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz", - "integrity": "sha1-0wC3TG+AVHQiVjLxhdqurnYKwrs=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha1-mMXx+Ll3a0o25GbiocntG6Uu8bY=", "cpu": [ "arm" ], @@ -3386,9 +3367,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz", - "integrity": "sha1-LKrGIjgPMUxBk07R5ozq9sw4DMM=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha1-uazs02cudC9wsMipQHXIFqkf8EA=", "cpu": [ "arm" ], @@ -3400,9 +3381,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz", - "integrity": "sha1-HshBZQsDjMFcGUwmMmSD/X6/8+M=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha1-emqwZlG8KeGLCaUO0aAryXKXfJs=", "cpu": [ "arm64" ], @@ -3414,9 +3395,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz", - "integrity": "sha1-L8cKRG2Ybif2EB6nToF0aYf2kVA=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha1-PIyQcrpKTU7xFWuFq5osu1fB+tA=", "cpu": [ "arm64" ], @@ -3427,10 +3408,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz", - "integrity": "sha1-VhvQRc2c6eCMlfQueoaIr4yT12Q=", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha1-F6evE1MPTkp7Es0mJ2xUMHqEqLA=", "cpu": [ "loong64" ], @@ -3441,10 +3422,38 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz", - "integrity": "sha1-RdhJoLM4E/M/5eup+Z4P8Vq1yq0=", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha1-XNepAP17B37NdT40qbf/EVf+cME=", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha1-A6CX5wJD3fHAe1nTwg845vaABTk=", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha1-pTiYcwOdRlDzW0+gYNKGOS6yGpQ=", "cpu": [ "ppc64" ], @@ -3456,9 +3465,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz", - "integrity": "sha1-eN3j5vz1tXM6l9CmdILXaKoeg6U=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha1-eJ5g59bit2Ey0AH/skuoAAf7F9A=", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha1-NVb6iNE5KC6ac8M3yaFw88X+eqQ=", "cpu": [ "riscv64" ], @@ -3470,9 +3493,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz", - "integrity": "sha1-LjSDUCD54D37QRRzpcKg6KnFA3s=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha1-wIWZWxAUPBZ0emfxpUh1ErL/BLI=", "cpu": [ "s390x" ], @@ -3484,9 +3507,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz", - "integrity": "sha1-T5d0vt3G9CdN9XrJmGLrIwQN5GE=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha1-lWOlQZ3SYEhButMaOcz90okWkPs=", "cpu": [ "x64" ], @@ -3498,9 +3521,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz", - "integrity": "sha1-38/ywa7VGLPSPM/7Sa+zSddPtgg=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha1-aRuwbmJpqJWcE0drDNKqdFj6yzE=", "cpu": [ "x64" ], @@ -3511,10 +3534,38 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha1-Ij5xIkdGpZzm2VW7xANXe7Wovp0=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha1-CBfl2Oy/64t5Ob9Y+M48ndZ/znc=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz", - "integrity": "sha1-sLN+LXcEHjqncvUZKRMJq/TAOoQ=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha1-3lbY8gE8hFcO9fuReq4DSr2pPko=", "cpu": [ "arm64" ], @@ -3526,9 +3577,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz", - "integrity": "sha1-W1pA5Ep0PdwOBrjhs5gvhW3Jzgo=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha1-ZZr/UkQxJHWu6iyUeabH05e1F78=", "cpu": [ "ia32" ], @@ -3539,10 +3590,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha1-LLCVScu2bBuXn5I4223UVMrBSog=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz", - "integrity": "sha1-BfJdvJmBvuGubnE9qrEDlwRKRso=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha1-95Q3k5AguDBX+vB+mDZbH6UcRYs=", "cpu": [ "x64" ], @@ -3746,9 +3811,9 @@ "license": "MIT" }, "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha1-buRkAGhfEw4ngSjHs4t+Ax/1svI=", + "version": "1.0.12", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha1-vlfOrB5GkrQb6d5r6MMqEGY226Q=", "dev": true, "license": "MIT" }, @@ -3798,9 +3863,9 @@ } }, "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -3848,9 +3913,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha1-+DbGH0ixNG59Kw2TxtrMW5U106s=", + "version": "7.27.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha1-tYGSlMUReZV6+uw0FEL5NB5BCKk=", "dev": true, "license": "MIT", "dependencies": { @@ -3869,13 +3934,13 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha1-jcnwrg8gLAjY1Nq2SJEsjWA44/c=", + "version": "7.28.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha1-B9cT1szg0mXJhJ2wy+YtP2Hzb3Q=", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/cacheable-request": { @@ -3891,9 +3956,9 @@ } }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha1-XXGKXklKgWb1admGeU5JxIshays=", + "version": "2.8.19", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha1-2T6iZz/YyfaXNn9e7vwrv6lPA0I=", "dev": true, "license": "MIT", "dependencies": { @@ -3930,9 +3995,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha1-Yo7/7q4gZKG055946B2Ht+X8e1A=", + "version": "1.0.8", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha1-lYuRyZGxhnztMYvt6g4hXuBQcm4=", "dev": true, "license": "MIT" }, @@ -4037,12 +4102,12 @@ } }, "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/node/-/node-22.13.10.tgz", - "integrity": "sha1-356jWMXtmRJmvswxCdwtyRJdd+Q=", + "version": "25.0.6", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/node/-/node-25.0.6.tgz", + "integrity": "sha1-XKPEbysla1kSj0M0JuQtRkdl2rE=", "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/parse5": { @@ -4099,9 +4164,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha1-gmioxXo+Sr0lwWXs02I323lIpV4=", + "version": "7.7.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha1-POOvGlUk7zJ9Lank/YttlcjXBSg=", "dev": true, "license": "MIT" }, @@ -4127,9 +4192,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha1-jDIwPag+7AUKhLPHrnufki0T4y0=", + "version": "17.0.35", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha1-BwE+RqpNfX1QpJ4VYEwcU0DU6yQ=", "dev": true, "license": "MIT", "dependencies": { @@ -4190,9 +4255,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -4208,9 +4273,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -4249,9 +4314,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -4313,9 +4378,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -4373,9 +4438,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -4391,9 +4456,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -4431,9 +4496,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -4821,9 +4886,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha1-ch1dwQ99W1YJqJF3PUdzF5aTXfs=", + "version": "8.15.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha1-o2CJi8QV7arEbIJB9jg5dbkwuBY=", "dev": true, "license": "MIT", "bin": { @@ -4844,6 +4909,19 @@ "acorn-walk": "^8.0.2" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha1-FuuFC6maBWy3y/6HL/uJcuGMi9c=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4881,9 +4959,9 @@ } }, "node_modules/agent-base/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -5010,6 +5088,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha1-0mCiSwGYQ24TP6JqUkptZfo7Ljc=", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -5064,9 +5155,9 @@ } }, "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha1-UlILiuW1aSFbNU78DKo/4eRaitw=", + "version": "2.1.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha1-dVAKGQMT2Vxk6HHn5ChMasIZ8LE=", "dev": true, "license": "ISC" }, @@ -5280,12 +5371,36 @@ "node": ">= 4.5.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha1-pcw3XWoDwu/IelU/PgsVIt7xSEY=", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha1-qZWH1Ou/vVpuOyG9tdX6OFdnq+Q=", + "version": "1.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha1-JM98zaKPVGW2auwrrGnjKAm/ES8=", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } }, "node_modules/babel-jest": { "version": "29.7.0", @@ -5343,14 +5458,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.12", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", - "integrity": "sha1-ylW77Iqw7e7vPXuP/XUyLiEIeak=", + "version": "0.4.14", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha1-gQG4K3acVog1YRVCSI1GM5XC748=", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.3", + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { @@ -5358,36 +5473,36 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha1-Tk4YLxuzfHumLir4HY3QnfMTRPY=", + "version": "0.13.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha1-u39q7vet3/F/dgKgim0ZoSjDAWQ=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", - "integrity": "sha1-q+sfPxx2LqzjdYf0JUiwi1d4m8g=", + "version": "0.6.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha1-MnUuOKtvZ2e5JlA0e/JqMbFq6MU=", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3" + "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha1-mpKer+zkGWEu9K5PYLGGLrrY7zA=", + "version": "1.2.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha1-IHMNbNx92l2JQByrEKxqMgZ6zeY=", "dev": true, "license": "MIT", "dependencies": { @@ -5408,7 +5523,7 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "node_modules/babel-preset-jest": { @@ -5436,33 +5551,50 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha1-FhQ9Q14e2er9GrhfEribM1ekF0U=", + "version": "2.8.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha1-ez4QvY4fyA2vOLtRaSFnj1ZquJ8=", "dev": true, "license": "Apache-2.0", - "optional": true + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } }, "node_modules/bare-fs": { - "version": "4.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-fs/-/bare-fs-4.0.1.tgz", - "integrity": "sha1-hYRPNNqBnHZ1TVRTI6iyPtNhfHY=", + "version": "4.5.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-fs/-/bare-fs-4.5.2.tgz", + "integrity": "sha1-2A/4qRd+DbSBjnukS5MCwM8HiK8=", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-events": "^2.0.0", + "bare-events": "^2.5.4", "bare-path": "^3.0.0", - "bare-stream": "^2.0.0" + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" }, "engines": { - "bare": ">=1.7.0" + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } } }, "node_modules/bare-os": { - "version": "3.6.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-os/-/bare-os-3.6.0.tgz", - "integrity": "sha1-FGXdfhvr4N7CMAl6I60A99tR+Vc=", + "version": "3.6.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha1-s8T1rV4yLA/Q88KfyX0ZAJ4nluU=", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5482,9 +5614,9 @@ } }, "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha1-u6joeWdMTCf34ngF3wBcFdeiygc=", + "version": "2.7.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha1-W5590KNU0G6C1kYMQmcoU2w114k=", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5504,6 +5636,17 @@ } } }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha1-Su84LvpmKyGApv5MoHpxs5vffKM=", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/base64-js/-/base64-js-1.5.1.tgz", @@ -5534,10 +5677,20 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.14", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", + "integrity": "sha1-O2rwvAMkRbygTeWMqpqHz+khy7M=", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha1-FKR09f/+zKH09AbxwmsY+AAiWsA=", + "version": "5.1.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha1-AOuBKM5TaqaXxFcWxzm/OOjYkPU=", "dev": true, "license": "MIT", "engines": { @@ -5579,24 +5732,24 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha1-GVNDEiHG+1zWPEs21T+rCSjlSMY=", + "version": "1.20.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha1-+OIPTQbKilCnHtMpwV3MrRzcVH8=", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -5663,23 +5816,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "version": "1.1.12", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha1-q5tFRGblqMw6GHvqrVgEEqnFuEM=", "dev": true, "license": "MIT", "dependencies": { @@ -5714,9 +5854,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha1-xrKGWj8IvLhgoOgnOJADuf5obks=", + "version": "4.28.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha1-f1NFlGKMU8YxAQeeJ+QN5JBFapU=", "dev": true, "funding": [ { @@ -5734,10 +5874,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -5882,9 +6023,9 @@ } }, "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -5892,9 +6033,9 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.4.5.tgz", - "integrity": "sha1-9NnwuQ/9urCcnXf18ptCYlF7CVY=", + "version": "10.5.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.5.0.tgz", + "integrity": "sha1-jsA1WRnNMzjChCiiPU8k7MX+c4w=", "dev": true, "license": "ISC", "dependencies": { @@ -5990,6 +6131,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha1-BzapZg9TfjOIgm9EDV7EX3ROqkw=", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -6060,9 +6220,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001704", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/caniuse-lite/-/caniuse-lite-1.0.30001704.tgz", - "integrity": "sha1-ZkT+kJ2SSsOnEl6KCravlbHzKZA=", + "version": "1.0.30001764", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha1-AyBsVkafI2EDuQ+a4QvLi54fYAU=", "dev": true, "funding": [ { @@ -6288,9 +6448,9 @@ } }, "node_modules/chromium-bidi": { - "version": "2.1.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/chromium-bidi/-/chromium-bidi-2.1.2.tgz", - "integrity": "sha1-sHECefmTEo1OC0HIkiCeoJMhfZc=", + "version": "12.0.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/chromium-bidi/-/chromium-bidi-12.0.1.tgz", + "integrity": "sha1-PtsUIKtaUgBMEMIjuShiLBKLTyc=", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6398,9 +6558,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha1-wLKbzTO80HeaE0TCE2BR5q/T2ek=", + "version": "1.0.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha1-zB8B640CKYy8mkN8dMcKtOUhC4A=", "dev": true, "license": "MIT" }, @@ -6505,9 +6665,9 @@ } }, "node_modules/compressing": { - "version": "1.10.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/compressing/-/compressing-1.10.1.tgz", - "integrity": "sha1-RvHK5tihhF6EqVUjm0GbrJHFZqM=", + "version": "1.10.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/compressing/-/compressing-1.10.3.tgz", + "integrity": "sha1-EWPxXSeKFHVCUfU+0O69CFPwvkM=", "dev": true, "license": "MIT", "dependencies": { @@ -6708,9 +6868,9 @@ } }, "node_modules/core-js": { - "version": "3.41.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/core-js/-/core-js-3.41.0.tgz", - "integrity": "sha1-V3FNr7jHUaYJXQKKdCjx+1g0p3Y=", + "version": "3.47.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha1-Q27wdlDhka/rhMJEgbKYvWDrShc=", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6719,13 +6879,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha1-TN/OlfOajyd1m2Z89pPZbl3aPRc=", + "version": "3.47.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/core-js-compat/-/core-js-compat-3.47.0.tgz", + "integrity": "sha1-aYIku9u28uPznezdpBR7Fh43cqM=", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.28.0" }, "funding": { "type": "opencollective", @@ -6788,9 +6948,9 @@ "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha1-wftl+PUBeQHN0slRhkuhhFihBgI=", + "version": "4.1.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha1-hUwpJGdwW2mUduGi3swMijRYgGs=", "dev": true, "license": "MIT", "dependencies": { @@ -6969,9 +7129,9 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha1-DzccfPbEiYzgr7CYNttzzYIBDyI=", + "version": "10.6.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha1-5kmkPjq5U6chkv9Zg4ZeUJ837Zo=", "dev": true, "license": "MIT" }, @@ -7013,9 +7173,9 @@ } }, "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha1-ma7hnrm65VpnMncXtuhI0L93flo=", + "version": "1.7.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha1-NkZh7qPXPz+rpwiSFEIOwvjxPhU=", "dev": true, "license": "MIT", "peerDependencies": { @@ -7241,9 +7401,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1413902", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/devtools-protocol/-/devtools-protocol-0.0.1413902.tgz", - "integrity": "sha1-oPAP6eslqzN6j5ZWop4KGmn0JAE=", + "version": "0.0.1534754", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz", + "integrity": "sha1-dfsElv8TPY1+c9LklgCzf8tPRqk=", "dev": true, "license": "BSD-3-Clause" }, @@ -7397,26 +7557,10 @@ "dev": true, "license": "MIT" }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha1-aauDWLFOiW+AzDnmIIe4hQDDrDs=", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/electron-to-chromium": { - "version": "1.5.116", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/electron-to-chromium/-/electron-to-chromium-1.5.116.tgz", - "integrity": "sha1-t3nXPNDMdTBdEq5PBh1/e87kx2E=", + "version": "1.5.267", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha1-XYTy34zba/5+hzcGuyG9S/tXTcc=", "dev": true, "license": "ISC" }, @@ -7484,18 +7628,18 @@ } }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "version": "1.4.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha1-c0TXEd6kDgt0q8LtSXeHQ8ztsIw=", "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha1-Comj5rbB1LDCoqY3SV58FJ7I2O4=", + "version": "6.6.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha1-oAlSL11WKBCXgbRnIgFDSYWSadI=", "dev": true, "license": "MIT", "dependencies": { @@ -7505,9 +7649,9 @@ "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" + "ws": "~8.18.3" }, "engines": { "node": ">=10.2.0" @@ -7524,9 +7668,9 @@ } }, "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.3.7.tgz", - "integrity": "sha1-h5RbQVGgEddtlaGY1xEchlw2ClI=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -7542,9 +7686,9 @@ } }, "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.17.1.tgz", - "integrity": "sha1-kpPaUwu1SP68lTcdkPnIeHJ9kZs=", + "version": "8.18.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.18.3.tgz", + "integrity": "sha1-tWuIq//eYnkcY5FwQAyT3LDJVHI=", "dev": true, "license": "MIT", "engines": { @@ -7594,9 +7738,9 @@ } }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/entities/-/entities-4.5.0.tgz", - "integrity": "sha1-XSaOpecRPsdMTQM7eepaNaSI+0g=", + "version": "6.0.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/entities/-/entities-6.0.1.tgz", + "integrity": "sha1-wow0pDN5yn9h0HQTCy9fcCCjBpQ=", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -7617,9 +7761,9 @@ } }, "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha1-JtrF21RBjypMEVkVOgsq6YCDiq4=", + "version": "7.21.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha1-BKJRvnn5JUhUHzfRPItvIpQMO64=", "dev": true, "license": "MIT", "bin": { @@ -7650,9 +7794,9 @@ } }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "version": "1.3.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha1-s6jYu2+S7swWKePifTyGB6ijJBQ=", "dev": true, "license": "MIT", "dependencies": { @@ -7680,9 +7824,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha1-2kn1h/2eaO4kBP5OJWwMfTqBviE=", + "version": "2.0.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha1-9lfNepRI3N2pwHCjy3Xl3B6F9bE=", "dev": true, "license": "MIT" }, @@ -7881,9 +8025,9 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -7899,9 +8043,9 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -7956,9 +8100,9 @@ "license": "Python-2.0" }, "node_modules/eslint/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -8017,26 +8161,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/globals/-/globals-13.24.0.tgz", - "integrity": "sha1-hDKhnXjODB6DOUnDats0VAC7EXE=", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha1-wftl+PUBeQHN0slRhkuhhFihBgI=", + "version": "4.1.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha1-hUwpJGdwW2mUduGi3swMijRYgGs=", "dev": true, "license": "MIT", "dependencies": { @@ -8078,19 +8206,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/espree/-/espree-9.6.1.tgz", @@ -8124,9 +8239,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha1-kUGSNPgE2FKoLc7sPhbNwiz52uc=", + "version": "1.7.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha1-CNBI8mHw3e21uulfRoCUY9nJSW0=", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -8222,6 +8337,16 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha1-tWqE/WEbZhDgotDwn4D9+THi3+Y=", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource": { "version": "2.0.2", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/eventsource/-/eventsource-2.0.2.tgz", @@ -8282,9 +8407,9 @@ } }, "node_modules/exponential-backoff": { - "version": "3.1.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/exponential-backoff/-/exponential-backoff-3.1.2.tgz", - "integrity": "sha1-qPJq25a/eOjNitEDeSjV5cBnnZE=", + "version": "3.1.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha1-Uc+SwcBJPHZgU/nTq+5ENMJE0vY=", "dev": true, "license": "Apache-2.0" }, @@ -8317,9 +8442,9 @@ } }, "node_modules/extract-zip/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -8409,9 +8534,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha1-iPEwt3z66iN41Wv5cN6iElemh0g=", + "version": "3.1.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha1-Zu7P9sdkwN+bdi5iyn7c+1O07fo=", "dev": true, "funding": [ { @@ -8436,9 +8561,9 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha1-1Q6rqAPIhGqIPBZJKCHrzSzaVfU=", + "version": "1.20.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha1-ynUKENySW8ixiDn9ID4+9LPO1nU=", "dev": true, "license": "ISC", "dependencies": { @@ -8497,39 +8622,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha1-94l4oelEd1/55i50RCTyFeWDUrU=", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha1-HPy4z1Ui6mmVLNKvla4JR38SKpY=", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/filemanager-webpack-plugin": { "version": "8.0.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/filemanager-webpack-plugin/-/filemanager-webpack-plugin-8.0.0.tgz", @@ -8726,9 +8818,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha1-pgT6EORDv5jKlCKNnuvMLoosjuE=", + "version": "1.15.11", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha1-d31z1yqS+OxNLkEOtHNSpWuOg0A=", "dev": true, "funding": [ { @@ -8746,6 +8838,22 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha1-1lBogCeCaSD+6wr3R+57lCGkHUc=", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/foreground-child/-/foreground-child-3.3.1.tgz", @@ -8777,15 +8885,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha1-Ncq73TDDznPessQtPI0+2cpReUw=", + "version": "4.0.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha1-tJ5IhYBF/0y/awPhgFzrytNnkFM=", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -8793,9 +8902,9 @@ } }, "node_modules/fp-ts": { - "version": "2.16.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fp-ts/-/fp-ts-2.16.9.tgz", - "integrity": "sha1-mWKPxeC7O0MsShbY9EVSRzgLroo=", + "version": "2.16.11", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fp-ts/-/fp-ts-2.16.11.tgz", + "integrity": "sha1-gxoQUUv04irfEgZXMvxaIMhdliM=", "dev": true, "license": "MIT" }, @@ -8996,9 +9105,9 @@ } }, "node_modules/get-uri": { - "version": "6.0.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/get-uri/-/get-uri-6.0.4.tgz", - "integrity": "sha1-barunhL5dZ4Z5VujE5Vog+9Q4Kc=", + "version": "6.0.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha1-cUiSqkqHHbZxq8U5Xl6UR7wwahY=", "dev": true, "license": "MIT", "dependencies": { @@ -9011,9 +9120,9 @@ } }, "node_modules/get-uri/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -9069,9 +9178,9 @@ "license": "BSD-2-Clause" }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -9111,9 +9220,9 @@ } }, "node_modules/global-agent/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -9124,13 +9233,19 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/globals/-/globals-11.12.0.tgz", - "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", + "version": "13.24.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/globals/-/globals-13.24.0.tgz", + "integrity": "sha1-hDKhnXjODB6DOUnDats0VAC7EXE=", "dev": true, "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -9252,6 +9367,28 @@ "dev": true, "license": "MIT" }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha1-QcQsGLG+I2VDkYjHfGr65xwM2ek=", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/has-flag/-/has-flag-4.0.0.tgz", @@ -9397,32 +9534,36 @@ "license": "MIT" }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha1-q+AvyymFRgvwMjvmZENuw0dqbVo=", + "version": "4.2.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha1-IF9Ntk+FYrdqT/kjWqUnmDmgndU=", "license": "BSD-2-Clause" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha1-t3dKFIbvc892Z6ya4IWMASxXudM=", + "version": "2.0.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha1-NtL2W8kJyHkAGN02+02T2myq4Gs=", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha1-VcsADM8dSHKL0jxoWgY5mM8aG2M=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha1-j3XuzvdlteHPzcCA2llAntQk44I=", "dev": true, "license": "MIT", "engines": { @@ -9460,9 +9601,9 @@ } }, "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -9505,9 +9646,9 @@ } }, "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -9599,9 +9740,9 @@ } }, "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -9769,26 +9910,15 @@ } }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha1-EXqWCBmwh4DDvR8U7zwcwdPz6lo=", + "version": "10.1.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha1-2Nz/s00OAuskFCdESm4j9bBZWqQ=", "dev": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha1-SRS5A6L4toXRf994pw6RfocuREo=", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -9825,6 +9955,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha1-O8KoXqdC2eNiBdys3XLKH9xRsFU=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-core-module/-/is-core-module-2.16.1.tgz", @@ -10015,6 +10158,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha1-S/tKRbYc7oOlpG+6d45OjVnAzgs=", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-wsl/-/is-wsl-2.2.0.tgz", @@ -10122,9 +10281,9 @@ } }, "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -10140,9 +10299,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha1-2u0SueHcpRjhXAVuHlN+dBKA+gs=", + "version": "3.2.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha1-y0U1FitXhKpiPO4hpyUs8sgHrJM=", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10169,25 +10328,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jake/-/jake-10.9.2.tgz", - "integrity": "sha1-auSH5qaa/sOl4WdiiZa1nzWuK38=", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jasmine": { "version": "3.99.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jasmine/-/jasmine-3.99.0.tgz", @@ -11216,9 +11356,9 @@ "license": "MIT" }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -11385,9 +11525,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc=", + "version": "3.14.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha1-d0hc4d1/M8Bh/RsW7OojtV/LBLA=", "dev": true, "license": "MIT", "dependencies": { @@ -11398,13 +11538,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=", - "dev": true, - "license": "MIT" - }, "node_modules/jsdoc-type-pratt-parser": { "version": "4.0.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", @@ -11462,9 +11595,9 @@ } }, "node_modules/jsdom/node_modules/ws": { - "version": "8.18.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.18.1.tgz", - "integrity": "sha1-6hMdN4Th39/5GtsKShFrEnUV48s=", + "version": "8.19.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.19.0.tgz", + "integrity": "sha1-3cK9+lua2GAgT1pypIY6iJX9jIs=", "dev": true, "license": "MIT", "engines": { @@ -11544,9 +11677,9 @@ } }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=", + "version": "6.2.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha1-fCZb0bZd5pd0eDAAh8mfHIQ4P2I=", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -12065,13 +12198,17 @@ "license": "MIT" }, "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha1-wbShY7mfYUgwNTsWdV5xSawjFOE=", + "version": "4.3.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha1-bHbtKbDMzprzeSCCmfB/h23nN+M=", "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -12300,9 +12437,9 @@ } }, "node_modules/log4js/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -12375,13 +12512,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha1-RQpElnPSRg5bvPupphkWoXFMdFM=", + "version": "0.30.21", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha1-VnY+wJoPqAkd8nh5/ZTRkHjADZE=", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-dir": { @@ -12401,9 +12538,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -12566,9 +12703,9 @@ } }, "node_modules/make-fetch-happen/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -12635,9 +12772,9 @@ } }, "node_modules/marky": { - "version": "1.2.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/marky/-/marky-1.2.5.tgz", - "integrity": "sha1-VXlraIy9cjkNLTmeqvGDLJQT48A=", + "version": "1.3.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/marky/-/marky-1.3.0.tgz", + "integrity": "sha1-QitjsLr2UCLwLtphojjszbvBSZc=", "dev": true, "license": "Apache-2.0" }, @@ -13291,9 +13428,9 @@ } }, "node_modules/node-gyp/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -13311,9 +13448,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha1-nkRaUpUJUexNF32EOvNwtBHK8xQ=", + "version": "2.0.27", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha1-7tylGSBc8g9lD2HVawcNsREjHk4=", "dev": true, "license": "MIT" }, @@ -13350,9 +13487,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -13411,9 +13548,9 @@ } }, "node_modules/npm-install-checks/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -13450,9 +13587,9 @@ } }, "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -13492,9 +13629,9 @@ } }, "node_modules/npm-pick-manifest/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -13618,9 +13755,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.18", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/nwsapi/-/nwsapi-2.2.18.tgz", - "integrity": "sha1-PE15J+HvTQQtMZQ47P2mzYG37kE=", + "version": "2.2.23", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha1-WXEsOojm3iuwtszBBwOXJnAZz2w=", "dev": true, "license": "MIT" }, @@ -13848,9 +13985,9 @@ } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha1-KUNeuCG8QZRjOluJ5bxHA7r8JaE=", + "version": "7.1.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha1-48121MVI7oldPD/Y3B9sW5Ay56g=", "dev": true, "license": "MIT", "engines": { @@ -13858,9 +13995,9 @@ } }, "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -14023,13 +14160,13 @@ } }, "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha1-iSj1WRXmEl9DDMRDCXZb8XVWozo=", + "version": "7.3.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha1-1+Ik+nI5nHoXUJn0X8KtAksF7AU=", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.5.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -14165,9 +14302,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha1-d8dCkx6PO4gglGx2zQwfE3MNHas=", + "version": "4.0.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha1-eWx2E20e6tcV2x57rXhd7daVoEI=", "dev": true, "license": "MIT", "engines": { @@ -14191,9 +14328,9 @@ } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha1-MBiuMuz8/2wpuiJny/IRZqwfNrk=", + "version": "4.0.7", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha1-ZDtKGMQlfIplEEtz8wSc6aChXiI=", "dev": true, "license": "MIT", "engines": { @@ -14213,6 +14350,16 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha1-k+NYK8DlQmWG2dB7ee5A/IQd5K4=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14257,9 +14404,9 @@ } }, "node_modules/pretty-format/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha1-Mo+4nkYQnsvbcMKV2W/y9G39Abk=", + "version": "15.0.20", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/yargs/-/yargs-15.0.20.tgz", + "integrity": "sha1-bQChJMn3V0J9TKPLyH2up3gFPGg=", "dev": true, "license": "MIT", "dependencies": { @@ -14358,9 +14505,9 @@ } }, "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha1-KUNeuCG8QZRjOluJ5bxHA7r8JaE=", + "version": "7.1.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha1-48121MVI7oldPD/Y3B9sW5Ay56g=", "dev": true, "license": "MIT", "engines": { @@ -14368,9 +14515,9 @@ } }, "node_modules/proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -14474,9 +14621,9 @@ } }, "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/pump/-/pump-3.0.2.tgz", - "integrity": "sha1-g28+3WvC7lmSVskk/+DYhXPdy/g=", + "version": "3.0.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/pump/-/pump-3.0.3.tgz", + "integrity": "sha1-FR2XnxopZo3AAl7FiaRVtTKCJo0=", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -14491,18 +14638,18 @@ "license": "MIT" }, "node_modules/puppeteer": { - "version": "24.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/puppeteer/-/puppeteer-24.4.0.tgz", - "integrity": "sha1-+0WmfnL05uNNuPQE72HN1CCZ5uY=", + "version": "24.34.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/puppeteer/-/puppeteer-24.34.0.tgz", + "integrity": "sha1-Bh9ul86VEYY+yDzW8XonJTxotek=", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.8.0", - "chromium-bidi": "2.1.2", + "@puppeteer/browsers": "2.11.0", + "chromium-bidi": "12.0.1", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1413902", - "puppeteer-core": "24.4.0", + "devtools-protocol": "0.0.1534754", + "puppeteer-core": "24.34.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -14554,9 +14701,9 @@ "license": "ISC" }, "node_modules/puppeteer-core/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -14630,9 +14777,9 @@ } }, "node_modules/puppeteer-core/node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha1-+zuIQ6JrbxOgjmBveSKHXrH7v5I=", + "version": "2.1.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha1-gAgk2/TvBt7Zr+pKyv5xxnx2uTA=", "dev": true, "license": "MIT", "dependencies": { @@ -14665,9 +14812,9 @@ } }, "node_modules/puppeteer/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -14683,27 +14830,28 @@ } }, "node_modules/puppeteer/node_modules/puppeteer-core": { - "version": "24.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/puppeteer-core/-/puppeteer-core-24.4.0.tgz", - "integrity": "sha1-owHFg0T+k5tIdwRZNoHqn5E/5vg=", + "version": "24.34.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/puppeteer-core/-/puppeteer-core-24.34.0.tgz", + "integrity": "sha1-AMf2O0qD1Mouxeo6I0WI+yznyZQ=", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.8.0", - "chromium-bidi": "2.1.2", - "debug": "^4.4.0", - "devtools-protocol": "0.0.1413902", + "@puppeteer/browsers": "2.11.0", + "chromium-bidi": "12.0.1", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1534754", "typed-query-selector": "^2.12.0", - "ws": "^8.18.1" + "webdriver-bidi-protocol": "0.3.10", + "ws": "^8.18.3" }, "engines": { "node": ">=18" } }, "node_modules/puppeteer/node_modules/ws": { - "version": "8.18.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.18.1.tgz", - "integrity": "sha1-6hMdN4Th39/5GtsKShFrEnUV48s=", + "version": "8.19.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.19.0.tgz", + "integrity": "sha1-3cK9+lua2GAgT1pypIY6iJX9jIs=", "dev": true, "license": "MIT", "engines": { @@ -14750,13 +14898,13 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/qs/-/qs-6.13.0.tgz", - "integrity": "sha1-bKO9WEOffiRWVXmJl3h7DYilGQY=", + "version": "6.14.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/qs/-/qs-6.14.1.tgz", + "integrity": "sha1-pB2FudOQLzHSeGF5BQYpSIGHEVk=", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -14843,16 +14991,16 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha1-mf69g7kOCJdQh+jx+UGaFJNmtoo=", + "version": "2.5.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha1-EcZlDudwp94bSU8ZeSfeDJI4IuI=", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" @@ -14906,9 +15054,9 @@ } }, "node_modules/read-package-json/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -14916,9 +15064,9 @@ } }, "node_modules/read-package-json/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.4.5.tgz", - "integrity": "sha1-9NnwuQ/9urCcnXf18ptCYlF7CVY=", + "version": "10.5.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.5.0.tgz", + "integrity": "sha1-jsA1WRnNMzjChCiiPU8k7MX+c4w=", "dev": true, "license": "ISC", "dependencies": { @@ -14997,9 +15145,9 @@ } }, "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -15066,9 +15214,9 @@ "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha1-Ym4534w3Izjqm4Ao0fmdw/2cPbA=", + "version": "10.2.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha1-qhE4ErqJm2MGWMdiNGa+ceH4b2Y=", "dev": true, "license": "MIT", "dependencies": { @@ -15078,36 +15226,19 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha1-NWreECY/aF3aElEAzYYsHbiVMn8=", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha1-W7rli1IgmOvfCbyi+Dg4kpABx6Q=", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha1-DlGQ155UK/KUlV3Mq64E08fVOCY=", + "version": "6.4.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha1-NYDODE+u3vWZ7MsUZhJDa2KhduU=", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" @@ -15121,31 +15252,18 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha1-DoRt9sZTBYZCk3feVuBHVYOwiNw=", + "version": "0.13.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha1-Afg1EzXPeJjUNoa8dNLdcchH7MA=", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha1-u4sJpll7pCZCXy5KByRcPQC5ND4=", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/require-directory/-/require-directory-2.1.1.tgz", @@ -15173,13 +15291,13 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha1-tmPoP/sJu/I4aURza6roAwKbizk=", + "version": "1.22.11", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha1-qthXzh/7i/qbCxrCnxFWOD9owmI=", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -15313,9 +15431,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -15323,9 +15441,9 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.4.5.tgz", - "integrity": "sha1-9NnwuQ/9urCcnXf18ptCYlF7CVY=", + "version": "10.5.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/glob/-/glob-10.5.0.tgz", + "integrity": "sha1-jsA1WRnNMzjChCiiPU8k7MX+c4w=", "dev": true, "license": "ISC", "dependencies": { @@ -15395,13 +15513,13 @@ "license": "BSD-3-Clause" }, "node_modules/rollup": { - "version": "4.35.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/rollup/-/rollup-4.35.0.tgz", - "integrity": "sha1-dslduheled9MAMOVWu0yql1Nxm0=", + "version": "4.55.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha1-TsGCgovkQGSOfuZSDcNenyDgUUQ=", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -15411,25 +15529,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.35.0", - "@rollup/rollup-android-arm64": "4.35.0", - "@rollup/rollup-darwin-arm64": "4.35.0", - "@rollup/rollup-darwin-x64": "4.35.0", - "@rollup/rollup-freebsd-arm64": "4.35.0", - "@rollup/rollup-freebsd-x64": "4.35.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", - "@rollup/rollup-linux-arm-musleabihf": "4.35.0", - "@rollup/rollup-linux-arm64-gnu": "4.35.0", - "@rollup/rollup-linux-arm64-musl": "4.35.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", - "@rollup/rollup-linux-riscv64-gnu": "4.35.0", - "@rollup/rollup-linux-s390x-gnu": "4.35.0", - "@rollup/rollup-linux-x64-gnu": "4.35.0", - "@rollup/rollup-linux-x64-musl": "4.35.0", - "@rollup/rollup-win32-arm64-msvc": "4.35.0", - "@rollup/rollup-win32-ia32-msvc": "4.35.0", - "@rollup/rollup-win32-x64-msvc": "4.35.0", + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" } }, @@ -15603,9 +15727,9 @@ } }, "node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha1-O2afBPcf8t+1q6fOLVqdebNWIsA=", + "version": "4.3.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha1-WxhQkS+jHfkHFpY9RdkSH9/An0Y=", "dev": true, "license": "MIT", "dependencies": { @@ -15698,9 +15822,9 @@ } }, "node_modules/selenium-standalone/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -15797,11 +15921,29 @@ "license": "ISC" }, "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha1-MBbxUAciAt++kPre4FNXPMidKUM=", + "version": "2.7.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha1-zNCGc6muXS5E6iot4lCJ5nx+32g=", "license": "MIT" }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha1-qscjFBmOrtl1z3eyw7a4gGleVEk=", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -16065,16 +16207,16 @@ "license": "0BSD" }, "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha1-+g6v+WXMl/30JF6NR5RhhFn3VYo=", + "version": "4.8.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha1-ymuhQxxpUy4eCm9Jbe6+tgHbxN8=", "dev": true, "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", - "debug": "~4.3.2", + "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" @@ -16084,20 +16226,20 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha1-x6H5xwPXdWhEdRtv+av8F4BmQII=", + "version": "2.5.6", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha1-xpf2CdNqZ2pGdJeCJ0YH2N9Swdg=", "dev": true, "license": "MIT", "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" + "debug": "~4.4.1", + "ws": "~8.18.3" } }, "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.3.7.tgz", - "integrity": "sha1-h5RbQVGgEddtlaGY1xEchlw2ClI=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -16113,9 +16255,9 @@ } }, "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.17.1.tgz", - "integrity": "sha1-kpPaUwu1SP68lTcdkPnIeHJ9kZs=", + "version": "8.18.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ws/-/ws-8.18.3.tgz", + "integrity": "sha1-tWuIq//eYnkcY5FwQAyT3LDJVHI=", "dev": true, "license": "MIT", "engines": { @@ -16135,23 +16277,23 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha1-yAaWbPcnBgHkdGnd7sMPvf2kTIM=", + "version": "4.2.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha1-P0G402kSmpMmjyq+y6lLUpKFAJk=", "dev": true, "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.3.7.tgz", - "integrity": "sha1-h5RbQVGgEddtlaGY1xEchlw2ClI=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -16167,9 +16309,9 @@ } }, "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.3.7.tgz", - "integrity": "sha1-h5RbQVGgEddtlaGY1xEchlw2ClI=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -16185,13 +16327,13 @@ } }, "node_modules/socks": { - "version": "2.8.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socks/-/socks-2.8.4.tgz", - "integrity": "sha1-BxCXVc3U2gMmm9pHJbqgYatW1cw=", + "version": "2.8.7", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/socks/-/socks-2.8.7.tgz", + "integrity": "sha1-4vsdmmA63XUFCiBn24w4GgtWaeo=", "dev": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -16215,9 +16357,9 @@ } }, "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -16283,9 +16425,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha1-bW6YDJ3ytvyQU0OjstcCpiOVNsM=", + "version": "3.0.22", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha1-q/Wgim9dcnlVm2afR/CkPo80ZO8=", "dev": true, "license": "CC0-1.0" }, @@ -16388,9 +16530,9 @@ } }, "node_modules/streamroller/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -16441,17 +16583,15 @@ } }, "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha1-zXteV8larvD/myrveQWvpi7G5Kc=", + "version": "2.23.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha1-fQ89ANSmxd5XKK7NZCK0AI1m/Qs=", "dev": true, "license": "MIT", "dependencies": { + "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" } }, "node_modules/strict-uri-encode": { @@ -16640,9 +16780,9 @@ } }, "node_modules/tar-fs": { - "version": "3.0.9", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tar-fs/-/tar-fs-3.0.9.tgz", - "integrity": "sha1-1XB5PGNw1weJJsQfpCKJFWagthc=", + "version": "3.1.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha1-TxZOWftg8QPUcjYHMejGu0p/6e8=", "dev": true, "license": "MIT", "dependencies": { @@ -16716,14 +16856,14 @@ "license": "ISC" }, "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/terser/-/terser-5.39.0.tgz", - "integrity": "sha1-DoIDPtV7Pd8flnCNEjzKcX2Gyjo=", + "version": "5.44.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/terser/-/terser-5.44.1.tgz", + "integrity": "sha1-45HpIXXCmbjChK1t7WCeNzA7Cpw=", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -16735,9 +16875,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha1-kDHUjlerJ1Z/AqzoXH1pDbZsPgY=", + "version": "5.3.16", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha1-dB5EjMP5PYAm6+T3755K+s/VYzA=", "dev": true, "license": "MIT", "dependencies": { @@ -16872,9 +17012,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha1-63g8wivB6L69BnFHbUbqTrMqea4=", + "version": "0.2.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha1-sGvNI/DzyDV7QmiRcm0WAVq/2Pg=", "dev": true, "license": "MIT", "engines": { @@ -16889,9 +17029,24 @@ "license": "BSD-3-Clause" }, "node_modules/to-buffer": { - "version": "1.1.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", + "version": "1.2.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha1-/+We91Iq2gotHLXf4Du4q8PNwTM=", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha1-ivHkwSISRMxiRZ+vOJQNTmRKVyM=", "dev": true, "license": "MIT" }, @@ -16988,20 +17143,20 @@ } }, "node_modules/ts-jest": { - "version": "29.2.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ts-jest/-/ts-jest-29.2.6.tgz", - "integrity": "sha1-31Pt+LcvuJ3gMs+jEKvzdYKFHZo=", + "version": "29.4.6", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha1-Uct8Ez8ic5aBi3EpetdAm7dxBuk=", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", - "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", + "handlebars": "^4.7.8", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.1", + "semver": "^7.7.3", + "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -17012,10 +17167,11 @@ }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { @@ -17033,13 +17189,16 @@ }, "esbuild": { "optional": true + }, + "jest-util": { + "optional": true } } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -17049,6 +17208,19 @@ "node": ">=10" } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha1-auHI5XMSc8K/H1itOcuuLJGkbFg=", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-loader": { "version": "8.4.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ts-loader/-/ts-loader-8.4.0.tgz", @@ -17071,9 +17243,9 @@ } }, "node_modules/ts-loader/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -17166,9 +17338,9 @@ } }, "node_modules/tuf-js/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -17282,9 +17454,9 @@ } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha1-0mCiSwGYQ24TP6JqUkptZfo7Ljc=", + "version": "0.20.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -17308,6 +17480,21 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha1-pyOVRQpIaewDP9VJNxtHrzou5TY=", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/typed-query-selector": { "version": "2.12.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typed-query-selector/-/typed-query-selector-2.12.0.tgz", @@ -17316,9 +17503,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha1-gXCzcC90t52y5aliB8FeZYB5meQ=", + "version": "5.9.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha1-W09Z4VMQqxeiFvXWz1PuR27eZw8=", "dev": true, "license": "Apache-2.0", "bin": { @@ -17330,9 +17517,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.40", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ua-parser-js/-/ua-parser-js-0.7.40.tgz", - "integrity": "sha1-yH2Dt7slgi7Ppjl6DaWQOTTqFWI=", + "version": "0.7.41", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/ua-parser-js/-/ua-parser-js-0.7.41.tgz", + "integrity": "sha1-n23uWMOJ6K+rq6YqSi3CLttppFI=", "dev": true, "funding": [ { @@ -17356,6 +17543,20 @@ "node": "*" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha1-gjFem7xvKyWIiFis0f/4RBA1t38=", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -17368,9 +17569,9 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha1-gXG/IsH1iNFVTVW/IEvGJK84hDM=", + "version": "7.16.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha1-/8zf82rqSITL/OmnUKBYAiT1ikY=", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -17398,9 +17599,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha1-oEAa7nJxRZj3ObaLEE5P46DLPHE=", + "version": "2.2.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha1-Zaet+thXTCGYkOIZKFzkxk7Wfqo=", "dev": true, "license": "MIT", "engines": { @@ -17408,9 +17609,9 @@ } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha1-Q9QeO+aYvUk++REHfJsTH4J+jM0=", + "version": "2.2.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha1-MB1PikPSt1yXrfrYfJ3VNQyUddE=", "dev": true, "license": "MIT", "engines": { @@ -17463,9 +17664,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha1-NIN33SRSFvnnBg/1CxWht0C3VCA=", + "version": "1.2.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha1-ZNdttYcTE2rL60xJEUNmzGzC6A0=", "dev": true, "funding": [ { @@ -17670,9 +17871,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha1-L+6u1nQS58MxhOWnnKc4+9OFZNo=", + "version": "2.5.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/watchpack/-/watchpack-2.5.0.tgz", + "integrity": "sha1-+hFdXMqkvzqllPWGJXwLxHaJOf0=", "dev": true, "license": "MIT", "dependencies": { @@ -17701,6 +17902,13 @@ "node": ">=10.0.0" } }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.3.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.10.tgz", + "integrity": "sha1-Q3QFVk/34gA3FGj08euh/1U34Rk=", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webdriverio": { "version": "6.12.1", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webdriverio/-/webdriverio-6.12.1.tgz", @@ -17768,19 +17976,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webdriverio/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -17792,35 +17987,37 @@ } }, "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha1-RK4ZqPK6l1N5eCRgcvuJ0Q0fvRc=", + "version": "5.104.1", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webpack/-/webpack-5.104.1.tgz", + "integrity": "sha1-lL1B612/Buk74WW6i+QbgmDU+xo=", "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.17.4", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -17912,9 +18109,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha1-LU2quEUf1LJAzCcFX/agwszqDN4=", + "version": "3.3.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha1-1L9/mQlnXXoHD/FNDvKk88mCxyM=", "dev": true, "license": "MIT", "engines": { @@ -17922,9 +18119,9 @@ } }, "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha1-coqwgvi3toNt5R8WN6q107lWj68=", + "version": "5.18.4", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha1-wi0zBV85UgNc5qFEzgkkR8Ul+Cg=", "dev": true, "license": "MIT", "dependencies": { @@ -17936,13 +18133,17 @@ } }, "node_modules/webpack/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha1-GWenPvQGCoLxKrlq+G1S/bdu7KA=", + "version": "2.3.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha1-fj6m1coxuo4Hi1YPDYPOmhSqi+Y=", "dev": true, "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/whatwg-encoding": { @@ -18010,6 +18211,28 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha1-3wOELocLa4jhF1JKSzZLb8aJ+VY=", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/wide-align/-/wide-align-1.1.5.tgz", @@ -18050,6 +18273,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -18319,9 +18549,9 @@ } }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/zod/-/zod-3.24.2.tgz", - "integrity": "sha1-jvp0EmKHxnXpL0aHHPyNFcNDcrM=", + "version": "3.25.76", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/zod/-/zod-3.25.76.tgz", + "integrity": "sha1-JoQcP2/SKmonYOfMtxkXl2hHHjQ=", "dev": true, "license": "MIT", "funding": { @@ -18388,7 +18618,7 @@ }, "src/JSInterop/Microsoft.JSInterop.JS/src": { "name": "@microsoft/dotnet-js-interop", - "version": "10.0.0-dev", + "version": "11.0.0-dev", "license": "MIT", "devDependencies": { "@babel/core": "^7.23.6", @@ -18621,9 +18851,9 @@ } }, "src/JSInterop/Microsoft.JSInterop.JS/src/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=", + "version": "2.0.2", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", "dev": true, "license": "MIT", "dependencies": { @@ -18641,9 +18871,9 @@ } }, "src/JSInterop/Microsoft.JSInterop.JS/src/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.0.tgz", - "integrity": "sha1-Kz8q6i/+t3ZHdGAmc3fchxD6uoo=", + "version": "4.4.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", "dev": true, "license": "MIT", "dependencies": { @@ -18715,9 +18945,9 @@ } }, "src/JSInterop/Microsoft.JSInterop.JS/src/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.1.tgz", - "integrity": "sha1-q9UJjYKxjGyB9gdP8mR/0+ciDJ8=", + "version": "7.7.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/semver/-/semver-7.7.3.tgz", + "integrity": "sha1-S19BQ9AHYzqNxnHNCm75FHuLuUY=", "dev": true, "license": "ISC", "bin": { @@ -18822,9 +19052,9 @@ "license": "MIT" }, "src/SignalR/clients/ts/FunctionalTests/node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha1-gXCzcC90t52y5aliB8FeZYB5meQ=", + "version": "5.9.3", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha1-W09Z4VMQqxeiFvXWz1PuR27eZw8=", "dev": true, "license": "Apache-2.0", "bin": { @@ -18895,7 +19125,7 @@ }, "src/SignalR/clients/ts/signalr": { "name": "@microsoft/signalr", - "version": "10.0.0-dev", + "version": "11.0.0-dev", "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -18907,10 +19137,10 @@ }, "src/SignalR/clients/ts/signalr-protocol-msgpack": { "name": "@microsoft/signalr-protocol-msgpack", - "version": "10.0.0-dev", + "version": "11.0.0-dev", "license": "MIT", "dependencies": { - "@microsoft/signalr": ">=10.0.0-dev", + "@microsoft/signalr": ">=11.0.0-dev", "@msgpack/msgpack": "^2.7.0" } }, diff --git a/src/aspnetcore/src/Analyzers/Analyzers/test/Microsoft.AspNetCore.Analyzers.Test.csproj b/src/aspnetcore/src/Analyzers/Analyzers/test/Microsoft.AspNetCore.Analyzers.Test.csproj index 9290cc9741f5..d6516e27d3b2 100644 --- a/src/aspnetcore/src/Analyzers/Analyzers/test/Microsoft.AspNetCore.Analyzers.Test.csproj +++ b/src/aspnetcore/src/Analyzers/Analyzers/test/Microsoft.AspNetCore.Analyzers.Test.csproj @@ -17,8 +17,19 @@ --> + + + + + + + + + + + diff --git a/src/aspnetcore/src/Antiforgery/samples/MinimalFormSample/MinimalFormSample.csproj b/src/aspnetcore/src/Antiforgery/samples/MinimalFormSample/MinimalFormSample.csproj index 5f2b3422a71e..072b845dfab7 100644 --- a/src/aspnetcore/src/Antiforgery/samples/MinimalFormSample/MinimalFormSample.csproj +++ b/src/aspnetcore/src/Antiforgery/samples/MinimalFormSample/MinimalFormSample.csproj @@ -10,8 +10,10 @@ + + diff --git a/src/aspnetcore/src/Antiforgery/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Antiforgery/src/PublicAPI.Shipped.txt index a83f5d0b56fc..45fa400154f1 100644 --- a/src/aspnetcore/src/Antiforgery/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Antiforgery/src/PublicAPI.Shipped.txt @@ -18,8 +18,8 @@ Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet.FormFieldName.get -> string Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet.HeaderName.get -> string? Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet.RequestToken.get -> string? Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException -Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException.AntiforgeryValidationException(string! message) -> void Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException.AntiforgeryValidationException(string! message, System.Exception? innerException) -> void +Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException.AntiforgeryValidationException(string! message) -> void Microsoft.AspNetCore.Antiforgery.IAntiforgery Microsoft.AspNetCore.Antiforgery.IAntiforgery.GetAndStoreTokens(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet! Microsoft.AspNetCore.Antiforgery.IAntiforgery.GetTokens(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet! @@ -35,6 +35,6 @@ Microsoft.AspNetCore.Antiforgery.RequireAntiforgeryTokenAttribute.RequiresValida Microsoft.AspNetCore.Builder.AntiforgeryApplicationBuilderExtensions Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions static Microsoft.AspNetCore.Builder.AntiforgeryApplicationBuilderExtensions.UseAntiforgery(this Microsoft.AspNetCore.Builder.IApplicationBuilder! builder) -> Microsoft.AspNetCore.Builder.IApplicationBuilder! -static Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions.AddAntiforgery(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions.AddAntiforgery(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! setupAction) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions.AddAntiforgery(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static readonly Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.DefaultCookiePrefix -> string! diff --git a/src/aspnetcore/src/Antiforgery/test/Microsoft.AspNetCore.Antiforgery.Test.csproj b/src/aspnetcore/src/Antiforgery/test/Microsoft.AspNetCore.Antiforgery.Test.csproj index a099c7a07ba8..68d4d6f6cf45 100644 --- a/src/aspnetcore/src/Antiforgery/test/Microsoft.AspNetCore.Antiforgery.Test.csproj +++ b/src/aspnetcore/src/Antiforgery/test/Microsoft.AspNetCore.Antiforgery.Test.csproj @@ -7,7 +7,11 @@ + + + + diff --git a/src/aspnetcore/src/Assets/build/Microsoft.AspNetCore.App.Internal.Assets.targets b/src/aspnetcore/src/Assets/build/Microsoft.AspNetCore.App.Internal.Assets.targets index 12b2cf3736b3..5cf826b35dd9 100644 --- a/src/aspnetcore/src/Assets/build/Microsoft.AspNetCore.App.Internal.Assets.targets +++ b/src/aspnetcore/src/Assets/build/Microsoft.AspNetCore.App.Internal.Assets.targets @@ -10,7 +10,7 @@ $(MSBuildThisFileDirectory)..\_framework - + <_FrameworkStaticWebAssetCandidate Include="$(BlazorFrameworkStaticWebAssetRoot)\blazor.web.js"> _framework/blazor.web.js diff --git a/src/aspnetcore/src/Azure/AzureAppServices.HostingStartup/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj b/src/aspnetcore/src/Azure/AzureAppServices.HostingStartup/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj index dbc464028962..6aa6057c14a4 100644 --- a/src/aspnetcore/src/Azure/AzureAppServices.HostingStartup/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj +++ b/src/aspnetcore/src/Azure/AzureAppServices.HostingStartup/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj @@ -12,6 +12,7 @@ + diff --git a/src/aspnetcore/src/Azure/AzureAppServicesIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj b/src/aspnetcore/src/Azure/AzureAppServicesIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj index a644cec712c6..41fe6ff58c88 100644 --- a/src/aspnetcore/src/Azure/AzureAppServicesIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj +++ b/src/aspnetcore/src/Azure/AzureAppServicesIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj @@ -11,6 +11,7 @@ + diff --git a/src/aspnetcore/src/Azure/AzureAppServicesIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj b/src/aspnetcore/src/Azure/AzureAppServicesIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj index 4307d4726052..45e215e07c96 100644 --- a/src/aspnetcore/src/Azure/AzureAppServicesIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj +++ b/src/aspnetcore/src/Azure/AzureAppServicesIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj @@ -5,8 +5,16 @@ + + + + + + + + diff --git a/src/aspnetcore/src/Azure/samples/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj b/src/aspnetcore/src/Azure/samples/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj index 81d6f1f4b572..3b194a128f2e 100644 --- a/src/aspnetcore/src/Azure/samples/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj +++ b/src/aspnetcore/src/Azure/samples/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj @@ -7,6 +7,13 @@ + + + + + + + diff --git a/src/aspnetcore/src/Azure/samples/AzureAppServicesSample/AzureAppServicesSample.csproj b/src/aspnetcore/src/Azure/samples/AzureAppServicesSample/AzureAppServicesSample.csproj index f83608da7977..c039665a6dc5 100644 --- a/src/aspnetcore/src/Azure/samples/AzureAppServicesSample/AzureAppServicesSample.csproj +++ b/src/aspnetcore/src/Azure/samples/AzureAppServicesSample/AzureAppServicesSample.csproj @@ -7,6 +7,14 @@ + + + + + + + + diff --git a/src/aspnetcore/src/Caching/SqlServer/src/SqlServerCacheOptions.cs b/src/aspnetcore/src/Caching/SqlServer/src/SqlServerCacheOptions.cs index 6885696027cf..8ec41a458932 100644 --- a/src/aspnetcore/src/Caching/SqlServer/src/SqlServerCacheOptions.cs +++ b/src/aspnetcore/src/Caching/SqlServer/src/SqlServerCacheOptions.cs @@ -8,39 +8,44 @@ namespace Microsoft.Extensions.Caching.SqlServer; /// -/// Configuration options for . +/// Represents configuration options for . /// public class SqlServerCacheOptions : IOptions { /// - /// An abstraction to represent the clock of a machine in order to enable unit testing. + /// Gets or sets an abstraction to represent the clock of a machine in order to enable unit testing. /// public ISystemClock SystemClock { get; set; } = new SystemClock(); /// - /// The periodic interval to scan and delete expired items in the cache. Default is 30 minutes. + /// Gets or sets the periodic interval to scan and delete expired items in the cache. Default is 30 minutes. /// + /// + /// The periodic interval to scan and delete expired items in the cache. The default is 30 minutes. + /// public TimeSpan? ExpiredItemsDeletionInterval { get; set; } /// - /// The connection string to the database. + /// Gets or sets the connection string to the database. /// public string? ConnectionString { get; set; } /// - /// The schema name of the table. + /// Gets or sets the schema name of the table. /// public string? SchemaName { get; set; } /// - /// Name of the table where the cache items are stored. + /// Gets or sets the name of the table where the cache items are stored. /// public string? TableName { get; set; } /// - /// The default sliding expiration set for a cache entry if neither Absolute or SlidingExpiration has been set explicitly. - /// By default, its 20 minutes. + /// Gets or sets the default sliding expiration set for a cache entry if neither Absolute or SlidingExpiration has been set explicitly. /// + /// + /// The default is 20 minutes. + /// public TimeSpan DefaultSlidingExpiration { get; set; } = TimeSpan.FromMinutes(20); SqlServerCacheOptions IOptions.Value diff --git a/src/aspnetcore/src/Components/AGENTS.md b/src/aspnetcore/src/Components/AGENTS.md new file mode 100644 index 000000000000..3ede49c1553a --- /dev/null +++ b/src/aspnetcore/src/Components/AGENTS.md @@ -0,0 +1,226 @@ +# Working on Issues in the Components Area + +This guide provides step-by-step instructions for working on issues in the ASP.NET Core Components area. + +## Working on issues + +You MUST follow this workflow when implementing new features or fixing bugs in the Components area. +* Add the workflow to your `todos` and follow it strictly. +- Create a sample scenario. +- If working on a bug, use playwright to reproduce the behavior/problem first. +- You MUST have reproduced the problem before attempting to fix it. +- Research the problem area using the microsoft docs, existing code, git history, and logging on the sample project. +- Implement the fix or feature in the sample project first. +- Test the fix or feature interactively using Playwright. +- Once the fix or feature is validated in the sample, implement E2E tests for it. + - When you create an E2E test. First execute it interactively with Playwright. + - If an E2E test is failing, debug it by running the test server manually and navigating to the scenario in a browser. +- Only after the E2E tests are passing, remove the sample code you added in the Samples projects. + - Use `git checkout` and `git clean -fd` to remove the sample code. + +### Overview + +The workflow for implementing new features in the Components area follows these steps: + +1. **Create a sample scenario first** - This is the most important first step. Update code in one of the projects in the `src/Components/Samples` folder to include the scenarios for the feature you want to build. This allows you to develop and test the feature interactively before writing formal tests. + +2. **Build and test interactively** - Build the feature and use Playwright to test it in the browser, ensuring it works end-to-end at a basic level. + +### Sample Projects + +The `src/Components/Samples` folder contains several sample projects you can use for developing and testing features: + +- **BlazorServerApp** - A Blazor Server application for testing server-side scenarios +- **BlazorUnitedApp** - A Blazor Web App (united/hybrid mode) for testing combined server and WebAssembly scenarios +- **BlazorUnitedApp.Client** - The client-side portion of the BlazorUnitedApp + +**Always start by adding your feature scenario to one of these sample projects first.** This allows you to: +- Quickly iterate on the implementation +- Test the feature interactively in a real browser +- Verify the feature works before writing formal E2E tests +- Debug issues more easily with full logging capabilities + +3. **Debug when needed**: + - If something isn't working as expected, increase the logging level in the sample for `Microsoft.AspNetCore.Components` to `Debug` to see detailed logs. + - Check browser console logs using Playwright's `browser_console_messages`. + - Use Microsoft documentation to learn more about troubleshooting Blazor applications. + - You can also increase the log level for JavaScript console output. + +4. **Validate the sample works** - You must have a validated, working sample in the Samples folder before proceeding. Use Playwright to confirm the feature works end-to-end in the browser. + +5. **Implement E2E tests** - Only after the sample is validated, implement E2E tests for it. + +6. **Clean up sample code** - After your E2E tests are passing, remove the sample code you added to the Samples projects. The sample was only for development and interactive testing; the E2E tests now provide the permanent test coverage. Use `git checkout -- src/Components/Samples` and `git clean -df -- src/Components/Samples` to remove the sample code. + +## Build Tips + +### Efficient Build Strategy + +To avoid unnecessary full repository builds, follow this optimized approach: + +#### 1. Initial Setup - Check for First Build +Before running any commands, check if a full build has already been completed: +- Look for `artifacts\agent-sentinel.txt` in the repository root +- If this file exists, skip to step 2 +- If not present, run the initial build and create the sentinel file: + +```bash +.\eng\build.cmd +echo "We ran eng\build.cmd successfully" > artifacts\agent-sentinel.txt +``` + +#### 2. Check for JavaScript Assets +Before running tests or samples, verify that JavaScript assets are built: +- Check for `src\Components\Web.JS\dist\Debug\blazor.web.js` +- If not present, run from the repository root: `npm run build` + +#### 3. Iterating on C# Changes + +**Most of the time (no dependency changes):** +```bash +dotnet build --no-restore -v:q +``` + +Or with `eng\build.cmd`: +```bash +.\eng\build.cmd -NoRestore -NoBuildDeps -NoBuildRepoTasks -NoBuildNative -NoBuildNodeJS -NoBuildJava -NoBuildInstallers -verbosity:quiet +``` + +**When you've added/changed project references or package dependencies:** + +First restore: +```bash +.\restore.cmd +``` + +Then build: +```bash +dotnet build --no-restore -v:q +``` + +**Note:** The `-v:q` (or `-verbosity:quiet`) flag minimizes build output to only show success/failure and error details. Remove this flag if you need to see detailed build output for debugging. + +#### 4. Building Individual Projects (Fixing Build Errors) + +When fixing build errors in a specific project, you can build just that project without its dependencies for even faster iteration: + +```bash +dotnet build --no-restore --no-dependencies -v:q +``` + +**When to use `--no-dependencies`:** +- Fixing compilation errors in a single project (syntax errors, type errors, etc.) +- Making isolated changes that don't affect project references +- Rapid iteration on a specific library + +**When NOT to use `--no-dependencies`:** +- You've changed public APIs that other projects depend on +- You need to verify that dependent projects still compile correctly +- You're unsure if your changes affect other projects (safer to build without this flag) + +**Example:** +```bash +# Fix a compilation error in Components.Endpoints +dotnet build src\Components\Endpoints\src\Microsoft.AspNetCore.Components.Endpoints.csproj --no-restore --no-dependencies -v:q +``` + +#### Quick Reference + +1. **First time only**: `.\eng\build.cmd` → create `artifacts\agent-sentinel.txt` +2. **Check JS assets**: Verify `src\Components\Web.JS\dist\Debug\blazor.web.js` exists, run `npm run build` if missing +3. **Most C# changes**: `dotnet build --no-restore -v:q` +4. **Fixing build errors in one project**: `dotnet build --no-restore --no-dependencies -v:q` +5. **Added/changed dependencies**: Run `.\restore.cmd` first, then use step 3 + +### E2E Testing Structure + +Tests live in `src/Components/test`. The structure includes: + +- **testassets folder** - Contains test assets and scenarios +- **Components.TestServer project** - A web application that launches multiple web servers with different scenarios (different project startups). Avoid adding new startup files unless strictly necessary. + +### Running E2E Tests Manually + +1. **Build the tests**: Follow the build instructions to build the E2E test project and its dependencies. +2. **Start Components.TestServer**: + ```bash + cd src\Components\test\testassets\Components.TestServer + dotnet run --project Components.TestServer.csproj + ``` +3. **Navigate to the test server** - The main server runs on `http://127.0.0.1:5019/subdir` +4. **Select a test scenario** - The main page shows a dropdown with all available test components +5. **Reproduce the scenario** to verify it works the same way as in the sample + +Note: There are also other server instances launched for different test configurations (authentication, CORS, prerendering, etc.). These are listed in the "scenarios" table on the main page. + +### Understanding Logging Configuration + +#### Server-side (.NET) Logging + +The server uses `Microsoft.Extensions.Logging.Testing.TestSink` for capturing logs. Log configuration is in `Program.cs`: + +```csharp +.ConfigureLogging((ctx, lb) => +{ + TestSink sink = new TestSink(); + lb.AddProvider(new TestLoggerProvider(sink)); + lb.Services.Add(ServiceDescriptor.Singleton(sink)); +}) +``` + +#### Client-side (Blazor WebAssembly) Logging + +Logs appear in the browser console. Log levels: +- Logs with `warn:` prefix are Warning level +- Logs with `info:` prefix are Information level +- Logs with `fail:` prefix are Error level + +The Blazor WebAssembly log level can be configured at startup: + +```javascript +Blazor.start({ + logLevel: 1 // LogLevel.Debug +}); +``` + +LogLevel values: Trace=0, Debug=1, Information=2, Warning=3, Error=4, Critical=5 + +For Server-side Blazor (SignalR): +```javascript +Blazor.start({ + circuit: { + configureSignalR: builder => { + builder.configureLogging("debug") // LogLevel.Debug + } + } +}); +``` + +#### Viewing Logs in Playwright + +Use `browser_console_messages` to see JavaScript console output including .NET logs routed to the console. + +### Creating E2E Tests + +E2E tests are located in `src/Components/test/E2ETest`. + +1. First, check if there are already E2E tests for the component/feature area you're working on +2. Try to add an additional test to existing test files when possible +3. When adding test coverage, prefer extending existing test components and assets over creating a set of new ones if it doesn't complicate the existing ones excessively. This reduces test infrastructure complexity and keeps related scenarios together. + +### Running E2E Tests + +The E2E tests use Selenium. To build and run tests: + +```bash +# Build the E2E test project (this includes all test assets as dependencies) +dotnet build src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj --no-restore -v:q + +# Run a specific test +dotnet test src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj --no-build --filter "FullyQualifiedName~TestName" +``` + +**Important**: Never run all E2E tests locally as that is extremely costly. Full test runs should only happen on CI machines. + +If a test is failing, it's best to run the server manually and navigate to the test to investigate. The test output won't be very useful for debugging. + diff --git a/src/aspnetcore/src/Components/Authorization/test/AuthorizeRouteViewTest.cs b/src/aspnetcore/src/Components/Authorization/test/AuthorizeRouteViewTest.cs index 912301e8b893..b3ec1ca1a7a4 100644 --- a/src/aspnetcore/src/Components/Authorization/test/AuthorizeRouteViewTest.cs +++ b/src/aspnetcore/src/Components/Authorization/test/AuthorizeRouteViewTest.cs @@ -35,7 +35,7 @@ public AuthorizeRouteViewTest() var services = serviceCollection.BuildServiceProvider(); _renderer = new TestRenderer(services); - var componentFactory = new ComponentFactory(new DefaultComponentActivator(services), _renderer); + var componentFactory = new ComponentFactory(new DefaultComponentActivator(services), new DefaultComponentPropertyActivator(), _renderer); _authorizeRouteViewComponent = (AuthorizeRouteView)componentFactory.InstantiateComponent(services, typeof(AuthorizeRouteView), null, null); _authorizeRouteViewComponentId = _renderer.AssignRootComponentId(_authorizeRouteViewComponent); } diff --git a/src/aspnetcore/src/Components/Authorization/test/Microsoft.AspNetCore.Components.Authorization.Tests.csproj b/src/aspnetcore/src/Components/Authorization/test/Microsoft.AspNetCore.Components.Authorization.Tests.csproj index 6852bbf3dec0..ec06302c9604 100644 --- a/src/aspnetcore/src/Components/Authorization/test/Microsoft.AspNetCore.Components.Authorization.Tests.csproj +++ b/src/aspnetcore/src/Components/Authorization/test/Microsoft.AspNetCore.Components.Authorization.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/aspnetcore/src/Components/Components/src/ComponentBase.cs b/src/aspnetcore/src/Components/Components/src/ComponentBase.cs index 5de04ae8d70b..eefe8eb74655 100644 --- a/src/aspnetcore/src/Components/Components/src/ComponentBase.cs +++ b/src/aspnetcore/src/Components/Components/src/ComponentBase.cs @@ -283,7 +283,10 @@ private async Task RunInitAndSetParametersAsync() // to defer calling StateHasChanged up until the first bit of async code happens or until // the end. Additionally, we want to avoid calling StateHasChanged if no // async work is to be performed. - StateHasChanged(); + if (task.Status != TaskStatus.Faulted) + { + StateHasChanged(); + } try { @@ -319,7 +322,10 @@ private Task CallOnParametersSetAsync() // We always call StateHasChanged here as we want to trigger a rerender after OnParametersSet and // the synchronous part of OnParametersSetAsync has run. - StateHasChanged(); + if (task.Status != TaskStatus.Faulted) + { + StateHasChanged(); + } return shouldAwaitTask ? CallStateHasChangedOnAsyncCompletion(task) : diff --git a/src/aspnetcore/src/Components/Components/src/ComponentFactory.cs b/src/aspnetcore/src/Components/Components/src/ComponentFactory.cs index 5a6c5b5c71c8..a4b5aaa3224e 100644 --- a/src/aspnetcore/src/Components/Components/src/ComponentFactory.cs +++ b/src/aspnetcore/src/Components/Components/src/ComponentFactory.cs @@ -5,19 +5,19 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.AspNetCore.Components.HotReload; -using Microsoft.AspNetCore.Components.Reflection; using Microsoft.AspNetCore.Components.RenderTree; -using Microsoft.Extensions.DependencyInjection; using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.AspNetCore.Components; internal sealed class ComponentFactory { - private const BindingFlags _injectablePropertyBindingFlags - = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + // This switch is unsupported and will be removed in a future version. + private static readonly bool _propertyInjectionDisabled = + AppContext.TryGetSwitch("Microsoft.AspNetCore.Components.Unsupported.DisablePropertyInjection", out var isDisabled) && + isDisabled; - private static readonly ConcurrentDictionary _cachedComponentTypeInfo = new(); + private static readonly ConcurrentDictionary _cachedComponentTypeRenderModes = new(); static ComponentFactory() { @@ -28,36 +28,35 @@ static ComponentFactory() } private readonly IComponentActivator _componentActivator; + private readonly IComponentPropertyActivator _propertyActivator; private readonly Renderer _renderer; - public ComponentFactory(IComponentActivator componentActivator, Renderer renderer) + public ComponentFactory(IComponentActivator componentActivator, IComponentPropertyActivator propertyActivator, Renderer renderer) { _componentActivator = componentActivator ?? throw new ArgumentNullException(nameof(componentActivator)); + _propertyActivator = propertyActivator ?? throw new ArgumentNullException(nameof(propertyActivator)); _renderer = renderer ?? throw new ArgumentNullException(nameof(renderer)); } - public static void ClearCache() => _cachedComponentTypeInfo.Clear(); + public static void ClearCache() => _cachedComponentTypeRenderModes.Clear(); - private static ComponentTypeInfoCacheEntry GetComponentTypeInfo([DynamicallyAccessedMembers(Component)] Type componentType) + private static IComponentRenderMode? GetComponentTypeRenderMode([DynamicallyAccessedMembers(Component)] Type componentType) { // Unfortunately we can't use 'GetOrAdd' here because the DynamicallyAccessedMembers annotation doesn't flow through to the // callback, so it becomes an IL2111 warning. The following is equivalent and thread-safe because it's a ConcurrentDictionary // and it doesn't matter if we build a cache entry more than once. - if (!_cachedComponentTypeInfo.TryGetValue(componentType, out var cacheEntry)) + if (!_cachedComponentTypeRenderModes.TryGetValue(componentType, out var renderMode)) { - var componentTypeRenderMode = componentType.GetCustomAttribute()?.Mode; - cacheEntry = new ComponentTypeInfoCacheEntry( - componentTypeRenderMode, - CreatePropertyInjector(componentType)); - _cachedComponentTypeInfo.TryAdd(componentType, cacheEntry); + renderMode = componentType.GetCustomAttribute()?.Mode; + _cachedComponentTypeRenderModes.TryAdd(componentType, renderMode); } - return cacheEntry; + return renderMode; } public IComponent InstantiateComponent(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType, IComponentRenderMode? callerSpecifiedRenderMode, int? parentComponentId) { - var (componentTypeRenderMode, propertyInjector) = GetComponentTypeInfo(componentType); + var componentTypeRenderMode = GetComponentTypeRenderMode(componentType); IComponent component; if (componentTypeRenderMode is null && callerSpecifiedRenderMode is null) @@ -82,110 +81,21 @@ public IComponent InstantiateComponent(IServiceProvider serviceProvider, [Dynami throw new InvalidOperationException($"The component activator returned a null value for a component of type {componentType.FullName}."); } - if (component.GetType() == componentType) + if (!_propertyInjectionDisabled) { - // Fast, common case: use the cached data we already looked up - propertyInjector(serviceProvider, component); - } - else - { - // Uncommon case where the activator/resolver returned a different type. Needs an extra cache lookup. PerformPropertyInjection(serviceProvider, component); } return component; } - private static void PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance) + private void PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance) { // Suppressed with "pragma warning disable" so ILLink Roslyn Anayzer doesn't report the warning. -#pragma warning disable IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'Microsoft.AspNetCore.Components.ComponentFactory.GetComponentTypeInfo(Type)'. - var componentTypeInfo = GetComponentTypeInfo(instance.GetType()); -#pragma warning restore IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'Microsoft.AspNetCore.Components.ComponentFactory.GetComponentTypeInfo(Type)'. - - componentTypeInfo.PerformPropertyInjection(serviceProvider, instance); - } - - private static Action CreatePropertyInjector([DynamicallyAccessedMembers(Component)] Type type) - { - // Do all the reflection up front - List<(string name, Type propertyType, PropertySetter setter, object? serviceKey)>? injectables = null; - foreach (var property in MemberAssignment.GetPropertiesIncludingInherited(type, _injectablePropertyBindingFlags)) - { - var injectAttribute = property.GetCustomAttribute(); - if (injectAttribute is null) - { - continue; - } - - injectables ??= new(); - injectables.Add((property.Name, property.PropertyType, new PropertySetter(type, property), injectAttribute.Key)); - } - - if (injectables is null) - { - return static (_, _) => { }; - } - - return Initialize; +#pragma warning disable IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'IComponentPropertyActivator.GetActivator(Type)'. + var propertyActivator = _propertyActivator.GetActivator(instance.GetType()); +#pragma warning restore IL2072 - // Return an action whose closure can write all the injected properties - // without any further reflection calls (just typecasts) - void Initialize(IServiceProvider serviceProvider, IComponent component) - { - foreach (var (propertyName, propertyType, setter, serviceKey) in injectables) - { - object? serviceInstance; - - if (serviceKey is not null) - { - if (serviceProvider is not IKeyedServiceProvider keyedServiceProvider) - { - throw new InvalidOperationException($"Cannot provide a value for property " + - $"'{propertyName}' on type '{type.FullName}'. The service provider " + - $"does not implement '{nameof(IKeyedServiceProvider)}' and therefore " + - $"cannot provide keyed services."); - } - - serviceInstance = keyedServiceProvider.GetKeyedService(propertyType, serviceKey) - ?? throw new InvalidOperationException($"Cannot provide a value for property " + - $"'{propertyName}' on type '{type.FullName}'. There is no " + - $"registered keyed service of type '{propertyType}' with key '{serviceKey}'."); - } - else - { - serviceInstance = serviceProvider.GetService(propertyType) - ?? throw new InvalidOperationException($"Cannot provide a value for property " + - $"'{propertyName}' on type '{type.FullName}'. There is no " + - $"registered service of type '{propertyType}'."); - } - - setter.SetValue(component, serviceInstance); - } - } - } - - // Tracks information about a specific component type that ComponentFactory uses - private sealed class ComponentTypeInfoCacheEntry - { - public IComponentRenderMode? ComponentTypeRenderMode { get; } - - public Action PerformPropertyInjection { get; } - - public ComponentTypeInfoCacheEntry( - IComponentRenderMode? componentTypeRenderMode, - Action performPropertyInjection) - { - ComponentTypeRenderMode = componentTypeRenderMode; - PerformPropertyInjection = performPropertyInjection; - } - - public void Deconstruct( - out IComponentRenderMode? componentTypeRenderMode, - out Action performPropertyInjection) - { - componentTypeRenderMode = ComponentTypeRenderMode; - performPropertyInjection = PerformPropertyInjection; - } + propertyActivator(serviceProvider, instance); } } diff --git a/src/aspnetcore/src/Components/Components/src/ComponentsMetrics.cs b/src/aspnetcore/src/Components/Components/src/ComponentsMetrics.cs index d75cb9ea4feb..64fad76c66dd 100644 --- a/src/aspnetcore/src/Components/Components/src/ComponentsMetrics.cs +++ b/src/aspnetcore/src/Components/Components/src/ComponentsMetrics.cs @@ -69,23 +69,19 @@ public ComponentsMetrics(IMeterFactory meterFactory) public void Navigation(string componentType, string route) { - var tags = new TagList - { - { "aspnetcore.components.type", componentType ?? "unknown" }, - { "aspnetcore.components.route", route ?? "unknown" }, - }; + var tags = new TagList(); + AddComponentTypeTag(ref tags, componentType); + AddRouteTag(ref tags, route); _navigationCount.Add(1, tags); } public async Task CaptureEventDuration(Task task, long startTimestamp, string? componentType, string? methodName, string? attributeName) { - var tags = new TagList - { - { "aspnetcore.components.type", componentType ?? "unknown" }, - { "code.function.name", methodName ?? "unknown" }, - { "aspnetcore.components.attribute.name", attributeName ?? "unknown" } - }; + var tags = new TagList(); + AddComponentTypeTag(ref tags, componentType); + AddMethodNameTag(ref tags, methodName); + AddAttributeNameTag(ref tags, attributeName); try { @@ -93,7 +89,7 @@ public async Task CaptureEventDuration(Task task, long startTimestamp, string? c } catch (Exception ex) { - tags.Add("error.type", ex.GetType().FullName ?? "unknown"); + AddErrorTag(ref tags, ex); } var duration = Stopwatch.GetElapsedTime(startTimestamp); _eventDuration.Record(duration.TotalSeconds, tags); @@ -101,23 +97,20 @@ public async Task CaptureEventDuration(Task task, long startTimestamp, string? c public void FailEventSync(Exception ex, long startTimestamp, string? componentType, string? methodName, string? attributeName) { - var tags = new TagList - { - { "aspnetcore.components.type", componentType ?? "unknown" }, - { "code.function.name", methodName ?? "unknown" }, - { "aspnetcore.components.attribute.name", attributeName ?? "unknown" }, - { "error.type", ex.GetType().FullName ?? "unknown" } - }; + var tags = new TagList(); + AddComponentTypeTag(ref tags, componentType); + AddMethodNameTag(ref tags, methodName); + AddAttributeNameTag(ref tags, attributeName); + AddErrorTag(ref tags, ex); + var duration = Stopwatch.GetElapsedTime(startTimestamp); _eventDuration.Record(duration.TotalSeconds, tags); } public async Task CaptureParametersDuration(Task task, long startTimestamp, string? componentType) { - var tags = new TagList - { - { "aspnetcore.components.type", componentType ?? "unknown" }, - }; + var tags = new TagList(); + AddComponentTypeTag(ref tags, componentType); try { @@ -125,7 +118,7 @@ public async Task CaptureParametersDuration(Task task, long startTimestamp, stri } catch(Exception ex) { - tags.Add("error.type", ex.GetType().FullName ?? "unknown"); + AddErrorTag(ref tags, ex); } var duration = Stopwatch.GetElapsedTime(startTimestamp); _parametersDuration.Record(duration.TotalSeconds, tags); @@ -134,11 +127,10 @@ public async Task CaptureParametersDuration(Task task, long startTimestamp, stri public void FailParametersSync(Exception ex, long startTimestamp, string? componentType) { var duration = Stopwatch.GetElapsedTime(startTimestamp); - var tags = new TagList - { - { "aspnetcore.components.type", componentType ?? "unknown" }, - { "error.type", ex.GetType().FullName ?? "unknown" } - }; + var tags = new TagList(); + AddComponentTypeTag(ref tags, componentType); + AddErrorTag(ref tags, ex); + _parametersDuration.Record(duration.TotalSeconds, tags); } @@ -152,7 +144,7 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL } catch (Exception ex) { - tags.Add("error.type", ex.GetType().FullName ?? "unknown"); + AddErrorTag(ref tags, ex); } var duration = Stopwatch.GetElapsedTime(startTimestamp); _batchDuration.Record(duration.TotalSeconds, tags); @@ -162,10 +154,9 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL public void FailBatchSync(Exception ex, long startTimestamp) { var duration = Stopwatch.GetElapsedTime(startTimestamp); - var tags = new TagList - { - { "error.type", ex.GetType().FullName ?? "unknown" } - }; + var tags = new TagList(); + AddErrorTag(ref tags, ex); + _batchDuration.Record(duration.TotalSeconds, tags); } @@ -174,4 +165,45 @@ public void Dispose() _meter.Dispose(); _lifeCycleMeter.Dispose(); } + + private static void AddComponentTypeTag(ref TagList tags, string? componentType) + { + if (componentType != null) + { + tags.Add("aspnetcore.components.type", componentType); + } + } + + private static void AddRouteTag(ref TagList tags, string? route) + { + if (route != null) + { + tags.Add("aspnetcore.components.route", route); + } + } + + private static void AddMethodNameTag(ref TagList tags, string? methodName) + { + if (methodName != null) + { + tags.Add("code.function.name", methodName); + } + } + + private static void AddAttributeNameTag(ref TagList tags, string? attributeName) + { + if (attributeName != null) + { + tags.Add("aspnetcore.components.attribute.name", attributeName); + } + } + + private static void AddErrorTag(ref TagList tags, Exception? exception) + { + var errorType = exception?.GetType().FullName; + if (errorType is not null) + { + tags.Add("error.type", errorType); + } + } } diff --git a/src/aspnetcore/src/Components/Components/src/DefaultComponentPropertyActivator.cs b/src/aspnetcore/src/Components/Components/src/DefaultComponentPropertyActivator.cs new file mode 100644 index 000000000000..3fa29d33903c --- /dev/null +++ b/src/aspnetcore/src/Components/Components/src/DefaultComponentPropertyActivator.cs @@ -0,0 +1,106 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Microsoft.AspNetCore.Components.HotReload; +using Microsoft.AspNetCore.Components.Reflection; +using Microsoft.Extensions.DependencyInjection; +using static Microsoft.AspNetCore.Internal.LinkerFlags; + +namespace Microsoft.AspNetCore.Components; + +internal sealed class DefaultComponentPropertyActivator : IComponentPropertyActivator +{ + private const BindingFlags InjectablePropertyBindingFlags + = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + + private static readonly ConcurrentDictionary> _cachedPropertyActivators = new(); + + static DefaultComponentPropertyActivator() + { + if (HotReloadManager.Default.MetadataUpdateSupported) + { + HotReloadManager.Default.OnDeltaApplied += ClearCache; + } + } + + public static void ClearCache() => _cachedPropertyActivators.Clear(); + + /// + public Action GetActivator( + [DynamicallyAccessedMembers(Component)] Type componentType) + { + // Unfortunately we can't use 'GetOrAdd' here because the DynamicallyAccessedMembers annotation doesn't flow through to the + // callback, so it becomes an IL2111 warning. The following is equivalent and thread-safe because it's a ConcurrentDictionary + // and it doesn't matter if we build a cache entry more than once. + if (!_cachedPropertyActivators.TryGetValue(componentType, out var activator)) + { + activator = CreatePropertyActivator(componentType); + _cachedPropertyActivators.TryAdd(componentType, activator); + } + + return activator; + } + + private static Action CreatePropertyActivator( + [DynamicallyAccessedMembers(Component)] Type type) + { + // Do all the reflection up front + List<(string name, Type propertyType, PropertySetter setter, object? serviceKey)>? injectables = null; + foreach (var property in MemberAssignment.GetPropertiesIncludingInherited(type, InjectablePropertyBindingFlags)) + { + var injectAttribute = property.GetCustomAttribute(); + if (injectAttribute is null) + { + continue; + } + + injectables ??= new(); + injectables.Add((property.Name, property.PropertyType, new PropertySetter(type, property), injectAttribute.Key)); + } + + if (injectables is null) + { + return static (_, _) => { }; + } + + return Initialize; + + // Return an action whose closure can write all the injected properties + // without any further reflection calls (just typecasts) + void Initialize(IServiceProvider serviceProvider, IComponent component) + { + foreach (var (propertyName, propertyType, setter, serviceKey) in injectables) + { + object? serviceInstance; + + if (serviceKey is not null) + { + if (serviceProvider is not IKeyedServiceProvider keyedServiceProvider) + { + throw new InvalidOperationException($"Cannot provide a value for property " + + $"'{propertyName}' on type '{type.FullName}'. The service provider " + + $"does not implement '{nameof(IKeyedServiceProvider)}' and therefore " + + $"cannot provide keyed services."); + } + + serviceInstance = keyedServiceProvider.GetKeyedService(propertyType, serviceKey) + ?? throw new InvalidOperationException($"Cannot provide a value for property " + + $"'{propertyName}' on type '{type.FullName}'. There is no " + + $"registered keyed service of type '{propertyType}' with key '{serviceKey}'."); + } + else + { + serviceInstance = serviceProvider.GetService(propertyType) + ?? throw new InvalidOperationException($"Cannot provide a value for property " + + $"'{propertyName}' on type '{type.FullName}'. There is no " + + $"registered service of type '{propertyType}'."); + } + + setter.SetValue(component, serviceInstance); + } + } + } +} diff --git a/src/aspnetcore/src/Components/Components/src/IComponentPropertyActivator.cs b/src/aspnetcore/src/Components/Components/src/IComponentPropertyActivator.cs new file mode 100644 index 000000000000..c3bd40c3ec8e --- /dev/null +++ b/src/aspnetcore/src/Components/Components/src/IComponentPropertyActivator.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using static Microsoft.AspNetCore.Internal.LinkerFlags; + +namespace Microsoft.AspNetCore.Components; + +/// +/// Provides a mechanism for activating properties on Blazor component instances. +/// +/// +/// This interface allows customization of how properties marked with +/// are populated on component instances. The default implementation uses the +/// to resolve services for injection. +/// +public interface IComponentPropertyActivator +{ + /// + /// Gets a delegate that activates properties on a component of the specified type. + /// + /// The type of component to create an activator for. + /// + /// A delegate that takes an and an + /// instance, and populates the component's injectable properties. + /// + Action GetActivator( + [DynamicallyAccessedMembers(Component)] Type componentType); +} diff --git a/src/aspnetcore/src/Components/Components/src/LayoutAttribute.cs b/src/aspnetcore/src/Components/Components/src/LayoutAttribute.cs index b7249f82c60c..30de25ca6568 100644 --- a/src/aspnetcore/src/Components/Components/src/LayoutAttribute.cs +++ b/src/aspnetcore/src/Components/Components/src/LayoutAttribute.cs @@ -36,5 +36,6 @@ public LayoutAttribute([DynamicallyAccessedMembers(Component)] Type layoutType) /// The type of the layout. The type must implement /// and must accept a parameter with the name 'Body'. ///
+ [DynamicallyAccessedMembers(Component)] public Type LayoutType { get; private set; } } diff --git a/src/aspnetcore/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/aspnetcore/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index f165e6f82489..ba3ae6420645 100644 --- a/src/aspnetcore/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/aspnetcore/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -35,18 +35,6 @@ - - - - - - diff --git a/src/aspnetcore/src/Components/Components/src/NavigationManager.cs b/src/aspnetcore/src/Components/Components/src/NavigationManager.cs index fecbfaca6c28..6f1996665b89 100644 --- a/src/aspnetcore/src/Components/Components/src/NavigationManager.cs +++ b/src/aspnetcore/src/Components/Components/src/NavigationManager.cs @@ -246,7 +246,7 @@ protected virtual void EnsureInitialized() /// /// Converts a relative URI into an absolute one (by resolving it - /// relative to the current absolute URI). + /// relative to the base URI). /// /// The relative URI. /// The absolute URI. diff --git a/src/aspnetcore/src/Components/Components/src/OwningComponentBase.cs b/src/aspnetcore/src/Components/Components/src/OwningComponentBase.cs index b2e9d9ef554a..b1b634f3b4ce 100644 --- a/src/aspnetcore/src/Components/Components/src/OwningComponentBase.cs +++ b/src/aspnetcore/src/Components/Components/src/OwningComponentBase.cs @@ -44,7 +44,7 @@ protected IServiceProvider ScopedServices } } - /// + /// void IDisposable.Dispose() { Dispose(disposing: true); @@ -69,12 +69,12 @@ protected virtual void Dispose(bool disposing) } } - /// + /// async ValueTask IAsyncDisposable.DisposeAsync() { await DisposeAsyncCore().ConfigureAwait(false); - Dispose(disposing: false); + Dispose(disposing: true); GC.SuppressFinalize(this); } @@ -89,8 +89,6 @@ protected virtual async ValueTask DisposeAsyncCore() await _scope.Value.DisposeAsync().ConfigureAwait(false); _scope = null; } - - IsDisposed = true; } } diff --git a/src/aspnetcore/src/Components/Components/src/PersistentComponentState.cs b/src/aspnetcore/src/Components/Components/src/PersistentComponentState.cs index 1c7337eb534f..56b669f5e2fe 100644 --- a/src/aspnetcore/src/Components/Components/src/PersistentComponentState.cs +++ b/src/aspnetcore/src/Components/Components/src/PersistentComponentState.cs @@ -115,12 +115,10 @@ public RestoringComponentStateSubscription RegisterOnRestoring(Action callback, throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback."); } - if (_currentState.ContainsKey(key)) + if (!_currentState.TryAdd(key, JsonSerializer.SerializeToUtf8Bytes(instance, JsonSerializerOptionsProvider.Options))) { throw new ArgumentException($"There is already a persisted object under the same key '{key}'"); } - - _currentState.Add(key, JsonSerializer.SerializeToUtf8Bytes(instance, JsonSerializerOptionsProvider.Options)); } [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] @@ -133,12 +131,10 @@ internal void PersistAsJson(string key, object instance, [DynamicallyAccessedMem throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback."); } - if (_currentState.ContainsKey(key)) + if (!_currentState.TryAdd(key, JsonSerializer.SerializeToUtf8Bytes(instance, type, JsonSerializerOptionsProvider.Options))) { throw new ArgumentException($"There is already a persisted object under the same key '{key}'"); } - - _currentState.Add(key, JsonSerializer.SerializeToUtf8Bytes(instance, type, JsonSerializerOptionsProvider.Options)); } /// @@ -155,12 +151,10 @@ internal void PersistAsBytes(string key, byte[] data) throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback."); } - if (_currentState.ContainsKey(key)) + if (!_currentState.TryAdd(key, data)) { throw new ArgumentException($"There is already a persisted object under the same key '{key}'"); } - - _currentState.Add(key, data); } /// diff --git a/src/aspnetcore/src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs b/src/aspnetcore/src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs index 41fa00a6bb7c..e0f82daf4b57 100644 --- a/src/aspnetcore/src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs +++ b/src/aspnetcore/src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs @@ -172,7 +172,13 @@ public void SetPlatformRenderMode(IComponentRenderMode renderMode) private void InferRenderModes(Renderer renderer) { - for (var i = 0; i < _registeredCallbacks.Count; i++) + // We are iterating backwards to allow the callbacks to remove themselves from the list. + // Otherwise, we would have to make a copy of the list to avoid running into situations + // where we don't run all the callbacks because the count of the list changed while we + // were iterating over it. + // It is not allowed to register a callback while we are persisting the state, so we don't + // need to worry about new callbacks being added to the list. + for (var i = _registeredCallbacks.Count - 1; i >= 0; i--) { var registration = _registeredCallbacks[i]; if (registration.RenderMode != null) diff --git a/src/aspnetcore/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs b/src/aspnetcore/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs index 30e537a418a3..35ad18735816 100644 --- a/src/aspnetcore/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs +++ b/src/aspnetcore/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs @@ -143,9 +143,9 @@ internal void RestoreProperty() Log.RestoringValueFromState(_logger, _storageKey, _propertyType.Name, _propertyName); var sequence = new ReadOnlySequence(data!); _lastValue = _customSerializer.Restore(_propertyType, sequence); + _ignoreComponentPropertyValue = true; if (!skipNotifications) { - _ignoreComponentPropertyValue = true; _subscriber.NotifyCascadingValueChanged(ParameterViewLifetime.Unbound); } } @@ -160,9 +160,9 @@ internal void RestoreProperty() { Log.RestoredValueFromPersistentState(_logger, _storageKey, _propertyType.Name, "null", _propertyName); _lastValue = value; + _ignoreComponentPropertyValue = true; if (!skipNotifications) { - _ignoreComponentPropertyValue = true; _subscriber.NotifyCascadingValueChanged(ParameterViewLifetime.Unbound); } } diff --git a/src/aspnetcore/src/Components/Components/src/PersistentStateAttribute.cs b/src/aspnetcore/src/Components/Components/src/PersistentStateAttribute.cs index cd8de101bda9..fc0f7f22d62f 100644 --- a/src/aspnetcore/src/Components/Components/src/PersistentStateAttribute.cs +++ b/src/aspnetcore/src/Components/Components/src/PersistentStateAttribute.cs @@ -15,9 +15,9 @@ public sealed class PersistentStateAttribute : CascadingParameterAttributeBase /// /// /// By default it always restores the value on all situations. - /// Use to skip restoring the initial value + /// Use to skip restoring the initial value /// when the host starts up. - /// Use to skip restoring the last value captured + /// Use to skip restoring the last value captured /// the last time the current host was shut down. /// public RestoreBehavior RestoreBehavior { get; set; } = RestoreBehavior.Default; diff --git a/src/aspnetcore/src/Components/Components/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/Components/src/PublicAPI.Shipped.txt index c417cab5be3a..5277e771faf7 100644 --- a/src/aspnetcore/src/Components/Components/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/Components/src/PublicAPI.Shipped.txt @@ -22,6 +22,8 @@ abstract Microsoft.AspNetCore.Components.Dispatcher.InvokeAsync(System.Func(System.Func!>! workItem) -> System.Threading.Tasks.Task! abstract Microsoft.AspNetCore.Components.Dispatcher.InvokeAsync(System.Func! workItem) -> System.Threading.Tasks.Task! abstract Microsoft.AspNetCore.Components.ErrorBoundaryBase.OnErrorAsync(System.Exception! exception) -> System.Threading.Tasks.Task! +abstract Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.Persist(T value, System.Buffers.IBufferWriter! writer) -> void +abstract Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.Restore(System.Buffers.ReadOnlySequence data) -> T abstract Microsoft.AspNetCore.Components.RenderModeAttribute.Mode.get -> Microsoft.AspNetCore.Components.IComponentRenderMode! abstract Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispatcher.get -> Microsoft.AspNetCore.Components.Dispatcher! abstract Microsoft.AspNetCore.Components.RenderTree.Renderer.HandleException(System.Exception! exception) -> void @@ -98,8 +100,8 @@ Microsoft.AspNetCore.Components.EditorRequiredAttribute.EditorRequiredAttribute( Microsoft.AspNetCore.Components.ElementReference Microsoft.AspNetCore.Components.ElementReference.Context.get -> Microsoft.AspNetCore.Components.ElementReferenceContext? Microsoft.AspNetCore.Components.ElementReference.ElementReference() -> void -Microsoft.AspNetCore.Components.ElementReference.ElementReference(string! id) -> void Microsoft.AspNetCore.Components.ElementReference.ElementReference(string! id, Microsoft.AspNetCore.Components.ElementReferenceContext? context) -> void +Microsoft.AspNetCore.Components.ElementReference.ElementReference(string! id) -> void Microsoft.AspNetCore.Components.ElementReference.Id.get -> string! Microsoft.AspNetCore.Components.ElementReferenceContext Microsoft.AspNetCore.Components.ElementReferenceContext.ElementReferenceContext() -> void @@ -151,8 +153,8 @@ Microsoft.AspNetCore.Components.EventHandlerAttribute.AttributeName.get -> strin Microsoft.AspNetCore.Components.EventHandlerAttribute.EnablePreventDefault.get -> bool Microsoft.AspNetCore.Components.EventHandlerAttribute.EnableStopPropagation.get -> bool Microsoft.AspNetCore.Components.EventHandlerAttribute.EventArgsType.get -> System.Type! -Microsoft.AspNetCore.Components.EventHandlerAttribute.EventHandlerAttribute(string! attributeName, System.Type! eventArgsType) -> void Microsoft.AspNetCore.Components.EventHandlerAttribute.EventHandlerAttribute(string! attributeName, System.Type! eventArgsType, bool enableStopPropagation, bool enablePreventDefault) -> void +Microsoft.AspNetCore.Components.EventHandlerAttribute.EventHandlerAttribute(string! attributeName, System.Type! eventArgsType) -> void Microsoft.AspNetCore.Components.ExcludeFromInteractiveRoutingAttribute Microsoft.AspNetCore.Components.ExcludeFromInteractiveRoutingAttribute.ExcludeFromInteractiveRoutingAttribute() -> void Microsoft.AspNetCore.Components.IComponent @@ -165,11 +167,17 @@ Microsoft.AspNetCore.Components.IHandleAfterRender Microsoft.AspNetCore.Components.IHandleAfterRender.OnAfterRenderAsync() -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.IHandleEvent Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(Microsoft.AspNetCore.Components.EventCallbackWorkItem item, object? arg) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager +Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.ComponentStatePersistenceManager(Microsoft.Extensions.Logging.ILogger! logger, System.IServiceProvider! serviceProvider) -> void Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.ComponentStatePersistenceManager(Microsoft.Extensions.Logging.ILogger! logger) -> void Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.PersistStateAsync(Microsoft.AspNetCore.Components.IPersistentComponentStateStore! store, Microsoft.AspNetCore.Components.RenderTree.Renderer! renderer) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.RestoreStateAsync(Microsoft.AspNetCore.Components.IPersistentComponentStateStore! store, Microsoft.AspNetCore.Components.RestoreContext! context) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.RestoreStateAsync(Microsoft.AspNetCore.Components.IPersistentComponentStateStore! store) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.SetPlatformRenderMode(Microsoft.AspNetCore.Components.IComponentRenderMode! renderMode) -> void Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.State.get -> Microsoft.AspNetCore.Components.PersistentComponentState! +Microsoft.AspNetCore.Components.Infrastructure.PersistentStateProviderServiceCollectionExtensions +Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions Microsoft.AspNetCore.Components.InjectAttribute Microsoft.AspNetCore.Components.InjectAttribute.InjectAttribute() -> void Microsoft.AspNetCore.Components.InjectAttribute.Key.get -> object? @@ -213,8 +221,10 @@ Microsoft.AspNetCore.Components.NavigationManager.NavigateTo(string! uri, bool f Microsoft.AspNetCore.Components.NavigationManager.NavigateTo(string! uri, bool forceLoad) -> void Microsoft.AspNetCore.Components.NavigationManager.NavigateTo(string! uri, Microsoft.AspNetCore.Components.NavigationOptions options) -> void Microsoft.AspNetCore.Components.NavigationManager.NavigationManager() -> void +Microsoft.AspNetCore.Components.NavigationManager.NotFound() -> void Microsoft.AspNetCore.Components.NavigationManager.NotifyLocationChanged(bool isInterceptedLink) -> void Microsoft.AspNetCore.Components.NavigationManager.NotifyLocationChangingAsync(string! uri, string? state, bool isNavigationIntercepted) -> System.Threading.Tasks.ValueTask +Microsoft.AspNetCore.Components.NavigationManager.OnNotFound -> System.EventHandler! Microsoft.AspNetCore.Components.NavigationManager.RegisterLocationChangingHandler(System.Func! locationChangingHandler) -> System.IDisposable! Microsoft.AspNetCore.Components.NavigationManager.ToAbsoluteUri(string? relativeUri) -> System.Uri! Microsoft.AspNetCore.Components.NavigationManager.ToBaseRelativePath(string! uri) -> string! @@ -251,17 +261,26 @@ Microsoft.AspNetCore.Components.ParameterView.Enumerator.Current.get -> Microsof Microsoft.AspNetCore.Components.ParameterView.Enumerator.Enumerator() -> void Microsoft.AspNetCore.Components.ParameterView.Enumerator.MoveNext() -> bool Microsoft.AspNetCore.Components.ParameterView.GetEnumerator() -> Microsoft.AspNetCore.Components.ParameterView.Enumerator -Microsoft.AspNetCore.Components.ParameterView.GetValueOrDefault(string! parameterName) -> TValue? Microsoft.AspNetCore.Components.ParameterView.GetValueOrDefault(string! parameterName, TValue defaultValue) -> TValue +Microsoft.AspNetCore.Components.ParameterView.GetValueOrDefault(string! parameterName) -> TValue? Microsoft.AspNetCore.Components.ParameterView.ParameterView() -> void Microsoft.AspNetCore.Components.ParameterView.SetParameterProperties(object! target) -> void Microsoft.AspNetCore.Components.ParameterView.ToDictionary() -> System.Collections.Generic.IReadOnlyDictionary! Microsoft.AspNetCore.Components.ParameterView.TryGetValue(string! parameterName, out TValue result) -> bool Microsoft.AspNetCore.Components.PersistentComponentState Microsoft.AspNetCore.Components.PersistentComponentState.PersistAsJson(string! key, TValue instance) -> void -Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting(System.Func! callback) -> Microsoft.AspNetCore.Components.PersistingComponentStateSubscription Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting(System.Func! callback, Microsoft.AspNetCore.Components.IComponentRenderMode? renderMode) -> Microsoft.AspNetCore.Components.PersistingComponentStateSubscription +Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting(System.Func! callback) -> Microsoft.AspNetCore.Components.PersistingComponentStateSubscription +Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnRestoring(System.Action! callback, Microsoft.AspNetCore.Components.RestoreOptions options) -> Microsoft.AspNetCore.Components.RestoringComponentStateSubscription Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(string! key, out TValue? instance) -> bool +Microsoft.AspNetCore.Components.PersistentComponentStateSerializer +Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.PersistentComponentStateSerializer() -> void +Microsoft.AspNetCore.Components.PersistentStateAttribute +Microsoft.AspNetCore.Components.PersistentStateAttribute.AllowUpdates.get -> bool +Microsoft.AspNetCore.Components.PersistentStateAttribute.AllowUpdates.set -> void +Microsoft.AspNetCore.Components.PersistentStateAttribute.PersistentStateAttribute() -> void +Microsoft.AspNetCore.Components.PersistentStateAttribute.RestoreBehavior.get -> Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.PersistentStateAttribute.RestoreBehavior.set -> void Microsoft.AspNetCore.Components.PersistingComponentStateSubscription Microsoft.AspNetCore.Components.PersistingComponentStateSubscription.Dispose() -> void Microsoft.AspNetCore.Components.PersistingComponentStateSubscription.PersistingComponentStateSubscription() -> void @@ -287,14 +306,15 @@ Microsoft.AspNetCore.Components.Rendering.ComponentState.ComponentId.get -> int Microsoft.AspNetCore.Components.Rendering.ComponentState.ComponentState(Microsoft.AspNetCore.Components.RenderTree.Renderer! renderer, int componentId, Microsoft.AspNetCore.Components.IComponent! component, Microsoft.AspNetCore.Components.Rendering.ComponentState? parentComponentState) -> void Microsoft.AspNetCore.Components.Rendering.ComponentState.LogicalParentComponentState.get -> Microsoft.AspNetCore.Components.Rendering.ComponentState? Microsoft.AspNetCore.Components.Rendering.ComponentState.ParentComponentState.get -> Microsoft.AspNetCore.Components.Rendering.ComponentState? +Microsoft.AspNetCore.Components.Rendering.ComponentState.Renderer.get -> Microsoft.AspNetCore.Components.RenderTree.Renderer! Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame frame) -> void -Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, bool value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, Microsoft.AspNetCore.Components.EventCallback value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, object? value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, string? value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, System.MulticastDelegate? value) -> void +Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddAttribute(int sequence, string! name, Microsoft.AspNetCore.Components.EventCallback value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddComponentParameter(int sequence, string! name, object? value) -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddComponentReferenceCapture(int sequence, System.Action! componentReferenceCaptureAction) -> void @@ -372,10 +392,10 @@ Microsoft.AspNetCore.Components.RenderTree.Renderer.GetCurrentRenderTreeFrames(i Microsoft.AspNetCore.Components.RenderTree.Renderer.GetEventArgsType(ulong eventHandlerId) -> System.Type! Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateComponent(System.Type! componentType) -> Microsoft.AspNetCore.Components.IComponent! Microsoft.AspNetCore.Components.RenderTree.Renderer.RemoveRootComponent(int componentId) -> void -Microsoft.AspNetCore.Components.RenderTree.Renderer.Renderer(System.IServiceProvider! serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void Microsoft.AspNetCore.Components.RenderTree.Renderer.Renderer(System.IServiceProvider! serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, Microsoft.AspNetCore.Components.IComponentActivator! componentActivator) -> void -Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.RenderTree.Renderer.Renderer(System.IServiceProvider! serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId, Microsoft.AspNetCore.Components.ParameterView initialParameters) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.RenderTree.Renderer.UnhandledSynchronizationException -> System.UnhandledExceptionEventHandler! Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiff Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiff.RenderTreeDiff() -> void @@ -417,7 +437,7 @@ Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType.Region = 5 -> Mic Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType.Text = 2 -> Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType Microsoft.AspNetCore.Components.ResourceAsset Microsoft.AspNetCore.Components.ResourceAsset.Properties.get -> System.Collections.Generic.IReadOnlyList? -Microsoft.AspNetCore.Components.ResourceAsset.ResourceAsset(string! url, System.Collections.Generic.IReadOnlyList? properties) -> void +Microsoft.AspNetCore.Components.ResourceAsset.ResourceAsset(string! url, System.Collections.Generic.IReadOnlyList? properties = null) -> void Microsoft.AspNetCore.Components.ResourceAsset.Url.get -> string! Microsoft.AspNetCore.Components.ResourceAssetCollection Microsoft.AspNetCore.Components.ResourceAssetCollection.IsContentSpecificUrl(string! path) -> bool @@ -427,6 +447,20 @@ Microsoft.AspNetCore.Components.ResourceAssetProperty Microsoft.AspNetCore.Components.ResourceAssetProperty.Name.get -> string! Microsoft.AspNetCore.Components.ResourceAssetProperty.ResourceAssetProperty(string! name, string! value) -> void Microsoft.AspNetCore.Components.ResourceAssetProperty.Value.get -> string! +Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.RestoreBehavior.Default = 0 -> Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.RestoreBehavior.SkipInitialValue = 1 -> Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.RestoreBehavior.SkipLastSnapshot = 2 -> Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.RestoreContext +Microsoft.AspNetCore.Components.RestoreOptions +Microsoft.AspNetCore.Components.RestoreOptions.AllowUpdates.get -> bool +Microsoft.AspNetCore.Components.RestoreOptions.AllowUpdates.init -> void +Microsoft.AspNetCore.Components.RestoreOptions.RestoreBehavior.get -> Microsoft.AspNetCore.Components.RestoreBehavior +Microsoft.AspNetCore.Components.RestoreOptions.RestoreBehavior.init -> void +Microsoft.AspNetCore.Components.RestoreOptions.RestoreOptions() -> void +Microsoft.AspNetCore.Components.RestoringComponentStateSubscription +Microsoft.AspNetCore.Components.RestoringComponentStateSubscription.Dispose() -> void +Microsoft.AspNetCore.Components.RestoringComponentStateSubscription.RestoringComponentStateSubscription() -> void Microsoft.AspNetCore.Components.RouteAttribute Microsoft.AspNetCore.Components.RouteAttribute.RouteAttribute(string! template) -> void Microsoft.AspNetCore.Components.RouteAttribute.Template.get -> string! @@ -445,6 +479,7 @@ Microsoft.AspNetCore.Components.RouteView.RouteData.set -> void Microsoft.AspNetCore.Components.RouteView.RouteView() -> void Microsoft.AspNetCore.Components.RouteView.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Routing.IHostEnvironmentNavigationManager +Microsoft.AspNetCore.Components.Routing.IHostEnvironmentNavigationManager.Initialize(string! baseUri, string! uri, System.Func! onNavigateTo) -> void Microsoft.AspNetCore.Components.Routing.IHostEnvironmentNavigationManager.Initialize(string! baseUri, string! uri) -> void Microsoft.AspNetCore.Components.Routing.INavigationInterception Microsoft.AspNetCore.Components.Routing.INavigationInterception.EnableNavigationInterceptionAsync() -> System.Threading.Tasks.Task! @@ -471,6 +506,10 @@ Microsoft.AspNetCore.Components.Routing.LocationChangingContext.TargetLocation.i Microsoft.AspNetCore.Components.Routing.NavigationContext Microsoft.AspNetCore.Components.Routing.NavigationContext.CancellationToken.get -> System.Threading.CancellationToken Microsoft.AspNetCore.Components.Routing.NavigationContext.Path.get -> string! +Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs +Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.NotFoundEventArgs() -> void +Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path.get -> string? +Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path.set -> void Microsoft.AspNetCore.Components.Routing.Router Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies.get -> System.Collections.Generic.IEnumerable! Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies.set -> void @@ -484,6 +523,8 @@ Microsoft.AspNetCore.Components.Routing.Router.Navigating.get -> Microsoft.AspNe Microsoft.AspNetCore.Components.Routing.Router.Navigating.set -> void Microsoft.AspNetCore.Components.Routing.Router.NotFound.get -> Microsoft.AspNetCore.Components.RenderFragment! Microsoft.AspNetCore.Components.Routing.Router.NotFound.set -> void +Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.get -> System.Type? +Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.set -> void Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync.get -> Microsoft.AspNetCore.Components.EventCallback Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync.set -> void Microsoft.AspNetCore.Components.Routing.Router.PreferExactMatches.get -> bool @@ -672,6 +713,10 @@ static Microsoft.AspNetCore.Components.EventCallbackFactoryEventArgsExtensions.C static Microsoft.AspNetCore.Components.EventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback static Microsoft.AspNetCore.Components.EventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback static Microsoft.AspNetCore.Components.EventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsMetrics(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsTracing(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.AspNetCore.Components.Infrastructure.PersistentStateProviderServiceCollectionExtensions.AddSupplyValueFromPersistentComponentStateProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(Microsoft.Extensions.DependencyInjection.IServiceCollection! services, Microsoft.AspNetCore.Components.IComponentRenderMode! componentRenderMode) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.AspNetCore.Components.MarkupString.explicit operator Microsoft.AspNetCore.Components.MarkupString(string! value) -> Microsoft.AspNetCore.Components.MarkupString static Microsoft.AspNetCore.Components.NavigationManagerExtensions.GetUriWithQueryParameter(this Microsoft.AspNetCore.Components.NavigationManager! navigationManager, string! name, bool value) -> string! static Microsoft.AspNetCore.Components.NavigationManagerExtensions.GetUriWithQueryParameter(this Microsoft.AspNetCore.Components.NavigationManager! navigationManager, string! name, bool? value) -> string! @@ -698,6 +743,9 @@ static Microsoft.AspNetCore.Components.NavigationManagerExtensions.GetUriWithQue static Microsoft.AspNetCore.Components.NavigationManagerExtensions.GetUriWithQueryParameters(this Microsoft.AspNetCore.Components.NavigationManager! navigationManager, System.Collections.Generic.IReadOnlyDictionary! parameters) -> string! static Microsoft.AspNetCore.Components.ParameterView.Empty.get -> Microsoft.AspNetCore.Components.ParameterView static Microsoft.AspNetCore.Components.ParameterView.FromDictionary(System.Collections.Generic.IDictionary! parameters) -> Microsoft.AspNetCore.Components.ParameterView +static Microsoft.AspNetCore.Components.RestoreContext.InitialValue.get -> Microsoft.AspNetCore.Components.RestoreContext! +static Microsoft.AspNetCore.Components.RestoreContext.LastSnapshot.get -> Microsoft.AspNetCore.Components.RestoreContext! +static Microsoft.AspNetCore.Components.RestoreContext.ValueUpdate.get -> Microsoft.AspNetCore.Components.RestoreContext! static Microsoft.AspNetCore.Components.SupplyParameterFromQueryProviderServiceCollectionExtensions.AddSupplyValueFromQueryProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.Extensions.DependencyInjection.CascadingValueServiceCollectionExtensions.AddCascadingValue(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, string! name, System.Func! initialValueFactory) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.Extensions.DependencyInjection.CascadingValueServiceCollectionExtensions.AddCascadingValue(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, System.Func!>! sourceFactory) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! @@ -726,12 +774,14 @@ virtual Microsoft.AspNetCore.Components.NavigationManager.NavigateToCore(string! virtual Microsoft.AspNetCore.Components.NavigationManager.Refresh(bool forceReload = false) -> void virtual Microsoft.AspNetCore.Components.NavigationManager.SetNavigationLockState(bool value) -> void virtual Microsoft.AspNetCore.Components.OwningComponentBase.Dispose(bool disposing) -> void +virtual Microsoft.AspNetCore.Components.OwningComponentBase.DisposeAsyncCore() -> System.Threading.Tasks.ValueTask virtual Microsoft.AspNetCore.Components.Rendering.ComponentState.DisposeAsync() -> System.Threading.Tasks.ValueTask +virtual Microsoft.AspNetCore.Components.Rendering.ComponentState.GetComponentKey() -> object? virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.AddPendingTask(Microsoft.AspNetCore.Components.Rendering.ComponentState? componentState, System.Threading.Tasks.Task! task) -> void virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.Assets.get -> Microsoft.AspNetCore.Components.ResourceAssetCollection! virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.CreateComponentState(int componentId, Microsoft.AspNetCore.Components.IComponent! component, Microsoft.AspNetCore.Components.Rendering.ComponentState? parentComponentState) -> Microsoft.AspNetCore.Components.Rendering.ComponentState! -virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo? fieldInfo, System.EventArgs! eventArgs) -> System.Threading.Tasks.Task! virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo? fieldInfo, System.EventArgs! eventArgs, bool waitForQuiescence) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo? fieldInfo, System.EventArgs! eventArgs) -> System.Threading.Tasks.Task! virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing) -> void virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.GetComponentRenderMode(Microsoft.AspNetCore.Components.IComponent! component) -> Microsoft.AspNetCore.Components.IComponentRenderMode? virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender() -> void diff --git a/src/aspnetcore/src/Components/Components/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/Components/src/PublicAPI.Unshipped.txt index 2da7d492e710..908e19bcf6f2 100644 --- a/src/aspnetcore/src/Components/Components/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/Components/src/PublicAPI.Unshipped.txt @@ -1,53 +1,6 @@ #nullable enable +Microsoft.AspNetCore.Components.IComponentPropertyActivator +Microsoft.AspNetCore.Components.IComponentPropertyActivator.GetActivator(System.Type! componentType) -> System.Action! *REMOVED*Microsoft.AspNetCore.Components.ResourceAsset.ResourceAsset(string! url, System.Collections.Generic.IReadOnlyList? properties) -> void -Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.RestoreStateAsync(Microsoft.AspNetCore.Components.IPersistentComponentStateStore! store, Microsoft.AspNetCore.Components.RestoreContext! context) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnRestoring(System.Action! callback, Microsoft.AspNetCore.Components.RestoreOptions options) -> Microsoft.AspNetCore.Components.RestoringComponentStateSubscription -Microsoft.AspNetCore.Components.PersistentStateAttribute.AllowUpdates.get -> bool -Microsoft.AspNetCore.Components.PersistentStateAttribute.AllowUpdates.set -> void -Microsoft.AspNetCore.Components.PersistentStateAttribute.RestoreBehavior.get -> Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.PersistentStateAttribute.RestoreBehavior.set -> void -Microsoft.AspNetCore.Components.Rendering.ComponentState.Renderer.get -> Microsoft.AspNetCore.Components.RenderTree.Renderer! -Microsoft.AspNetCore.Components.ResourceAsset.ResourceAsset(string! url, System.Collections.Generic.IReadOnlyList? properties = null) -> void -Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.RestoreBehavior.Default = 0 -> Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.RestoreBehavior.SkipInitialValue = 1 -> Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.RestoreBehavior.SkipLastSnapshot = 2 -> Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.RestoreContext -Microsoft.AspNetCore.Components.RestoreOptions -Microsoft.AspNetCore.Components.RestoreOptions.AllowUpdates.get -> bool -Microsoft.AspNetCore.Components.RestoreOptions.AllowUpdates.init -> void -Microsoft.AspNetCore.Components.RestoreOptions.RestoreBehavior.get -> Microsoft.AspNetCore.Components.RestoreBehavior -Microsoft.AspNetCore.Components.RestoreOptions.RestoreBehavior.init -> void -Microsoft.AspNetCore.Components.RestoreOptions.RestoreOptions() -> void -Microsoft.AspNetCore.Components.RestoringComponentStateSubscription -Microsoft.AspNetCore.Components.RestoringComponentStateSubscription.Dispose() -> void -Microsoft.AspNetCore.Components.RestoringComponentStateSubscription.RestoringComponentStateSubscription() -> void -Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.get -> System.Type? -Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.set -> void -Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions -Microsoft.AspNetCore.Components.NavigationManager.OnNotFound -> System.EventHandler! -Microsoft.AspNetCore.Components.NavigationManager.NotFound() -> void -Microsoft.AspNetCore.Components.Routing.IHostEnvironmentNavigationManager.Initialize(string! baseUri, string! uri, System.Func! onNavigateTo) -> void -Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs -Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.NotFoundEventArgs() -> void -Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path.get -> string? -Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path.set -> void -Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.ComponentStatePersistenceManager(Microsoft.Extensions.Logging.ILogger! logger, System.IServiceProvider! serviceProvider) -> void -Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.SetPlatformRenderMode(Microsoft.AspNetCore.Components.IComponentRenderMode! renderMode) -> void -Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions -Microsoft.AspNetCore.Components.PersistentStateAttribute -Microsoft.AspNetCore.Components.PersistentStateAttribute.PersistentStateAttribute() -> void -Microsoft.AspNetCore.Components.Infrastructure.PersistentStateProviderServiceCollectionExtensions -Microsoft.AspNetCore.Components.PersistentComponentStateSerializer -Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.PersistentComponentStateSerializer() -> void -abstract Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.Persist(T value, System.Buffers.IBufferWriter! writer) -> void -abstract Microsoft.AspNetCore.Components.PersistentComponentStateSerializer.Restore(System.Buffers.ReadOnlySequence data) -> T -static Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(Microsoft.Extensions.DependencyInjection.IServiceCollection! services, Microsoft.AspNetCore.Components.IComponentRenderMode! componentRenderMode) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsMetrics(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsTracing(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -static Microsoft.AspNetCore.Components.RestoreContext.InitialValue.get -> Microsoft.AspNetCore.Components.RestoreContext! -static Microsoft.AspNetCore.Components.RestoreContext.LastSnapshot.get -> Microsoft.AspNetCore.Components.RestoreContext! -static Microsoft.AspNetCore.Components.RestoreContext.ValueUpdate.get -> Microsoft.AspNetCore.Components.RestoreContext! -virtual Microsoft.AspNetCore.Components.OwningComponentBase.DisposeAsyncCore() -> System.Threading.Tasks.ValueTask -static Microsoft.AspNetCore.Components.Infrastructure.PersistentStateProviderServiceCollectionExtensions.AddSupplyValueFromPersistentComponentStateProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -virtual Microsoft.AspNetCore.Components.Rendering.ComponentState.GetComponentKey() -> object? +*REMOVED*Microsoft.AspNetCore.Components.Routing.Router.PreferExactMatches.get -> bool +*REMOVED*Microsoft.AspNetCore.Components.Routing.Router.PreferExactMatches.set -> void diff --git a/src/aspnetcore/src/Components/Components/src/RenderFragment.cs b/src/aspnetcore/src/Components/Components/src/RenderFragment.cs index e5016ddfeae1..f1c28f34a624 100644 --- a/src/aspnetcore/src/Components/Components/src/RenderFragment.cs +++ b/src/aspnetcore/src/Components/Components/src/RenderFragment.cs @@ -18,4 +18,4 @@ namespace Microsoft.AspNetCore.Components; /// /// The type of object. /// The value used to build the content. -public delegate RenderFragment RenderFragment(TValue value); +public delegate RenderFragment RenderFragment(TValue value); diff --git a/src/aspnetcore/src/Components/Components/src/RenderTree/Renderer.cs b/src/aspnetcore/src/Components/Components/src/RenderTree/Renderer.cs index 0b2095f6f0e1..6b6b9df93f8b 100644 --- a/src/aspnetcore/src/Components/Components/src/RenderTree/Renderer.cs +++ b/src/aspnetcore/src/Components/Components/src/RenderTree/Renderer.cs @@ -54,6 +54,7 @@ public abstract partial class Renderer : IDisposable, IAsyncDisposable private bool _rendererIsDisposed; private bool _hotReloadInitialized; + private HotReloadRenderHandler? _hotReloadRenderHandler; /// /// Allows the caller to handle exceptions from the SynchronizationContext when one is available. @@ -98,7 +99,7 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, // has always taken ILoggerFactory so to avoid the per-instance string allocation of the logger name we just pass the // logger name in here as a string literal. _logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Components.RenderTree.Renderer"); - _componentFactory = new ComponentFactory(componentActivator, this); + _componentFactory = new ComponentFactory(componentActivator, GetComponentPropertyActivatorOrDefault(serviceProvider), this); _componentsMetrics = serviceProvider.GetService(); _componentsActivitySource = serviceProvider.GetService(); _componentsActivitySource?.Init(new ComponentsActivityLinkStore(this)); @@ -121,6 +122,12 @@ private static IComponentActivator GetComponentActivatorOrDefault(IServiceProvid ?? new DefaultComponentActivator(serviceProvider); } + private static IComponentPropertyActivator GetComponentPropertyActivatorOrDefault(IServiceProvider serviceProvider) + { + return serviceProvider.GetService() + ?? new DefaultComponentPropertyActivator(); + } + /// /// Gets the associated with this . /// @@ -185,6 +192,7 @@ private async void RenderRootComponentsOnHotReload() ComponentFactory.ClearCache(); ComponentProperties.ClearCache(); DefaultComponentActivator.ClearCache(); + DefaultComponentPropertyActivator.ClearCache(); await Dispatcher.InvokeAsync(() => { @@ -231,7 +239,12 @@ protected internal int AssignRootComponentId(IComponent component) _hotReloadInitialized = true; if (HotReloadManager.MetadataUpdateSupported) { - HotReloadManager.OnDeltaApplied += RenderRootComponentsOnHotReload; + // Capture the current ExecutionContext so AsyncLocal values present during initial root component + // registration flow through to hot reload re-renders. Without this, hot reload callbacks execute + // on a thread without the original ambient context and AsyncLocal values appear null. + var executionContext = ExecutionContext.Capture(); + _hotReloadRenderHandler = new HotReloadRenderHandler(this, executionContext); + HotReloadManager.OnDeltaApplied += _hotReloadRenderHandler.RerenderOnHotReload; } } @@ -1234,9 +1247,9 @@ protected virtual void Dispose(bool disposing) _rendererIsDisposed = true; } - if (_hotReloadInitialized && HotReloadManager.MetadataUpdateSupported) + if (_hotReloadInitialized && HotReloadManager.MetadataUpdateSupported && _hotReloadRenderHandler is not null) { - HotReloadManager.OnDeltaApplied -= RenderRootComponentsOnHotReload; + HotReloadManager.OnDeltaApplied -= _hotReloadRenderHandler.RerenderOnHotReload; } // It's important that we handle all exceptions here before reporting any of them. @@ -1371,4 +1384,19 @@ public async ValueTask DisposeAsync() } } } + + private sealed class HotReloadRenderHandler(Renderer renderer, ExecutionContext? executionContext) + { + public void RerenderOnHotReload() + { + if (executionContext is null) + { + renderer.RenderRootComponentsOnHotReload(); + } + else + { + ExecutionContext.Run(executionContext, static s => ((Renderer)s!).RenderRootComponentsOnHotReload(), renderer); + } + } + } } diff --git a/src/aspnetcore/src/Components/Components/src/RouteView.cs b/src/aspnetcore/src/Components/Components/src/RouteView.cs index 46cda45a0fed..36560a2aa342 100644 --- a/src/aspnetcore/src/Components/Components/src/RouteView.cs +++ b/src/aspnetcore/src/Components/Components/src/RouteView.cs @@ -68,6 +68,7 @@ public Task SetParametersAsync(ParameterView parameters) /// Renders the component. /// /// The . + [UnconditionalSuppressMessage("Trimming", "IL2110", Justification = "Layout components are preserved because the LayoutAttribute constructor parameter is correctly annotated.")] [UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Layout components are preserved because the LayoutAttribute constructor parameter is correctly annotated.")] [UnconditionalSuppressMessage("Trimming", "IL2118", Justification = "Layout components are preserved because the LayoutAttribute constructor parameter is correctly annotated.")] protected virtual void Render(RenderTreeBuilder builder) diff --git a/src/aspnetcore/src/Components/Components/src/Routing/Router.cs b/src/aspnetcore/src/Components/Components/src/Routing/Router.cs index ecb69fe2cf63..54fc0c87aa46 100644 --- a/src/aspnetcore/src/Components/Components/src/Routing/Router.cs +++ b/src/aspnetcore/src/Components/Components/src/Routing/Router.cs @@ -98,14 +98,6 @@ static readonly IReadOnlyDictionary _emptyParametersDictionary /// [Parameter] public EventCallback OnNavigateAsync { get; set; } - /// - /// Gets or sets a flag to indicate whether route matching should prefer exact matches - /// over wildcards. - /// This property is obsolete and configuring it does nothing. - /// - [Obsolete("This property is obsolete and configuring it has no effect.")] - [Parameter] public bool PreferExactMatches { get; set; } - private RouteTable Routes { get; set; } /// diff --git a/src/aspnetcore/src/Components/Components/test/ComponentBaseTest.cs b/src/aspnetcore/src/Components/Components/test/ComponentBaseTest.cs index b8a928a0ec41..c525dfccc1ac 100644 --- a/src/aspnetcore/src/Components/Components/test/ComponentBaseTest.cs +++ b/src/aspnetcore/src/Components/Components/test/ComponentBaseTest.cs @@ -372,6 +372,126 @@ public async Task DoesNotRenderAfterOnInitAsyncTaskIsCancelledUsingCancellationT Assert.NotEmpty(renderer.Batches); } + [Fact] + public async Task ErrorBoundaryHandlesOnInitializedAsyncReturnFaultedTask() + { + // Arrange + var renderer = new TestRenderer(); + TestErrorBoundary capturedBoundary = null; + + // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary + var rootComponent = new TestComponent(); + rootComponent.ChildContent = builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(TestErrorBoundary.ChildContent), (RenderFragment)(childBuilder => + { + childBuilder.OpenComponent(0); + childBuilder.AddComponentParameter(1, nameof(TestComponentErrorBuildRenderTree.FaultedTaskOnInitializedAsync), true); + childBuilder.CloseComponent(); + })); + builder.AddComponentReferenceCapture(2, inst => capturedBoundary = (TestErrorBoundary)inst); + builder.CloseComponent(); + }; + + // Act + var rootComponentId = renderer.AssignRootComponentId(rootComponent); + await renderer.RenderRootComponentAsync(rootComponentId); + + // Assert + Assert.NotNull(capturedBoundary); + Assert.NotNull(capturedBoundary!.ReceivedException); + Assert.Equal(typeof(InvalidTimeZoneException), capturedBoundary!.ReceivedException.GetType()); + } + + [Fact] + public async Task ErrorBoundaryHandlesCallOnParametersSetAsyncReturnFaultedTask() + { + // Arrange + var renderer = new TestRenderer(); + TestErrorBoundary capturedBoundary = null; + + // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary + var rootComponent = new TestComponent(); + rootComponent.ChildContent = builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(TestErrorBoundary.ChildContent), (RenderFragment)(childBuilder => + { + childBuilder.OpenComponent(0); + childBuilder.AddComponentParameter(1, nameof(TestComponentErrorBuildRenderTree.FaultedTaskOnParametersSetAsync), true); + childBuilder.CloseComponent(); + })); + builder.AddComponentReferenceCapture(2, inst => capturedBoundary = (TestErrorBoundary)inst); + builder.CloseComponent(); + }; + + // Act + var rootComponentId = renderer.AssignRootComponentId(rootComponent); + await renderer.RenderRootComponentAsync(rootComponentId); + + // Assert + Assert.NotNull(capturedBoundary); + Assert.NotNull(capturedBoundary!.ReceivedException); + Assert.Equal(typeof(InvalidTimeZoneException), capturedBoundary!.ReceivedException.GetType()); + } + + [Fact] + public async Task ComponentBaseDoesntRenderWhenOnInitializedAsyncFaultedTask() + { + // Arrange + var renderer = new TestRenderer(); + renderer.ShouldHandleExceptions = true; + TestComponentErrorBuildRenderTree testComponentErrorBuildRenderTree = null; + + // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary + var rootComponent = new TestComponent(); + rootComponent.ChildContent = builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(TestComponentErrorBuildRenderTree.FaultedTaskOnInitializedAsync), true); + builder.AddComponentReferenceCapture(2, inst => testComponentErrorBuildRenderTree = (TestComponentErrorBuildRenderTree)inst); + builder.CloseComponent(); + }; + + // Act + var rootComponentId = renderer.AssignRootComponentId(rootComponent); + await renderer.RenderRootComponentAsync(rootComponentId); + + // Assert + Assert.IsType(renderer.HandledExceptions[0]); + Assert.NotNull(testComponentErrorBuildRenderTree); + Assert.Equal(0, testComponentErrorBuildRenderTree.StateHasChangedCalled); + } + + [Fact] + public async Task ComponentBaseDoesntRenderWhenOnSetParametersSetAsyncFaultedTask() + { + // Arrange + var renderer = new TestRenderer(); + renderer.ShouldHandleExceptions = true; + TestComponentErrorBuildRenderTree testComponentErrorBuildRenderTree = null; + + // Create root component that wraps the TestComponentErrorBuildRenderTree in an TestErrorBoundary + var rootComponent = new TestComponent(); + rootComponent.ChildContent = builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(TestComponentErrorBuildRenderTree.FaultedTaskOnParametersSetAsync), true); + builder.AddComponentReferenceCapture(2, inst => testComponentErrorBuildRenderTree = (TestComponentErrorBuildRenderTree)inst); + builder.CloseComponent(); + }; + + // Act + var rootComponentId = renderer.AssignRootComponentId(rootComponent); + await renderer.RenderRootComponentAsync(rootComponentId); + + // Assert + Assert.IsType(renderer.HandledExceptions[0]); + Assert.NotNull(testComponentErrorBuildRenderTree); + Assert.Equal(0, testComponentErrorBuildRenderTree.StateHasChangedCalled); + } + [Fact] public async Task DoesNotRenderAfterOnParametersSetAsyncTaskIsCanceled() { @@ -491,11 +611,20 @@ private class TestComponent : ComponentBase public int Counter { get; set; } + public RenderFragment ChildContent { get; set; } + protected override void BuildRenderTree(RenderTreeBuilder builder) { - builder.OpenElement(0, "p"); - builder.AddContent(1, Counter); - builder.CloseElement(); + if (ChildContent != null) + { + builder.AddContent(0, ChildContent); + } + else + { + builder.OpenElement(0, "p"); + builder.AddContent(1, Counter); + builder.CloseElement(); + } } protected override void OnInitialized() @@ -570,4 +699,65 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } } } + + private class TestErrorBoundary : ErrorBoundaryBase + { + public Exception ReceivedException => CurrentException; + + protected override Task OnErrorAsync(Exception exception) + { + return Task.CompletedTask; + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (CurrentException == null) + { + builder.AddContent(0, ChildContent); + } + else + { + builder.OpenElement(2, "div"); + builder.AddAttribute(3, "class", "blazor-error-boundary"); + builder.CloseElement(); + } + } + } + + private class TestComponentErrorBuildRenderTree : ComponentBase + { + [Parameter] public bool FaultedTaskOnInitializedAsync { get; set; } = false; + [Parameter] public bool FaultedTaskOnParametersSetAsync { get; set; } = false; + + public int StateHasChangedCalled { get; set; } = 0; + + protected new void StateHasChanged() + { + StateHasChangedCalled++; + base.StateHasChanged(); + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + throw new InvalidOperationException("Error in BuildRenderTree"); + } + + protected override Task OnInitializedAsync() + { + if (FaultedTaskOnInitializedAsync) + { + return Task.FromException(new InvalidTimeZoneException()); + } + return Task.CompletedTask; + } + + protected override Task OnParametersSetAsync() + { + if (FaultedTaskOnParametersSetAsync) + { + return Task.FromException(new InvalidTimeZoneException()); + } + return Task.CompletedTask; + } + } } diff --git a/src/aspnetcore/src/Components/Components/test/ComponentFactoryTest.cs b/src/aspnetcore/src/Components/Components/test/ComponentFactoryTest.cs index 5b0b32bc7535..f937d0b67230 100644 --- a/src/aspnetcore/src/Components/Components/test/ComponentFactoryTest.cs +++ b/src/aspnetcore/src/Components/Components/test/ComponentFactoryTest.cs @@ -16,8 +16,8 @@ public void InstantiateComponent_CreatesInstance() // Arrange var componentType = typeof(EmptyComponent); var serviceProvider = GetServiceProvider(); - var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new TestRenderer()); - + var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new DefaultComponentPropertyActivator(), new TestRenderer()); + // Act var instance = factory.InstantiateComponent(GetServiceProvider(), componentType, null, null); @@ -32,8 +32,8 @@ public void InstantiateComponent_CreatesInstance_NonComponent() // Arrange var componentType = typeof(List); var serviceProvider = GetServiceProvider(); - var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new TestRenderer()); - + var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new DefaultComponentPropertyActivator(), new TestRenderer()); + // Assert var ex = Assert.Throws(() => factory.InstantiateComponent(GetServiceProvider(), componentType, null, null)); Assert.StartsWith($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", ex.Message); @@ -44,7 +44,7 @@ public void InstantiateComponent_CreatesInstance_WithCustomActivator() { // Arrange var componentType = typeof(EmptyComponent); - var factory = new ComponentFactory(new CustomComponentActivator(), new TestRenderer()); + var factory = new ComponentFactory(new CustomComponentActivator(), new DefaultComponentPropertyActivator(), new TestRenderer()); // Act var instance = factory.InstantiateComponent(GetServiceProvider(), componentType, null, null); @@ -65,7 +65,7 @@ public void InstantiateComponent_ThrowsForNullInstance() { // Arrange var componentType = typeof(EmptyComponent); - var factory = new ComponentFactory(new NullResultComponentActivator(), new TestRenderer()); + var factory = new ComponentFactory(new NullResultComponentActivator(), new DefaultComponentPropertyActivator(), new TestRenderer()); // Act var ex = Assert.Throws(() => factory.InstantiateComponent(GetServiceProvider(), componentType, null, null)); @@ -77,7 +77,7 @@ public void InstantiateComponent_AssignsPropertiesWithInjectAttributeOnBaseType( { // Arrange var componentType = typeof(DerivedComponent); - var factory = new ComponentFactory(new CustomComponentActivator(), new TestRenderer()); + var factory = new ComponentFactory(new CustomComponentActivator(), new DefaultComponentPropertyActivator(), new TestRenderer()); // Act var instance = factory.InstantiateComponent(GetServiceProvider(), componentType, null, null); @@ -94,6 +94,8 @@ public void InstantiateComponent_AssignsPropertiesWithInjectAttributeOnBaseType( Assert.Null(component.Property4); // Property on the base type with the [Inject] attribute should Assert.NotNull(((ComponentWithInjectProperties)component).Property4); + // Property on derived type with [Inject] should be assigned + Assert.NotNull(component.Property5); } [Fact] @@ -102,7 +104,7 @@ public void InstantiateComponent_IgnoresPropertiesWithoutInjectAttribute() // Arrange var componentType = typeof(ComponentWithNonInjectableProperties); var serviceProvider = GetServiceProvider(); - var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new TestRenderer()); + var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new DefaultComponentPropertyActivator(), new TestRenderer()); // Act var instance = factory.InstantiateComponent(serviceProvider, componentType, null, null); @@ -123,7 +125,7 @@ public void InstantiateComponent_WithNoRenderMode_DoesNotUseRenderModeResolver() var renderer = new RendererWithResolveComponentForRenderMode( /* won't be used */ new ComponentWithRenderMode()); var serviceProvider = GetServiceProvider(); - var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), renderer); + var factory = new ComponentFactory(new DefaultComponentActivator(serviceProvider), new DefaultComponentPropertyActivator(), renderer); // Act var instance = factory.InstantiateComponent(serviceProvider, componentType, null, null); @@ -142,7 +144,7 @@ public void InstantiateComponent_WithRenderModeOnComponent_UsesRenderModeResolve var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); var serviceProvider = GetServiceProvider(); var componentActivator = new DefaultComponentActivator(serviceProvider); - var factory = new ComponentFactory(componentActivator, renderer); + var factory = new ComponentFactory(componentActivator, new DefaultComponentPropertyActivator(), renderer); // Act var instance = (ComponentWithInjectProperties)factory.InstantiateComponent(serviceProvider, componentType, null, 1234); @@ -174,7 +176,7 @@ public void InstantiateComponent_WithDerivedRenderModeOnDerivedComponent_CausesA var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); var serviceProvider = GetServiceProvider(); var componentActivator = new DefaultComponentActivator(serviceProvider); - var factory = new ComponentFactory(componentActivator, renderer); + var factory = new ComponentFactory(componentActivator, new DefaultComponentPropertyActivator(), renderer); // Act/Assert Assert.Throws( @@ -193,7 +195,7 @@ public void InstantiateComponent_WithRenderModeOnCallSite_UsesRenderModeResolver var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); var serviceProvider = GetServiceProvider(); var componentActivator = new DefaultComponentActivator(serviceProvider); - var factory = new ComponentFactory(componentActivator, renderer); + var factory = new ComponentFactory(componentActivator, new DefaultComponentPropertyActivator(), renderer); // Act var instance = (ComponentWithInjectProperties)factory.InstantiateComponent(serviceProvider, componentType, callSiteRenderMode, 1234); @@ -216,7 +218,7 @@ public void InstantiateComponent_WithRenderModeOnComponentAndCallSite_Throws() var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); var serviceProvider = GetServiceProvider(); var componentActivator = new DefaultComponentActivator(serviceProvider); - var factory = new ComponentFactory(componentActivator, renderer); + var factory = new ComponentFactory(componentActivator, new DefaultComponentPropertyActivator(), renderer); // Even though the two rendermodes are literally the same object, we don't allow specifying any nonnull // rendermode at the callsite if there's a nonnull fixed rendermode @@ -237,7 +239,7 @@ public void InstantiateComponent_CreatesInstance_WithTypeActivation() var resolvedComponent = new ComponentWithInjectProperties(); var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); var defaultComponentActivator = new DefaultComponentActivator(serviceProvider); - var factory = new ComponentFactory(defaultComponentActivator, renderer); + var factory = new ComponentFactory(defaultComponentActivator, new DefaultComponentPropertyActivator(), renderer); // Act var instance = factory.InstantiateComponent(serviceProvider, componentType, null, null); @@ -250,6 +252,227 @@ public void InstantiateComponent_CreatesInstance_WithTypeActivation() Assert.NotNull(component.Property3); // Property injection should still work. } + [Fact] + public void InstantiateComponent_WithCustomPropertyActivator_UsesCustomActivator() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var serviceProvider = GetServiceProvider(); + var customPropertyActivator = new CustomPropertyActivator(); + var factory = new ComponentFactory( + new DefaultComponentActivator(serviceProvider), + customPropertyActivator, + new TestRenderer()); + + // Act + var instance = factory.InstantiateComponent(serviceProvider, componentType, null, null); + + // Assert + Assert.NotNull(instance); + var component = Assert.IsType(instance); + Assert.True(customPropertyActivator.GetActivatorCalled); + Assert.Equal(componentType, customPropertyActivator.RequestedType); + Assert.True(customPropertyActivator.ActivatorInvoked); + } + + [Fact] + public void InstantiateComponent_WithCustomPropertyActivator_ReceivesCorrectServiceProvider() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var serviceProvider = GetServiceProvider(); + var customPropertyActivator = new CustomPropertyActivator(); + var factory = new ComponentFactory( + new DefaultComponentActivator(serviceProvider), + customPropertyActivator, + new TestRenderer()); + + // Act + factory.InstantiateComponent(serviceProvider, componentType, null, null); + + // Assert + Assert.Same(serviceProvider, customPropertyActivator.ReceivedServiceProvider); + } + + [Fact] + public void InstantiateComponent_WithCustomPropertyActivator_WhenActivatorReturnsDifferentType_StillUsesCustomActivator() + { + // Arrange + // The component activator returns a different type than requested + var requestedType = typeof(EmptyComponent); + var actualType = typeof(ComponentWithInjectProperties); + var serviceProvider = GetServiceProvider(); + var customPropertyActivator = new CustomPropertyActivator(); + var factory = new ComponentFactory( + new CustomComponentActivator(), + customPropertyActivator, + new TestRenderer()); + + // Act + var instance = factory.InstantiateComponent(serviceProvider, requestedType, null, null); + + // Assert + Assert.IsType(instance); + // The property activator should be called with the actual type, not the requested type + Assert.True(customPropertyActivator.GetActivatorCalled); + // Since the types differ, GetActivator is called twice (once for requested, once for actual) + // But the activator should have been invoked for the actual component type + Assert.True(customPropertyActivator.ActivatorInvoked); + } + + [Fact] + public void InstantiateComponent_WhenActivatorReturnsSubtype_InjectsAllSubtypeProperties() + { + // Arrange + // Request base type but activator returns derived type + var requestedType = typeof(ComponentWithInjectProperties); + var serviceProvider = GetServiceProvider(); + var factory = new ComponentFactory( + new CustomComponentActivator(), + new DefaultComponentPropertyActivator(), + new TestRenderer()); + + // Act + var instance = factory.InstantiateComponent(serviceProvider, requestedType, null, null); + + // Assert + var component = Assert.IsType(instance); + + // All base type properties with [Inject] should be injected + Assert.NotNull(component.Property1); + Assert.NotNull(component.GetProperty2()); + Assert.NotNull(component.Property3); + Assert.NotNull(((ComponentWithInjectProperties)component).Property4); + Assert.NotNull(component.KeyedProperty); + + // Derived type properties with [Inject] should also be injected + Assert.NotNull(component.Property5); + + // Derived type property without [Inject] should not be injected + Assert.Null(component.Property4); + } + + [Fact] + public void InstantiateComponent_WhenActivatorReturnsExactType_InjectsPropertiesIdenticallyToSubtype() + { + // Arrange + // This test validates that there's no difference in property injection behavior + // whether the activator returns the exact type or a subtype + var requestedType = typeof(DerivedComponent); + var serviceProvider = GetServiceProvider(); + var factory = new ComponentFactory( + new CustomComponentActivator(), + new DefaultComponentPropertyActivator(), + new TestRenderer()); + + // Act + var instance = factory.InstantiateComponent(serviceProvider, requestedType, null, null); + + // Assert + var component = Assert.IsType(instance); + + // All base type properties with [Inject] should be injected + Assert.NotNull(component.Property1); + Assert.NotNull(component.GetProperty2()); + Assert.NotNull(component.Property3); + Assert.NotNull(((ComponentWithInjectProperties)component).Property4); + Assert.NotNull(component.KeyedProperty); + + // Derived type properties with [Inject] should also be injected + Assert.NotNull(component.Property5); + + // Derived type property without [Inject] should not be injected + Assert.Null(component.Property4); + } + + [Fact] + public void DefaultComponentPropertyActivator_GetActivator_ReturnsActivatorThatInjectsProperties() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var serviceProvider = GetServiceProvider(); + var propertyActivator = new DefaultComponentPropertyActivator(); + var component = new ComponentWithInjectProperties(); + + // Act + var activator = propertyActivator.GetActivator(componentType); + activator(serviceProvider, component); + + // Assert + Assert.NotNull(component.Property1); + Assert.NotNull(component.GetProperty2()); + Assert.NotNull(component.Property3); + Assert.NotNull(component.Property4); + Assert.NotNull(component.KeyedProperty); + } + + [Fact] + public void DefaultComponentPropertyActivator_GetActivator_CachesActivator() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var propertyActivator = new DefaultComponentPropertyActivator(); + + // Act + var activator1 = propertyActivator.GetActivator(componentType); + var activator2 = propertyActivator.GetActivator(componentType); + + // Assert + Assert.Same(activator1, activator2); + } + + [Fact] + public void DefaultComponentPropertyActivator_GetActivator_ReturnsNoOpForComponentWithoutInjectableProperties() + { + // Arrange + var componentType = typeof(EmptyComponent); + var serviceProvider = GetServiceProvider(); + var propertyActivator = new DefaultComponentPropertyActivator(); + var component = new EmptyComponent(); + + // Act + var activator = propertyActivator.GetActivator(componentType); + + // Should not throw + activator(serviceProvider, component); + + // Assert - nothing to verify, just ensuring no exception + } + + [Fact] + public void DefaultComponentPropertyActivator_GetActivator_ThrowsForMissingService() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); // Empty provider + var propertyActivator = new DefaultComponentPropertyActivator(); + var component = new ComponentWithInjectProperties(); + + // Act + var activator = propertyActivator.GetActivator(componentType); + var ex = Assert.Throws(() => activator(serviceProvider, component)); + + // Assert + Assert.Contains("There is no registered service of type", ex.Message); + } + + [Fact] + public void DefaultComponentPropertyActivator_GetActivator_ThrowsForMissingKeyedService() + { + // Arrange + var componentType = typeof(ComponentWithOnlyKeyedProperty); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); // Empty provider + var propertyActivator = new DefaultComponentPropertyActivator(); + var component = new ComponentWithOnlyKeyedProperty(); + + // Act + var activator = propertyActivator.GetActivator(componentType); + var ex = Assert.Throws(() => activator(serviceProvider, component)); + + // Assert + Assert.Contains("registered keyed service", ex.Message); + } + private const string KeyedServiceKey = "my-keyed-service"; private static IServiceProvider GetServiceProvider() @@ -359,6 +582,22 @@ public class TestService1 { } public class TestService2 { } public class TestService3 { } + private class ComponentWithOnlyKeyedProperty : IComponent + { + [Inject(Key = KeyedServiceKey)] + public TestService3 KeyedProperty { get; set; } + + public void Attach(RenderHandle renderHandle) + { + throw new NotImplementedException(); + } + + public Task SetParametersAsync(ParameterView parameters) + { + throw new NotImplementedException(); + } + } + private class CustomComponentActivator : IComponentActivator where TResult : IComponent, new() { public IComponent CreateInstance(Type componentType) @@ -375,6 +614,31 @@ public IComponent CreateInstance(Type componentType) } } + private class CustomPropertyActivator : IComponentPropertyActivator + { + public bool GetActivatorCalled { get; private set; } + public Type RequestedType { get; private set; } + public bool ActivatorInvoked { get; private set; } + public IServiceProvider ReceivedServiceProvider { get; private set; } + + public Action GetActivator(Type componentType) + { + GetActivatorCalled = true; + RequestedType = componentType; + + // Return an activator that tracks invocation and delegates to the default + var defaultActivator = new DefaultComponentPropertyActivator(); + var defaultAction = defaultActivator.GetActivator(componentType); + + return (sp, component) => + { + ActivatorInvoked = true; + ReceivedServiceProvider = sp; + defaultAction(sp, component); + }; + } + } + private class TestRenderMode : IComponentRenderMode { } private class DerivedComponentRenderMode : IComponentRenderMode { } diff --git a/src/aspnetcore/src/Components/Components/test/ComponentsMetricsTest.cs b/src/aspnetcore/src/Components/Components/test/ComponentsMetricsTest.cs index 388f83bd95a8..195b2d316efa 100644 --- a/src/aspnetcore/src/Components/Components/test/ComponentsMetricsTest.cs +++ b/src/aspnetcore/src/Components/Components/test/ComponentsMetricsTest.cs @@ -400,4 +400,134 @@ public void Dispose_DisposesAllMeters() // Assert - MeterFactory.Create was called twice in constructor Assert.Equal(2, _meterFactory.Meters.Count); } + + [Fact] + public void Navigation_WithNullValues_OmitsTags() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var navigationCounter = new MetricCollector(_meterFactory, + ComponentsMetrics.MeterName, "aspnetcore.components.navigate"); + + // Act + componentsMetrics.Navigation(null, null); + + // Assert + var measurements = navigationCounter.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.Equal(1, measurements[0].Value); + Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags); + Assert.DoesNotContain("aspnetcore.components.route", measurements[0].Tags); + } + + [Fact] + public async Task CaptureEventDuration_WithNullValues_OmitsTags() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var eventDurationHistogram = new MetricCollector(_meterFactory, + ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration"); + + // Act + var startTimestamp = Stopwatch.GetTimestamp(); + await componentsMetrics.CaptureEventDuration(Task.CompletedTask, startTimestamp, null, null, null); + + // Assert + var measurements = eventDurationHistogram.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.True(measurements[0].Value >= 0); + Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags); + Assert.DoesNotContain("code.function.name", measurements[0].Tags); + Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags); + Assert.DoesNotContain("error.type", measurements[0].Tags); + } + + [Fact] + public void FailEventSync_WithNullValues_OmitsTags() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var eventDurationHistogram = new MetricCollector(_meterFactory, + ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration"); + var exception = new InvalidOperationException(); + + // Act + var startTimestamp = Stopwatch.GetTimestamp(); + componentsMetrics.FailEventSync(exception, startTimestamp, null, null, null); + + // Assert + var measurements = eventDurationHistogram.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.True(measurements[0].Value >= 0); + Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags); + Assert.DoesNotContain("code.function.name", measurements[0].Tags); + Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags); + Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags)); + } + + [Fact] + public async Task CaptureParametersDuration_WithNullValues_OmitsTags() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var parametersDurationHistogram = new MetricCollector(_meterFactory, + ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration"); + + // Act + var startTimestamp = Stopwatch.GetTimestamp(); + await componentsMetrics.CaptureParametersDuration(Task.CompletedTask, startTimestamp, null); + + // Assert + var measurements = parametersDurationHistogram.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.True(measurements[0].Value >= 0); + Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags); + Assert.DoesNotContain("error.type", measurements[0].Tags); + } + + [Fact] + public void FailParametersSync_WithNullValues_OmitsTags() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var parametersDurationHistogram = new MetricCollector(_meterFactory, + ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration"); + var exception = new InvalidOperationException(); + + // Act + var startTimestamp = Stopwatch.GetTimestamp(); + componentsMetrics.FailParametersSync(exception, startTimestamp, null); + + // Assert + var measurements = parametersDurationHistogram.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.True(measurements[0].Value >= 0); + Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags); + Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags)); + } + + [Fact] + public async Task CaptureBatchDuration_WithoutException_OmitsErrorTag() + { + // Arrange + var componentsMetrics = new ComponentsMetrics(_meterFactory); + using var batchDurationHistogram = new MetricCollector(_meterFactory, + ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration"); + + // Act + var startTimestamp = Stopwatch.GetTimestamp(); + await componentsMetrics.CaptureBatchDuration(Task.CompletedTask, startTimestamp, 25); + + // Assert + var measurements = batchDurationHistogram.GetMeasurementSnapshot(); + + Assert.Single(measurements); + Assert.True(measurements[0].Value >= 0); + Assert.DoesNotContain("error.type", measurements[0].Tags); + } } diff --git a/src/aspnetcore/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj b/src/aspnetcore/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj index 732ebbb65892..012c36a3c274 100644 --- a/src/aspnetcore/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj +++ b/src/aspnetcore/src/Components/Components/test/Microsoft.AspNetCore.Components.Tests.csproj @@ -6,6 +6,10 @@ + + + + diff --git a/src/aspnetcore/src/Components/Components/test/OwningComponentBaseTest.cs b/src/aspnetcore/src/Components/Components/test/OwningComponentBaseTest.cs index ae9a2b4a67ac..c7414190e156 100644 --- a/src/aspnetcore/src/Components/Components/test/OwningComponentBaseTest.cs +++ b/src/aspnetcore/src/Components/Components/test/OwningComponentBaseTest.cs @@ -111,6 +111,59 @@ public MyService(Counter counter) void IDisposable.Dispose() => Counter.DisposedCount++; } + [Fact] + public async Task DisposeAsync_CallsDispose_WithDisposingTrue() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddTransient(); + var serviceProvider = services.BuildServiceProvider(); + + var renderer = new TestRenderer(serviceProvider); + var component = (ComponentWithDispose)renderer.InstantiateComponent(); + + _ = component.MyService; + await ((IAsyncDisposable)component).DisposeAsync(); + Assert.True(component.DisposingParameter); + } + + [Fact] + public async Task DisposeAsync_ThenDispose_IsIdempotent() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddTransient(); + var serviceProvider = services.BuildServiceProvider(); + + var counter = serviceProvider.GetRequiredService(); + var renderer = new TestRenderer(serviceProvider); + var component = (ComponentWithDispose)renderer.InstantiateComponent(); + + _ = component.MyService; + + await ((IAsyncDisposable)component).DisposeAsync(); + var firstCallCount = component.DisposeCallCount; + Assert.Equal(1, counter.DisposedCount); + + ((IDisposable)component).Dispose(); + Assert.True(component.DisposeCallCount >= firstCallCount); + Assert.Equal(1, counter.DisposedCount); + } + + private class ComponentWithDispose : OwningComponentBase + { + public MyService MyService => Service; + public bool? DisposingParameter { get; private set; } + public int DisposeCallCount { get; private set; } + + protected override void Dispose(bool disposing) + { + DisposingParameter = disposing; + DisposeCallCount++; + base.Dispose(disposing); + } + } + private class MyOwningComponent : OwningComponentBase { public MyService MyService => Service; @@ -118,4 +171,69 @@ private class MyOwningComponent : OwningComponentBase // Expose IsDisposed for testing public bool IsDisposedPublic => IsDisposed; } + + [Fact] + public async Task ComplexComponent_DisposesResourcesOnlyWhenDisposingIsTrue() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddTransient(); + var serviceProvider = services.BuildServiceProvider(); + + var renderer = new TestRenderer(serviceProvider); + var component = (ComplexComponent)renderer.InstantiateComponent(); + + _ = component.MyService; + + await ((IAsyncDisposable)component).DisposeAsync(); + + // Verify all managed resources were disposed because disposing=true + Assert.True(component.TimerDisposed); + Assert.True(component.CancellationTokenSourceDisposed); + Assert.True(component.EventUnsubscribed); + Assert.Equal(1, component.ManagedResourcesCleanedUpCount); + } + + private class ComplexComponent : OwningComponentBase + { + private readonly System.Threading.Timer _timer; + private readonly CancellationTokenSource _cts; + private bool _eventSubscribed; + + public MyService MyService => Service; + public bool TimerDisposed { get; private set; } + public bool CancellationTokenSourceDisposed { get; private set; } + public bool EventUnsubscribed { get; private set; } + public int ManagedResourcesCleanedUpCount { get; private set; } + + public ComplexComponent() + { + _timer = new System.Threading.Timer(_ => { }, null, Timeout.Infinite, Timeout.Infinite); + _cts = new CancellationTokenSource(); + _eventSubscribed = true; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _timer?.Dispose(); + TimerDisposed = true; + + _cts?.Cancel(); + _cts?.Dispose(); + CancellationTokenSourceDisposed = true; + + if (_eventSubscribed) + { + EventUnsubscribed = true; + _eventSubscribed = false; + } + + ManagedResourcesCleanedUpCount++; + } + + base.Dispose(disposing); + } + } } diff --git a/src/aspnetcore/src/Components/Components/test/PersistentState/ComponentStatePersistenceManagerTest.cs b/src/aspnetcore/src/Components/Components/test/PersistentState/ComponentStatePersistenceManagerTest.cs index a8857107a782..34046eaf2342 100644 --- a/src/aspnetcore/src/Components/Components/test/PersistentState/ComponentStatePersistenceManagerTest.cs +++ b/src/aspnetcore/src/Components/Components/test/PersistentState/ComponentStatePersistenceManagerTest.cs @@ -353,6 +353,53 @@ public async Task PersistStateAsync_InvokesAllCallbacksEvenIfACallbackIsRemovedA Assert.Equal(4, executionSequence.Count); } + [Fact] + public async Task PersistStateAsync_InvokesAllCallbacksWhenFirstCallbackUnregistersItself() + { + // Arrange + var state = new Dictionary(); + var store = new TestStore(state); + var persistenceManager = new ComponentStatePersistenceManager( + NullLogger.Instance, + CreateServiceProvider()); + var renderer = new TestRenderer(); + + var executionSequence = new List(); + + // Register the first callback that will unregister itself - this is the key test case + // where the first callback removes itself from the collection during iteration + PersistingComponentStateSubscription firstSubscription = default; + firstSubscription = persistenceManager.State.RegisterOnPersisting(() => + { + executionSequence.Add(1); + firstSubscription.Dispose(); // Remove itself from the collection + return Task.CompletedTask; + }, new TestRenderMode()); + + // Register additional callbacks to ensure they still get executed + persistenceManager.State.RegisterOnPersisting(() => + { + executionSequence.Add(2); + return Task.CompletedTask; + }, new TestRenderMode()); + + persistenceManager.State.RegisterOnPersisting(() => + { + executionSequence.Add(3); + return Task.CompletedTask; + }, new TestRenderMode()); + + // Act + await persistenceManager.PersistStateAsync(store, renderer); + + // Assert + // All callbacks should be executed even though the first one removed itself + Assert.Contains(1, executionSequence); + Assert.Contains(2, executionSequence); + Assert.Contains(3, executionSequence); + Assert.Equal(3, executionSequence.Count); + } + [Fact] public async Task RestoreStateAsync_ValidatesOnlySupportUpdatesWhenRestoreContextValueUpdate() { diff --git a/src/aspnetcore/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs b/src/aspnetcore/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs index 273246516f7a..fe0be71cfa25 100644 --- a/src/aspnetcore/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs +++ b/src/aspnetcore/src/Components/Components/test/PersistentValueProviderComponentSubscriptionTests.cs @@ -256,14 +256,14 @@ public async Task GetOrComputeLastValue_FollowsCorrectValueTransitionSequence() // Pre-populate the state with serialized data var key = PersistentStateValueProviderKeyResolver.ComputeKey(componentState, nameof(TestComponent.State)); - appState[key] = JsonSerializer.SerializeToUtf8Bytes("first-restored-value", JsonSerializerOptions.Web); + appState[key] = JsonSerializer.SerializeToUtf8Bytes("first-persisted-value", JsonSerializerOptions.Web); await manager.RestoreStateAsync(new TestStore(appState), RestoreContext.InitialValue); await renderer.Dispatcher.InvokeAsync(() => renderer.RenderRootComponentAsync(componentId, ParameterView.Empty)); var cascadingParameterInfo = CreateCascadingParameterInfo(nameof(TestComponent.State), typeof(string)); // Act & Assert - First call: Returns restored value from state - Assert.Equal("first-restored-value", component.State); + Assert.Equal("first-persisted-value", provider.GetCurrentValue(componentState, cascadingParameterInfo)); // Change the component's property value component.State = "updated-property-value"; @@ -279,7 +279,7 @@ public async Task GetOrComputeLastValue_FollowsCorrectValueTransitionSequence() }; // Simulate invoking the callback with a value update. await renderer.Dispatcher.InvokeAsync(() => manager.RestoreStateAsync(new TestStore(newState), RestoreContext.ValueUpdate)); - Assert.Equal("second-restored-value", component.State); + Assert.Equal("second-restored-value", provider.GetCurrentValue(componentState, cascadingParameterInfo)); component.State = "another-updated-value"; // Other calls: Returns the updated value from state diff --git a/src/aspnetcore/src/Components/Components/test/RendererTest.cs b/src/aspnetcore/src/Components/Components/test/RendererTest.cs index 492b5c8cc2f9..56fc26840edd 100644 --- a/src/aspnetcore/src/Components/Components/test/RendererTest.cs +++ b/src/aspnetcore/src/Components/Components/test/RendererTest.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; +using System.Reflection; using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Components.CompilerServices; using Microsoft.AspNetCore.Components.HotReload; @@ -2242,7 +2243,7 @@ public void RenderBatchIncludesListOfDisposedComponents() .Where(frame => frame.FrameType == RenderTreeFrameType.Component) .Select(frame => frame.ComponentId) .ToList(); - var childComponent3 = batch.ReferenceFrames.Where(f => f.ComponentId == 3) + var childComponent3 = batch.ReferenceFrames.Where(f => f.FrameType == RenderTreeFrameType.Component && f.ComponentId == 3) .Single().Component; Assert.Equal(new[] { 1, 2 }, childComponentIds); Assert.IsType(childComponent3); @@ -3049,7 +3050,7 @@ public void QueuedRenderIsSkippedIfComponentWasAlreadyDisposedInSameBatch() component.TriggerRender(); var childComponentId = renderer.Batches.Single() .ReferenceFrames - .Where(f => f.ComponentId != 0) + .Where(f => f.FrameType == RenderTreeFrameType.Component && f.ComponentId != 0) .Single() .ComponentId; var origEventHandlerId = renderer.Batches.Single() @@ -5027,6 +5028,40 @@ public async Task DisposingRenderer_UnsubsribesFromHotReloadManager() Assert.False(hotReloadManager.IsSubscribedTo); } + [Fact] + public async Task HotReload_ReRenderPreservesAsyncLocalValues() + { + await using var renderer = new TestRenderer(); + + var hotReloadManager = new HotReloadManager { MetadataUpdateSupported = true }; + renderer.HotReloadManager = hotReloadManager; + HotReloadManager.Default.MetadataUpdateSupported = true; + + var component = new AsyncLocalCaptureComponent(); + + // Establish AsyncLocal value before registering hot reload handler / rendering. + ServiceAccessor.TestAsyncLocal.Value = "AmbientValue"; + + var componentId = renderer.AssignRootComponentId(component); + await renderer.Dispatcher.InvokeAsync(() => renderer.RenderRootComponentAsync(componentId)); + + // Sanity: initial render should not have captured a hot-reload value yet. + Assert.Null(component.HotReloadValue); + + // Simulate hot reload delta applied from a fresh thread (different ExecutionContext) so the AsyncLocal value is lost. + var expected = ServiceAccessor.TestAsyncLocal.Value; + var thread = new Thread(() => + { + // Simulate environment where the ambient value is not present on the hot reload thread. + ServiceAccessor.TestAsyncLocal.Value = null; + hotReloadManager.TriggerOnDeltaApplied(); + }); + thread.Start(); + thread.Join(); + + Assert.Equal(expected, component.HotReloadValue); + } + [Fact] public void ThrowsForUnknownRenderMode_OnComponentType() { @@ -5123,6 +5158,130 @@ public class HasSubstituteComponentRenderMode : RenderModeAttribute } } + [Fact] + public void RenderFragmentContravariance_WorksWithBaseClassParameter() + { + // Arrange + var renderer = new TestRenderer(); + var baseFragment = (RenderFragment)((Animal animal) => builder => + { + builder.AddContent(0, $"Animal: {animal.Name}"); + }); + + var component = new TestComponent(builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(ComponentWithRenderFragmentOfDog.Template), baseFragment); + builder.CloseComponent(); + }); + + // Act + var componentId = renderer.AssignRootComponentId(component); + component.TriggerRender(); + + // Assert - Should compile and render without exception + var batch = renderer.Batches.Single(); + var componentFrame = batch.ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component); + Assert.IsType(componentFrame.Component); + var dogComponent = (ComponentWithRenderFragmentOfDog)componentFrame.Component; + Assert.NotNull(dogComponent.Template); + } + + [Fact] + public void RenderFragmentContravariance_WorksWithInterfaceParameter() + { + // Arrange + var renderer = new TestRenderer(); + var baseFragment = (RenderFragment>)((IList items) => builder => + { + builder.AddContent(0, $"Count: {items.Count}"); + }); + + var component = new TestComponent(builder => + { + builder.OpenComponent(0); + builder.AddComponentParameter(1, nameof(ComponentWithRenderFragmentOfListOfString.Template), baseFragment); + builder.CloseComponent(); + }); + + // Act + var componentId = renderer.AssignRootComponentId(component); + component.TriggerRender(); + + // Assert - Should compile and render without exception + var batch = renderer.Batches.Single(); + var componentFrame = batch.ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component); + Assert.IsType(componentFrame.Component); + var listComponent = (ComponentWithRenderFragmentOfListOfString)componentFrame.Component; + Assert.NotNull(listComponent.Template); + } + + [Fact] + public void RenderFragmentContravariance_WorksWithInterfaceHierarchy() + { + // C# variance only works with reference types. This test uses interface hierarchy. + // IComparable is contravariant, so we can demonstrate the concept + + // Arrange - Create a fragment that accepts any IComparable + RenderFragment baseFragment = (IComparable value) => builder => + { + builder.AddContent(0, $"Value: {value}"); + }; + + // Act - Assign to a variable that accepts string (which implements IComparable) + RenderFragment specificFragment = baseFragment; + var builder = new RenderTreeBuilder(); + var result = specificFragment("test"); + + // Assert - Should compile and execute without exception + result(builder); + Assert.NotNull(result); + } + + [Fact] + public void RenderFragmentContravariance_WorksWithObjectToPrimitiveWrapper() + { + // For value types, contravariance only works when going through object (boxing) + + // Arrange - Create a fragment that accepts object + RenderFragment baseFragment = (object value) => builder => + { + builder.AddContent(0, $"Value: {value}"); + }; + + // Act - Can use this with a string (reference type derived from object) + RenderFragment stringFragment = baseFragment; + var builder = new RenderTreeBuilder(); + var result = stringFragment("test value"); + + // Assert - Should compile and execute without exception + result(builder); + Assert.NotNull(result); + } + + [Fact] + public void RenderFragmentContravariance_WorksWithComparableTypes() + { + // Demonstrating contravariance with reference types implementing IComparable + + // Arrange - Create a fragment that accepts IComparable + RenderFragment baseFragment = (IComparable value) => builder => + { + builder.AddContent(0, $"Value: {value}"); + }; + + // Act - Can use this with Version (reference type that implements IComparable) + RenderFragment versionFragment = baseFragment; + var builder = new RenderTreeBuilder(); + var result = versionFragment(new Version(1, 0)); + + // Assert - Should compile and execute without exception + result(builder); + Assert.NotNull(result); + } + [HasUnknownRenderMode] private class ComponentWithUnknownRenderMode : IComponent { @@ -5180,6 +5339,34 @@ protected override Task UpdateDisplayAsync(in RenderBatch renderBatch) => Task.CompletedTask; } + private class ServiceAccessor + { + public static AsyncLocal TestAsyncLocal = new AsyncLocal(); + } + + private class AsyncLocalCaptureComponent : IComponent + { + private bool _initialized; + private RenderHandle _renderHandle; + public string HotReloadValue { get; private set; } + + public void Attach(RenderHandle renderHandle) => _renderHandle = renderHandle; + + public Task SetParametersAsync(ParameterView parameters) + { + if (!_initialized) + { + _initialized = true; // First (normal) render, don't capture. + } + else + { + // Hot reload re-render path. + HotReloadValue = ServiceAccessor.TestAsyncLocal.Value; + } + return Task.CompletedTask; + } + } + private class TestComponent : IComponent, IDisposable { private RenderHandle _renderHandle; @@ -6099,4 +6286,68 @@ public ImplicitlyConvertsToString(string value) public static implicit operator string(ImplicitlyConvertsToString value) => value._value; } + + // Test classes for RenderFragment contravariance + private class Animal + { + public string Name { get; set; } = string.Empty; + } + + private class Dog : Animal + { + public string Breed { get; set; } = string.Empty; + } + + private class ComponentWithRenderFragmentOfDog : AutoRenderComponent + { + [Parameter] + public RenderFragment Template { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (Template != null) + { + var dog = new Dog { Name = "Buddy", Breed = "Golden Retriever" }; + builder.AddContent(0, Template(dog)); + } + } + } + + private class ComponentWithRenderFragmentOfListOfString : AutoRenderComponent + { + [Parameter] + public RenderFragment> Template { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (Template != null) + { + var list = new List { "Item1", "Item2", "Item3" }; + builder.AddContent(0, Template(list)); + } + } + } + + // Struct that implements IComparable for testing contravariance + private struct TestStructWithInterface : IComparable + { + public int Value { get; set; } + + public int CompareTo(object obj) + { + if (obj is TestStructWithInterface other) + { + return Value.CompareTo(other.Value); + } + return 1; + } + } + + // Enum for testing contravariance (enums implement IConvertible) + private enum TestEnum + { + Value1, + Value2, + Value3 + } } diff --git a/src/aspnetcore/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs b/src/aspnetcore/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs index 33e6cc1e0e40..5b0983369a06 100644 --- a/src/aspnetcore/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs +++ b/src/aspnetcore/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs @@ -157,7 +157,6 @@ public async Task Post_CanRunAsynchronously_CaptureExecutionContext() } [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61639")] public async Task Post_CanRunAsynchronously_WhenBusy_Exception() { // Arrange diff --git a/src/aspnetcore/src/Components/Components/test/RouteViewTest.cs b/src/aspnetcore/src/Components/Components/test/RouteViewTest.cs index 09715b18d239..7a1fc359ae0d 100644 --- a/src/aspnetcore/src/Components/Components/test/RouteViewTest.cs +++ b/src/aspnetcore/src/Components/Components/test/RouteViewTest.cs @@ -23,7 +23,7 @@ public RouteViewTest() var services = serviceCollection.BuildServiceProvider(); _renderer = new TestRenderer(services); - var componentFactory = new ComponentFactory(new DefaultComponentActivator(services), _renderer); + var componentFactory = new ComponentFactory(new DefaultComponentActivator(services), new DefaultComponentPropertyActivator(), _renderer); _routeViewComponent = (RouteView)componentFactory.InstantiateComponent(services, typeof(RouteView), null, null); _routeViewComponentId = _renderer.AssignRootComponentId(_routeViewComponent); diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs index a81610542792..7ce2a54419b0 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs @@ -20,8 +20,8 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp private readonly object _lock = new(); private readonly List> _conventions = []; private readonly List> _finallyConventions = []; + private readonly List> _componentApplicationBuilderActions = []; private readonly RazorComponentDataSourceOptions _options = new(); - private readonly ComponentApplicationBuilder _builder; private readonly IEndpointRouteBuilder _endpointRouteBuilder; private readonly ResourceCollectionResolver _resourceCollectionResolver; private readonly RenderModeEndpointProvider[] _renderModeEndpointProviders; @@ -32,33 +32,29 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp private IChangeToken _changeToken; private IDisposable? _disposableChangeToken; // THREADING: protected by _lock - public Func SetDisposableChangeTokenAction = disposableChangeToken => disposableChangeToken; - // Internal for testing. - internal ComponentApplicationBuilder Builder => _builder; internal List> Conventions => _conventions; + internal List> ComponentApplicationBuilderActions => _componentApplicationBuilderActions; + internal CancellationTokenSource ChangeTokenSource => _cancellationTokenSource; public RazorComponentEndpointDataSource( - ComponentApplicationBuilder builder, IEnumerable renderModeEndpointProviders, IEndpointRouteBuilder endpointRouteBuilder, RazorComponentEndpointFactory factory, HotReloadService? hotReloadService = null) { - _builder = builder; _endpointRouteBuilder = endpointRouteBuilder; _resourceCollectionResolver = new ResourceCollectionResolver(endpointRouteBuilder); _renderModeEndpointProviders = renderModeEndpointProviders.ToArray(); _factory = factory; _hotReloadService = hotReloadService; - HotReloadService.ClearCacheEvent += OnHotReloadClearCache; DefaultBuilder = new RazorComponentsEndpointConventionBuilder( _lock, - builder, endpointRouteBuilder, _options, _conventions, - _finallyConventions); + _finallyConventions, + _componentApplicationBuilderActions); _cancellationTokenSource = new CancellationTokenSource(); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); @@ -106,8 +102,20 @@ private void UpdateEndpoints() lock (_lock) { + _disposableChangeToken?.Dispose(); + _disposableChangeToken = null; + var endpoints = new List(); - var context = _builder.Build(); + + var componentApplicationBuilder = new ComponentApplicationBuilder(); + + foreach (var action in ComponentApplicationBuilderActions) + { + action?.Invoke(componentApplicationBuilder); + } + + var context = componentApplicationBuilder.Build(); + var configuredRenderModesMetadata = new ConfiguredRenderModesMetadata( [.. Options.ConfiguredRenderModes]); @@ -168,8 +176,7 @@ private void UpdateEndpoints() oldCancellationTokenSource?.Dispose(); if (_hotReloadService is { MetadataUpdateSupported: true }) { - _disposableChangeToken?.Dispose(); - _disposableChangeToken = SetDisposableChangeTokenAction(ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints)); + _disposableChangeToken = ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints); } } } @@ -195,15 +202,6 @@ private void AddBlazorWebEndpoints(List endpoints) } } - public void OnHotReloadClearCache(Type[]? types) - { - lock (_lock) - { - _disposableChangeToken?.Dispose(); - _disposableChangeToken = null; - } - } - public override IChangeToken GetChangeToken() { Initialize(); diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs index 84327f3f6d54..a8f178a4fc25 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs @@ -17,9 +17,14 @@ internal class RazorComponentEndpointDataSourceFactory( { public RazorComponentEndpointDataSource CreateDataSource<[DynamicallyAccessedMembers(Component)] TRootComponent>(IEndpointRouteBuilder endpoints) { - var builder = ComponentApplicationBuilder.GetBuilder() ?? - DefaultRazorComponentApplication.Instance.GetBuilder(); + var dataSource = new RazorComponentEndpointDataSource(providers, endpoints, factory, hotReloadService); - return new RazorComponentEndpointDataSource(builder, providers, endpoints, factory, hotReloadService); + dataSource.ComponentApplicationBuilderActions.Add(builder => + { + var assembly = typeof(TRootComponent).Assembly; + IRazorComponentApplication.GetBuilderForAssembly(builder, assembly); + }); + + return dataSource; } } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs index bf16eec3cf9d..c714400b2380 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Components.Endpoints; internal class RazorComponentEndpointFactory { - private static readonly HttpMethodMetadata HttpMethodsMetadata = new([HttpMethods.Get, HttpMethods.Post]); + private static readonly HttpMethodMetadata HttpMethodsMetadata = new([HttpMethods.Get, HttpMethods.Head, HttpMethods.Post]); #pragma warning disable CA1822 // It's a singleton internal void AddEndpoints( diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs index 04b1daa9e952..46808fbfdfb0 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs @@ -18,27 +18,25 @@ public sealed class RazorComponentsEndpointConventionBuilder : IEndpointConventi private readonly RazorComponentDataSourceOptions _options; private readonly List> _conventions; private readonly List> _finallyConventions; + private readonly List> _componentApplicationBuilderActions; internal RazorComponentsEndpointConventionBuilder( object @lock, - ComponentApplicationBuilder builder, IEndpointRouteBuilder endpointRouteBuilder, RazorComponentDataSourceOptions options, List> conventions, - List> finallyConventions) + List> finallyConventions, + List> componentApplicationBuilderActions) { _lock = @lock; - ApplicationBuilder = builder; EndpointRouteBuilder = endpointRouteBuilder; _options = options; _conventions = conventions; _finallyConventions = finallyConventions; + _componentApplicationBuilderActions = componentApplicationBuilderActions; } - /// - /// Gets the that is used to build the endpoints. - /// - internal ComponentApplicationBuilder ApplicationBuilder { get; } + internal List> ComponentApplicationBuilderActions => _componentApplicationBuilderActions; internal string? ManifestPath { get => _options.ManifestPath; set => _options.ManifestPath = value; } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs index fd0a6aec0c4d..7cc4fa4dc764 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs @@ -30,7 +30,7 @@ public static RazorComponentsEndpointConventionBuilder AddAdditionalAssemblies( foreach (var assembly in assemblies) { - builder.ApplicationBuilder.AddAssembly(assembly); + builder.ComponentApplicationBuilderActions.Add(b => b.AddAssembly(assembly)); } return builder; } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/ResourceCollectionUrlEndpoint.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/ResourceCollectionUrlEndpoint.cs index b09e7d7be431..2021246656d5 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/ResourceCollectionUrlEndpoint.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/ResourceCollectionUrlEndpoint.cs @@ -107,7 +107,7 @@ export function get() { return content.ToArray(); } - private static string ComputeFingerprintSuffix(ResourceAssetCollection resourceCollection) + internal static string ComputeFingerprintSuffix(ResourceAssetCollection resourceCollection) { var resources = (IReadOnlyList)resourceCollection; var incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); @@ -118,6 +118,12 @@ private static string ComputeFingerprintSuffix(ResourceAssetCollection resourceC { var url = resource.Url; AppendToHash(incrementalHash, buffer, ref rented, url); + + var additionalData = GetAdditionalDataForFingerprint(resource); + if (additionalData is not null) + { + AppendToHash(incrementalHash, buffer, ref rented, additionalData); + } } incrementalHash.GetCurrentHash(result); // Base64 encoding at most increases size by (4 * byteSize / 3 + 2), @@ -128,6 +134,37 @@ private static string ComputeFingerprintSuffix(ResourceAssetCollection resourceC return fingerprintSpan[..(length + 1)].ToString(); } + private static string? GetAdditionalDataForFingerprint(ResourceAsset resource) + { + // For non-fingerprinted assets (those without a 'label' property), we need to include + // the integrity hash in the fingerprint calculation. This ensures that if the content + // changes between builds, the resource-collection fingerprint also changes. + if (resource.Properties is null) + { + return null; + } + + var hasLabel = false; + string? integrity = null; + foreach (var property in resource.Properties) + { + if (property.Name.Equals("label", StringComparison.OrdinalIgnoreCase)) + { + hasLabel = true; + // No need to continue if we found a label - we won't use integrity + break; + } + else if (property.Name.Equals("integrity", StringComparison.OrdinalIgnoreCase)) + { + integrity = property.Value; + // Continue searching in case there's also a label property + } + } + + // If there's no label (non-fingerprinted) but there is an integrity value, return it + return !hasLabel ? integrity : null; + } + private static void AppendToHash(IncrementalHash incrementalHash, Span buffer, ref byte[]? rented, string value) { if (Encoding.UTF8.TryGetBytes(value, buffer, out var written)) diff --git a/src/aspnetcore/src/Components/Endpoints/src/FormMapping/PrefixResolver.cs b/src/aspnetcore/src/Components/Endpoints/src/FormMapping/PrefixResolver.cs index 64d2a8d8f259..6b6775af4be3 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/FormMapping/PrefixResolver.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/FormMapping/PrefixResolver.cs @@ -58,39 +58,38 @@ public int Compare(FormKey x, FormKey y) { separatorX = x.Value.Span[currentXPos..].IndexOfAny('.', '['); separatorY = y.Value.Span[currentYPos..].IndexOfAny('.', '['); + int compare; if (separatorX == -1 && separatorY == -1) { - // no more segments, compare the remaining substrings + // Both x and y have no more segments, compare the remaining segments return MemoryExtensions.CompareTo(x.Value.Span[currentXPos..], y.Value.Span[currentYPos..], StringComparison.Ordinal); } else if (separatorX == -1) { - // x has no more segments, but y does, so x is less than y - return -1; + // x has no more segments, y has remaining segments + compare = MemoryExtensions.CompareTo(x.Value.Span[currentXPos..], y.Value.Span[currentYPos..][..separatorY], StringComparison.Ordinal); + if (compare == 0 && !checkPrefix) + { + return -1; + } + return compare; } else if (separatorY == -1) { - if (!checkPrefix) + // y has no more segments, x has remaining segments + compare = MemoryExtensions.CompareTo(x.Value.Span[currentXPos..][..separatorX], y.Value.Span[currentYPos..], StringComparison.Ordinal); + if (compare == 0 && !checkPrefix) { - // We are just sorting, so x is greater than y because it has more segments. return 1; } - - var match = MemoryExtensions.CompareTo( - x.Value.Span[currentXPos..][..separatorX], - y.Value.Span[currentYPos..], StringComparison.Ordinal); - - return match; + return compare; } - // both have segments, compare the segments - var segmentX = x.Value.Span[currentXPos..][..separatorX]; - var segmentY = y.Value.Span[currentYPos..][..separatorY]; - var compareResult = MemoryExtensions.CompareTo(segmentX, segmentY, StringComparison.Ordinal); - if (compareResult != 0) + compare = MemoryExtensions.CompareTo(x.Value.Span[currentXPos..][..separatorX], y.Value.Span[currentYPos..][..separatorY], StringComparison.Ordinal); + if (compare != 0) { - return compareResult; + return compare; } currentXPos += separatorX + 1; diff --git a/src/aspnetcore/src/Components/Endpoints/src/Microsoft.AspNetCore.Components.Endpoints.csproj b/src/aspnetcore/src/Components/Endpoints/src/Microsoft.AspNetCore.Components.Endpoints.csproj index de0329c94e63..ab17f88d4940 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Microsoft.AspNetCore.Components.Endpoints.csproj +++ b/src/aspnetcore/src/Components/Endpoints/src/Microsoft.AspNetCore.Components.Endpoints.csproj @@ -51,7 +51,10 @@ + + + diff --git a/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Shipped.txt index 6e93e89dadda..6946d09ccdcd 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Shipped.txt @@ -52,6 +52,8 @@ Microsoft.AspNetCore.Components.PersistedStateSerializationMode Microsoft.AspNetCore.Components.PersistedStateSerializationMode.Infer = 1 -> Microsoft.AspNetCore.Components.PersistedStateSerializationMode Microsoft.AspNetCore.Components.PersistedStateSerializationMode.Server = 2 -> Microsoft.AspNetCore.Components.PersistedStateSerializationMode Microsoft.AspNetCore.Components.PersistedStateSerializationMode.WebAssembly = 3 -> Microsoft.AspNetCore.Components.PersistedStateSerializationMode +Microsoft.AspNetCore.Components.ResourcePreloader +Microsoft.AspNetCore.Components.ResourcePreloader.ResourcePreloader() -> void Microsoft.AspNetCore.Components.Routing.RazorComponentsEndpointHttpContextExtensions Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.ServerAuthenticationStateProvider() -> void @@ -64,9 +66,9 @@ Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.ExecuteAsync(Microsof Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.Parameters.get -> System.Collections.Generic.IReadOnlyDictionary! Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.PreventStreamingRendering.get -> bool Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.PreventStreamingRendering.set -> void -Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComponentResult(System.Type! componentType) -> void Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComponentResult(System.Type! componentType, object! parameters) -> void Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComponentResult(System.Type! componentType, System.Collections.Generic.IReadOnlyDictionary! parameters) -> void +Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComponentResult(System.Type! componentType) -> void Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.StatusCode.set -> void Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult @@ -75,6 +77,7 @@ Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComp Microsoft.AspNetCore.Http.HttpResults.RazorComponentResult.RazorComponentResult(System.Collections.Generic.IReadOnlyDictionary! parameters) -> void Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions Microsoft.Extensions.DependencyInjection.RazorComponentsServiceCollectionExtensions override Microsoft.AspNetCore.Components.ImportMapDefinition.ToString() -> string! override Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task! @@ -86,4 +89,5 @@ static Microsoft.AspNetCore.Components.Endpoints.Infrastructure.ComponentEndpoin static Microsoft.AspNetCore.Components.ImportMapDefinition.Combine(params Microsoft.AspNetCore.Components.ImportMapDefinition![]! sources) -> Microsoft.AspNetCore.Components.ImportMapDefinition! static Microsoft.AspNetCore.Components.ImportMapDefinition.FromResourceCollection(Microsoft.AspNetCore.Components.ResourceAssetCollection! assets) -> Microsoft.AspNetCore.Components.ImportMapDefinition! static Microsoft.AspNetCore.Components.Routing.RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting(this Microsoft.AspNetCore.Http.HttpContext! context) -> bool +static Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions.RegisterPersistentService(this Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! builder, Microsoft.AspNetCore.Components.IComponentRenderMode! renderMode) -> Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! static Microsoft.Extensions.DependencyInjection.RazorComponentsServiceCollectionExtensions.AddRazorComponents(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action? configure = null) -> Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! diff --git a/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Unshipped.txt index 424ca155339b..ac1780ba883e 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/Endpoints/src/PublicAPI.Unshipped.txt @@ -1,5 +1,3 @@ #nullable enable -Microsoft.AspNetCore.Components.ResourcePreloader -Microsoft.AspNetCore.Components.ResourcePreloader.ResourcePreloader() -> void -Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions -static Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions.RegisterPersistentService(this Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! builder, Microsoft.AspNetCore.Components.IComponentRenderMode! renderMode) -> Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! +Microsoft.AspNetCore.Components.Endpoints.BasePath +Microsoft.AspNetCore.Components.Endpoints.BasePath.BasePath() -> void diff --git a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs index abc9441d54fa..0276cdfd1515 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs @@ -20,7 +20,7 @@ internal partial class EndpointHtmlRenderer protected override IComponent ResolveComponentForRenderMode([DynamicallyAccessedMembers(Component)] Type componentType, int? parentComponentId, IComponentActivator componentActivator, IComponentRenderMode renderMode) { - if (_isHandlingErrors || _isReExecuted) + if (_isHandlingErrors) { // Ignore the render mode boundary in error scenarios. return componentActivator.CreateInstance(componentType); @@ -168,30 +168,7 @@ private async Task WaitForResultReady(bool waitForQuiescence, PrerenderedCompone } else if (_nonStreamingPendingTasks.Count > 0) { - if (_isReExecuted) - { - HandleNonStreamingTasks(); - } - else - { - await WaitForNonStreamingPendingTasks(); - } - } - } - - public void HandleNonStreamingTasks() - { - if (NonStreamingPendingTasksCompletion == null) - { - foreach (var task in _nonStreamingPendingTasks) - { - _ = GetErrorHandledTask(task); - } - - // Clear the pending tasks since we are handling them - _nonStreamingPendingTasks.Clear(); - - NonStreamingPendingTasksCompletion = Task.CompletedTask; + await WaitForNonStreamingPendingTasks(); } } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs index ac6d24419895..50728a8c3271 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs @@ -324,7 +324,7 @@ private void WriteComponentHtml(int componentId, TextWriter output, bool allowBo } } - private static bool IsProgressivelyEnhancedNavigation(HttpRequest request) + internal static bool IsProgressivelyEnhancedNavigation(HttpRequest request) { // For enhanced nav, the Blazor JS code controls the "accept" header precisely, so we can be very specific about the format var accept = request.Headers.Accept; diff --git a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs index 4d95f68c0fc5..ac24baa2f7d5 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs @@ -85,8 +85,8 @@ internal async Task InitializeStandardComponentServicesAsync( { var navigationManager = httpContext.RequestServices.GetRequiredService(); ((IHostEnvironmentNavigationManager)navigationManager)?.Initialize( - GetContextBaseUri(httpContext.Request), - GetFullUri(httpContext.Request), + GetContextBaseUri(httpContext.Request), + GetFullUri(httpContext.Request), uri => GetErrorHandledTask(OnNavigateTo(uri))); navigationManager?.OnNotFound += (sender, args) => NotFoundEventArgs = args; @@ -168,11 +168,6 @@ protected override ComponentState CreateComponentState(int componentId, ICompone protected override void AddPendingTask(ComponentState? componentState, Task task) { - if (_isReExecuted) - { - return; - } - var streamRendering = componentState is null ? false : ((EndpointComponentState)componentState).StreamRendering; diff --git a/src/aspnetcore/src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs b/src/aspnetcore/src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs index e9930ea24991..1febaa8f4af5 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs @@ -124,6 +124,11 @@ public Task SetParametersAsync(ParameterView parameters) private void PreloadWebAssemblyAssets() { + if (EndpointHtmlRenderer.IsProgressivelyEnhancedNavigation(_httpContext.Request)) + { + return; + } + var preloads = _httpContext.GetEndpoint()?.Metadata.GetMetadata(); if (preloads != null && preloads.TryGetAssets("webassembly", out var preloadAssets)) { diff --git a/src/aspnetcore/src/Components/Endpoints/src/Routing/BasePath.cs b/src/aspnetcore/src/Components/Endpoints/src/Routing/BasePath.cs new file mode 100644 index 000000000000..1e7435d2613e --- /dev/null +++ b/src/aspnetcore/src/Components/Endpoints/src/Routing/BasePath.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.Rendering; + +namespace Microsoft.AspNetCore.Components.Endpoints; + +/// +/// Renders a <base> element whose href value matches the current request path base. +/// +public sealed class BasePath : IComponent +{ + private RenderHandle _renderHandle; + + [Inject] + private NavigationManager NavigationManager { get; set; } = default!; + + void IComponent.Attach(RenderHandle renderHandle) + { + _renderHandle = renderHandle; + } + + Task IComponent.SetParametersAsync(ParameterView parameters) + { + _renderHandle.Render(Render); + return Task.CompletedTask; + } + + private void Render(RenderTreeBuilder builder) + { + builder.OpenElement(0, "base"); + builder.AddAttribute(1, "href", ComputeHref()); + builder.CloseElement(); + } + + private string ComputeHref() + { + var baseUri = NavigationManager.BaseUri; + if (Uri.TryCreate(baseUri, UriKind.Absolute, out var absoluteUri)) + { + return absoluteUri.AbsolutePath; + } + + return "/"; + } +} diff --git a/src/aspnetcore/src/Components/Endpoints/test/Binding/PrefixResolverTests.cs b/src/aspnetcore/src/Components/Endpoints/test/Binding/PrefixResolverTests.cs index 0b710c020706..479a621e5797 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/Binding/PrefixResolverTests.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/Binding/PrefixResolverTests.cs @@ -79,6 +79,40 @@ public void ContainsPrefix_HasEntries_NoMatch(string prefix) Assert.False(result); } + [Theory] + [InlineData("Model")] + [InlineData("Model.Model")] + [InlineData("Model[0].Model")] + public void ContainsPrefix_HasEntries(string prefix) + { + // Arrange - Simulating the exact scenario from debugging + var keys = new string[] { "__RequestVerificationToken", "_handler", "Model.Name", "Model.Model.Name", "Model[0].Model.Name", "Model[0].Name" }; + var container = new PrefixResolver(GetKeys(keys), keys.Length); + + // Act + var result = container.HasPrefix(prefix.AsMemory()); + + // Assert + Assert.True(result); + } + + [Theory] + [InlineData("Model")] + [InlineData("Model.Model")] + [InlineData("Model[0].Model")] + public void ContainsPrefix_DoesNotHaveEntries(string prefix) + { + // Arrange - Simulating the exact scenario from debugging + var keys = new string[] { "__RequestVerificationToken", "_handler" }; + var container = new PrefixResolver(GetKeys(keys), keys.Length); + + // Act + var result = container.HasPrefix(prefix.AsMemory()); + + // Assert + Assert.False(result); + } + [Theory] [InlineData("a")] [InlineData("b")] diff --git a/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs b/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs index 70fa780fcbaf..e1a5116ee0be 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs @@ -245,7 +245,7 @@ public void MapRazorComponents_CanAddConventions_ToBlazorWebEndpoints(string fra private RazorComponentsEndpointConventionBuilder CreateRazorComponentsAppBuilder(IEndpointRouteBuilder endpointBuilder) { var builder = endpointBuilder.MapRazorComponents(); - builder.ApplicationBuilder.AddLibrary(new AssemblyComponentLibraryDescriptor( + builder.ComponentApplicationBuilderActions.Add(b => b.AddLibrary(new AssemblyComponentLibraryDescriptor( "App", [new PageComponentBuilder { PageType = typeof(App), @@ -253,7 +253,7 @@ [new PageComponentBuilder { AssemblyName = "App", }], [] - )); + ))); return builder; } diff --git a/src/aspnetcore/src/Components/Endpoints/test/Builder/ResourceCollectionUrlEndpointTest.cs b/src/aspnetcore/src/Components/Endpoints/test/Builder/ResourceCollectionUrlEndpointTest.cs new file mode 100644 index 000000000000..ae8266dcc61e --- /dev/null +++ b/src/aspnetcore/src/Components/Endpoints/test/Builder/ResourceCollectionUrlEndpointTest.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Components.Endpoints; + +public class ResourceCollectionUrlEndpointTest +{ + [Fact] + public void ComputeFingerprintSuffix_IncludesIntegrityForNonFingerprintedAssets() + { + // Arrange - Create a collection with non-fingerprinted assets that have integrity hashes + ResourceAsset[] resources = + [ + // Non-fingerprinted asset with integrity (simulates WasmFingerprintAssets=false scenario) + new("/_framework/MyApp.dll", + [ + new("integrity", "sha256-ABC123") + ]), + new("/_framework/System.dll", + [ + new("integrity", "sha256-XYZ789") + ]) + ]; + var collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint1 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Arrange - Change the integrity of one asset (simulates content change between builds) + resources = + [ + new("/_framework/MyApp.dll", + [ + new("integrity", "sha256-CHANGED") + ]), + new("/_framework/System.dll", + [ + new("integrity", "sha256-XYZ789") + ]) + ]; + collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint2 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Assert - Fingerprints should be different because integrity changed + Assert.NotEqual(fingerprint1, fingerprint2); + } + + [Fact] + public void ComputeFingerprintSuffix_DoesNotIncludeIntegrityForFingerprintedAssets() + { + // Arrange - Create a collection with fingerprinted assets (have label property) + ResourceAsset[] resources = + [ + // Fingerprinted asset with label (simulates WasmFingerprintAssets=true scenario) + new("/_framework/MyApp.ABC123.dll", + [ + new("label", "MyApp.dll"), + new("integrity", "sha256-ABC123") + ]), + new("/_framework/System.XYZ789.dll", + [ + new("label", "System.dll"), + new("integrity", "sha256-XYZ789") + ]) + ]; + var collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint1 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Arrange - Change the integrity (but not the URL) of one asset + // For fingerprinted assets, the URL already contains the hash, so we don't need to include integrity + resources = + [ + new("/_framework/MyApp.ABC123.dll", + [ + new("label", "MyApp.dll"), + new("integrity", "sha256-CHANGED") + ]), + new("/_framework/System.XYZ789.dll", + [ + new("label", "System.dll"), + new("integrity", "sha256-XYZ789") + ]) + ]; + collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint2 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Assert - Fingerprints should be the same because for fingerprinted assets, + // the URL (not integrity) is what matters, and the URL didn't change + Assert.Equal(fingerprint1, fingerprint2); + } + + [Fact] + public void ComputeFingerprintSuffix_HandlesMixedAssets() + { + // Arrange - Mix of fingerprinted and non-fingerprinted assets + ResourceAsset[] resources = + [ + // Fingerprinted asset + new("/_framework/MyApp.ABC123.dll", + [ + new("label", "MyApp.dll"), + new("integrity", "sha256-ABC123") + ]), + // Non-fingerprinted asset + new("/_framework/custom.js", + [ + new("integrity", "sha256-CUSTOM") + ]) + ]; + var collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint1 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Arrange - Change only the non-fingerprinted asset's integrity + resources = + [ + // Fingerprinted asset (same as before) + new("/_framework/MyApp.ABC123.dll", + [ + new("label", "MyApp.dll"), + new("integrity", "sha256-ABC123") + ]), + // Non-fingerprinted asset with changed integrity + new("/_framework/custom.js", + [ + new("integrity", "sha256-MODIFIED") + ]) + ]; + collection = new ResourceAssetCollection(resources); + + // Act + var fingerprint2 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + + // Assert - Fingerprints should be different because non-fingerprinted asset's integrity changed + Assert.NotEqual(fingerprint1, fingerprint2); + } + + [Fact] + public void ComputeFingerprintSuffix_HandlesAssetsWithNoProperties() + { + // Arrange + ResourceAsset[] resources = + [ + new("/_framework/file1.dll", null), + new("/_framework/file2.dll", []) + ]; + var collection = new ResourceAssetCollection(resources); + + // Act & Assert - Should not throw + var fingerprint = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection); + Assert.NotNull(fingerprint); + Assert.StartsWith(".", fingerprint); + } + + [Fact] + public void ComputeFingerprintSuffix_ChangesWhenNonFingerprintedAssetIntegrityChanges() + { + // This test simulates the original bug scenario: + // When WasmFingerprintAssets=false, changing a file (e.g., Counter.razor or wwwroot/styles.css) + // updates its integrity hash but not its URL. The resource-collection fingerprint must change + // to prevent serving stale cached resource-collection.js with outdated integrity values. + + // Arrange - Initial state: non-fingerprinted asset with original integrity + var collection1 = new ResourceAssetCollection( + [ + new("/_framework/app.styles.css", + [ + new("integrity", "sha256-OriginalHash123456") + ]) + ]); + + // Act - Compute initial fingerprint + var fingerprintSuffix1 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection1); + + // Arrange - Simulate content change: same URL, different integrity (e.g., after modifying styles.css) + var collection2 = new ResourceAssetCollection( + [ + new("/_framework/app.styles.css", + [ + new("integrity", "sha256-ModifiedHash789012") + ]) + ]); + + // Act - Compute fingerprint after content change + var fingerprintSuffix2 = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection2); + + // Assert - Fingerprint must be different to ensure browser fetches updated resource-collection.js + Assert.NotEqual(fingerprintSuffix1, fingerprintSuffix2); + } +} diff --git a/src/aspnetcore/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs b/src/aspnetcore/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs index 7fa40b2cd010..62151e6bdbaf 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/EndpointHtmlRendererTest.cs @@ -718,6 +718,133 @@ public async Task CanPrerender_ComponentWithNullParameters_ServerPrerenderedMode Assert.Null(epilogueMarker.Type); } + [Fact] + public async Task CanRender_ClosedGenericComponent() + { + // Arrange + var httpContext = GetHttpContext(); + var writer = new StringWriter(); + + // Act + var parameters = ParameterView.FromDictionary(new Dictionary { { "Value", 42 } }); + var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GenericComponent), null, parameters); + await renderer.Dispatcher.InvokeAsync(() => result.WriteTo(writer, HtmlEncoder.Default)); + var content = writer.ToString(); + + // Assert + Assert.Equal("

Generic value: 42

", content); + } + + [Fact] + public async Task CanRender_ClosedGenericComponent_ServerMode() + { + // Arrange + var httpContext = GetHttpContext(); + var protector = _dataprotectorProvider.CreateProtector(ServerComponentSerializationSettings.DataProtectionProviderPurpose) + .ToTimeLimitedDataProtector(); + + // Act + var parameters = ParameterView.FromDictionary(new Dictionary { { "Value", "TestString" } }); + var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GenericComponent), new InteractiveServerRenderMode(false), parameters); + var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result)); + var match = Regex.Match(content, ComponentPattern); + + // Assert + Assert.True(match.Success); + var marker = JsonSerializer.Deserialize(match.Groups[1].Value, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(0, marker.Sequence); + Assert.Null(marker.PrerenderId); + Assert.NotNull(marker.Descriptor); + Assert.Equal("server", marker.Type); + + var unprotectedServerComponent = protector.Unprotect(marker.Descriptor); + var serverComponent = JsonSerializer.Deserialize(unprotectedServerComponent, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(0, serverComponent.Sequence); + Assert.Equal(typeof(GenericComponent).Assembly.GetName().Name, serverComponent.AssemblyName); + Assert.Equal(typeof(GenericComponent).FullName, serverComponent.TypeName); + Assert.NotEqual(Guid.Empty, serverComponent.InvocationId); + + var parameterDefinition = Assert.Single(serverComponent.ParameterDefinitions); + Assert.Equal("Value", parameterDefinition.Name); + Assert.Equal("System.String", parameterDefinition.TypeName); + Assert.Equal("System.Private.CoreLib", parameterDefinition.Assembly); + + var value = Assert.Single(serverComponent.ParameterValues); + var rawValue = Assert.IsType(value); + Assert.Equal("TestString", rawValue.GetString()); + } + + [Fact] + public async Task CanPrerender_ClosedGenericComponent_ServerMode() + { + // Arrange + var httpContext = GetHttpContext(); + var protector = _dataprotectorProvider.CreateProtector(ServerComponentSerializationSettings.DataProtectionProviderPurpose) + .ToTimeLimitedDataProtector(); + + // Act + var parameters = ParameterView.FromDictionary(new Dictionary { { "Value", 123 } }); + var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GenericComponent), RenderMode.InteractiveServer, parameters); + var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result)); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); + + // Assert + Assert.True(match.Success); + var preamble = match.Groups["preamble"].Value; + var preambleMarker = JsonSerializer.Deserialize(preamble, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(0, preambleMarker.Sequence); + Assert.NotNull(preambleMarker.PrerenderId); + Assert.NotNull(preambleMarker.Descriptor); + Assert.Equal("server", preambleMarker.Type); + + var unprotectedServerComponent = protector.Unprotect(preambleMarker.Descriptor); + var serverComponent = JsonSerializer.Deserialize(unprotectedServerComponent, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.NotEqual(default, serverComponent); + Assert.Equal(0, serverComponent.Sequence); + Assert.Equal(typeof(GenericComponent).Assembly.GetName().Name, serverComponent.AssemblyName); + Assert.Equal(typeof(GenericComponent).FullName, serverComponent.TypeName); + Assert.NotEqual(Guid.Empty, serverComponent.InvocationId); + + var prerenderedContent = match.Groups["content"].Value; + Assert.Equal("

Generic value: 123

", prerenderedContent); + + var epilogue = match.Groups["epilogue"].Value; + var epilogueMarker = JsonSerializer.Deserialize(epilogue, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(preambleMarker.PrerenderId, epilogueMarker.PrerenderId); + } + + [Fact] + public async Task CanPrerender_ClosedGenericComponent_ClientMode() + { + // Arrange + var httpContext = GetHttpContext(); + var writer = new StringWriter(); + + // Act + var parameters = ParameterView.FromDictionary(new Dictionary { { "Value", 456 } }); + var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GenericComponent), RenderMode.InteractiveWebAssembly, parameters); + await renderer.Dispatcher.InvokeAsync(() => result.WriteTo(writer, HtmlEncoder.Default)); + var content = writer.ToString(); + content = AssertAndStripWebAssemblyOptions(content); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); + + // Assert + Assert.True(match.Success); + var preamble = match.Groups["preamble"].Value; + var preambleMarker = JsonSerializer.Deserialize(preamble, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.NotNull(preambleMarker.PrerenderId); + Assert.Equal("webassembly", preambleMarker.Type); + Assert.Equal(typeof(GenericComponent).Assembly.GetName().Name, preambleMarker.Assembly); + Assert.Equal(typeof(GenericComponent).FullName, preambleMarker.TypeName); + + var prerenderedContent = match.Groups["content"].Value; + Assert.Equal("

Generic value: 456

", prerenderedContent); + + var epilogue = match.Groups["epilogue"].Value; + var epilogueMarker = JsonSerializer.Deserialize(epilogue, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(preambleMarker.PrerenderId, epilogueMarker.PrerenderId); + } + [Fact] public async Task ComponentWithInvalidRenderMode_Throws() { diff --git a/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs b/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs index 011056c2e7c6..a8e2280d77e9 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs @@ -23,9 +23,8 @@ public class HotReloadServiceTests public void UpdatesEndpointsWhenHotReloadChangeTokenTriggered() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); var invoked = false; // Act @@ -41,9 +40,8 @@ public void UpdatesEndpointsWhenHotReloadChangeTokenTriggered() public void AddNewEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType( @@ -52,15 +50,17 @@ public void AddNewEndpointWhenDataSourceChanges() Assert.Equal("/server", endpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.Pages.AddFromLibraryInfo("TestAssembly2", new[] - { - new PageComponentBuilder + endpointDataSource.ComponentApplicationBuilderActions.Add( + b => b.Pages.AddFromLibraryInfo("TestAssembly2", new[] { - AssemblyName = "TestAssembly2", - PageType = typeof(StaticComponent), - RouteTemplates = new List { "/app/test" } - } - }); + new PageComponentBuilder + { + AssemblyName = "TestAssembly2", + PageType = typeof(StaticComponent), + RouteTemplates = new List { "/app/test" } + } + })); + HotReloadService.UpdateApplication(null); // Assert - 2 @@ -76,9 +76,8 @@ public void AddNewEndpointWhenDataSourceChanges() public void RemovesEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, @@ -87,7 +86,7 @@ public void RemovesEndpointWhenDataSourceChanges() Assert.Equal("/server", endpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.RemoveLibrary("TestAssembly"); + endpointDataSource.ComponentApplicationBuilderActions.Add(b => b.RemoveLibrary("TestAssembly")); endpointDataSource.Options.ConfiguredRenderModes.Clear(); HotReloadService.UpdateApplication(null); @@ -100,9 +99,8 @@ public void RemovesEndpointWhenDataSourceChanges() public void ModifiesEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, e => e.Metadata.GetMetadata() != null)); @@ -124,9 +122,8 @@ public void ModifiesEndpointWhenDataSourceChanges() public void NotifiesCompositeEndpointDataSource() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); var compositeEndpointDataSource = new CompositeEndpointDataSource( new[] { endpointDataSource }); @@ -137,7 +134,7 @@ public void NotifiesCompositeEndpointDataSource() Assert.Equal("/server", compositeEndpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.Pages.RemoveFromAssembly("TestAssembly"); + endpointDataSource.ComponentApplicationBuilderActions.Add(b => b.Pages.RemoveFromAssembly("TestAssembly")); endpointDataSource.Options.ConfiguredRenderModes.Clear(); HotReloadService.UpdateApplication(null); @@ -148,37 +145,14 @@ public void NotifiesCompositeEndpointDataSource() Assert.Empty(compositePageEndpoints); } - private sealed class WrappedChangeTokenDisposable : IDisposable - { - public bool IsDisposed { get; private set; } - private readonly IDisposable _innerDisposable; - - public WrappedChangeTokenDisposable(IDisposable innerDisposable) - { - _innerDisposable = innerDisposable; - } - - public void Dispose() - { - IsDisposed = true; - _innerDisposable.Dispose(); - } - } - [Fact] public void ConfirmChangeTokenDisposedHotReload() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); - - WrappedChangeTokenDisposable wrappedChangeTokenDisposable = null; - - endpointDataSource.SetDisposableChangeTokenAction = (IDisposable disposableChangeToken) => { - wrappedChangeTokenDisposable = new WrappedChangeTokenDisposable(disposableChangeToken); - return wrappedChangeTokenDisposable; - }; + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder, null); + var changeTokenSource = endpointDataSource.ChangeTokenSource; + var changeToken = endpointDataSource.GetChangeToken(); var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, e => e.Metadata.GetMetadata() != null)); Assert.Equal("/server", endpoint.RoutePattern.RawText); @@ -187,18 +161,21 @@ public void ConfirmChangeTokenDisposedHotReload() // Make a modification and then perform a hot reload. endpointDataSource.Conventions.Add(builder => builder.Metadata.Add(new TestMetadata())); + HotReloadService.UpdateApplication(null); HotReloadService.ClearCache(null); // Confirm the change token is disposed after ClearCache - Assert.True(wrappedChangeTokenDisposable.IsDisposed); + Assert.True(changeToken.HasChanged); + Assert.Throws(() => changeTokenSource.Token); } private class TestMetadata { } - private ComponentApplicationBuilder CreateBuilder(params Type[] types) + private class TestAssembly : Assembly; + + private static void ConfigureBuilder(ComponentApplicationBuilder builder, params Type[] types) { - var builder = new ComponentApplicationBuilder(); builder.AddLibrary(new AssemblyComponentLibraryDescriptor( "TestAssembly", Array.Empty(), @@ -208,8 +185,11 @@ private ComponentApplicationBuilder CreateBuilder(params Type[] types) ComponentType = t, RenderMode = t.GetCustomAttribute() }).ToArray())); + } - return builder; + private static void ConfigureServerComponentBuilder(ComponentApplicationBuilder builder) + { + ConfigureBuilder(builder, typeof(ServerComponent)); } private IServiceProvider CreateServices(params Type[] types) @@ -227,16 +207,21 @@ private IServiceProvider CreateServices(params Type[] types) } private static RazorComponentEndpointDataSource CreateDataSource( - ComponentApplicationBuilder builder, IServiceProvider services, - IComponentRenderMode[] renderModes = null) + Action configureBuilder = null, + IComponentRenderMode[] renderModes = null, + HotReloadService hotReloadService = null) { var result = new RazorComponentEndpointDataSource( - builder, new[] { new MockEndpointProvider() }, new TestEndpointRouteBuilder(services), new RazorComponentEndpointFactory(), - new HotReloadService() { MetadataUpdateSupported = true }); + hotReloadService ?? new HotReloadService() { MetadataUpdateSupported = true }); + + if (configureBuilder is not null) + { + result.ComponentApplicationBuilderActions.Add(configureBuilder); + } if (renderModes != null) { diff --git a/src/aspnetcore/src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Tests.csproj b/src/aspnetcore/src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Tests.csproj index 3fe362d5d236..c9e5d41f10d3 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Tests.csproj +++ b/src/aspnetcore/src/Components/Endpoints/test/Microsoft.AspNetCore.Components.Endpoints.Tests.csproj @@ -6,13 +6,28 @@ - + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs index 52cd757d62ab..26a154b2cb11 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs @@ -24,6 +24,11 @@ public class RazorComponentEndpointDataSourceTest public void RegistersEndpoints() { var endpointDataSource = CreateDataSource(); + endpointDataSource.ComponentApplicationBuilderActions.Add(builder => + { + var assembly = typeof(App).Assembly; + IRazorComponentApplication.GetBuilderForAssembly(builder, assembly); + }); var endpoints = endpointDataSource.Endpoints; @@ -33,10 +38,15 @@ public void RegistersEndpoints() [Fact] public void NoDiscoveredModesDefaultsToStatic() { - - var builder = CreateBuilder(); var services = CreateServices(typeof(ServerEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services); + + endpointDataSource.ComponentApplicationBuilderActions.Add(builder => + { + builder.AddLibrary(new AssemblyComponentLibraryDescriptor( + "TestAssembly", + Array.Empty(), Array.Empty())); + }); var endpoints = endpointDataSource.Endpoints; @@ -199,22 +209,6 @@ public void NoDiscoveredModesDefaultsToStatic() }, }; - private ComponentApplicationBuilder CreateBuilder(params Type[] types) - { - var builder = new ComponentApplicationBuilder(); - builder.AddLibrary(new AssemblyComponentLibraryDescriptor( - "TestAssembly", - Array.Empty(), - types.Select(t => new ComponentBuilder - { - AssemblyName = "TestAssembly", - ComponentType = t, - RenderMode = t.GetCustomAttribute() - }).ToArray())); - - return builder; - } - private IServiceProvider CreateServices(params Type[] types) { var services = new ServiceCollection(); @@ -230,12 +224,10 @@ private IServiceProvider CreateServices(params Type[] types) } private RazorComponentEndpointDataSource CreateDataSource( - ComponentApplicationBuilder builder = null, IServiceProvider services = null, IComponentRenderMode[] renderModes = null) { var result = new RazorComponentEndpointDataSource( - builder ?? DefaultRazorComponentApplication.Instance.GetBuilder(), services?.GetService>() ?? Enumerable.Empty(), new TestEndpointRouteBuilder(services ?? CreateServices()), new RazorComponentEndpointFactory(), diff --git a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointFactoryTest.cs b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointFactoryTest.cs index 9ad0897be660..ef23326a6fa0 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointFactoryTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointFactoryTest.cs @@ -47,6 +47,7 @@ public void AddEndpoints_CreatesEndpointWithExpectedMetadata() var methods = Assert.Single(endpoint.Metadata.GetOrderedMetadata()); Assert.Collection(methods.HttpMethods, method => Assert.Equal("GET", method), + method => Assert.Equal("HEAD", method), method => Assert.Equal("POST", method) ); } diff --git a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointInvokerTest.cs b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointInvokerTest.cs index 88dff7565185..c67c9e078b5a 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointInvokerTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointInvokerTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Endpoints.Tests.TestComponents; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; @@ -51,6 +52,49 @@ public async Task Invoker_RejectsPostRequestsWithNonFormDataContentTypesAsync() Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); } + [Fact] + public async Task Invoker_HandlesHeadRequestAsync() + { + // Arrange + var services = new ServiceCollection().AddRazorComponents() + .Services.AddAntiforgery() + .AddSingleton(new ConfigurationBuilder().Build()) + .AddSingleton(new TestWebHostEnvironment()) + .BuildServiceProvider(); + + var invoker = new RazorComponentEndpointInvoker( + new EndpointHtmlRenderer( + services, + NullLoggerFactory.Instance), + NullLogger.Instance); + + var context = new DefaultHttpContext(); + context.SetEndpoint(new RouteEndpoint( + ctx => Task.CompletedTask, + RoutePatternFactory.Parse("/"), + 0, + new EndpointMetadataCollection( + new ComponentTypeMetadata(typeof(SimpleComponent)), + new RootComponentMetadata(typeof(SimpleComponent)), + new ConfiguredRenderModesMetadata(Array.Empty())), + "test")); + context.Request.Method = "HEAD"; + context.Request.Scheme = "https"; + context.Request.Host = new HostString("localhost"); + context.Request.Path = "/"; + context.Response.Body = new MemoryStream(); + context.RequestServices = services; + + // Act + await invoker.Render(context); + + // Assert + // HEAD requests should execute the full request like GET, returning 200 OK with headers. + // The HTTP server (Kestrel) handles suppressing the response body for HEAD requests. + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + Assert.Equal("text/html; charset=utf-8", context.Response.ContentType); + } + private class TestWebHostEnvironment : IWebHostEnvironment { public string WebRootPath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } diff --git a/src/aspnetcore/src/Components/Endpoints/test/Routing/BasePathTest.cs b/src/aspnetcore/src/Components/Endpoints/test/Routing/BasePathTest.cs new file mode 100644 index 000000000000..ec2c4fa02fc6 --- /dev/null +++ b/src/aspnetcore/src/Components/Endpoints/test/Routing/BasePathTest.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.RenderTree; +using Microsoft.AspNetCore.Components.Test.Helpers; + +#nullable enable + +namespace Microsoft.AspNetCore.Components.Endpoints; + +public class BasePathTest +{ + [Fact] + public void PreservesCasingFromNavigationManagerBaseUri() + { + _ = CreateServices(out var renderer, "https://example.com/Dashboard/"); + var componentId = RenderBasePath(renderer); + + Assert.Equal("/Dashboard/", GetHref(renderer, componentId)); + } + + [Theory] + [InlineData("https://example.com/a/b/", "/a/b/")] + [InlineData("https://example.com/a/b", "/a/")] + public void RendersBaseUriPathExactly(string baseUri, string expected) + { + _ = CreateServices(out var renderer, baseUri); + + var componentId = RenderBasePath(renderer); + + Assert.Equal(expected, GetHref(renderer, componentId)); + } + + private static TestServiceProvider CreateServices(out TestRenderer renderer, string baseUri = "https://example.com/app/") + { + var services = new TestServiceProvider(); + var uri = baseUri.EndsWith('/') ? baseUri + "dashboard" : baseUri + "/dashboard"; + var navigationManager = new TestNavigationManager(baseUri, uri); + services.AddService(navigationManager); + services.AddService(services); + + renderer = new TestRenderer(services); + return services; + } + + private static int RenderBasePath(TestRenderer renderer) + { + var component = (BasePath)renderer.InstantiateComponent(); + var componentId = renderer.AssignRootComponentId(component); + renderer.RenderRootComponent(componentId); + return componentId; + } + + private static string? GetHref(TestRenderer renderer, int componentId) + { + var frames = renderer.GetCurrentRenderTreeFrames(componentId); + for (var i = 0; i < frames.Count; i++) + { + ref readonly var frame = ref frames.Array[i]; + if (frame.FrameType == RenderTreeFrameType.Element && frame.ElementName == "base") + { + for (var j = i + 1; j < frames.Count; j++) + { + ref readonly var attribute = ref frames.Array[j]; + if (attribute.FrameType == RenderTreeFrameType.Attribute && attribute.AttributeName == "href") + { + return attribute.AttributeValue?.ToString(); + } + + if (attribute.FrameType != RenderTreeFrameType.Attribute) + { + break; + } + } + } + } + + return null; + } + + private sealed class TestNavigationManager : NavigationManager + { + public TestNavigationManager(string baseUri, string uri) + { + Initialize(baseUri, uri); + } + + protected override void NavigateToCore(string uri, bool forceLoad) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/aspnetcore/src/Components/Endpoints/test/TestComponents/GenericComponent.razor b/src/aspnetcore/src/Components/Endpoints/test/TestComponents/GenericComponent.razor new file mode 100644 index 000000000000..d0fe347178d6 --- /dev/null +++ b/src/aspnetcore/src/Components/Endpoints/test/TestComponents/GenericComponent.razor @@ -0,0 +1,6 @@ +@typeparam TValue + +

Generic value: @(Value?.ToString() ?? "(null)")

+@code { + [Parameter] public TValue Value { get; set; } +} diff --git a/src/aspnetcore/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs b/src/aspnetcore/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs index 559c2881127d..955e74b84d38 100644 --- a/src/aspnetcore/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs +++ b/src/aspnetcore/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs @@ -20,27 +20,6 @@ namespace Microsoft.AspNetCore.Components.Forms; /// public static partial class EditContextDataAnnotationsExtensions { - /// - /// Adds DataAnnotations validation support to the . - /// - /// The . - [Obsolete("Use " + nameof(EnableDataAnnotationsValidation) + " instead.")] - public static EditContext AddDataAnnotationsValidation(this EditContext editContext) - { - EnableDataAnnotationsValidation(editContext); - return editContext; - } - - /// - /// Enables DataAnnotations validation support for the . - /// - /// The . - /// A disposable object whose disposal will remove DataAnnotations validation support from the . - [Obsolete("This API is obsolete and may be removed in future versions. Use the overload that accepts an IServiceProvider instead.")] - public static IDisposable EnableDataAnnotationsValidation(this EditContext editContext) - { - return new DataAnnotationsEventSubscriptions(editContext, null!); - } /// /// Enables DataAnnotations validation support for the . /// diff --git a/src/aspnetcore/src/Components/Forms/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/Forms/src/PublicAPI.Shipped.txt index 3489bd13cb78..9e488e88a198 100644 --- a/src/aspnetcore/src/Components/Forms/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/Forms/src/PublicAPI.Shipped.txt @@ -60,8 +60,8 @@ override Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator.OnParame override Microsoft.AspNetCore.Components.Forms.FieldIdentifier.Equals(object? obj) -> bool override Microsoft.AspNetCore.Components.Forms.FieldIdentifier.GetHashCode() -> int static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.AddDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext) -> Microsoft.AspNetCore.Components.Forms.EditContext! -static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext) -> System.IDisposable! static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext, System.IServiceProvider! serviceProvider) -> System.IDisposable! +static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext) -> System.IDisposable! static Microsoft.AspNetCore.Components.Forms.FieldIdentifier.Create(System.Linq.Expressions.Expression!>! accessor) -> Microsoft.AspNetCore.Components.Forms.FieldIdentifier static readonly Microsoft.AspNetCore.Components.Forms.ValidationRequestedEventArgs.Empty -> Microsoft.AspNetCore.Components.Forms.ValidationRequestedEventArgs! static readonly Microsoft.AspNetCore.Components.Forms.ValidationStateChangedEventArgs.Empty -> Microsoft.AspNetCore.Components.Forms.ValidationStateChangedEventArgs! diff --git a/src/aspnetcore/src/Components/Forms/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/Forms/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..8e50b300e33d 100644 --- a/src/aspnetcore/src/Components/Forms/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/Forms/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +*REMOVED*static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.AddDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext) -> Microsoft.AspNetCore.Components.Forms.EditContext! +*REMOVED*static Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.EnableDataAnnotationsValidation(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext) -> System.IDisposable! diff --git a/src/aspnetcore/src/Components/Forms/test/EditContextDataAnnotationsExtensionsTest.cs b/src/aspnetcore/src/Components/Forms/test/EditContextDataAnnotationsExtensionsTest.cs index 3ebd566d4f65..e58b5a736d6c 100644 --- a/src/aspnetcore/src/Components/Forms/test/EditContextDataAnnotationsExtensionsTest.cs +++ b/src/aspnetcore/src/Components/Forms/test/EditContextDataAnnotationsExtensionsTest.cs @@ -18,16 +18,6 @@ public void CannotUseNullEditContext() Assert.Equal("editContext", ex.ParamName); } - [Fact] - public void ObsoleteApiReturnsEditContextForChaining() - { - var editContext = new EditContext(new object()); -#pragma warning disable 0618 - var returnValue = editContext.AddDataAnnotationsValidation(); -#pragma warning restore 0618 - Assert.Same(editContext, returnValue); - } - [Fact] public void GetsValidationMessagesFromDataAnnotations() { diff --git a/src/aspnetcore/src/Components/Forms/test/Microsoft.AspNetCore.Components.Forms.Tests.csproj b/src/aspnetcore/src/Components/Forms/test/Microsoft.AspNetCore.Components.Forms.Tests.csproj index c6a022be0923..92498c29485f 100644 --- a/src/aspnetcore/src/Components/Forms/test/Microsoft.AspNetCore.Components.Forms.Tests.csproj +++ b/src/aspnetcore/src/Components/Forms/test/Microsoft.AspNetCore.Components.Forms.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Shipped.txt index 1b580b50deff..e9ace4103474 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Shipped.txt @@ -102,6 +102,7 @@ Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.ChildContent.set Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Class.get -> string? Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Class.set -> void Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.DisposeAsync() -> System.Threading.Tasks.ValueTask +Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.HideColumnOptionsAsync() -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.ItemKey.get -> System.Func! Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.ItemKey.set -> void Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Items.get -> System.Linq.IQueryable? @@ -116,6 +117,8 @@ Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Pagination.get -> Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Pagination.set -> void Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.QuickGrid() -> void Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.RefreshDataAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.RowClass.get -> System.Func? +Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.RowClass.set -> void Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.ShowColumnOptionsAsync(Microsoft.AspNetCore.Components.QuickGrid.ColumnBase! column) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.SortByColumnAsync(Microsoft.AspNetCore.Components.QuickGrid.ColumnBase! column, Microsoft.AspNetCore.Components.QuickGrid.SortDirection direction = Microsoft.AspNetCore.Components.QuickGrid.SortDirection.Auto) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.Theme.get -> string? diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Unshipped.txt index a5806f90a9db..8aa8edca4b77 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ #nullable enable -Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.HideColumnOptionsAsync() -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.RowClass.get -> System.Func? -Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.RowClass.set -> void \ No newline at end of file +Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.OnRowClick.get -> Microsoft.AspNetCore.Components.EventCallback +Microsoft.AspNetCore.Components.QuickGrid.QuickGrid.OnRowClick.set -> void diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor index 5228ed1d2b00..e9368104ec43 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor @@ -66,7 +66,15 @@ private void RenderRow(RenderTreeBuilder __builder, int rowIndex, TGridItem item) { var rowClass = RowClass?.Invoke(item); - + var combinedClass = OnRowClick.HasDelegate + ? string.IsNullOrEmpty(rowClass) ? "row-clickable" : $"row-clickable {rowClass}" + : rowClass; + var rowClick = OnRowClick.HasDelegate ? EventCallback.Factory.Create(this, () => OnRowClick.InvokeAsync(item)) : default; + + @foreach (var col in _columns) { @{ col.CellContent(__builder, item); } diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.cs b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.cs index fa20650559b8..f8d34dc34925 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.cs +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.cs @@ -109,6 +109,11 @@ public partial class QuickGrid : IAsyncDisposable /// [Parameter] public Func? RowClass { get; set; } + /// + /// Optional. A callback that is invoked when a row is clicked. + /// + [Parameter] public EventCallback OnRowClick { get; set; } + [Inject] private IServiceProvider Services { get; set; } = default!; [Inject] private IJSRuntime JS { get; set; } = default!; @@ -155,6 +160,8 @@ public partial class QuickGrid : IAsyncDisposable // If the QuickGrid is disposed while the JS module is being loaded, we need to avoid calling JS methods private bool _wasDisposed; + private bool _firstRefreshDataAsync = true; + /// /// Constructs an instance of . /// @@ -311,6 +318,13 @@ public async Task RefreshDataAsync() // because in that case there's going to be a re-render anyway. private async Task RefreshDataCoreAsync() { + // First render of Virtualize component will handle the data load itself. + if (_firstRefreshDataAsync && Virtualize) + { + _firstRefreshDataAsync = false; + return; + } + // Move into a "loading" state, cancelling any earlier-but-still-pending load _pendingDataLoadCancellationTokenSource?.Cancel(); var thisLoadCts = _pendingDataLoadCancellationTokenSource = new CancellationTokenSource(); @@ -326,7 +340,6 @@ private async Task RefreshDataCoreAsync() else { // If we're not using Virtualize, we build and execute a request against the items provider directly - _lastRefreshedPaginationStateHash = Pagination?.GetHashCode(); var startIndex = Pagination is null ? 0 : (Pagination.CurrentPageIndex * Pagination.ItemsPerPage); var request = new GridItemsProviderRequest( startIndex, Pagination?.ItemsPerPage, _sortByColumn, _sortByAscending, thisLoadCts.Token); @@ -336,6 +349,7 @@ private async Task RefreshDataCoreAsync() _currentNonVirtualizedViewItems = result.Items; _ariaBodyRowCount = _currentNonVirtualizedViewItems.Count; Pagination?.SetTotalItemCountAsync(result.TotalItemCount); + _lastRefreshedPaginationStateHash = Pagination?.GetHashCode(); _pendingDataLoadCancellationTokenSource = null; } } diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.css b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.css index 4747ec263339..e6766fa5040c 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.css +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.css @@ -116,3 +116,8 @@ html[dir=rtl] .col-justify-end .col-options { right: unset; left: 0; } + +/* Clickable rows when OnRowClick is set */ +tr.row-clickable { + cursor: pointer; +} diff --git a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/test/Microsoft.AspNetCore.Components.QuickGrid.Tests.csproj b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/test/Microsoft.AspNetCore.Components.QuickGrid.Tests.csproj index 21eb671ff544..e6af048ce8d2 100644 --- a/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/test/Microsoft.AspNetCore.Components.QuickGrid.Tests.csproj +++ b/src/aspnetcore/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/test/Microsoft.AspNetCore.Components.QuickGrid.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/aspnetcore/src/Components/Samples/BlazorServerApp/BlazorServerApp.csproj b/src/aspnetcore/src/Components/Samples/BlazorServerApp/BlazorServerApp.csproj index 02f0243552d8..5f86734b1619 100644 --- a/src/aspnetcore/src/Components/Samples/BlazorServerApp/BlazorServerApp.csproj +++ b/src/aspnetcore/src/Components/Samples/BlazorServerApp/BlazorServerApp.csproj @@ -9,8 +9,24 @@ + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/App.razor b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/App.razor index e04f7fc9e8e4..17c3f20485ec 100644 --- a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/App.razor +++ b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/App.razor @@ -3,7 +3,7 @@ - + diff --git a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/BlazorUnitedApp.csproj b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/BlazorUnitedApp.csproj index 62460261002b..fac9017777b2 100644 --- a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/BlazorUnitedApp.csproj +++ b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/BlazorUnitedApp.csproj @@ -15,10 +15,23 @@ + + + + + + + + + + + + +
diff --git a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/_Imports.razor b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/_Imports.razor index 1be7a7e9a5bf..8cacc98a9e57 100644 --- a/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/_Imports.razor +++ b/src/aspnetcore/src/Components/Samples/BlazorUnitedApp/_Imports.razor @@ -3,6 +3,7 @@ @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Endpoints @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using BlazorUnitedApp diff --git a/src/aspnetcore/src/Components/Server/src/Circuits/CircuitFactory.cs b/src/aspnetcore/src/Components/Server/src/Circuits/CircuitFactory.cs index 0c086f054c03..a9aefb9fbfc9 100644 --- a/src/aspnetcore/src/Components/Server/src/Circuits/CircuitFactory.cs +++ b/src/aspnetcore/src/Components/Server/src/Circuits/CircuitFactory.cs @@ -93,6 +93,7 @@ public async ValueTask CreateCircuitHostAsync( resourceCollection); circuitActivitySource.Init(new Infrastructure.Server.ComponentsActivityLinkStore(renderer)); + renderer.GetOrCreateWebRootComponentManager(); // In Blazor Server we have already restored the app state, so we can get the handlers from DI. // In Blazor Web the state is provided in the first call to UpdateRootComponents, so we need to diff --git a/src/aspnetcore/src/Components/Server/src/ComponentHub.cs b/src/aspnetcore/src/Components/Server/src/ComponentHub.cs index 7455ef8c6f85..c8a698071c7a 100644 --- a/src/aspnetcore/src/Components/Server/src/ComponentHub.cs +++ b/src/aspnetcore/src/Components/Server/src/ComponentHub.cs @@ -316,9 +316,10 @@ public async ValueTask ResumeCircuit( persistedCircuitState = await _circuitPersistenceManager.ResumeCircuitAsync(circuitId, Context.ConnectionAborted); if (persistedCircuitState == null) { + // The circuit state cannot be retrieved. It might have been deleted or expired. + // We do not send an error to the client as this is a valid scenario + // that will be handled by the client reconnection logic. Log.InvalidInputData(_logger); - await NotifyClientError(Clients.Caller, "The circuit state could not be retrieved. It may have been deleted or expired."); - Context.Abort(); return null; } } diff --git a/src/aspnetcore/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj b/src/aspnetcore/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj index a26f7d17f2d1..3efe5346b208 100644 --- a/src/aspnetcore/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj +++ b/src/aspnetcore/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj @@ -17,10 +17,26 @@ + + + + + + + + + + + + + + + + @@ -39,6 +55,7 @@ + diff --git a/src/aspnetcore/src/Components/Server/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/Server/src/PublicAPI.Shipped.txt index 32aa747a67f0..ca46a148ebbd 100644 --- a/src/aspnetcore/src/Components/Server/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/Server/src/PublicAPI.Shipped.txt @@ -14,10 +14,18 @@ Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitMaxReta Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitMaxRetained.set -> void Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitRetentionPeriod.get -> System.TimeSpan Microsoft.AspNetCore.Components.Server.CircuitOptions.DisconnectedCircuitRetentionPeriod.set -> void +Microsoft.AspNetCore.Components.Server.CircuitOptions.HybridPersistenceCache.get -> Microsoft.Extensions.Caching.Hybrid.HybridCache? +Microsoft.AspNetCore.Components.Server.CircuitOptions.HybridPersistenceCache.set -> void Microsoft.AspNetCore.Components.Server.CircuitOptions.JSInteropDefaultCallTimeout.get -> System.TimeSpan Microsoft.AspNetCore.Components.Server.CircuitOptions.JSInteropDefaultCallTimeout.set -> void Microsoft.AspNetCore.Components.Server.CircuitOptions.MaxBufferedUnacknowledgedRenderBatches.get -> int Microsoft.AspNetCore.Components.Server.CircuitOptions.MaxBufferedUnacknowledgedRenderBatches.set -> void +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitDistributedRetentionPeriod.get -> System.TimeSpan? +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitDistributedRetentionPeriod.set -> void +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryMaxRetained.get -> int +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryMaxRetained.set -> void +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryRetentionPeriod.get -> System.TimeSpan +Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryRetentionPeriod.set -> void Microsoft.AspNetCore.Components.Server.CircuitOptions.RootComponents.get -> Microsoft.AspNetCore.Components.Server.CircuitRootComponentOptions! Microsoft.AspNetCore.Components.Server.CircuitRootComponentOptions Microsoft.AspNetCore.Components.Server.CircuitRootComponentOptions.CircuitRootComponentOptions() -> void @@ -63,12 +71,12 @@ Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder.Services.get - Microsoft.Extensions.DependencyInjection.ServerRazorComponentsBuilderExtensions Microsoft.Extensions.DependencyInjection.ServerSideBlazorBuilderExtensions override Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task! (forwarded, contained in Microsoft.AspNetCore.Components.Endpoints) -static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! path) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! path, System.Action! configureOptions) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! +static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! path) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, System.Action! configureOptions) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions.AddInteractiveServerRenderMode(this Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder) -> Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! +static Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints) -> Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder! static Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions.AddInteractiveServerRenderMode(this Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! +static Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions.AddInteractiveServerRenderMode(this Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder) -> Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! static Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action? configure = null) -> Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder! static Microsoft.Extensions.DependencyInjection.ServerRazorComponentsBuilderExtensions.AddInteractiveServerComponents(this Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! builder, System.Action? configure = null) -> Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder! static Microsoft.Extensions.DependencyInjection.ServerSideBlazorBuilderExtensions.AddCircuitOptions(this Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder! builder, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServerSideBlazorBuilder! diff --git a/src/aspnetcore/src/Components/Server/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/Server/src/PublicAPI.Unshipped.txt index 210a9b4fdd3c..7dc5c58110bf 100644 --- a/src/aspnetcore/src/Components/Server/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/Server/src/PublicAPI.Unshipped.txt @@ -1,9 +1 @@ #nullable enable -Microsoft.AspNetCore.Components.Server.CircuitOptions.HybridPersistenceCache.get -> Microsoft.Extensions.Caching.Hybrid.HybridCache? -Microsoft.AspNetCore.Components.Server.CircuitOptions.HybridPersistenceCache.set -> void -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitDistributedRetentionPeriod.get -> System.TimeSpan? -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitDistributedRetentionPeriod.set -> void -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryMaxRetained.get -> int -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryMaxRetained.set -> void -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryRetentionPeriod.get -> System.TimeSpan -Microsoft.AspNetCore.Components.Server.CircuitOptions.PersistedCircuitInMemoryRetentionPeriod.set -> void diff --git a/src/aspnetcore/src/Components/Server/test/Circuits/ComponentHubTest.cs b/src/aspnetcore/src/Components/Server/test/Circuits/ComponentHubTest.cs index 6582f7d40a7d..e970cdd2f4d2 100644 --- a/src/aspnetcore/src/Components/Server/test/Circuits/ComponentHubTest.cs +++ b/src/aspnetcore/src/Components/Server/test/Circuits/ComponentHubTest.cs @@ -246,8 +246,6 @@ public async Task CannotResumeAppWhenPersistedComponentStateIsNotAvailable() var circuitSecret = await hub.StartCircuit("https://localhost:5000", "https://localhost:5000/subdir", "{}", null); var result = await hub.ResumeCircuit(circuitSecret, "https://localhost:5000", "https://localhost:5000/subdir", "[]", ""); Assert.Null(result); - var errorMessage = "The circuit state could not be retrieved. It may have been deleted or expired."; - mockClientProxy.Verify(m => m.SendCoreAsync("JS.Error", new[] { errorMessage }, It.IsAny()), Times.Once()); } [Fact] diff --git a/src/aspnetcore/src/Components/Server/test/Circuits/RemoteRendererTest.cs b/src/aspnetcore/src/Components/Server/test/Circuits/RemoteRendererTest.cs index 73af5c13651c..92839155eb42 100644 --- a/src/aspnetcore/src/Components/Server/test/Circuits/RemoteRendererTest.cs +++ b/src/aspnetcore/src/Components/Server/test/Circuits/RemoteRendererTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Components.Server; using Microsoft.AspNetCore.Components.Server.Circuits; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.InternalTesting; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -126,6 +127,7 @@ public async Task ProducesNewBatch_WhenABatchGetsAcknowledged() } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61807")] public async Task ProcessBufferedRenderBatches_WritesRenders() { // Arrange @@ -767,7 +769,13 @@ public Task SetParametersAsync(ParameterView parameters) public void TriggerRender() { var task = _renderHandle.Dispatcher.InvokeAsync(() => _renderHandle.Render(_renderFragment)); - Assert.True(task.IsCompletedSuccessfully); + + // Log the task state for debugging purposes. + var status = task.Status; + var innerException = task.Exception?.InnerException; + var message = $"Render task should succeed synchronously.\nStatus: '{status}'\nInner exception: '{innerException}'"; + + Assert.True(task.IsCompletedSuccessfully, message); } } diff --git a/src/aspnetcore/src/Components/Server/test/Circuits/RevalidatingServerAuthenticationStateProviderTest.cs b/src/aspnetcore/src/Components/Server/test/Circuits/RevalidatingServerAuthenticationStateProviderTest.cs index c734415ce0cc..483fc9f667d6 100644 --- a/src/aspnetcore/src/Components/Server/test/Circuits/RevalidatingServerAuthenticationStateProviderTest.cs +++ b/src/aspnetcore/src/Components/Server/test/Circuits/RevalidatingServerAuthenticationStateProviderTest.cs @@ -142,7 +142,6 @@ public async Task StopsRevalidatingAfterDisposal() } [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/60472")] public async Task SuppliesCancellationTokenThatSignalsWhenRevalidationLoopIsBeingDiscarded() { // Arrange @@ -174,9 +173,11 @@ public async Task SuppliesCancellationTokenThatSignalsWhenRevalidationLoopIsBein Assert.Equal("different user", (await provider.GetAuthenticationStateAsync()).User.Identity.Name); // Subsequent revalidation can complete successfully + // We are checking all new logs because the revalidation loop iteration + // may happen multiple times (this made the test flaky in the past) await provider.NextValidateAuthenticationStateAsyncCall; - Assert.Collection(provider.RevalidationCallLog.Skip(1), - call => Assert.Equal("different user", call.AuthenticationState.User.Identity.Name)); + Assert.All(provider.RevalidationCallLog.Skip(1), + call => Assert.Equal("different user", call.AuthenticationState.User.Identity.Name)); } [Fact] diff --git a/src/aspnetcore/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs b/src/aspnetcore/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs index 0d04a3135dfe..1950bba84d1d 100644 --- a/src/aspnetcore/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs +++ b/src/aspnetcore/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Globalization; using System.Text.Json; using Microsoft.AspNetCore.Components.Endpoints; using Microsoft.AspNetCore.DataProtection; @@ -75,6 +76,74 @@ public void CanParseSingleMarkerWithNullParameters() Assert.Null(parameters["Parameter"]); } + [Fact] + public void CanParseSingleMarkerForClosedGenericComponent() + { + // Arrange + var markers = SerializeMarkers(CreateMarkers(typeof(GenericTestComponent))); + var serverComponentDeserializer = CreateServerComponentDeserializer(); + + // Act & assert + Assert.True(serverComponentDeserializer.TryDeserializeComponentDescriptorCollection(markers, out var descriptors)); + var deserializedDescriptor = Assert.Single(descriptors); + Assert.Equal(typeof(GenericTestComponent).FullName, deserializedDescriptor.ComponentType.FullName); + Assert.Equal(0, deserializedDescriptor.Sequence); + } + + [Fact] + public void CanParseSingleMarkerForClosedGenericComponentWithStringTypeParameter() + { + // Arrange + var markers = SerializeMarkers(CreateMarkers(typeof(GenericTestComponent))); + var serverComponentDeserializer = CreateServerComponentDeserializer(); + + // Act & assert + Assert.True(serverComponentDeserializer.TryDeserializeComponentDescriptorCollection(markers, out var descriptors)); + var deserializedDescriptor = Assert.Single(descriptors); + Assert.Equal(typeof(GenericTestComponent).FullName, deserializedDescriptor.ComponentType.FullName); + Assert.Equal(0, deserializedDescriptor.Sequence); + } + + [Fact] + public void CanParseSingleMarkerForClosedGenericComponentWithParameters() + { + // Arrange + var markers = SerializeMarkers(CreateMarkers( + (typeof(GenericTestComponent), new Dictionary { ["Value"] = 42 }))); + var serverComponentDeserializer = CreateServerComponentDeserializer(); + + // Act & assert + Assert.True(serverComponentDeserializer.TryDeserializeComponentDescriptorCollection(markers, out var descriptors)); + var deserializedDescriptor = Assert.Single(descriptors); + Assert.Equal(typeof(GenericTestComponent).FullName, deserializedDescriptor.ComponentType.FullName); + Assert.Equal(0, deserializedDescriptor.Sequence); + + var parameters = deserializedDescriptor.Parameters.ToDictionary(); + Assert.Single(parameters); + Assert.Contains("Value", parameters.Keys); + Assert.Equal(42, Convert.ToInt32(parameters["Value"]!, CultureInfo.InvariantCulture)); + } + + [Fact] + public void CanParseMultipleMarkersForClosedGenericComponents() + { + // Arrange + var markers = SerializeMarkers(CreateMarkers(typeof(GenericTestComponent), typeof(GenericTestComponent))); + var serverComponentDeserializer = CreateServerComponentDeserializer(); + + // Act & assert + Assert.True(serverComponentDeserializer.TryDeserializeComponentDescriptorCollection(markers, out var descriptors)); + Assert.Equal(2, descriptors.Count); + + var firstDescriptor = descriptors[0]; + Assert.Equal(typeof(GenericTestComponent).FullName, firstDescriptor.ComponentType.FullName); + Assert.Equal(0, firstDescriptor.Sequence); + + var secondDescriptor = descriptors[1]; + Assert.Equal(typeof(GenericTestComponent).FullName, secondDescriptor.ComponentType.FullName); + Assert.Equal(1, secondDescriptor.Sequence); + } + [Fact] public void CanParseMultipleMarkers() { @@ -517,4 +586,12 @@ private class DynamicallyAddedComponent : IComponent public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterView parameters) => throw new NotImplementedException(); } + + private class GenericTestComponent : IComponent + { + [Parameter] public T Value { get; set; } + + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); + public Task SetParametersAsync(ParameterView parameters) => throw new NotImplementedException(); + } } diff --git a/src/aspnetcore/src/Components/Server/test/Microsoft.AspNetCore.Components.Server.Tests.csproj b/src/aspnetcore/src/Components/Server/test/Microsoft.AspNetCore.Components.Server.Tests.csproj index 2ed7219f993f..4e98df25bed5 100644 --- a/src/aspnetcore/src/Components/Server/test/Microsoft.AspNetCore.Components.Server.Tests.csproj +++ b/src/aspnetcore/src/Components/Server/test/Microsoft.AspNetCore.Components.Server.Tests.csproj @@ -5,8 +5,25 @@ + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/Shared/src/HotReloadManager.cs b/src/aspnetcore/src/Components/Shared/src/HotReloadManager.cs index b760a65004b9..f3ca59cf2651 100644 --- a/src/aspnetcore/src/Components/Shared/src/HotReloadManager.cs +++ b/src/aspnetcore/src/Components/Shared/src/HotReloadManager.cs @@ -25,4 +25,7 @@ internal sealed class HotReloadManager /// MetadataUpdateHandler event. This is invoked by the hot reload host via reflection. /// public static void UpdateApplication(Type[]? _) => Default.OnDeltaApplied?.Invoke(); + + // For testing purposes only + internal void TriggerOnDeltaApplied() => OnDeltaApplied?.Invoke(); } diff --git a/src/aspnetcore/src/Components/Shared/src/RootTypeCache.cs b/src/aspnetcore/src/Components/Shared/src/RootTypeCache.cs index 9c41b8cd1533..016ab5acd814 100644 --- a/src/aspnetcore/src/Components/Shared/src/RootTypeCache.cs +++ b/src/aspnetcore/src/Components/Shared/src/RootTypeCache.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.AspNetCore.Components.HotReload; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -12,10 +13,28 @@ namespace Microsoft.AspNetCore.Components; #endif // A cache for root component types -internal sealed class RootTypeCache +internal sealed class RootTypeCache : IDisposable { private readonly ConcurrentDictionary _typeToKeyLookUp = new(); + public RootTypeCache() + { + if (HotReloadManager.Default.MetadataUpdateSupported) + { + HotReloadManager.Default.OnDeltaApplied += ClearCache; + } + } + + internal void ClearCache() => _typeToKeyLookUp.Clear(); + + public void Dispose() + { + if (HotReloadManager.Default.MetadataUpdateSupported) + { + HotReloadManager.Default.OnDeltaApplied -= ClearCache; + } + } + public Type? GetRootType(string assembly, string type) { var key = new Key(assembly, type); diff --git a/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.Common.ts b/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.Common.ts index 6f8d20d19d05..93bfe6fa2862 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.Common.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.Common.ts @@ -11,6 +11,7 @@ import { discoverServerPersistedState, ServerComponentDescriptor } from './Servi import { fetchAndInvokeInitializers } from './JSInitializers/JSInitializers.Server'; import { RootComponentManager } from './Services/RootComponentManager'; import { WebRendererId } from './Rendering/WebRendererId'; +import { addDispatchEventMiddleware } from './Rendering/WebRendererInteropMethods'; let initializersPromise: Promise | undefined; let appState: string; @@ -51,6 +52,11 @@ async function startServerCore(components: RootComponentManager { + logger.log(LogLevel.Debug, `Dispatching event with handler id ${eventHandlerId}.`); + continuation(); + }); + logger.log(LogLevel.Information, 'Starting up Blazor server-side application.'); Blazor.reconnect = async () => { diff --git a/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.ts b/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.ts index 824c12546963..307e8e9eed4c 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Boot.Server.ts @@ -10,15 +10,19 @@ import { DotNet } from '@microsoft/dotnet-js-interop'; import { InitialRootComponentsList } from './Services/InitialRootComponentsList'; import { JSEventRegistry } from './Services/JSEventRegistry'; +type BlazorServerStartOptions = Partial & { circuit?: Partial }; + let started = false; -function boot(userOptions?: Partial): Promise { +function boot(userOptions?: BlazorServerStartOptions): Promise { if (started) { throw new Error('Blazor has already started.'); } started = true; - const configuredOptions = resolveOptions(userOptions); + // Accept the `circuit` property from the blazor.web.js options format + const normalizedOptions = userOptions?.circuit ?? userOptions; + const configuredOptions = resolveOptions(normalizedOptions); setCircuitOptions(Promise.resolve(configuredOptions || {})); JSEventRegistry.create(Blazor); diff --git a/src/aspnetcore/src/Components/Web.JS/src/Boot.Web.ts b/src/aspnetcore/src/Components/Web.JS/src/Boot.Web.ts index df605ceebc52..82ab6352e8f5 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Boot.Web.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Boot.Web.ts @@ -15,6 +15,7 @@ import { shouldAutoStart } from './BootCommon'; import { Blazor } from './GlobalExports'; import { WebStartOptions } from './Platform/WebStartOptions'; import { attachStreamingRenderingListener } from './Rendering/StreamingRendering'; +import { resetScrollIfNeeded, ScrollResetSchedule } from './Rendering/Renderer'; import { NavigationEnhancementCallbacks, attachProgressivelyEnhancedNavigationListener } from './Services/NavigationEnhancement'; import { WebRootComponentManager } from './Services/WebRootComponentManager'; import { hasProgrammaticEnhancedNavigationHandler, performProgrammaticEnhancedNavigation } from './Services/NavigationUtils'; @@ -39,6 +40,7 @@ function boot(options?: Partial) : Promise { started = true; options = options || {}; options.logLevel ??= LogLevel.Error; + Blazor._internal.isBlazorWeb = true; // Defined here to avoid inadvertently imported enhanced navigation // related APIs in WebAssembly or Blazor Server contexts. @@ -57,6 +59,7 @@ function boot(options?: Partial) : Promise { }, documentUpdated: () => { rootComponentManager.onDocumentUpdated(); + resetScrollIfNeeded(ScrollResetSchedule.AfterDocumentUpdate); jsEventRegistry.dispatchEvent('enhancedload', {}); }, enhancedNavigationCompleted() { diff --git a/src/aspnetcore/src/Components/Web.JS/src/Boot.WebAssembly.ts b/src/aspnetcore/src/Components/Web.JS/src/Boot.WebAssembly.ts index a1b106b924c2..df014548cf08 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Boot.WebAssembly.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Boot.WebAssembly.ts @@ -12,15 +12,19 @@ import { InitialRootComponentsList } from './Services/InitialRootComponentsList' import { JSEventRegistry } from './Services/JSEventRegistry'; import { printErr } from './Platform/Mono/MonoPlatform'; +type BlazorWebAssemblyStartOptions = Partial & { webAssembly?: Partial }; + let started = false; -async function boot(options?: Partial): Promise { +async function boot(options?: BlazorWebAssemblyStartOptions): Promise { if (started) { throw new Error('Blazor has already started.'); } started = true; - setWebAssemblyOptions(Promise.resolve(options || {})); + // Accept the `webAssembly` property from the blazor.web.js options format + const normalizedOptions = options?.webAssembly ?? options ?? {}; + setWebAssemblyOptions(Promise.resolve(normalizedOptions)); JSEventRegistry.create(Blazor); const webAssemblyComponents = discoverComponents(document, 'webassembly') as WebAssemblyComponentDescriptor[]; diff --git a/src/aspnetcore/src/Components/Web.JS/src/GlobalExports.ts b/src/aspnetcore/src/Components/Web.JS/src/GlobalExports.ts index 4e3bf21e6fe4..6bd2fbe69c75 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/GlobalExports.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/GlobalExports.ts @@ -19,6 +19,8 @@ import { attachWebRendererInterop } from './Rendering/WebRendererInteropMethods' import { WebStartOptions } from './Platform/WebStartOptions'; import { RuntimeAPI } from '@microsoft/dotnet-runtime'; import { JSEventRegistry } from './Services/JSEventRegistry'; +import { BinaryMedia } from './Rendering/BinaryMedia'; + // TODO: It's kind of hard to tell which .NET platform(s) some of these APIs are relevant to. // It's important to know this information when dealing with the possibility of mulitple .NET platforms being available. @@ -50,6 +52,7 @@ export interface IBlazor { navigationManager: typeof navigationManagerInternalFunctions | any; domWrapper: typeof domFunctions; Virtualize: typeof Virtualize; + BinaryMedia: typeof BinaryMedia; PageTitle: typeof PageTitle; forceCloseConnection?: () => Promise; InputFile?: typeof InputFile; @@ -72,6 +75,7 @@ export interface IBlazor { renderBatch?: (browserRendererId: number, batchAddress: Pointer) => void; getConfig?: (fileName: string) => Uint8Array | undefined; getApplicationEnvironment?: () => string; + getApplicationCulture?: () => string; dotNetCriticalError?: any; loadLazyAssembly?: any; loadSatelliteAssemblies?: any; @@ -80,6 +84,7 @@ export interface IBlazor { receiveWebAssemblyDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void; receiveWebViewDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void; attachWebRendererInterop?: typeof attachWebRendererInterop; + isBlazorWeb?: boolean; // JSExport APIs dotNetExports?: { @@ -111,6 +116,7 @@ export const Blazor: IBlazor = { NavigationLock, getJSDataStreamChunk: getNextChunk, attachWebRendererInterop, + BinaryMedia, }, }; diff --git a/src/aspnetcore/src/Components/Web.JS/src/InputFile.ts b/src/aspnetcore/src/Components/Web.JS/src/InputFile.ts index 982e224f4628..c6377430f562 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/InputFile.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/InputFile.ts @@ -52,6 +52,15 @@ function init(callbackWrapper: any, elem: InputElement): void { callbackWrapper.invokeMethodAsync('NotifyChange', fileList); }); + + // The 'cancel' event is fired when the user cancels the file picker dialog. + // This event is part of the HTML5 standard and is supported in modern browsers. + // For browsers that don't support this event, it will be silently ignored. + elem.addEventListener('cancel', function(): void { + // Notify with an empty list when the file dialog is cancelled. + elem._blazorFilesById = {}; + callbackWrapper.invokeMethodAsync('NotifyChange', []); + }); } async function toImageFile(elem: InputElement, fileId: number, format: string, maxWidth: number, maxHeight: number): Promise { diff --git a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectDisplay.ts b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectDisplay.ts index a14aa03363c7..4d424447ce90 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectDisplay.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectDisplay.ts @@ -96,6 +96,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay { this.reconnect = options?.type === 'reconnect'; + this.resumeButton.style.display = 'none'; this.reloadButton.style.display = 'none'; this.rejoiningAnimation.style.display = 'block'; this.status.innerHTML = 'Rejoining the server...'; @@ -106,6 +107,8 @@ export class DefaultReconnectDisplay implements ReconnectDisplay { update(options: ReconnectDisplayUpdateOptions): void { this.reconnect = options.type === 'reconnect'; if (this.reconnect) { + this.reloadButton.style.display = 'none'; + this.resumeButton.style.display = 'none'; const { currentAttempt, secondsToNextAttempt } = options as ReconnectOptions; if (currentAttempt === 1 || secondsToNextAttempt === 0) { this.status.innerHTML = 'Rejoining the server...'; @@ -129,12 +132,13 @@ export class DefaultReconnectDisplay implements ReconnectDisplay { failed(): void { this.rejoiningAnimation.style.display = 'none'; if (this.reconnect) { + this.resumeButton.style.display = 'none'; this.reloadButton.style.display = 'block'; this.status.innerHTML = 'Failed to rejoin.
Please retry or reload the page.'; this.document.addEventListener('visibilitychange', this.retryWhenDocumentBecomesVisible); } else { - this.status.innerHTML = 'Failed to resume the session.
Please reload the page.'; - this.resumeButton.style.display = 'none'; + this.status.innerHTML = 'Failed to resume the session.
Please retry or reload the page.'; + this.resumeButton.style.display = 'block'; this.reloadButton.style.display = 'none'; } } @@ -157,7 +161,6 @@ export class DefaultReconnectDisplay implements ReconnectDisplay { const successful = await Blazor.reconnect!(); if (!successful) { // Try to resume the circuit if the reconnect failed - this.update({ type: 'pause', remote: this.remote }); const resumeSuccessful = await Blazor.resumeCircuit!(); if (!resumeSuccessful) { this.rejected(); @@ -178,7 +181,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay { // - exception to mean we didn't reach the server (this can be sync or async) const successful = await Blazor.resumeCircuit!(); if (!successful) { - this.failed(); + this.rejected(); } } catch (err: unknown) { // We got an exception, server is currently unavailable diff --git a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectionHandler.ts b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectionHandler.ts index 31528e850a41..a1db061fcd62 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectionHandler.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectionHandler.ts @@ -126,13 +126,12 @@ class ReconnectionProcess { if (!result) { // Try to resume the circuit if the reconnect failed // If the server responded and refused to reconnect, stop auto-retrying. - this.reconnectDisplay.update({ type: 'pause', remote: true }); const resumeResult = await this.resumeCallback(); if (resumeResult) { return; } - this.reconnectDisplay.failed(); + this.reconnectDisplay.rejected(); return; } return; diff --git a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/UserSpecifiedDisplay.ts b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/UserSpecifiedDisplay.ts index 01106b9a6665..6438168fac6e 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/UserSpecifiedDisplay.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Platform/Circuits/UserSpecifiedDisplay.ts @@ -27,7 +27,9 @@ export class UserSpecifiedDisplay implements ReconnectDisplay { static readonly ReconnectStateChangedEventName = 'components-reconnect-state-changed'; - private reconnect = false; + reconnect = true; + + remote = false; constructor(private dialog: HTMLElement, private readonly document: Document, maxRetries?: number) { this.document = document; @@ -70,10 +72,10 @@ export class UserSpecifiedDisplay implements ReconnectDisplay { this.dispatchReconnectStateChangedEvent({ state: 'retrying', currentAttempt, secondsToNextAttempt }); } if (options.type === 'pause') { - const remote = options.remote; + this.remote = options.remote; this.dialog.classList.remove(UserSpecifiedDisplay.ShowClassName, UserSpecifiedDisplay.RetryingClassName); this.dialog.classList.add(UserSpecifiedDisplay.PausedClassName); - this.dispatchReconnectStateChangedEvent({ state: 'paused', remote: remote }); + this.dispatchReconnectStateChangedEvent({ state: 'paused', remote: this.remote }); } } @@ -90,7 +92,7 @@ export class UserSpecifiedDisplay implements ReconnectDisplay { this.dispatchReconnectStateChangedEvent({ state: 'failed' }); } else { this.dialog.classList.add(UserSpecifiedDisplay.ResumeFailedClassName); - this.dispatchReconnectStateChangedEvent({ state: 'resume-failed' }); + this.dispatchReconnectStateChangedEvent({ state: 'resume-failed', remote: this.remote }); } } diff --git a/src/aspnetcore/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/aspnetcore/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 1465e5f79cc8..efb8cb7d233b 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -154,6 +154,7 @@ function prepareRuntimeConfig(options: Partial, onConfi } Blazor._internal.getApplicationEnvironment = () => loadedConfig.applicationEnvironment!; + Blazor._internal.getApplicationCulture = () => loadedConfig.applicationCulture!; onConfigLoadedCallback?.(loadedConfig); diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/BinaryMedia.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/BinaryMedia.ts new file mode 100644 index 000000000000..c4e2475bd95c --- /dev/null +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/BinaryMedia.ts @@ -0,0 +1,735 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Logger, LogLevel } from '../Platform/Logging/Logger'; +import { ConsoleLogger } from '../Platform/Logging/Loggers'; + +// Minimal File System Access API typings +interface FileSystemWritableFileStream { + write(data: BufferSource | Blob | Uint8Array): Promise; + close(): Promise; + abort(): Promise; +} +interface FileSystemFileHandle { + createWritable(): Promise; +} +interface SaveFilePickerOptions { + suggestedName?: string; +} +declare global { + interface Window { + showSaveFilePicker?: (options?: SaveFilePickerOptions) => Promise; + } +} + +export interface MediaLoadResult { + success: boolean; + fromCache: boolean; + objectUrl: string | null; + error?: string; +} + +/** + * Provides functionality for rendering binary media data in Blazor components. + */ +export class BinaryMedia { + private static readonly CACHE_NAME = 'blazor-media-cache'; + + private static cachePromise?: Promise = undefined; + + private static logger: Logger = new ConsoleLogger(LogLevel.Warning); + + private static loadingElements: Set = new Set(); + + private static activeCacheKey: WeakMap = new WeakMap(); + + private static tracked: WeakMap = new WeakMap(); + + private static observersByParent: WeakMap = new WeakMap(); + + private static controllers: WeakMap = new WeakMap(); + + private static initializeParentObserver(parent: Element): void { + if (this.observersByParent.has(parent)) { + return; + } + + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + // Handle removed nodes within this parent subtree + if (mutation.type === 'childList') { + for (const node of Array.from(mutation.removedNodes)) { + if (node.nodeType === Node.ELEMENT_NODE) { + const element = node as HTMLElement; + + // If the removed element itself is tracked, revoke + if (this.tracked.has(element)) { + this.revokeTrackedUrl(element); + } + + // Any tracked descendants (look for elements that might carry src or href) + element.querySelectorAll('[src],[href]').forEach((child) => { + const childEl = child as HTMLElement; + if (this.tracked.has(childEl)) { + this.revokeTrackedUrl(childEl); + } + }); + } + } + } + + // Attribute changes in this subtree + if (mutation.type === 'attributes') { + const attrName = (mutation as MutationRecord).attributeName; + if (attrName === 'src' || attrName === 'href') { + const element = mutation.target as HTMLElement; + const tracked = this.tracked.get(element); + if (tracked && tracked.attr === attrName) { + const current = element.getAttribute(attrName) || ''; + if (!current || current !== tracked.url) { + this.revokeTrackedUrl(element); + } + } + } + } + } + }); + + observer.observe(parent, { + childList: true, + attributes: true, + attributeFilter: ['src', 'href'], + }); + + this.observersByParent.set(parent, observer); + } + + private static revokeTrackedUrl(el: HTMLElement): void { + const tracked = this.tracked.get(el); + if (tracked) { + try { + URL.revokeObjectURL(tracked.url); + } catch { + // ignore + } + this.tracked.delete(el); + this.loadingElements.delete(el); + this.activeCacheKey.delete(el); + } + // Abort any in-flight stream tied to this element + const controller = this.controllers.get(el); + if (controller) { + try { + controller.abort(); + } catch { + // ignore + } + this.controllers.delete(el); + } + } + + /** + * Single entry point for setting media content - handles cache check and streaming. + */ + public static async setContentAsync( + element: HTMLElement, + streamRef: { stream: () => Promise> } | null, + mimeType: string, + cacheKey: string, + totalBytes: number | null, + targetAttr: 'src' | 'href' + ): Promise { + if (!element || !cacheKey) { + return { success: false, fromCache: false, objectUrl: null, error: 'Invalid parameters' }; + } + + // Ensure we are observing this element's parent + const parent = element.parentElement; + if (parent) { + this.initializeParentObserver(parent); + } + + // If there was a previous different key for this element, abort its in-flight operation + const previousKey = this.activeCacheKey.get(element); + if (previousKey && previousKey !== cacheKey) { + const prevController = this.controllers.get(element); + if (prevController) { + try { + prevController.abort(); + } catch { + // ignore + } + this.controllers.delete(element); + } + } + + this.activeCacheKey.set(element, cacheKey); + + try { + // Try cache first + try { + const cache = await this.getCache(); + if (cache) { + const cachedResponse = await cache.match(encodeURIComponent(cacheKey)); + if (cachedResponse) { + const blob = await cachedResponse.blob(); + const url = URL.createObjectURL(blob); + + this.setUrl(element, url, cacheKey, targetAttr); + return { success: true, fromCache: true, objectUrl: url }; + } + } + } catch (err) { + this.logger.log(LogLevel.Debug, `Cache lookup failed: ${err}`); + } + + if (streamRef) { + const url = await this.streamAndCreateUrl(element, streamRef, mimeType, cacheKey, totalBytes, targetAttr); + if (url) { + return { success: true, fromCache: false, objectUrl: url }; + } + } + + return { success: false, fromCache: false, objectUrl: null, error: 'No/empty stream provided and not in cache' }; + } catch (error) { + this.logger.log(LogLevel.Debug, `Error in setContentAsync: ${error}`); + return { success: false, fromCache: false, objectUrl: null, error: String(error) }; + } + } + + private static async streamAndCreateUrl( + element: HTMLElement, + streamRef: { stream: () => Promise> }, + mimeType: string, + cacheKey: string, + totalBytes: number | null, + targetAttr: 'src' | 'href' + ): Promise { + + // if (targetAttr === 'src' && element instanceof HTMLVideoElement) { + // try { + // const mediaSourceUrl = await this.tryMediaSourceVideoStreaming( + // element, + // streamRef, + // mimeType, + // cacheKey, + // totalBytes + // ); + // if (mediaSourceUrl) { + // return mediaSourceUrl; + // } + // } catch (msErr) { + // this.logger.log(LogLevel.Debug, `MediaSource video streaming path failed, falling back. Error: ${msErr}`); + // } + // } + + this.loadingElements.add(element); + + // Create and track an AbortController for this element + const controller = new AbortController(); + this.controllers.set(element, controller); + + const readable = await streamRef.stream(); + let displayStream = readable; + + if (cacheKey) { + const cache = await this.getCache(); + if (cache) { + const [display, cacheStream] = readable.tee(); + displayStream = display; + cache.put(encodeURIComponent(cacheKey), new Response(cacheStream)).catch(err => { + this.logger.log(LogLevel.Debug, `Failed to put cache entry: ${err}`); + }); + } + } + + let resultUrl: string | null = null; + try { + const { aborted, chunks, bytesRead } = await this.readAllChunks(element, displayStream, controller, totalBytes); + + if (!aborted) { + if (bytesRead === 0) { + if (typeof totalBytes === 'number' && totalBytes > 0) { + throw new Error('Stream was already consumed or at end position'); + } + resultUrl = null; + } else { + const combined = this.combineChunks(chunks); + const baseMimeType = this.extractBaseMimeType(mimeType); + const blob = new Blob([combined.slice()], { type: baseMimeType }); + const url = URL.createObjectURL(blob); + this.setUrl(element, url, cacheKey, targetAttr); + resultUrl = url; + } + } else { + resultUrl = null; + } + } finally { + if (this.controllers.get(element) === controller) { + this.controllers.delete(element); + } + this.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + } + + return resultUrl; + } + + private static async readAllChunks( + element: HTMLElement, + stream: ReadableStream, + controller: AbortController, + totalBytes: number | null + ): Promise<{ aborted: boolean; chunks: Uint8Array[]; bytesRead: number }> { + const chunks: Uint8Array[] = []; + let bytesRead = 0; + for await (const chunk of this.iterateStream(stream, controller.signal)) { + if (controller.signal.aborted) { + return { aborted: true, chunks, bytesRead }; + } + chunks.push(chunk); + bytesRead += chunk.byteLength; + if (totalBytes) { + const progress = Math.min(1, bytesRead / totalBytes); + element.style.setProperty('--blazor-media-progress', progress.toString()); + } + } + return { aborted: controller.signal.aborted, chunks, bytesRead }; + } + + private static combineChunks(chunks: Uint8Array[]): Uint8Array { + if (chunks.length === 1) { + return chunks[0]; + } + + const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0); + const combined = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + combined.set(chunk, offset); + offset += chunk.byteLength; + } + return combined; + } + + private static setUrl(element: HTMLElement, url: string, cacheKey: string, targetAttr: 'src' | 'href'): void { + const tracked = this.tracked.get(element); + if (tracked) { + try { + URL.revokeObjectURL(tracked.url); + } catch { + // ignore + } + } + + this.tracked.set(element, { url, cacheKey, attr: targetAttr }); + + this.setupEventHandlers(element, cacheKey); + + if (targetAttr === 'src') { + (element as HTMLImageElement | HTMLVideoElement).src = url; + } else { + (element as HTMLAnchorElement).href = url; + } + } + + // Streams binary content to a user-selected file when possible, + // otherwise falls back to buffering in memory and triggering a blob download via an anchor. + public static async downloadAsync( + element: HTMLElement, + streamRef: { stream: () => Promise> } | null, + mimeType: string, + totalBytes: number | null, + fileName: string, + ): Promise { + if (!element || !fileName || !streamRef) { + return false; + } + + this.loadingElements.add(element); + const controller = new AbortController(); + this.controllers.set(element, controller); + + try { + const readable = await streamRef.stream(); + + // Native picker direct-to-file streaming available + if (typeof window.showSaveFilePicker === 'function') { + try { + const handle = await window.showSaveFilePicker({ suggestedName: fileName }); + + const writer = await handle.createWritable(); + const writeResult = await this.writeStreamToFile(element, readable, writer, totalBytes, controller); + if (writeResult === 'success') { + return true; + } + if (writeResult === 'aborted') { + return false; + } + } catch (pickerErr) { + this.logger.log(LogLevel.Debug, `Native picker streaming path failed or cancelled: ${pickerErr}`); + } + } + + // In-memory fallback: read all bytes then trigger anchor download + const readResult = await this.readAllChunks(element, readable, controller, totalBytes); + if (readResult.aborted) { + return false; + } + const combined = this.combineChunks(readResult.chunks); + const baseMimeType = this.extractBaseMimeType(mimeType); + const blob = new Blob([combined.slice()], { type: baseMimeType }); + const url = URL.createObjectURL(blob); + this.triggerDownload(url, fileName); + + return true; + } catch (error) { + this.logger.log(LogLevel.Debug, `Error in downloadAsync: ${error}`); + return false; + } finally { + if (this.controllers.get(element) === controller) { + this.controllers.delete(element); + } + this.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + } + } + + private static async writeStreamToFile( + element: HTMLElement, + stream: ReadableStream, + writer: FileSystemWritableFileStream, + totalBytes: number | null, + controller?: AbortController + ): Promise<'success' | 'aborted' | 'error'> { + let written = 0; + try { + for await (const chunk of this.iterateStream(stream, controller?.signal)) { + if (controller?.signal.aborted) { + try { + await writer.abort(); + } catch { + /* ignore */ + } + element.style.removeProperty('--blazor-media-progress'); + return 'aborted'; + } + try { + await writer.write(chunk); + } catch (wErr) { + if (controller?.signal.aborted) { + try { + await writer.abort(); + } catch { + /* ignore */ + } + return 'aborted'; + } + return 'error'; + } + written += chunk.byteLength; + if (totalBytes) { + const progress = Math.min(1, written / totalBytes); + element.style.setProperty('--blazor-media-progress', progress.toString()); + } + } + + if (controller?.signal.aborted) { + try { + await writer.abort(); + } catch { + /* ignore */ + } + element.style.removeProperty('--blazor-media-progress'); + return 'aborted'; + } + + try { + await writer.close(); + } catch (closeErr) { + if (controller?.signal.aborted) { + return 'aborted'; + } + return 'error'; + } + return 'success'; + } catch (e) { + try { + await writer.abort(); + } catch { + /* ignore */ + } + return controller?.signal.aborted ? 'aborted' : 'error'; + } finally { + element.style.removeProperty('--blazor-media-progress'); + } + } + + private static triggerDownload(url: string, fileName: string): void { + try { + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + a.style.display = 'none'; + document.body.appendChild(a); + a.click(); + + setTimeout(() => { + try { + a.remove(); + URL.revokeObjectURL(url); + } catch { + // ignore + } + }, 0); + } catch { + // ignore + } + } + + private static async getCache(): Promise { + if (!('caches' in window)) { + this.logger.log(LogLevel.Warning, 'Cache API not supported in this browser'); + return null; + } + + if (!this.cachePromise) { + this.cachePromise = (async () => { + try { + return await caches.open(this.CACHE_NAME); + } catch (error) { + this.logger.log(LogLevel.Debug, `Failed to open cache: ${error}`); + return null; + } + })(); + } + + const cache = await this.cachePromise; + // If opening failed previously, allow retry next time + if (!cache) { + this.cachePromise = undefined; + } + return cache; + } + + private static setupEventHandlers( + element: HTMLElement, + cacheKey: string | null = null + ): void { + const clearIfActive = () => { + if (!cacheKey || BinaryMedia.activeCacheKey.get(element) === cacheKey) { + BinaryMedia.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + } + }; + + const onLoad = (_e: Event) => { + clearIfActive(); + }; + + const onError = (_e: Event) => { + if (!cacheKey || BinaryMedia.activeCacheKey.get(element) === cacheKey) { + BinaryMedia.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + element.setAttribute('data-state', 'error'); + } + }; + + element.addEventListener('load', onLoad, { once: true }); + element.addEventListener('error', onError, { once: true }); + + if (element instanceof HTMLVideoElement) { + const onLoadedData = (_e: Event) => clearIfActive(); + element.addEventListener('loadeddata', onLoadedData, { once: true }); + } + } + + private static async *iterateStream(stream: ReadableStream, signal?: AbortSignal): AsyncGenerator { + const reader = stream.getReader(); + let finished = false; + try { + while (true) { + if (signal?.aborted) { + try { + await reader.cancel(); + } catch { + // ignore + } + return; + } + const { done, value } = await reader.read(); + if (done) { + finished = true; + return; + } + if (value) { + yield value; + } + } + } finally { + if (!finished) { + try { + await reader.cancel(); + } catch { + // ignore + } + } + try { + reader.releaseLock?.(); + } catch { + // ignore + } + } + } + + /** + * Extracts the base MIME type from a MIME type that may contain codecs. + * Examples: "video/mp4; codecs=\"avc1.64001E\"" -> "video/mp4" + */ + private static extractBaseMimeType(mimeType: string): string { + const semicolonIndex = mimeType.indexOf(';'); + return semicolonIndex !== -1 ? mimeType.substring(0, semicolonIndex).trim() : mimeType; + } + + private static async tryMediaSourceVideoStreaming( + element: HTMLVideoElement, + streamRef: { stream: () => Promise> }, + mimeType: string, + cacheKey: string, + totalBytes: number | null + ): Promise { + try { + if (!('MediaSource' in window) || !MediaSource.isTypeSupported(mimeType)) { + return null; + } + } catch { + return null; + } + + this.loadingElements.add(element); + const controller = new AbortController(); + this.controllers.set(element, controller); + + const mediaSource = new MediaSource(); + const objectUrl = URL.createObjectURL(mediaSource); + + this.setUrl(element, objectUrl, cacheKey, 'src'); + + try { + await new Promise((resolve, reject) => { + const onOpen = () => resolve(); + mediaSource.addEventListener('sourceopen', onOpen, { once: true }); + mediaSource.addEventListener('error', () => reject(new Error('MediaSource error event')), { once: true }); + }); + + if (controller.signal.aborted) { + return null; + } + + const sourceBuffer: SourceBuffer = mediaSource.addSourceBuffer(mimeType); + + const originalStream = await streamRef.stream(); + let displayStream: ReadableStream = originalStream; + + if (cacheKey) { + try { + const cache = await this.getCache(); + if (cache) { + const [display, cacheStream] = originalStream.tee(); + displayStream = display; + cache.put(encodeURIComponent(cacheKey), new Response(cacheStream)) + .catch(err => this.logger.log(LogLevel.Debug, `Failed to put cache entry (MediaSource path): ${err}`)); + } + } catch (cacheErr) { + this.logger.log(LogLevel.Debug, `Cache setup failed (MediaSource path): ${cacheErr}`); + } + } + + let bytesRead = 0; + + for await (const chunk of this.iterateStream(displayStream, controller.signal)) { + if (controller.signal.aborted) { + break; + } + + // Wait until sourceBuffer ready + if (sourceBuffer.updating) { + await new Promise((resolve) => { + const handler = () => resolve(); + sourceBuffer.addEventListener('updateend', handler, { once: true }); + }); + if (controller.signal.aborted) { + break; + } + } + + try { + const copy = new Uint8Array(chunk.byteLength); + copy.set(chunk); + sourceBuffer.appendBuffer(copy); + } catch (appendErr) { + this.logger.log(LogLevel.Debug, `SourceBuffer append failed: ${appendErr}`); + try { + mediaSource.endOfStream(); + } catch { + // ignore + } + break; + } + + bytesRead += chunk.byteLength; + if (totalBytes) { + const progress = Math.min(1, bytesRead / totalBytes); + element.style.setProperty('--blazor-media-progress', progress.toString()); + } + } + + if (controller.signal.aborted) { + try { + URL.revokeObjectURL(objectUrl); + } catch { + // ignore + } + return null; + } + + // Wait for any pending update to finish before ending stream + if (sourceBuffer.updating) { + await new Promise((resolve) => { + const handler = () => resolve(); + sourceBuffer.addEventListener('updateend', handler, { once: true }); + }); + } + try { + mediaSource.endOfStream(); + } catch { + // ignore + } + + this.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + + return objectUrl; + } catch (err) { + try { + URL.revokeObjectURL(objectUrl); + } catch { + // ignore + } + // Remove tracking so fallback can safely set a new URL + this.revokeTrackedUrl(element); + if (controller.signal.aborted) { + return null; + } + return null; + } finally { + if (this.controllers.get(element) === controller) { + this.controllers.delete(element); + } + if (controller.signal.aborted) { + this.loadingElements.delete(element); + element.style.removeProperty('--blazor-media-progress'); + } + } + } +} diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts index 6cd526a49e61..08cfd8f86608 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts @@ -3,12 +3,13 @@ import { RenderBatch, ArrayBuilderSegment, RenderTreeEdit, RenderTreeFrame, EditType, FrameType, ArrayValues } from './RenderBatch/RenderBatch'; import { EventDelegator } from './Events/EventDelegator'; -import { LogicalElement, PermutationListEntry, toLogicalElement, insertLogicalChild, removeLogicalChild, getLogicalParent, getLogicalChild, createAndInsertLogicalContainer, isSvgElement, permuteLogicalChildren, getClosestDomElement, emptyLogicalElement, getLogicalChildrenArray, depthFirstNodeTreeTraversal } from './LogicalElements'; +import { LogicalElement, PermutationListEntry, toLogicalElement, insertLogicalChild, removeLogicalChild, getLogicalParent, getLogicalChild, createAndInsertLogicalContainer, isSvgElement, isMathMLElement, permuteLogicalChildren, getClosestDomElement, emptyLogicalElement, getLogicalChildrenArray, depthFirstNodeTreeTraversal } from './LogicalElements'; import { applyCaptureIdToElement } from './ElementReferenceCapture'; import { attachToEventDelegator as attachNavigationManagerToEventDelegator } from '../Services/NavigationManager'; import { applyAnyDeferredValue, tryApplySpecialProperty } from './DomSpecialPropertyUtil'; const sharedTemplateElemForParsing = document.createElement('template'); const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g'); +const sharedMathMLElemForParsing = document.createElementNS('http://www.w3.org/1998/Math/MathML', 'mrow'); const elementsToClearOnRootComponentRender = new Set(); const internalAttributeNamePrefix = '__internal_'; const eventPreventDefaultAttributeNamePrefix = 'preventDefault_'; @@ -266,9 +267,14 @@ export class BrowserRenderer { const frameReader = batch.frameReader; const tagName = frameReader.elementName(frame)!; - const newDomElementRaw = (tagName === 'svg' || isSvgElement(parent)) ? - document.createElementNS('http://www.w3.org/2000/svg', tagName) : - document.createElement(tagName); + let newDomElementRaw: Element; + if (tagName === 'svg' || isSvgElement(parent)) { + newDomElementRaw = document.createElementNS('http://www.w3.org/2000/svg', tagName); + } else if (tagName === 'math' || isMathMLElement(parent)) { + newDomElementRaw = document.createElementNS('http://www.w3.org/1998/Math/MathML', tagName); + } else { + newDomElementRaw = document.createElement(tagName); + } const newElement = toLogicalElement(newDomElementRaw); let inserted = false; @@ -316,7 +322,7 @@ export class BrowserRenderer { const markupContainer = createAndInsertLogicalContainer(parent, childIndex); const markupContent = batch.frameReader.markupContent(markupFrame); - const parsedMarkup = parseMarkup(markupContent, isSvgElement(parent)); + const parsedMarkup = parseMarkup(markupContent, isSvgElement(parent), isMathMLElement(parent)); let logicalSiblingIndex = 0; while (parsedMarkup.firstChild) { insertLogicalChild(parsedMarkup.firstChild, markupContainer, logicalSiblingIndex++); @@ -410,10 +416,13 @@ export interface ComponentDescriptor { end: Node; } -function parseMarkup(markup: string, isSvg: boolean) { +function parseMarkup(markup: string, isSvg: boolean, isMathML: boolean) { if (isSvg) { sharedSvgElemForParsing.innerHTML = markup || ' '; return sharedSvgElemForParsing; + } else if (isMathML) { + sharedMathMLElemForParsing.innerHTML = markup || ' '; + return sharedMathMLElemForParsing; } else { sharedTemplateElemForParsing.innerHTML = markup || ' '; diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/DomMerging/DomSync.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/DomMerging/DomSync.ts index 2707f3755a63..b86ff3bd8d03 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/DomMerging/DomSync.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/DomMerging/DomSync.ts @@ -308,11 +308,6 @@ function domNodeComparer(a: Node, b: Node): UpdateCost { return UpdateCost.Infinite; } - // Always treat "preloads" as new elements. - if (isPreloadElement(a as Element) || isPreloadElement(b as Element)) { - return UpdateCost.Infinite; - } - return UpdateCost.None; case Node.DOCUMENT_TYPE_NODE: // It's invalid to insert or delete doctype, and we have no use case for doing that. So just skip such @@ -324,10 +319,6 @@ function domNodeComparer(a: Node, b: Node): UpdateCost { } } -function isPreloadElement(el: Element): boolean { - return el.tagName === 'LINK' && el.attributes.getNamedItem('rel')?.value === 'preload'; -} - function upgradeComponentCommentsToLogicalRootComments(root: Node): ComponentDescriptor[] { const serverDescriptors = discoverComponents(root, 'server') as ServerComponentDescriptor[]; const webAssemblyDescriptors = discoverComponents(root, 'webassembly') as WebAssemblyComponentDescriptor[]; diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/Events/EventTypes.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/Events/EventTypes.ts index 89eb799ed3b9..e2cdef267a20 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/Events/EventTypes.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/Events/EventTypes.ts @@ -23,6 +23,12 @@ export function registerCustomEventType(eventName: string, options: EventTypeOpt throw new Error(`The event '${eventName}' is already registered.`); } + // When aliasing a browser event, the custom event name must be different from the browser event name + // to avoid double-triggering (once for the browser event, once for the custom event wrapper) + if (options.browserEventName && eventName === options.browserEventName) { + throw new Error(`The custom event '${eventName}' cannot have the same name as its browserEventName '${options.browserEventName}'. Choose a different name for the custom event.`); + } + // If applicable, register this as an alias of the given browserEventName if (options.browserEventName) { const aliasGroup = browserEventNamesToAliases.get(options.browserEventName); diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/JSRootComponents.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/JSRootComponents.ts index a18a7f2d6cd4..afa013d1a4e9 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/JSRootComponents.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/JSRootComponents.ts @@ -10,7 +10,9 @@ let nextPendingDynamicRootComponentIdentifier = 0; type ComponentParameters = object | null | undefined; let manager: DotNet.DotNetObject | undefined; +let currentRendererId: number | undefined; let jsComponentParametersByIdentifier: JSComponentParametersByIdentifier; +let hasInitializedJsComponents = false; // These are the public APIs at Blazor.rootComponents.* export const RootComponentsFunctions = { @@ -116,28 +118,38 @@ class DynamicRootComponent { // Called by the framework export function enableJSRootComponents( + rendererId: number, managerInstance: DotNet.DotNetObject, jsComponentParameters: JSComponentParametersByIdentifier, jsComponentInitializers: JSComponentIdentifiersByInitializer ): void { - if (manager) { - // This will only happen in very nonstandard cases where someone has multiple hosts. - // It's up to the developer to ensure that only one of them enables dynamic root components. + if (manager && currentRendererId !== rendererId) { + // A different renderer type (e.g., Server vs WebAssembly) is trying to enable JS root components. + // This is a multi-host scenario which is not supported for dynamic root components. throw new Error('Dynamic root components have already been enabled.'); } + // When the same renderer type re-enables (e.g., circuit restart or new circuit on same page), + // accept the new manager. The old manager's DotNetObjectReference is no longer valid anyway + // because the old circuit is gone. We don't dispose the old manager - doing so would cause + // JSDisconnectedException because the circuit that created it no longer exists. + currentRendererId = rendererId; manager = managerInstance; jsComponentParametersByIdentifier = jsComponentParameters; - // Call the registered initializers. This is an arbitrary subset of the JS component types that are registered - // on the .NET side - just those of them that require some JS-side initialization (e.g., to register them - // as custom elements). - for (const [initializerIdentifier, componentIdentifiers] of Object.entries(jsComponentInitializers)) { - const initializerFunc = DotNet.findJSFunction(initializerIdentifier, 0) as JSComponentInitializerCallback; - for (const componentIdentifier of componentIdentifiers) { - const parameters = jsComponentParameters[componentIdentifier]; - initializerFunc(componentIdentifier, parameters); + if (!hasInitializedJsComponents) { + // Call the registered initializers. This is an arbitrary subset of the JS component types that are registered + // on the .NET side - just those of them that require some JS-side initialization (e.g., to register them + // as custom elements). + for (const [initializerIdentifier, componentIdentifiers] of Object.entries(jsComponentInitializers)) { + const initializerFunc = DotNet.findJSFunction(initializerIdentifier, 0) as JSComponentInitializerCallback; + for (const componentIdentifier of componentIdentifiers) { + const parameters = jsComponentParameters[componentIdentifier]; + initializerFunc(componentIdentifier, parameters); + } } + + hasInitializedJsComponents = true; } } diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/LogicalElements.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/LogicalElements.ts index d963499c5a75..cd71867412b9 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/LogicalElements.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/LogicalElements.ts @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { ComponentDescriptor } from '../Services/ComponentDescriptorDiscovery'; +import { ComponentDescriptor, isMetadataComment } from '../Services/ComponentDescriptorDiscovery'; /* A LogicalElement plays the same role as an Element instance from the point of view of the @@ -109,6 +109,12 @@ export function toLogicalElement(element: Node, allowExistingContents?: boolean) } element.childNodes.forEach(child => { + // Skip metadata comments that will be consumed during discovery + // These are not components and should not be part of the logical tree + if (isMetadataComment(child)) { + return; + } + const childLogicalElement = toLogicalElement(child, /* allowExistingContents */ true); childLogicalElement[logicalParentPropname] = element; childrenArray.push(childLogicalElement); @@ -236,6 +242,14 @@ export function isSvgElement(element: LogicalElement): boolean { return closestElement.namespaceURI === 'http://www.w3.org/2000/svg' && closestElement['tagName'] !== 'foreignObject'; } +// MathML elements need to be created with the MathML namespace to render correctly. +// Similar to SVG, MathML has its own namespace (http://www.w3.org/1998/Math/MathML) +// and elements created without this namespace will not render properly in browsers. +export function isMathMLElement(element: LogicalElement): boolean { + const closestElement = getClosestDomElement(element) as any; + return closestElement.namespaceURI === 'http://www.w3.org/1998/Math/MathML'; +} + export function getLogicalChildrenArray(element: LogicalElement): LogicalElement[] { return element[logicalChildrenPropname] as LogicalElement[]; } diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/Renderer.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/Renderer.ts index a5619de92702..cce062e89866 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/Renderer.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/Renderer.ts @@ -11,8 +11,15 @@ import { getAndRemovePendingRootComponentContainer } from './JSRootComponents'; interface BrowserRendererRegistry { [browserRendererId: number]: BrowserRenderer; } + +export enum ScrollResetSchedule { + None, + AfterBatch, // Reset scroll after interactive components finish rendering (interactive navigation) + AfterDocumentUpdate, // Reset scroll after enhanced navigation updates the DOM (enhanced navigation) +} + const browserRenderers: BrowserRendererRegistry = {}; -let shouldResetScrollAfterNextBatch = false; +let pendingScrollResetTiming: ScrollResetSchedule = ScrollResetSchedule.None; export function attachRootComponentToLogicalElement(browserRendererId: number, logicalElement: LogicalElement, componentId: number, appendContent: boolean): void { let browserRenderer = browserRenderers[browserRendererId]; @@ -88,19 +95,28 @@ export function renderBatch(browserRendererId: number, batch: RenderBatch): void browserRenderer.disposeEventHandler(eventHandlerId); } - resetScrollIfNeeded(); + resetScrollIfNeeded(ScrollResetSchedule.AfterBatch); } -export function resetScrollAfterNextBatch(): void { - shouldResetScrollAfterNextBatch = true; -} +export function scheduleScrollReset(timing: ScrollResetSchedule): void { + if (timing !== ScrollResetSchedule.AfterBatch) { + pendingScrollResetTiming = timing; + return; + } -export function resetScrollIfNeeded() { - if (shouldResetScrollAfterNextBatch) { - shouldResetScrollAfterNextBatch = false; + if (pendingScrollResetTiming !== ScrollResetSchedule.AfterDocumentUpdate) { + pendingScrollResetTiming = ScrollResetSchedule.AfterBatch; + } +} - // This assumes the scroller is on the window itself. There isn't a general way to know - // if some other element is playing the role of the primary scroll region. - window.scrollTo && window.scrollTo(0, 0); +export function resetScrollIfNeeded(triggerTiming: ScrollResetSchedule) { + if (pendingScrollResetTiming !== triggerTiming) { + return; } + + pendingScrollResetTiming = ScrollResetSchedule.None; + + // This assumes the scroller is on the window itself. There isn't a general way to know + // if some other element is playing the role of the primary scroll region. + window.scrollTo && window.scrollTo(0, 0); } diff --git a/src/aspnetcore/src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts b/src/aspnetcore/src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts index 08ad1f73553b..d8b20b5a6b81 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts @@ -31,7 +31,7 @@ export function attachWebRendererInterop( if (jsComponentParameters && jsComponentInitializers && Object.keys(jsComponentParameters).length > 0) { const manager = getInteropMethods(rendererId); - enableJSRootComponents(manager, jsComponentParameters, jsComponentInitializers); + enableJSRootComponents(rendererId, manager, jsComponentParameters, jsComponentInitializers); } rendererByIdResolverMap.get(rendererId)?.[0]?.(); diff --git a/src/aspnetcore/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts b/src/aspnetcore/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts index 4fb4e39d8fc2..7f478665c3eb 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts @@ -1,6 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// Metadata comments are consumed during discovery and should not be part of the logical tree. +// They include: WebAssembly options, component state, and web initializers. +export function isMetadataComment(node: Node): boolean { + if (node.nodeType !== Node.COMMENT_NODE) { + return false; + } + const content = node.textContent || ''; + return content.trim().startsWith('Blazor-Server-Component-State:') || + content.trim().startsWith('Blazor-WebAssembly-Component-State:') || + content.trim().startsWith('Blazor-Web-Initializers:') || + content.trim().startsWith('Blazor-WebAssembly:'); +} + export function discoverComponents(root: Node, type: 'webassembly' | 'server' | 'auto'): ComponentDescriptor[] { switch (type) { case 'webassembly': diff --git a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationEnhancement.ts b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationEnhancement.ts index 9dcd7540d866..9eb91a80e01d 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationEnhancement.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationEnhancement.ts @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. import { synchronizeDomContent } from '../Rendering/DomMerging/DomSync'; -import { attachProgrammaticEnhancedNavigationHandler, handleClickForNavigationInterception, hasInteractiveRouter, isForSamePath, isSamePageWithHash, notifyEnhancedNavigationListeners, performScrollToElementOnTheSamePage } from './NavigationUtils'; -import { resetScrollAfterNextBatch, resetScrollIfNeeded } from '../Rendering/Renderer'; +import { attachProgrammaticEnhancedNavigationHandler, handleClickForNavigationInterception, hasInteractiveRouter, isForSamePath, notifyEnhancedNavigationListeners, performScrollToElementOnTheSamePage, isSamePageWithHash } from './NavigationUtils'; +import { scheduleScrollReset, ScrollResetSchedule } from '../Rendering/Renderer'; /* In effect, we have two separate client-side navigation mechanisms: @@ -81,7 +81,7 @@ function performProgrammaticEnhancedNavigation(absoluteInternalHref: string, rep } if (!isForSamePath(absoluteInternalHref, originalLocation)) { - resetScrollAfterNextBatch(); + scheduleScrollReset(ScrollResetSchedule.AfterDocumentUpdate); } performEnhancedPageLoad(absoluteInternalHref, /* interceptedLink */ false); @@ -99,7 +99,7 @@ function onDocumentClick(event: MouseEvent) { handleClickForNavigationInterception(event, absoluteInternalHref => { const originalLocation = location.href; - const shouldScrollToHash = isSamePageWithHash(absoluteInternalHref); + const shouldScrollToHash = isSamePageWithHash(originalLocation, absoluteInternalHref); history.pushState(null, /* ignored title */ '', absoluteInternalHref); if (shouldScrollToHash) { @@ -108,8 +108,7 @@ function onDocumentClick(event: MouseEvent) { let isSelfNavigation = isForSamePath(absoluteInternalHref, originalLocation); performEnhancedPageLoad(absoluteInternalHref, /* interceptedLink */ true); if (!isSelfNavigation) { - resetScrollAfterNextBatch(); - resetScrollIfNeeded(); + scheduleScrollReset(ScrollResetSchedule.AfterDocumentUpdate); } } }); @@ -120,6 +119,11 @@ function onPopState(state: PopStateEvent) { return; } + if (state.state == null && isSamePageWithHash(currentContentUrl, location.href)) { + currentContentUrl = location.href; + return; + } + // load the new page performEnhancedPageLoad(location.href, /* interceptedLink */ false); } diff --git a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationManager.ts b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationManager.ts index b3352b399f55..29e1d892d354 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationManager.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationManager.ts @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. import '@microsoft/dotnet-js-interop'; -import { resetScrollAfterNextBatch } from '../Rendering/Renderer'; +import { scheduleScrollReset, ScrollResetSchedule } from '../Rendering/Renderer'; import { EventDelegator } from '../Rendering/Events/EventDelegator'; import { attachEnhancedNavigationListener, getInteractiveRouterRendererId, handleClickForNavigationInterception, hasInteractiveRouter, hasProgrammaticEnhancedNavigationHandler, isForSamePath, isSamePageWithHash, isWithinBaseUriSpace, performProgrammaticEnhancedNavigation, performScrollToElementOnTheSamePage, scrollToElement, setHasInteractiveRouter, toAbsoluteUri } from './NavigationUtils'; import { WebRendererId } from '../Rendering/WebRendererId'; import { isRendererAttached } from '../Rendering/WebRendererInteropMethods'; +import { IBlazor } from '../GlobalExports'; let hasRegisteredNavigationEventListeners = false; let currentHistoryIndex = 0; @@ -116,18 +117,21 @@ function navigateToFromDotNet(uri: string, options: NavigationOptions): void { function navigateToCore(uri: string, options: NavigationOptions, skipLocationChangingCallback = false): void { const absoluteUri = toAbsoluteUri(uri); + const pageLoadMechanism = currentPageLoadMechanism(); - if (!options.forceLoad && isWithinBaseUriSpace(absoluteUri)) { - if (shouldUseClientSideRouting()) { - performInternalNavigation(absoluteUri, false, options.replaceHistoryEntry, options.historyEntryState, skipLocationChangingCallback); - } else { - performProgrammaticEnhancedNavigation(absoluteUri, options.replaceHistoryEntry); - } - } else { + if (options.forceLoad || !isWithinBaseUriSpace(absoluteUri) || pageLoadMechanism === 'serverside-fullpageload') { // For external navigation, we work in terms of the originally-supplied uri string, // not the computed absoluteUri. This is in case there are some special URI formats // we're unable to translate into absolute URIs. performExternalNavigation(uri, options.replaceHistoryEntry); + } else if (pageLoadMechanism === 'clientside-router') { + performInternalNavigation(absoluteUri, false, options.replaceHistoryEntry, options.historyEntryState, skipLocationChangingCallback); + } else if (pageLoadMechanism === 'serverside-enhanced') { + performProgrammaticEnhancedNavigation(absoluteUri, options.replaceHistoryEntry); + } else { + // Force a compile-time error if some other case needs to be handled in the future + const unreachable: never = pageLoadMechanism; + throw new Error(`Unsupported page load mechanism: ${unreachable}`); } } @@ -150,7 +154,7 @@ function performExternalNavigation(uri: string, replace: boolean) { async function performInternalNavigation(absoluteInternalHref: string, interceptedLink: boolean, replace: boolean, state: string | undefined = undefined, skipLocationChangingCallback = false) { ignorePendingNavigation(); - if (isSamePageWithHash(absoluteInternalHref)) { + if (isSamePageWithHash(location.href, absoluteInternalHref)) { saveToBrowserHistory(absoluteInternalHref, replace, state); performScrollToElementOnTheSamePage(absoluteInternalHref); return; @@ -170,7 +174,7 @@ async function performInternalNavigation(absoluteInternalHref: string, intercept // To avoid ugly flickering effects, we don't want to change the scroll position until // we render the new page. As a best approximation, wait until the next batch. if (!isForSamePath(absoluteInternalHref, location.href)) { - resetScrollAfterNextBatch(); + scheduleScrollReset(ScrollResetSchedule.AfterBatch); } saveToBrowserHistory(absoluteInternalHref, replace, state); @@ -266,7 +270,7 @@ async function notifyLocationChanged(interceptedLink: boolean, internalDestinati } async function onPopState(state: PopStateEvent) { - if (popStateCallback && shouldUseClientSideRouting()) { + if (popStateCallback && currentPageLoadMechanism() !== 'serverside-enhanced') { await popStateCallback(state); } @@ -282,10 +286,24 @@ function getInteractiveRouterNavigationCallbacks(): NavigationCallbacks | undefi return navigationCallbacks.get(interactiveRouterRendererId); } -function shouldUseClientSideRouting() { - return hasInteractiveRouter() || !hasProgrammaticEnhancedNavigationHandler(); +function currentPageLoadMechanism(): PageLoadMechanism { + if (hasInteractiveRouter()) { + return 'clientside-router'; + } else if (hasProgrammaticEnhancedNavigationHandler()) { + return 'serverside-enhanced'; + } else { + // For back-compat, in blazor.server.js or blazor.webassembly.js, we always behave as if there's an interactive + // router even if there isn't one attached. This preserves a niche case where people may call Blazor.navigateTo + // without a router and expect to receive a notification on the .NET side but no page load occurs. + // In blazor.web.js, we explicitly recognize the case where you have neither an interactive nor enhanced SSR router + // attached, and then handle Blazor.navigateTo by doing a full page load because that's more useful (issue #51636). + const isBlazorWeb = (window['Blazor'] as IBlazor)._internal.isBlazorWeb; + return isBlazorWeb ? 'serverside-fullpageload' : 'clientside-router'; + } } +type PageLoadMechanism = 'clientside-router' | 'serverside-enhanced' | 'serverside-fullpageload'; + // Keep in sync with Components/src/NavigationOptions.cs export interface NavigationOptions { forceLoad: boolean; diff --git a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationUtils.ts b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationUtils.ts index bc58636c39c6..9976eafc898c 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationUtils.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/Services/NavigationUtils.ts @@ -47,9 +47,11 @@ export function isWithinBaseUriSpace(href: string) { && (nextChar === '' || nextChar === '/' || nextChar === '?' || nextChar === '#'); } -export function isSamePageWithHash(absoluteHref: string): boolean { - const url = new URL(absoluteHref); - return url.hash !== '' && location.origin === url.origin && location.pathname === url.pathname && location.search === url.search; +export function isSamePageWithHash(oldUrl: string, newUrl: string): boolean { + const a = new URL(oldUrl); + const b = new URL(newUrl); + return a.origin === b.origin && a.pathname === b.pathname + && a.search === b.search && b.hash !== ''; } export function isForSamePath(url1: string, url2: string) { diff --git a/src/aspnetcore/src/Components/Web.JS/src/StreamingInterop.ts b/src/aspnetcore/src/Components/Web.JS/src/StreamingInterop.ts index 193afe97ec19..1e25c2a87a9e 100644 --- a/src/aspnetcore/src/Components/Web.JS/src/StreamingInterop.ts +++ b/src/aspnetcore/src/Components/Web.JS/src/StreamingInterop.ts @@ -23,11 +23,11 @@ function getChunkFromArrayBufferView(data: ArrayBufferView, position: number, ne return nextChunkData; } -const transmittingDotNetToJSStreams = new Map>(); +const transmittingDotNetToJSStreams = new Map>(); export function receiveDotNetDataStream(dispatcher: DotNet.ICallDispatcher, streamId: number, data: Uint8Array, bytesRead: number, errorMessage: string): void { let streamController = transmittingDotNetToJSStreams.get(streamId); if (!streamController) { - const readableStream = new ReadableStream({ + const readableStream = new ReadableStream({ start(controller) { transmittingDotNetToJSStreams.set(streamId, controller); streamController = controller; @@ -44,6 +44,7 @@ export function receiveDotNetDataStream(dispatcher: DotNet.ICallDispatcher, stre streamController!.close(); transmittingDotNetToJSStreams.delete(streamId); } else { - streamController!.enqueue(data.length === bytesRead ? data : data.subarray(0, bytesRead)); + const chunk = data.length === bytesRead ? data : new Uint8Array(data.buffer, data.byteOffset, bytesRead); + streamController!.enqueue(chunk); } } diff --git a/src/aspnetcore/src/Components/Web/src/Forms/DisplayName.cs b/src/aspnetcore/src/Components/Web/src/Forms/DisplayName.cs new file mode 100644 index 000000000000..8cf9ec26f04e --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Forms/DisplayName.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq.Expressions; +using Microsoft.AspNetCore.Components.Rendering; + +namespace Microsoft.AspNetCore.Components.Forms; + +/// +/// Displays the display name for a specified field, reading from +/// or if present, or falling back to the property name. +/// +/// The type of the field. +public class DisplayName : IComponent +{ + + private RenderHandle _renderHandle; + private Expression>? _previousFieldAccessor; + private string? _displayName; + + /// + /// Specifies the field for which the display name should be shown. + /// + [Parameter, EditorRequired] + public Expression>? For { get; set; } + /// + void IComponent.Attach(RenderHandle renderHandle) + { + _renderHandle = renderHandle; + } + + /// + Task IComponent.SetParametersAsync(ParameterView parameters) + { + parameters.SetParameterProperties(this); + + if (For is null) + { + throw new InvalidOperationException($"{GetType()} requires a value for the " + + $"{nameof(For)} parameter."); + } + + // Only recalculate if the expression changed + if (For != _previousFieldAccessor) + { + var newDisplayName = ExpressionMemberAccessor.GetDisplayName(For); + + if (newDisplayName != _displayName) + { + _displayName = newDisplayName; + _renderHandle.Render(BuildRenderTree); + } + + _previousFieldAccessor = For; + } + + return Task.CompletedTask; + } + + private void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, _displayName); + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Forms/ExpressionMemberAccessor.cs b/src/aspnetcore/src/Components/Web/src/Forms/ExpressionMemberAccessor.cs new file mode 100644 index 000000000000..e1a81d4f0062 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Forms/ExpressionMemberAccessor.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.AspNetCore.Components.HotReload; + +namespace Microsoft.AspNetCore.Components.Forms; + +internal static class ExpressionMemberAccessor +{ + private static readonly ConcurrentDictionary _memberInfoCache = new(); + private static readonly ConcurrentDictionary _displayNameCache = new(); + + static ExpressionMemberAccessor() + { + if (HotReloadManager.Default.MetadataUpdateSupported) + { + HotReloadManager.Default.OnDeltaApplied += ClearCache; + } + } + + private static MemberInfo GetMemberInfo(Expression> accessor) + { + ArgumentNullException.ThrowIfNull(accessor); + + return _memberInfoCache.GetOrAdd(accessor, static expr => + { + var lambdaExpression = (LambdaExpression)expr; + var accessorBody = lambdaExpression.Body; + + if (accessorBody is UnaryExpression unaryExpression + && unaryExpression.NodeType == ExpressionType.Convert + && unaryExpression.Type == typeof(object)) + { + accessorBody = unaryExpression.Operand; + } + + if (accessorBody is not MemberExpression memberExpression) + { + throw new ArgumentException( + $"The provided expression contains a {accessorBody.GetType().Name} which is not supported. " + + $"Only simple member accessors (fields, properties) of an object are supported."); + } + + return memberExpression.Member; + }); + } + + public static string GetDisplayName(MemberInfo member) + { + ArgumentNullException.ThrowIfNull(member); + + return _displayNameCache.GetOrAdd(member, static m => + { + var displayAttribute = m.GetCustomAttribute(); + if (displayAttribute is not null) + { + var name = displayAttribute.GetName(); + if (name is not null) + { + return name; + } + } + + var displayNameAttribute = m.GetCustomAttribute(); + if (displayNameAttribute?.DisplayName is not null) + { + return displayNameAttribute.DisplayName; + } + + return m.Name; + }); + } + + public static string GetDisplayName(Expression> accessor) + { + ArgumentNullException.ThrowIfNull(accessor); + var member = GetMemberInfo(accessor); + return GetDisplayName(member); + } + + private static void ClearCache() + { + _memberInfoCache.Clear(); + _displayNameCache.Clear(); + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Forms/InputBase.cs b/src/aspnetcore/src/Components/Web/src/Forms/InputBase.cs index a62199a08728..c745da39cb82 100644 --- a/src/aspnetcore/src/Components/Web/src/Forms/InputBase.cs +++ b/src/aspnetcore/src/Components/Web/src/Forms/InputBase.cs @@ -369,10 +369,7 @@ protected virtual void Dispose(bool disposing) void IDisposable.Dispose() { // When initialization in the SetParametersAsync method fails, the EditContext property can remain equal to null - if (EditContext is not null) - { - EditContext.OnValidationStateChanged -= _validationStateChangedHandler; - } + EditContext?.OnValidationStateChanged -= _validationStateChangedHandler; // Clear parsing validation messages store owned by the input when the input is disposed. if (_parsingValidationMessages != null) diff --git a/src/aspnetcore/src/Components/Web/src/Forms/InputFile/RemoteBrowserFileStreamOptions.cs b/src/aspnetcore/src/Components/Web/src/Forms/InputFile/RemoteBrowserFileStreamOptions.cs deleted file mode 100644 index 4de7bab18c17..000000000000 --- a/src/aspnetcore/src/Components/Web/src/Forms/InputFile/RemoteBrowserFileStreamOptions.cs +++ /dev/null @@ -1,39 +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.Runtime.Versioning; - -namespace Microsoft.AspNetCore.Components.Forms; - -/// -/// Repesents configurable options for with Blazor Server. -/// -[UnsupportedOSPlatform("browser")] -[Obsolete("RemoteJSDataStream defaults are utilized instead of the options here.")] -public class RemoteBrowserFileStreamOptions -{ - /// - /// Gets or sets the maximum segment size for file data sent over a SignalR circuit. - /// The default value is 20K. - /// - /// This only has an effect when using Blazor Server. - /// - /// - public int MaxSegmentSize { get; set; } = 20 * 1024; // SignalR limit is 32K. - - /// - /// Gets or sets the maximum internal buffer size for unread data sent over a SignalR circuit. - /// - /// This only has an effect when using Blazor Server. - /// - /// - public int MaxBufferSize { get; set; } = 1024 * 1024; - - /// - /// Gets or sets the time limit for fetching a segment of file data. - /// - /// This only has an effect when using Blazor Server. - /// - /// - public TimeSpan SegmentFetchTimeout { get; set; } = TimeSpan.FromMinutes(1); -} diff --git a/src/aspnetcore/src/Components/Web/src/Forms/ValidationMessage.cs b/src/aspnetcore/src/Components/Web/src/Forms/ValidationMessage.cs index 766b12656452..cc84a0ab7a2a 100644 --- a/src/aspnetcore/src/Components/Web/src/Forms/ValidationMessage.cs +++ b/src/aspnetcore/src/Components/Web/src/Forms/ValidationMessage.cs @@ -94,9 +94,6 @@ void IDisposable.Dispose() private void DetachValidationStateChangedListener() { - if (_previousEditContext != null) - { - _previousEditContext.OnValidationStateChanged -= _validationStateChangedHandler; - } + _previousEditContext?.OnValidationStateChanged -= _validationStateChangedHandler; } } diff --git a/src/aspnetcore/src/Components/Web/src/Forms/ValidationSummary.cs b/src/aspnetcore/src/Components/Web/src/Forms/ValidationSummary.cs index 66d03f2e8ed7..e2580fd0cf97 100644 --- a/src/aspnetcore/src/Components/Web/src/Forms/ValidationSummary.cs +++ b/src/aspnetcore/src/Components/Web/src/Forms/ValidationSummary.cs @@ -103,9 +103,6 @@ void IDisposable.Dispose() private void DetachValidationStateChangedListener() { - if (_previousEditContext != null) - { - _previousEditContext.OnValidationStateChanged -= _validationStateChangedHandler; - } + _previousEditContext?.OnValidationStateChanged -= _validationStateChangedHandler; } } diff --git a/src/aspnetcore/src/Components/Web/src/Media/FileDownload.cs b/src/aspnetcore/src/Components/Web/src/Media/FileDownload.cs new file mode 100644 index 000000000000..8ea054d55ca6 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/FileDownload.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.JSInterop; + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/* This is equivalent to a .razor file containing: + * + * @(Text ?? "Download") + * + */ +/// +/// A component that provides an anchor element to download the provided media source. +/// +public sealed class FileDownload : MediaComponentBase +{ + /// + /// File name to suggest to the browser for the download. Must be provided. + /// + [Parameter, EditorRequired] public string FileName { get; set; } = default!; + + /// + /// Provides custom link text. Defaults to "Download". + /// + [Parameter] public string? Text { get; set; } + + internal override string TargetAttributeName => string.Empty; // Not used – object URL not tracked for downloads. + + /// + internal override bool ShouldAutoLoad => false; + + /// + /// Allows customizing the rendering of the file download component. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + private protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (ChildContent is not null) + { + var context = new FileDownloadContext + { + IsLoading = IsLoading, + HasError = _hasError, + FileName = FileName, + }; + context.Initialize(r => Element = r, EventCallback.Factory.Create(this, OnClickAsync)); + builder.AddContent(0, ChildContent, context); + return; + } + + // Default rendering + builder.OpenElement(0, "a"); + + builder.AddAttribute(1, "data-blazor-file-download", ""); + + if (IsLoading) + { + builder.AddAttribute(2, "data-state", "loading"); + } + else if (_hasError) + { + builder.AddAttribute(2, "data-state", "error"); + } + + builder.AddAttribute(3, "href", "javascript:void(0)"); + builder.AddAttribute(4, "onclick", EventCallback.Factory.Create(this, OnClickAsync)); + + IEnumerable>? attributesToRender = AdditionalAttributes; + if (AdditionalAttributes is not null && AdditionalAttributes.ContainsKey("href")) + { + var copy = new Dictionary(AdditionalAttributes.Count); + foreach (var kvp in AdditionalAttributes) + { + if (kvp.Key == "href") + { + continue; + } + copy.Add(kvp.Key, kvp.Value!); + } + attributesToRender = copy; + } + builder.AddMultipleAttributes(6, attributesToRender); + + builder.AddElementReferenceCapture(7, elementReference => Element = elementReference); + + builder.AddContent(8, Text ?? "Download"); + + builder.CloseElement(); + } + + private async Task OnClickAsync() + { + if (Source is null || !IsInteractive || string.IsNullOrWhiteSpace(FileName)) + { + return; + } + + CancelPreviousLoad(); + var token = ResetCancellationToken(); + _hasError = false; + _currentSource = Source; + Render(); + + var source = Source; + + using var streamRef = new DotNetStreamReference(source.Stream, leaveOpen: true); + + try + { + var result = await JSRuntime.InvokeAsync( + "Blazor._internal.BinaryMedia.downloadAsync", + token, + Element, + streamRef, + source.MimeType, + source.Length, + FileName); + + if (!token.IsCancellationRequested) + { + _currentSource = null; + if (!result) + { + _hasError = true; + } + Render(); + } + } + catch (OperationCanceledException) + { + _currentSource = null; + Render(); + } + catch + { + _currentSource = null; + _hasError = true; + Render(); + } + } +} + +/// +/// Extended media context for the FileDownload component providing click invocation and filename. +/// +public sealed class FileDownloadContext : MediaContext +{ + /// + /// Gets the file name suggested to the browser when initiating the download. + /// + public string FileName { get; internal set; } = string.Empty; + private EventCallback _onClick; + internal void Initialize(Action capture, EventCallback onClick) + { + base.Initialize(capture); + _onClick = onClick; + } + /// + /// Initiates the download by invoking the underlying click handler of the parent. + /// + public Task InvokeAsync() => _onClick.InvokeAsync(); +} diff --git a/src/aspnetcore/src/Components/Web/src/Media/Image.cs b/src/aspnetcore/src/Components/Web/src/Media/Image.cs new file mode 100644 index 000000000000..752f4452ea05 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/Image.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.Rendering; + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/* This is equivalent to a .razor file containing: + * + * + * + */ +/// +/// A component that efficiently renders images from non-HTTP sources like byte arrays. +/// +public sealed class Image : MediaComponentBase +{ + internal override string TargetAttributeName => "src"; + + /// + /// Allows customizing the rendering of the image component. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + private protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (ChildContent is not null) + { + var showInitial = Source != null && _currentSource == null && string.IsNullOrEmpty(_currentObjectUrl) && !_hasError; + var context = new MediaContext + { + ObjectUrl = _currentObjectUrl, + IsLoading = IsLoading || showInitial, + HasError = _hasError, + }; + context.Initialize(r => Element = r); + builder.AddContent(0, ChildContent, context); + return; + } + + // Default rendering + builder.OpenElement(0, "img"); + + if (!string.IsNullOrEmpty(_currentObjectUrl)) + { + builder.AddAttribute(1, TargetAttributeName, _currentObjectUrl); + } + + builder.AddAttribute(2, "data-blazor-image", ""); + + var defaultShowInitial = Source != null && _currentSource == null && string.IsNullOrEmpty(_currentObjectUrl) && !_hasError; + if (IsLoading || defaultShowInitial) + { + builder.AddAttribute(3, "data-state", "loading"); + } + else if (_hasError) + { + builder.AddAttribute(3, "data-state", "error"); + } + + builder.AddMultipleAttributes(4, AdditionalAttributes); + builder.AddElementReferenceCapture(5, r => Element = r); + builder.CloseElement(); + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Media/MediaComponentBase.cs b/src/aspnetcore/src/Components/Web/src/Media/MediaComponentBase.cs new file mode 100644 index 000000000000..5ea37e4b259a --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/MediaComponentBase.cs @@ -0,0 +1,311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.Extensions.Logging; +using Microsoft.JSInterop; + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/// +/// Base component that handles turning a media stream into an object URL plus caching and lifetime management. +/// Subclasses implement their own rendering and provide the target attribute (e.g., src or href) used +/// +public abstract partial class MediaComponentBase : IComponent, IHandleAfterRender, IAsyncDisposable +{ + private RenderHandle _renderHandle; + + /// + /// The current object URL (blob URL) assigned to the underlying element, or null if not yet loaded + /// or if a previous load failed/was cancelled. + /// + internal string? _currentObjectUrl; + + /// + /// Indicates whether the last load attempt ended in an error state for the active cache key. + /// + internal bool _hasError; + + private bool _isDisposed; + private bool _initialized; + private bool _hasPendingRender; + + /// + /// The cache key associated with the currently active/most recent load operation. Used to ignore + /// out-of-order JS interop responses belonging to stale operations. + /// + internal string? _activeCacheKey; + + /// + /// The instance currently being processed (or null if none). + /// + internal MediaSource? _currentSource; + private CancellationTokenSource? _loadCts; + + /// + /// Gets a value indicating whether the component is currently loading the media content. + /// True when a source has been provided, no object URL is available yet, and there is no error. + /// + internal bool IsLoading => _currentSource != null && string.IsNullOrEmpty(_currentObjectUrl) && !_hasError; + + /// + /// Gets a value indicating whether the renderer is interactive so client-side JS interop can be performed. + /// + internal bool IsInteractive => _renderHandle.IsInitialized && _renderHandle.RendererInfo.IsInteractive; + + /// + /// Gets the reference to the rendered HTML element for this media component. + /// + internal ElementReference? Element { get; set; } + + /// + /// Gets or sets the JS runtime used for interop with the browser to materialize media object URLs. + /// + [Inject] internal IJSRuntime JSRuntime { get; set; } = default!; + + /// + /// Gets or sets the logger factory used to create the instance. + /// + [Inject] internal ILoggerFactory LoggerFactory { get; set; } = default!; + + /// + /// Logger for media operations. + /// + private ILogger Logger => _logger ??= LoggerFactory.CreateLogger(GetType()); + private ILogger? _logger; + + internal abstract string TargetAttributeName { get; } + + /// + /// Determines whether the component should automatically invoke a media load after the first render + /// and whenever the changes. Override and return false for components + /// (such as download buttons) that defer loading until an explicit user action. + /// + internal virtual bool ShouldAutoLoad => true; + + /// + /// Gets or sets the media source. + /// + [Parameter, EditorRequired] public required MediaSource Source { get; set; } + + /// + /// Unmatched attributes applied to the rendered element. + /// + [Parameter(CaptureUnmatchedValues = true)] public Dictionary? AdditionalAttributes { get; set; } + + void IComponent.Attach(RenderHandle renderHandle) + { + if (_renderHandle.IsInitialized) + { + throw new InvalidOperationException("Component is already attached to a render handle."); + } + _renderHandle = renderHandle; + } + + Task IComponent.SetParametersAsync(ParameterView parameters) + { + var previousSource = Source; + + parameters.SetParameterProperties(this); + + if (Source is null) + { + throw new InvalidOperationException($"{nameof(MediaComponentBase)}.{nameof(Source)} is required."); + } + + if (!_initialized) + { + Render(); + _initialized = true; + return Task.CompletedTask; + } + + if (!HasSameKey(previousSource, Source)) + { + Render(); + } + + return Task.CompletedTask; + } + + async Task IHandleAfterRender.OnAfterRenderAsync() + { + var source = Source; + if (!IsInteractive || source is null || !ShouldAutoLoad) + { + return; + } + + if (_currentSource != null && HasSameKey(_currentSource, source)) + { + return; + } + + CancelPreviousLoad(); + var token = ResetCancellationToken(); + + _currentSource = source; + try + { + await LoadMediaAsync(source, token); + } + catch (OperationCanceledException) + { + } + } + + /// + /// Triggers a render of the component by invoking the method. + /// Ensures that only one render operation is pending at a time to prevent redundant renders. + /// + internal void Render() + { + Debug.Assert(_renderHandle.IsInitialized); + + if (!_hasPendingRender) + { + _hasPendingRender = true; + _renderHandle.Render(BuildRenderTree); + _hasPendingRender = false; + } + } + + private protected virtual void BuildRenderTree(RenderTreeBuilder builder) { } + + private sealed class MediaLoadResult + { + public bool Success { get; set; } + public bool FromCache { get; set; } + public string? ObjectUrl { get; set; } + public string? Error { get; set; } + } + + private async Task LoadMediaAsync(MediaSource? source, CancellationToken cancellationToken) + { + if (source == null || !IsInteractive) + { + return; + } + + _activeCacheKey = source.CacheKey; + + try + { + Log.BeginLoad(Logger, source.CacheKey); + + cancellationToken.ThrowIfCancellationRequested(); + + using var streamRef = new DotNetStreamReference(source.Stream, leaveOpen: true); + + var result = await JSRuntime.InvokeAsync( + "Blazor._internal.BinaryMedia.setContentAsync", + cancellationToken, + Element, + streamRef, + source.MimeType, + source.CacheKey, + source.Length, + TargetAttributeName); + + if (_activeCacheKey == source.CacheKey && !cancellationToken.IsCancellationRequested) + { + if (result.Success) + { + _currentObjectUrl = result.ObjectUrl; + _hasError = false; + + if (result.FromCache) + { + Log.CacheHit(Logger, source.CacheKey); + } + else + { + Log.StreamStart(Logger, source.CacheKey); + } + + Log.LoadSuccess(Logger, source.CacheKey); + } + else + { + _hasError = true; + Log.LoadFailed(Logger, source.CacheKey, new InvalidOperationException(result.Error ?? "Unknown error")); + } + + Render(); + } + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + Log.LoadFailed(Logger, source?.CacheKey ?? "(null)", ex); + if (source != null && _activeCacheKey == source.CacheKey && !cancellationToken.IsCancellationRequested) + { + _currentObjectUrl = null; + _hasError = true; + Render(); + } + } + } + + /// + public ValueTask DisposeAsync() + { + if (!_isDisposed) + { + _isDisposed = true; + CancelPreviousLoad(); + } + return new ValueTask(); + } + + /// + /// Cancels any in-flight media load operation, if one is active, by signalling its . + /// + internal void CancelPreviousLoad() + { + try + { + _loadCts?.Cancel(); + } + catch + { + } + _loadCts?.Dispose(); + _loadCts = null; + } + + /// + /// Creates a new for an upcoming load operation and returns its token. + /// + internal CancellationToken ResetCancellationToken() + { + _loadCts = new CancellationTokenSource(); + return _loadCts.Token; + } + + private static bool HasSameKey(MediaSource? a, MediaSource? b) + { + return a is not null && b is not null && string.Equals(a.CacheKey, b.CacheKey, StringComparison.Ordinal); + } + + private static partial class Log + { + [LoggerMessage(1, LogLevel.Debug, "Begin load for key '{CacheKey}'", EventName = "BeginLoad")] + public static partial void BeginLoad(ILogger logger, string cacheKey); + + [LoggerMessage(2, LogLevel.Debug, "Loaded media from cache for key '{CacheKey}'", EventName = "CacheHit")] + public static partial void CacheHit(ILogger logger, string cacheKey); + + [LoggerMessage(3, LogLevel.Debug, "Streaming media for key '{CacheKey}'", EventName = "StreamStart")] + public static partial void StreamStart(ILogger logger, string cacheKey); + + [LoggerMessage(4, LogLevel.Debug, "Media load succeeded for key '{CacheKey}'", EventName = "LoadSuccess")] + public static partial void LoadSuccess(ILogger logger, string cacheKey); + + [LoggerMessage(5, LogLevel.Debug, "Media load failed for key '{CacheKey}'", EventName = "LoadFailed")] + public static partial void LoadFailed(ILogger logger, string cacheKey, Exception exception); + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Media/MediaContext.cs b/src/aspnetcore/src/Components/Web/src/Media/MediaContext.cs new file mode 100644 index 000000000000..3e29e4ad9994 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/MediaContext.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/// +/// Base context supplied to media component custom content. +/// Used by and , and as the base for . +/// +public class MediaContext +{ + /// + /// The object URL for the media (image/video) or null if not loaded yet (not used for FileDownload). + /// + public string? ObjectUrl { get; internal set; } + + /// + /// Indicates whether the media is currently loading. + /// + public bool IsLoading { get; internal set; } + + /// + /// Indicates whether the last load attempt failed. + /// + public bool HasError { get; internal set; } + + private Action? _capture; + private ElementReference _element; + + internal void Initialize(Action capture) => _capture = capture; + + /// + /// Element reference for use with @ref. Assigning this propagates the DOM element to the component. + /// + public ElementReference Element + { + get => _element; + set + { + _element = value; + _capture?.Invoke(value); + } + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Media/MediaSource.cs b/src/aspnetcore/src/Components/Web/src/Media/MediaSource.cs new file mode 100644 index 000000000000..8d51cbc771dc --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/MediaSource.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/// +/// Represents a single-use source for media data. A corresponds to +/// exactly one load operation. It holds a single underlying that will be +/// consumed by a media component. Reuse of an instance for multiple components or multiple +/// loads is not supported. +/// +public class MediaSource +{ + /// + /// Gets the MIME type of the media. + /// + public string MimeType { get; } + + /// + /// Gets the cache key for the media. Always non-null. + /// + public string CacheKey { get; } + + /// + /// Gets the underlying stream. + /// + public Stream Stream { get; } + + /// + /// Gets the length of the media data in bytes if known. + /// + public long? Length { get; } + + /// + /// Initializes a new instance of with byte array data. + /// A non-writable is created over the provided data. The byte + /// array reference is not copied, so callers should not mutate it afterwards. + /// + /// The media data as a byte array. + /// The media MIME type. + /// The cache key used for caching and re-use. + public MediaSource(byte[] data, string mimeType, string cacheKey) + { + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(mimeType); + ArgumentNullException.ThrowIfNull(cacheKey); + + MimeType = mimeType; + CacheKey = cacheKey; + Stream = new MemoryStream(data, writable: false); + Length = data.LongLength; + } + + /// + /// Initializes a new instance of from an existing stream. + /// The stream reference is retained (not copied). The caller retains ownership and is + /// responsible for disposal after the media has loaded. The stream must remain readable + /// for the duration of the load. + /// + /// The readable stream positioned at the beginning. + /// The media MIME type. + /// The cache key. + public MediaSource(Stream stream, string mimeType, string cacheKey) + { + ArgumentNullException.ThrowIfNull(stream); + ArgumentNullException.ThrowIfNull(mimeType); + ArgumentNullException.ThrowIfNull(cacheKey); + + Stream = stream; + MimeType = mimeType; + CacheKey = cacheKey; + if (stream.CanSeek) + { + Length = stream.Length; + } + else + { + Length = null; + } + } +} diff --git a/src/aspnetcore/src/Components/Web/src/Media/Video.cs b/src/aspnetcore/src/Components/Web/src/Media/Video.cs new file mode 100644 index 000000000000..feda4e6177a9 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/src/Media/Video.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Components.Rendering; + +namespace Microsoft.AspNetCore.Components.Web.Media; + +/* This is equivalent to a .razor file containing: + * + * + * + */ +/// +/// A component that efficiently renders video content from non-HTTP sources like byte arrays. +/// +public sealed class Video : MediaComponentBase +{ + internal override string TargetAttributeName => "src"; + + /// + /// Allows customizing the rendering of the video component. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + private protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (ChildContent is not null) + { + var showInitial = Source != null && _currentSource == null && string.IsNullOrEmpty(_currentObjectUrl) && !_hasError; + var context = new MediaContext + { + ObjectUrl = _currentObjectUrl, + IsLoading = IsLoading || showInitial, + HasError = _hasError, + }; + context.Initialize(r => Element = r); + builder.AddContent(0, ChildContent, context); + return; + } + + // Default rendering + builder.OpenElement(0, "video"); + + if (!string.IsNullOrEmpty(_currentObjectUrl)) + { + builder.AddAttribute(1, TargetAttributeName, _currentObjectUrl); + } + + builder.AddAttribute(2, "data-blazor-video", ""); + + var defaultShowInitial = Source != null && _currentSource == null && string.IsNullOrEmpty(_currentObjectUrl) && !_hasError; + if (IsLoading || defaultShowInitial) + { + builder.AddAttribute(3, "data-state", "loading"); + } + else if (_hasError) + { + builder.AddAttribute(3, "data-state", "error"); + } + + builder.AddMultipleAttributes(4, AdditionalAttributes); + builder.AddElementReferenceCapture(5, r => Element = r); + builder.CloseElement(); + } +} diff --git a/src/aspnetcore/src/Components/Web/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/Web/src/PublicAPI.Shipped.txt index ac01a1916c34..afadaf0c38cd 100644 --- a/src/aspnetcore/src/Components/Web/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/Web/src/PublicAPI.Shipped.txt @@ -124,6 +124,10 @@ Microsoft.AspNetCore.Components.Forms.InputFileChangeEventArgs.File.get -> Micro Microsoft.AspNetCore.Components.Forms.InputFileChangeEventArgs.FileCount.get -> int Microsoft.AspNetCore.Components.Forms.InputFileChangeEventArgs.GetMultipleFiles(int maximumFileCount = 10) -> System.Collections.Generic.IReadOnlyList! Microsoft.AspNetCore.Components.Forms.InputFileChangeEventArgs.InputFileChangeEventArgs(System.Collections.Generic.IReadOnlyList! files) -> void +Microsoft.AspNetCore.Components.Forms.InputHidden +Microsoft.AspNetCore.Components.Forms.InputHidden.Element.get -> Microsoft.AspNetCore.Components.ElementReference? +Microsoft.AspNetCore.Components.Forms.InputHidden.Element.set -> void +Microsoft.AspNetCore.Components.Forms.InputHidden.InputHidden() -> void Microsoft.AspNetCore.Components.Forms.InputNumber Microsoft.AspNetCore.Components.Forms.InputNumber.Element.get -> Microsoft.AspNetCore.Components.ElementReference? Microsoft.AspNetCore.Components.Forms.InputNumber.Element.set -> void @@ -306,16 +310,16 @@ Microsoft.AspNetCore.Components.Web.HeadContent.HeadContent() -> void Microsoft.AspNetCore.Components.Web.HeadOutlet Microsoft.AspNetCore.Components.Web.HeadOutlet.HeadOutlet() -> void Microsoft.AspNetCore.Components.Web.HtmlRenderer -Microsoft.AspNetCore.Components.Web.HtmlRenderer.BeginRenderingComponent(System.Type! componentType) -> Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent Microsoft.AspNetCore.Components.Web.HtmlRenderer.BeginRenderingComponent(System.Type! componentType, Microsoft.AspNetCore.Components.ParameterView parameters) -> Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent +Microsoft.AspNetCore.Components.Web.HtmlRenderer.BeginRenderingComponent(System.Type! componentType) -> Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent Microsoft.AspNetCore.Components.Web.HtmlRenderer.BeginRenderingComponent() -> Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent Microsoft.AspNetCore.Components.Web.HtmlRenderer.BeginRenderingComponent(Microsoft.AspNetCore.Components.ParameterView parameters) -> Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent Microsoft.AspNetCore.Components.Web.HtmlRenderer.Dispatcher.get -> Microsoft.AspNetCore.Components.Dispatcher! Microsoft.AspNetCore.Components.Web.HtmlRenderer.Dispose() -> void Microsoft.AspNetCore.Components.Web.HtmlRenderer.DisposeAsync() -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.Components.Web.HtmlRenderer.HtmlRenderer(System.IServiceProvider! services, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void -Microsoft.AspNetCore.Components.Web.HtmlRenderer.RenderComponentAsync(System.Type! componentType) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Web.HtmlRenderer.RenderComponentAsync(System.Type! componentType, Microsoft.AspNetCore.Components.ParameterView parameters) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.Web.HtmlRenderer.RenderComponentAsync(System.Type! componentType) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Web.HtmlRenderer.RenderComponentAsync() -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Web.HtmlRenderer.RenderComponentAsync(Microsoft.AspNetCore.Components.ParameterView parameters) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Components.Web.HtmlRendering.HtmlRootComponent @@ -343,6 +347,7 @@ Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode.Interactive Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode.InteractiveWebAssemblyRenderMode(bool prerender) -> void Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode.Prerender.get -> bool Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime +Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string! Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string! Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions Microsoft.AspNetCore.Components.Web.JSComponentConfigurationStore @@ -543,6 +548,8 @@ override Microsoft.AspNetCore.Components.Forms.InputDate.OnParametersSet override Microsoft.AspNetCore.Components.Forms.InputDate.TryParseValueFromString(string? value, out TValue result, out string? validationErrorMessage) -> bool override Microsoft.AspNetCore.Components.Forms.InputFile.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void override Microsoft.AspNetCore.Components.Forms.InputFile.OnAfterRenderAsync(bool firstRender) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Components.Forms.InputHidden.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void +override Microsoft.AspNetCore.Components.Forms.InputHidden.TryParseValueFromString(string? value, out string? result, out string? validationErrorMessage) -> bool override Microsoft.AspNetCore.Components.Forms.InputNumber.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void override Microsoft.AspNetCore.Components.Forms.InputNumber.FormatValueAsString(TValue? value) -> string? override Microsoft.AspNetCore.Components.Forms.InputNumber.TryParseValueFromString(string? value, out TValue result, out string? validationErrorMessage) -> bool @@ -575,17 +582,17 @@ override Microsoft.AspNetCore.Components.Routing.NavLink.OnInitialized() -> void override Microsoft.AspNetCore.Components.Routing.NavLink.OnParametersSet() -> void override Microsoft.AspNetCore.Components.Web.ErrorBoundary.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void override Microsoft.AspNetCore.Components.Web.ErrorBoundary.OnErrorAsync(System.Exception! exception) -> System.Threading.Tasks.Task! -static Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) -> System.Threading.Tasks.ValueTask static Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference, bool preventScroll) -> System.Threading.Tasks.ValueTask +static Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) -> System.Threading.Tasks.ValueTask static Microsoft.AspNetCore.Components.Forms.BrowserFileExtensions.RequestImageFileAsync(this Microsoft.AspNetCore.Components.Forms.IBrowserFile! browserFile, string! format, int maxWidth, int maxHeight) -> System.Threading.Tasks.ValueTask static Microsoft.AspNetCore.Components.Forms.EditContextFieldClassExtensions.FieldCssClass(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext, in Microsoft.AspNetCore.Components.Forms.FieldIdentifier fieldIdentifier) -> string! static Microsoft.AspNetCore.Components.Forms.EditContextFieldClassExtensions.FieldCssClass(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext, System.Linq.Expressions.Expression!>! accessor) -> string! static Microsoft.AspNetCore.Components.Forms.EditContextFieldClassExtensions.SetFieldCssClassProvider(this Microsoft.AspNetCore.Components.Forms.EditContext! editContext, Microsoft.AspNetCore.Components.Forms.FieldCssClassProvider! fieldCssClassProvider) -> void static Microsoft.AspNetCore.Components.Forms.Mapping.SupplyParameterFromFormServiceCollectionExtensions.AddSupplyValueFromFormProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, System.Type! componentType, string! identifier) -> void static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, System.Type! componentType, string! identifier, string! javaScriptInitializer) -> void -static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, string! identifier) -> void +static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, System.Type! componentType, string! identifier) -> void static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, string! identifier, string! javaScriptInitializer) -> void +static Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, string! identifier) -> void static Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveAuto.get -> Microsoft.AspNetCore.Components.Web.InteractiveAutoRenderMode! static Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer.get -> Microsoft.AspNetCore.Components.Web.InteractiveServerRenderMode! static Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveWebAssembly.get -> Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode! @@ -619,5 +626,6 @@ virtual Microsoft.AspNetCore.Components.Forms.ValidationSummary.Dispose(bool dis virtual Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.RenderChildComponent(System.IO.TextWriter! output, ref Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame componentFrame) -> void virtual Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.WriteComponentHtml(int componentId, System.IO.TextWriter! output) -> void virtual Microsoft.AspNetCore.Components.RenderTree.WebRenderer.GetWebRendererId() -> int +virtual Microsoft.AspNetCore.Components.Routing.NavLink.ShouldMatch(string! uriAbsolute) -> bool virtual Microsoft.AspNetCore.Components.Web.Infrastructure.JSComponentInterop.AddRootComponent(string! identifier, string! domElementSelector) -> int virtual Microsoft.AspNetCore.Components.Web.Infrastructure.JSComponentInterop.RemoveRootComponent(int componentId) -> void diff --git a/src/aspnetcore/src/Components/Web/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/Web/src/PublicAPI.Unshipped.txt index 5b85eaf45fdc..96e56989f2a9 100644 --- a/src/aspnetcore/src/Components/Web/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/Web/src/PublicAPI.Unshipped.txt @@ -1,9 +1,76 @@ #nullable enable -Microsoft.AspNetCore.Components.Forms.InputHidden -Microsoft.AspNetCore.Components.Forms.InputHidden.Element.get -> Microsoft.AspNetCore.Components.ElementReference? -Microsoft.AspNetCore.Components.Forms.InputHidden.Element.set -> void -Microsoft.AspNetCore.Components.Forms.InputHidden.InputHidden() -> void -Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string! -override Microsoft.AspNetCore.Components.Forms.InputHidden.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void -override Microsoft.AspNetCore.Components.Forms.InputHidden.TryParseValueFromString(string? value, out string? result, out string? validationErrorMessage) -> bool -virtual Microsoft.AspNetCore.Components.Routing.NavLink.ShouldMatch(string! uriAbsolute) -> bool \ No newline at end of file +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.MaxBufferSize.get -> int +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.MaxBufferSize.set -> void +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.MaxSegmentSize.get -> int +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.MaxSegmentSize.set -> void +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.RemoteBrowserFileStreamOptions() -> void +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.SegmentFetchTimeout.get -> System.TimeSpan +*REMOVED*Microsoft.AspNetCore.Components.Forms.RemoteBrowserFileStreamOptions.SegmentFetchTimeout.set -> void +*REMOVED*Microsoft.AspNetCore.Components.RenderTree.WebRenderer.RendererId.init -> void +*REMOVED*Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +*REMOVED*static Microsoft.AspNetCore.Components.Web.WebEventCallbackFactoryEventArgsExtensions.Create(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Func! callback) -> Microsoft.AspNetCore.Components.EventCallback +Microsoft.AspNetCore.Components.Web.Media.FileDownload +Microsoft.AspNetCore.Components.Web.Media.FileDownload.ChildContent.get -> Microsoft.AspNetCore.Components.RenderFragment? +Microsoft.AspNetCore.Components.Web.Media.FileDownload.ChildContent.set -> void +Microsoft.AspNetCore.Components.Web.Media.FileDownload.FileDownload() -> void +Microsoft.AspNetCore.Components.Web.Media.FileDownload.FileName.get -> string! +Microsoft.AspNetCore.Components.Web.Media.FileDownload.FileName.set -> void +Microsoft.AspNetCore.Components.Web.Media.FileDownload.Text.get -> string? +Microsoft.AspNetCore.Components.Web.Media.FileDownload.Text.set -> void +Microsoft.AspNetCore.Components.Web.Media.FileDownloadContext +Microsoft.AspNetCore.Components.Web.Media.FileDownloadContext.FileDownloadContext() -> void +Microsoft.AspNetCore.Components.Web.Media.FileDownloadContext.FileName.get -> string! +Microsoft.AspNetCore.Components.Web.Media.FileDownloadContext.InvokeAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Components.Web.Media.Image +Microsoft.AspNetCore.Components.Web.Media.Image.ChildContent.get -> Microsoft.AspNetCore.Components.RenderFragment? +Microsoft.AspNetCore.Components.Web.Media.Image.ChildContent.set -> void +Microsoft.AspNetCore.Components.Web.Media.Image.Image() -> void +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.Source.get -> Microsoft.AspNetCore.Components.Web.Media.MediaSource! +Microsoft.AspNetCore.Components.Web.Media.MediaContext +Microsoft.AspNetCore.Components.Web.Media.MediaContext.Element.get -> Microsoft.AspNetCore.Components.ElementReference +Microsoft.AspNetCore.Components.Web.Media.MediaContext.Element.set -> void +Microsoft.AspNetCore.Components.Web.Media.MediaContext.HasError.get -> bool +Microsoft.AspNetCore.Components.Web.Media.MediaContext.IsLoading.get -> bool +Microsoft.AspNetCore.Components.Web.Media.MediaContext.MediaContext() -> void +Microsoft.AspNetCore.Components.Web.Media.MediaContext.ObjectUrl.get -> string? +Microsoft.AspNetCore.Components.Web.Media.Video +Microsoft.AspNetCore.Components.Web.Media.Video.ChildContent.get -> Microsoft.AspNetCore.Components.RenderFragment? +Microsoft.AspNetCore.Components.Web.Media.Video.ChildContent.set -> void +Microsoft.AspNetCore.Components.Web.Media.Video.Video() -> void +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.AdditionalAttributes.get -> System.Collections.Generic.Dictionary? +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.AdditionalAttributes.set -> void +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.DisposeAsync() -> System.Threading.Tasks.ValueTask +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.MediaComponentBase() -> void +Microsoft.AspNetCore.Components.Web.Media.MediaComponentBase.Source.set -> void +Microsoft.AspNetCore.Components.Web.Media.MediaSource +Microsoft.AspNetCore.Components.Web.Media.MediaSource.CacheKey.get -> string! +Microsoft.AspNetCore.Components.Web.Media.MediaSource.Length.get -> long? +Microsoft.AspNetCore.Components.Web.Media.MediaSource.MediaSource(byte[]! data, string! mimeType, string! cacheKey) -> void +Microsoft.AspNetCore.Components.Web.Media.MediaSource.MediaSource(System.IO.Stream! stream, string! mimeType, string! cacheKey) -> void +Microsoft.AspNetCore.Components.Web.Media.MediaSource.MimeType.get -> string! +Microsoft.AspNetCore.Components.Web.Media.MediaSource.Stream.get -> System.IO.Stream! +Microsoft.AspNetCore.Components.Forms.DisplayName +Microsoft.AspNetCore.Components.Forms.DisplayName.DisplayName() -> void +Microsoft.AspNetCore.Components.Forms.DisplayName.For.get -> System.Linq.Expressions.Expression!>? +Microsoft.AspNetCore.Components.Forms.DisplayName.For.set -> void diff --git a/src/aspnetcore/src/Components/Web/src/Virtualization/Virtualize.cs b/src/aspnetcore/src/Components/Web/src/Virtualization/Virtualize.cs index 6ce14ee0952d..8e2d84f2f11b 100644 --- a/src/aspnetcore/src/Components/Web/src/Virtualization/Virtualize.cs +++ b/src/aspnetcore/src/Components/Web/src/Virtualization/Virtualize.cs @@ -362,6 +362,10 @@ private void CalcualteItemDistribution( _ => MaxItemCount }; + // Count the OverscanCount as used capacity, so we don't end up in a situation where + // the user has set a very low MaxItemCount and we end up in an infinite loading loop. + maxItemCount += OverscanCount * 2; + itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - OverscanCount); visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2 * OverscanCount; unusedItemCapacity = Math.Max(0, visibleItemCapacity - maxItemCount); diff --git a/src/aspnetcore/src/Components/Web/src/Web/WebEventCallbackFactoryEventArgsExtensions.cs b/src/aspnetcore/src/Components/Web/src/Web/WebEventCallbackFactoryEventArgsExtensions.cs deleted file mode 100644 index 4c34865354ea..000000000000 --- a/src/aspnetcore/src/Components/Web/src/Web/WebEventCallbackFactoryEventArgsExtensions.cs +++ /dev/null @@ -1,329 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Components.Web; - -/// -/// Provides extension methods for and types. -/// -public static class WebEventCallbackFactoryEventArgsExtensions -{ - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Action callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } - - /// - /// Creates an for the provided and - /// . - /// - /// The . - /// The event receiver. - /// The event callback. - /// The . - [Obsolete("This extension method is obsolete and will be removed in a future version. Use the generic overload instead.")] - public static EventCallback Create(this EventCallbackFactory factory, object receiver, Func callback) - { - ArgumentNullException.ThrowIfNull(factory); - - return factory.Create(receiver, callback); - } -} diff --git a/src/aspnetcore/src/Components/Web/src/WebRenderer.cs b/src/aspnetcore/src/Components/Web/src/WebRenderer.cs index becd887453d0..fc83c3c360f6 100644 --- a/src/aspnetcore/src/Components/Web/src/WebRenderer.cs +++ b/src/aspnetcore/src/Components/Web/src/WebRenderer.cs @@ -52,9 +52,6 @@ public WebRenderer( protected int RendererId { get => _rendererId; - - [Obsolete($"The renderer ID can be assigned by overriding '{nameof(GetWebRendererId)}'.")] - init { /* No-op */ } } /// diff --git a/src/aspnetcore/src/Components/Web/test/Forms/DisplayNameTest.cs b/src/aspnetcore/src/Components/Web/test/Forms/DisplayNameTest.cs new file mode 100644 index 000000000000..f7835c540827 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/test/Forms/DisplayNameTest.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.AspNetCore.Components.Test.Helpers; + +namespace Microsoft.AspNetCore.Components.Forms; + +public class DisplayNameTest +{ + [Fact] + public async Task ThrowsIfNoForParameterProvided() + { + // Arrange + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.CloseComponent(); + } + }; + + var testRenderer = new TestRenderer(); + var componentId = testRenderer.AssignRootComponentId(rootComponent); + + // Act & Assert + var ex = await Assert.ThrowsAsync( + async () => await testRenderer.RenderRootComponentAsync(componentId)); + Assert.Contains("For", ex.Message); + Assert.Contains("parameter", ex.Message); + } + + [Fact] + public async Task DisplaysPropertyNameWhenNoAttributePresent() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.PlainProperty)); + builder.CloseComponent(); + } + }; + + // Act + var output = await RenderAndGetOutput(rootComponent); + + // Assert + Assert.Equal("PlainProperty", output); + } + + [Fact] + public async Task DisplaysDisplayAttributeName() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.PropertyWithDisplayAttribute)); + builder.CloseComponent(); + } + }; + + // Act + var output = await RenderAndGetOutput(rootComponent); + + // Assert + Assert.Equal("Custom Display Name", output); + } + + [Fact] + public async Task DisplaysDisplayNameAttributeName() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.PropertyWithDisplayNameAttribute)); + builder.CloseComponent(); + } + }; + + // Act + var output = await RenderAndGetOutput(rootComponent); + + // Assert + Assert.Equal("Custom DisplayName", output); + } + + [Fact] + public async Task DisplayAttributeTakesPrecedenceOverDisplayNameAttribute() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.PropertyWithBothAttributes)); + builder.CloseComponent(); + } + }; + + // Act + var output = await RenderAndGetOutput(rootComponent); + + // Assert + // DisplayAttribute should take precedence per MVC conventions + Assert.Equal("Display Takes Precedence", output); + } + + [Fact] + public async Task WorksWithDifferentPropertyTypes() + { + // Arrange + var model = new TestModel(); + var intComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.IntProperty)); + builder.CloseComponent(); + } + }; + var dateComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.DateProperty)); + builder.CloseComponent(); + } + }; + + // Act + var intOutput = await RenderAndGetOutput(intComponent); + var dateOutput = await RenderAndGetOutput(dateComponent); + + // Assert + Assert.Equal("Integer Value", intOutput); + Assert.Equal("Date Value", dateOutput); + } + + [Fact] + public async Task SupportsLocalizationWithResourceType() + { + var model = new TestModel(); + var rootComponent = new TestHostComponent + { + InnerContent = builder => + { + builder.OpenComponent>(0); + builder.AddComponentParameter(1, "For", (System.Linq.Expressions.Expression>)(() => model.PropertyWithResourceBasedDisplay)); + builder.CloseComponent(); + } + }; + + var output = await RenderAndGetOutput(rootComponent); + Assert.Equal("Localized Display Name", output); + } + + private static async Task RenderAndGetOutput(TestHostComponent rootComponent) + { + var testRenderer = new TestRenderer(); + var componentId = testRenderer.AssignRootComponentId(rootComponent); + await testRenderer.RenderRootComponentAsync(componentId); + + var batch = testRenderer.Batches.Single(); + var displayLabelComponentFrame = batch.ReferenceFrames + .First(f => f.FrameType == RenderTree.RenderTreeFrameType.Component && + f.Component is DisplayName or DisplayName or DisplayName); + + // Find the text content frame within the component + var textFrame = batch.ReferenceFrames + .First(f => f.FrameType == RenderTree.RenderTreeFrameType.Text); + + return textFrame.TextContent; + } + + private class TestHostComponent : ComponentBase + { + public RenderFragment InnerContent { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + InnerContent(builder); + } + } + + private class TestModel + { + public string PlainProperty { get; set; } = string.Empty; + + [Display(Name = "Custom Display Name")] + public string PropertyWithDisplayAttribute { get; set; } = string.Empty; + + [DisplayName("Custom DisplayName")] + public string PropertyWithDisplayNameAttribute { get; set; } = string.Empty; + + [Display(Name = "Display Takes Precedence")] + [DisplayName("This Should Not Be Used")] + public string PropertyWithBothAttributes { get; set; } = string.Empty; + + [Display(Name = "Integer Value")] + public int IntProperty { get; set; } + + [Display(Name = "Date Value")] + public DateTime DateProperty { get; set; } + + [Display(Name = nameof(TestResources.LocalizedDisplayName), ResourceType = typeof(TestResources))] + public string PropertyWithResourceBasedDisplay { get; set; } = string.Empty; + } + + public static class TestResources + { + public static string LocalizedDisplayName => "Localized Display Name"; + } +} diff --git a/src/aspnetcore/src/Components/Web/test/Media/FileDownloadTest.cs b/src/aspnetcore/src/Components/Web/test/Media/FileDownloadTest.cs new file mode 100644 index 000000000000..cc4f95964ed9 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/test/Media/FileDownloadTest.cs @@ -0,0 +1,276 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Collections.Concurrent; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.RenderTree; +using Microsoft.AspNetCore.Components.Test.Helpers; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.Web.Media; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.JSInterop; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Web.Media.Tests; + +/// +/// Unit tests for focusing only on behaviors not covered by Image/Video tests. +/// +public class FileDownloadTest +{ + private static readonly byte[] SampleBytes = new byte[] { 1, 2, 3, 4, 5 }; + + [Fact] + public async Task InitialRender_DoesNotInvokeJs() + { + var js = new FakeDownloadJsRuntime(); + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(FileDownload.Source)] = new MediaSource(SampleBytes, "application/octet-stream", "file-init"), + [nameof(FileDownload.FileName)] = "first.bin" + })); + + Assert.Equal(0, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + } + + [Fact] + public async Task Click_InvokesDownloadOnce() + { + var js = new FakeDownloadJsRuntime { Result = true }; + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + await renderer.RenderRootComponentAsync(id, Params("file-click", "ok.bin")); + + await ClickAnchorAsync(renderer, id); + + Assert.Equal(1, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + Assert.False(HasDataState(renderer, id, "error")); + } + + [Fact] + public async Task BlankFileName_SuppressesDownload() + { + var js = new FakeDownloadJsRuntime { Result = true }; + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + await renderer.RenderRootComponentAsync(id, Params("file-noname", " ")); + + await ClickAnchorAsync(renderer, id); + + Assert.Equal(0, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + } + + [Fact] + public async Task JsReturnsFalse_SetsErrorState() + { + var js = new FakeDownloadJsRuntime { Result = false }; + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + await renderer.RenderRootComponentAsync(id, Params("file-false", "fail.bin")); + + await ClickAnchorAsync(renderer, id); + + Assert.Equal(1, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + Assert.True(HasDataState(renderer, id, "error")); + } + + [Fact] + public async Task JsThrows_SetsErrorState() + { + var js = new FakeDownloadJsRuntime { Throw = true }; + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + await renderer.RenderRootComponentAsync(id, Params("file-throw", "throws.bin")); + + await ClickAnchorAsync(renderer, id); + + Assert.Equal(1, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + Assert.True(HasDataState(renderer, id, "error")); + } + + [Fact] + public async Task SecondClick_CancelsFirst() + { + var js = new FakeDownloadJsRuntime { DelayOnFirst = true }; + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + await renderer.RenderRootComponentAsync(id, Params("file-cancel", "cancel.bin")); + + var first = ClickAnchorAsync(renderer, id); // starts first (will delay) + await ClickAnchorAsync(renderer, id); // second click immediately + await first; // allow completion + + Assert.Equal(2, js.Count("Blazor._internal.BinaryMedia.downloadAsync")); + Assert.True(js.CapturedTokens.First().IsCancellationRequested); + Assert.False(js.CapturedTokens.Last().IsCancellationRequested); + } + + [Fact] + public async Task ProvidedHref_IsRemoved_InertHrefUsed() + { + var js = new FakeDownloadJsRuntime(); + using var renderer = CreateRenderer(js); + var comp = (FileDownload)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + var attrs = new Dictionary { ["href"] = "https://example.org/real", ["class"] = "btn" }; + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(FileDownload.Source)] = new MediaSource(SampleBytes, "application/octet-stream", "file-href"), + [nameof(FileDownload.FileName)] = "href.bin", + [nameof(FileDownload.AdditionalAttributes)] = attrs + })); + + var frames = renderer.GetCurrentRenderTreeFrames(id); + var anchorIndex = FindAnchorIndex(frames); + Assert.True(anchorIndex >= 0, "anchor not found"); + var href = GetAttributeValue(frames, anchorIndex, "href"); + var @class = GetAttributeValue(frames, anchorIndex, "class"); + Assert.Equal("javascript:void(0)", href); + Assert.Equal("btn", @class); + } + + // Helpers + private static ParameterView Params(string key, string fileName) => ParameterView.FromDictionary(new Dictionary + { + [nameof(FileDownload.Source)] = new MediaSource(SampleBytes, "application/octet-stream", key), + [nameof(FileDownload.FileName)] = fileName + }); + + private static async Task ClickAnchorAsync(TestRenderer renderer, int componentId) + { + var frames = renderer.GetCurrentRenderTreeFrames(componentId); + var anchorIndex = FindAnchorIndex(frames); + Assert.True(anchorIndex >= 0, "anchor not found"); + ulong? handlerId = null; + for (var i = anchorIndex + 1; i < frames.Count; i++) + { + ref readonly var frame = ref frames.Array[i]; + if (frame.FrameType == RenderTreeFrameType.Attribute) + { + if (frame.AttributeName == "onclick") + { + handlerId = frame.AttributeEventHandlerId; + } + + continue; + } + break; + } + Assert.True(handlerId.HasValue, "onclick handler not found"); + await renderer.DispatchEventAsync(handlerId.Value, new MouseEventArgs()); + } + + private static bool HasDataState(TestRenderer renderer, int componentId, string state) + { + var frames = renderer.GetCurrentRenderTreeFrames(componentId); + var anchorIndex = FindAnchorIndex(frames); + if (anchorIndex < 0) + { + return false; + } + + var value = GetAttributeValue(frames, anchorIndex, "data-state"); + return string.Equals(value, state, StringComparison.Ordinal); + } + + private static int FindAnchorIndex(ArrayRange frames) + { + for (var i = 0; i < frames.Count; i++) + { + ref readonly var f = ref frames.Array[i]; + if (f.FrameType == RenderTreeFrameType.Element && string.Equals(f.ElementName, "a", StringComparison.OrdinalIgnoreCase)) + { + return i; + } + } + return -1; + } + + private static string? GetAttributeValue(ArrayRange frames, int elementIndex, string name) + { + for (var i = elementIndex + 1; i < frames.Count; i++) + { + ref readonly var frame = ref frames.Array[i]; + if (frame.FrameType == RenderTreeFrameType.Attribute) + { + if (string.Equals(frame.AttributeName, name, StringComparison.Ordinal)) + { + return frame.AttributeValue?.ToString(); + } + continue; + } + break; + } + return null; + } + + private static TestRenderer CreateRenderer(IJSRuntime js) + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddSingleton(js); + return new InteractiveTestRenderer(services.BuildServiceProvider()); + } + + private sealed class InteractiveTestRenderer : TestRenderer + { + public InteractiveTestRenderer(IServiceProvider services) : base(services) { } + protected internal override RendererInfo RendererInfo => new RendererInfo("Test", isInteractive: true); + } + + private sealed class FakeDownloadJsRuntime : IJSRuntime + { + private readonly ConcurrentQueue _invocations = new(); + public bool Result { get; set; } = true; + public bool Throw { get; set; } + public bool DelayOnFirst { get; set; } + private int _calls; + + public IReadOnlyList CapturedTokens => _invocations.Select(i => i.Token).ToList(); + public int Count(string id) => _invocations.Count(i => i.Identifier == id); + + public ValueTask InvokeAsync(string identifier, object?[]? args) => InvokeAsync(identifier, CancellationToken.None, args ?? Array.Empty()); + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) + { + _invocations.Enqueue(new Invocation(identifier, cancellationToken)); + if (identifier == "Blazor._internal.BinaryMedia.downloadAsync") + { + if (Throw) + { + return ValueTask.FromException(new InvalidOperationException("Download failed")); + } + if (DelayOnFirst && _calls == 0) + { + _calls++; + return new ValueTask(DelayAsync(cancellationToken)); + } + _calls++; + object boxed = Result; + return new ValueTask((TValue)boxed); + } + return ValueTask.FromException(new InvalidOperationException("Unexpected identifier: " + identifier)); + } + + private async Task DelayAsync(CancellationToken token) + { + try { await Task.Delay(50, token); } catch { } + object boxed = Result; + return (TValue)boxed; + } + + private record struct Invocation(string Identifier, CancellationToken Token); + } +} diff --git a/src/aspnetcore/src/Components/Web/test/Media/ImageTest.cs b/src/aspnetcore/src/Components/Web/test/Media/ImageTest.cs new file mode 100644 index 000000000000..20c7d2e35d93 --- /dev/null +++ b/src/aspnetcore/src/Components/Web/test/Media/ImageTest.cs @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Linq; +using System.Text.Json; +using System.IO; +using Microsoft.AspNetCore.Components.Test.Helpers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.JSInterop; +using Xunit; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.RenderTree; +using Microsoft.AspNetCore.Components.Web.Media; + +namespace Microsoft.AspNetCore.Components.Web.Media.Tests; + +/// +/// Unit tests for the new Media.Image component +/// +public class ImageTest +{ + private static readonly byte[] PngBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdjqK6u/g8ABVcCcYoGhmwAAAAASUVORK5CYII="); + + [Fact] + public async Task LoadsImage_InvokesSetContentAsync_WhenSourceProvided() + { + var js = new FakeMediaJsRuntime(cacheHit: false); + using var renderer = CreateRenderer(js); + var comp = (Image)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + var source = new MediaSource(PngBytes, "image/png", cacheKey: "png-1"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = source, + })); + + Assert.Equal(1, js.Count("Blazor._internal.BinaryMedia.setContentAsync")); + } + + [Fact] + public async Task SkipsReload_OnSameCacheKey() + { + var js = new FakeMediaJsRuntime(cacheHit: false); + using var renderer = CreateRenderer(js); + var comp = (Image)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + var s1 = new MediaSource(new byte[10], "image/png", cacheKey: "same"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s1, + })); + + var s2 = new MediaSource(new byte[20], "image/png", cacheKey: "same"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s2, + })); + + Assert.Equal(1, js.Count("Blazor._internal.BinaryMedia.setContentAsync")); + } + + [Fact] + public async Task NullSource_Throws() + { + var js = new FakeMediaJsRuntime(cacheHit: false); + using var renderer = CreateRenderer(js); + var comp = (Image)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + await Assert.ThrowsAsync(async () => + { + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = null, + })); + }); + + // Ensure no JS interop calls were made + Assert.Equal(0, js.TotalInvocationCount); + } + + [Fact] + public async Task ParameterChange_DifferentCacheKey_Reloads() + { + var js = new FakeMediaJsRuntime(cacheHit: false); + using var renderer = CreateRenderer(js); + var comp = (Image)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + var s1 = new MediaSource(new byte[4], "image/png", "key-a"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s1, + })); + var s2 = new MediaSource(new byte[6], "image/png", "key-b"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s2, + })); + Assert.Equal(2, js.Count("Blazor._internal.BinaryMedia.setContentAsync")); + } + + [Fact] + public async Task ChangingSource_CancelsPreviousLoad() + { + var js = new FakeMediaJsRuntime(cacheHit: false) { DelayOnFirstSetCall = true }; + using var renderer = CreateRenderer(js); + var comp = (Image)renderer.InstantiateComponent(); + var id = renderer.AssignRootComponentId(comp); + + var s1 = new MediaSource(new byte[10], "image/png", "k1"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s1, + })); + + var s2 = new MediaSource(new byte[10], "image/png", "k2"); + await renderer.RenderRootComponentAsync(id, ParameterView.FromDictionary(new Dictionary + { + [nameof(Image.Source)] = s2, + })); + + for (var i = 0; i < 10 && js.CapturedTokens.Count < 2; i++) + { + await Task.Delay(10); + } + + Assert.NotEmpty(js.CapturedTokens); + Assert.True(js.CapturedTokens.First().IsCancellationRequested); + Assert.Equal(2, js.Count("Blazor._internal.BinaryMedia.setContentAsync")); + } + + private static TestRenderer CreateRenderer(IJSRuntime js) + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddSingleton(js); + return new InteractiveTestRenderer(services.BuildServiceProvider()); + } + + private sealed class InteractiveTestRenderer : TestRenderer + { + public InteractiveTestRenderer(IServiceProvider serviceProvider) : base(serviceProvider) { } + protected internal override RendererInfo RendererInfo => new RendererInfo("Test", isInteractive: true); + } + + private sealed class FakeMediaJsRuntime : IJSRuntime + { + public sealed record Invocation(string Identifier, object?[] Args, CancellationToken Token); + private readonly ConcurrentQueue _invocations = new(); + private readonly ConcurrentDictionary _memoryCache = new(); + private readonly bool _forceCacheHit; + + public FakeMediaJsRuntime(bool cacheHit) { _forceCacheHit = cacheHit; } + + public int TotalInvocationCount => _invocations.Count; + public int Count(string id) => _invocations.Count(i => i.Identifier == id); + public IReadOnlyList CapturedTokens => _invocations.Select(i => i.Token).ToList(); + + public bool DelayOnFirstSetCall { get; set; } + public bool ForceFail { get; set; } + public bool FailOnce { get; set; } = true; + public bool FailIfTotalBytesIsZero { get; set; } + private bool _failUsed; + private int _setCalls; + + public ValueTask InvokeAsync(string identifier, object?[]? args) + => InvokeAsync(identifier, CancellationToken.None, args ?? Array.Empty()); + + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) + { + args ??= Array.Empty(); + _invocations.Enqueue(new Invocation(identifier, args, cancellationToken)); + + if (identifier == "Blazor._internal.BinaryMedia.setContentAsync") + { + _setCalls++; + var cacheKey = args.Length >= 4 ? args[3] as string : null; + var hasStream = args.Length >= 2 && args[1] != null; + long? totalBytes = null; + if (args.Length >= 5 && args[4] != null) + { + try { totalBytes = Convert.ToInt64(args[4], System.Globalization.CultureInfo.InvariantCulture); } catch { totalBytes = null; } + } + + if (DelayOnFirstSetCall && _setCalls == 1) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + cancellationToken.Register(() => tcs.TrySetException(new OperationCanceledException(cancellationToken))); + return new ValueTask(tcs.Task); + } + + var shouldFail = (ForceFail && (!_failUsed || !FailOnce)) + || (FailIfTotalBytesIsZero && (totalBytes.HasValue && totalBytes.Value == 0)); + if (ForceFail) + { + _failUsed = true; + } + + var fromCache = !shouldFail && cacheKey != null && (_forceCacheHit || _memoryCache.ContainsKey(cacheKey)); + if (!fromCache && hasStream && !string.IsNullOrEmpty(cacheKey) && !shouldFail) + { + _memoryCache[cacheKey!] = true; + } + + var t = typeof(TValue); + object? instance = Activator.CreateInstance(t, nonPublic: true); + if (instance is null) + { + return ValueTask.FromResult(default(TValue)!); + } + + var setProp = (string name, object? value) => + { + var p = t.GetProperty(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + p?.SetValue(instance, value); + }; + + if (shouldFail) + { + setProp("Success", false); + setProp("FromCache", false); + setProp("ObjectUrl", null); + setProp("Error", "simulated-failure"); + } + else + { + setProp("Success", hasStream || fromCache); + setProp("FromCache", fromCache); + setProp("ObjectUrl", (hasStream || fromCache) && cacheKey != null ? $"blob:{cacheKey}:{Guid.NewGuid()}" : null); + setProp("Error", null); + } + + return ValueTask.FromResult((TValue)instance); + } + + return ValueTask.FromResult(default(TValue)!); + } + } +} diff --git a/src/aspnetcore/src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj b/src/aspnetcore/src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj index d4fe6205d0ad..8f45ac00bcd7 100644 --- a/src/aspnetcore/src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj +++ b/src/aspnetcore/src/Components/Web/test/Microsoft.AspNetCore.Components.Web.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/aspnetcore/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj b/src/aspnetcore/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj index a320af38fa9c..11b352cbe4af 100644 --- a/src/aspnetcore/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj @@ -19,8 +19,20 @@ + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs index 4ad543a146ce..680a1c67b087 100644 --- a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs +++ b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs @@ -1,22 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices.JavaScript; namespace WebAssembly.JSInterop; internal static partial class InternalCalls { - // This method only exists for backwards compatibility and will be removed in the future. - // The exact namespace, type, and method name must match the corresponding entries - // in driver.c in the Mono distribution. - // See: https://github.com/mono/mono/blob/90574987940959fe386008a850982ea18236a533/sdks/wasm/src/driver.c#L318-L319 - [MethodImpl(MethodImplOptions.InternalCall)] - [Obsolete] - public static extern TRes InvokeJS(out string exception, ref JSCallInfo callInfo, [AllowNull] T0 arg0, [AllowNull] T1 arg1, [AllowNull] T2 arg2); - [JSImport("Blazor._internal.invokeJSJson", "blazor-internal")] public static partial string InvokeJSJson( string identifier, diff --git a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Shipped.txt index 7a868e7c891d..dbb8b3c10572 100644 --- a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Shipped.txt @@ -3,7 +3,9 @@ ~override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.InvokeJS(string identifier, string argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.WebAssemblyJSRuntime() -> void +override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.BeginInvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> void override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.BeginInvokeJS(long asyncHandle, string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> void override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo callInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult dispatchResult) -> void +override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string! override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.InvokeJS(string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string! override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.SendByteArray(int id, byte[]! data) -> void diff --git a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Unshipped.txt index b92576acb036..7dc5c58110bf 100644 --- a/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/JSInterop/src/PublicAPI.Unshipped.txt @@ -1,3 +1 @@ #nullable enable -override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.BeginInvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> void -override Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime.InvokeJS(in Microsoft.JSInterop.Infrastructure.JSInvocationInfo invocationInfo) -> string! diff --git a/src/aspnetcore/src/Components/WebAssembly/Samples/HostedBlazorWebassemblyApp/Server/HostedBlazorWebassemblyApp.Server.csproj b/src/aspnetcore/src/Components/WebAssembly/Samples/HostedBlazorWebassemblyApp/Server/HostedBlazorWebassemblyApp.Server.csproj index 339fea3989f3..567d4b464a8d 100644 --- a/src/aspnetcore/src/Components/WebAssembly/Samples/HostedBlazorWebassemblyApp/Server/HostedBlazorWebassemblyApp.Server.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/Samples/HostedBlazorWebassemblyApp/Server/HostedBlazorWebassemblyApp.Server.csproj @@ -9,9 +9,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/Server/src/Microsoft.AspNetCore.Components.WebAssembly.Server.csproj b/src/aspnetcore/src/Components/WebAssembly/Server/src/Microsoft.AspNetCore.Components.WebAssembly.Server.csproj index b3f42e28fe8a..e81cc430ff20 100644 --- a/src/aspnetcore/src/Components/WebAssembly/Server/src/Microsoft.AspNetCore.Components.WebAssembly.Server.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/Server/src/Microsoft.AspNetCore.Components.WebAssembly.Server.csproj @@ -14,6 +14,13 @@ + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/Server/test/Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj b/src/aspnetcore/src/Components/WebAssembly/Server/test/Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj index 292ec82b5d4a..2476cd29a579 100644 --- a/src/aspnetcore/src/Components/WebAssembly/Server/test/Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/Server/test/Microsoft.AspNetCore.Components.WebAssembly.Server.Tests.csproj @@ -5,8 +5,16 @@ + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Shipped.txt index 30d69b11b605..4b06b0b92dee 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Shipped.txt @@ -151,8 +151,8 @@ Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationS Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.JsRuntime.get -> Microsoft.JSInterop.IJSRuntime! Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.Navigation.get -> Microsoft.AspNetCore.Components.NavigationManager! Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.Options.get -> Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationOptions! -Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.RemoteAuthenticationService(Microsoft.JSInterop.IJSRuntime! jsRuntime, Microsoft.Extensions.Options.IOptionsSnapshot!>! options, Microsoft.AspNetCore.Components.NavigationManager! navigation, Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccountClaimsPrincipalFactory! accountClaimsPrincipalFactory) -> void Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.RemoteAuthenticationService(Microsoft.JSInterop.IJSRuntime! jsRuntime, Microsoft.Extensions.Options.IOptionsSnapshot!>! options, Microsoft.AspNetCore.Components.NavigationManager! navigation, Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccountClaimsPrincipalFactory! accountClaimsPrincipalFactory, Microsoft.Extensions.Logging.ILogger!>? logger) -> void +Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.RemoteAuthenticationService(Microsoft.JSInterop.IJSRuntime! jsRuntime, Microsoft.Extensions.Options.IOptionsSnapshot!>! options, Microsoft.AspNetCore.Components.NavigationManager! navigation, Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccountClaimsPrincipalFactory! accountClaimsPrincipalFactory) -> void Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState.RemoteAuthenticationState() -> void Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState.ReturnUrl.get -> string? @@ -218,26 +218,26 @@ override Microsoft.AspNetCore.Components.WebAssembly.Authentication.Authorizatio override Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task! override Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticatorViewCore.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void override Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticatorViewCore.OnParametersSetAsync() -> System.Threading.Tasks.Task! -static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogin(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! loginPath) -> void static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogin(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! loginPath, Microsoft.AspNetCore.Components.WebAssembly.Authentication.InteractiveRequestOptions! request) -> void -static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogout(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! logoutPath) -> void +static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogin(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! loginPath) -> void static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogout(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! logoutPath, string? returnUrl) -> void +static Microsoft.AspNetCore.Components.WebAssembly.Authentication.NavigationManagerExtensions.NavigateToLogout(this Microsoft.AspNetCore.Components.NavigationManager! manager, string! logoutPath) -> void static Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationActions.IsAction(string! action, string! candidate) -> bool static Microsoft.Extensions.DependencyInjection.RemoteAuthenticationBuilderExtensions.AddAccountClaimsPrincipalFactory(this Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! builder) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.RemoteAuthenticationBuilderExtensions.AddAccountClaimsPrincipalFactory(this Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! builder) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.RemoteAuthenticationBuilderExtensions.AddAccountClaimsPrincipalFactory(this Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! builder) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! -static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! -static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! +static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! -static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! +static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! +static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddApiAuthorization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddAuthenticationStateDeserialization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action? configure = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddOidcAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddOidcAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddOidcAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>! configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! -static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddRemoteAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddRemoteAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action!>? configure) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! +static Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.AddRemoteAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IRemoteAuthenticationBuilder! static readonly Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationDefaults.LoginCallbackPath -> string! static readonly Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationDefaults.LoginFailedPath -> string! static readonly Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationDefaults.LoginPath -> string! diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..dbe9cee9e3c9 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/PublicAPI.Unshipped.txt @@ -1 +1,8 @@ #nullable enable +*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenResult.AccessTokenResult(Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenResultStatus status, Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessToken! token, string! redirectUrl) -> void +*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenResult.RedirectUrl.get -> string? +*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService.RemoteAuthenticationService(Microsoft.JSInterop.IJSRuntime! jsRuntime, Microsoft.Extensions.Options.IOptionsSnapshot!>! options, Microsoft.AspNetCore.Components.NavigationManager! navigation, Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccountClaimsPrincipalFactory! accountClaimsPrincipalFactory) -> void +*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionStateManager +*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionStateManager.SignOutSessionStateManager(Microsoft.JSInterop.IJSRuntime! jsRuntime) -> void +*REMOVED*virtual Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionStateManager.SetSignOutState() -> System.Threading.Tasks.ValueTask +*REMOVED*virtual Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutSessionStateManager.ValidateSignOutState() -> System.Threading.Tasks.Task! diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticatorViewCore.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticatorViewCore.cs index 69c9b1e70c86..37f6c0ad3b70 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticatorViewCore.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/RemoteAuthenticatorViewCore.cs @@ -106,10 +106,6 @@ public RemoteAuthenticationApplicationPathsOptions ApplicationPaths [Inject] internal AuthenticationStateProvider AuthenticationProvider { get; set; } = default!; -#pragma warning disable CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility - [Inject] internal SignOutSessionStateManager SignOutManager { get; set; } = default!; -#pragma warning restore CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility - [Inject] internal ILogger> Logger { get; set; } = default!; /// @@ -282,9 +278,7 @@ private async Task ProcessLogInCallback() private async Task ProcessLogOut(string returnUrl) { - if ((Navigation.HistoryEntryState != null && !ValidateSignOutRequestState()) || - // For backcompat purposes, keep SignOutManager working, even though we now use the history.state for this. - (Navigation.HistoryEntryState == null && !await SignOutManager.ValidateSignOutState())) + if (Navigation.HistoryEntryState != null && !ValidateSignOutRequestState()) { Log.LogoutOperationInitiatedExternally(Logger); Navigation.NavigateTo(ApplicationPaths.LogOutFailedPath, AuthenticationNavigationOptions with { HistoryEntryState = "The logout was not initiated from within the page." }); diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenNotAvailableException.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenNotAvailableException.cs index d1ce1b61e484..1c42d3d79423 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenNotAvailableException.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenNotAvailableException.cs @@ -41,9 +41,7 @@ public void Redirect() } else { -#pragma warning disable CS0618 // Type or member is obsolete - _navigation.NavigateTo(_tokenResult.RedirectUrl!); -#pragma warning restore CS0618 // Type or member is obsolete + _navigation.NavigateTo(_tokenResult.InteractiveRequestUrl!); } } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenResult.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenResult.cs index 576b323509d9..3971b66eda16 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenResult.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/AccessTokenResult.cs @@ -12,20 +12,6 @@ public class AccessTokenResult { private readonly AccessToken _token; - /// - /// Initializes a new instance of . - /// - /// The status of the result. - /// The in case it was successful. - /// The redirect uri to go to for provisioning the token. - [Obsolete("Use the AccessTokenResult(AccessTokenResultStatus, AccessToken, string, InteractiveRequestOptions)")] - public AccessTokenResult(AccessTokenResultStatus status, AccessToken token, [StringSyntax(StringSyntaxAttribute.Uri)] string redirectUrl) - { - Status = status; - _token = token; - RedirectUrl = redirectUrl; - } - /// /// Initializes a new instance of . /// @@ -46,12 +32,6 @@ public AccessTokenResult(AccessTokenResultStatus status, AccessToken token, [Str /// public AccessTokenResultStatus Status { get; } - /// - /// Gets the URL to redirect to if is . - /// - [Obsolete("Use 'InteractiveRequestUrl' and 'InteractiveRequest' instead.")] - public string? RedirectUrl { get; } - /// /// Gets the URL to call if is /// . diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/RemoteAuthenticationService.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/RemoteAuthenticationService.cs index 12d315bdaf60..61e5d080e492 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/RemoteAuthenticationService.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/RemoteAuthenticationService.cs @@ -57,23 +57,6 @@ public class RemoteAuthenticationService< /// protected RemoteAuthenticationOptions Options { get; } - /// - /// Initializes a new instance. - /// - /// The to use for performing JavaScript interop operations. - /// The options to be passed down to the underlying JavaScript library handling the authentication operations. - /// The used to generate URLs. - /// The used to generate the for the user. - [Obsolete("Use the constructor RemoteAuthenticationService(IJSRuntime,IOptionsSnapshot>,NavigationManager,AccountClaimsPrincipalFactory,ILogger>) instead.")] - public RemoteAuthenticationService( - IJSRuntime jsRuntime, - IOptionsSnapshot> options, - NavigationManager navigation, - AccountClaimsPrincipalFactory accountClaimsPrincipalFactory) - : this(jsRuntime, options, navigation, accountClaimsPrincipalFactory, null) - { - } - /// /// Initializes a new instance. /// diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/SignOutSessionStateManager.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/SignOutSessionStateManager.cs deleted file mode 100644 index 74b61cb7aca2..000000000000 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/SignOutSessionStateManager.cs +++ /dev/null @@ -1,88 +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.Diagnostics.CodeAnalysis; -using System.Text.Json; -using Microsoft.JSInterop; -using static Microsoft.AspNetCore.Internal.LinkerFlags; - -namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication; - -/// -/// Handles CSRF protection for the logout endpoint. -/// -[Obsolete("Use 'Microsoft.AspNetCore.Components.Webassembly.Authentication.NavigationManagerExtensions.NavigateToLogout' instead.")] -public class SignOutSessionStateManager -{ - private readonly IJSRuntime _jsRuntime; - - /// - /// Initialize a new instance of . - /// - /// The . - public SignOutSessionStateManager(IJSRuntime jsRuntime) => _jsRuntime = jsRuntime; - - /// - /// Sets up some state in session storage to allow for logouts from within the page. - /// - /// A that completes when the state has been saved to session storage. - [DynamicDependency(JsonSerialized, typeof(SignOutState))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The correct members will be preserved by the above DynamicDependency.")] - // This should use JSON source generation - public virtual ValueTask SetSignOutState() - { - return _jsRuntime.InvokeVoidAsync( - "sessionStorage.setItem", - "Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutState", - JsonSerializer.Serialize(SignOutState.Instance, JsonSerializerOptions.Web)); - } - - /// - /// Validates the existence of some state previously setup by in session storage to allow - /// logouts from within the page. - /// - /// A that completes when the state has been validated and indicates the validity of the state. - public virtual async Task ValidateSignOutState() - { - var state = await GetSignOutState(); - if (state.Local) - { - await ClearSignOutState(); - return true; - } - - return false; - } - - private async ValueTask GetSignOutState() - { - var result = await _jsRuntime.InvokeAsync( - "sessionStorage.getItem", - "Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutState"); - if (result == null) - { - return default; - } - - return DeserializeSignOutState(result); - } - - [DynamicDependency(JsonSerialized, typeof(SignOutState))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The correct members will be preserved by the above DynamicDependency.")] - // This should use JSON source generation - private static SignOutState DeserializeSignOutState(string result) => JsonSerializer.Deserialize(result, JsonSerializerOptions.Web); - - private ValueTask ClearSignOutState() - { - return _jsRuntime.InvokeVoidAsync( - "sessionStorage.removeItem", - "Microsoft.AspNetCore.Components.WebAssembly.Authentication.SignOutState"); - } - - private struct SignOutState - { - public static readonly SignOutState Instance = new SignOutState { Local = true }; - - public bool Local { get; set; } - } -} diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs index 20cf1e0867f7..ee43cde4379f 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs @@ -71,9 +71,6 @@ public static IRemoteAuthenticationBuilder services.TryAddScoped>(); services.TryAddScoped(); -#pragma warning disable CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility - services.TryAddScoped(); -#pragma warning restore CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility services.TryAddScoped>(); diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj index 01092f99d71d..4180f2e54394 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj @@ -6,6 +6,8 @@ + + diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/RemoteAuthenticatorCoreTests.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/RemoteAuthenticatorCoreTests.cs index 694e3aea29f4..c624fcfc9178 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/RemoteAuthenticatorCoreTests.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly.Authentication/test/RemoteAuthenticatorCoreTests.cs @@ -34,7 +34,7 @@ public async Task AuthenticationManager_Throws_ForInvalidAction() }); // Act & assert - await Assert.ThrowsAsync(() => remoteAuthenticator.SetParametersAsync(parameters)); + await Assert.ThrowsAsync(() => remoteAuthenticator.SetParametersAsync(parameters)); } [Fact] @@ -370,37 +370,6 @@ public async Task AuthenticationManager_Logout_DoesNothingOnRedirect() } - [Fact] - public async Task AuthenticationManager_Logout_RedirectsToFailureOnInvalidSignOutState() - { - // Arrange - var (remoteAuthenticator, renderer, authServiceMock) = CreateAuthenticationManager( - "https://www.example.com/base/authentication/logout", - new InteractiveRequestOptions { Interaction = InteractionType.SignIn, ReturnUrl = "https://www.example.com/base/fetchData" }.ToState()); - - if (remoteAuthenticator.SignOutManager is TestSignOutSessionStateManager testManager) - { - testManager.SignOutState = false; - } - - var parameters = ParameterView.FromDictionary(new Dictionary - { - [_action] = RemoteAuthenticationActions.LogOut - }); - - // Act - await renderer.Dispatcher.InvokeAsync(() => remoteAuthenticator.SetParametersAsync(parameters)); - - // Assert - Assert.Equal( - "https://www.example.com/base/authentication/logout-failed", - remoteAuthenticator.Navigation.Uri); - - Assert.Equal( - "The logout was not initiated from within the page.", - ((TestNavigationManager)remoteAuthenticator.Navigation).HistoryEntryState); - } - [Fact] public async Task AuthenticationManager_Logout_NavigatesToLogoutFailureOnError() { @@ -731,8 +700,6 @@ private static Mock.Of>>(), navigationManager); - remoteAuthenticator.SignOutManager = new TestSignOutSessionStateManager(); - remoteAuthenticator.AuthenticationService = authenticationServiceMock; remoteAuthenticator.AuthenticationProvider = authenticationServiceMock; return (remoteAuthenticator, renderer, authenticationServiceMock); @@ -756,24 +723,6 @@ protected override void NavigateToCore(string uri, NavigationOptions options) } } -#pragma warning disable CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility - private class TestSignOutSessionStateManager : SignOutSessionStateManager -#pragma warning restore CS0618 // Type or member is obsolete, we keep it for now for backwards compatibility - { - public TestSignOutSessionStateManager() : base(null) - { - } - public bool SignOutState { get; set; } = true; - - public override ValueTask SetSignOutState() - { - SignOutState = true; - return default; - } - - public override Task ValidateSignOutState() => Task.FromResult(SignOutState); - } - private class TestJsRuntime : IJSRuntime { public (string identifier, object[] args) LastInvocation { get; set; } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/HostedServiceExecutor.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/HostedServiceExecutor.cs new file mode 100644 index 000000000000..a1866a988647 --- /dev/null +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/HostedServiceExecutor.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +internal sealed partial class HostedServiceExecutor +{ + private readonly IEnumerable _services; + private readonly ILogger _logger; + + public HostedServiceExecutor(IEnumerable services, ILogger logger) + { + _services = services; + _logger = logger; + } + + public async Task StartAsync(CancellationToken token) + { + foreach (var service in _services) + { + await service.StartAsync(token); + } + } + + public async Task StopAsync(CancellationToken token) + { + List? exceptions = null; + + foreach (var service in _services) + { + try + { + await service.StopAsync(token); + } + catch (Exception ex) + { + exceptions ??= []; + exceptions.Add(ex); + } + } + + // Throw an aggregate exception if there were any exceptions + if (exceptions is not null) + { + var aggregateException = new AggregateException(exceptions); + try + { + Log.ErrorStoppingHostedServices(_logger, aggregateException); + } + catch + { + // Ignore logging errors + } + throw aggregateException; + } + } + + private static partial class Log + { + [LoggerMessage(1, LogLevel.Error, "An error occurred stopping hosted services.", EventName = "ErrorStoppingHostedServices")] + public static partial void ErrorStoppingHostedServices(ILogger logger, Exception exception); + } +} diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs index 99d1476dc10d..4d9f51634da0 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyCultureProvider.cs @@ -17,23 +17,19 @@ internal partial class WebAssemblyCultureProvider internal const string ReadSatelliteAssemblies = "window.Blazor._internal.readSatelliteAssemblies"; // For unit testing. - internal WebAssemblyCultureProvider(CultureInfo initialCulture, CultureInfo initialUICulture) + internal WebAssemblyCultureProvider(CultureInfo initialCulture) { InitialCulture = initialCulture; - InitialUICulture = initialUICulture; } public static WebAssemblyCultureProvider? Instance { get; private set; } public CultureInfo InitialCulture { get; } - public CultureInfo InitialUICulture { get; } - internal static void Initialize() { Instance = new WebAssemblyCultureProvider( - initialCulture: CultureInfo.CurrentCulture, - initialUICulture: CultureInfo.CurrentUICulture); + initialCulture: CultureInfo.GetCultureInfo(WebAssemblyCultureProviderInterop.GetApplicationCulture() ?? CultureInfo.InvariantCulture.Name)); } public void ThrowIfCultureChangeIsUnsupported() @@ -48,8 +44,7 @@ public void ThrowIfCultureChangeIsUnsupported() // The current method is invoked as part of WebAssemblyHost.RunAsync i.e. after user code in Program.MainAsync has run // thus allows us to detect if the culture was changed by user code. if (Environment.GetEnvironmentVariable("__BLAZOR_SHARDED_ICU") == "1" && - ((!CultureInfo.CurrentCulture.Name.Equals(InitialCulture.Name, StringComparison.Ordinal) || - !CultureInfo.CurrentUICulture.Name.Equals(InitialUICulture.Name, StringComparison.Ordinal)))) + (!CultureInfo.CurrentCulture.Name.Equals(InitialCulture.Name, StringComparison.Ordinal))) { throw new InvalidOperationException("Blazor detected a change in the application's culture that is not supported with the current project configuration. " + "To change culture dynamically during startup, set true in the application's project file."); @@ -118,5 +113,8 @@ private partial class WebAssemblyCultureProviderInterop { [JSImport("INTERNAL.loadSatelliteAssemblies")] public static partial Task LoadSatelliteAssemblies(string[] culturesToLoad); + + [JSImport("Blazor._internal.getApplicationCulture", "blazor-internal")] + public static partial string GetApplicationCulture(); } } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index afc21cf445ab..9ba493f76a46 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -38,6 +38,7 @@ public sealed class WebAssemblyHost : IAsyncDisposable private bool _disposed; private bool _started; private WebAssemblyRenderer? _renderer; + private HostedServiceExecutor? _hostedServiceExecutor; internal WebAssemblyHost( WebAssemblyHostBuilder builder, @@ -78,7 +79,20 @@ public async ValueTask DisposeAsync() _disposed = true; - if (_renderer != null) + // Stop hosted services first + if (_hostedServiceExecutor is not null) + { + try + { + await _hostedServiceExecutor.StopAsync(CancellationToken.None); + } + catch + { + // Ignore errors when stopping hosted services during disposal + } + } + + if (_renderer is not null) { await _renderer.DisposeAsync(); } @@ -137,6 +151,10 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken, WebAssembl manager.SetPlatformRenderMode(RenderMode.InteractiveWebAssembly); await manager.RestoreStateAsync(store, RestoreContext.InitialValue); + // Start hosted services + _hostedServiceExecutor = Services.GetRequiredService(); + await _hostedServiceExecutor.StartAsync(cancellationToken); + var tcs = new TaskCompletionSource(); using (cancellationToken.Register(() => tcs.TrySetResult())) { diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs index c9971669d6c0..dbddb912eeb9 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs @@ -52,6 +52,11 @@ public static WebAssemblyHostBuilder CreateDefault(string[]? args = default) WebAssemblyCultureProvider.Initialize(); + // Add environment variables to configuration by default. + // This aligns WebAssembly behavior with server-side ASP.NET Core applications + // where environment variables are automatically included in IConfiguration. + builder.Configuration.AddEnvironmentVariables(); + // Right now we don't have conventions or behaviors that are specific to this method // however, making this the default for the template allows us to add things like that // in the future, while giving `new WebAssemblyHostBuilder` as an opt-out of opinionated @@ -315,6 +320,8 @@ public WebAssemblyHost Build() return new WebAssemblyHost(this, services, scope, _persistedState); } + [DynamicDependency(JsonSerialized, typeof(DefaultAntiforgeryStateProvider))] + [DynamicDependency(JsonSerialized, typeof(AntiforgeryRequestToken))] internal void InitializeDefaultServices() { Services.AddSingleton(DefaultWebAssemblyJSRuntime.Instance); @@ -337,5 +344,6 @@ internal void InitializeDefaultServices() Services.AddSingleton(); RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(Services, RenderMode.InteractiveWebAssembly); Services.AddSupplyValueFromQueryProvider(); + Services.AddSingleton(); } } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Infrastructure/JSInteropMethods.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Infrastructure/JSInteropMethods.cs index 12849698a874..fffc8dd15a18 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Infrastructure/JSInteropMethods.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Infrastructure/JSInteropMethods.cs @@ -14,13 +14,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Infrastructure; [EditorBrowsable(EditorBrowsableState.Never)] public static class JSInteropMethods { - /// - /// For framework use only. - /// - [Obsolete("This API is for framework use only and is no longer used in the current version")] - public static void NotifyLocationChanged(string uri, bool isInterceptedLink) - => WebAssemblyNavigationManager.Instance.SetLocation(uri, null, isInterceptedLink); - /// /// For framework use only. /// diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index f0418fd10354..9f6faa18141c 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -17,13 +17,16 @@ + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt index 61755ac3eb86..b8dde15da73b 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Shipped.txt @@ -2,11 +2,11 @@ ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment.BaseAddress.get -> string ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment.Environment.get -> string ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.ComponentType.get -> System.Type -~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type componentType, string selector) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type componentType, string selector, Microsoft.AspNetCore.Components.ParameterView parameters) -> void +~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type componentType, string selector) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.Selector.get -> string -~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type componentType, string selector) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type componentType, string selector, Microsoft.AspNetCore.Components.ParameterView parameters) -> void +~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type componentType, string selector) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(string selector) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.AddRange(System.Collections.Generic.IEnumerable items) -> void ~Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost.Configuration.get -> Microsoft.Extensions.Configuration.IConfiguration @@ -46,12 +46,12 @@ Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.ComponentType.get -> System.Type! Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.Parameters.get -> Microsoft.AspNetCore.Components.ParameterView Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping() -> void -Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type! componentType, string! selector) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type! componentType, string! selector, Microsoft.AspNetCore.Components.ParameterView parameters) -> void +Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.RootComponentMapping(System.Type! componentType, string! selector) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMapping.Selector.get -> string! Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection -Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type! componentType, string! selector) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type! componentType, string! selector, Microsoft.AspNetCore.Components.ParameterView parameters) -> void +Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(System.Type! componentType, string! selector) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.Add(string! selector) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.AddRange(System.Collections.Generic.IEnumerable! items) -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection.JSComponents.get -> Microsoft.AspNetCore.Components.Web.JSComponentConfigurationStore! @@ -69,6 +69,8 @@ Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.HostE Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.Logging.get -> Microsoft.Extensions.Logging.ILoggingBuilder! Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents.get -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.RootComponentMappingCollection! Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.UseServiceProviderOptions(Microsoft.Extensions.DependencyInjection.ServiceProviderOptions! options) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.Add(Microsoft.Extensions.Configuration.IConfigurationSource! source) -> Microsoft.Extensions.Configuration.IConfigurationBuilder! Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.Build() -> Microsoft.Extensions.Configuration.IConfigurationRoot! @@ -80,7 +82,6 @@ Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.this[string! key].set -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostConfiguration.WebAssemblyHostConfiguration() -> void Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions -Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.Default = 0 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache.ForceCache = 4 -> Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache @@ -103,12 +104,12 @@ Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader.LazyAssemblyLoader(Microsoft.JSInterop.IJSRuntime! jsRuntime) -> void Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader.LoadAssembliesAsync(System.Collections.Generic.IEnumerable! assembliesToLoad) -> System.Threading.Tasks.Task!>! static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault(string![]? args = null) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! +static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsDevelopment(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsEnvironment(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment, string! environmentName) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsProduction(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostEnvironmentExtensions.IsStaging(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment! hostingEnvironment) -> bool -static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void -static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCache(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCache requestCache) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestCredentials(this System.Net.Http.HttpRequestMessage! requestMessage, Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestCredentials requestCredentials) -> System.Net.Http.HttpRequestMessage! static Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestIntegrity(this System.Net.Http.HttpRequestMessage! requestMessage, string! integrity) -> System.Net.Http.HttpRequestMessage! diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt index 7cfa4804d26b..c65885b63ac7 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/PublicAPI.Unshipped.txt @@ -1,8 +1,3 @@ #nullable enable -Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.UseServiceProviderOptions(Microsoft.Extensions.DependencyInjection.ServiceProviderOptions! options) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! -Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions -static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! -static Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilderExtensions.UseDefaultServiceProvider(this Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! builder, System.Action! configure) -> Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder! -*REMOVED*Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload -*REMOVED*static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDelta(string! moduleIdString, byte[]! metadataDelta, byte[]! ilDelta, byte[]! pdbBytes, int[]? updatedTypes) -> void -*REMOVED*static Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.GetApplyUpdateCapabilities() -> string! +*REMOVED*~static Microsoft.AspNetCore.Components.WebAssembly.Infrastructure.JSInteropMethods.NotifyLocationChanged(string uri, bool isInterceptedLink) -> void +*REMOVED*static Microsoft.AspNetCore.Components.WebAssembly.Infrastructure.JSInteropMethods.NotifyLocationChanged(string! uri, bool isInterceptedLink) -> void diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs index 83f14d1bd2b1..560f64fa1154 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/IInternalJSImportMethods.cs @@ -8,6 +8,8 @@ internal interface IInternalJSImportMethods string GetPersistedState(); string GetApplicationEnvironment(); + + string GetApplicationCulture(); void AttachRootComponentToElement(string domElementSelector, int componentId, int rendererId); diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs index 811a78dbc652..628d1c4f7186 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/src/Services/InternalJSImportMethods.cs @@ -27,6 +27,9 @@ public static async Task GetInitialComponentUpdate( public string GetApplicationEnvironment() => GetApplicationEnvironmentCore(); + public string GetApplicationCulture() + => GetApplicationCultureCore(); + public void AttachRootComponentToElement(string domElementSelector, int componentId, int rendererId) => AttachRootComponentToElementCore(domElementSelector, componentId, rendererId); @@ -72,6 +75,9 @@ public string RegisteredComponents_GetParameterValues(int id) [JSImport("Blazor._internal.getApplicationEnvironment", "blazor-internal")] private static partial string GetApplicationEnvironmentCore(); + [JSImport("Blazor._internal.getApplicationCulture", "blazor-internal")] + private static partial string GetApplicationCultureCore(); + [JSImport("Blazor._internal.attachRootComponentToElement", "blazor-internal")] private static partial void AttachRootComponentToElementCore(string domElementSelector, int componentId, int rendererId); diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs index 22ad7d18fffc..2406a919b2f6 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyCultureProviderTest.cs @@ -47,7 +47,7 @@ public void ThrowIfCultureChangeIsUnsupported_ThrowsIfCulturesAreDifferentAndICU try { // WebAssembly is initialized with en-US - var cultureProvider = new WebAssemblyCultureProvider(new CultureInfo("en-US"), new CultureInfo("en-US")); + var cultureProvider = new WebAssemblyCultureProvider(new CultureInfo("en-US")); // Culture is changed to fr-FR as part of the app using var cultureReplacer = new CultureReplacer("fr-FR"); diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs index 901712a21240..e2321da4593a 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostBuilderTest.cs @@ -310,4 +310,35 @@ private class CircularServiceB { public CircularServiceB(CircularServiceA serviceA) { } } + + [Fact] + public void Configuration_IncludesEnvironmentVariables_WhenAddedExplicitly() + { + // Arrange + var testEnvVarKey = $"TEST_WASM_CONFIG_{Guid.NewGuid():N}"; + var testEnvVarValue = "test-value-12345"; + + try + { + // Set an environment variable before creating the builder + Environment.SetEnvironmentVariable(testEnvVarKey, testEnvVarValue); + + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods()); + + // This mimics what CreateDefault now does + builder.Configuration.AddEnvironmentVariables(); + + // Act + var host = builder.Build(); + + // Assert + var configuration = host.Services.GetRequiredService(); + Assert.Equal(testEnvVarValue, configuration[testEnvVarKey]); + } + finally + { + // Clean up the environment variable + Environment.SetEnvironmentVariable(testEnvVarKey, null); + } + } } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs index 552ca8272707..fafb1a9cfecb 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs @@ -5,6 +5,7 @@ using System.Text.Json; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.JSInterop; using Moq; @@ -83,6 +84,172 @@ public async Task DisposeAsync_CanDisposeAfterCallingRunAsync() Assert.Equal(1, disposable.DisposeCount); } + [Fact] + public async Task RunAsync_StartsHostedServices() + { + // Arrange + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods()); + builder.Services.AddSingleton(Mock.Of()); + + var testHostedService = new TestHostedService(); + builder.Services.AddSingleton(testHostedService); + + var host = builder.Build(); + var cultureProvider = new TestSatelliteResourcesLoader(); + + var cts = new CancellationTokenSource(); + + // Act + var task = host.RunAsyncCore(cts.Token, cultureProvider); + + // Give hosted services time to start + await Task.Delay(100); + cts.Cancel(); + await task.TimeoutAfter(TimeSpan.FromSeconds(3)); + + // Assert + Assert.True(testHostedService.StartCalled); + Assert.Equal(cts.Token, testHostedService.StartToken); + } + + [Fact] + public async Task DisposeAsync_StopsHostedServices() + { + // Arrange + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods()); + builder.Services.AddSingleton(Mock.Of()); + + var testHostedService1 = new TestHostedService(); + var testHostedService2 = new TestHostedService(); + builder.Services.AddSingleton(testHostedService1); + builder.Services.AddSingleton(testHostedService2); + + var host = builder.Build(); + var cultureProvider = new TestSatelliteResourcesLoader(); + + var cts = new CancellationTokenSource(); + + // Start the host to initialize hosted services + var runTask = host.RunAsyncCore(cts.Token, cultureProvider); + await Task.Delay(100); + + // Act - dispose the host + await host.DisposeAsync(); + cts.Cancel(); + await runTask.TimeoutAfter(TimeSpan.FromSeconds(3)); + + // Assert + Assert.True(testHostedService1.StartCalled); + Assert.True(testHostedService1.StopCalled); + Assert.True(testHostedService2.StartCalled); + Assert.True(testHostedService2.StopCalled); + } + + [Fact] + public async Task DisposeAsync_HandlesHostedServiceStopErrors() + { + // Arrange + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods()); + builder.Services.AddSingleton(Mock.Of()); + + var goodService = new TestHostedService(); + var faultyService = new FaultyHostedService(); + builder.Services.AddSingleton(goodService); + builder.Services.AddSingleton(faultyService); + + var host = builder.Build(); + var cultureProvider = new TestSatelliteResourcesLoader(); + + var cts = new CancellationTokenSource(); + + // Start the host to initialize hosted services + var runTask = host.RunAsyncCore(cts.Token, cultureProvider); + await Task.Delay(100); + + // Act & Assert - dispose should not throw even if hosted service fails + await host.DisposeAsync(); + cts.Cancel(); + await runTask.TimeoutAfter(TimeSpan.FromSeconds(3)); + + Assert.True(goodService.StartCalled); + Assert.True(goodService.StopCalled); + Assert.True(faultyService.StartCalled); + Assert.True(faultyService.StopCalled); + } + + [Fact] + public async Task RunAsync_SupportsAddHostedServiceExtension() + { + // Arrange + var builder = new WebAssemblyHostBuilder(new TestInternalJSImportMethods()); + builder.Services.AddSingleton(Mock.Of()); + + // Test manual hosted service registration (equivalent to AddHostedService) + builder.Services.AddSingleton(); + builder.Services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); + + var host = builder.Build(); + var cultureProvider = new TestSatelliteResourcesLoader(); + + var cts = new CancellationTokenSource(); + + // Act + var task = host.RunAsyncCore(cts.Token, cultureProvider); + + // Give hosted services time to start + await Task.Delay(100); + cts.Cancel(); + await task.TimeoutAfter(TimeSpan.FromSeconds(3)); + + // Assert - verify the hosted service was started via service collection + var hostedServices = host.Services.GetServices(); + Assert.Single(hostedServices); + + var testService = hostedServices.First(); + Assert.IsType(testService); + Assert.True(((TestHostedService)testService).StartCalled); + } + + private class TestHostedService : IHostedService + { + public bool StartCalled { get; private set; } + public bool StopCalled { get; private set; } + public CancellationToken StartToken { get; private set; } + public CancellationToken StopToken { get; private set; } + + public Task StartAsync(CancellationToken cancellationToken) + { + StartCalled = true; + StartToken = cancellationToken; + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + StopCalled = true; + StopToken = cancellationToken; + return Task.CompletedTask; + } + } + + private class FaultyHostedService : IHostedService + { + public bool StartCalled { get; private set; } + public bool StopCalled { get; private set; } + + public Task StartAsync(CancellationToken cancellationToken) + { + StartCalled = true; + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + StopCalled = true; + throw new InvalidOperationException("Simulated hosted service stop error"); + } + } + private class DisposableService : IAsyncDisposable { public int DisposeCount { get; private set; } @@ -97,7 +264,7 @@ public ValueTask DisposeAsync() private class TestSatelliteResourcesLoader : WebAssemblyCultureProvider { internal TestSatelliteResourcesLoader() - : base(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture) + : base(CultureInfo.CurrentCulture) { } diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj index 2f9ac20c7c09..9a1a4a3a4b94 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj @@ -5,8 +5,11 @@ + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs index 74e1d6b676ac..9aa4ebd48f62 100644 --- a/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs +++ b/src/aspnetcore/src/Components/WebAssembly/WebAssembly/test/TestInternalJSImportMethods.cs @@ -16,6 +16,9 @@ public TestInternalJSImportMethods(string environment = "Production") public string GetApplicationEnvironment() => _environment; + + public string GetApplicationCulture() + => "en-US"; public string GetPersistedState() => null; diff --git a/src/aspnetcore/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj b/src/aspnetcore/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj index 3b727d71c2b8..bbbcdc1aa08d 100644 --- a/src/aspnetcore/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj @@ -13,7 +13,30 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/testassets/StandaloneApp/Pages/Index.razor b/src/aspnetcore/src/Components/WebAssembly/testassets/StandaloneApp/Pages/Index.razor index 16dac3192520..05ee2ee15015 100644 --- a/src/aspnetcore/src/Components/WebAssembly/testassets/StandaloneApp/Pages/Index.razor +++ b/src/aspnetcore/src/Components/WebAssembly/testassets/StandaloneApp/Pages/Index.razor @@ -1,5 +1,5 @@ @page "/" -

Hello, world!

+

Hello, world!

Welcome to your new app. diff --git a/src/aspnetcore/src/Components/WebAssembly/testassets/ThreadingApp.Server/ThreadingApp.Server.csproj b/src/aspnetcore/src/Components/WebAssembly/testassets/ThreadingApp.Server/ThreadingApp.Server.csproj index 860fc351bae6..4ea0982d4e5f 100644 --- a/src/aspnetcore/src/Components/WebAssembly/testassets/ThreadingApp.Server/ThreadingApp.Server.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/testassets/ThreadingApp.Server/ThreadingApp.Server.csproj @@ -11,12 +11,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj b/src/aspnetcore/src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj index 793e251088b9..c7170840dd57 100644 --- a/src/aspnetcore/src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj +++ b/src/aspnetcore/src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj @@ -12,13 +12,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs b/src/aspnetcore/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs index e997d5dd7bc6..c7dbe764b9b0 100644 --- a/src/aspnetcore/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs +++ b/src/aspnetcore/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs @@ -15,10 +15,10 @@ public class WebViewManagerE2ETests // There's probably some way to make it work, but it's not currently a supported Blazor Hybrid scenario anyway // - macOS is skipped due to the test not being able to detect when the WebView is ready. There's probably an issue // with the JS code sending a WebMessage to C# and not being sent properly or detected properly. - [ConditionalFact] - [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, - SkipReason = "On Helix/Ubuntu the native Photino assemblies can't be found, and on macOS it can't detect when the WebView is ready")] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/50802")] + // [ConditionalFact] + // [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, + // SkipReason = "On Helix/Ubuntu the native Photino assemblies can't be found, and on macOS it can't detect when the WebView is ready")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/54017")] public async Task CanLaunchPhotinoWebViewAndClickButton() { var photinoTestProgramExePath = typeof(WebViewManagerE2ETests).Assembly.Location; diff --git a/src/aspnetcore/src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj b/src/aspnetcore/src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj index 0a92ed6f8863..79a371cdc758 100644 --- a/src/aspnetcore/src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj +++ b/src/aspnetcore/src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj @@ -11,6 +11,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs index cd2edbe5b129..a93abfb0b062 100644 --- a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs +++ b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs @@ -79,7 +79,10 @@ public static async Task Main(string[] args) var timeForEachRun = TimeSpan.FromMinutes(3); var launchUrl = $"{testAppUrl}?resultsUrl={UrlEncoder.Default.Encode(receiverUrl)}#automated"; - var page = await browser.NewPageAsync(); + var page = await browser.NewPageAsync(new() + { + Locale = "en-US", + }); await page.GotoAsync(launchUrl); page.Console += WriteBrowserConsoleMessage; diff --git a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj index 99555fccda38..31a271d0d389 100644 --- a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +++ b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj @@ -13,8 +13,21 @@ - + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/dockerfile b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/dockerfile index 77b82df53363..6bbb775f1be8 100644 --- a/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/dockerfile +++ b/src/aspnetcore/src/Components/benchmarkapps/Wasm.Performance/dockerfile @@ -25,11 +25,11 @@ RUN git init \ RUN ./restore.sh RUN npm run build -RUN .dotnet/dotnet publish -c Release -r linux-x64 --sc true -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +RUN .dotnet/dotnet publish -c Release -r linux-x64 --sc true -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj -p:BlazorFingerprintBlazorJs=false RUN chmod +x /app/Wasm.Performance.Driver WORKDIR /app -FROM mcr.microsoft.com/playwright/dotnet:v1.54.0-jammy-amd64 AS final +FROM mcr.microsoft.com/playwright/dotnet:v1.56.0-jammy-amd64 AS final COPY --from=build ./app ./ COPY ./exec.sh ./ diff --git a/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ScrollOverrideScope.cs b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ScrollOverrideScope.cs new file mode 100644 index 000000000000..e6f84fabcf56 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ScrollOverrideScope.cs @@ -0,0 +1,181 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using OpenQA.Selenium; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure; + +internal sealed class ScrollOverrideScope : IDisposable +{ + private readonly IJavaScriptExecutor _executor; + private readonly bool _isActive; + + public ScrollOverrideScope(IWebDriver browser, bool isActive) + { + _executor = (IJavaScriptExecutor)browser; + _isActive = isActive; + + if (!_isActive) + { + return; + } + + _executor.ExecuteScript(@" +(function() { + if (window.__enhancedNavScrollOverride) { + if (window.__clearEnhancedNavScrollLog) { + window.__clearEnhancedNavScrollLog(); + } + return; + } + + const original = window.scrollTo.bind(window); + const log = []; + + function resolvePage() { + const landing = document.getElementById('test-info-1'); + if (landing && landing.textContent === 'Scroll tests landing page') { + return 'landing'; + } + + const next = document.getElementById('test-info-2'); + if (next && next.textContent === 'Scroll tests next page') { + return 'next'; + } + + return 'other'; + } + + window.__enhancedNavScrollOverride = true; + window.__enhancedNavOriginalScrollTo = original; + window.__enhancedNavScrollLog = log; + window.__clearEnhancedNavScrollLog = () => { log.length = 0; }; + window.__drainEnhancedNavScrollLog = () => { + const copy = log.slice(); + log.length = 0; + return copy; + }; + + window.scrollTo = function(...args) { + log.push({ + page: resolvePage(), + url: location.href, + time: performance.now(), + args + }); + + return original(...args); + }; +})(); +"); + + ClearLog(); + } + + public void ClearLog() + { + if (!_isActive) + { + return; + } + + _executor.ExecuteScript("if (window.__clearEnhancedNavScrollLog) { window.__clearEnhancedNavScrollLog(); }"); + } + + public void AssertNoPrematureScroll(string expectedPage, string navigationDescription) + { + if (!_isActive) + { + return; + } + + var entries = DrainLog(); + if (entries.Length == 0) + { + return; + } + + var unexpectedEntries = entries + .Where(entry => !string.Equals(entry.Page, expectedPage, StringComparison.Ordinal)) + .ToArray(); + + if (unexpectedEntries.Length == 0) + { + return; + } + + var details = string.Join( + ", ", + unexpectedEntries.Select(entry => $"page={entry.Page ?? "null"} url={entry.Url} time={entry.Time:F2}")); + + throw new XunitException($"Detected a scroll reset while the DOM still displayed '{unexpectedEntries[0].Page ?? "unknown"}' during {navigationDescription}. Entries: {details}"); + } + + private ScrollInvocation[] DrainLog() + { + if (!_isActive) + { + return Array.Empty(); + } + + var result = _executor.ExecuteScript("return window.__drainEnhancedNavScrollLog ? window.__drainEnhancedNavScrollLog() : [];"); + if (result is not IReadOnlyList entries || entries.Count == 0) + { + return Array.Empty(); + } + + var resolved = new ScrollInvocation[entries.Count]; + for (var i = 0; i < entries.Count; i++) + { + if (entries[i] is IReadOnlyDictionary dict) + { + dict.TryGetValue("page", out var pageValue); + dict.TryGetValue("url", out var urlValue); + dict.TryGetValue("time", out var timeValue); + + resolved[i] = new ScrollInvocation( + pageValue as string, + urlValue as string, + timeValue is null ? 0D : Convert.ToDouble(timeValue, CultureInfo.InvariantCulture)); + continue; + } + + resolved[i] = new ScrollInvocation(null, null, 0D); + } + + return resolved; + } + + public void Dispose() + { + if (!_isActive) + { + return; + } + + _executor.ExecuteScript(@" +(function() { + if (!window.__enhancedNavScrollOverride) { + return; + } + + if (window.__enhancedNavOriginalScrollTo) { + window.scrollTo = window.__enhancedNavOriginalScrollTo; + delete window.__enhancedNavOriginalScrollTo; + } + + delete window.__enhancedNavScrollOverride; + delete window.__enhancedNavScrollLog; + delete window.__clearEnhancedNavScrollLog; + delete window.__drainEnhancedNavScrollLog; +})(); +"); + } + + private readonly record struct ScrollInvocation(string Page, string Url, double Time); +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ServerTestBase.cs b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ServerTestBase.cs index 841cecb32695..315b80cd2248 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ServerTestBase.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/ServerTestBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests; using Microsoft.AspNetCore.E2ETesting; using OpenQA.Selenium; using Xunit.Abstractions; @@ -31,6 +32,12 @@ public void Navigate(string relativeUrl) Browser.Navigate(_serverFixture.RootUri, relativeUrl); } + public override async Task DisposeAsync() + { + EnhancedNavigationTestUtil.CleanEnhancedNavigationSuppression(this); + await base.DisposeAsync(); + } + protected override void InitializeAsyncCore() { // Clear logs - we check these during tests in some cases. diff --git a/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/WebDriverExtensions/WebDriverExtensions.cs b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/WebDriverExtensions/WebDriverExtensions.cs index 34276e78613e..c6221085a514 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/WebDriverExtensions/WebDriverExtensions.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Infrastructure/WebDriverExtensions/WebDriverExtensions.cs @@ -1,17 +1,16 @@ // 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.Threading; using OpenQA.Selenium; using OpenQA.Selenium.Support.UI; -using System; +using Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests; namespace Microsoft.AspNetCore.Components.E2ETest; internal static class WebDriverExtensions { - private static string GetFindPositionScript(string elementId) => - $"return Math.round(document.getElementById('{elementId}').getBoundingClientRect().top + window.scrollY);"; - public static void Navigate(this IWebDriver browser, Uri baseUri, string relativeUrl) { var absoluteUrl = new Uri(baseUri, relativeUrl); @@ -45,27 +44,101 @@ public static void WaitForElementToBeVisible(this IWebDriver browser, By by, int public static long GetElementPositionWithRetry(this IWebDriver browser, string elementId, int retryCount = 3, int delayBetweenRetriesMs = 100) { - var jsExecutor = (IJavaScriptExecutor)browser; - string script = GetFindPositionScript(elementId); - browser.WaitForElementToBeVisible(By.Id(elementId)); + string log = ""; + for (int i = 0; i < retryCount; i++) { try { - var result = jsExecutor.ExecuteScript(script); - if (result != null) - { - return (long)result; - } + browser.WaitForElementToBeVisible(By.Id(elementId)); + var element = browser.FindElement(By.Id(elementId)); + return element.Location.Y; } - catch (OpenQA.Selenium.JavaScriptException) + catch (Exception ex) { - // JavaScript execution failed, retry + log += $"Attempt {i + 1}: - {ex.Message}. "; } - Thread.Sleep(delayBetweenRetriesMs); + if (i < retryCount - 1) + { + Thread.Sleep(delayBetweenRetriesMs); + } } - throw new Exception($"Failed to execute script after {retryCount} retries."); + throw new Exception($"Failed to get position for element '{elementId}' after {retryCount} retries. Debug log: {log}"); + } + + internal static ScrollObservation BeginScrollObservation(this IWebDriver browser, IWebElement element, Func domMutationPredicate) + { + ArgumentNullException.ThrowIfNull(browser); + ArgumentNullException.ThrowIfNull(element); + ArgumentNullException.ThrowIfNull(domMutationPredicate); + + var initialScrollPosition = browser.GetScrollY(); + return new ScrollObservation(element, initialScrollPosition, domMutationPredicate); + } + + internal static ScrollObservationResult WaitForStaleDomOrScrollChange(this IWebDriver browser, ScrollObservation observation, TimeSpan? timeout = null, TimeSpan? pollingInterval = null) + { + ArgumentNullException.ThrowIfNull(browser); + + var wait = new DefaultWait(browser) + { + Timeout = timeout ?? TimeSpan.FromSeconds(10), + PollingInterval = pollingInterval ?? TimeSpan.FromMilliseconds(50), + }; + wait.IgnoreExceptionTypes(typeof(InvalidOperationException)); + + ScrollObservationOutcome? detectedOutcome = null; + wait.Until(driver => + { + if (observation.DomMutationPredicate(driver)) + { + detectedOutcome = ScrollObservationOutcome.DomUpdated; + return true; + } + + if (observation.Element.IsStale()) + { + detectedOutcome = ScrollObservationOutcome.DomUpdated; + return true; + } + + if (browser.GetScrollY() != observation.InitialScrollPosition) + { + detectedOutcome = ScrollObservationOutcome.ScrollChanged; + return true; + } + + return false; + }); + + var outcome = detectedOutcome ?? ScrollObservationOutcome.DomUpdated; + + var finalScrollPosition = browser.GetScrollY(); + return new ScrollObservationResult(outcome, observation.InitialScrollPosition, finalScrollPosition); + } + + internal static bool IsStale(this IWebElement element) + { + try + { + _ = element.Enabled; + return false; + } + catch (StaleElementReferenceException) + { + return true; + } } } + +internal readonly record struct ScrollObservation(IWebElement Element, long InitialScrollPosition, Func DomMutationPredicate); + +internal readonly record struct ScrollObservationResult(ScrollObservationOutcome Outcome, long InitialScrollPosition, long FinalScrollPosition); + +internal enum ScrollObservationOutcome +{ + ScrollChanged, + DomUpdated, +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj b/src/aspnetcore/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj index b147f6bc4877..d0c6c4336a49 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj +++ b/src/aspnetcore/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj @@ -43,8 +43,30 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitContextTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitContextTest.cs index 91c2b580fe60..5fe0f8a2ee37 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitContextTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitContextTest.cs @@ -41,7 +41,7 @@ public void ComponentMethods_HaveCircuitContext_OnInitialPageLoad() // Internal for reuse in Blazor Web tests internal static void TestCircuitContextCore(IWebDriver browser) { - browser.Equal("Circuit Context", () => browser.Exists(By.TagName("h1")).Text); + browser.Equal("Circuit Context", () => browser.Exists(By.Id("circuit-context-title")).Text); browser.Click(By.Id("trigger-click-event-button")); diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitGracefulTerminationTests.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitGracefulTerminationTests.cs index ff7e56f9a363..37ecf327872a 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitGracefulTerminationTests.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/CircuitGracefulTerminationTests.cs @@ -40,7 +40,7 @@ protected override void InitializeAsyncCore() { Navigate(ServerPathBase); Browser.MountTestComponent(); - Browser.Equal("Graceful Termination", () => Browser.Exists(By.TagName("h1")).Text); + Browser.Equal("Graceful Termination", () => Browser.Exists(By.Id("graceful-termination-title")).Text); GracefulDisconnectCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Sink = _serverFixture.Host.Services.GetRequiredService(); diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/NavigationLockPrerenderingTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/NavigationLockPrerenderingTest.cs index 645a29cc7bfa..7b88e1d88745 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/NavigationLockPrerenderingTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/NavigationLockPrerenderingTest.cs @@ -24,9 +24,8 @@ public NavigationLockPrerenderingTest( public override Task InitializeAsync() => InitializeAsync(BrowserFixture.RoutingTestContext); - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/57153")] - public void NavigationIsLockedAfterPrerendering() + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/57153")] + public void ExternalNavigationIsLockedAfterPrerendering() { Navigate("/locked-navigation"); @@ -35,13 +34,24 @@ public void NavigationIsLockedAfterPrerendering() BeginInteractivity(); - // Assert that internal navigations are blocked - Browser.Click(By.Id("internal-navigation-link")); - Browser.Equal("Prevented navigations: 1", () => Browser.FindElement(By.Id("num-prevented-navigations")).Text); - // Assert that external navigations are blocked Browser.Navigate().GoToUrl("about:blank"); Browser.SwitchTo().Alert().Dismiss(); + Browser.Equal("Prevented navigations: 0", () => Browser.FindElement(By.Id("num-prevented-navigations")).Text); + } + + [Fact] + public void InternalNavigationIsLockedAfterPrerendering() + { + Navigate("/locked-navigation"); + + // Assert that the component rendered successfully + Browser.Equal("Prevented navigations: 0", () => Browser.FindElement(By.Id("num-prevented-navigations")).Text); + + BeginInteractivity(); + + // Assert that internal navigations are blocked + Browser.Click(By.Id("internal-navigation-link")); Browser.Equal("Prevented navigations: 1", () => Browser.FindElement(By.Id("num-prevented-navigations")).Text); } diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerNestedOptionsTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerNestedOptionsTest.cs new file mode 100644 index 000000000000..05d9416fdac1 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerNestedOptionsTest.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using TestServer; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests; + +public class ServerNestedOptionsTest : ServerTestBase> +{ + public ServerNestedOptionsTest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate($"{ServerPathBase}/nestedCircuitOptions"); + } + + [Fact] + public void NestedCircuitOptionsAreAccepted() + { + var appElement = Browser.MountTestComponent(); + var countDisplayElement = appElement.FindElement(By.TagName("p")); + Browser.Equal("Current count: 0", () => countDisplayElement.Text); + + appElement.FindElement(By.TagName("button")).Click(); + Browser.Equal("Current count: 1", () => countDisplayElement.Text); + } +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionWithoutStateTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionWithoutStateTest.cs new file mode 100644 index 000000000000..d6892741e7c6 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionWithoutStateTest.cs @@ -0,0 +1,168 @@ +// 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.Collections.Generic; +using System.Text; +using Components.TestServer.RazorComponents; +using Microsoft.AspNetCore.Components.E2ETest; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using OpenQA.Selenium.BiDi.Communication; +using OpenQA.Selenium.DevTools; +using TestServer; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests; + +public class ServerReconnectionWithoutStateTest : ServerTestBase>> +{ + public ServerReconnectionWithoutStateTest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture> serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + serverFixture.AdditionalArguments.AddRange("--DisableReconnectionCache", "true"); + serverFixture.AdditionalArguments.AddRange("--DisableCircuitPersistence", "true"); + } + + protected override void InitializeAsyncCore() + { + Navigate(TestUrl); + Browser.Exists(By.Id("render-mode-interactive")); + } + + public string TestUrl { get; set; } = "/subdir/persistent-state/disconnection"; + + public bool UseShadowRoot { get; set; } = true; + + [Fact] + public void ReloadsPage_AfterDisconnection_WithoutServerState() + { + // Check interactivity + Browser.Equal("5", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + Browser.Exists(By.Id("increment-non-persisted-counter")).Click(); + Browser.Equal("6", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + + // Store a reference to an element to detect page reload + // When the page reloads, this element reference will become stale + var initialElement = Browser.Exists(By.Id("non-persisted-counter")); + var initialConnectedLogCount = GetConnectedLogCount(); + + // Force close the connection + // The client should get rejected on both reconnection and circuit resume because the server has no state + var javascript = (IJavaScriptExecutor)Browser; + javascript.ExecuteScript("Blazor._internal.forceCloseConnection()"); + + // Check for page reload using multiple conditions: + // 1. Previously captured element is stale + Browser.True(initialElement.IsStale); + // 2. Counter state is reset + Browser.Equal("5", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + // 3. WebSocket connection has been re-established + Browser.True(() => GetConnectedLogCount() == initialConnectedLogCount + 1); + + int GetConnectedLogCount() => Browser.Manage().Logs.GetLog(LogType.Browser) + .Where(l => l.Level == LogLevel.Info && l.Message.Contains("Information: WebSocket connected")).Count(); + } + + [Fact] + public void CanResume_AfterClientPause_WithoutServerState() + { + // Initial state: NonPersistedCounter should be 5 + Browser.Equal("5", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + + // Increment both counters + Browser.Exists(By.Id("increment-persistent-counter-count")).Click(); + Browser.Exists(By.Id("increment-non-persisted-counter")).Click(); + + Browser.Equal("1", () => Browser.Exists(By.Id("persistent-counter-count")).Text); + Browser.Equal("6", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + + var javascript = (IJavaScriptExecutor)Browser; + TriggerClientPauseAndInteract(javascript); + + // After first reconnection: + Browser.Equal("2", () => Browser.Exists(By.Id("persistent-counter-count")).Text); + Browser.Equal("0", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + + // Increment non-persisted counter again + Browser.Exists(By.Id("increment-non-persisted-counter")).Click(); + Browser.Equal("1", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + + TriggerClientPauseAndInteract(javascript); + + // After second reconnection: + Browser.Equal("3", () => Browser.Exists(By.Id("persistent-counter-count")).Text); + Browser.Equal("0", () => Browser.Exists(By.Id("non-persisted-counter")).Text); + } + + private void TriggerClientPauseAndInteract(IJavaScriptExecutor javascript) + { + var previousText = Browser.Exists(By.Id("persistent-counter-render")).Text; + javascript.ExecuteScript("Blazor.pauseCircuit()"); + Browser.Equal("block", () => Browser.Exists(By.Id("components-reconnect-modal")).GetCssValue("display")); + + // Retry button should be hidden + Browser.Equal( + (false, true), + () => Browser.Exists( + () => + { + var buttons = UseShadowRoot ? + Browser.Exists(By.Id("components-reconnect-modal")) + .GetShadowRoot() + .FindElements(By.CssSelector(".components-reconnect-dialog button")) : + Browser.Exists(By.Id("components-reconnect-modal")) + .FindElements(By.CssSelector(".components-reconnect-container button")); + + Assert.Equal(2, buttons.Count); + return (buttons[0].Displayed, buttons[1].Displayed); + }, + TimeSpan.FromSeconds(1))); + + Browser.Exists( + () => + { + var buttons = UseShadowRoot ? + Browser.Exists(By.Id("components-reconnect-modal")) + .GetShadowRoot() + .FindElements(By.CssSelector(".components-reconnect-dialog button")) : + Browser.Exists(By.Id("components-reconnect-modal")) + .FindElements(By.CssSelector(".components-reconnect-container button")); + return buttons[1]; + }, + TimeSpan.FromSeconds(1)).Click(); + + // Then it should disappear + Browser.Equal("none", () => Browser.Exists(By.Id("components-reconnect-modal")).GetCssValue("display")); + + var newText = Browser.Exists(By.Id("persistent-counter-render")).Text; + Assert.NotEqual(previousText, newText); + + Browser.Exists(By.Id("increment-persistent-counter-count")).Click(); + } +} + +public class ServerReconnectionWithoutStateCustomUITest : ServerReconnectionWithoutStateTest +{ + public ServerReconnectionWithoutStateCustomUITest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture> serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + TestUrl = "/subdir/persistent-state/disconnection?custom-reconnect-ui=true"; + UseShadowRoot = false; // Custom UI does not use shadow DOM + } + + protected override void InitializeAsyncCore() + { + base.InitializeAsyncCore(); + Browser.Exists(By.CssSelector("#components-reconnect-modal[data-nosnippet]")); + } +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/WebSocketCompressionTests.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/WebSocketCompressionTests.cs index 8468ea27ef5b..e6fa6e34e032 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/WebSocketCompressionTests.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/WebSocketCompressionTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.E2ETesting; +using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.DependencyInjection; using OpenQA.Selenium; using TestServer; @@ -104,11 +105,17 @@ public void EmbeddingServerAppInsideIframe_WithCompressionEnabled_Fails() Assert.True(logs.Count > 0); - Assert.Matches(ParseErrorMessageRegex, logs[0].Message); + Assert.True( + ParseErrorMessageRegexOld.IsMatch(logs[0].Message) || + ParseErrorMessageRegexNew.IsMatch(logs[0].Message), + $"Expected log message to match one of the CSP error patterns: {ParseErrorMessageRegexOld} or {ParseErrorMessageRegexNew}. Actual: {logs[0].Message}"); } [GeneratedRegex(@"security - Refused to frame 'http://\d+\.\d+\.\d+\.\d+:\d+/' because an ancestor violates the following Content Security Policy directive: ""frame-ancestors 'none'"".")] - private static partial Regex ParseErrorMessageRegex { get; } + private static partial Regex ParseErrorMessageRegexOld { get; } + + [GeneratedRegex(@"security - Framing 'http://\d+\.\d+\.\d+\.\d+:\d+/' violates the following Content Security Policy directive: ""frame-ancestors 'none'"".")] + private static partial Regex ParseErrorMessageRegexNew { get; } } public partial class DefaultConfigurationWebSocketCompressionTests : AllowedWebSocketCompressionTests diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs index 7dfaf3523287..5b28c9d299ac 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs @@ -1,6 +1,8 @@ // 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.Globalization; using System.Threading.Tasks; using Components.TestServer.RazorComponents; using Microsoft.AspNetCore.Components.E2ETest; @@ -10,6 +12,7 @@ using Microsoft.AspNetCore.InternalTesting; using OpenQA.Selenium; using OpenQA.Selenium.BiDi.Communication; +using OpenQA.Selenium.DevTools; using OpenQA.Selenium.Support.Extensions; using TestServer; using Xunit.Abstractions; @@ -195,6 +198,40 @@ public void CanScrollToHashWithoutPerformingFullNavigation() .EndsWith("scroll-to-hash", StringComparison.Ordinal)); } + [Fact] + public void NonEnhancedNavCanScrollToHashWithoutFetchingPageAnchor() + { + Navigate($"{ServerPathBase}/nav/scroll-to-hash"); + var originalTextElem = Browser.Exists(By.CssSelector("#anchor #text")); + Browser.Equal("Text", () => originalTextElem.Text); + + Browser.Exists(By.CssSelector("#anchor #scroll-anchor")).Click(); + Browser.True(() => Browser.GetScrollY() > 500); + Browser.True(() => Browser + .Exists(By.CssSelector("#anchor #uri-on-page-load")) + .GetDomAttribute("data-value") + .EndsWith("scroll-to-hash", StringComparison.Ordinal)); + + Browser.Equal("Text", () => originalTextElem.Text); + } + + [Fact] + public void NonEnhancedNavCanScrollToHashWithoutFetchingPageNavLink() + { + Navigate($"{ServerPathBase}/nav/scroll-to-hash"); + var originalTextElem = Browser.Exists(By.CssSelector("#navlink #text")); + Browser.Equal("Text", () => originalTextElem.Text); + + Browser.Exists(By.CssSelector("#navlink #scroll-anchor")).Click(); + Browser.True(() => Browser.GetScrollY() > 500); + Browser.True(() => Browser + .Exists(By.CssSelector("#navlink #uri-on-page-load")) + .GetDomAttribute("data-value") + .EndsWith("scroll-to-hash", StringComparison.Ordinal)); + + Browser.Equal("Text", () => originalTextElem.Text); + } + [Theory] [InlineData("server")] [InlineData("webassembly")] @@ -212,22 +249,22 @@ public void CanPerformProgrammaticEnhancedNavigation(string renderMode) Browser.Exists(By.TagName("nav")).FindElement(By.LinkText($"Interactive component navigation ({renderMode})")).Click(); Browser.Equal("Page with interactive components that navigate", () => Browser.Exists(By.TagName("h1")).Text); - Browser.False(() => IsElementStale(elementForStalenessCheck)); + Browser.False(() => elementForStalenessCheck.IsStale()); Browser.Exists(By.Id("navigate-to-another-page")).Click(); Browser.Equal("Hello", () => Browser.Exists(By.TagName("h1")).Text); Assert.EndsWith("/nav", Browser.Url); - Browser.False(() => IsElementStale(elementForStalenessCheck)); + Browser.False(() => elementForStalenessCheck.IsStale()); // Ensure that the history stack was correctly updated Browser.Navigate().Back(); Browser.Equal("Page with interactive components that navigate", () => Browser.Exists(By.TagName("h1")).Text); - Browser.False(() => IsElementStale(elementForStalenessCheck)); + Browser.False(() => elementForStalenessCheck.IsStale()); Browser.Navigate().Back(); Browser.Equal("Hello", () => Browser.Exists(By.TagName("h1")).Text); Assert.EndsWith("/nav", Browser.Url); - Browser.False(() => IsElementStale(elementForStalenessCheck)); + Browser.False(() => elementForStalenessCheck.IsStale()); } [Theory] @@ -254,7 +291,7 @@ public void CanPerformProgrammaticEnhancedRefresh(string renderMode, string refr Browser.Exists(By.Id(refreshButtonId)).Click(); Browser.True(() => { - if (IsElementStale(renderIdElement) || !int.TryParse(renderIdElement.Text, out var newRenderId)) + if (renderIdElement.IsStale() || !int.TryParse(renderIdElement.Text, out var newRenderId)) { return false; } @@ -288,7 +325,7 @@ public void NavigateToCanFallBackOnFullPageReload(string renderMode) Assert.NotEqual(-1, initialRenderId); Browser.Exists(By.Id("reload-with-navigate-to")).Click(); - Browser.True(() => IsElementStale(initialRenderIdElement)); + Browser.True(() => initialRenderIdElement.IsStale()); var finalRenderIdElement = Browser.Exists(By.Id("render-id")); var finalRenderId = -1; @@ -317,6 +354,9 @@ public void RefreshCanFallBackOnFullPageReload(string renderMode) Browser.Navigate().Refresh(); Browser.Equal("Page with interactive components that navigate", () => Browser.Exists(By.TagName("h1")).Text); + // if we don't clean up the suppression, all subsequent navigations will be suppressed by default + EnhancedNavigationTestUtil.CleanEnhancedNavigationSuppression(this, skipNavigation: true); + // Normally, you shouldn't store references to elements because they could become stale references // after the page re-renders. However, we want to explicitly test that the element becomes stale // across renders to ensure that a full page reload occurs. @@ -325,8 +365,8 @@ public void RefreshCanFallBackOnFullPageReload(string renderMode) Browser.True(() => int.TryParse(initialRenderIdElement.Text, out initialRenderId)); Assert.NotEqual(-1, initialRenderId); - Browser.Exists(By.Id("refresh-with-refresh")).Click(); - Browser.True(() => IsElementStale(initialRenderIdElement)); + Browser.Exists(By.Id("refresh-with-refresh")).Click(); + Browser.True(() => initialRenderIdElement.IsStale()); var finalRenderIdElement = Browser.Exists(By.Id("render-id")); var finalRenderId = -1; @@ -359,8 +399,8 @@ public void RefreshWithForceReloadDoesFullPageReload(string renderMode) Browser.True(() => int.TryParse(initialRenderIdElement.Text, out initialRenderId)); Assert.NotEqual(-1, initialRenderId); - Browser.Exists(By.Id("reload-with-refresh")).Click(); - Browser.True(() => IsElementStale(initialRenderIdElement)); + Browser.Exists(By.Id("reload-with-refresh")).Click(); + Browser.True(() => initialRenderIdElement.IsStale()); var finalRenderIdElement = Browser.Exists(By.Id("render-id")); var finalRenderId = -1; @@ -677,11 +717,10 @@ public void CanUpdateHrefOnLinkTagWithIntegrity() } [Theory] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/60875")] - // [InlineData(false, false, false)] // https://github.com/dotnet/aspnetcore/issues/60875 + [InlineData(false, false, false)] [InlineData(false, true, false)] [InlineData(true, true, false)] - // [InlineData(true, false, false)] // https://github.com/dotnet/aspnetcore/issues/60875 + [InlineData(true, false, false)] // [InlineData(false, false, true)] programmatic navigation doesn't work without enhanced navigation [InlineData(false, true, true)] [InlineData(true, true, true)] @@ -692,50 +731,80 @@ public void EnhancedNavigationScrollBehavesSameAsBrowserOnNavigation(bool enable // or to the beginning of a fragment, regardless of the previous scroll position string landingPageSuffix = enableStreaming ? "" : "-no-streaming"; string buttonKeyword = programmaticNavigation ? "-programmatic" : ""; + EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation); Navigate($"{ServerPathBase}/nav/scroll-test{landingPageSuffix}"); - EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation, skipNavigation: true); // "landing" page: scroll maximally down and go to "next" page - we should land at the top of that page AssertWeAreOnLandingPage(); - // staleness check is used to assert enhanced navigation is enabled/disabled, as requested - var elementForStalenessCheckOnNextPage = Browser.Exists(By.TagName("html")); + var scrollOverride = new ScrollOverrideScope(Browser, useEnhancedNavigation); - var button1Id = $"do{buttonKeyword}-navigation"; - var button1Pos = Browser.GetElementPositionWithRetry(button1Id); - Browser.SetScrollY(button1Pos); - Browser.Exists(By.Id(button1Id)).Click(); - - // "next" page: check if we landed at 0, then navigate to "landing" - AssertWeAreOnNextPage(); - WaitStreamingRendersFullPage(enableStreaming); - string fragmentId = "some-content"; - Browser.WaitForElementToBeVisible(By.Id(fragmentId)); - AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); - Assert.Equal(0, Browser.GetScrollY()); - var elementForStalenessCheckOnLandingPage = Browser.Exists(By.TagName("html")); - var fragmentScrollPosition = Browser.GetElementPositionWithRetry(fragmentId); - Browser.Exists(By.Id(button1Id)).Click(); - - // "landing" page: navigate to a fragment on another page - we should land at the beginning of the fragment - AssertWeAreOnLandingPage(); - WaitStreamingRendersFullPage(enableStreaming); - AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); - - var button2Id = $"do{buttonKeyword}-navigation-with-fragment"; - Browser.Exists(By.Id(button2Id)).Click(); - AssertWeAreOnNextPage(); - WaitStreamingRendersFullPage(enableStreaming); - AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); - var expectedFragmentScrollPosition = fragmentScrollPosition; - Assert.Equal(expectedFragmentScrollPosition, Browser.GetScrollY()); + try + { + // Staleness check is used to assert enhanced navigation is enabled/disabled, as requested + var elementForStalenessCheckOnNextPage = Browser.Exists(By.TagName("html")); + + var button1Id = $"do{buttonKeyword}-navigation"; + var button1Pos = Browser.GetElementPositionWithRetry(button1Id); + Browser.SetScrollY(button1Pos); + scrollOverride.ClearLog(); + var firstNavigationObservation = BeginEnhancedNavigationObservationIfEnhancedNavigation( + useEnhancedNavigation, + elementForStalenessCheckOnNextPage, + ElementWithTextAppears(By.Id("test-info-2"), "Scroll tests next page")); + Browser.Exists(By.Id(button1Id)).Click(); + + // "next" page: check if we landed at 0, then navigate to "landing" + AssertWeAreOnNextPage(); + WaitStreamingRendersFullPage(enableStreaming); + const string fragmentId = "some-content"; + Browser.WaitForElementToBeVisible(By.Id(fragmentId)); + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); + AssertNoPrematureScrollBeforeDomSwapIfEnhancedNavigation(firstNavigationObservation, "landing -> next navigation"); + scrollOverride.AssertNoPrematureScroll("next", "landing -> next navigation"); + Assert.Equal(0, Browser.GetScrollY()); + var elementForStalenessCheckOnLandingPage = Browser.Exists(By.TagName("html")); + var fragmentScrollPosition = Browser.GetElementPositionWithRetry(fragmentId); + var secondNavigationObservation = BeginEnhancedNavigationObservationIfEnhancedNavigation( + useEnhancedNavigation, + elementForStalenessCheckOnLandingPage, + ElementWithTextAppears(By.Id("test-info-1"), "Scroll tests landing page")); + scrollOverride.ClearLog(); + Browser.Exists(By.Id(button1Id)).Click(); + + // "landing" page: navigate to a fragment on another page - we should land at the beginning of the fragment + AssertWeAreOnLandingPage(); + WaitStreamingRendersFullPage(enableStreaming); + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnLandingPage); + AssertNoPrematureScrollBeforeDomSwapIfEnhancedNavigation(secondNavigationObservation, "next -> landing navigation"); + scrollOverride.AssertNoPrematureScroll("landing", "next -> landing navigation"); + + var button2Id = $"do{buttonKeyword}-navigation-with-fragment"; + var thirdNavigationObservation = BeginEnhancedNavigationObservationIfEnhancedNavigation( + useEnhancedNavigation, + elementForStalenessCheckOnNextPage, + ElementWithTextAppears(By.Id("test-info-2"), "Scroll tests next page")); + scrollOverride.ClearLog(); + Browser.Exists(By.Id(button2Id)).Click(); + AssertWeAreOnNextPage(); + WaitStreamingRendersFullPage(enableStreaming); + AssertEnhancedNavigation(useEnhancedNavigation, elementForStalenessCheckOnNextPage); + AssertNoPrematureScrollBeforeDomSwapIfEnhancedNavigation(thirdNavigationObservation, "landing -> next (fragment) navigation"); + scrollOverride.AssertNoPrematureScroll("next", "landing -> next (fragment) navigation"); + var expectedFragmentScrollPosition = fragmentScrollPosition; + Assert.Equal(expectedFragmentScrollPosition, Browser.GetScrollY()); + } + finally + { + scrollOverride.Dispose(); + } } [Theory] - // [InlineData(false, false, false)] // https://github.com/dotnet/aspnetcore/issues/60875 + [InlineData(false, false, false)] [InlineData(false, true, false)] [InlineData(true, true, false)] - // [InlineData(true, false, false)] // https://github.com/dotnet/aspnetcore/issues/60875 + [InlineData(true, false, false)] // [InlineData(false, false, true)] programmatic navigation doesn't work without enhanced navigation [InlineData(false, true, true)] [InlineData(true, true, true)] @@ -745,8 +814,8 @@ public void EnhancedNavigationScrollBehavesSameAsBrowserOnBackwardsForwardsActio // This test checks if the scroll position is preserved after backwards/forwards action string landingPageSuffix = enableStreaming ? "" : "-no-streaming"; string buttonKeyword = programmaticNavigation ? "-programmatic" : ""; + EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation); Navigate($"{ServerPathBase}/nav/scroll-test{landingPageSuffix}"); - EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress: !useEnhancedNavigation, skipNavigation: true); // "landing" page: scroll to pos1, navigate away AssertWeAreOnLandingPage(); @@ -831,38 +900,44 @@ private void AssertScrollPositionCorrect(bool useEnhancedNavigation, long previo private void AssertEnhancedNavigation(bool useEnhancedNavigation, IWebElement elementForStalenessCheck, int retryCount = 3, int delayBetweenRetriesMs = 1000) { bool enhancedNavigationDetected = false; + string logging = ""; + string isNavigationSuppressed = ""; for (int i = 0; i < retryCount; i++) { try { - enhancedNavigationDetected = !IsElementStale(elementForStalenessCheck); + enhancedNavigationDetected = !elementForStalenessCheck.IsStale(); Assert.Equal(useEnhancedNavigation, enhancedNavigationDetected); return; } catch (XunitException) { + var logs = Browser.GetBrowserLogs(LogLevel.Warning); + logging += $"{string.Join(", ", logs.Select(l => l.Message))}\n"; + isNavigationSuppressed = (string)((IJavaScriptExecutor)Browser).ExecuteScript("return sessionStorage.getItem('suppress-enhanced-navigation');"); + + logging += $" isNavigationSuppressed: {isNavigationSuppressed}\n"; // Maybe the check was done too early to change the DOM ref, retry } Thread.Sleep(delayBetweenRetriesMs); } - string expectedNavigation = useEnhancedNavigation ? "enhanced navigation" : "browser navigation"; + string expectedNavigation = useEnhancedNavigation ? "enhanced navigation" : "full page load"; string isStale = enhancedNavigationDetected ? "is not stale" : "is stale"; - var isNavigationSupressed = (string)((IJavaScriptExecutor)Browser).ExecuteScript("return sessionStorage.getItem('suppress-enhanced-navigation');"); - throw new Exception($"Expected to use {expectedNavigation} because 'suppress-enhanced-navigation' is set to {isNavigationSupressed} but the element from previous path {isStale}"); + throw new Exception($"Expected to use {expectedNavigation} because 'suppress-enhanced-navigation' is set to {isNavigationSuppressed} but the element from previous path {isStale}. logging={logging}"); } private void AssertWeAreOnLandingPage() { string infoName = "test-info-1"; - Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 20); + Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 30); Browser.Equal("Scroll tests landing page", () => Browser.Exists(By.Id(infoName)).Text); } private void AssertWeAreOnNextPage() { string infoName = "test-info-2"; - Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 20); + Browser.WaitForElementToBeVisible(By.Id(infoName), timeoutInSeconds: 30); Browser.Equal("Scroll tests next page", () => Browser.Exists(By.Id(infoName)).Text); } @@ -877,16 +952,41 @@ private void WaitStreamingRendersFullPage(bool enableStreaming) private void AssertEnhancedUpdateCountEquals(long count) => Browser.Equal(count, () => ((IJavaScriptExecutor)Browser).ExecuteScript("return window.enhancedPageUpdateCount;")); - private static bool IsElementStale(IWebElement element) + private ScrollObservation? BeginEnhancedNavigationObservationIfEnhancedNavigation(bool useEnhancedNavigation, IWebElement elementForStalenessCheck, Func domMutationPredicate) => + useEnhancedNavigation ? Browser.BeginScrollObservation(elementForStalenessCheck, domMutationPredicate) : null; + + private void AssertNoPrematureScrollBeforeDomSwapIfEnhancedNavigation(ScrollObservation? observation, string navigationDescription) { + if (observation is not ScrollObservation context) + { + return; + } + + ScrollObservationResult result; try { - _ = element.Enabled; - return false; + result = Browser.WaitForStaleDomOrScrollChange(context); + } + catch (WebDriverTimeoutException ex) + { + throw new XunitException($"Timed out while waiting for the DOM to update or the scroll position to change during {navigationDescription}.", ex); } - catch (StaleElementReferenceException) + + if (result.Outcome == ScrollObservationOutcome.ScrollChanged) { - return true; + throw new XunitException($"Detected a scroll reset before the DOM update completed during {navigationDescription}. Scroll moved from {result.InitialScrollPosition} to {result.FinalScrollPosition} before the page rendered new content."); } } + + private static Func ElementWithTextAppears(By selector, string expectedText) => driver => + { + var elements = driver.FindElements(selector); + if (elements.Count == 0) + { + return false; + } + + // Ensure we actually observed the new content, not just the presence of the element. + return string.Equals(elements[0].Text, expectedText, StringComparison.Ordinal); + }; } diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTestUtil.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTestUtil.cs index 799b915f8fdd..1fa7b8fce9e3 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTestUtil.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTestUtil.cs @@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests; public static class EnhancedNavigationTestUtil { + private static bool _isSuppressed; + public static void SuppressEnhancedNavigation(ServerTestBase fixture, bool shouldSuppress, bool skipNavigation = false) where TServerFixture : ServerFixture { @@ -20,16 +22,76 @@ public static void SuppressEnhancedNavigation(ServerTestBase browser.Exists(By.TagName("h1")).Text); + NavigateToOrigin(fixture); + } + + try + { + ((IJavaScriptExecutor)browser).ExecuteScript("sessionStorage.length"); + } + catch (Exception ex) + { + throw new InvalidOperationException("Session storage not found. Ensure that the browser is on the correct origin by navigating to a page or by setting skipNavigation to false.", ex); } ((IJavaScriptExecutor)browser).ExecuteScript("sessionStorage.setItem('suppress-enhanced-navigation', 'true')"); + + var suppressEnhancedNavigation = ((IJavaScriptExecutor)browser).ExecuteScript("return sessionStorage.getItem('suppress-enhanced-navigation');"); + Assert.True(suppressEnhancedNavigation is not null && (string)suppressEnhancedNavigation == "true", + "Expected 'suppress-enhanced-navigation' to be set in sessionStorage."); + _isSuppressed = true; } } + public static void CleanEnhancedNavigationSuppression(ServerTestBase fixture, bool skipNavigation = false) + where TServerFixture : ServerFixture + { + if (!_isSuppressed) + { + return; + } + + var browser = fixture.Browser; + + try + { + // First, ensure we're on the correct origin to access sessionStorage + try + { + // Check if we can access sessionStorage from current location + ((IJavaScriptExecutor)browser).ExecuteScript("sessionStorage.length"); + } + catch + { + if (skipNavigation) + { + throw new InvalidOperationException("Session storage not found. Ensure that the browser is on the correct origin by navigating to a page or by setting skipNavigation to false."); + } + NavigateToOrigin(fixture); + } + ((IJavaScriptExecutor)browser).ExecuteScript($"sessionStorage.removeItem('suppress-enhanced-navigation')"); + } + catch (WebDriverException ex) when (ex.Message.Contains("invalid session id")) + { + // Browser session is no longer valid (e.g., browser was closed) + // Session storage is automatically cleared when browser closes, so cleanup is already done + // This is expected in some tests, so we silently return + return; + } + finally + { + _isSuppressed = false; + } + } + + private static void NavigateToOrigin(ServerTestBase fixture) + where TServerFixture : ServerFixture + { + // Navigate to the test origin to ensure the browser is on the correct state to access sessionStorage + fixture.Navigate($"{fixture.ServerPathBase}/"); + fixture.Browser.Exists(By.Id("session-storage-marker")); + } + public static long GetScrollY(this IWebDriver browser) => Convert.ToInt64(((IJavaScriptExecutor)browser).ExecuteScript("return window.scrollY"), CultureInfo.CurrentCulture); diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/AntiforgeryTests.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/AntiforgeryTests.cs index c56278d24d1e..ad1777e4f950 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/AntiforgeryTests.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/AntiforgeryTests.cs @@ -33,7 +33,6 @@ public override Task InitializeAsync() [Theory] [InlineData("server")] [InlineData("webassembly")] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/57766")] public void CanUseAntiforgeryAfterInitialRender(string target) { Navigate($"{ServerPathBase}/{target}-antiforgery-form"); @@ -47,7 +46,6 @@ public void CanUseAntiforgeryAfterInitialRender(string target) var submit = Browser.Exists(By.Id("submit")); submit.Click(); - var result = Browser.Exists(By.Id("result")); - Browser.Equal("Test", () => result.Text); + Browser.Equal("Test", () => Browser.FindElement(By.Id("result")).Text); } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithNoBackForwardCacheTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithNoBackForwardCacheTest.cs new file mode 100644 index 000000000000..3efc991508a7 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithNoBackForwardCacheTest.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.ObjectModel; +using System.Net.Http; +using System.Text; +using Components.TestServer.RazorComponents; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using Microsoft.AspNetCore.InternalTesting; +using OpenQA.Selenium; +using TestServer; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests.FormHandlingTests; + +public class FormWithNoBackForwardCacheTest : ServerTestBase>> +{ + public FormWithNoBackForwardCacheTest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture> serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + public override Task InitializeAsync() + { + return InitializeAsync(BrowserFixture.StreamingBackForwardCacheContext); + } + + private void SuppressEnhancedNavigation(bool shouldSuppress) + => EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, shouldSuppress); + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void CanUseFormWithMethodGet(bool suppressEnhancedNavigation) + { + SuppressEnhancedNavigation(suppressEnhancedNavigation); + Navigate($"{ServerPathBase}/forms/method-get"); + Browser.Equal("Form with method=get", () => Browser.FindElement(By.TagName("h2")).Text); + + // Validate initial state + var stringInput = Browser.FindElement(By.Id("mystring")); + var boolInput = Browser.FindElement(By.Id("mybool")); + Browser.Equal("Initial value", () => stringInput.GetDomProperty("value")); + Browser.Equal("False", () => boolInput.GetDomProperty("checked")); + + // Edit and submit the form; check it worked + stringInput.Clear(); + stringInput.SendKeys("Edited value"); + boolInput.Click(); + Browser.FindElement(By.Id("submit-get-form")).Click(); + AssertUiState("Edited value", true); + Browser.Contains($"MyString=Edited+value", () => Browser.Url); + Browser.Contains($"MyBool=True", () => Browser.Url); + + // Check 'back' correctly gets us to the previous state + Browser.Navigate().Back(); + AssertUiState("Initial value", false); + Browser.False(() => Browser.Url.Contains("MyString")); + Browser.False(() => Browser.Url.Contains("MyBool")); + + // Check 'forward' correctly recreates the edited state + Browser.Navigate().Forward(); + AssertUiState("Edited value", true); + Browser.Contains($"MyString=Edited+value", () => Browser.Url); + Browser.Contains($"MyBool=True", () => Browser.Url); + + void AssertUiState(string expectedStringValue, bool expectedBoolValue) + { + Browser.Equal(expectedStringValue, () => Browser.FindElement(By.Id("mystring-value")).Text); + Browser.Equal(expectedBoolValue.ToString(), () => Browser.FindElement(By.Id("mybool-value")).Text); + + // If we're not suppressing, we'll keep referencing the same elements to show they were preserved + if (suppressEnhancedNavigation) + { + stringInput = Browser.FindElement(By.Id("mystring")); + boolInput = Browser.FindElement(By.Id("mybool")); + } + + Browser.Equal(expectedStringValue, () => stringInput.GetDomProperty("value")); + Browser.Equal(expectedBoolValue.ToString(), () => boolInput.GetDomProperty("checked")); + } + } +} + diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs index d6dc438db468..270db100927d 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Text; using Components.TestServer.RazorComponents; +using Microsoft.AspNetCore.Components.E2ETest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.E2ETesting; @@ -1369,57 +1370,14 @@ public void CanBindToFormWithFiles() Assert.Equal("Total: 7", Browser.Exists(By.Id("form-collection")).Text); } - [Theory] - // [InlineData(true)] QuarantinedTest: https://github.com/dotnet/aspnetcore/issues/61882 - [InlineData(false)] - public void CanUseFormWithMethodGet(bool suppressEnhancedNavigation) + [Fact] + public void EditFormRecursiveBinding() { - SuppressEnhancedNavigation(suppressEnhancedNavigation); - GoTo("forms/method-get"); - Browser.Equal("Form with method=get", () => Browser.FindElement(By.TagName("h2")).Text); - - // Validate initial state - var stringInput = Browser.FindElement(By.Id("mystring")); - var boolInput = Browser.FindElement(By.Id("mybool")); - Browser.Equal("Initial value", () => stringInput.GetDomProperty("value")); - Browser.Equal("False", () => boolInput.GetDomProperty("checked")); - - // Edit and submit the form; check it worked - stringInput.Clear(); - stringInput.SendKeys("Edited value"); - boolInput.Click(); - Browser.FindElement(By.Id("submit-get-form")).Click(); - AssertUiState("Edited value", true); - Browser.Contains($"MyString=Edited+value", () => Browser.Url); - Browser.Contains($"MyBool=True", () => Browser.Url); - - // Check 'back' correctly gets us to the previous state - Browser.Navigate().Back(); - AssertUiState("Initial value", false); - Browser.False(() => Browser.Url.Contains("MyString")); - Browser.False(() => Browser.Url.Contains("MyBool")); - - // Check 'forward' correctly recreates the edited state - Browser.Navigate().Forward(); - AssertUiState("Edited value", true); - Browser.Contains($"MyString=Edited+value", () => Browser.Url); - Browser.Contains($"MyBool=True", () => Browser.Url); - - void AssertUiState(string expectedStringValue, bool expectedBoolValue) - { - Browser.Equal(expectedStringValue, () => Browser.FindElement(By.Id("mystring-value")).Text); - Browser.Equal(expectedBoolValue.ToString(), () => Browser.FindElement(By.Id("mybool-value")).Text); - - // If we're not suppressing, we'll keep referencing the same elements to show they were preserved - if (suppressEnhancedNavigation) - { - stringInput = Browser.FindElement(By.Id("mystring")); - boolInput = Browser.FindElement(By.Id("mybool")); - } - - Browser.Equal(expectedStringValue, () => stringInput.GetDomProperty("value")); - Browser.Equal(expectedBoolValue.ToString(), () => boolInput.GetDomProperty("checked")); - } + GoTo("forms/recursive-edit-form"); + Browser.Equal("", () => Browser.Exists(By.Id("result-form")).Text); + Browser.Exists(By.Id("text-input")).SendKeys("John"); + Browser.Exists(By.Id("submit-button")).Click(); + Browser.Equal("John", () => Browser.Exists(By.Id("result-form")).Text); } [Fact] @@ -1443,7 +1401,6 @@ public void RadioButtonGetsResetAfterSubmittingEnhancedForm() } [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/60067")] public void SubmitButtonFormactionAttributeOverridesEnhancedFormAction() { GoTo("forms/form-submit-button-with-formaction"); @@ -1678,7 +1635,7 @@ private void DispatchToFormCore(DispatchToForm dispatch) if (!dispatch.FormIsEnhanced) { // Verify the same form element is *not* still in the page - Browser.True(() => IsElementStale(form)); + Browser.True(() => form.IsStale()); } else if (!dispatch.SuppressEnhancedNavigation) { @@ -1730,19 +1687,6 @@ private void GoTo(string relativePath) Navigate($"{ServerPathBase}/{relativePath}"); } - private static bool IsElementStale(IWebElement element) - { - try - { - _ = element.Enabled; - return false; - } - catch (StaleElementReferenceException) - { - return true; - } - } - private struct TempFile { public string Name { get; } diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs index 3322c184ef7a..8ad4cbb048e4 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Components.TestServer.RazorComponents; +using Microsoft.AspNetCore.Components.E2ETest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests; @@ -1287,6 +1288,30 @@ void SetUpPageWithOneInteractiveServerComponent() } } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void CanPerformNavigateToFromInteractiveEventHandler(bool suppressEnhancedNavigation, bool forceLoad) + { + EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, suppressEnhancedNavigation); + + // Get to the test page + Navigate($"{ServerPathBase}/interactivity/navigateto"); + Browser.Equal("Interactive NavigateTo", () => Browser.FindElement(By.TagName("h1")).Text); + var originalNavElem = Browser.FindElement(By.TagName("nav")); + + // Perform the navigation + Browser.Click(By.Id(forceLoad ? "perform-navigateto-force" : "perform-navigateto")); + Browser.True(() => Browser.Url.EndsWith("/nav", StringComparison.Ordinal)); + Browser.Equal("Hello", () => Browser.FindElement(By.Id("nav-home")).Text); + + // Verify the elements were preserved if and only if they should be + var shouldPreserveElements = !suppressEnhancedNavigation && !forceLoad; + Assert.Equal(shouldPreserveElements, !originalNavElem.IsStale()); + } + private void BlockWebAssemblyResourceLoad() { // Force a WebAssembly resource cache miss so that we can fall back to using server interactivity @@ -1515,6 +1540,24 @@ public void BrowserNavigationToNotExistingPathReExecutesTo404(string renderMode) Assert404ReExecuted(); } + [Fact] + public void BrowserNavigationToNotExistingPathReExecutesTo404_Interactive() + { + // non-existing path has to have re-execution middleware set up + // so it has to have "interactive-reexecution" prefix. Otherwise middleware mapping + // will not be activated, see configuration in Startup + Navigate($"{ServerPathBase}/interactive-reexecution/not-existing-page"); + Assert404ReExecuted(); + AssertReExecutedPageIsInteractive(); + } + + private void AssertReExecutedPageIsInteractive() + { + Browser.Equal("Current count: 0", () => Browser.FindElement(By.CssSelector("[role='status']")).Text); + Browser.Click(By.Id("increment-button")); + Browser.Equal("Current count: 1", () => Browser.FindElement(By.CssSelector("[role='status']")).Text); + } + private void Assert404ReExecuted() => Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text); } diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs index a6421eb94689..9aead93eee25 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Net.Http; +using System; using Components.TestServer.RazorComponents; using Microsoft.AspNetCore.Components.E2ETest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; @@ -125,6 +125,23 @@ public void BrowserNavigationToNotExistingPath_ReExecutesTo404(bool streaming) AssertReExecutionPageRendered(); } + [Fact] + public void BrowserNavigationToNotExistingPath_WithOnNavigateAsync_ReExecutesTo404() + { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); + Navigate($"{ServerPathBase}/reexecution/not-existing-page?useOnNavigateAsync=true"); + AssertReExecutionPageRendered(); + } + + [Fact] + public void BrowserNavigationToNotExistingPath_WithOnNavigateAsync_ReExecutesTo404_CanStream() + { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); + Navigate($"{ServerPathBase}/streaming-reexecution/not-existing-page?useOnNavigateAsync=true"); + AssertReExecutionPageRendered(); + Browser.Equal("Streaming completed.", () => Browser.Exists(By.Id("reexecute-streaming-status")).Text); + } + private void AssertReExecutionPageRendered() => Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text); @@ -192,7 +209,7 @@ public void NotFoundSetOnInitialization_ResponseNotStarted_SSR(bool hasReExecuti [InlineData(false, true)] [InlineData(false, false)] // This tests the application subscribing to OnNotFound event and setting NotFoundEventArgs.Path, opposed to the framework doing it for the app. - public void NotFoundSetOnInitialization_ApplicationSubscribesToNotFoundEventToSetNotFoundPath_SSR (bool streaming, bool customRouter) + public void NotFoundSetOnInitialization_ApplicationSubscribesToNotFoundEventToSetNotFoundPath_SSR(bool streaming, bool customRouter) { string streamingPath = streaming ? "-streaming" : ""; string testUrl = $"{ServerPathBase}/set-not-found-ssr{streamingPath}?useCustomRouter={customRouter}&appSetsEventArgsPath=true"; diff --git a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs index 0f9b419fb847..60a5c34ed822 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/ServerRenderingTests/RedirectionTest.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.E2ETesting; +using Microsoft.AspNetCore.InternalTesting; using OpenQA.Selenium; using TestServer; using Xunit.Abstractions; @@ -283,6 +284,7 @@ public void RedirectEnhancedGetToInternalWithErrorBoundary(bool disableThrowNavi } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/pull/63708/")] public void NavigationException_InAsyncContext_DoesNotBecomeUnobservedTaskException() { AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", false); diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/CircuitTests.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/CircuitTests.cs index f4b4999c8b25..72de6e3b960c 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/CircuitTests.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/CircuitTests.cs @@ -35,7 +35,6 @@ protected override void InitializeAsyncCore() [InlineData("render-throw")] [InlineData("afterrender-sync-throw")] [InlineData("afterrender-async-throw")] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/57588")] public void ComponentLifecycleMethodThrowsExceptionTerminatesTheCircuit(string id) { Browser.MountTestComponent(); @@ -44,8 +43,7 @@ public void ComponentLifecycleMethodThrowsExceptionTerminatesTheCircuit(string i var targetButton = Browser.Exists(By.Id(id)); targetButton.Click(); - // Triggering an error will show the exception UI - Browser.Exists(By.CssSelector("#blazor-error-ui[style='display: block;']")); + DismissBlazorErrorUI(); // Clicking the button again will trigger a server disconnect targetButton.Click(); @@ -54,7 +52,6 @@ public void ComponentLifecycleMethodThrowsExceptionTerminatesTheCircuit(string i } [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/57588")] public void ComponentDisposeMethodThrowsExceptionTerminatesTheCircuit() { Browser.MountTestComponent(); @@ -67,7 +64,8 @@ public void ComponentDisposeMethodThrowsExceptionTerminatesTheCircuit() targetButton.Click(); // Clicking it again hides the component and invokes the rethrow which triggers the exception targetButton.Click(); - Browser.Exists(By.CssSelector("#blazor-error-ui[style='display: block;']")); + + DismissBlazorErrorUI(); // Clicking it again causes the circuit to disconnect targetButton.Click(); @@ -95,4 +93,17 @@ void AssertLogContains(params string[] messages) Assert.Contains(log, entry => entry.Message.Contains(message)); } } + + void DismissBlazorErrorUI() + { + // Triggering an error will show the exception UI + Browser.Exists(By.CssSelector("#blazor-error-ui[style='display: block;']")); + + // Dismiss the error UI by clicking the dismiss button + var dismissButton = Browser.Exists(By.CssSelector("#blazor-error-ui .dismiss")); + dismissButton.Click(); + + // Wait for error UI to be hidden + Browser.Exists(By.CssSelector("#blazor-error-ui[style='display: none;']")); + } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/ComponentRenderingTestBase.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/ComponentRenderingTestBase.cs index 92552b7ddc42..9c957c2da8a7 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/ComponentRenderingTestBase.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/ComponentRenderingTestBase.cs @@ -374,7 +374,7 @@ public void CanUseJsInteropToReferenceElements() [Fact] public void CanUseFocusExtensionToFocusElement() { - Browser.Manage().Window.Size = new System.Drawing.Size(100, 300); + Browser.SetWindowSize(100, 300); var appElement = Browser.MountTestComponent(); // y scroll position before click @@ -408,7 +408,7 @@ public void CanUseFocusExtensionToFocusElement() [Fact] public void CanUseFocusExtensionToFocusSvgElement() { - Browser.Manage().Window.Size = new System.Drawing.Size(100, 300); + Browser.SetWindowSize(100, 300); var appElement = Browser.MountTestComponent(); var buttonElement = appElement.FindElement(By.Id("focus-button")); @@ -430,7 +430,7 @@ public void CanUseFocusExtensionToFocusSvgElement() [Fact] public void CanUseFocusExtensionToFocusElementPreventScroll() { - Browser.Manage().Window.Size = new System.Drawing.Size(600, 600); + Browser.SetWindowSize(600, 600); var appElement = Browser.MountTestComponent(); var buttonElement = Browser.Exists(By.Id("focus-button-prevented")); diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs index 50e6fd06deef..80402e38bf54 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs @@ -126,6 +126,17 @@ public void CanHandleErrorsAfterDisposingComponent() AssertGlobalErrorState(false); } + [Fact] + public void CanHandleErrorsAfterDisposingErrorBoundaryComponent() + { + var container = Browser.Exists(By.Id("multiple-errors-at-once-test")); + container.FindElement(By.ClassName("throw-multiple-errors")).Click(); + // The error boundary is still there, so we see the error message + Browser.Collection(() => container.FindElements(By.ClassName("error-message")), + elem => Assert.Equal("OnInitializedAsyncError", elem.Text)); + AssertGlobalErrorState(false); + } + [Fact] public async Task CanHandleErrorsAfterDisposingErrorBoundary() { diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/EventCustomArgsTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/EventCustomArgsTest.cs index a5a236a67585..a0bafe47d750 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/EventCustomArgsTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/EventCustomArgsTest.cs @@ -170,6 +170,17 @@ public void CanRegisterCustomEventAndSupplyComplexParams() line => Assert.Equal("Event with IJSObjectReference received: Hello!", line)); } + [Fact] + public void ThrowsErrorWhenEventNameMatchesBrowserEventName() + { + // Attempt to register a custom event with the same name as the browser event + Browser.Exists(By.Id("register-invalid-same-name-event")).Click(); + + // Verify that an error is thrown + var errorMessage = Browser.Exists(By.Id("same-name-event-error")).Text; + Assert.Contains("cannot have the same name as its browserEventName", errorMessage); + } + void SendKeysSequentially(IWebElement target, string text) { foreach (var c in text) diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/EventTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/EventTest.cs index f102f85343eb..8316852991de 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/EventTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/EventTest.cs @@ -278,12 +278,13 @@ public void PreventDefault_AppliesToFormOnSubmitHandlers() } [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62533")] - public void PreventDefault_DotNotApplyByDefault() + public void PreventDefault_DoNotApplyByDefault() { var appElement = Browser.MountTestComponent(); appElement.FindElement(By.Id("form-2-button")).Click(); - Assert.Contains("about:blank", Browser.Url); + + // The URL should change because the submit event is not prevented + Browser.Contains("about:blank", () => Browser.Url); } [Fact] diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/FileDownloadTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/FileDownloadTest.cs new file mode 100644 index 000000000000..6307be02f5f5 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/FileDownloadTest.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using BasicTestApp; +using BasicTestApp.MediaTest; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; +using Xunit; +using Xunit.Abstractions; +using System.Globalization; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests; + +public class FileDownloadTest : ServerTestBase> +{ + public FileDownloadTest( + BrowserFixture browserFixture, + ToggleExecutionModeServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase); + Browser.MountTestComponent(); + } + + private void InstrumentDownload() + { + var success = ((IJavaScriptExecutor)Browser).ExecuteAsyncScript(@" + var callback = arguments[arguments.length - 1]; + (function(){ + if (window.__downloadInstrumentationStarted){ callback(true); return; } + window.__downloadInstrumentationStarted = true; + function tryPatch(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (!root || !root.downloadAsync){ setTimeout(tryPatch, 50); return; } + if (!window.__origDownloadAsync){ + window.__origDownloadAsync = root.downloadAsync; + window.__downloadCalls = 0; + window.__lastFileName = null; + root.downloadAsync = async function(...a){ + window.__downloadCalls++; + // downloadAsync(element, streamRef, mimeType, totalBytes, fileName) + window.__lastFileName = a[4]; // fileName index + if (window.__forceErrorFileName && a[4] === window.__forceErrorFileName){ + return false; // simulate failure + } + return window.__origDownloadAsync.apply(this, a); + }; + } + callback(true); + } + tryPatch(); + })(); + ") is true; + Assert.True(success, "Failed to instrument downloadAsync"); + Thread.Sleep(100); + } + + private int GetDownloadCallCount() => Convert.ToInt32(((IJavaScriptExecutor)Browser).ExecuteScript("return window.__downloadCalls || 0;"), CultureInfo.InvariantCulture); + + private string? GetLastFileName() => (string?)((IJavaScriptExecutor)Browser).ExecuteScript("return window.__lastFileName || null;"); + + [Fact] + public void InitialRender_DoesNotStartDownload() + { + InstrumentDownload(); + // Component rendered but no download link shown until button clicked + Assert.Equal(0, GetDownloadCallCount()); + } + + [Fact] + public void Click_InitiatesDownload() + { + InstrumentDownload(); + Browser.FindElement(By.Id("show-download")).Click(); + var link = Browser.FindElement(By.Id("download-link")); + link.Click(); + Browser.True(() => GetDownloadCallCount() >= 1); + Browser.True(() => GetLastFileName() == "test.png"); + Assert.Null(link.GetAttribute("data-state")); // no error or loading after completion + } + + [Fact] + public void BlankFileName_SuppressesDownload() + { + InstrumentDownload(); + Browser.FindElement(By.Id("show-blank-filename")).Click(); + var link = Browser.FindElement(By.Id("blank-download-link")); + link.Click(); + // Should not invoke JS because filename blank. Wait briefly to ensure no async call occurs. + var start = DateTime.UtcNow; + while (DateTime.UtcNow - start < TimeSpan.FromMilliseconds(200)) + { + Assert.True(GetDownloadCallCount() == 0, "Download should not have started for blank filename."); + Thread.Sleep(20); + } + Assert.Equal(0, GetDownloadCallCount()); + } + + [Fact] + public void ErrorDownload_SetsErrorState() + { + InstrumentDownload(); + // Force simulated failure via instrumentation hook + ((IJavaScriptExecutor)Browser).ExecuteScript("window.__forceErrorFileName='error.txt';"); + Browser.FindElement(By.Id("show-error-download")).Click(); + var link = Browser.FindElement(By.Id("error-download-link")); + link.Click(); + Browser.Equal("error", () => link.GetAttribute("data-state")); + } + + [Fact] + public void ProvidedHref_IsRemoved_AndInertHrefUsed() + { + Browser.FindElement(By.Id("show-custom-href")).Click(); + var link = Browser.FindElement(By.Id("custom-href-download-link")); + var href = link.GetAttribute("href"); + Assert.Equal("javascript:void(0)", href); + } + + [Fact] + public void RapidClicks_CancelsFirstAndStartsSecond() + { + // Instrument with controllable delay on first call for cancellation scenario + var success = ((IJavaScriptExecutor)Browser).ExecuteAsyncScript(@" + var callback = arguments[arguments.length - 1]; + (function(){ + function patch(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (!root || !root.downloadAsync){ requestAnimationFrame(patch); return; } + if (!window.__origDownloadAsyncDelay){ + window.__origDownloadAsyncDelay = root.downloadAsync; + window.__downloadCalls = 0; + window.__downloadDelayResolvers = null; + root.downloadAsync = async function(...a){ + window.__downloadCalls++; + if (window.__downloadCalls === 1){ + const getResolvers = () => { + if (Promise.fromResolvers) return Promise.fromResolvers(); + let resolve, reject; const p = new Promise((r,j)=>{ resolve=r; reject=j; }); + return { promise: p, resolve, reject }; + }; + if (!window.__downloadDelayResolvers){ + window.__downloadDelayResolvers = getResolvers(); + } + await window.__downloadDelayResolvers.promise; + } + return window.__origDownloadAsyncDelay.apply(this, a); + }; + } + callback(true); + } + patch(); + })(); + ") is true; + Assert.True(success, "Failed to instrument for rapid clicks test"); + + Browser.FindElement(By.Id("show-download")).Click(); + var link = Browser.FindElement(By.Id("download-link")); + link.Click(); // first (delayed) + link.Click(); // second should cancel first + + ((IJavaScriptExecutor)Browser).ExecuteScript("if (window.__downloadDelayResolvers) { window.__downloadDelayResolvers.resolve(); }"); + + Browser.True(() => Convert.ToInt32(((IJavaScriptExecutor)Browser).ExecuteScript("return window.__downloadCalls || 0;"), CultureInfo.InvariantCulture) >= 2); + Browser.True(() => string.IsNullOrEmpty(link.GetAttribute("data-state")) || link.GetAttribute("data-state") == null); + + // Cleanup instrumentation + ((IJavaScriptExecutor)Browser).ExecuteScript(@" + (function(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (root && window.__origDownloadAsyncDelay){ root.downloadAsync = window.__origDownloadAsyncDelay; delete window.__origDownloadAsyncDelay; } + delete window.__downloadDelayResolvers; + })();"); + } + + [Fact] + public void TemplatedFileDownload_Works() + { + InstrumentDownload(); + Browser.FindElement(By.Id("show-templated-download")).Click(); + var link = Browser.FindElement(By.Id("templated-download-link")); + Assert.NotNull(link); + link.Click(); + Browser.True(() => GetDownloadCallCount() >= 1); + Browser.True(() => GetLastFileName() == "templated.png"); + var status = Browser.FindElement(By.Id("templated-download-status")).Text; + Assert.True(status == "Idle/Done"); + } +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/FormsTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/FormsTest.cs index b58dee62cf0b..304ea1721abc 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/FormsTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/FormsTest.cs @@ -555,6 +555,32 @@ public void ErrorsFromCompareAttribute() Browser.Empty(confirmEmailValidationMessage); } + [Fact] + public void DisplayNameReadsAttributesCorrectly() + { + var appElement = Browser.MountTestComponent(); + + // Check that DisplayAttribute.Name is displayed + var displayNameLabel = appElement.FindElement(By.Id("product-name-label")); + Browser.Equal("Product Name", () => displayNameLabel.Text); + + // Check that DisplayNameAttribute is displayed + var priceLabel = appElement.FindElement(By.Id("price-label")); + Browser.Equal("Unit Price", () => priceLabel.Text); + + // Check that DisplayAttribute takes precedence over DisplayNameAttribute + var stockLabel = appElement.FindElement(By.Id("stock-label")); + Browser.Equal("Stock Quantity", () => stockLabel.Text); + + // Check fallback to property name when no attributes present + var descriptionLabel = appElement.FindElement(By.Id("description-label")); + Browser.Equal("Description", () => descriptionLabel.Text); + + // Check that ResourceType localization works with English resources + var localizedLabel = appElement.FindElement(By.Id("localized-label")); + Browser.Equal("Product Name", () => localizedLabel.Text); + } + [Fact] public void InputComponentsCauseContainerToRerenderOnChange() { diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs index 3e0a68780580..6ffd03d4636c 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs @@ -195,4 +195,21 @@ public void StatusCodePagesWithReExecution() } private void AssertReExecutedPageRendered() => Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text); + + [Fact] + public async Task HeadRequestReturnsSuccessWithNoBody() + { + // Arrange + using var client = new HttpClient() { BaseAddress = _serverFixture.RootUri }; + var request = new HttpRequestMessage(HttpMethod.Head, "/subdir/globally-interactive"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType?.ToString()); + var body = await response.Content.ReadAsStringAsync(); + Assert.Empty(body); + } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/ImageTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/ImageTest.cs new file mode 100644 index 000000000000..18a24255857f --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/ImageTest.cs @@ -0,0 +1,399 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using BasicTestApp; +using BasicTestApp.MediaTest; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests; + +public class ImageTest : ServerTestBase> +{ + public ImageTest( + BrowserFixture browserFixture, + ToggleExecutionModeServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase); + Browser.MountTestComponent(); + } + + private void ClearMediaCache() + { + var ok = (bool)((IJavaScriptExecutor)Browser).ExecuteAsyncScript(@" + var done = arguments[0]; + (async () => { + try { + if ('caches' in window) { + await caches.delete('blazor-media-cache'); + } + // Reset memoized cache promise if present + try { + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (root && 'cachePromise' in root) { + root.cachePromise = undefined; + } + } catch {} + done(true); + } catch (e) { + done(false); + } + })(); + "); + Assert.True(ok, "Failed to clear media cache"); + } + + [Fact] + public void CanLoadPngImage() + { + Browser.FindElement(By.Id("load-png")).Click(); + + Browser.Equal("PNG basic loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var imageElement = Browser.FindElement(By.Id("png-basic")); + + Assert.NotNull(imageElement); + + var src = imageElement.GetAttribute("src"); + Assert.True(!string.IsNullOrEmpty(src), "Image src should not be empty"); + Assert.True(src.StartsWith("blob:", StringComparison.Ordinal), $"Expected blob URL, but got: {src}"); + + var marker = imageElement.GetAttribute("data-blazor-image"); + Assert.NotNull(marker); + + var state = imageElement.GetAttribute("data-state"); + + Assert.True(string.IsNullOrEmpty(state), $"Expected data-state to be cleared after load, but found '{state}'"); + } + + [Fact] + public void CanLoadJpgImageFromStream() + { + Browser.FindElement(By.Id("load-jpg-stream")).Click(); + + Browser.Equal("JPG from stream loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var imageElement = Browser.FindElement(By.Id("jpg-stream")); + Assert.NotNull(imageElement); + + var src = imageElement.GetAttribute("src"); + Assert.True(!string.IsNullOrEmpty(src), "Image src should not be empty"); + Assert.True(src.StartsWith("blob:", StringComparison.Ordinal), $"Expected blob URL, but got: {src}"); + } + + [Fact] + public void CanChangeDynamicImageSource() + { + // First click - initialize with PNG + Browser.FindElement(By.Id("change-source")).Click(); + Browser.Equal("Dynamic source initialized with PNG", () => Browser.FindElement(By.Id("current-status")).Text); + + // Verify the image element exists and has a blob URL + var imageElement = Browser.FindElement(By.Id("dynamic-source")); + Assert.NotNull(imageElement); + + var firstSrc = imageElement.GetAttribute("src"); + Assert.True(!string.IsNullOrEmpty(firstSrc), "Image src should not be empty"); + Assert.True(firstSrc.StartsWith("blob:", StringComparison.Ordinal), $"Expected blob URL, but got: {firstSrc}"); + + // Second click - change to JPG + Browser.FindElement(By.Id("change-source")).Click(); + Browser.Equal("Dynamic source changed to JPG", () => Browser.FindElement(By.Id("current-status")).Text); + + // Verify the image source has changed + var secondSrc = imageElement.GetAttribute("src"); + Assert.True(!string.IsNullOrEmpty(secondSrc), "Image src should not be empty after change"); + Assert.True(secondSrc.StartsWith("blob:", StringComparison.Ordinal), $"Expected blob URL, but got: {secondSrc}"); + Assert.NotEqual(firstSrc, secondSrc); + + // Third click - change back to PNG + Browser.FindElement(By.Id("change-source")).Click(); + Browser.Equal("Dynamic source changed to PNG", () => Browser.FindElement(By.Id("current-status")).Text); + + // Verify the image source has changed again + var thirdSrc = imageElement.GetAttribute("src"); + Assert.True(!string.IsNullOrEmpty(thirdSrc), "Image src should not be empty after second change"); + Assert.True(thirdSrc.StartsWith("blob:", StringComparison.Ordinal), $"Expected blob URL, but got: {thirdSrc}"); + Assert.NotEqual(secondSrc, thirdSrc); + } + + [Fact] + public void ErrorImage_SetsErrorState() + { + Browser.FindElement(By.Id("load-error")).Click(); + Browser.Equal("Error image loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var errorImg = Browser.FindElement(By.Id("error-image")); + + Browser.Equal("error", () => Browser.FindElement(By.Id("error-image")).GetAttribute("data-state")); + var src = errorImg.GetAttribute("src"); + Assert.True(string.IsNullOrEmpty(src) || !src.StartsWith("blob:", StringComparison.Ordinal)); + } + + [Fact] + public void ImageRenders_WithCorrectDimensions() + { + Browser.FindElement(By.Id("load-png")).Click(); + Browser.Equal("PNG basic loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var imageElement = Browser.FindElement(By.Id("png-basic")); + + // Wait for actual dimensions to be set + Browser.True(() => + { + var width = imageElement.GetAttribute("naturalWidth"); + return !string.IsNullOrEmpty(width) && int.Parse(width, CultureInfo.InvariantCulture) > 0; + }); + + var naturalWidth = int.Parse(imageElement.GetAttribute("naturalWidth"), CultureInfo.InvariantCulture); + var naturalHeight = int.Parse(imageElement.GetAttribute("naturalHeight"), CultureInfo.InvariantCulture); + + Assert.Equal(1, naturalWidth); + Assert.Equal(1, naturalHeight); + } + + [Fact] + public void Image_CompletesLoad_AfterArtificialDelay() + { + // Instrument setContentAsync to pause before fulfilling first image load until explicitly resolved. + ((IJavaScriptExecutor)Browser).ExecuteScript(@" + (function(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (!root) return; + if (!window.__origSetContentAsync) { + window.__origSetContentAsync = root.setContentAsync; + root.setContentAsync = async function(...args){ + const getResolvers = () => { + if (Promise.fromResolvers) return Promise.fromResolvers(); + let resolve, reject; const promise = new Promise((r,j)=>{ resolve=r; reject=j; }); + return { promise, resolve, reject }; + }; + if (!window.__imageContentDelay){ + const resolvers = getResolvers(); + window.__imageContentDelay = resolvers; // first invocation delayed + await resolvers.promise; + } + return window.__origSetContentAsync.apply(this, args); + }; + } + })();"); + + Browser.FindElement(By.Id("load-png")).Click(); + + var imageElement = Browser.FindElement(By.Id("png-basic")); + Assert.NotNull(imageElement); + + // Release the delayed promise so load can complete. + ((IJavaScriptExecutor)Browser).ExecuteScript("if (window.__imageContentDelay) { window.__imageContentDelay.resolve(); }"); + + Browser.True(() => { + var src = imageElement.GetAttribute("src"); + return !string.IsNullOrEmpty(src) && src.StartsWith("blob:", StringComparison.Ordinal); + }); + Browser.Equal("PNG basic loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + // Restore original function and clean up instrumentation + ((IJavaScriptExecutor)Browser).ExecuteScript(@" + (function(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (root && window.__origSetContentAsync) { + root.setContentAsync = window.__origSetContentAsync; + delete window.__origSetContentAsync; + } + delete window.__imageContentDelay; + })();"); + } + + [Fact] + public void ImageCache_PersistsAcrossPageReloads() + { + ClearMediaCache(); + + Browser.FindElement(By.Id("load-cached-jpg")).Click(); + Browser.Equal("Cached JPG loaded", () => Browser.FindElement(By.Id("current-status")).Text); + var firstImg = Browser.FindElement(By.Id("cached-jpg")); + Browser.True(() => !string.IsNullOrEmpty(firstImg.GetAttribute("src"))); + var firstSrc = firstImg.GetAttribute("src"); + Assert.StartsWith("blob:", firstSrc, StringComparison.Ordinal); + + Browser.Navigate().Refresh(); + Navigate(ServerPathBase); + Browser.MountTestComponent(); + + // Re‑instrument after refresh so we see cache vs stream on the second load + ((IJavaScriptExecutor)Browser).ExecuteScript(@" + (function(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (!root) return; + window.__cacheHits = 0; + window.__streamCalls = 0; + if (!window.__origSetContentAsync){ + window.__origSetContentAsync = root.setContentAsync; + root.setContentAsync = async function(...a){ + const result = await window.__origSetContentAsync.apply(this, a); + if (result && result.fromCache) window.__cacheHits++; + if (result && result.success && !result.fromCache) window.__streamCalls++; + return result; + }; + } + })();"); + + // Second load should hit cache + Browser.FindElement(By.Id("load-cached-jpg")).Click(); + Browser.Equal("Cached JPG loaded", () => Browser.FindElement(By.Id("current-status")).Text); + var secondImg = Browser.FindElement(By.Id("cached-jpg")); + Browser.True(() => !string.IsNullOrEmpty(secondImg.GetAttribute("src"))); + var secondSrc = secondImg.GetAttribute("src"); + Assert.StartsWith("blob:", secondSrc, StringComparison.Ordinal); + + var hits = (long)((IJavaScriptExecutor)Browser).ExecuteScript("return window.__cacheHits || 0;"); + var streamCalls = (long)((IJavaScriptExecutor)Browser).ExecuteScript("return window.__streamCalls || 0;"); + + Assert.Equal(1, hits); + Assert.Equal(0, streamCalls); + Assert.NotEqual(firstSrc, secondSrc); + + // Restore + ((IJavaScriptExecutor)Browser).ExecuteScript(@" + (function(){ + const root = Blazor && Blazor._internal && Blazor._internal.BinaryMedia; + if (root && window.__origSetContentAsync){ root.setContentAsync = window.__origSetContentAsync; delete window.__origSetContentAsync; } + delete window.__cacheHits; + delete window.__streamCalls; + })();"); + } + + [Fact] + public void RapidSourceChanges_MaintainsConsistency() + { + // Initialize dynamic image + Browser.FindElement(By.Id("change-source")).Click(); + Browser.Equal("Dynamic source initialized with PNG", () => Browser.FindElement(By.Id("current-status")).Text); + + var imageElement = Browser.FindElement(By.Id("dynamic-source")); + Browser.True(() => !string.IsNullOrEmpty(imageElement.GetAttribute("src"))); + var initialSrc = imageElement.GetAttribute("src"); + + // Simulate user quickly clicking + for (int i = 0; i < 10; i++) + { + Browser.FindElement(By.Id("change-source")).Click(); + } + + Browser.True(() => + { + var status = Browser.FindElement(By.Id("current-status")).Text; + var src = imageElement.GetAttribute("src"); + var state = imageElement.GetAttribute("data-state"); + if (string.IsNullOrEmpty(src) || !src.StartsWith("blob:", StringComparison.Ordinal)) + { + return false; + } + + if (state == "loading" || state == "error") + { + return false; + } + + return status.Contains("Dynamic source changed to PNG") || status.Contains("Dynamic source changed to JPG"); + }); + + var finalSrc = imageElement.GetAttribute("src"); + Assert.False(string.IsNullOrEmpty(finalSrc)); + Assert.StartsWith("blob:", finalSrc, StringComparison.Ordinal); + + Assert.NotEqual(initialSrc, finalSrc); + } + + [Fact] + public void UrlRevoked_WhenImageRemovedFromDom() + { + // Load an image and capture its blob URL + Browser.FindElement(By.Id("load-png")).Click(); + Browser.Equal("PNG basic loaded", () => Browser.FindElement(By.Id("current-status")).Text); + var imageElement = Browser.FindElement(By.Id("png-basic")); + var blobUrl = imageElement.GetAttribute("src"); + Assert.False(string.IsNullOrEmpty(blobUrl)); + Assert.StartsWith("blob:", blobUrl, StringComparison.Ordinal); + + // MutationObserver should revoke the URL + ((IJavaScriptExecutor)Browser).ExecuteScript("document.getElementById('png-basic').remove();"); + + // Poll until fetch fails, indicating the URL has been revoked + Browser.True(() => + { + try + { + var ok = (bool)((IJavaScriptExecutor)Browser).ExecuteAsyncScript(@" + var callback = arguments[arguments.length - 1]; + var url = arguments[0]; + (async () => { + try { + await fetch(url); + callback(false); // still reachable + } catch { + callback(true); // revoked or unreachable + } + })(); + ", blobUrl); + return ok; + } + catch + { + return false; + } + }); + } + + [Fact] + public void InvalidMimeImage_SetsErrorState() + { + Browser.FindElement(By.Id("load-invalid-mime")).Click(); + Browser.Equal("Invalid mime image loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var img = Browser.FindElement(By.Id("invalid-mime-image")); + Assert.NotNull(img); + + Browser.Equal("error", () => img.GetAttribute("data-state")); + + var src = img.GetAttribute("src"); + Assert.True(string.IsNullOrEmpty(src) || src.StartsWith("blob:", StringComparison.Ordinal)); + } + + [Fact] + public void TemplatedImage_Loads_WithContextStates() + { + Browser.FindElement(By.Id("load-templated-image")).Click(); + Browser.Equal("Templated image loaded", () => Browser.FindElement(By.Id("current-status")).Text); + + var wrapper = Browser.FindElement(By.Id("templated-image-wrapper")); + Assert.NotNull(wrapper); + + var img = Browser.FindElement(By.Id("templated-image")); + Browser.True(() => + { + var src = img.GetAttribute("src"); + return !string.IsNullOrEmpty(src) && src.StartsWith("blob:", StringComparison.Ordinal); + }); + + var status = Browser.FindElement(By.Id("templated-image-status")).Text; + Assert.Equal("Loaded", status); + + var cls = wrapper.GetAttribute("class"); + Assert.Contains("templated-image", cls); + Assert.Contains("ready", cls); + Assert.DoesNotContain("loading", cls); + Assert.DoesNotContain("error", cls); + } +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/InputFileTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/InputFileTest.cs index bfeb682482fa..a83dcb56b217 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/InputFileTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/InputFileTest.cs @@ -196,6 +196,31 @@ public void ThrowsWhenOversizedFileIsSelected() Browser.Equal("Supplied file with size 32 bytes exceeds the maximum of 10 bytes.", () => exceptionMessage.Text); } + [Fact] + public void CanClearFilesByInvokingCancelEvent() + { + // Upload a file first + var file = TempFile.Create(_tempDirectory, "txt", "This is a test file."); + var inputFile = Browser.Exists(By.Id("input-file")); + inputFile.SendKeys(file.Path); + + // Verify the file was uploaded + var fileContainer = Browser.Exists(By.Id($"file-{file.Name}")); + + // Get the file count element + var fileCount = Browser.Exists(By.Id("file-count")); + Browser.Equal("1", () => fileCount.Text); + + // Trigger the cancel event via JavaScript to simulate canceling the file dialog + Browser.ExecuteJavaScript(@" + const inputElement = document.getElementById('input-file'); + inputElement.dispatchEvent(new Event('cancel')); + "); + + // Wait a moment for the event to be processed and verify the file list was cleared + Browser.Equal("0", () => Browser.Exists(By.Id("file-count")).Text); + } + public void Dispose() { Directory.Delete(_tempDirectory, recursive: true); diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/InteropTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/InteropTest.cs index 07d54f0fa3d8..9293fdda9b9c 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/InteropTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/InteropTest.cs @@ -78,6 +78,7 @@ public void CanInvokeInteropMethods() ["testDtoAsync"] = "Same", ["returnPrimitiveAsync"] = "123", ["returnArrayAsync"] = "first,second", + ["elementReference"] = "Success", ["jsObjectReference.identity"] = "Invoked from JSObjectReference", ["jsObjectReference.nested.add"] = "5", ["addViaJSObjectReference"] = "5", diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/MathMLTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/MathMLTest.cs new file mode 100644 index 000000000000..c0ff61610001 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/MathMLTest.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests; + +public class MathMLTest : ServerTestBase> +{ + public MathMLTest( + BrowserFixture browserFixture, + ToggleExecutionModeServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase); + } + + [Fact] + public void CanRenderMathMLWithCorrectNamespace() + { + var appElement = Browser.MountTestComponent(); + + var mathElement = appElement.FindElement(By.Id("mathml-with-callback")); + Assert.NotNull(mathElement); + + // Verify the math element has the correct MathML namespace + var mathMrowElement = mathElement.FindElement(By.XPath(".//*[local-name()='mrow' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mathMrowElement); + + // Verify child elements also have the correct namespace + var mathMnElement = mathElement.FindElement(By.XPath(".//*[local-name()='mn' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mathMnElement); + Assert.Equal("10", mathMnElement.Text); + + // Click button to update and verify the value changes while maintaining correct namespace + appElement.FindElement(By.Id("increment-btn")).Click(); + Browser.Equal("11", () => mathMnElement.Text); + } + + [Fact] + public void CanRenderStaticMathMLWithCorrectNamespace() + { + var appElement = Browser.MountTestComponent(); + + var mathElement = appElement.FindElement(By.Id("mathml-static")); + Assert.NotNull(mathElement); + + // Verify msup elements have the correct namespace + var msupElements = mathElement.FindElements(By.XPath(".//*[local-name()='msup' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.Equal(3, msupElements.Count); + } + + [Fact] + public void CanRenderMathMLChildComponentWithCorrectNamespace() + { + var appElement = Browser.MountTestComponent(); + + var mathElement = appElement.FindElement(By.Id("mathml-with-child-component")); + Assert.NotNull(mathElement); + + // The child component should render mrow with correct namespace + var mathMrowElement = mathElement.FindElement(By.XPath(".//*[local-name()='mrow' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mathMrowElement); + + // Verify mi element from child component + var mathMiElement = mathElement.FindElement(By.XPath(".//*[local-name()='mi' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mathMiElement); + Assert.Equal("z", mathMiElement.Text); + } + + [Fact] + public void CanRenderConditionalMathMLWithCorrectNamespace() + { + var appElement = Browser.MountTestComponent(); + + // Initially the conditional MathML should not be present + var conditionalMath = appElement.FindElements(By.Id("mathml-conditional")); + Assert.Empty(conditionalMath); + + // Click toggle to show the conditional MathML + appElement.FindElement(By.Id("toggle-btn")).Click(); + + // Now the MathML should be present with correct namespace + Browser.Exists(By.Id("mathml-conditional")); + var mathElement = appElement.FindElement(By.Id("mathml-conditional")); + + var mathMrowElement = mathElement.FindElement(By.XPath(".//*[local-name()='mrow' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mathMrowElement); + } + + [Fact] + public void CanRenderComplexMathMLWithCorrectNamespace() + { + var appElement = Browser.MountTestComponent(); + + var mathElement = appElement.FindElement(By.Id("mathml-complex")); + Assert.NotNull(mathElement); + + // Verify mfrac element has the correct namespace + var mfracElement = mathElement.FindElement(By.XPath(".//*[local-name()='mfrac' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(mfracElement); + + // Verify msqrt element has the correct namespace + var msqrtElement = mathElement.FindElement(By.XPath(".//*[local-name()='msqrt' and namespace-uri()='http://www.w3.org/1998/Math/MathML']")); + Assert.NotNull(msqrtElement); + } +} diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/QuickGridTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/QuickGridTest.cs index 63ea37319277..fcafb8c4ec1b 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/QuickGridTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/QuickGridTest.cs @@ -136,11 +136,11 @@ public void RowClassApplied() if (firstName == "Julie") { isJulieRowFound = true; - Assert.Equal("highlight", row.GetDomAttribute("class")); + Assert.Equal("row-clickable highlight", row.GetDomAttribute("class")); } else { - Assert.Null(row.GetDomAttribute("class")); + Assert.Equal("row-clickable", row.GetDomAttribute("class")); } } @@ -166,7 +166,7 @@ public void RowStyleApplied() const p = document.querySelector('tbody > tr:first-child > td:nth-child(5)'); return p ? getComputedStyle(p).textAlign : null;")); } - + [Fact] public void CanOpenColumnOptions() { @@ -208,4 +208,80 @@ public void CanCloseColumnOptionsByHideColumnOptionsAsync() var firstNameSearchSelector = "#grid > table > thead > tr > th:nth-child(2) input[type=search]"; Browser.DoesNotExist(By.CssSelector(firstNameSearchSelector)); } + + [Fact] + public void ItemsProviderCalledOnceWithVirtualize() + { + app = Browser.MountTestComponent(); + Browser.Equal("1", () => app.FindElement(By.Id("items-provider-call-count")).Text); + } + + [Fact] + public void FilterUsingSetCurrentPageDoesNotCauseExtraRefresh() + { + app = Browser.MountTestComponent(); + + Browser.Equal("1", () => app.FindElement(By.Id("items-provider-calls")).Text); + + var filterInput = app.FindElement(By.Id("filter-input")); + filterInput.Clear(); + filterInput.SendKeys("Item 1"); + app.FindElement(By.Id("apply-filter-reset-pagination-btn")).Click(); + + Browser.Equal("2", () => app.FindElement(By.Id("items-provider-calls")).Text); + } + + [Fact] + public void FilterUsingRefreshDataDoesNotCauseExtraRefresh() + { + app = Browser.MountTestComponent(); + + Browser.Equal("1", () => app.FindElement(By.Id("items-provider-calls")).Text); + + var filterInput = app.FindElement(By.Id("filter-input")); + filterInput.Clear(); + filterInput.SendKeys("Item 1"); + app.FindElement(By.Id("apply-filter-refresh-data-btn")).Click(); + + Browser.Equal("2", () => app.FindElement(By.Id("items-provider-calls")).Text); + } + + [Fact] + public void OnRowClickTriggersCallback() + { + var grid = app.FindElement(By.CssSelector("#grid > table")); + + // Verify no row has been clicked yet + Browser.Exists(By.Id("no-click")); + + // Click on the first row (Julie Smith) + var firstRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(1)")); + firstRow.Click(); + + // Verify the callback was triggered with correct data + Browser.Equal("PersonId: 11203", () => app.FindElement(By.Id("clicked-person-id")).Text); + Browser.Equal("Name: Julie Smith", () => app.FindElement(By.Id("clicked-person-name")).Text); + Browser.Equal("Click count: 1", () => app.FindElement(By.Id("click-count")).Text); + + // Click on another row (Jose Hernandez - 3rd row) + var thirdRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(3)")); + thirdRow.Click(); + + // Verify the callback was triggered with the new row's data + Browser.Equal("PersonId: 11898", () => app.FindElement(By.Id("clicked-person-id")).Text); + Browser.Equal("Name: Jose Hernandez", () => app.FindElement(By.Id("clicked-person-name")).Text); + Browser.Equal("Click count: 2", () => app.FindElement(By.Id("click-count")).Text); + } + + [Fact] + public void OnRowClickAppliesCursorPointerStyle() + { + var grid = app.FindElement(By.CssSelector("#grid > table")); + + // Verify the row has cursor: pointer style via the row-clickable class + var cursorStyle = Browser.ExecuteJavaScript(@" + const row = document.querySelector('#grid > table > tbody > tr:nth-child(1)'); + return row ? getComputedStyle(row).cursor : null;"); + Assert.Equal("pointer", cursorStyle); + } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/RoutingTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/RoutingTest.cs index 3457ca4cd4d1..0cdefdf53d89 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/RoutingTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/RoutingTest.cs @@ -994,6 +994,7 @@ public void NavigationLock_OverlappingNavigationsCancelExistingNavigations_Histo Browser.Navigate().Back(); // The navigation lock has initiated its "location changing" handler and is displaying navigation controls + Browser.Equal(expectedStartingAbsoluteUri, () => app.FindElement(By.Id("test-info")).Text); Browser.Exists(By.CssSelector("#navigation-lock-0 > div.blocking-controls")); // The location was reverted to what it was before the navigation started @@ -1155,8 +1156,7 @@ public void NavigationLock_HistoryNavigationWorks_AfterRefresh() Browser.Equal("1", () => app.FindElement(By.Id("location-changed-count"))?.Text); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/57153")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/57153")] public void NavigationLock_CanBlockExternalNavigation() { SetUrlViaPushState("/"); diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithCascadingParametersTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithCascadingParametersTest.cs index bd69e484a5f8..73a860124ecb 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithCascadingParametersTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithCascadingParametersTest.cs @@ -35,7 +35,7 @@ public void RenderSectionContent_CascadingParameterForSectionOutletIsDeterminedB Browser.FindElement(By.Id("render-section-outlet")).Click(); Browser.FindElement(By.Id("render-second-section-content")).Click(); - Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -46,7 +46,7 @@ public void ChangeCascadingValueForSectionContent_CascadingValueForSectionOutlet Browser.FindElement(By.Id("change-cascading-value")).Click(); - Browser.Equal("First Section with additional text for second section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("First Section with additional text for second section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -56,7 +56,7 @@ public void RenderTwoSectionContentsWithSameId_CascadingParameterForSectionOutle Browser.FindElement(By.Id("render-first-section-content")).Click(); Browser.FindElement(By.Id("render-section-outlet")).Click(); - Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -68,7 +68,7 @@ public void SecondSectionContentIdChanged_CascadingParameterForSectionOutletIsDe Browser.FindElement(By.Id("change-second-section-content-id")).Click(); - Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -80,7 +80,7 @@ public void SecondSectionContentDisposed_CascadingParameterForSectionOutletIsDet Browser.FindElement(By.Id("dispose-second-section-content")).Click(); - Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("First Section with additional text for first section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -92,7 +92,7 @@ public void FirstSectionContentDisposedThenRenderSecondSectionContent_CascadingP Browser.FindElement(By.Id("dispose-first-section-content")).Click(); Browser.FindElement(By.Id("render-second-section-content")).Click(); - Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.Id("text-component")).Text); } [Fact] @@ -105,6 +105,6 @@ public void SectionOutletIdChanged_CascadingParameterForSectionOutletIsDetermine Browser.FindElement(By.Id("change-section-outlet-id")).Click(); - Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Second Section with additional text for second section", () => Browser.Exists(By.Id("text-component")).Text); } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithErrorBoundaryTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithErrorBoundaryTest.cs index 3509030754af..25106e9ba4f2 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithErrorBoundaryTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/SectionsWithErrorBoundaryTest.cs @@ -37,7 +37,7 @@ public void RenderSectionContent_ErrorBoundaryForSectionOutletContentIsDetermine Browser.FindElement(By.Id("error-button")).Click(); - Browser.Equal("Sorry!", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Sorry!", () => Browser.Exists(By.Id("error-content")).Text); } [Fact] @@ -88,7 +88,7 @@ public void FirstSectionContentDisposedThenRenderSecondSectionContent_ErrorBound Browser.FindElement(By.Id("render-second-section-content")).Click(); Browser.FindElement(By.Id("error-button")).Click(); - Browser.Equal("Sorry!", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Sorry!", () => Browser.Exists(By.Id("error-content")).Text); } [Fact] @@ -102,6 +102,6 @@ public void SectionOutletIdChanged_ErrorBoundaryForSectionOutletIsDeterminedByMa Browser.FindElement(By.Id("change-section-outlet-id")).Click(); Browser.FindElement(By.Id("error-button")).Click(); - Browser.Equal("Sorry!", () => Browser.Exists(By.TagName("p")).Text); + Browser.Equal("Sorry!", () => Browser.Exists(By.Id("error-content")).Text); } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs index abdc012a2482..f5370b03a22b 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests; public class StandaloneAppTest - : ServerTestBase>, IDisposable + : ServerTestBase> { public StandaloneAppTest( BrowserFixture browserFixture, @@ -22,6 +22,12 @@ public StandaloneAppTest( protected override void InitializeAsyncCore() { + // The sidebar is hidden if the screen is too narrow. + // Without setting the window size explicitly, visibility-sensitive properties (e.g. IWebElement.Text) + // and element finders (e.g. By.LinkText) can behave unexpectedly, causing assertions to fail. + // In particular, this happens in the headless mode (used when running without debugger). + Browser.SetWindowSize(1920, 1080); + Navigate("/"); WaitUntilLoaded(); } @@ -126,10 +132,12 @@ private void WaitUntilLoaded() Browser.NotEqual("Loading...", () => app.Text); } - public void Dispose() + public override Task DisposeAsync() { // Make the tests run faster by navigating back to the home page when we are done // If we don't, then the next test will reload the whole page before it starts Browser.Exists(By.LinkText("Home")).Click(); + + return base.DisposeAsync(); } } diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs index a05fbfa05979..3f74c7bc177a 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/StatePersistenceTest.cs @@ -116,15 +116,22 @@ public void CanRenderComponentWithPersistedState(bool suppressEnhancedNavigation // In each case, we validate that the state is available until the initial set of components first render reaches quiescence. Similar to how it works for Server and WebAssembly. // For server we validate that the state is provided every time a circuit is initialized. [Theory] - [InlineData(typeof(InteractiveServerRenderMode), (string)null)] - [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming")] - [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null)] - [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming")] - [InlineData(typeof(InteractiveAutoRenderMode), (string)null)] - [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming")] + [InlineData(typeof(InteractiveServerRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming", "yes")] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming", "yes")] + [InlineData(typeof(InteractiveAutoRenderMode), (string)null, "yes")] + [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming", "yes")] + [InlineData(typeof(InteractiveServerRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveServerRenderMode), "ServerStreaming", null)] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveWebAssemblyRenderMode), "WebAssemblyStreaming", null)] + [InlineData(typeof(InteractiveAutoRenderMode), (string)null, null)] + [InlineData(typeof(InteractiveAutoRenderMode), "AutoStreaming", null)] public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( Type renderMode, - string streaming) + string streaming, + string key) { var mode = renderMode switch { @@ -136,7 +143,7 @@ public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( // Navigate to a page without components first to make sure that we exercise rendering components // with enhanced navigation on. - NavigateToInitialPage(streaming, mode); + NavigateToInitialPage(streaming, mode, key); if (mode == "auto") { BlockWebAssemblyResourceLoad(); @@ -156,22 +163,36 @@ public void CanUpdateComponentsWithPersistedStateAndEnhancedNavUpdates( UnblockWebAssemblyResourceLoad(); Browser.Navigate().Refresh(); - NavigateToInitialPage(streaming, mode); + NavigateToInitialPage(streaming, mode, key); Browser.Click(By.Id("call-blazor-start")); Browser.Click(By.Id("page-with-components-link-and-declarative-state")); RenderComponentsWithDeclarativePersistentStateAndValidate(mode, renderMode, streaming, interactiveRuntime: "wasm", stateValue: "other"); } - void NavigateToInitialPage(string streaming, string mode) + void NavigateToInitialPage(string streaming, string mode, string key) { - if (streaming == null) + if (key == null) { - Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart"); + if (streaming == null) + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&suppress-autostart"); + } + else + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart"); + } } else { - Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&streaming-id={streaming}&suppress-autostart"); + if (streaming == null) + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&key={key}&suppress-autostart"); + } + else + { + Navigate($"subdir/persistent-state/page-no-components?render-mode={mode}&key={key}&streaming-id={streaming}&suppress-autostart"); + } } } } @@ -255,6 +276,20 @@ public async Task StateIsProvidedEveryTimeACircuitGetsCreated(string streaming) RenderComponentsWithPersistentStateAndValidate(suppressEnhancedNavigation: false, mode, typeof(InteractiveServerRenderMode), streaming, stateValue: "other"); } + [Theory] + [InlineData("ServerNonPrerendered")] + [InlineData("WebAssemblyNonPrerendered")] + public void PersistentStateIsSupportedInDynamicJSRoots(string renderMode) + { + Navigate($"subdir/WasmMinimal/dynamic-js-root.html?renderMode={renderMode}"); + + Browser.Equal("Counter", () => Browser.Exists(By.TagName("h1")).Text); + Browser.Equal("Current count: 0", () => Browser.Exists(By.CssSelector("p[role='status']")).Text); + + Browser.Click(By.CssSelector("button.btn-primary")); + Browser.Equal("Current count: 1", () => Browser.Exists(By.CssSelector("p[role='status']")).Text); + } + private void BlockWebAssemblyResourceLoad() { // Clear local storage so that the resource hash is not found diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppHostedTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppHostedTest.cs index b9b6bd1db8a5..ff075400ada3 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppHostedTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppHostedTest.cs @@ -32,15 +32,13 @@ protected override void InitializeAsyncCore() WaitUntilLoaded(); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void HasHeading() { Assert.Equal("Hello, world!", Browser.Exists(By.TagName("h1")).Text); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void NavMenuHighlightsCurrentLocation() { var activeNavLinksSelector = By.CssSelector(".sidebar a.active"); @@ -66,8 +64,7 @@ public void NavMenuHighlightsCurrentLocation() item => Assert.Equal("Home", item.Text.Trim())); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void CounterPageCanUseThreads() { // Navigate to "Counter" @@ -84,8 +81,7 @@ public void CounterPageCanUseThreads() Browser.NotEqual("Current count: 0", () => Browser.Exists(By.CssSelector("h1 + p")).Text); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void HasFetchDataPage() { // Navigate to "Fetch data" diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppTest.cs index cd430537e177..98953991d8b2 100644 --- a/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppTest.cs +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/ThreadingAppTest.cs @@ -28,22 +28,19 @@ protected override void InitializeAsyncCore() WaitUntilLoaded(); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void HasTitle() { Assert.Equal("Blazor standalone", Browser.Title); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void HasHeading() { Assert.Equal("Hello, world!", Browser.Exists(By.TagName("h1")).Text); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54497")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void NavMenuHighlightsCurrentLocation() { var activeNavLinksSelector = By.CssSelector(".sidebar a.active"); @@ -69,8 +66,7 @@ public void NavMenuHighlightsCurrentLocation() item => Assert.Equal("Home", item.Text.Trim())); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/58242")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void CounterPageCanUseThreads() { // Navigate to "Counter" @@ -87,8 +83,7 @@ public void CounterPageCanUseThreads() Browser.NotEqual("Current count: 0", () => Browser.Exists(By.CssSelector("h1 + p")).Text); } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void HasFetchDataPage() { // Navigate to "Fetch data" @@ -109,8 +104,7 @@ public void HasFetchDataPage() } } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/54761")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/63524")] public void IsStarted() { // Read from property diff --git a/src/aspnetcore/src/Components/test/E2ETest/Tests/VideoTest.cs b/src/aspnetcore/src/Components/test/E2ETest/Tests/VideoTest.cs new file mode 100644 index 000000000000..37f416a14791 --- /dev/null +++ b/src/aspnetcore/src/Components/test/E2ETest/Tests/VideoTest.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: Most shared media component behaviors (caching, error handling and URL revocation) +// are validated in ImageTest. To avoid duplication, this suite intentionally contains only +// tests that exercise