diff --git a/.vsts-ci.yml b/.vsts-ci.yml
index 3df0da1ce832..775bc568d823 100644
--- a/.vsts-ci.yml
+++ b/.vsts-ci.yml
@@ -3,7 +3,7 @@ trigger:
branches:
include:
- main
- - release/8.0.1xx
+ - release/8.0.2xx
- internal/release/*
- exp/*
diff --git a/Directory.Build.props b/Directory.Build.props
index fc5cbc439c2f..ae3882f1df94 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -16,7 +16,7 @@
- net7.0
+ net8.0
$(SdkTargetFramework)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2dcf8979312d..5d37eb795d6b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -41,6 +41,7 @@
+
@@ -54,8 +55,8 @@
-
+
@@ -103,7 +104,7 @@
-
+
diff --git a/NuGet.config b/NuGet.config
index 5bd5b884357a..4632f1861eb3 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,30 +4,28 @@
-
-
-
-
+
-
-
-
-
+
+
+
+
-
+
+
-
+
@@ -39,6 +37,7 @@
+
@@ -52,13 +51,18 @@
-
+
+
+
+
+
+
diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml
index 123bd07493ff..3dcc5ba862f5 100644
--- a/eng/SourceBuildPrebuiltBaseline.xml
+++ b/eng/SourceBuildPrebuiltBaseline.xml
@@ -32,5 +32,9 @@
+
+
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 6669f98b9fe9..b1d4cb9ee3c4 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,204 +1,200 @@
-
+
https://github.com/dotnet/templating
- ae1a4c208a624e5cb5e0b7c5df1bd066afc48b81
-
-
- https://github.com/dotnet/templating
- ae1a4c208a624e5cb5e0b7c5df1bd066afc48b81
+ 563930b1d9ad60c8d7dbd1975390f5870601defa
+
-
+
https://github.com/dotnet/templating
- ae1a4c208a624e5cb5e0b7c5df1bd066afc48b81
-
+ 563930b1d9ad60c8d7dbd1975390f5870601defa
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
-
+
https://github.com/dotnet/emsdk
- 201f4dae9d1a1e105d8ba86d7ece61eed1f665e0
+ 2fc2ffd960930318f33fcaa690cbdbc55d72f52d
-
+
https://github.com/dotnet/msbuild
- b5265ef370a651f8c3458110b804e5cbf869eeb5
+ 90725d08d9825ad5897029b47f600345c29125b7
-
+
https://github.com/dotnet/msbuild
- b5265ef370a651f8c3458110b804e5cbf869eeb5
+ 90725d08d9825ad5897029b47f600345c29125b7
-
+
https://github.com/dotnet/msbuild
- b5265ef370a651f8c3458110b804e5cbf869eeb5
+ 90725d08d9825ad5897029b47f600345c29125b7
-
+
https://github.com/dotnet/fsharp
- 424e4b7cffb7656efd63f7a905a2498e39011104
+ a7979111f86ab7332897ea617635bf3435c39bc3
-
+
https://github.com/dotnet/fsharp
- 424e4b7cffb7656efd63f7a905a2498e39011104
+ a7979111f86ab7332897ea617635bf3435c39bc3
-
- https://github.com/dotnet/format
- 28925c0e519d66c80328aacf973b74e40bb1d5bd
+
+ https://dev.azure.com/dnceng/internal/_git/dotnet-format
+ 2651752953c0d41c8c7b8d661cf2237151af33d0
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://github.com/dotnet/roslyn
- 82a29fc43b3e0a9c53d6cc2be950e43765682170
+ 4fc721bbc2c0eac5931f588e1d14ab2a1f936646
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
- https://github.com/nuget/nuget.client
- 0dd5a1ea536201af94725353e4bc711d7560b246
+
+ https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
+ 623fde83a3cd73cb479ec7fa03866c6116894dbf
-
+
https://github.com/microsoft/vstest
- aa59400b11e1aeee2e8af48928dbd48748a8bef9
+ 053d7114a72aac12d1382ecc2a23b2dfdd5b084b
-
+
https://github.com/microsoft/vstest
- aa59400b11e1aeee2e8af48928dbd48748a8bef9
+ 053d7114a72aac12d1382ecc2a23b2dfdd5b084b
-
+
https://github.com/microsoft/vstest
- aa59400b11e1aeee2e8af48928dbd48748a8bef9
+ 053d7114a72aac12d1382ecc2a23b2dfdd5b084b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -216,116 +212,116 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- a0e7b5d8673f28c41bcac6e2001b39ba2c8fab54
+ 593444ad8328a5a933c006c6564469666f45ad2e
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- a0e7b5d8673f28c41bcac6e2001b39ba2c8fab54
+ 593444ad8328a5a933c006c6564469666f45ad2e
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- a0e7b5d8673f28c41bcac6e2001b39ba2c8fab54
+ 593444ad8328a5a933c006c6564469666f45ad2e
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- a0e7b5d8673f28c41bcac6e2001b39ba2c8fab54
+ 593444ad8328a5a933c006c6564469666f45ad2e
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-wpf
- ac40bed7a33baf164d3984ca90c2aedba996a7b2
+ 472140dd926227876848e48f41cfc9acb9275492
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://github.com/dotnet/razor
- d135dd8d2ec1c2fbdee220e8656b308694e17a4b
+ df383360c34ada8889fdf18dc36d245f2938db66
-
+
https://github.com/dotnet/razor
- d135dd8d2ec1c2fbdee220e8656b308694e17a4b
+ df383360c34ada8889fdf18dc36d245f2938db66
-
+
https://github.com/dotnet/razor
- d135dd8d2ec1c2fbdee220e8656b308694e17a4b
+ df383360c34ada8889fdf18dc36d245f2938db66
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
https://github.com/dotnet/xdt
9a1c3e1b7f0c8763d4c96e593961a61a72679a7b
-
+
https://github.com/dotnet/roslyn-analyzers
- b4d9a1334d5189172977ba8fddd00bda70161e4a
+ abef8ced132657943b7150f01a308e2199a17d5d
-
+
https://github.com/dotnet/roslyn-analyzers
- b4d9a1334d5189172977ba8fddd00bda70161e4a
+ abef8ced132657943b7150f01a308e2199a17d5d
-
+
https://github.com/dotnet/roslyn-analyzers
- b4d9a1334d5189172977ba8fddd00bda70161e4a
+ abef8ced132657943b7150f01a308e2199a17d5d
@@ -337,9 +333,9 @@
02fe27cd6a9b001c8feb7938e6ef4b3799745759
-
+
https://github.com/dotnet/source-build-externals
- 7134e53b6b1210a1ce8838b12b8f6071e0a3433b
+ 83274d94c7e2ff21081b0d75ecbec2da2241f831
@@ -413,9 +409,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- bf5e279d9239bfef5bb1b8d6212f1b971c434606
+ 1381d5ebd2ab1f292848d5b19b80cf71ac332508
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -425,9 +421,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 8e941eb42f819adb116b881195158b3887a70a1c
+ da7e9894ce22ef8cc02e5acc56e95a6f8cf8f644
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -457,9 +453,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-winforms
- e4ede9b8979b9d2b1b1d4383f30a791414f0625b
+ 0b4028eb507aeb222f5bd1fc421876cc5e5e3fb8
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
diff --git a/eng/Versions.props b/eng/Versions.props
index 83158ff93351..eb210470a04d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -11,13 +11,13 @@
- 8.0.102
- 8.0.100
+ 8.0.200
+ 8.0.200
true
release
- rtm
+ preview
rtm
servicing
@@ -48,21 +48,22 @@
- 8.0.1
- 8.0.1-servicing.23580.1
- 8.0.1
+ 8.0.2
+ 8.0.2-servicing.24067.11
+ 8.0.2
$(MicrosoftNETCoreAppRuntimewinx64PackageVersion)
8.0.0
- 8.0.1
- 8.0.1-servicing.23580.1
+ 8.0.2
+ 8.0.2-servicing.24067.11
8.0.0
+ $(MicrosoftExtensionsDependencyModelPackageVersion)
8.0.0
8.0.0
8.0.0
- 8.0.1
+ 8.0.2
8.0.0
8.0.0
- 8.0.1
+ 8.0.2
8.0.0
8.0.0
8.0.0
@@ -70,36 +71,36 @@
8.0.0
8.0.0
8.0.0
- 8.0.0
+ 8.0.1
8.0.0
8.0.0
8.0.0
8.0.0
- 8.0.1
+ 8.0.2
8.0.0
- 6.8.0-rc.122
- 6.8.0-rc.122
+ 6.9.1-rc.3
+ 6.9.1-rc.3
6.0.0-rc.278
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
- 6.8.0-rc.122
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
+ 6.9.1-rc.3
$(NuGetPackagingPackageVersion)
$(NuGetProjectModelPackageVersion)
- 17.8.0-release-23615-02
- 17.8.0-release-23615-02
- 17.8.0-release-23615-02
+ 17.9.0-release-23627-01
+ 17.9.0-release-23627-01
+ 17.9.0-release-23627-01
@@ -109,16 +110,16 @@
- 8.0.456602
+ 8.0.453106
- 8.0.0-preview.23525.2
- 3.11.0-beta1.23525.2
+ 8.0.0-preview.23614.1
+ 3.11.0-beta1.23614.1
- 17.8.5
+ 17.9.4
$(MicrosoftBuildPackageVersion)
- 8.0.102
+ 8.0.200-preview.24060.12
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
- 8.0.102-servicing.24064.3
+ 8.0.200-preview.24060.12
$(MicrosoftTemplateEngineMocksPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineMocksPackageVersion)
- 12.8.0-beta.23563.2
+ 12.8.200-beta.24062.3
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
- 4.8.0-7.23614.11
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
+ 4.9.0-3.24067.18
$(MicrosoftNetCompilersToolsetPackageVersion)
- 8.0.1
- 8.0.1-servicing.23580.8
- 8.0.1-servicing.23580.8
- 8.0.1-servicing.23580.8
- 8.0.1-servicing.23580.8
- 8.0.1-servicing.23580.8
- 8.0.1
+ 8.0.2
+ 8.0.2-servicing.24068.4
+ 8.0.2-servicing.24068.4
+ 8.0.2-servicing.24068.4
+ 8.0.2-servicing.24068.4
+ 8.0.2-servicing.24068.4
+ 8.0.2
- 7.0.0-preview.24052.1
- 7.0.0-preview.24052.1
- 7.0.0-preview.24052.1
+ 7.0.0-preview.24053.4
+ 7.0.0-preview.24053.4
+ 7.0.0-preview.24053.4
- 8.0.1-servicing.23580.5
+ 8.0.2-servicing.24068.6
@@ -222,7 +224,7 @@
- 8.0.1
+ 8.0.2
$(MicrosoftNETWorkloadEmscriptenCurrentManifest80100PackageVersion)
8.0.100$([System.Text.RegularExpressions.Regex]::Match($(EmscriptenWorkloadManifestVersion), `-rtm|-[A-z]*\.*\d*`))
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompat.Task/Microsoft.DotNet.ApiCompat.Task.csproj b/src/ApiCompat/Microsoft.DotNet.ApiCompat.Task/Microsoft.DotNet.ApiCompat.Task.csproj
index a7a964dbc99f..6e6311533f68 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompat.Task/Microsoft.DotNet.ApiCompat.Task.csproj
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompat.Task/Microsoft.DotNet.ApiCompat.Task.csproj
@@ -43,6 +43,7 @@
+
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Microsoft.DotNet.ApiCompatibility.csproj b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Microsoft.DotNet.ApiCompatibility.csproj
index 60dfe5a399f3..6f12ef4f6bd7 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Microsoft.DotNet.ApiCompatibility.csproj
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Microsoft.DotNet.ApiCompatibility.csproj
@@ -15,6 +15,8 @@
+
+
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index 1a03973fcd4f..747b43b278a6 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -26,9 +26,12 @@
+
+
+
- $(PkgMicrosoft_Build_Runtime)\contentFiles\any\net7.0\MSBuild.dll
+ $(PkgMicrosoft_Build_Runtime)\contentFiles\any\net8.0\MSBuild.dll
$(PkgMicrosoft_Build_Runtime)\contentFiles\any\$(NetCurrent)\MSBuild.dll
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.cs.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.cs.xlf
index 0a19b54165ac..059c3cc90ea8 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.cs.xlf
@@ -12,6 +12,11 @@
NesprávnÄ› naformátovaný text pÅ™Ãkazu {0}
+
+ Could not execute because the specified command or file was not found.
+ Nebylo možné provést, protože zadaný pÅ™Ãkaz nebo soubor nebyl nalezen.
+
+
Unable to locate dotnet multiplexer
NepodaÅ™ilo se najÃt multiplexor dotnetu.
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- SpuÅ¡tÄ›nà nebylo úspěšné, protože zadaný pÅ™Ãkaz nebo soubor se nenaÅ¡ly.
-Mezi možné pÅ™ÃÄiny patřà toto:
- * V integrovaném pÅ™Ãkazu dotnet je pÅ™eklep.
+ Možné důvody:
+ * ChybnÄ› jste napsali integrovaný pÅ™Ãkaz dotnet.
* Chtěli jste spustit program .NET, ale {0} neexistuje.
- * ChtÄ›li jste spustit globálnà nástroj, ale v promÄ›nné PATH se nepovedlo najÃt spustitelný soubor s pÅ™edponou dotnet s tÃmto názvem.
+ * ChtÄ›li jste spustit nástroj global, ale v cestÄ› PATH nebyl nalezen spustitelný soubor s pÅ™edponou dotnet s tÃmto názvem.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.de.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.de.xlf
index 931f8681cf5a..b65426871939 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.de.xlf
@@ -12,6 +12,11 @@
Fehlerhafter Befehlstext "{0}".
+
+ Could not execute because the specified command or file was not found.
+ Die Ausführung war nicht möglich, da der angegebene Befehl oder die angegebene Datei nicht gefunden wurde.
+
+
Unable to locate dotnet multiplexer
Dotnetmultiplexer nicht gefunden
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Die Ausführung war nicht möglich, weil der angegebene Befehl oder die Datei nicht gefunden wurde.
-Mögliche Ursachen:
- * Sie haben sich bei einem integrierten dotnet-Befehl verschrieben.
- * Sie wollten ein .NET-Programm ausführen, aber "{0}" ist nicht vorhanden.
- * Sie wollten ein globales Tool ausführen, aber in PATH wurde keine ausführbare Datei dieses Namens mit dotnet-Präfix gefunden.
+ Mögliche Gründe hierfür sind:
+ * Sie haben einen integrierten dotnet-Befehl falsch geschrieben.
+ * Sie wollten ein .NET-Programm ausführen, aber {0} ist nicht vorhanden.
+ * Sie wollten ein globales Tool ausführen, aber eine ausführbare Datei mit dotnet-Präfix und diesem Namen wurde in PATH nicht gefunden.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.es.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.es.xlf
index 6c95a5a48054..ab310ba0919e 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.es.xlf
@@ -12,6 +12,11 @@
Texto de comando con formato incorrecto "{0}"
+
+ Could not execute because the specified command or file was not found.
+ No se pudo ejecutar porque no se encontró el comando o archivo especificado.
+
+
Unable to locate dotnet multiplexer
No se puede ubicar el multiplexor dotnet
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- No se pudo ejecutar porque no se encontró el comando o el archivo especificado.
-Algunas de las posibles causas son :
- * Escribió mal un comando dotnet integrado.
- * PretendÃa ejecutar un programa .NET, pero {0} no existe.
- * PretendÃa ejecutar una herramienta global, pero no se encontró ningún ejecutable con prefijo dotnet con este nombre en PATH.
+ Entre las posibles razones para esto se incluyen:
+ * Escribió de manera incorrecta un comando dotnet integrado.
+ * TenÃa previsto ejecutar un programa .NET, pero {0} no existe.
+ * Tuvo la intención de ejecutar una herramienta global, pero no se encontró un ejecutable con el prefijo dotnet con este nombre en la ruta.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.fr.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.fr.xlf
index ac52d261b855..83aafec69438 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.fr.xlf
@@ -12,6 +12,11 @@
Texte de commande incorrect '{0}'
+
+ Could not execute because the specified command or file was not found.
+ Exécution impossible, car la commande ou le fichier spécifié est introuvable.
+
+
Unable to locate dotnet multiplexer
Le multiplexeur dotnet est introuvable
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Impossible d'effectuer l'exécution, car la commande ou le fichier spécifié est introuvable.
-Raisons possibles :
+ Les raisons possibles sont les suivantes :
* Vous avez mal orthographié une commande dotnet intégrée.
- * Vous avez voulu exécuter un programme .NET, mais {0} n'existe pas.
- * Vous avez voulu exécuter un outil global, mais l'exécutable de ce nom avec le préfixe dotnet est introuvable dans le CHEMIN.
+ * Vous avez l’intention d’exécuter un programme .NET, mais {0} n’existe pas.
+ * Vous avez l’intention d’exécuter un outil global, mais un exécutable avec un préfixe dotnet portant ce nom est introuvable sur le chemin d’accès.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.it.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.it.xlf
index a6c286a1e24c..d14e1840c5e8 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.it.xlf
@@ -12,6 +12,11 @@
Il testo del comando '{0}' non è corretto
+
+ Could not execute because the specified command or file was not found.
+ Non è stato possibile eseguire perché il comando o il file specificato non è stato trovato.
+
+
Unable to locate dotnet multiplexer
Il multiplexer dotnet non è stato trovato
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Non è stato possibile completare l'esecuzione perché il comando o il file specificato non è stato trovato.
-Motivi possibili:
- * Il nome di un comando dotnet predefinito non è stato digitato correttamente.
+ I possibili motivi includono:
+ * È stato digitato in modo errato un comando dotnet predefinito.
* Si intendeva eseguire un programma .NET, ma {0} non esiste.
- * Si intendeva eseguire uno strumento globale, ma in PATH non è stato trovato alcun eseguibile con questo nome e prefisso dotnet.
+ * Si intendeva eseguire uno strumento globale, ma non è stato possibile trovare un eseguibile con prefisso dotnet con questo nome in PATH.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ja.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ja.xlf
index db28e5cadd21..ac1e63ccf812 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ja.xlf
@@ -12,6 +12,11 @@
無効ãªå½¢å¼ã®ã‚³ãƒžãƒ³ãƒ‰ テã‚スト '{0}'
+
+ Could not execute because the specified command or file was not found.
+ 指定ã•れãŸã‚³ãƒžãƒ³ãƒ‰ã¾ãŸã¯ãƒ•ァイルãŒè¦‹ã¤ã‹ã‚‰ãªã‹ã£ãŸãŸã‚ã€å®Ÿè¡Œã§ãã¾ã›ã‚“ã§ã—ãŸã€‚
+
+
Unable to locate dotnet multiplexer
dotnet マルãƒãƒ—レクサーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- 指定ã•れãŸã‚³ãƒžãƒ³ãƒ‰ã¾ãŸã¯ãƒ•ァイルãŒè¦‹ã¤ã‹ã‚‰ãªã‹ã£ãŸãŸã‚ã€å®Ÿè¡Œã§ãã¾ã›ã‚“ã§ã—ãŸã€‚
-次ã®ã‚ˆã†ãªåŽŸå› ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚
- * 組ã¿è¾¼ã¿ã® dotnet コマンドã®ã‚¹ãƒšãƒ«ãŒé–“é•ã£ã¦ã„る。
- * .NET プãƒã‚°ãƒ©ãƒ を実行ã—よã†ã¨ã—ãŸãŒã€{0} ãŒå˜åœ¨ã—ãªã„。
- * ã‚°ãƒãƒ¼ãƒãƒ« ツールを実行ã—よã†ã¨ã—ãŸãŒã€ãƒ—レフィックスã¨ã—㦠dotnet ãŒä»˜ã„ãŸã“ã®åå‰ã®å®Ÿè¡Œå¯èƒ½ãªã‚‚ã®ãŒ PATH ã«è¦‹ã¤ã‹ã‚‰ãªã‹ã£ãŸã€‚
+ ã“れã«ã¯ã€æ¬¡ã®ã‚ˆã†ãªç†ç”±ãŒè€ƒãˆã‚‰ã‚Œã¾ã™:
+ * 組ã¿è¾¼ã¿ã® dotnet コマンドã®ã‚¹ãƒšãƒ«ãŒé–“é•ã£ã¦ã„ã¾ã™ã€‚
+ * .NET プãƒã‚°ãƒ©ãƒ を実行ã—よã†ã¨ã—ã¾ã—ãŸãŒã€{0} ãŒå˜åœ¨ã—ã¾ã›ã‚“。
+ * ã‚°ãƒãƒ¼ãƒãƒ« ツールを実行ã—よã†ã¨ã—ã¾ã—ãŸãŒã€ã“ã®åå‰ã® dotnet プレフィックス付ã実行å¯èƒ½ãƒ•ァイル㌠PATH ã«è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ko.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ko.xlf
index dbe9f93adc8f..4f2d0561d088 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ko.xlf
@@ -12,6 +12,11 @@
형ì‹ì´ ìž˜ëª»ëœ ëª…ë ¹ í…스트 '{0}'
+
+ Could not execute because the specified command or file was not found.
+ ì§€ì •í•œ ëª…ë ¹ ë˜ëŠ” 파ì¼ì„ ì°¾ì„ ìˆ˜ 없어 실행하지 못했습니다.
+
+
Unable to locate dotnet multiplexer
dotnet multiplexer를 ì°¾ì„ ìˆ˜ ì—†ìŒ
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- ì§€ì •ëœ ëª…ë ¹ ë˜ëŠ” 파ì¼ì„ ì°¾ì„ ìˆ˜ 없으므로 ì‹¤í–‰í• ìˆ˜ 없습니다.
-가능한 ì›ì¸ì€ 다ìŒê³¼ 같습니다.
- * 기본 ì œê³µ dotnet ëª…ë ¹ ì² ìžê°€ 잘못 ìž…ë ¥ë˜ì—ˆìŠµë‹ˆë‹¤.
- * .NET í”„ë¡œê·¸ëž¨ì„ ì‹¤í–‰í•˜ë ¤ê³ í–ˆì§€ë§Œ, {0}ì´(ê°€) 없습니다.
- * ì „ì— ë„구를 ì‹¤í–‰í•˜ë ¤ê³ í–ˆì§€ë§Œ, ì´ ì´ë¦„ì˜ dotnet ì ‘ë‘사가 있는 실행 파ì¼ì„ PATHì—서 ì°¾ì„ ìˆ˜ 없습니다.
+ ì´ì— 대한 ì˜ˆìƒ ì›ì¸ì€ 다ìŒê³¼ 같습니다.
+ * 기본 ì œê³µ dotnet ëª…ë ¹ì˜ ì² ìžê°€ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤.
+ * .NET í”„ë¡œê·¸ëž¨ì„ ì‹¤í–‰í•˜ë ¤ê³ í–ˆì§€ë§Œ {0}ì´(ê°€) 없습니다.
+ * ì „ì— ë„구를 ì‹¤í–‰í•˜ë ¤ê³ í–ˆì§€ë§Œ PATHì—서 ì´ ì´ë¦„ì˜ dotnet ì ‘ë‘사 실행 파ì¼ì„ 찾지 못했습니다.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pl.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pl.xlf
index bb872cff6b03..f6b356e4a6eb 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pl.xlf
@@ -12,6 +12,11 @@
NieprawidÅ‚owo sformuÅ‚owany tekst polecenia „{0}â€
+
+ Could not execute because the specified command or file was not found.
+ Nie można wykonać, ponieważ nie znaleziono określonego polecenia lub pliku.
+
+
Unable to locate dotnet multiplexer
Nie można zlokalizować multipleksera dotnet
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Nie można wykonać, ponieważ nie odnaleziono określonego polecenia lub pliku.
-Możliwe przyczyny:
- * Błąd pisowni wbudowanego polecenia dotnet .
- * Planowano wykonanie programu platformy .NET, ale element {0} nie istnieje.
- * Planowano uruchomienie narzędzia globalnego, ale nie można odnaleźć pliku wykonywalnego z prefiksem dotnet o tej nazwie w zmiennej PATH.
+ Możliwe przyczyny tego są następujące:
+ * Błędnie napisano wbudowane polecenie dotnet.
+ * Zamierzano wykonać program .NET, ale {0} nie istnieje.
+ * Zamierzano uruchomić narzędzie globalne, ale nie można odnaleźć pliku wykonywalnego z prefiksem dotnet o tej nazwie w ścieżce PATH.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pt-BR.xlf
index c3a32a20687d..b29d04bbe723 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.pt-BR.xlf
@@ -12,6 +12,11 @@
Texto do comando malformado '{0}'
+
+ Could not execute because the specified command or file was not found.
+ Não foi possÃvel executar porque o comando ou arquivo especificado não foi encontrado.
+
+
Unable to locate dotnet multiplexer
Não é possÃvel localizar o multiplexador do dotnet
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Não foi possÃvel executar porque o comando ou o arquivo especificado não foi encontrado.
-PossÃveis motivos para isso incluem:
- * Você digitou incorretamente um comando de dotnet interno.
+ Os possÃveis motivos para isso incluem:
+ * Você digitou incorretamente um comando dotnet interno.
* Você pretendia executar um programa .NET, mas {0} não existe.
- * Você pretendia executar uma ferramenta global, mas não foi possÃvel encontrar um executável com prefixo de dotnet com esse nome no CAMINHO.
+ * Você pretendia executar uma ferramenta global, mas não foi possÃvel encontrar um executável com prefixo dotnet com esse nome no PATH.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ru.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ru.xlf
index 84b4742064c3..0de8ace38958 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.ru.xlf
@@ -12,6 +12,11 @@
Ðеправильный формат текÑта команды "{0}"
+
+ Could not execute because the specified command or file was not found.
+ Ðе удалоÑÑŒ выполнить, поÑкольку ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° или файл не найдены.
+
+
Unable to locate dotnet multiplexer
Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ мультиплекÑор dotnet.
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Ðе удалоÑÑŒ выполнить, так как не найдены ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° или указанный файл.
-Возможные причины:
- * вы неправильно набрали вÑтроенную команду dotnet;
- * вы планировали выполнить программу .NET, однако {0} не ÑущеÑтвует;
- * вы хотели запуÑтить глобальное ÑредÑтво, но по указанному в PATH пути не удалоÑÑŒ найти иÑполнÑемый файл Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом dotnet, имеющий такое имÑ.
+ Возможные причины Ñтого включают:
+ * Ð’Ñ‹ допуÑтили ошибку во вÑтроенной команде dotnet.
+ * Ð’Ñ‹ ÑобиралиÑÑŒ выполнить программу .NET, но {0} не ÑущеÑтвует.
+ * Ð’Ñ‹ ÑобиралиÑÑŒ запуÑтить глобальное ÑредÑтво, но в PATH не удалоÑÑŒ найти иÑполнÑемый файл Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑом dotnet и таким именем.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.tr.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.tr.xlf
index 546b74e21b8f..effa36b8a0ee 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.tr.xlf
@@ -12,6 +12,11 @@
Hatalı biçimlendirilmiş komut metni: '{0}'
+
+ Could not execute because the specified command or file was not found.
+ Belirtilen komut veya dosya bulunamadığından yürütülemedi.
+
+
Unable to locate dotnet multiplexer
Dotnet çoğullayıcısı bulunamadı
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- Belirtilen komut veya dosya bulunamadığından yürütülemedi.
-Bunun nedeni ÅŸunlardan biri olabilir:
+ Bunun olası nedenleri şunlar olabilir:
* Yerleşik bir dotnet komutunu yanlış yazdınız.
- * Bir .NET programını yürütmeyi amaçladınız, ancak {0} yok.
- * Genel bir aracı çalıştırmayı amaçladınız, ancak bu ada sahip dotnet ön ekli yürütülebilir dosya PATH üzerinde bulunamadı.
+ * Bir .NET programını yürütmeyi amaçladınız ancak {0} mevcut değil.
+ * Bir genel aracı çalıştırmayı amaçladınız ancak YOL dizininde bu ada sahip dotnet ön eki olan bir yürütülebilir dosya bulunamadı.
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hans.xlf
index 5d2ec87f2da8..92d85374f55b 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hans.xlf
@@ -12,6 +12,11 @@
命令文本“{0}â€æ ¼å¼é”™è¯¯
+
+ Could not execute because the specified command or file was not found.
+ æ— æ³•æ‰§è¡Œï¼Œå› ä¸ºæ‰¾ä¸åˆ°æŒ‡å®šçš„命令或文件。
+
+
Unable to locate dotnet multiplexer
找ä¸åˆ° dotnet 多路å¤ç”¨å™¨
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- æ— æ³•æ‰§è¡Œï¼Œå› ä¸ºæ‰¾ä¸åˆ°æŒ‡å®šçš„命令或文件。
-å¯èƒ½çš„åŽŸå› åŒ…æ‹¬:
- *内置的 dotnet 命令拼写错误。
- *ä½ æ‰“ç®—æ‰§è¡Œ .NET 程åºï¼Œä½† {0} ä¸å˜åœ¨ã€‚
- *ä½ æ‰“ç®—è¿è¡Œå…¨å±€å·¥å…·ï¼Œä½†åœ¨è·¯å¾„上找ä¸åˆ°å…·æœ‰æ¤å称且å‰ç¼€ä¸º dotnet çš„å¯æ‰§è¡Œæ–‡ä»¶ã€‚
+ å¯èƒ½é€ æˆæ¤é—®é¢˜çš„åŽŸå› åŒ…æ‹¬:
+ *内置 dotnet 命令拼写错误。
+ *ä½ æ‰“ç®—æ‰§è¡Œ .NET 程åºï¼Œä½† {0} ä¸å˜åœ¨ã€‚
+ *ä½ æ‰“ç®—è¿è¡Œå…¨å±€å·¥å…·ï¼Œä½†åœ¨ PATH 上找ä¸åˆ°å…·æœ‰æ¤å称且带有 dotnet å‰ç¼€çš„坿‰§è¡Œæ–‡ä»¶ã€‚
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hant.xlf
index 829e3039ab97..2a19383fc1ce 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/xlf/LocalizableStrings.zh-Hant.xlf
@@ -12,6 +12,11 @@
å‘½ä»¤æ–‡å— '{0}' æ ¼å¼éŒ¯èª¤
+
+ Could not execute because the specified command or file was not found.
+ ç„¡æ³•åŸ·è¡Œï¼Œå› ç‚ºæ‰¾ä¸åˆ°æŒ‡å®šçš„命令或檔案。
+
+
Unable to locate dotnet multiplexer
找ä¸åˆ° dotnet multiplexer
@@ -28,16 +33,14 @@
- Could not execute because the specified command or file was not found.
-Possible reasons for this include:
+ Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET program, but {0} does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
- å› ç‚ºæ‰¾ä¸åˆ°æŒ‡å®šçš„命令或檔案,所以無法執行。
-å¯èƒ½çš„åŽŸå› åŒ…æ‹¬:
- * 內建 dotnet 命令拼寫錯誤。
- * 您é 計è¦åŸ·è¡Œ .NET 程å¼ï¼Œä½†ä¸å˜åœ¨ {0}。
- * 您é 計è¦åŸ·è¡Œå…¨åŸŸå·¥å…·ï¼Œä½†åœ¨ PATH 上找ä¸åˆ°æ¤å稱且開é 為 dotnet çš„å¯åŸ·è¡Œæª”。
+ å¯èƒ½çš„åŽŸå› åŒ…æ‹¬:
+ * 您拼錯了內建 dotnet 命令。
+ * 您打算執行 .NET 程å¼ï¼Œä½† {0} ä¸å˜åœ¨ã€‚
+ * 您打算執行全域工具,但在 PATH 上找ä¸åˆ°å…·æœ‰æ¤å稱的以 dotnet 為首碼的å¯åŸ·è¡Œæª”。
diff --git a/src/Cli/Microsoft.DotNet.Configurer/xlf/LocalizableStrings.es.xlf b/src/Cli/Microsoft.DotNet.Configurer/xlf/LocalizableStrings.es.xlf
index 7c393562ad06..c4a282ffbb99 100644
--- a/src/Cli/Microsoft.DotNet.Configurer/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/Microsoft.DotNet.Configurer/xlf/LocalizableStrings.es.xlf
@@ -39,7 +39,7 @@ Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cl
Escribir su primera aplicación: https://aka.ms/dotnet-hello-world
Descubra las novedades: https://aka.ms/dotnet-whats-new
Explore la documentación: https://aka.ms/dotnet-docs
-Notificar problemas y encontrar el origen en GitHub: https://github.com/dotnet/core
+Notificar problemas y encontrar el código fuente en GitHub: https://github.com/dotnet/core
Use "dotnet --help" para ver los comandos disponibles o visite: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
diff --git a/src/Cli/dotnet/CommandLineInfo.cs b/src/Cli/dotnet/CommandLineInfo.cs
index 2a67d2bd2f14..b931a2cbe157 100644
--- a/src/Cli/dotnet/CommandLineInfo.cs
+++ b/src/Cli/dotnet/CommandLineInfo.cs
@@ -36,7 +36,7 @@ private static void PrintWorkloadsInfo()
{
Reporter.Output.WriteLine();
Reporter.Output.WriteLine($"{LocalizableStrings.DotnetWorkloadInfoLabel}");
- WorkloadCommandParser.ShowWorkloadsInfo();
+ WorkloadCommandParser.ShowWorkloadsInfo(showVersion: false);
}
private static string GetDisplayRid(DotnetVersionFile versionFile)
diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs
index 3547fd59ed1c..a11b13c4476c 100644
--- a/src/Cli/dotnet/CommonOptions.cs
+++ b/src/Cli/dotnet/CommonOptions.cs
@@ -249,9 +249,9 @@ public static string GetCurrentRuntimeId()
return currentRuntimeIdentifiers[0]; // First rid is the most specific (ex win-x64)
}
- private static string GetOsFromRid(string rid) => rid.Substring(0, rid.LastIndexOf("-"));
+ private static string GetOsFromRid(string rid) => rid.Substring(0, rid.LastIndexOf("-", StringComparison.InvariantCulture));
- private static string GetArchFromRid(string rid) => rid.Substring(rid.LastIndexOf("-") + 1, rid.Length - rid.LastIndexOf("-") - 1);
+ private static string GetArchFromRid(string rid) => rid.Substring(rid.LastIndexOf("-", StringComparison.InvariantCulture) + 1, rid.Length - rid.LastIndexOf("-", StringComparison.InvariantCulture) - 1);
private static IEnumerable ForwardSelfContainedOptions(bool isSelfContained, ParseResult parseResult)
{
diff --git a/src/Cli/dotnet/Installer/Windows/ISynchronizingLogger.cs b/src/Cli/dotnet/Installer/Windows/ISynchronizingLogger.cs
new file mode 100644
index 000000000000..a442a7f4401a
--- /dev/null
+++ b/src/Cli/dotnet/Installer/Windows/ISynchronizingLogger.cs
@@ -0,0 +1,17 @@
+// 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.Installer.Windows
+{
+ ///
+ /// Represents a type used to log setup operations that can manage a series of named pipes writing to it.
+ ///
+ internal interface ISynchronizingLogger : ISetupLogger
+ {
+ ///
+ /// Starts a new thread to listen for log requests messages from external processes.
+ ///
+ /// The name of the pipe.
+ void AddNamedPipe(string pipeName);
+ }
+}
diff --git a/src/Cli/dotnet/Installer/Windows/InstallClientElevationContext.cs b/src/Cli/dotnet/Installer/Windows/InstallClientElevationContext.cs
index 65c24dfbe79c..045c4e0b7ea7 100644
--- a/src/Cli/dotnet/Installer/Windows/InstallClientElevationContext.cs
+++ b/src/Cli/dotnet/Installer/Windows/InstallClientElevationContext.cs
@@ -14,13 +14,13 @@ namespace Microsoft.DotNet.Installer.Windows
[SupportedOSPlatform("windows")]
internal sealed class InstallClientElevationContext : InstallElevationContextBase
{
- private TimestampedFileLogger _log;
+ private ISynchronizingLogger _log;
private Process _serverProcess;
public override bool IsClient => true;
- public InstallClientElevationContext(TimestampedFileLogger logger)
+ public InstallClientElevationContext(ISynchronizingLogger logger)
{
_log = logger;
}
@@ -60,7 +60,7 @@ public override void Elevate()
// Add a pipe to the logger to allow the server to send log requests. This avoids having an elevated process writing
// to a less privileged location. It also simplifies troubleshooting because log events will be chronologically
- // ordered in a single file.
+ // ordered in a single file.
_log.AddNamedPipe(WindowsUtils.CreatePipeName(_serverProcess.Id, "log"));
HasElevated = true;
diff --git a/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs b/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs
index 8ca9141a9343..7d446db4944d 100644
--- a/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs
+++ b/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs
@@ -146,5 +146,51 @@ public InstallResponseMessage SendWorkloadRecordRequest(InstallRequestType reque
SdkFeatureBand = sdkFeatureBand.ToString(),
});
}
+
+ ///
+ /// Send an to delete the install state file.
+ ///
+ /// The SDK feature band of the install state file to delete.
+ ///
+ public InstallResponseMessage SendRemoveManifestsFromInstallStateFileRequest(SdkFeatureBand sdkFeatureBand)
+ {
+ return Send(new InstallRequestMessage
+ {
+ RequestType = InstallRequestType.RemoveManifestsFromInstallStateFile,
+ SdkFeatureBand = sdkFeatureBand.ToString(),
+ });
+ }
+
+ ///
+ /// Sends an to write the install state file.
+ ///
+ /// The SDK feature band of the install state file to write
+ /// A multi-line string containing the formatted JSON data to write.
+ ///
+ public InstallResponseMessage SendSaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary manifestContents)
+ {
+ return Send(new InstallRequestMessage
+ {
+ RequestType = InstallRequestType.SaveInstallStateManifestVersions,
+ SdkFeatureBand = sdkFeatureBand.ToString(),
+ InstallStateManifestVersions = manifestContents
+ });
+ }
+
+ ///
+ /// Send an to adjust the mode used for installing and updating workloads
+ ///
+ /// The SDK feature band of the install state file to write
+ /// Whether to use workload sets or not
+ ///
+ public InstallResponseMessage SendUpdateWorkloadModeRequest(SdkFeatureBand sdkFeatureBand, bool newMode)
+ {
+ return Send(new InstallRequestMessage
+ {
+ RequestType = InstallRequestType.AdjustWorkloadMode,
+ SdkFeatureBand = sdkFeatureBand.ToString(),
+ UseWorkloadSets = newMode,
+ });
+ }
}
}
diff --git a/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs b/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs
index 712534d0b666..5ab6430bcbf4 100644
--- a/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs
+++ b/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs
@@ -29,6 +29,16 @@ public string ManifestPath
set;
}
+ ///
+ /// The contents of the install state file. Each element corresponds to a single line of
+ /// the JSON file to be written.
+ ///
+ public Dictionary InstallStateManifestVersions
+ {
+ get;
+ set;
+ }
+
///
/// The path of the MSI log file to generate when installing, uninstalling or repairing a specific MSI.
///
@@ -110,6 +120,14 @@ public string WorkloadId
set;
}
+ ///
+ /// The new mode to use: workloadset or loosemanifests
+ ///
+ public bool UseWorkloadSets
+ {
+ get; set;
+ }
+
///
/// Converts a deserialized array of bytes into an .
///
diff --git a/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs b/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs
index 7bb46285b29a..2d90d3f2f68f 100644
--- a/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs
+++ b/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs
@@ -53,6 +53,21 @@ public enum InstallRequestType
///
/// Remove a workload installation record.
///
- DeleteWorkloadInstallationRecord
+ DeleteWorkloadInstallationRecord,
+
+ ///
+ /// Creates an install state file.
+ ///
+ SaveInstallStateManifestVersions,
+
+ ///
+ /// Removes an install state file.
+ ///
+ RemoveManifestsFromInstallStateFile,
+
+ ///
+ /// Changes the workload mode
+ ///
+ AdjustWorkloadMode,
}
}
diff --git a/src/Cli/dotnet/Installer/Windows/MsiPackageCache.cs b/src/Cli/dotnet/Installer/Windows/MsiPackageCache.cs
index 9b300f91af77..54f0939a4785 100644
--- a/src/Cli/dotnet/Installer/Windows/MsiPackageCache.cs
+++ b/src/Cli/dotnet/Installer/Windows/MsiPackageCache.cs
@@ -1,11 +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.IO.Pipes;
using System.Runtime.Versioning;
-using System.Security.AccessControl;
-using System.Security.Principal;
-using Microsoft.DotNet.Cli.Utils;
#if !DOT_NET_BUILD_FROM_SOURCE
using Microsoft.DotNet.Installer.Windows.Security;
#endif
@@ -25,55 +21,6 @@ internal class MsiPackageCache : InstallerBase
///
private bool _allowOnlineRevocationChecks;
- ///
- /// Default inheritance to apply to directory ACLs.
- ///
- private static readonly InheritanceFlags s_DefaultInheritance = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
-
- ///
- /// SID that matches built-in administrators.
- ///
- private static readonly SecurityIdentifier s_AdministratorsSid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
-
- ///
- /// SID that matches everyone.
- ///
- private static readonly SecurityIdentifier s_EveryoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
-
- ///
- /// Local SYSTEM SID.
- ///
- private static readonly SecurityIdentifier s_LocalSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
-
- ///
- /// SID matching built-in user accounts.
- ///
- private static readonly SecurityIdentifier s_UsersSid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
-
- ///
- /// ACL rule associated with the Administrators SID.
- ///
- private static readonly FileSystemAccessRule s_AdministratorRule = new FileSystemAccessRule(s_AdministratorsSid, FileSystemRights.FullControl,
- s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
-
- ///
- /// ACL rule associated with the Everyone SID.
- ///
- private static readonly FileSystemAccessRule s_EveryoneRule = new FileSystemAccessRule(s_EveryoneSid, FileSystemRights.ReadAndExecute,
- s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
-
- ///
- /// ACL rule associated with the Local SYSTEM SID.
- ///
- private static readonly FileSystemAccessRule s_LocalSystemRule = new FileSystemAccessRule(s_LocalSystemSid, FileSystemRights.FullControl,
- s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
-
- ///
- /// ACL rule associated with the built-in users SID.
- ///
- private static readonly FileSystemAccessRule s_UsersRule = new FileSystemAccessRule(s_UsersSid, FileSystemRights.ReadAndExecute,
- s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
-
///
/// The root directory of the package cache where MSI workload packs are stored.
///
@@ -88,21 +35,6 @@ public MsiPackageCache(InstallElevationContextBase elevationContext, ISetupLogge
_allowOnlineRevocationChecks = SignCheck.AllowOnlineRevocationChecks();
}
- ///
- /// Creates the specified directory and secures it by configuring access rules (ACLs) that allow sub-directories
- /// and files to inherit access control entries.
- ///
- /// The path of the directory to create.
- public static void CreateSecureDirectory(string path)
- {
- if (!Directory.Exists(path))
- {
- DirectorySecurity ds = new();
- SetDirectoryAccessRules(ds);
- ds.CreateDirectory(path);
- }
- }
-
///
/// Moves the MSI payload described by the manifest file to the cache.
///
@@ -129,7 +61,7 @@ public void CachePayload(string packageId, string packageVersion, string manifes
Directory.Delete(packageDirectory, recursive: true);
}
- CreateSecureDirectory(packageDirectory);
+ SecurityUtils.CreateSecureDirectory(packageDirectory);
// We cannot assume that the MSI adjacent to the manifest is the one to cache. We'll trust
// the manifest to provide the MSI filename.
@@ -140,8 +72,8 @@ public void CachePayload(string packageId, string packageVersion, string manifes
string cachedMsiPath = Path.Combine(packageDirectory, Path.GetFileName(msiPath));
string cachedManifestPath = Path.Combine(packageDirectory, Path.GetFileName(manifestPath));
- MoveAndSecureFile(manifestPath, cachedManifestPath, Log);
- MoveAndSecureFile(msiPath, cachedMsiPath, Log);
+ SecurityUtils.MoveAndSecureFile(manifestPath, cachedManifestPath, Log);
+ SecurityUtils.MoveAndSecureFile(msiPath, cachedMsiPath, Log);
}
else if (IsClient)
{
@@ -160,37 +92,6 @@ public string GetPackageDirectory(string packageId, string packageVersion)
return Path.Combine(PackageCacheRoot, packageId, packageVersion);
}
- ///
- /// Moves a file from one location to another if the destination file does not already exist and
- /// configure its permissions.
- ///
- /// The source file to move.
- /// The destination where the source file will be moved.
- /// The underlying setup log to use.
- public static void MoveAndSecureFile(string sourceFile, string destinationFile, ISetupLogger log = null)
- {
- if (!File.Exists(destinationFile))
- {
- FileAccessRetrier.RetryOnMoveAccessFailure(() =>
- {
- // Moving the file preserves the owner SID and fails to inherit the WD ACE.
- File.Copy(sourceFile, destinationFile, overwrite: true);
- File.Delete(sourceFile);
- });
- log?.LogMessage($"Moved '{sourceFile}' to '{destinationFile}'");
-
- FileInfo fi = new(destinationFile);
- FileSecurity fs = new();
-
- // Set the owner and group to built-in administrators (BA). All other ACE values are inherited from
- // the parent directory. See https://github.com/dotnet/sdk/issues/28450. If the directory's descriptor
- // is correctly configured, we should end up with an inherited ACE for Everyone: (A;ID;0x1200a9;;;WD)
- fs.SetOwner(s_AdministratorsSid);
- fs.SetGroup(s_AdministratorsSid);
- fi.SetAccessControl(fs);
- }
- }
-
///
/// Determines if the workload pack MSI is cached and tries to retrieve its payload from the cache.
///
@@ -243,22 +144,6 @@ public bool TryGetMsiPathFromPackageData(string packageDataPath, out string msiP
return true;
}
- ///
- /// Apply a standard set of access rules to the directory security descriptor. The owner and group will
- /// be set to built-in Administrators. Full access is granted to built-in administators and SYSTEM with
- /// read, execute, synchronize permssions for built-in users and Everyone.
- ///
- /// The security descriptor to update.
- private static void SetDirectoryAccessRules(DirectorySecurity ds)
- {
- ds.SetOwner(s_AdministratorsSid);
- ds.SetGroup(s_AdministratorsSid);
- ds.SetAccessRule(s_AdministratorRule);
- ds.SetAccessRule(s_LocalSystemRule);
- ds.SetAccessRule(s_UsersRule);
- ds.SetAccessRule(s_EveryoneRule);
- }
-
///
/// Verifies that an MSI package contains an Authenticode signature that terminates in a trusted Microsoft root certificate.
///
diff --git a/src/Cli/dotnet/Installer/Windows/NullInstallerLogger.cs b/src/Cli/dotnet/Installer/Windows/NullInstallerLogger.cs
new file mode 100644
index 000000000000..2c61375d89a1
--- /dev/null
+++ b/src/Cli/dotnet/Installer/Windows/NullInstallerLogger.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.Installer.Windows;
+
+///
+/// A class that swallows all logging - used for code paths where logging setup operations isn't valuable, like dotnet --info.
+///
+internal class NullInstallerLogger : ISynchronizingLogger
+{
+ public string LogPath => String.Empty;
+
+ public void AddNamedPipe(string pipeName)
+ {
+
+ }
+
+ public void LogMessage(string message)
+ {
+
+ }
+}
diff --git a/src/Cli/dotnet/Installer/Windows/SecurityUtils.cs b/src/Cli/dotnet/Installer/Windows/SecurityUtils.cs
new file mode 100644
index 000000000000..38ca0e78499d
--- /dev/null
+++ b/src/Cli/dotnet/Installer/Windows/SecurityUtils.cs
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO.Pipes;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using System.Security.Principal;
+using Microsoft.DotNet.Cli.Utils;
+
+namespace Microsoft.DotNet.Installer.Windows
+{
+ ///
+ /// Defines some generic security related helper methods.
+ ///
+ [SupportedOSPlatform("windows")]
+ internal static class SecurityUtils
+ {
+ ///
+ /// Default inheritance to apply to directory ACLs.
+ ///
+ private static readonly InheritanceFlags s_DefaultInheritance = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
+
+ ///
+ /// SID that matches built-in administrators.
+ ///
+ private static readonly SecurityIdentifier s_AdministratorsSid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
+
+ ///
+ /// SID that matches everyone.
+ ///
+ private static readonly SecurityIdentifier s_EveryoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
+
+ ///
+ /// Local SYSTEM SID.
+ ///
+ private static readonly SecurityIdentifier s_LocalSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
+
+ ///
+ /// SID matching built-in user accounts.
+ ///
+ private static readonly SecurityIdentifier s_UsersSid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
+
+ ///
+ /// ACL rule associated with the Administrators SID.
+ ///
+ private static readonly FileSystemAccessRule s_AdministratorRule = new FileSystemAccessRule(s_AdministratorsSid, FileSystemRights.FullControl,
+ s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
+
+ ///
+ /// ACL rule associated with the Everyone SID.
+ ///
+ private static readonly FileSystemAccessRule s_EveryoneRule = new FileSystemAccessRule(s_EveryoneSid, FileSystemRights.ReadAndExecute,
+ s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
+
+ ///
+ /// ACL rule associated with the Local SYSTEM SID.
+ ///
+ private static readonly FileSystemAccessRule s_LocalSystemRule = new FileSystemAccessRule(s_LocalSystemSid, FileSystemRights.FullControl,
+ s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
+
+ ///
+ /// ACL rule associated with the built-in users SID.
+ ///
+ private static readonly FileSystemAccessRule s_UsersRule = new FileSystemAccessRule(s_UsersSid, FileSystemRights.ReadAndExecute,
+ s_DefaultInheritance, PropagationFlags.None, AccessControlType.Allow);
+
+ ///
+ /// Creates the specified directory and secures it by configuring access rules (ACLs) that allow sub-directories
+ /// and files to inherit access control entries.
+ ///
+ /// The path of the directory to create.
+ public static void CreateSecureDirectory(string path)
+ {
+ if (!Directory.Exists(path))
+ {
+ DirectorySecurity ds = new();
+ SecurityUtils.SetDirectoryAccessRules(ds);
+ ds.CreateDirectory(path);
+ }
+ }
+
+ ///
+ /// Moves a file from one location to another if the destination file does not already exist and
+ /// configure its permissions.
+ ///
+ /// The source file to move.
+ /// The destination where the source file will be moved.
+ /// The underlying setup log to use.
+ public static void MoveAndSecureFile(string sourceFile, string destinationFile, ISetupLogger log = null)
+ {
+ if (!File.Exists(destinationFile))
+ {
+ FileAccessRetrier.RetryOnMoveAccessFailure(() =>
+ {
+ // Moving the file preserves the owner SID and fails to inherit the WD ACE.
+ File.Copy(sourceFile, destinationFile, overwrite: true);
+ File.Delete(sourceFile);
+ });
+ log?.LogMessage($"Moved '{sourceFile}' to '{destinationFile}'");
+
+ SecureFile(destinationFile);
+ }
+ }
+
+ ///
+ /// Secures a file by setting the owner and group to built-in administrators (BA). All other ACE values are inherited from
+ /// the parent directory.
+ ///
+ /// The path of the file to secure.
+ public static void SecureFile(string path)
+ {
+ FileInfo fi = new(path);
+ FileSecurity fs = new();
+
+ // See https://github.com/dotnet/sdk/issues/28450. If the directory's descriptor
+ // is correctly configured, we should end up with an inherited ACE for Everyone: (A;ID;0x1200a9;;;WD)
+ fs.SetOwner(s_AdministratorsSid);
+ fs.SetGroup(s_AdministratorsSid);
+ fi.SetAccessControl(fs);
+ }
+
+ ///
+ /// Apply a standard set of access rules to the directory security descriptor. The owner and group will
+ /// be set to built-in Administrators. Full access is granted to built-in administators and SYSTEM with
+ /// read, execute, synchronize permssions for built-in users and Everyone.
+ ///
+ /// The security descriptor to update.
+ private static void SetDirectoryAccessRules(DirectorySecurity ds)
+ {
+ ds.SetOwner(s_AdministratorsSid);
+ ds.SetGroup(s_AdministratorsSid);
+ ds.SetAccessRule(s_AdministratorRule);
+ ds.SetAccessRule(s_LocalSystemRule);
+ ds.SetAccessRule(s_UsersRule);
+ ds.SetAccessRule(s_EveryoneRule);
+ }
+ }
+}
diff --git a/src/Cli/dotnet/Installer/Windows/TimestampedFileLogger.cs b/src/Cli/dotnet/Installer/Windows/TimestampedFileLogger.cs
index c606499ca841..5f0d422b938f 100644
--- a/src/Cli/dotnet/Installer/Windows/TimestampedFileLogger.cs
+++ b/src/Cli/dotnet/Installer/Windows/TimestampedFileLogger.cs
@@ -13,7 +13,7 @@ namespace Microsoft.DotNet.Installer.Windows
/// queue messages.
///
[SupportedOSPlatform("windows")]
- internal class TimestampedFileLogger : SetupLoggerBase, IDisposable, ISetupLogger
+ internal class TimestampedFileLogger : SetupLoggerBase, IDisposable, ISynchronizingLogger
{
///
/// Thread safe queue use to store incoming log request messages.
@@ -81,10 +81,6 @@ public TimestampedFileLogger(string path, int flushThreshold, params string[] lo
LogMessage($"=== Logging started ===");
}
- ///
- /// Starts a new thread to listen for log requests messages from external processes.
- ///
- /// The name of the pipe.
public void AddNamedPipe(string pipeName)
{
Thread logRequestThread = new Thread(ProcessLogRequests) { IsBackground = true };
diff --git a/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs b/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
index 463d64513bf8..99e9b8f161c6 100644
--- a/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
+++ b/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
@@ -64,8 +64,10 @@ public static bool RebootRequired()
using RegistryKey sessionKey = localMachineKey?.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager");
string[] pendingFileRenameOperations = (string[])sessionKey?.GetValue("PendingFileRenameOperations") ?? new string[0];
+ // Destination files for pending renames start with !\??\, whereas the source does not have the leading "!".
+ bool hasPendingFileRenames = pendingFileRenameOperations.Any(s => !string.IsNullOrWhiteSpace(s) && s.StartsWith(@"!\??\"));
- return (auKey != null || cbsKey != null || pendingFileRenameOperations.Length > 0);
+ return (auKey != null || cbsKey != null || hasPendingFileRenames);
}
}
}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.cs.xlf
index c00aae5e30af..a99263b23eec 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.cs.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Požadované zásady řetězu certifikátů nebylo možné zkontrolovat: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.de.xlf
index 108efca9275b..c618130cbb2a 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.de.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Die angeforderte Zertifikatkettenrichtlinie konnte nicht überprüft werden: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.es.xlf
index 196614f4c45f..71298e7b2d27 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.es.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ No se pudo comprobar la directiva de cadena de certificados solicitada: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.fr.xlf
index d9453fce2c36..cd58f475fcde 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.fr.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Impossible de vérifier la stratégie de chaîne de certificats demandée : {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.it.xlf
index 5cdcdd9e9707..24789b558ec4 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.it.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Non è stato possibile controllare il criterio della catena di certificati richiesto: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ja.xlf
index 29bca91ae045..b8da6643583e 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ja.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ è¦æ±‚ã•れãŸè¨¼æ˜Žæ›¸ãƒã‚§ãƒ¼ãƒ³ ãƒãƒªã‚·ãƒ¼ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸ: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ko.xlf
index eff70df8d5b1..c5dcfc917c3d 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ko.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ ìš”ì²í•œ ì¸ì¦ì„œ ì²´ì¸ ì •ì±…ì„ í™•ì¸í• 수 없습니다: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pl.xlf
index d47b4882d5bb..5db4a271b19c 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pl.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Nie można sprawdzić żądanych zasad łańcucha certyfikatów: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pt-BR.xlf
index 457c0255912b..2f5e0a444dd3 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.pt-BR.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Não foi possÃvel verificar a polÃtica de cadeia de certificados solicitada: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ru.xlf
index d952a0d46d26..8f58abf58003 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.ru.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ Ðе удалоÑÑŒ проверить запрошенную политику цепочки Ñертификатов {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.tr.xlf
index 341d602fc487..a22be57d131d 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.tr.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ İstenen sertifika zinciri ilkesi denetlenemedi: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hans.xlf
index 3323173002c8..0ad42e9a1659 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hans.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ æ— æ³•æ£€æŸ¥è¯·æ±‚çš„è¯ä¹¦é“¾ç–ç•¥: {0}
diff --git a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hant.xlf
index 28ffbf5688f0..314465d1da63 100644
--- a/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/Installer/Windows/xlf/LocalizableStrings.zh-Hant.xlf
@@ -4,7 +4,7 @@
The requested certificate chain policy could not be checked: {0}
- The requested certificate chain policy could not be checked: {0}
+ ç„¡æ³•æª¢æŸ¥è¦æ±‚的憑è‰éˆçµåŽŸå‰‡: {0}
diff --git a/src/Cli/dotnet/NugetPackageDownloader/LocalizableStrings.resx b/src/Cli/dotnet/NugetPackageDownloader/LocalizableStrings.resx
index bfb61a0a4c16..cb0ad6727fe8 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/LocalizableStrings.resx
+++ b/src/Cli/dotnet/NugetPackageDownloader/LocalizableStrings.resx
@@ -144,4 +144,19 @@
Package Source Mapping is enabled, but no source mapped under the specified package ID: {0}. See the documentation for Package Source Mapping at https://aka.ms/nuget-package-source-mapping for more details.
+
+ A version of {0} of package {1}
+
+
+ Version {0} of package {1}
+
+
+ A version between {0} and {1} of package {2}
+
+
+ A version higher than {0} of package {1}
+
+
+ A version less than {0} of package {1}
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
index ac176a717930..933f561d985c 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
+++ b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
@@ -1,11 +1,13 @@
// 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.RegularExpressions;
using System.Threading;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ToolPackage;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
+using Microsoft.TemplateEngine.Abstractions;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Credentials;
@@ -161,8 +163,8 @@ public async Task GetPackageUrl(PackageId packageId,
PackageSourceLocation packageSourceLocation = null,
bool includePreview = false)
{
- (var source, var resolvedPackageVersion) = await GetPackageSourceAndVersion(packageId, packageVersion, packageSourceLocation, includePreview);
-
+ (var source, var resolvedPackageVersion) = await GetPackageSourceAndVersion(packageId, packageVersion, packageSourceLocation, includePreview).ConfigureAwait(false);
+
SourceRepository repository = GetSourceRepository(source);
if (repository.PackageSource.IsLocal)
{
@@ -450,15 +452,45 @@ await Task.WhenAll(
throw new NuGetPackageNotFoundException(
string.Format(
LocalizableStrings.IsNotFoundInNuGetFeeds,
- $"{packageIdentifier}::{versionRange}",
+ GenerateVersionRangeErrorDescription(packageIdentifier, versionRange),
string.Join(", ", packageSources.Select(source => source.Source))));
}
+ }
+
+ private string GenerateVersionRangeErrorDescription(string packageIdentifier, VersionRange versionRange)
+ {
+ if (!string.IsNullOrEmpty(versionRange.OriginalString) && versionRange.OriginalString == "*")
+ {
+ return $"{packageIdentifier}";
+ }
+ else if (versionRange.HasLowerAndUpperBounds && versionRange.MinVersion == versionRange.MaxVersion)
+ {
+ return string.Format(LocalizableStrings.PackageVersionDescriptionForExactVersionMatch,
+ versionRange.MinVersion, packageIdentifier);
+ }
+ else if (versionRange.HasLowerAndUpperBounds)
+ {
+ return string.Format(LocalizableStrings.PackageVersionDescriptionForVersionWithLowerAndUpperBounds,
+ versionRange.MinVersion, versionRange.MaxVersion, packageIdentifier);
+ }
+ else if (versionRange.HasLowerBound)
+ {
+ return string.Format(LocalizableStrings.PackageVersionDescriptionForVersionWithLowerBound,
+ versionRange.MinVersion, packageIdentifier);
+ }
+ else if (versionRange.HasUpperBound)
+ {
+ return string.Format(LocalizableStrings.PackageVersionDescriptionForVersionWithUpperBound,
+ versionRange.MaxVersion, packageIdentifier);
+ }
+ // Default message if the format doesn't match any of the expected cases
+ return string.Format(LocalizableStrings.PackageVersionDescriptionDefault, versionRange, packageIdentifier);
}
- private async Task<(PackageSource, IPackageSearchMetadata)> GetLatestVersionInternalAsync(
- string packageIdentifier, IEnumerable packageSources, bool includePreview,
- CancellationToken cancellationToken)
+ private async Task<(PackageSource, IPackageSearchMetadata)> GetLatestVersionInternalAsync(
+ string packageIdentifier, IEnumerable packageSources, bool includePreview,
+ CancellationToken cancellationToken)
{
if (packageSources == null)
{
@@ -499,7 +531,7 @@ await Task.WhenAll(
.SelectMany(result => result.foundPackages.Select(package => (result.source, package)));
if (!accumulativeSearchResults.Any())
- {
+ {
throw new NuGetPackageNotFoundException(
string.Format(
LocalizableStrings.IsNotFoundInNuGetFeeds,
@@ -527,8 +559,7 @@ public async Task GetBestPackageVersionAsync(PackageId packageId,
VersionRange versionRange,
PackageSourceLocation packageSourceLocation = null)
{
- if (versionRange.MinVersion != null && !versionRange.IsFloating &&
- (versionRange.MaxVersion == null || versionRange.MinVersion == versionRange.MaxVersion))
+ if (versionRange.MinVersion != null && versionRange.MaxVersion != null && versionRange.MinVersion == versionRange.MaxVersion)
{
return versionRange.MinVersion;
}
@@ -625,7 +656,8 @@ bool TryGetPackageMetadata(
}
throw new NuGetPackageNotFoundException(string.Format(LocalizableStrings.IsNotFoundInNuGetFeeds,
- $"{packageIdentifier}::{packageVersion}", string.Join(";", sources.Select(s => s.Source))));
+ GenerateVersionRangeErrorDescription(packageIdentifier, new VersionRange(minVersion: packageVersion, maxVersion: packageVersion, includeMaxVersion: true)),
+ string.Join(";", sources.Select(s => s.Source))));
}
private async Task<(PackageSource source, IEnumerable foundPackages)>
diff --git a/src/Cli/dotnet/NugetPackageDownloader/ToolPackageException.cs b/src/Cli/dotnet/NugetPackageDownloader/ToolPackageException.cs
index b2475bdeefcd..8e087629e471 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/ToolPackageException.cs
+++ b/src/Cli/dotnet/NugetPackageDownloader/ToolPackageException.cs
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.DotNet.Cli.Utils;
+
namespace Microsoft.DotNet.Cli.NuGetPackageDownloader
{
- internal class NuGetPackageInstallerException : Exception
+ internal class NuGetPackageInstallerException : GracefulException
{
public NuGetPackageInstallerException()
{
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.cs.xlf
index 911fd33da0d3..6668ef85e22e 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.cs.xlf
@@ -42,6 +42,31 @@
PÅ™eskakuje se ověřenà podpisu balÃÄku NuGet.
+
+ A version of {0} of package {1}
+ Verze {0} balÃÄku {1}
+
+
+
+ Version {0} of package {1}
+ Verze {0} balÃÄku {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Verze mezi {0} a {1} balÃÄku {2}
+
+
+
+ A version higher than {0} of package {1}
+ Verze vyššà než {0} balÃÄku {1}
+
+
+
+ A version less than {0} of package {1}
+ Verze menšà než {0} balÃÄku {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
PÅ™eskoÄit ověřovánà podepisovánà balÃÄku NuGet. Ověřovánà podepisovánà balÃÄku NuGet nenà k dispozici na Linuxu nebo v macOS https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.de.xlf
index 59eb4441ee9b..5dadf0d934f3 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.de.xlf
@@ -42,6 +42,31 @@
Die Überprüfung der NuGet-Paketsignatur wird übersprungen.
+
+ A version of {0} of package {1}
+ Eine Version von "{0}" des Pakets "{1}"
+
+
+
+ Version {0} of package {1}
+ Version "{0}" des Pakets "{1}"
+
+
+
+ A version between {0} and {1} of package {2}
+ Eine Version zwischen "{0}" und "{1}" des Pakets "{2}"
+
+
+
+ A version higher than {0} of package {1}
+ Eine Version, die höher als "{0}" des Pakets "{1}" ist.
+
+
+
+ A version less than {0} of package {1}
+ Eine Version, die niedriger als "{0}" des Pakets "{1}" ist.
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Überprüfung der NuGet-Paketsignierung überspringen. Die Überprüfung der NuGet-Signierung ist auf Linux- oder macOS nicht verfügbar https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.es.xlf
index 92ffef1b484e..cca9fc12e15c 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.es.xlf
@@ -42,6 +42,31 @@
Omitiendo la comprobación de la firma del paquete NuGet.
+
+ A version of {0} of package {1}
+ Versión de {0} del paquete {1}
+
+
+
+ Version {0} of package {1}
+ Versión {0} del paquete {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Una versión entre {0} y {1} del paquete {2}
+
+
+
+ A version higher than {0} of package {1}
+ Una versión superior a {0} del paquete {1}
+
+
+
+ A version less than {0} of package {1}
+ Versión anterior a la {0} del paquete {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Omitir la validación de firma del paquete NuGet. La validación de firma de NuGet no está disponible en Linux o macOS https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.fr.xlf
index 3dd83630d84a..2486bfba02c4 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.fr.xlf
@@ -42,6 +42,31 @@
La vérification de la signature du package NuGet est ignorée.
+
+ A version of {0} of package {1}
+ Une version de {0} du package {1}
+
+
+
+ Version {0} of package {1}
+ Une version {0} du package {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Une version comprise entre {0} et {1} du package {2}
+
+
+
+ A version higher than {0} of package {1}
+ Une version supérieure à {0} du package {1}
+
+
+
+ A version less than {0} of package {1}
+ Une version inférieure à {0} du package {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Ignorer la validation de signature de package NuGet. La validation de signature NuGet n’est pas disponible sur Linux ou macOS https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.it.xlf
index e62cebfc5365..f8cb87b2bd63 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.it.xlf
@@ -42,6 +42,31 @@
La verifica della firma del pacchetto NuGet verrà ignorata.
+
+ A version of {0} of package {1}
+ Versione di {0} del pacchetto {1}
+
+
+
+ Version {0} of package {1}
+ Versione {0} del pacchetto {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Versione compresa tra {0} e {1} del pacchetto {2}
+
+
+
+ A version higher than {0} of package {1}
+ Versione superiore a {0} del pacchetto {1}
+
+
+
+ A version less than {0} of package {1}
+ Versione inferiore a {0} del pacchetto {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Ignorare la convalida della firma del pacchetto NuGet. La convalida della firma NuGet non è disponibile in Linux o macOS https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ja.xlf
index 2f19c3d49013..9c05afb0a3b4 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ja.xlf
@@ -42,6 +42,31 @@
NuGet パッケージ署åã®èªè¨¼ã‚’スã‚ップã—ã¦ã„ã¾ã™ã€‚
+
+ A version of {0} of package {1}
+ パッケージ {1} ã® {0} ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+
+
+
+ Version {0} of package {1}
+ パッケージ {1} ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {0}
+
+
+
+ A version between {0} and {1} of package {2}
+ パッケージ {2} ã® {0} 㨠{1} ã®é–“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+
+
+
+ A version higher than {0} of package {1}
+ パッケージ {1} ã® {0} より上ä½ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+
+
+
+ A version less than {0} of package {1}
+ パッケージ {1} ã® {0} 未満ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
NuGet パッケージ署åã®æ¤œè¨¼ã‚’スã‚ップã—ã¾ã™ã€‚NuGet ç½²åã®æ¤œè¨¼ã¯ã€Linux ã¾ãŸã¯ macOS https://aka.ms/workloadskippackagevalidation ã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ko.xlf
index 7510fae557a0..4001ca7986f8 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ko.xlf
@@ -42,6 +42,31 @@
NuGet 패키지 서명 확ì¸ì„ 건너뛰는 중입니다.
+
+ A version of {0} of package {1}
+ {0}ì˜ ë²„ì „ì˜ íŒ¨í‚¤ì§€ {1}
+
+
+
+ Version {0} of package {1}
+ ë²„ì „ {0}ì˜ íŒ¨í‚¤ì§€ {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ {0} ë° {1} 사ì´ì˜ ë²„ì „ì˜ íŒ¨í‚¤ì§€ {2}
+
+
+
+ A version higher than {0} of package {1}
+ {0}보다 ë†’ì€ ë²„ì „ì˜ íŒ¨í‚¤ì§€ {1}
+
+
+
+ A version less than {0} of package {1}
+ {0}보다 ë‚®ì€ ë²„ì „ì˜ íŒ¨í‚¤ì§€ {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
NuGet 패키지 서명 ìœ íš¨ì„± 검사를 건너ëœë‹ˆë‹¤. NuGet 서명 ìœ íš¨ì„± 검사는 Linux ë˜ëŠ” macOS https://aka.ms/workloadskippackagevalidationì—서 ì‚¬ìš©í• ìˆ˜ 없습니다.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pl.xlf
index 6dc11d30cf84..c37e1a4a00e0 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pl.xlf
@@ -42,6 +42,31 @@
Pomijanie weryfikacji podpisu pakietu NuGet.
+
+ A version of {0} of package {1}
+ Wersja {0} pakietu {1}
+
+
+
+ Version {0} of package {1}
+ Wersja {0} pakietu {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Wersja między {0} i {1} pakietu {2}
+
+
+
+ A version higher than {0} of package {1}
+ Wersja nowsza niż {0} pakietu {1}
+
+
+
+ A version less than {0} of package {1}
+ Wersja mniejsza niż {0} pakietu {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Pomiń weryfikację podpisywania pakietu NuGet. Sprawdzanie podpisywania NuGet nie jest dostępne w systemie Linux ani MacOS https://aka.ms/workloadskippackagevalidation .
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pt-BR.xlf
index 4cd6fd064461..1e7289a6ecb5 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.pt-BR.xlf
@@ -42,6 +42,31 @@
Ignorando a verificação de assinatura do pacote NuGet.
+
+ A version of {0} of package {1}
+ Uma versão do {0} do pacote {1}
+
+
+
+ Version {0} of package {1}
+ Versão {0} do pacote {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ Uma versão entre {0} e {1} do pacote {2}
+
+
+
+ A version higher than {0} of package {1}
+ Uma versão superior a {0} do pacote {1}
+
+
+
+ A version less than {0} of package {1}
+ Uma versão anterior a {0} do pacote {1}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
Ignorar a validação da assinatura do pacote NuGet. A validação da assinatura do NuGet não está disponÃvel no Linux ou macOS https://aka.ms/workloadskippackagevalidation .
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ru.xlf
index 7a8e0b3d4166..b94dffd62b7a 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.ru.xlf
@@ -42,6 +42,31 @@
ПропуÑк проверки подпиÑи пакета NuGet.
+
+ A version of {0} of package {1}
+ ВерÑÐ¸Ñ {0} пакета {1}
+
+
+
+ Version {0} of package {1}
+ ВерÑÐ¸Ñ {0} пакета {1}
+
+
+
+ A version between {0} and {1} of package {2}
+ ВерÑÐ¸Ñ Ð¾Ñ‚ {0} до {1} пакета {2}
+
+
+
+ A version higher than {0} of package {1}
+ ВерÑÐ¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° {1} выше {0}
+
+
+
+ A version less than {0} of package {1}
+ ВерÑÐ¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° {1} ниже {0}
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
ПропуÑтить проверку подпиÑи пакета NuGet. Проверка подпиÑи NuGet недоÑтупна в Linux или macOS https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.tr.xlf
index 449b304962f6..fd6d4ff5e9a4 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.tr.xlf
@@ -42,6 +42,31 @@
NuGet paket imzası doğrulaması atlanıyor.
+
+ A version of {0} of package {1}
+ {1} paketinin bir {0} sürümü
+
+
+
+ Version {0} of package {1}
+ {1} paketinin {0} sürümü
+
+
+
+ A version between {0} and {1} of package {2}
+ {2} paketinin {0} ve {1} arasındaki bir sürümü
+
+
+
+ A version higher than {0} of package {1}
+ {1} paketinin {0} sürümünden yüksek bir sürümü
+
+
+
+ A version less than {0} of package {1}
+ {1} paketinin {0} sürümünden düşük bir sürümü
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
NuGet paketi imza doğrulamasını atlayın. NuGet imza doğrulaması Linux veya macOS üzerinde kullanılamıyor. Daha fazla bilgi için bkz. https://aka.ms/workloadskippackagevalidation.
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hans.xlf
index d8fa4715311c..2df9a0fe0842 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hans.xlf
@@ -42,6 +42,31 @@
æ£åœ¨è·³è¿‡ NuGet 包ç¾å验è¯ã€‚
+
+ A version of {0} of package {1}
+ 包 {1} 的版本 {0}
+
+
+
+ Version {0} of package {1}
+ 包 {1} 的版本 {0}
+
+
+
+ A version between {0} and {1} of package {2}
+ 包 {2} 的 {0} 和 {1} 之间的版本
+
+
+
+ A version higher than {0} of package {1}
+ 包 {1} 的高于 {0} 的版本
+
+
+
+ A version less than {0} of package {1}
+ 包 {1} 的低于 {0} 的版本
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
跳过 NuGet 包ç¾å验è¯ã€‚Linux 或 macOS ä¸Šä¸æä¾› NuGet ç¾å验è¯ã€‚https://aka.ms/workloadskippackagevalidation。
diff --git a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hant.xlf
index ddafb653bcaf..ff999afa8383 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.zh-Hant.xlf
@@ -42,6 +42,31 @@
æ£åœ¨ç•¥éŽ NuGet å¥—ä»¶ç°½ç« é©—è‰ã€‚
+
+ A version of {0} of package {1}
+ å°è£ {1} 的版本 {0}
+
+
+
+ Version {0} of package {1}
+ å°è£ {1} 的版本 {0}
+
+
+
+ A version between {0} and {1} of package {2}
+ å°è£ {2} çš„ {0} 與 {1} 之間的版本
+
+
+
+ A version higher than {0} of package {1}
+ å°è£ {1} 高於 {0} 的版本
+
+
+
+ A version less than {0} of package {1}
+ å°è£ {1} å°æ–¼ {0} 的版本
+
+
Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .
ç•¥éŽ NuGet 套件簽署驗è‰ã€‚NuGet 套件簽署驗è‰åœ¨ Linux 或 macOS 上無法使用 https://aka.ms/workloadskippackagevalidation。
diff --git a/src/Cli/dotnet/ParseResultExtensions.cs b/src/Cli/dotnet/ParseResultExtensions.cs
index d7f48e207cf6..ff49d8d2bdff 100644
--- a/src/Cli/dotnet/ParseResultExtensions.cs
+++ b/src/Cli/dotnet/ParseResultExtensions.cs
@@ -122,16 +122,38 @@ public static string[] GetSubArguments(this string[] args)
var subargs = args.ToList();
// Don't remove any arguments that are being passed to the app in dotnet run
- var runArgs = subargs.Contains("--") ? subargs.GetRange(subargs.IndexOf("--"), subargs.Count() - subargs.IndexOf("--")) : new List();
- subargs = subargs.Contains("--") ? subargs.GetRange(0, subargs.IndexOf("--")) : subargs;
+ var dashDashIndex = subargs.IndexOf("--");
- subargs.RemoveAll(arg => DiagOption.Name.Equals(arg) || DiagOption.Aliases.Contains(arg));
- if (subargs[0].Equals("dotnet"))
+ var runArgs = dashDashIndex > -1 ? subargs.GetRange(dashDashIndex, subargs.Count() - dashDashIndex) : new List(0);
+ subargs = dashDashIndex > -1 ? subargs.GetRange(0, dashDashIndex) : subargs;
+
+ return subargs
+ .SkipWhile(arg => DiagOption.Name.Equals(arg) || DiagOption.Aliases.Contains(arg) || arg.Equals("dotnet"))
+ .Skip(1) // remove top level command (ex build or publish)
+ .Concat(runArgs)
+ .ToArray();
+ }
+
+ public static bool DiagOptionPrecedesSubcommand(this string[] args, string subCommand)
+ {
+ if (string.IsNullOrEmpty(subCommand))
{
- subargs.RemoveAt(0);
+ return true;
}
- subargs.RemoveAt(0); // remove top level command (ex build or publish)
- return subargs.Concat(runArgs).ToArray();
+
+ for (var i = 0; i < args.Length; i++)
+ {
+ if (args[i].Equals(subCommand))
+ {
+ return false;
+ }
+ else if (DiagOption.Name.Equals(args) || DiagOption.Aliases.Contains(args[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
private static string GetSymbolResultValue(ParseResult parseResult, SymbolResult symbolResult)
diff --git a/src/Cli/dotnet/Parser.cs b/src/Cli/dotnet/Parser.cs
index 12da079c7f53..1a19ef71612c 100644
--- a/src/Cli/dotnet/Parser.cs
+++ b/src/Cli/dotnet/Parser.cs
@@ -38,6 +38,7 @@ public static class Parser
NewCommandParser.GetCommand(),
NuGetCommandParser.GetCommand(),
PackCommandParser.GetCommand(),
+ PackageCommandParser.GetCommand(),
ParseCommandParser.GetCommand(),
PublishCommandParser.GetCommand(),
RemoveCommandParser.GetCommand(),
diff --git a/src/Cli/dotnet/Program.cs b/src/Cli/dotnet/Program.cs
index 72307655b8af..3f178fda6490 100644
--- a/src/Cli/dotnet/Program.cs
+++ b/src/Cli/dotnet/Program.cs
@@ -142,9 +142,14 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry
ToolPathSentinelFileName)));
if (parseResult.GetValue(Parser.DiagOption) && parseResult.IsDotnetBuiltInCommand())
{
- Environment.SetEnvironmentVariable(CommandLoggingContext.Variables.Verbose, bool.TrueString);
- CommandLoggingContext.SetVerbose(true);
- Reporter.Reset();
+ // We found --diagnostic or -d, but we still need to determine whether the option should
+ // be attached to the dotnet command or the subcommand.
+ if (args.DiagOptionPrecedesSubcommand(parseResult.RootSubCommandResult()))
+ {
+ Environment.SetEnvironmentVariable(CommandLoggingContext.Variables.Verbose, bool.TrueString);
+ CommandLoggingContext.SetVerbose(true);
+ Reporter.Reset();
+ }
}
if (parseResult.HasOption(Parser.VersionOption) && parseResult.IsTopLevelDotnetCommand())
{
@@ -237,17 +242,26 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry
else
{
PerformanceLogEventSource.Log.ExtensibleCommandResolverStart();
- var resolvedCommand = CommandFactoryUsingResolver.Create(
- "dotnet-" + parseResult.GetValue(Parser.DotnetSubCommand),
- args.GetSubArguments(),
- FrameworkConstants.CommonFrameworks.NetStandardApp15);
- PerformanceLogEventSource.Log.ExtensibleCommandResolverStop();
+ try
+ {
+ var resolvedCommand = CommandFactoryUsingResolver.Create(
+ "dotnet-" + parseResult.GetValue(Parser.DotnetSubCommand),
+ args.GetSubArguments(),
+ FrameworkConstants.CommonFrameworks.NetStandardApp15);
+ PerformanceLogEventSource.Log.ExtensibleCommandResolverStop();
- PerformanceLogEventSource.Log.ExtensibleCommandStart();
- var result = resolvedCommand.Execute();
- PerformanceLogEventSource.Log.ExtensibleCommandStop();
+ PerformanceLogEventSource.Log.ExtensibleCommandStart();
+ var result = resolvedCommand.Execute();
+ PerformanceLogEventSource.Log.ExtensibleCommandStop();
- exitCode = result.ExitCode;
+ exitCode = result.ExitCode;
+ }
+ catch (CommandUnknownException e)
+ {
+ Reporter.Error.WriteLine(e.Message.Red());
+ Reporter.Output.WriteLine(e.InstructionMessage);
+ exitCode = 1;
+ }
}
PerformanceLogEventSource.Log.TelemetryClientFlushStart();
diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageException.cs b/src/Cli/dotnet/ToolPackage/ToolPackageException.cs
index 5617dbc72f20..0614f662482e 100644
--- a/src/Cli/dotnet/ToolPackage/ToolPackageException.cs
+++ b/src/Cli/dotnet/ToolPackage/ToolPackageException.cs
@@ -1,9 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.ToolPackage
{
- internal class ToolPackageException : Exception
+ internal class ToolPackageException : GracefulException
{
public ToolPackageException()
{
diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
index 0d87f4f82e00..9e010dfb3632 100644
--- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
+++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
@@ -3,8 +3,11 @@
using System.CommandLine;
using System.IO;
+using System.Linq;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using Microsoft.Deployment.DotNet.Releases;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
@@ -88,38 +91,10 @@ public InstallingWorkloadCommand(
_workloadManifestUpdaterFromConstructor = workloadManifestUpdater;
}
- protected internal void UpdateInstallState(bool createDefaultJson, IEnumerable manifestVersionUpdates)
- {
- var defaultJsonPath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json");
- if (createDefaultJson)
- {
- var jsonContents = WorkloadSet.FromManifests(
+ protected static Dictionary GetInstallStateContents(IEnumerable manifestVersionUpdates) =>
+ WorkloadSet.FromManifests(
manifestVersionUpdates.Select(update => new WorkloadManifestInfo(update.ManifestId.ToString(), update.NewVersion.ToString(), /* We don't actually use the directory here */ string.Empty, update.NewFeatureBand))
).ToDictionaryForJson();
- Directory.CreateDirectory(Path.GetDirectoryName(defaultJsonPath));
- File.WriteAllLines(defaultJsonPath, ToJsonEnumerable(jsonContents));
- }
- else
- {
- if (File.Exists(defaultJsonPath))
- {
- File.Delete(defaultJsonPath);
- }
- }
- }
-
- private IEnumerable ToJsonEnumerable(Dictionary dict)
- {
- yield return "{";
- yield return "\"manifests\": {";
- foreach (KeyValuePair line in dict)
- {
- yield return $"\"{line.Key}\": \"{line.Value}\",";
- }
- yield return "}";
- yield return "}";
- yield break;
- }
protected async Task> GetDownloads(IEnumerable workloadIds, bool skipManifestUpdate, bool includePreview, string downloadFolder = null)
{
@@ -221,6 +196,12 @@ protected IEnumerable GetInstalledWorkloads(bool fromPreviousSdk)
internal static class InstallingWorkloadCommandParser
{
+ public static readonly CliOption WorkloadSetMode = new("--mode")
+ {
+ Description = Strings.WorkloadSetMode,
+ Hidden = true
+ };
+
public static readonly CliOption PrintDownloadLinkOnlyOption = new("--print-download-link-only")
{
Description = Strings.PrintDownloadLinkOnlyDescription,
diff --git a/src/Cli/dotnet/commands/dotnet-package/PackageCommandParser.cs b/src/Cli/dotnet/commands/dotnet-package/PackageCommandParser.cs
new file mode 100644
index 000000000000..63bf74e8faa3
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/PackageCommandParser.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.
+
+using System.CommandLine;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal class PackageCommandParser
+ {
+ private const string DocsLink = "https://aka.ms/dotnet-package";
+
+ public static CliCommand GetCommand()
+ {
+ CliCommand command = new DocumentedCommand("package", DocsLink);
+ command.SetAction((parseResult) => parseResult.HandleMissingCommand());
+ command.Subcommands.Add(PackageSearchCommandParser.GetCommand());
+
+ return command;
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-package/search/LocalizableStrings.resx
new file mode 100644
index 000000000000..de09b2b0ad7f
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/LocalizableStrings.resx
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+ ConfigFile
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+ Format
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+ Include prerelease packages.
+
+
+ SearchTerm
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+ Skip
+
+
+ Number of results to skip, to allow pagination. Default 0.
+
+
+ Source
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+ Take
+
+
+ Number of results to return. Default 20.
+
+
+ Verbosity
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommand.cs b/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommand.cs
new file mode 100644
index 000000000000..f7a6d4800217
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommand.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.DotNet.Tools.NuGet;
+using System.CommandLine;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal class PackageSearchCommand : CommandBase
+ {
+ public PackageSearchCommand(ParseResult parseResult) : base(parseResult) { }
+
+ public override int Execute()
+ {
+ var args = new List
+ {
+ "package",
+ "search"
+ };
+
+ var searchArgument = _parseResult.GetValue(PackageSearchCommandParser.SearchTermArgument);
+ if (searchArgument != null)
+ {
+ args.Add(searchArgument);
+ }
+
+ args.AddRange(_parseResult.OptionValuesToBeForwarded(PackageSearchCommandParser.GetCommand()));
+ return NuGetCommand.Run(args.ToArray());
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommandParser.cs b/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommandParser.cs
new file mode 100644
index 000000000000..5bc951d470c4
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/PackageSearchCommandParser.cs
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.CommandLine;
+using LocalizableStrings = Microsoft.DotNet.Tools.Package.Search.LocalizableStrings;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal static class PackageSearchCommandParser
+ {
+ public static readonly CliArgument SearchTermArgument = new CliArgument("SearchTerm")
+ {
+ HelpName = LocalizableStrings.SearchTermArgumentName,
+ Description = LocalizableStrings.SearchTermDescription,
+ Arity = ArgumentArity.ZeroOrOne
+ };
+
+ public static readonly CliOption Sources = new ForwardedOption>("--source")
+ {
+ Description = LocalizableStrings.SourceDescription,
+ HelpName = LocalizableStrings.SourceArgumentName
+ }.ForwardAsManyArgumentsEachPrefixedByOption("--source")
+ .AllowSingleArgPerToken();
+
+ public static readonly CliOption Take = new ForwardedOption("--take")
+ {
+ Description = LocalizableStrings.TakeDescription,
+ HelpName = LocalizableStrings.TakeArgumentName
+ }.ForwardAsSingle(o => $"--take:{o}");
+
+ public static readonly CliOption Skip = new ForwardedOption("--skip")
+ {
+ Description = LocalizableStrings.SkipDescription,
+ HelpName = LocalizableStrings.SkipArgumentName
+ }.ForwardAsSingle(o => $"--skip:{o}");
+
+ public static readonly CliOption ExactMatch = new ForwardedOption("--exact-match")
+ {
+ Description = LocalizableStrings.ExactMatchDescription
+ }.ForwardAs("--exact-match");
+
+ public static readonly CliOption Interactive = new ForwardedOption("--interactive")
+ {
+ Description = LocalizableStrings.InteractiveDescription
+ }.ForwardAs("--interactive");
+
+ public static readonly CliOption Prerelease = new ForwardedOption("--prerelease")
+ {
+ Description = LocalizableStrings.PrereleaseDescription
+ }.ForwardAs("--prerelease");
+
+ public static readonly CliOption ConfigFile = new ForwardedOption("--configfile")
+ {
+ Description = LocalizableStrings.ConfigFileDescription,
+ HelpName = LocalizableStrings.ConfigFileArgumentName
+ }.ForwardAsSingle(o => $"--configfile:{o}");
+
+ public static readonly CliOption Format = new ForwardedOption("--format")
+ {
+ Description = LocalizableStrings.FormatDescription,
+ HelpName = LocalizableStrings.FormatArgumentName
+ }.ForwardAsSingle(o => $"--format:{o}");
+
+ public static readonly CliOption Verbosity = new ForwardedOption("--verbosity")
+ {
+ Description = LocalizableStrings.VerbosityDescription,
+ HelpName = LocalizableStrings.VerbosityArgumentName
+ }.ForwardAsSingle(o => $"--verbosity:{o}");
+
+ private static readonly CliCommand Command = ConstructCommand();
+
+ public static CliCommand GetCommand()
+ {
+ return Command;
+ }
+
+ private static CliCommand ConstructCommand()
+ {
+ CliCommand searchCommand = new("search", LocalizableStrings.CommandDescription);
+
+ searchCommand.Arguments.Add(SearchTermArgument);
+ searchCommand.Options.Add(Sources);
+ searchCommand.Options.Add(Take);
+ searchCommand.Options.Add(Skip);
+ searchCommand.Options.Add(ExactMatch);
+ searchCommand.Options.Add(Interactive);
+ searchCommand.Options.Add(Prerelease);
+ searchCommand.Options.Add(ConfigFile);
+ searchCommand.Options.Add(Format);
+ searchCommand.Options.Add(Verbosity);
+
+ searchCommand.SetAction((parseResult) => {
+ var command = new PackageSearchCommand(parseResult);
+ int exitCode = command.Execute();
+
+ if (exitCode == 1)
+ {
+ parseResult.ShowHelp();
+ }
+ });
+
+ return searchCommand;
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.cs.xlf
new file mode 100644
index 000000000000..ab95f376c5ef
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.cs.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.de.xlf
new file mode 100644
index 000000000000..25c81c1bd851
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.de.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.es.xlf
new file mode 100644
index 000000000000..e588d1659213
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.es.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.fr.xlf
new file mode 100644
index 000000000000..af84c66837d9
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.fr.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.it.xlf
new file mode 100644
index 000000000000..d11f25fd0541
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.it.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ja.xlf
new file mode 100644
index 000000000000..11c4cb28b221
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ja.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ko.xlf
new file mode 100644
index 000000000000..2920635a01c3
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ko.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pl.xlf
new file mode 100644
index 000000000000..a9051a7b47fc
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pl.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pt-BR.xlf
new file mode 100644
index 000000000000..c5c8365f6a04
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.pt-BR.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ru.xlf
new file mode 100644
index 000000000000..46b1600c3a28
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.ru.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.tr.xlf
new file mode 100644
index 000000000000..4a383e5cda10
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.tr.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hans.xlf
new file mode 100644
index 000000000000..dd4e698e80cd
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hans.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hant.xlf
new file mode 100644
index 000000000000..f66a8fe3c625
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-package/search/xlf/LocalizableStrings.zh-Hant.xlf
@@ -0,0 +1,97 @@
+
+
+
+
+
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+ Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.
+
+
+
+ ConfigFile
+ ConfigFile
+
+
+
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+ The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used. For more information, see https://docs.microsoft.com/nuget/consume-packages/configuring-nuget-behavior
+
+
+
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+ Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored.
+
+
+
+ Format
+ Format
+
+
+
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+ Format the output accordingly. Either `table`, or `json`. The default value is `table`.
+
+
+
+ Stop and wait for user input or action (for example to complete authentication).
+ Stop and wait for user input or action (for example to complete authentication).
+
+
+
+ Include prerelease packages.
+ Include prerelease packages.
+
+
+
+ SearchTerm
+ SearchTerm
+
+
+
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+ Search term to filter package names, descriptions, and tags. Used as a literal value. Example: `dotnet package search some.package`. See also `--exact-match`.
+
+
+
+ Skip
+ Skip
+
+
+
+ Number of results to skip, to allow pagination. Default 0.
+ Number of results to skip, to allow pagination. Default 0.
+
+
+
+ Source
+ Source
+
+
+
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+ The package source to search. You can pass multiple `--source` options to search multiple package sources. Example: `--source https://api.nuget.org/v3/index.json`.
+
+
+
+ Take
+ Take
+
+
+
+ Number of results to return. Default 20.
+ Number of results to return. Default 20.
+
+
+
+ Verbosity
+ Verbosity
+
+
+
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+ Display this amount of details in the output: `normal`, `minimal`, `detailed`. The default is `normal`
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-restore/RestoreCommandParser.cs b/src/Cli/dotnet/commands/dotnet-restore/RestoreCommandParser.cs
index 0d08befe5c19..bce6bc18d5c7 100644
--- a/src/Cli/dotnet/commands/dotnet-restore/RestoreCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-restore/RestoreCommandParser.cs
@@ -80,9 +80,9 @@ public static void AddImplicitRestoreOptions(CliCommand command, bool showHelp =
command.Options.Add(option);
}
}
- private static string GetOsFromRid(string rid) => rid.Substring(0, rid.LastIndexOf("-"));
- private static string GetArchFromRid(string rid) => rid.Substring(rid.LastIndexOf("-") + 1, rid.Length - rid.LastIndexOf("-") - 1);
- public static string RestoreRuntimeArgFunc(IEnumerable rids)
+ private static string GetOsFromRid(string rid) => rid.Substring(0, rid.LastIndexOf("-", StringComparison.InvariantCulture));
+ private static string GetArchFromRid(string rid) => rid.Substring(rid.LastIndexOf("-", StringComparison.InvariantCulture) + 1, rid.Length - rid.LastIndexOf("-", StringComparison.InvariantCulture) - 1);
+ public static string RestoreRuntimeArgFunc(IEnumerable rids)
{
List convertedRids = new();
foreach (string rid in rids)
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
index 33d8e9437480..03da9ca92fb6 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
@@ -142,7 +142,7 @@ private static CliOption CreateBlameHangDumpOption()
public static readonly CliOption NoLogoOption = new ForwardedOption("--nologo")
{
Description = LocalizableStrings.CmdNoLogo
- }.ForwardAs("-property:VSTestNoLogo=nologo");
+ }.ForwardAs("-property:VSTestNoLogo=true");
public static readonly CliOption NoRestoreOption = CommonOptions.NoRestoreOption;
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-tool/install/LocalizableStrings.resx
index cf87d1bac6c3..bacf2bd43283 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/LocalizableStrings.resx
@@ -235,4 +235,7 @@ If you would like to create a manifest, use `dotnet new tool-manifest`, usually
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
+
+ Allow package downgrade when installing a .NET tool package.
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallCommandParser.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallCommandParser.cs
index 4f79a5a2362e..32f93512db6d 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallCommandParser.cs
@@ -48,6 +48,11 @@ internal static class ToolInstallCommandParser
Description = LocalizableStrings.CreateManifestIfNeededOptionDescription
};
+ public static readonly CliOption AllowPackageDowngradeOption = new("--allow-downgrade")
+ {
+ Description = LocalizableStrings.AllowPackageDowngradeOptionDescription
+ };
+
public static readonly CliOption VerbosityOption = CommonOptions.VerbosityOption;
// Don't use the common options version as we don't want this to be a forwarded option
@@ -77,6 +82,19 @@ private static CliCommand ConstructCommand()
{
CliCommand command = new("install", LocalizableStrings.CommandDescription);
+ AddCommandOptions(command);
+
+ command.Options.Add(ArchitectureOption);
+ command.Options.Add(CreateManifestIfNeededOption);
+ command.Options.Add(AllowPackageDowngradeOption);
+
+ command.SetAction((parseResult) => new ToolInstallCommand(parseResult).Execute());
+
+ return command;
+ }
+
+ public static CliCommand AddCommandOptions(CliCommand command)
+ {
command.Arguments.Add(PackageIdArgument);
command.Options.Add(GlobalOption.WithHelpDescription(command, LocalizableStrings.GlobalOptionDescription));
command.Options.Add(LocalOption.WithHelpDescription(command, LocalizableStrings.LocalOptionDescription));
@@ -92,12 +110,7 @@ private static CliCommand ConstructCommand()
command.Options.Add(ToolCommandRestorePassThroughOptions.NoCacheOption);
command.Options.Add(ToolCommandRestorePassThroughOptions.InteractiveRestoreOption);
command.Options.Add(VerbosityOption);
- command.Options.Add(ArchitectureOption);
- command.Options.Add(CreateManifestIfNeededOption);
-
- command.SetAction((parseResult) => new ToolInstallCommand(parseResult).Execute());
-
return command;
- }
+ }
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
index a9cbfe8d1a63..e722b6af6c5e 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
@@ -11,10 +11,12 @@
using Microsoft.DotNet.ToolPackage;
using Microsoft.DotNet.Tools.Tool.Common;
using Microsoft.DotNet.Tools.Tool.Uninstall;
+using Microsoft.DotNet.Tools.Tool.Update;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.Versioning;
+using static System.Formats.Asn1.AsnWriter;
namespace Microsoft.DotNet.Tools.Tool.Install
{
@@ -30,7 +32,7 @@ internal class ToolInstallGlobalOrToolPathCommand : CommandBase
private readonly IReporter _reporter;
private readonly IReporter _errorReporter;
private CreateShellShimRepository _createShellShimRepository;
- private CreateToolPackageStoresAndDownloader _createToolPackageStoresAndDownloader;
+ private readonly CreateToolPackageStoresAndDownloaderAndUninstaller _createToolPackageStoreDownloaderUninstaller;
private readonly ShellShimTemplateFinder _shellShimTemplateFinder;
private readonly PackageId _packageId;
@@ -43,10 +45,11 @@ internal class ToolInstallGlobalOrToolPathCommand : CommandBase
private readonly string _toolPath;
private readonly string _architectureOption;
private IEnumerable _forwardRestoreArguments;
+ private readonly bool _allowPackageDowngrade;
public ToolInstallGlobalOrToolPathCommand(
ParseResult parseResult,
- CreateToolPackageStoresAndDownloader createToolPackageStoreAndDownloader = null,
+ CreateToolPackageStoresAndDownloaderAndUninstaller createToolPackageStoreDownloaderUninstaller = null,
CreateShellShimRepository createShellShimRepository = null,
IEnvironmentPathInstruction environmentPathInstruction = null,
IReporter reporter = null,
@@ -63,8 +66,6 @@ public ToolInstallGlobalOrToolPathCommand(
_toolPath = parseResult.GetValue(ToolAppliedOption.ToolPathOption);
_architectureOption = parseResult.GetValue(ToolInstallCommandParser.ArchitectureOption);
- _createToolPackageStoresAndDownloader = createToolPackageStoreAndDownloader ?? ToolPackageFactory.CreateToolPackageStoresAndDownloader;
-
_forwardRestoreArguments = parseResult.OptionValuesToBeForwarded(ToolInstallCommandParser.GetCommand());
_environmentPathInstruction = environmentPathInstruction
@@ -80,6 +81,9 @@ public ToolInstallGlobalOrToolPathCommand(
Interactive: parseResult.GetValue(ToolCommandRestorePassThroughOptions.InteractiveRestoreOption));
nugetPackageDownloader ??= new NuGetPackageDownloader(tempDir, verboseLogger: new NullLogger(), restoreActionConfig: restoreAction);
_shellShimTemplateFinder = new ShellShimTemplateFinder(nugetPackageDownloader, tempDir, packageSourceLocation);
+ _allowPackageDowngrade = parseResult.GetValue(ToolInstallCommandParser.AllowPackageDowngradeOption);
+ _createToolPackageStoreDownloaderUninstaller = createToolPackageStoreDownloaderUninstaller ??
+ ToolPackageFactory.CreateToolPackageStoresAndDownloaderAndUninstaller;
_reporter = (reporter ?? Reporter.Output);
_errorReporter = (reporter ?? Reporter.Error);
@@ -87,15 +91,7 @@ public ToolInstallGlobalOrToolPathCommand(
public override int Execute()
{
- if (!string.IsNullOrEmpty(_configFilePath) && !File.Exists(_configFilePath))
- {
- throw new GracefulException(
- string.Format(
- LocalizableStrings.NuGetConfigurationFileDoesNotExist,
- Path.GetFullPath(_configFilePath)));
- }
-
- VersionRange versionRange = _parseResult.GetVersionRange();
+ ValidateArguments();
DirectoryPath? toolPath = null;
if (!string.IsNullOrEmpty(_toolPath))
@@ -103,31 +99,39 @@ public override int Execute()
toolPath = new DirectoryPath(_toolPath);
}
- (IToolPackageStore toolPackageStore, IToolPackageStoreQuery toolPackageStoreQuery, IToolPackageDownloader toolPackageDownloader) =
- _createToolPackageStoresAndDownloader(toolPath, _forwardRestoreArguments);
+ VersionRange versionRange = _parseResult.GetVersionRange();
- // Prevent installation if any version of the package is installed
- if (toolPackageStoreQuery.EnumeratePackageVersions(_packageId).FirstOrDefault() != null)
- {
- _errorReporter.WriteLine(string.Format(LocalizableStrings.ToolAlreadyInstalled, _packageId).Red());
- return 1;
- }
+ (IToolPackageStore toolPackageStore,
+ IToolPackageStoreQuery toolPackageStoreQuery,
+ IToolPackageDownloader toolPackageDownloader,
+ IToolPackageUninstaller toolPackageUninstaller) = _createToolPackageStoreDownloaderUninstaller(toolPath, _forwardRestoreArguments);
- FilePath? configFile = null;
- if (!string.IsNullOrEmpty(_configFilePath))
- {
- configFile = new FilePath(_configFilePath);
- }
+ var appHostSourceDirectory = ShellShimTemplateFinder.GetDefaultAppHostSourceDirectory();
+ IShellShimRepository shellShimRepository = _createShellShimRepository(appHostSourceDirectory, toolPath);
- try
+ IToolPackage oldPackageNullable = GetOldPackage(toolPackageStoreQuery);
+
+ using (var scope = new TransactionScope(
+ TransactionScopeOption.Required,
+ TimeSpan.Zero))
{
- IToolPackage package = null;
- using (var scope = new TransactionScope(
- TransactionScopeOption.Required,
- TimeSpan.Zero))
+ if (oldPackageNullable != null)
{
- package = toolPackageDownloader.InstallPackage(
- new PackageLocation(nugetConfig: configFile, additionalFeeds: _source),
+ RunWithHandlingUninstallError(() =>
+ {
+ foreach (RestoredCommand command in oldPackageNullable.Commands)
+ {
+ shellShimRepository.RemoveShim(command.Name);
+ }
+
+ toolPackageUninstaller.Uninstall(oldPackageNullable.PackageDirectory);
+ });
+ }
+
+ RunWithHandlingInstallError(() =>
+ {
+ IToolPackage newInstalledPackage = toolPackageDownloader.InstallPackage(
+ new PackageLocation(nugetConfig: GetConfigFile(), additionalFeeds: _source),
packageId: _packageId,
versionRange: versionRange,
targetFramework: _framework,
@@ -135,10 +139,12 @@ public override int Execute()
isGlobalTool: true
);
+ EnsureVersionIsHigher(oldPackageNullable, newInstalledPackage, _allowPackageDowngrade);
+
NuGetFramework framework;
- if (string.IsNullOrEmpty(_framework) && package.Frameworks.Count() > 0)
+ if (string.IsNullOrEmpty(_framework) && newInstalledPackage.Frameworks.Count() > 0)
{
- framework = package.Frameworks
+ framework = newInstalledPackage.Frameworks
.Where(f => f.Version < (new NuGetVersion(Product.Version)).Version)
.MaxBy(f => f.Version);
}
@@ -148,43 +154,170 @@ public override int Execute()
null :
NuGetFramework.Parse(_framework);
}
-
string appHostSourceDirectory = _shellShimTemplateFinder.ResolveAppHostSourceDirectoryAsync(_architectureOption, framework, RuntimeInformation.ProcessArchitecture).Result;
- IShellShimRepository shellShimRepository = _createShellShimRepository(appHostSourceDirectory, toolPath);
- foreach (var command in package.Commands)
+ foreach (RestoredCommand command in newInstalledPackage.Commands)
{
- shellShimRepository.CreateShim(command.Executable, command.Name, package.PackagedShims);
+ shellShimRepository.CreateShim(command.Executable, command.Name, newInstalledPackage.PackagedShims);
}
- scope.Complete();
- }
+ foreach (string w in newInstalledPackage.Warnings)
+ {
+ _reporter.WriteLine(w.Yellow());
+ }
+ if (_global)
+ {
+ _environmentPathInstruction.PrintAddPathInstructionIfPathDoesNotExist();
+ }
- foreach (string w in package.Warnings)
- {
- _reporter.WriteLine(w.Yellow());
- }
+ PrintSuccessMessage(oldPackageNullable, newInstalledPackage);
+ });
- if (_global)
- {
- _environmentPathInstruction.PrintAddPathInstructionIfPathDoesNotExist();
- }
+ scope.Complete();
+
+ }
+ return 0;
+ }
+
+ private static void EnsureVersionIsHigher(IToolPackage oldPackageNullable, IToolPackage newInstalledPackage, bool allowDowngrade)
+ {
+ if (oldPackageNullable != null && (newInstalledPackage.Version < oldPackageNullable.Version && !allowDowngrade))
+ {
+ throw new GracefulException(
+ new[]
+ {
+ string.Format(Update.LocalizableStrings.UpdateToLowerVersion,
+ newInstalledPackage.Version.ToNormalizedString(),
+ oldPackageNullable.Version.ToNormalizedString())
+ },
+ isUserError: false);
+ }
+ }
- _reporter.WriteLine(
+ private void ValidateArguments()
+ {
+ if (!string.IsNullOrEmpty(_configFilePath) && !File.Exists(_configFilePath))
+ {
+ throw new GracefulException(
string.Format(
- LocalizableStrings.InstallationSucceeded,
- string.Join(", ", package.Commands.Select(c => c.Name)),
- package.Id,
- package.Version.ToNormalizedString()).Green());
- return 0;
+ LocalizableStrings.NuGetConfigurationFileDoesNotExist,
+ Path.GetFullPath(_configFilePath)));
}
- catch (Exception ex) when (InstallToolCommandLowLevelErrorConverter.ShouldConvertToUserFacingError(ex))
+ }
+
+ private void RunWithHandlingInstallError(Action installAction)
+ {
+ try
+ {
+ installAction();
+ }
+ catch (Exception ex)
+ when (InstallToolCommandLowLevelErrorConverter.ShouldConvertToUserFacingError(ex))
+ {
+ var message = new List
+ {
+ string.Format(Update.LocalizableStrings.UpdateToolFailed, _packageId)
+ };
+ message.AddRange(
+ InstallToolCommandLowLevelErrorConverter.GetUserFacingMessages(ex, _packageId));
+
+
+ throw new GracefulException(
+ messages: message,
+ verboseMessages: new[] { ex.ToString() },
+ isUserError: false);
+ }
+ }
+
+ private void RunWithHandlingUninstallError(Action uninstallAction)
+ {
+ try
{
+ uninstallAction();
+ }
+ catch (Exception ex)
+ when (ToolUninstallCommandLowLevelErrorConverter.ShouldConvertToUserFacingError(ex))
+ {
+ var message = new List
+ {
+ string.Format(Update.LocalizableStrings.UpdateToolFailed, _packageId)
+ };
+ message.AddRange(
+ ToolUninstallCommandLowLevelErrorConverter.GetUserFacingMessages(ex, _packageId));
+
throw new GracefulException(
- messages: InstallToolCommandLowLevelErrorConverter.GetUserFacingMessages(ex, _packageId),
+ messages: message,
verboseMessages: new[] { ex.ToString() },
isUserError: false);
}
}
+
+ private FilePath? GetConfigFile()
+ {
+ FilePath? configFile = null;
+ if (!string.IsNullOrEmpty(_configFilePath))
+ {
+ configFile = new FilePath(_configFilePath);
+ }
+
+ return configFile;
+ }
+
+ private IToolPackage GetOldPackage(IToolPackageStoreQuery toolPackageStoreQuery)
+ {
+ IToolPackage oldPackageNullable;
+ try
+ {
+ oldPackageNullable = toolPackageStoreQuery.EnumeratePackageVersions(_packageId).SingleOrDefault();
+ }
+ catch (InvalidOperationException)
+ {
+ throw new GracefulException(
+ messages: new[]
+ {
+ string.Format(
+ Update.LocalizableStrings.ToolHasMultipleVersionsInstalled,
+ _packageId),
+ },
+ isUserError: false);
+ }
+
+ return oldPackageNullable;
+ }
+
+ private void PrintSuccessMessage(IToolPackage oldPackage, IToolPackage newInstalledPackage)
+ {
+ if (!_verbosity.IsQuiet())
+ {
+ if (oldPackage == null)
+ {
+ _reporter.WriteLine(
+ string.Format(
+ Install.LocalizableStrings.InstallationSucceeded,
+ string.Join(", ", newInstalledPackage.Commands.Select(c => c.Name)),
+ newInstalledPackage.Id,
+ newInstalledPackage.Version.ToNormalizedString()).Green());
+ }
+ else if (oldPackage.Version != newInstalledPackage.Version)
+ {
+ _reporter.WriteLine(
+ string.Format(
+ Update.LocalizableStrings.UpdateSucceeded,
+ newInstalledPackage.Id,
+ oldPackage.Version.ToNormalizedString(),
+ newInstalledPackage.Version.ToNormalizedString()).Green());
+ }
+ else
+ {
+ _reporter.WriteLine(
+ string.Format(
+ (
+ newInstalledPackage.Version.IsPrerelease ?
+ Update.LocalizableStrings.UpdateSucceededPreVersionNoChange : Update.LocalizableStrings.UpdateSucceededStableVersionNoChange
+ ),
+ newInstalledPackage.Id, newInstalledPackage.Version).Green());
+ }
+ }
+ }
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
index 9f9980ba9371..d9c5fcfaec00 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
@@ -19,6 +19,8 @@ internal class ToolInstallLocalCommand : CommandBase
private readonly ILocalToolsResolverCache _localToolsResolverCache;
private readonly ToolInstallLocalInstaller _toolLocalPackageInstaller;
private readonly IReporter _reporter;
+ private readonly PackageId _packageId;
+ private readonly bool _allowPackageDowngrade;
private readonly string _explicitManifestFile;
private readonly bool _createManifestIfNeeded;
@@ -44,15 +46,89 @@ public ToolInstallLocalCommand(
_toolManifestEditor = toolManifestEditor ?? new ToolManifestEditor();
_localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
_toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader);
+ _packageId = new PackageId(parseResult.GetValue(ToolUpdateCommandParser.PackageIdArgument));
+ _allowPackageDowngrade = parseResult.GetValue(ToolInstallCommandParser.AllowPackageDowngradeOption);
}
+
public override int Execute()
{
FilePath manifestFile = GetManifestFilePath();
- return Install(manifestFile);
+
+ (FilePath? manifestFileOptional, string warningMessage) =
+ _toolManifestFinder.ExplicitManifestOrFindManifestContainPackageId(_explicitManifestFile, _packageId);
+
+ if (warningMessage != null)
+ {
+ _reporter.WriteLine(warningMessage.Yellow());
+ }
+
+ manifestFile = manifestFileOptional ?? GetManifestFilePath();
+ var existingPackageWithPackageId = _toolManifestFinder.Find(manifestFile).Where(p => p.PackageId.Equals(_packageId));
+
+ if (!existingPackageWithPackageId.Any())
+ {
+ return InstallNewTool(manifestFile);
+ }
+
+ var existingPackage = existingPackageWithPackageId.Single();
+ var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile);
+
+ InstallToolUpdate(existingPackage, toolDownloadedPackage, manifestFile);
+
+ _localToolsResolverCache.SaveToolPackage(
+ toolDownloadedPackage,
+ _toolLocalPackageInstaller.TargetFrameworkToInstall);
+
+ return 0;
}
- public int Install(FilePath manifestFile)
+ public int InstallToolUpdate(ToolManifestPackage existingPackage, IToolPackage toolDownloadedPackage, FilePath manifestFile)
+ {
+ if (existingPackage.Version > toolDownloadedPackage.Version && !_allowPackageDowngrade)
+ {
+ throw new GracefulException(new[]
+ {
+ string.Format(
+ Update.LocalizableStrings.UpdateLocalToolToLowerVersion,
+ toolDownloadedPackage.Version.ToNormalizedString(),
+ existingPackage.Version.ToNormalizedString(),
+ manifestFile.Value)
+ },
+ isUserError: false);
+ }
+ else if (existingPackage.Version == toolDownloadedPackage.Version)
+ {
+ _reporter.WriteLine(
+ string.Format(
+ Update.LocalizableStrings.UpdateLocaToolSucceededVersionNoChange,
+ toolDownloadedPackage.Id,
+ existingPackage.Version.ToNormalizedString(),
+ manifestFile.Value));
+ }
+ else
+ {
+ _toolManifestEditor.Edit(
+ manifestFile,
+ _packageId,
+ toolDownloadedPackage.Version,
+ toolDownloadedPackage.Commands.Select(c => c.Name).ToArray());
+ _reporter.WriteLine(
+ string.Format(
+ Update.LocalizableStrings.UpdateLocalToolSucceeded,
+ toolDownloadedPackage.Id,
+ existingPackage.Version.ToNormalizedString(),
+ toolDownloadedPackage.Version.ToNormalizedString(),
+ manifestFile.Value).Green());
+ }
+
+ _localToolsResolverCache.SaveToolPackage(
+ toolDownloadedPackage,
+ _toolLocalPackageInstaller.TargetFrameworkToInstall);
+
+ return 0;
+ }
+ public int InstallNewTool(FilePath manifestFile)
{
IToolPackage toolDownloadedPackage =
_toolLocalPackageInstaller.Install(manifestFile);
@@ -78,7 +154,7 @@ public int Install(FilePath manifestFile)
return 0;
}
- private FilePath GetManifestFilePath()
+ public FilePath GetManifestFilePath()
{
try
{
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.cs.xlf
index 1073407eb811..43ef15efbca5 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.cs.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ PÅ™i instalaci balÃÄku nástroje .NET povolte downgrade balÃÄku.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
VytvoÅ™te manifest nástroje, pokud se nÄ›jaký nenajde bÄ›hem instalace nástroje. Informace o tom, jak se manifesty nacházejÃ, najdete v tématu https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.de.xlf
index 8439dc3b3908..e3ca99a38cec 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.de.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Paketdowngrade beim Installieren eines .NET-Toolpakets zulassen.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Erstellen Sie ein Toolmanifest, wenn es während der Toolinstallation nicht gefunden wird. Informationen dazu, wie Manifeste gefunden werden, finden Sie unter https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.es.xlf
index 1bea2d9d6a19..86a9057d5377 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.es.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Permitir la degradación del paquete al instalar un paquete de herramientas de .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Cree un manifiesto de herramienta si no se encuentra ninguno durante la instalación de la herramienta. Para obtener información sobre cómo se encuentran los manifiestos, consulte https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.fr.xlf
index 62b5b8ea09b7..cfdf3f4a5cfa 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.fr.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Autoriser le passage à une version antérieure du package lors de l’installation d’un package d’outils .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Créez un manifeste d'outil si aucun n'est trouvé lors de l'installation de l'outil. Pour plus d'informations sur la localisation des manifestes, voir https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.it.xlf
index 5b79eb906320..c5b0b0a52ee4 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.it.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Consente il downgrade del pacchetto durante l'installazione di un pacchetto di strumenti .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Creare un manifesto dello strumento se non ne viene trovato uno durante l'installazione dello strumento. Per informazioni sulla posizione dei manifesti, vedere https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ja.xlf
index dc9cbe468dfa..f398d692956f 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ja.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ .NET ツール パッケージã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«æ™‚ã«ãƒ‘ッケージã®ãƒ€ã‚¦ãƒ³ã‚°ãƒ¬ãƒ¼ãƒ‰ã‚’許å¯ã—ã¾ã™ã€‚
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
ツールã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸ã«ãƒ„ール マニフェスト見ã¤ã‹ã‚‰ãªã„å ´åˆã¯ä½œæˆã—ã¾ã™ã€‚マニフェストã®å ´æ‰€ã«ã¤ã„ã¦ã¯ã€https://aka.ms/dotnet/tools/create-manifest-if-needed ã‚’å‚ç…§ã—ã¦ãã ã•ã„
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ko.xlf
index 09523ab43bdc..91df7322d886 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ko.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ .NET ë„구 패키지를 ì„¤ì¹˜í• ë•Œ 패키지 ë‹¤ìš´ê·¸ë ˆì´ë“œë¥¼ 허용합니다.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
ë„구 설치 ì¤‘ì— ë„구 매니페스트를 ì°¾ì„ ìˆ˜ 없는 경우 ë„구 매니페스트를 만드세요. ë§¤ë‹ˆíŽ˜ìŠ¤íŠ¸ì˜ ìœ„ì¹˜ëŠ” https://aka.ms/dotnet/tools/create-manifest-if-needed를 참조하세요.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pl.xlf
index 76b632ec7f10..b5a1cef3c5bf 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pl.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Zezwalaj na obniżanie wersji pakietu podczas instalowania pakietu narzędzi .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Utwórz manifest narzędzi, jeśli nie zostanie znaleziony podczas instalacji narzędzia. Aby uzyskać informacje o lokalizacji manifestów, zobacz https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pt-BR.xlf
index 7392c799046c..21d7783b11d8 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.pt-BR.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Permitir downgrade de pacote ao instalar um pacote de ferramentas do .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Crie um manifesto de ferramenta se nenhum for encontrado durante a instalação da ferramenta. Para obter informações sobre como os manifestos estão localizados, consulte https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ru.xlf
index 94c8bd4d3f22..ad8741fc30a3 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.ru.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Разрешить переход на иÑпользование более ранней верÑии пакета при уÑтановке пакета инÑтрументов .NET.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Создайте манифеÑÑ‚ инÑтрумента, еÑли он не найден во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки инÑтрумента. Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ ÑпоÑобе Ñ€Ð°Ð·Ð¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¼Ð°Ð½Ð¸Ñ„ÐµÑтов Ñм. на Ñтранице https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.tr.xlf
index 6977fd232503..233a159082b4 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.tr.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ Bir .NET araç paketini yüklerken paketi eski sürüme düşürmeye izin verin.
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
Araç yüklemesi sırasında bir araç bildirimi bulunmazsa bir araç bildirimi oluşturun. Bildirimlerin nasıl bulunduğu hakkında bilgi edinmek için bkz. https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hans.xlf
index 1687123d06da..740148454acd 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hans.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ 安装 .NET 工具包时å…许包é™çº§ã€‚
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
如果在工具安装期间找ä¸åˆ°å·¥å…·æ¸…å•,请创建一个工具清å•。若è¦äº†è§£å¦‚何查找清å•,请å‚阅 https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hant.xlf
index 23b08414bf46..06cbc589d936 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/xlf/LocalizableStrings.zh-Hant.xlf
@@ -2,6 +2,11 @@
+
+ Allow package downgrade when installing a .NET tool package.
+ å®‰è£ .NET 工具套件時å…許套件é™ç´šã€‚
+
+
Create a tool manifest if one isn't found during tool installation. For information on how manifests are located, see https://aka.ms/dotnet/tools/create-manifest-if-needed
å¦‚æžœåœ¨å·¥å…·å®‰è£æœŸé–“找ä¸åˆ°å·¥å…·è³‡è¨Šæ¸…單,則建立工具資訊清單。如需如何尋找資訊清單的相關資訊,請åƒé–± https://aka.ms/dotnet/tools/create-manifest-if-needed
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-tool/update/LocalizableStrings.resx
index af3c3cf46ee8..52cbe1ac3ad2 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/LocalizableStrings.resx
@@ -183,10 +183,10 @@ and the corresponding package Ids for installed tools using the command
'dotnet tool list'.
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
Tool '{0}' failed to update due to the following:
@@ -218,10 +218,10 @@ and the corresponding package Ids for installed tools using the command
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
Tool '{0}' is up to date (version '{1}' manifest file {2}) .
-
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateCommandParser.cs b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateCommandParser.cs
index 37d1114ba0c2..4fda156ac3f7 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateCommandParser.cs
@@ -32,6 +32,8 @@ internal static class ToolUpdateCommandParser
public static readonly CliOption VerbosityOption = ToolInstallCommandParser.VerbosityOption;
+ public static readonly CliOption AllowPackageDowngradeOption = ToolInstallCommandParser.AllowPackageDowngradeOption;
+
private static readonly CliCommand Command = ConstructCommand();
public static CliCommand GetCommand()
@@ -43,21 +45,8 @@ private static CliCommand ConstructCommand()
{
CliCommand command = new("update", LocalizableStrings.CommandDescription);
- command.Arguments.Add(PackageIdArgument);
- command.Options.Add(GlobalOption.WithHelpDescription(command, LocalizableStrings.GlobalOptionDescription));
- command.Options.Add(ToolPathOption.WithHelpDescription(command, LocalizableStrings.ToolPathOptionDescription));
- command.Options.Add(LocalOption.WithHelpDescription(command, LocalizableStrings.LocalOptionDescription));
- command.Options.Add(ConfigOption);
- command.Options.Add(AddSourceOption);
- command.Options.Add(FrameworkOption);
- command.Options.Add(VersionOption);
- command.Options.Add(ToolManifestOption.WithHelpDescription(command, LocalizableStrings.ManifestPathOptionDescription));
- command.Options.Add(PrereleaseOption);
- command.Options.Add(ToolCommandRestorePassThroughOptions.DisableParallelOption);
- command.Options.Add(ToolCommandRestorePassThroughOptions.IgnoreFailedSourcesOption);
- command.Options.Add(ToolCommandRestorePassThroughOptions.NoCacheOption);
- command.Options.Add(ToolCommandRestorePassThroughOptions.InteractiveRestoreOption);
- command.Options.Add(VerbosityOption);
+ ToolInstallCommandParser.AddCommandOptions(command);
+ command.Options.Add(AllowPackageDowngradeOption);
command.SetAction((parseResult) => new ToolUpdateCommand(parseResult).Execute());
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateGlobalOrToolPathCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateGlobalOrToolPathCommand.cs
index 86a737cb6693..74c034b509c1 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateGlobalOrToolPathCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateGlobalOrToolPathCommand.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.CommandLine;
-using System.Transactions;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ShellShim;
@@ -10,33 +9,20 @@
using Microsoft.DotNet.Tools.Tool.Install;
using Microsoft.DotNet.Tools.Tool.Uninstall;
using Microsoft.Extensions.EnvironmentAbstractions;
-using NuGet.Versioning;
using Microsoft.DotNet.Cli.ToolPackage;
+using CreateShellShimRepository = Microsoft.DotNet.Tools.Tool.Install.CreateShellShimRepository;
namespace Microsoft.DotNet.Tools.Tool.Update
{
- internal delegate IShellShimRepository CreateShellShimRepository(string appHostSourceDirectory, DirectoryPath? nonGlobalLocation = null);
-
internal delegate (IToolPackageStore, IToolPackageStoreQuery, IToolPackageDownloader, IToolPackageUninstaller) CreateToolPackageStoresAndDownloaderAndUninstaller(
DirectoryPath? nonGlobalLocation = null,
IEnumerable additionalRestoreArguments = null);
internal class ToolUpdateGlobalOrToolPathCommand : CommandBase
{
- private readonly IReporter _reporter;
- private readonly IReporter _errorReporter;
private readonly CreateShellShimRepository _createShellShimRepository;
private readonly CreateToolPackageStoresAndDownloaderAndUninstaller _createToolPackageStoreDownloaderUninstaller;
-
- private readonly PackageId _packageId;
- private readonly string _configFilePath;
- private readonly string _framework;
- private readonly string[] _additionalFeeds;
- private readonly bool _global;
- private readonly VerbosityOptions _verbosity;
- private readonly string _toolPath;
- private readonly IEnumerable _forwardRestoreArguments;
- private readonly string _packageVersion;
+ private readonly ToolInstallGlobalOrToolPathCommand _toolInstallGlobalOrToolPathCommand;
public ToolUpdateGlobalOrToolPathCommand(ParseResult parseResult,
CreateToolPackageStoresAndDownloaderAndUninstaller createToolPackageStoreDownloaderUninstaller = null,
@@ -44,228 +30,24 @@ public ToolUpdateGlobalOrToolPathCommand(ParseResult parseResult,
IReporter reporter = null)
: base(parseResult)
{
- _packageId = new PackageId(parseResult.GetValue(ToolUninstallCommandParser.PackageIdArgument));
- _configFilePath = parseResult.GetValue(ToolUpdateCommandParser.ConfigOption);
- _framework = parseResult.GetValue(ToolUpdateCommandParser.FrameworkOption);
- _additionalFeeds = parseResult.GetValue(ToolUpdateCommandParser.AddSourceOption);
- _packageVersion = parseResult.GetValue(ToolUpdateCommandParser.VersionOption);
- _global = parseResult.GetValue(ToolUpdateCommandParser.GlobalOption);
- _verbosity = parseResult.GetValue(ToolUpdateCommandParser.VerbosityOption);
- _toolPath = parseResult.GetValue(ToolUpdateCommandParser.ToolPathOption);
- _forwardRestoreArguments = parseResult.OptionValuesToBeForwarded(ToolUpdateCommandParser.GetCommand());
-
_createToolPackageStoreDownloaderUninstaller = createToolPackageStoreDownloaderUninstaller ??
ToolPackageFactory.CreateToolPackageStoresAndDownloaderAndUninstaller;
_createShellShimRepository =
createShellShimRepository ?? ShellShimRepositoryFactory.CreateShellShimRepository;
- _reporter = (reporter ?? Reporter.Output);
- _errorReporter = (reporter ?? Reporter.Error);
+ _toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+
+ parseResult,
+ _createToolPackageStoreDownloaderUninstaller,
+ _createShellShimRepository,
+ reporter: reporter);
}
public override int Execute()
{
- ValidateArguments();
-
- DirectoryPath? toolPath = null;
- if (!string.IsNullOrEmpty(_toolPath))
- {
- toolPath = new DirectoryPath(_toolPath);
- }
-
- VersionRange versionRange = _parseResult.GetVersionRange();
-
- (IToolPackageStore toolPackageStore,
- IToolPackageStoreQuery toolPackageStoreQuery,
- IToolPackageDownloader toolPackageDownloader,
- IToolPackageUninstaller toolPackageUninstaller) = _createToolPackageStoreDownloaderUninstaller(toolPath, _forwardRestoreArguments);
-
- var appHostSourceDirectory = ShellShimTemplateFinder.GetDefaultAppHostSourceDirectory();
- IShellShimRepository shellShimRepository = _createShellShimRepository(appHostSourceDirectory, toolPath);
-
- IToolPackage oldPackageNullable = GetOldPackage(toolPackageStoreQuery);
-
- using (var scope = new TransactionScope(
- TransactionScopeOption.Required,
- TimeSpan.Zero))
- {
- if (oldPackageNullable != null)
- {
- RunWithHandlingUninstallError(() =>
- {
- foreach (RestoredCommand command in oldPackageNullable.Commands)
- {
- shellShimRepository.RemoveShim(command.Name);
- }
-
- toolPackageUninstaller.Uninstall(oldPackageNullable.PackageDirectory);
- });
- }
-
- RunWithHandlingInstallError(() =>
- {
- IToolPackage newInstalledPackage = toolPackageDownloader.InstallPackage(
- new PackageLocation(nugetConfig: GetConfigFile(), additionalFeeds: _additionalFeeds),
- packageId: _packageId,
- versionRange: versionRange,
- targetFramework: _framework,
- verbosity: _verbosity,
- isGlobalTool: true
- );
-
- EnsureVersionIsHigher(oldPackageNullable, newInstalledPackage);
-
- foreach (RestoredCommand command in newInstalledPackage.Commands)
- {
- shellShimRepository.CreateShim(command.Executable, command.Name, newInstalledPackage.PackagedShims);
- }
-
- PrintSuccessMessage(oldPackageNullable, newInstalledPackage);
- });
-
- scope.Complete();
- }
-
+ _toolInstallGlobalOrToolPathCommand.Execute();
return 0;
}
-
- private static void EnsureVersionIsHigher(IToolPackage oldPackageNullable, IToolPackage newInstalledPackage)
- {
- if (oldPackageNullable != null && (newInstalledPackage.Version < oldPackageNullable.Version))
- {
- throw new GracefulException(
- new[]
- {
- string.Format(LocalizableStrings.UpdateToLowerVersion,
- newInstalledPackage.Version.ToNormalizedString(),
- oldPackageNullable.Version.ToNormalizedString())
- },
- isUserError: false);
- }
- }
-
- private void ValidateArguments()
- {
- if (!string.IsNullOrEmpty(_configFilePath) && !File.Exists(_configFilePath))
- {
- throw new GracefulException(
- string.Format(
- LocalizableStrings.NuGetConfigurationFileDoesNotExist,
- Path.GetFullPath(_configFilePath)));
- }
- }
-
- private void RunWithHandlingInstallError(Action installAction)
- {
- try
- {
- installAction();
- }
- catch (Exception ex)
- when (InstallToolCommandLowLevelErrorConverter.ShouldConvertToUserFacingError(ex))
- {
- var message = new List
- {
- string.Format(LocalizableStrings.UpdateToolFailed, _packageId)
- };
- message.AddRange(
- InstallToolCommandLowLevelErrorConverter.GetUserFacingMessages(ex, _packageId));
-
-
- throw new GracefulException(
- messages: message,
- verboseMessages: new[] { ex.ToString() },
- isUserError: false);
- }
- }
-
- private void RunWithHandlingUninstallError(Action uninstallAction)
- {
- try
- {
- uninstallAction();
- }
- catch (Exception ex)
- when (ToolUninstallCommandLowLevelErrorConverter.ShouldConvertToUserFacingError(ex))
- {
- var message = new List
- {
- string.Format(LocalizableStrings.UpdateToolFailed, _packageId)
- };
- message.AddRange(
- ToolUninstallCommandLowLevelErrorConverter.GetUserFacingMessages(ex, _packageId));
-
- throw new GracefulException(
- messages: message,
- verboseMessages: new[] { ex.ToString() },
- isUserError: false);
- }
- }
-
- private FilePath? GetConfigFile()
- {
- FilePath? configFile = null;
- if (!string.IsNullOrEmpty(_configFilePath))
- {
- configFile = new FilePath(_configFilePath);
- }
-
- return configFile;
- }
-
- private IToolPackage GetOldPackage(IToolPackageStoreQuery toolPackageStoreQuery)
- {
- IToolPackage oldPackageNullable;
- try
- {
- oldPackageNullable = toolPackageStoreQuery.EnumeratePackageVersions(_packageId).SingleOrDefault();
- }
- catch (InvalidOperationException)
- {
- throw new GracefulException(
- messages: new[]
- {
- string.Format(
- LocalizableStrings.ToolHasMultipleVersionsInstalled,
- _packageId),
- },
- isUserError: false);
- }
-
- return oldPackageNullable;
- }
-
- private void PrintSuccessMessage(IToolPackage oldPackage, IToolPackage newInstalledPackage)
- {
- if (oldPackage == null)
- {
- _reporter.WriteLine(
- string.Format(
- Install.LocalizableStrings.InstallationSucceeded,
- string.Join(", ", newInstalledPackage.Commands.Select(c => c.Name)),
- newInstalledPackage.Id,
- newInstalledPackage.Version.ToNormalizedString()).Green());
- }
- else if (oldPackage.Version != newInstalledPackage.Version)
- {
- _reporter.WriteLine(
- string.Format(
- LocalizableStrings.UpdateSucceeded,
- newInstalledPackage.Id,
- oldPackage.Version.ToNormalizedString(),
- newInstalledPackage.Version.ToNormalizedString()).Green());
- }
- else
- {
- _reporter.WriteLine(
- string.Format(
- (
- newInstalledPackage.Version.IsPrerelease ?
- LocalizableStrings.UpdateSucceededPreVersionNoChange : LocalizableStrings.UpdateSucceededStableVersionNoChange
- ),
- newInstalledPackage.Id, newInstalledPackage.Version).Green());
- }
- }
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateLocalCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateLocalCommand.cs
index 8118adee5229..5a3f232865b1 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateLocalCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/ToolUpdateLocalCommand.cs
@@ -19,13 +19,9 @@ internal class ToolUpdateLocalCommand : CommandBase
private readonly IToolManifestEditor _toolManifestEditor;
private readonly ILocalToolsResolverCache _localToolsResolverCache;
private readonly IToolPackageDownloader _toolPackageDownloader;
- private readonly ToolInstallLocalInstaller _toolLocalPackageInstaller;
private readonly Lazy _toolInstallLocalCommand;
private readonly IReporter _reporter;
- private readonly PackageId _packageId;
- private readonly string _explicitManifestFile;
-
public ToolUpdateLocalCommand(
ParseResult parseResult,
IToolPackageDownloader toolPackageDownloader = null,
@@ -35,9 +31,6 @@ public ToolUpdateLocalCommand(
IReporter reporter = null)
: base(parseResult)
{
- _packageId = new PackageId(parseResult.GetValue(ToolUpdateCommandParser.PackageIdArgument));
- _explicitManifestFile = parseResult.GetValue(ToolUpdateCommandParser.ToolManifestOption);
-
_reporter = (reporter ?? Reporter.Output);
if (toolPackageDownloader == null)
@@ -59,7 +52,6 @@ public ToolUpdateLocalCommand(
_toolManifestEditor = toolManifestEditor ?? new ToolManifestEditor();
_localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
- _toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader);
_toolInstallLocalCommand = new Lazy(
() => new ToolInstallLocalCommand(
parseResult,
@@ -72,73 +64,7 @@ public ToolUpdateLocalCommand(
public override int Execute()
{
- (FilePath? manifestFileOptional, string warningMessage) =
- _toolManifestFinder.ExplicitManifestOrFindManifestContainPackageId(_explicitManifestFile, _packageId);
-
- var manifestFile = manifestFileOptional ?? _toolManifestFinder.FindFirst();
-
- var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile);
- var existingPackageWithPackageId =
- _toolManifestFinder
- .Find(manifestFile)
- .Where(p => p.PackageId.Equals(_packageId));
-
- if (!existingPackageWithPackageId.Any())
- {
- return _toolInstallLocalCommand.Value.Install(manifestFile);
- }
-
- var existingPackage = existingPackageWithPackageId.Single();
- if (existingPackage.Version > toolDownloadedPackage.Version)
- {
- throw new GracefulException(new[]
- {
- string.Format(
- LocalizableStrings.UpdateLocaToolToLowerVersion,
- toolDownloadedPackage.Version.ToNormalizedString(),
- existingPackage.Version.ToNormalizedString(),
- manifestFile.Value)
- },
- isUserError: false);
- }
-
- if (existingPackage.Version != toolDownloadedPackage.Version)
- {
- _toolManifestEditor.Edit(
- manifestFile,
- _packageId,
- toolDownloadedPackage.Version,
- toolDownloadedPackage.Commands.Select(c => c.Name).ToArray());
- }
-
- _localToolsResolverCache.SaveToolPackage(
- toolDownloadedPackage,
- _toolLocalPackageInstaller.TargetFrameworkToInstall);
-
- if (warningMessage != null)
- {
- _reporter.WriteLine(warningMessage.Yellow());
- }
-
- if (existingPackage.Version == toolDownloadedPackage.Version)
- {
- _reporter.WriteLine(
- string.Format(
- LocalizableStrings.UpdateLocaToolSucceededVersionNoChange,
- toolDownloadedPackage.Id,
- existingPackage.Version.ToNormalizedString(),
- manifestFile.Value));
- }
- else
- {
- _reporter.WriteLine(
- string.Format(
- LocalizableStrings.UpdateLocalToolSucceeded,
- toolDownloadedPackage.Id,
- existingPackage.Version.ToNormalizedString(),
- toolDownloadedPackage.Version.ToNormalizedString(),
- manifestFile.Value).Green());
- }
+ _toolInstallLocalCommand.Value.Execute();
return 0;
}
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.cs.xlf
index 49017f3ead99..60cc43f41a94 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.cs.xlf
@@ -42,24 +42,24 @@
Nástroj {0} je aktuálnà (verze {1}, soubor manifestu {2}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- Požadovaná verze {0} je nižšà než stávajÃcà verze {1} (soubor manifestu {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
Nástroj {0} byl úspěšně aktualizován z verze {1} na verzi {2} (soubor manifestu {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ Požadovaná verze {0} je nižšà než stávajÃcà verze {1} (soubor manifestu {2}). Pokud chcete povolit tuto aktualizaci, použijte možnost --allow-downgrade.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- Nástroj{0}byl znovu nainstalován s nejnovějšà předběžnou verzà (verze{1}).
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ Nástroj {0} se přeinstaloval na předběžnou verzi (verze {1}).
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- Nástroj {0} byl přeinstalován nejnovějšà stabilnà verzà (verze {1}).
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ Nástroj {0} se přeinstaloval na stabilnà verzi (verze {1}).
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.de.xlf
index 36cf57b95ead..b93c1827fbb7 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.de.xlf
@@ -42,24 +42,24 @@
Das Tool "{0}" ist auf dem neuesten Stand (Version {1}, Manifestdatei "{2}").
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- Die angeforderte Version {0} ist niedriger als die vorhandene Version {1} (Manifestdatei "{2}").
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
Das Tool "{0}" wurde erfolgreich von Version {1} auf Version {2} aktualisiert (Manifestdatei "{3}").
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ Die angeforderte Version {0} ist niedriger als die vorhandene Version {1} (Manifestdatei {2}). Verwenden Sie die Option --Downgrade zulassen, um dieses Update zuzulassen.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- Das Tool „{0}“ wurde in der neuesten Vorabversion neu installiert (Version „{1}“).
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ Das Tool "{0}" wurde mit der Vorabversion (Version "{1}" ) neu installiert.
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- Das Tool "{0}" wurde in der neuesten stabilen Version neu installiert (Version {1}).
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ Das Tool "{0}" wurde mit der stabilen Version (Version "{1}" ) neu installiert.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.es.xlf
index e57a011b7607..f8fe5b7b2cfb 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.es.xlf
@@ -42,23 +42,23 @@
La herramienta "{0}" está actualizada (versión "{1}", archivo de manifiesto {2}) .
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- La versión solicitada {0} es anterior a la versión existente {1} (archivo de manifiesto {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
La herramienta "{0}" se actualizó correctamente de la versión "{1}" a la versión "{2}" (archivo de manifiesto {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ La versión solicitada {0} es anterior a la versión existente {1} (archivo de manifiesto {2}). Use la opción --allow-downgrade para permitir esta actualización.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
La herramienta "{0}" se ha reinstalado con la versión preliminar más reciente (versión "{1}").
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
La herramienta "{0}" se reinstaló con la versión estable más reciente (versión "{1}").
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.fr.xlf
index 4bf16511cbb6..9b4433a9f6fb 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.fr.xlf
@@ -42,24 +42,24 @@
L'outil '{0}' est à jour (version '{1}', fichier manifeste {2}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- La version demandée {0} est inférieure à la version existante {1} (fichier manifeste {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
L'outil '{0}' a été correctement mis à jour de la version '{1}' à la version '{2}' (fichier manifeste {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ La version demandée {0} est inférieure à la version existante{1} (fichier manifeste {2}). Utilisez l’option --allow-downgrade pour autoriser cette mise à jour.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- L'outil '{0}' a été réinstallé avec la dernière version préliminaire (version '{1}').
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ L'outil « {0} » a été réinstallé avec la version préliminaire (version « {1} »).
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- L'outil '{0}' a été réinstallé avec la dernière version stable (version '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ L'outil « {0} » a été réinstallé avec la version stable (version « {1} »).
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.it.xlf
index 1b64464c6117..8f657409de82 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.it.xlf
@@ -42,24 +42,24 @@
Lo strumento '{0}' è aggiornato (file manifesto {2} versione '{1}').
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- La versione richiesta {0} è inferiore a quella esistente {1} (file manifesto {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
Lo strumento '{0}' è stato aggiornato dalla versione '{1}' alla versione '{2}' (file manifesto {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ La versione richiesta {0} è inferiore a quella esistente {1} (file manifesto {2}). Usare l'opzione --allow-downgrade per consentire questo aggiornamento.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- Lo strumento '{0}' è stato reinstallato con l'ultima versione preliminare (versione '{1}').
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ Lo strumento '{0}' è stato reinstallato con la versione preliminare (versione '{1}').
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- Lo strumento '{0}' è stato reinstallato con l'ultima versione stabile (versione '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ Lo strumento '{0}' è stato reinstallato con la versione stabile (versione '{1}').
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ja.xlf
index 3d7d560ea2a2..97851054ddaa 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ja.xlf
@@ -42,24 +42,24 @@
ツール '{0}' ã¯æœ€æ–°ã®çŠ¶æ…‹ã§ã™ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}' マニフェスト ファイル {2})。
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- è¦æ±‚ã•れãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ {0} ã¯ã€æ—¢å˜ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {1} (マニフェスト ファイル {2}) よりも低ããªã£ã¦ã„ã¾ã™ã€‚
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
ツール '{0}' ãŒãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}' ã‹ã‚‰ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{2}' (マニフェストファイル {3}) ã«æ£å¸¸ã«æ›´æ–°ã•れã¾ã—ãŸã€‚
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ è¦æ±‚ã•れãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ {0} ã¯ã€æ—¢å˜ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {1} (マニフェスト ファイル {2}) よりも低ããªã£ã¦ã„ã¾ã™ã€‚ã“ã®æ›´æ–°ã‚’許å¯ã™ã‚‹ã«ã¯ã€--allow-downgrade オプションを使用ã—ã¾ã™ã€‚
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- ツール '{0}' ã¯ã€æœ€æ–°ã®ãƒ—レリリース ãƒãƒ¼ã‚¸ãƒ§ãƒ³ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}') ã§å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸã€‚
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ ツール '{0}' ãŒãƒ—レリリース ãƒãƒ¼ã‚¸ãƒ§ãƒ³ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}') ã§å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸã€‚
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- ツール '{0}' ãŒå®‰å®šã—ãŸæœ€æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}') ã§å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸã€‚
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ ツール '{0}' ãŒå®‰å®šã—ãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '{1}') ã§å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸã€‚
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ko.xlf
index e89364423fa2..54b7678313fd 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ko.xlf
@@ -42,24 +42,24 @@
'{0}' ë„구는 ìµœì‹ ë²„ì „(ë²„ì „ '{1}' 매니페스트 íŒŒì¼ {2})입니다.
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- ìš”ì²ëœ ë²„ì „ {0}ì´(ê°€) 기존 ë²„ì „ {1}(매니페스트 íŒŒì¼ {2})보다 낮습니다.
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
'{0}' ë„구가 '{1}' ë²„ì „ì—서 '{2}' ë²„ì „(매니페스트 íŒŒì¼ {3})으로 ì—…ë°ì´íЏë˜ì—ˆìŠµë‹ˆë‹¤.
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ ìš”ì²ëœ {0} ë²„ì „ì´ ê¸°ì¡´ {1} ë²„ì „(매니페스트 íŒŒì¼ {2})보다 낮습니다. --allow-downgrade ì˜µì…˜ì„ ì‚¬ìš©í•˜ì—¬ ì´ ì—…ë°ì´íŠ¸ë¥¼ 허용하세요.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- ë„구 '{0}'ì´(ê°€) ìµœì‹ ì‹œí—˜íŒ ë²„ì „(ë²„ì „ '{1}')으로 다시 설치ë˜ì—ˆìŠµë‹ˆë‹¤.
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ '{0}' ë„구가 ì‹œí—˜íŒ ë²„ì „('{1}' ë²„ì „)으로 다시 설치ë˜ì—ˆìŠµë‹ˆë‹¤.
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- '{0}' ë„구가 ì•ˆì •ì ì¸ ìµœì‹ ë²„ì „('{1}' ë²„ì „)으로 다시 설치ë˜ì—ˆìŠµë‹ˆë‹¤.
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ '{0}' ë„구가 ì•ˆì •í™” ë²„ì „('{1}' ë²„ì „)으로 다시 설치ë˜ì—ˆìŠµë‹ˆë‹¤.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pl.xlf
index d9ebce6c0772..78b6bfddfbcb 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pl.xlf
@@ -42,23 +42,23 @@
NarzÄ™dzie „{0}†jest aktualne (wersja „{1}â€, plik manifestu {2}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- Żądana wersja {0} jest niższa niż obecna wersja {1} (plik manifestu {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
Narzędzie „{0}†zostało pomyślnie zaktualizowane z wersji „{1}†do wersji „{2}†(plik manifestu {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ Żądana wersja {0} jest niższa niż obecna wersja {1} (plik manifestu {2}). Użyj opcji --allow-downgrade (zezwól na zmianę na starszą lub mniej zaawansowaną wersję), aby zezwolić na tę aktualizację.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- NarzÄ™dzie „{0}†zostaÅ‚o ponownie zainstalowane przy użyciu najnowszej stabilnej wersji (wersja „{1}â€).
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ NarzÄ™dzie „{0}†zostaÅ‚o ponownie zainstalowane z wersjÄ… wstÄ™pnÄ… (wersja „{1}â€).
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
NarzÄ™dzie „{0}†zostaÅ‚o ponownie zainstalowane przy użyciu najnowszej stabilnej wersji (wersja „{1}â€).
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pt-BR.xlf
index 88f0816772ae..adc7d93e0036 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.pt-BR.xlf
@@ -42,24 +42,24 @@
A ferramenta '{0}' está atualizada (versão '{1}' arquivo de manifesto {2}) .
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- A versão solicitada {0} é inferior à versão existente {1} (arquivo de manifesto {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
A ferramenta '{0}' foi atualizada com êxito da versão '{1}' para a versão '{2}' (arquivo de manifesto {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ A versão {0} solicitada é inferior à versão {1} existente (arquivo de manifesto {2}). Use a opção --allow-downgrade para permitir essa atualização.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- A ferramenta '{0}' foi reinstalada com a versão de pré-lançamento mais recente (versão '{1}').
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ A ferramenta "{0}" foi reinstalada com a versão de pré-lançamento mais recente (versão "{1}").
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- A ferramenta '{0}' foi reinstalada com a versão estável mais recente (versão '{1}').
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ A ferramenta "{0}" foi reinstalada com a versão estável mais recente (versão "{1}").
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ru.xlf
index 46bf064da5cd..3360bfae0ad8 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.ru.xlf
@@ -42,24 +42,24 @@
СредÑтво "{0}" обновлено (верÑÐ¸Ñ "{1}", файл манифеÑта {2}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ {0} ниже ÑущеÑтвующей верÑии {1} (файл манифеÑта {2}).
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
СредÑтво "{0}" обновлено Ñ Ð²ÐµÑ€Ñии "{1}" до верÑии "{2}" (файл манифеÑта {3}).
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ {0} ниже ÑущеÑтвующей верÑии {1} (файл манифеÑта {2}). Чтобы разрешить Ñто обновление, иÑпользуйте параметр --allow-downgrade.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- ИнÑтрумент "{0}" был переуÑтановлен Ñ Ð¿Ð¾Ñледней предварительной верÑией (верÑией "{1}").
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ ИнÑтрумент "{0}" был переуÑтановлен. УÑтановлена Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ (верÑÐ¸Ñ "{1}").
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- ИнÑтрумент "{0}" был переуÑтановлен Ñ Ð¿Ð¾Ñледней Ñтабильной верÑией (верÑией "{1}").
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ ИнÑтрумент "{0}" был переуÑтановлен. УÑтановлена ÑÑ‚Ð°Ð±Ð¸Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ (верÑÐ¸Ñ "{1}").
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.tr.xlf
index 9ae1020925b0..75e7380a661b 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.tr.xlf
@@ -42,24 +42,24 @@
'{0}' aracı güncel (sürüm '{1}' bildirim dosyası {2}).
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- İstenen sürüm ({0}) mevcut sürümünden ({1}) (bildirim dosyası {2}) düşük.
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
'{0}' aracı, '{1}' sürümünden '{2}' (bildirim dosyası {3}) sürümüne başarıyla güncelleştirildi.
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ İstenen {0} sürümü mevcut {1} sürümünden ({2}bildirim dosyası) düşük. Bu güncelleştirmeye izin vermek için --allow-downgrade seçeneğini kullanın.
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- '{0}' aracı, en yeni ön sürüm (sürüm '{1}') ile yeniden yüklendi.
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ '{0}' aracı, ön sürüm (sürüm '{1}') ile yeniden yüklendi.
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- '{0}' aracı, en son kararlı sürüm (sürüm '{1}') ile yeniden yüklendi.
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ '{0}' aracı, kararlı sürüm (sürüm '{1}') ile yeniden yüklendi.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hans.xlf
index a7264b53a603..0580fb08f0b6 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hans.xlf
@@ -42,24 +42,24 @@
工具“{0}â€æ˜¯æœ€æ–°çš„(版本“{1}â€æ¸…啿–‡ä»¶ {2})。
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- 请求的版本 {0} 低于现有版本 {1} (æ¸…å•æ–‡ä»¶{2})。
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
工具“{0}â€å·²æˆåŠŸä»Žç‰ˆæœ¬â€œ{1}â€æ›´æ–°åˆ°ç‰ˆæœ¬â€œ{2}â€(æ¸…å•æ–‡ä»¶ {3})。
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ 请求的版本 {0} 低于现有版本 {1} (æ¸…å•æ–‡ä»¶ {2})。使用 --allow-downgrade 选项å…è®¸æ¤æ›´æ–°ã€‚
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- 工具“{0}â€å·²é‡æ–°å®‰è£…最新预å‘行版本(版本“{1}â€)。
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ 工具“{0}â€å·²é‡æ–°å®‰è£…预å‘行版本(版本“{1}â€)。
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- 工具“{0}â€å·²é‡æ–°å®‰è£…最新稳定版本(版本“{1}â€)。
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ 工具“{0}â€å·²é‡æ–°å®‰è£…稳定版本(版本“{1}â€)。
diff --git a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hant.xlf
index 24f4b489a06f..8deb59eb6be0 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/update/xlf/LocalizableStrings.zh-Hant.xlf
@@ -42,24 +42,24 @@
工具 '{0}' 為最新 (版本 '{1}' 資訊清單檔 {2})。
-
- The requested version {0} is lower than existing version {1} (manifest file {2}).
- è¦æ±‚的版本 {0} ä½Žæ–¼ç¾æœ‰ç‰ˆæœ¬ {1} (資訊清單檔 {2})。
-
-
Tool '{0}' was successfully updated from version '{1}' to version '{2}' (manifest file {3}).
å·²æˆåŠŸå°‡å·¥å…· '{0}' 從 '{1}' 版更新為 '{2}' 版 (資訊清單檔 {3})。
+
+ The requested version {0} is lower than existing version {1} (manifest file {2}). Use the --allow-downgrade option to allow this update.
+ è¦æ±‚的版本 {0} ä½Žæ–¼ç¾æœ‰ç‰ˆæœ¬ {1} (資訊清單檔 {2})。使用 --allow-downgrade é¸é …以å…è¨±æ¤æ›´æ–°ã€‚
+
+
- Tool '{0}' was reinstalled with the latest prerelease version (version '{1}').
- 已使用最新æ¶é®®ç‰ˆ ('{0}' 版) 來釿–°å®‰è£å·¥å…· '{1}'。
+ Tool '{0}' was reinstalled with the prerelease version (version '{1}').
+ 已使用æ¶é®®ç‰ˆæœ¬ (版本 '{1}') 來釿–°å®‰è£å·¥å…· '{0}'。
- Tool '{0}' was reinstalled with the latest stable version (version '{1}').
- 已使用最新穩定版本 ('{1}' 版) 來釿–°å®‰è£å·¥å…· '{0}'。
+ Tool '{0}' was reinstalled with the stable version (version '{1}').
+ 已使用穩定版本 (版本 '{1}') 來釿–°å®‰è£å·¥å…· '{0}'。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs b/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs
new file mode 100644
index 000000000000..d6fcc23bf654
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs
@@ -0,0 +1,33 @@
+// 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.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.DotNet.Workloads.Workload
+{
+ internal class InstallStateContents
+ {
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public bool? UseWorkloadSets { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public Dictionary Manifests { get; set; }
+
+ private static readonly JsonSerializerOptions s_options = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true,
+ };
+
+ public static InstallStateContents FromString(string contents)
+ {
+ return JsonSerializer.Deserialize(contents, s_options);
+ }
+
+ public override string ToString()
+ {
+ return JsonSerializer.Serialize(this, s_options);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
index a66915b36625..58a178422aea 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandParser.cs
@@ -47,7 +47,7 @@ internal static string GetWorkloadsVersion(WorkloadInfoHelper workloadInfoHelper
return workloadInfoHelper.ManifestProvider.GetWorkloadVersion();
}
- internal static void ShowWorkloadsInfo(ParseResult parseResult = null, WorkloadInfoHelper workloadInfoHelper = null, IReporter reporter = null, string dotnetDir = null)
+ internal static void ShowWorkloadsInfo(ParseResult parseResult = null, WorkloadInfoHelper workloadInfoHelper = null, IReporter reporter = null, string dotnetDir = null, bool showVersion = true)
{
workloadInfoHelper ??= new WorkloadInfoHelper(parseResult != null ? parseResult.HasOption(SharedOptions.InteractiveOption) : false);
IEnumerable installedList = workloadInfoHelper.InstalledSdkWorkloadIds;
@@ -55,7 +55,10 @@ internal static void ShowWorkloadsInfo(ParseResult parseResult = null, WorkloadI
reporter ??= Cli.Utils.Reporter.Output;
string dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath);
- reporter.WriteLine($" Workload version: {workloadInfoHelper.ManifestProvider.GetWorkloadVersion()}");
+ if (showVersion)
+ {
+ reporter.WriteLine($" Workload version: {workloadInfoHelper.ManifestProvider.GetWorkloadVersion()}");
+ }
if (installedWorkloads.Count == 0)
{
diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs
index a8ee40187373..f686d494a53d 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs
@@ -56,7 +56,8 @@ public WorkloadInfoHelper(
userProfileDir,
verifySignatures ?? !SignCheck.IsDotNetSigned(),
restoreActionConfig: restoreConfig,
- elevationRequired: false);
+ elevationRequired: false,
+ shouldLog: false);
WorkloadRecordRepo = workloadRecordRepo ?? Installer.GetWorkloadInstallationRecordRepository();
}
@@ -66,8 +67,7 @@ public WorkloadInfoHelper(
public IWorkloadInstallationRecordRepository WorkloadRecordRepo { get; private init; }
public IWorkloadResolver WorkloadResolver { get; private init; }
- public IEnumerable InstalledSdkWorkloadIds =>
- WorkloadRecordRepo.GetInstalledWorkloads(_currentSdkFeatureBand);
+ public IEnumerable InstalledSdkWorkloadIds => WorkloadRecordRepo.GetInstalledWorkloads(_currentSdkFeatureBand);
public InstalledWorkloadsCollection AddInstalledVsWorkloads(IEnumerable sdkWorkloadIds)
{
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
index 22b073f6aa80..08cd93199320 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text.Json;
-using Microsoft.Deployment.DotNet.Releases;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
@@ -453,6 +452,36 @@ public void GarbageCollect(Func getResolverForWorkloa
}
+ public void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");
+
+ if (File.Exists(path))
+ {
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.Manifests = null;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
+ }
+
+ public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary manifestContents)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.Manifests = manifestContents;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
+
+ public void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, _dotnetDir), "default.json");
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.UseWorkloadSets = newMode;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
+
///
/// Remove all workload installation records that aren't from Visual Studio.
///
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
index 9415fa72f1de..5d388ef8bd21 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
@@ -33,6 +33,21 @@ internal interface IInstaller : IWorkloadManifestInstaller
void ReplaceWorkloadResolver(IWorkloadResolver workloadResolver);
void Shutdown();
+
+ ///
+ /// Delete the install state file at the specified path.
+ ///
+ /// The SDK feature band of the install state file.
+ void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand);
+
+ ///
+ /// Writes the specified JSON contents to the install state file.
+ ///
+ /// The SDK feature band of the install state file.
+ /// The JSON contents describing the install state.
+ void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary manifestContents);
+
+ void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode);
}
// Interface to pass to workload manifest updater
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs
index 87957062092f..d9dec221ad29 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs
@@ -12,13 +12,9 @@ internal interface IWorkloadManifestUpdater
Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync();
- IEnumerable<(
- ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads
- )> CalculateManifestUpdates();
+ IEnumerable CalculateManifestUpdates();
- IEnumerable
- CalculateManifestRollbacks(string rollbackDefinitionFilePath);
+ IEnumerable CalculateManifestRollbacks(string rollbackDefinitionFilePath);
Task> GetManifestPackageDownloadsAsync(bool includePreviews, SdkFeatureBand providedSdkFeatureBand, SdkFeatureBand installedSdkFeatureBand);
@@ -26,4 +22,6 @@ Dictionary Workloads
void DeleteUpdatableWorkloadsFile();
}
+
+ internal record ManifestUpdateWithWorkloads(ManifestVersionUpdate ManifestUpdate, WorkloadCollection Workloads);
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
index 08f453f48894..b9cd5af6b5dd 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
@@ -183,6 +183,9 @@
Only print the list of links to download without downloading.
+
+ Control whether future workload operations should use workload sets or loose manifests.
+
Download packages needed to install a workload to a folder that can be used for offline installation.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs b/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs
index e33f8a56668a..60d086bb4d1a 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs
@@ -1,9 +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.Diagnostics;
using System.Runtime.Versioning;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Workloads.Workload;
+using Microsoft.DotNet.Workloads.Workload.Install;
using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord;
using Microsoft.NET.Sdk.WorkloadManifestReader;
using Microsoft.Win32;
@@ -26,6 +29,11 @@ internal abstract class MsiInstallerBase : InstallerBase
///
private string _dotNetHome;
+ ///
+ /// Full path to the root directory for storing workload data.
+ ///
+ public static readonly string WorkloadDataRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "dotnet", "workloads");
+
///
/// Default reinstall mode (equivalent to VOMUS).
///
@@ -267,6 +275,40 @@ protected uint RepairMsi(string productCode, string logFile)
throw new InvalidOperationException($"Invalid configuration: elevated: {IsElevated}, client: {IsClient}");
}
+ ///
+ /// Instructs future workload operations to use workload sets or loose manifests, per newMode.
+ ///
+ /// The feature band to update
+ /// Where to use loose manifests or workload sets
+ /// Full path of the log file
+ /// Error code indicating the result of the operation
+ ///
+ protected void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode)
+ {
+ Elevate();
+
+ if (IsElevated)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, DotNetHome), "default.json");
+ // Create the parent folder for the state file and set up all required ACLs
+ SecurityUtils.CreateSecureDirectory(Path.GetDirectoryName(path));
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.UseWorkloadSets = newMode;
+ File.WriteAllText(path, installStateContents.ToString());
+
+ SecurityUtils.SecureFile(path);
+ }
+ else if (IsClient)
+ {
+ InstallResponseMessage response = Dispatcher.SendUpdateWorkloadModeRequest(sdkFeatureBand, newMode);
+ ExitOnFailure(response, "Failed to update install mode.");
+ }
+ else
+ {
+ throw new InvalidOperationException($"Invalid configuration: elevated: {IsElevated}, client: {IsClient}");
+ }
+ }
+
///
/// Installs the specified MSI.
///
@@ -487,5 +529,65 @@ protected void UpdateDependent(InstallRequestType requestType, string providerKe
ExitOnFailure(response, $"Failed to update dependent, providerKey: {providerKeyName}, dependent: {dependent}.");
}
}
+
+ ///
+ /// Deletes manifests from the install state file for the specified feature band.
+ ///
+ /// The feature band of the install state file.
+ public void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, DotNetHome), "default.json");
+
+ if (!File.Exists(path))
+ {
+ Log?.LogMessage($"Install state file does not exist: {path}");
+ return;
+ }
+
+ Elevate();
+
+ if (IsElevated)
+ {
+ if (File.Exists(path))
+ {
+ var installStateContents = InstallStateContents.FromString(File.ReadAllText(path));
+ installStateContents.Manifests = null;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
+ }
+ else if (IsClient)
+ {
+ InstallResponseMessage response = Dispatcher.SendRemoveManifestsFromInstallStateFileRequest(sdkFeatureBand);
+ ExitOnFailure(response, $"Failed to remove install state file: {path}");
+ }
+ }
+
+ ///
+ /// Writes the contents of the install state JSON file.
+ ///
+ /// The path of the isntall state file to write.
+ /// The contents of the JSON file, formatted as a single line.
+ public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary manifestContents)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, DotNetHome), "default.json");
+ Elevate();
+
+ if (IsElevated)
+ {
+ // Create the parent folder for the state file and set up all required ACLs
+ SecurityUtils.CreateSecureDirectory(Path.GetDirectoryName(path));
+
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.Manifests = manifestContents;
+ File.WriteAllText(path, installStateContents.ToString());
+
+ SecurityUtils.SecureFile(path);
+ }
+ else if (IsClient)
+ {
+ InstallResponseMessage respone = Dispatcher.SendSaveInstallStateManifestVersions(sdkFeatureBand, manifestContents);
+ ExitOnFailure(respone, $"Failed to write install state file: {path}");
+ }
+ }
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
index a7629da7ac62..6ff3b44635d5 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
@@ -1019,9 +1019,12 @@ public static NetSdkMsiInstallerClient Create(
PackageSourceLocation packageSourceLocation = null,
IReporter reporter = null,
string tempDirPath = null,
- RestoreActionConfig restoreActionConfig = null)
+ RestoreActionConfig restoreActionConfig = null,
+ bool shouldLog = true)
{
- TimestampedFileLogger logger = new(Path.Combine(Path.GetTempPath(), $"Microsoft.NET.Workload_{Environment.ProcessId}_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"));
+ ISynchronizingLogger logger =
+ shouldLog ? new TimestampedFileLogger(Path.Combine(Path.GetTempPath(), $"Microsoft.NET.Workload_{Environment.ProcessId}_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"))
+ : new NullInstallerLogger();
InstallClientElevationContext elevationContext = new(logger);
if (nugetPackageDownloader == null)
@@ -1064,9 +1067,14 @@ private void OnProcessExit(object sender, EventArgs e)
}
finally
{
- ((TimestampedFileLogger)Log).Dispose();
+ if (Log is IDisposable tfl)
+ {
+ tfl.Dispose();
+ }
}
}
}
+
+ void IInstaller.UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode) => UpdateInstallMode(sdkFeatureBand, newMode);
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs
index 06e55b87c783..c62dd4b36096 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs
@@ -93,6 +93,22 @@ public void Run()
Dispatcher.ReplySuccess($"Updated dependent '{request.Dependent}' for provider key '{request.ProviderKeyName}'");
break;
+ case InstallRequestType.SaveInstallStateManifestVersions:
+ SaveInstallStateManifestVersions(new SdkFeatureBand(request.SdkFeatureBand), request.InstallStateManifestVersions);
+ Dispatcher.ReplySuccess($"Created install state file for {request.SdkFeatureBand}.");
+ break;
+
+ case InstallRequestType.RemoveManifestsFromInstallStateFile:
+ RemoveManifestsFromInstallState(new SdkFeatureBand(request.SdkFeatureBand));
+ Dispatcher.ReplySuccess($"Deleted install state file for {request.SdkFeatureBand}.");
+ break;
+
+ case InstallRequestType.AdjustWorkloadMode:
+ UpdateInstallMode(new SdkFeatureBand(request.SdkFeatureBand), request.UseWorkloadSets);
+ string newMode = request.ProductCode.Equals("true", StringComparison.OrdinalIgnoreCase) ? "workload sets" : "loose manifests";
+ Dispatcher.ReplySuccess($"Updated install mode to use {newMode}.");
+ break;
+
default:
throw new InvalidOperationException($"Unknown message request: {(int)request.RequestType}");
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
index 11689ddcc858..97a63aea9d47 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
@@ -40,7 +40,7 @@ public WorkloadInstallCommand(
_workloadResolver, Verbosity, _userProfileDir, VerifySignatures, PackageDownloader, _dotnetPath, TempDirectoryPath,
_packageSourceLocation, RestoreActionConfiguration, elevationRequired: !_printDownloadLinkOnly && string.IsNullOrWhiteSpace(_downloadToCacheOption));
- _workloadManifestUpdater = _workloadManifestUpdaterFromConstructor ?? new WorkloadManifestUpdater(Reporter, _workloadResolver, PackageDownloader, _userProfileDir, TempDirectoryPath,
+ _workloadManifestUpdater = _workloadManifestUpdaterFromConstructor ?? new WorkloadManifestUpdater(Reporter, _workloadResolver, PackageDownloader, _userProfileDir,
_workloadInstaller.GetWorkloadInstallationRecordRepository(), _workloadInstaller, _packageSourceLocation, displayManifestUpdates: Verbosity.IsDetailedOrDiagnostic());
ValidateWorkloadIdsInput();
@@ -99,7 +99,7 @@ public override int Execute()
}
else if (_skipManifestUpdate && usedRollback)
{
- throw new GracefulException(string.Format(LocalizableStrings.CannotCombineSkipManifestAndRollback,
+ throw new GracefulException(string.Format(LocalizableStrings.CannotCombineSkipManifestAndRollback,
WorkloadInstallCommandParser.SkipManifestUpdateOption.Name, InstallingWorkloadCommandParser.FromRollbackFileOption.Name,
WorkloadInstallCommandParser.SkipManifestUpdateOption.Name, InstallingWorkloadCommandParser.FromRollbackFileOption.Name), isUserError: true);
}
@@ -128,13 +128,13 @@ public void InstallWorkloads(IEnumerable workloadIds, bool skipManif
{
Reporter.WriteLine();
- var manifestsToUpdate = Enumerable.Empty ();
+ var manifestsToUpdate = Enumerable.Empty();
var useRollback = false;
if (!skipManifestUpdate)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json");
- if (File.Exists(installStateFilePath))
+ if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && File.Exists(installStateFilePath) && InstallStateContents.FromString(File.ReadAllText(installStateFilePath)).Manifests is not null)
{
// If there is a rollback state file, then we don't want to automatically update workloads when a workload is installed
// To update to a new version, the user would need to run "dotnet workload update"
@@ -162,7 +162,7 @@ public void InstallWorkloads(IEnumerable workloadIds, bool skipManif
_workloadManifestUpdater.UpdateAdvertisingManifestsAsync(includePreviews, offlineCache).Wait();
manifestsToUpdate = useRollback ?
_workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) :
- _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.manifestUpdate);
+ _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate);
}
InstallWorkloadsWithInstallRecord(_workloadInstaller, workloadIds, _sdkFeatureBand, manifestsToUpdate, offlineCache, useRollback);
@@ -229,7 +229,7 @@ private void InstallWorkloadsWithInstallRecord(
if (usingRollback)
{
- UpdateInstallState(true, manifestsToUpdate);
+ installer.SaveInstallStateManifestVersions(sdkFeatureBand, GetInstallStateContents(manifestsToUpdate));
}
},
rollback: () =>
@@ -249,7 +249,7 @@ private void InstallWorkloadsWithInstallRecord(
private async Task> GetPackageDownloadUrlsAsync(IEnumerable workloadIds, bool skipManifestUpdate, bool includePreview)
{
var downloads = await GetDownloads(workloadIds, skipManifestUpdate, includePreview);
-
+
var urls = new List();
foreach (var download in downloads)
{
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
index aa1ae48eb1cc..ceb8fece65e8 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs
@@ -23,7 +23,8 @@ public static IInstaller GetWorkloadInstaller(
string tempDirPath = null,
PackageSourceLocation packageSourceLocation = null,
RestoreActionConfig restoreActionConfig = null,
- bool elevationRequired = true)
+ bool elevationRequired = true,
+ bool shouldLog = true)
{
dotnetDir = string.IsNullOrWhiteSpace(dotnetDir) ? Path.GetDirectoryName(Environment.ProcessPath) : dotnetDir;
var installType = WorkloadInstallType.GetWorkloadInstallType(sdkFeatureBand, dotnetDir);
@@ -34,9 +35,9 @@ public static IInstaller GetWorkloadInstaller(
{
throw new InvalidOperationException(LocalizableStrings.OSDoesNotSupportMsi);
}
-
+ // TODO: should restoreActionConfig be flowed through to the client here as well like it is for the FileBasedInstaller below?
return NetSdkMsiInstallerClient.Create(verifySignatures, sdkFeatureBand, workloadResolver,
- nugetPackageDownloader, verbosity, packageSourceLocation, reporter, tempDirPath);
+ nugetPackageDownloader, verbosity, packageSourceLocation, reporter, tempDirPath, shouldLog: shouldLog);
}
if (elevationRequired && !WorkloadFileBasedInstall.IsUserLocal(dotnetDir, sdkFeatureBand.ToString()) && !CanWriteToDotnetRoot(dotnetDir))
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
index da18d53e4e6d..a7fe27c39f50 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
@@ -22,9 +22,8 @@ internal class WorkloadManifestUpdater : IWorkloadManifestUpdater
private readonly INuGetPackageDownloader _nugetPackageDownloader;
private readonly SdkFeatureBand _sdkFeatureBand;
private readonly string _userProfileDir;
- private readonly string _tempDirPath;
private readonly PackageSourceLocation _packageSourceLocation;
- Func _getEnvironmentVariable;
+ private readonly Func _getEnvironmentVariable;
private readonly IWorkloadInstallationRecordRepository _workloadRecordRepo;
private readonly IWorkloadManifestInstaller _workloadManifestInstaller;
private readonly bool _displayManifestUpdates;
@@ -33,7 +32,6 @@ public WorkloadManifestUpdater(IReporter reporter,
IWorkloadResolver workloadResolver,
INuGetPackageDownloader nugetPackageDownloader,
string userProfileDir,
- string tempDirPath,
IWorkloadInstallationRecordRepository workloadRecordRepo,
IWorkloadManifestInstaller workloadManifestInstaller,
PackageSourceLocation packageSourceLocation = null,
@@ -44,7 +42,6 @@ public WorkloadManifestUpdater(IReporter reporter,
_reporter = reporter;
_workloadResolver = workloadResolver;
_userProfileDir = userProfileDir;
- _tempDirPath = tempDirPath;
_nugetPackageDownloader = nugetPackageDownloader;
_sdkFeatureBand = sdkFeatureBand ?? new SdkFeatureBand(_workloadResolver.GetSdkFeatureBand());
_packageSourceLocation = packageSourceLocation;
@@ -72,15 +69,14 @@ private static WorkloadManifestUpdater GetInstance(string userProfileDir)
workloadResolver, VerbosityOptions.normal, userProfileDir, verifySignatures: false);
var workloadRecordRepo = installer.GetWorkloadInstallationRecordRepository();
- return new WorkloadManifestUpdater(reporter, workloadResolver, nugetPackageDownloader, userProfileDir, tempPackagesDir.Value, workloadRecordRepo, installer);
+ return new WorkloadManifestUpdater(reporter, workloadResolver, nugetPackageDownloader, userProfileDir, workloadRecordRepo, installer);
}
public async Task UpdateAdvertisingManifestsAsync(bool includePreviews, DirectoryPath? offlineCache = null)
{
// this updates all the manifests
var manifests = _workloadResolver.GetInstalledManifests();
- await Task.WhenAll(manifests.Select(manifest => UpdateAdvertisingManifestAsync(manifest, includePreviews, offlineCache)))
- .ConfigureAwait(false);
+ await Task.WhenAll(manifests.Select(manifest => UpdateAdvertisingManifestAsync(manifest, includePreviews, offlineCache))).ConfigureAwait(false);
WriteUpdatableWorkloadsFile();
}
@@ -161,37 +157,26 @@ public static void AdvertiseWorkloadUpdates()
}
}
- public IEnumerable<(
- ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads
- )>
- CalculateManifestUpdates()
+ public IEnumerable CalculateManifestUpdates()
{
- var manifestUpdates =
- new List<(ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)>();
var currentManifestIds = GetInstalledManifestIds();
foreach (var manifestId in currentManifestIds)
{
- var currentManifestVersion = GetInstalledManifestVersion(manifestId);
- var advertisingManifestVersionAndWorkloads = GetAdvertisingManifestVersionAndWorkloads(manifestId);
- if (advertisingManifestVersionAndWorkloads == null)
+ var advertisingInfo = GetAdvertisingManifestVersionAndWorkloads(manifestId);
+ if (advertisingInfo == null)
{
continue;
}
- if (advertisingManifestVersionAndWorkloads != null &&
- ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0
- && advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.Equals(currentManifestVersion.sdkFeatureBand)) ||
- advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0))
+ var (installedVersion, installedBand) = GetInstalledManifestVersion(manifestId);
+ var ((adVersion, adBand), adWorkloads) = advertisingInfo.Value;
+ if ((adVersion.CompareTo(installedVersion) > 0 && adBand.Equals(installedBand)) ||
+ adBand.CompareTo(installedBand) > 0)
{
- manifestUpdates.Add((new ManifestVersionUpdate(manifestId, currentManifestVersion.manifestVersion, currentManifestVersion.sdkFeatureBand.ToString(),
- advertisingManifestVersionAndWorkloads.Value.ManifestVersion, advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.ToString()),
- advertisingManifestVersionAndWorkloads.Value.Workloads));
+ var update = new ManifestVersionUpdate(manifestId, installedVersion, installedBand.ToString(), adVersion, adBand.ToString());
+ yield return new(update, adWorkloads);
}
}
-
- return manifestUpdates;
}
public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable installedWorkloads)
@@ -213,25 +198,23 @@ public IEnumerable CalculateManifestRollbacks(string roll
var currentManifestIds = GetInstalledManifestIds();
var manifestRollbacks = ParseRollbackDefinitionFile(rollbackDefinitionFilePath);
- var unrecognizedManifestIds = manifestRollbacks.Where(rollbackManifest => !currentManifestIds.Contains(rollbackManifest.Item1));
+ var unrecognizedManifestIds = manifestRollbacks.Where(rollbackManifest => !currentManifestIds.Contains(rollbackManifest.Id));
if (unrecognizedManifestIds.Any())
{
_reporter.WriteLine(string.Format(LocalizableStrings.RollbackDefinitionContainsExtraneousManifestIds, rollbackDefinitionFilePath, string.Join(" ", unrecognizedManifestIds)).Yellow());
- manifestRollbacks = manifestRollbacks.Where(rollbackManifest => currentManifestIds.Contains(rollbackManifest.Item1));
+ manifestRollbacks = manifestRollbacks.Where(rollbackManifest => currentManifestIds.Contains(rollbackManifest.Id));
}
- var manifestUpdates = manifestRollbacks
- .Select(manifest =>
- {
- var installedManifestInfo = GetInstalledManifestVersion(manifest.id);
- return new ManifestVersionUpdate(manifest.id, installedManifestInfo.manifestVersion, installedManifestInfo.sdkFeatureBand.ToString(),
- manifest.version, manifest.featureBand.ToString());
- });
+ var manifestUpdates = manifestRollbacks.Select(manifest =>
+ {
+ var (id, (version, band)) = manifest;
+ var (installedVersion, installedBand) = GetInstalledManifestVersion(id);
+ return new ManifestVersionUpdate(id, installedVersion, installedBand.ToString(), version, band.ToString());
+ });
return manifestUpdates;
}
-
public async Task> GetManifestPackageDownloadsAsync(bool includePreviews, SdkFeatureBand providedSdkFeatureBand, SdkFeatureBand installedSdkFeatureBand)
{
var downloads = new List();
@@ -239,97 +222,103 @@ public async Task> GetManifestPackageDownloadsAsyn
{
try
{
- var packageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), providedSdkFeatureBand);
-
- bool success;
- // After checking the --sdk-version, check the current sdk band, and then the manifest band in that order
- (success, var latestVersion) = await GetPackageVersion(packageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews);
- if (success)
- {
- downloads.Add(new WorkloadDownload(manifest.Id, packageId.ToString(), latestVersion.ToString()));
- }
-
- if (!success && !installedSdkFeatureBand.Equals(providedSdkFeatureBand))
+ PackageId? providedPackageId = null;
+ var fallbackFeatureBand = new SdkFeatureBand(manifest.ManifestFeatureBand);
+ // The bands should be checked in the order defined here.
+ SdkFeatureBand[] bands = [providedSdkFeatureBand, installedSdkFeatureBand, fallbackFeatureBand];
+ var success = false;
+ // Use Distinct to eliminate bands that are the same.
+ foreach (var band in bands.Distinct())
{
- var newPackageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), installedSdkFeatureBand);
-
- (success, latestVersion) = await GetPackageVersion(newPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews);
+ var packageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), band);
+ providedPackageId ??= packageId;
- if (success)
+ try
{
- downloads.Add(new WorkloadDownload(manifest.Id, newPackageId.ToString(), latestVersion.ToString()));
+ var latestVersion = await _nugetPackageDownloader.GetLatestPackageVersion(packageId, _packageSourceLocation, includePreviews);
+ success = true;
+ downloads.Add(new WorkloadDownload(manifest.Id, packageId.ToString(), latestVersion.ToString()));
+ break;
}
- }
- var fallbackFeatureBand = new SdkFeatureBand(manifest.ManifestFeatureBand);
- if (!success && !fallbackFeatureBand.Equals(installedSdkFeatureBand))
- {
- var newPackageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), fallbackFeatureBand);
-
- (success, latestVersion) = await GetPackageVersion(newPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews);
-
- if (success)
+ catch (NuGetPackageNotFoundException)
{
- downloads.Add(new WorkloadDownload(manifest.Id, newPackageId.ToString(), latestVersion.ToString()));
}
}
+
if (!success)
{
- _reporter.WriteLine(string.Format(LocalizableStrings.ManifestPackageUrlNotResolved, packageId));
+ _reporter.WriteLine(LocalizableStrings.ManifestPackageUrlNotResolved, providedPackageId);
}
}
catch
{
- _reporter.WriteLine(string.Format(LocalizableStrings.ManifestPackageUrlNotResolved, manifest.Id));
+ _reporter.WriteLine(LocalizableStrings.ManifestPackageUrlNotResolved, manifest.Id);
}
}
+
return downloads;
}
- private IEnumerable GetInstalledManifestIds()
- {
- return _workloadResolver.GetInstalledManifests().Select(manifest => new ManifestId(manifest.Id));
- }
+ private IEnumerable GetInstalledManifestIds() => _workloadResolver.GetInstalledManifests().Select(manifest => new ManifestId(manifest.Id));
private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, bool includePreviews, DirectoryPath? offlineCache = null)
{
string packagePath = null;
var manifestId = new ManifestId(manifest.Id);
- string currentFeatureBand = _sdkFeatureBand.ToString();
-
try
{
- var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId);
-
- bool success;
- (success, packagePath) = await GetManifestPackageUpdate(_sdkFeatureBand, manifestId, includePreviews, offlineCache);
- if (!success)
+ SdkFeatureBand? currentFeatureBand = null;
+ var fallbackFeatureBand = new SdkFeatureBand(manifest.ManifestFeatureBand);
+ // The bands should be checked in the order defined here.
+ SdkFeatureBand[] bands = [_sdkFeatureBand, fallbackFeatureBand];
+ var success = false;
+ // Use Distinct to eliminate bands that are the same.
+ foreach (var band in bands.Distinct())
{
- if (!(manifest.ManifestFeatureBand).Equals(_sdkFeatureBand))
+ var manifestPackageId = _workloadManifestInstaller.GetManifestPackageId(manifestId, band);
+ currentFeatureBand = band;
+
+ try
+ {
+ // If an offline cache is present, use that. Otherwise, try to acquire the package online.
+ packagePath = offlineCache != null ?
+ Directory.GetFiles(offlineCache.Value.Value)
+ .Where(path => path.EndsWith(".nupkg") && Path.GetFileName(path).StartsWith(manifestPackageId.ToString(), StringComparison.OrdinalIgnoreCase))
+ .Max() :
+ await _nugetPackageDownloader.DownloadPackageAsync(manifestPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews);
+
+ if (packagePath != null)
+ {
+ success = true;
+ break;
+ }
+ }
+ catch (NuGetPackageNotFoundException)
{
- (success, packagePath) = await GetManifestPackageUpdate(new SdkFeatureBand(manifest.ManifestFeatureBand), manifestId, includePreviews, offlineCache);
- currentFeatureBand = manifest.ManifestFeatureBand.ToString();
}
}
+
if (!success)
{
- _reporter.WriteLine(string.Format(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId));
+ _reporter.WriteLine(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId);
return;
}
+ var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId);
await _workloadManifestInstaller.ExtractManifestAsync(packagePath, adManifestPath);
- // add file that contains the advertisted manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not
- File.WriteAllText(Path.Combine(adManifestPath, "AdvertisedManifestFeatureBand.txt"), currentFeatureBand);
+ // add file that contains the advertised manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not
+ File.WriteAllText(Path.Combine(adManifestPath, "AdvertisedManifestFeatureBand.txt"), currentFeatureBand.ToString());
if (_displayManifestUpdates)
{
- _reporter.WriteLine(string.Format(LocalizableStrings.AdManifestUpdated, manifestId));
+ _reporter.WriteLine(LocalizableStrings.AdManifestUpdated, manifestId);
}
}
catch (Exception e)
{
- _reporter.WriteLine(string.Format(LocalizableStrings.FailedAdManifestUpdate, manifestId, e.Message));
+ _reporter.WriteLine(LocalizableStrings.FailedAdManifestUpdate, manifestId, e.Message);
}
finally
{
@@ -354,51 +343,45 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest,
}
}
- private (ManifestVersion ManifestVersion, SdkFeatureBand ManifestFeatureBand, Dictionary Workloads)?
- GetAdvertisingManifestVersionAndWorkloads(ManifestId manifestId)
+ private (ManifestVersionWithBand ManifestWithBand, WorkloadCollection Workloads)? GetAdvertisingManifestVersionAndWorkloads(ManifestId manifestId)
{
- var manifestPath = Path.Combine(GetAdvertisingManifestPath(_sdkFeatureBand, manifestId),
- "WorkloadManifest.json");
+ var manifestPath = Path.Combine(GetAdvertisingManifestPath(_sdkFeatureBand, manifestId), "WorkloadManifest.json");
if (!File.Exists(manifestPath))
{
return null;
}
- using (FileStream fsSource = new FileStream(manifestPath, FileMode.Open, FileAccess.Read))
- {
- var manifest = WorkloadManifestReader.ReadWorkloadManifest(manifestId.ToString(), fsSource, manifestPath);
- // we need to know the feature band of the advertised manifest (read it from the AdvertisedManifestFeatureBand.txt file)
- // if we don't find the file then use the current feature band
- var adManifestFeatureBandPath = Path.Combine(GetAdvertisingManifestPath(_sdkFeatureBand, manifestId), "AdvertisedManifestFeatureBand.txt");
-
- SdkFeatureBand adManifestFeatureBand = _sdkFeatureBand;
- if (File.Exists(adManifestFeatureBandPath))
- {
- adManifestFeatureBand = new SdkFeatureBand(File.ReadAllText(adManifestFeatureBandPath));
- }
-
+ using FileStream fsSource = new(manifestPath, FileMode.Open, FileAccess.Read);
+ var manifest = WorkloadManifestReader.ReadWorkloadManifest(manifestId.ToString(), fsSource, manifestPath);
+ // we need to know the feature band of the advertised manifest (read it from the AdvertisedManifestFeatureBand.txt file)
+ // if we don't find the file then use the current feature band
+ var adManifestFeatureBandPath = Path.Combine(GetAdvertisingManifestPath(_sdkFeatureBand, manifestId), "AdvertisedManifestFeatureBand.txt");
- return (new ManifestVersion(manifest.Version), adManifestFeatureBand, manifest.Workloads.Values.OfType().ToDictionary(w => w.Id));
+ SdkFeatureBand adManifestFeatureBand = _sdkFeatureBand;
+ if (File.Exists(adManifestFeatureBandPath))
+ {
+ adManifestFeatureBand = new SdkFeatureBand(File.ReadAllText(adManifestFeatureBandPath));
}
+
+ ManifestVersionWithBand manifestWithBand = new(new ManifestVersion(manifest.Version), adManifestFeatureBand);
+ var workloads = manifest.Workloads.Values.OfType().ToDictionary(w => w.Id);
+ return (manifestWithBand, workloads);
}
- private (ManifestVersion manifestVersion, SdkFeatureBand sdkFeatureBand) GetInstalledManifestVersion(ManifestId manifestId)
+ private ManifestVersionWithBand GetInstalledManifestVersion(ManifestId manifestId)
{
-
- var manifest = _workloadResolver.GetInstalledManifests()
- .FirstOrDefault(manifest => manifest.Id.ToLowerInvariant().Equals(manifestId.ToString()));
+ var manifest = _workloadResolver.GetInstalledManifests().FirstOrDefault(manifest => manifest.Id.ToLowerInvariant().Equals(manifestId.ToString()));
if (manifest == null)
{
throw new Exception(string.Format(LocalizableStrings.ManifestDoesNotExist, manifestId.ToString()));
}
- return (new ManifestVersion(manifest.Version), new SdkFeatureBand(manifest.ManifestFeatureBand));
+ return new(new ManifestVersion(manifest.Version), new SdkFeatureBand(manifest.ManifestFeatureBand));
}
private bool AdManifestSentinelIsDueForUpdate()
{
var sentinelPath = GetAdvertisingManifestSentinelPath(_sdkFeatureBand);
- int updateIntervalHours;
- if (!int.TryParse(_getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_INTERVAL_HOURS), out updateIntervalHours))
+ if (!int.TryParse(_getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_INTERVAL_HOURS), out int updateIntervalHours))
{
updateIntervalHours = 24;
}
@@ -418,8 +401,7 @@ private bool AdManifestSentinelIsDueForUpdate()
private async Task UpdatedAdManifestPackagesExistAsync()
{
var manifests = GetInstalledManifestIds();
- var availableUpdates = await Task.WhenAll(manifests.Select(manifest => NewerManifestPackageExists(manifest)))
- .ConfigureAwait(false);
+ var availableUpdates = await Task.WhenAll(manifests.Select(manifest => NewerManifestPackageExists(manifest))).ConfigureAwait(false);
return availableUpdates.Any();
}
@@ -437,7 +419,7 @@ private async Task NewerManifestPackageExists(ManifestId manifest)
}
}
- private IEnumerable<(ManifestId id, ManifestVersion version, SdkFeatureBand featureBand)> ParseRollbackDefinitionFile(string rollbackDefinitionFilePath)
+ private IEnumerable<(ManifestId Id, ManifestVersionWithBand ManifestWithBand)> ParseRollbackDefinitionFile(string rollbackDefinitionFilePath)
{
string fileContent;
@@ -457,12 +439,11 @@ private async Task NewerManifestPackageExists(ManifestId manifest)
}
}
- return WorkloadSet.FromJson(fileContent, _sdkFeatureBand).ManifestVersions
- .Select(kvp => (kvp.Key, kvp.Value.Version, kvp.Value.FeatureBand));
+ var versions = WorkloadSet.FromJson(fileContent, _sdkFeatureBand).ManifestVersions;
+ return versions.Select(kvp => (kvp.Key, new ManifestVersionWithBand(kvp.Value.Version, kvp.Value.FeatureBand)));
}
- private bool BackgroundUpdatesAreDisabled() =>
- bool.TryParse(_getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_DISABLE), out var disableEnvVar) && disableEnvVar;
+ private bool BackgroundUpdatesAreDisabled() => bool.TryParse(_getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_DISABLE), out var disableEnvVar) && disableEnvVar;
private string GetAdvertisingManifestSentinelPath(SdkFeatureBand featureBand) => Path.Combine(_userProfileDir, $".workloadAdvertisingManifestSentinel{featureBand}");
@@ -470,67 +451,8 @@ private bool BackgroundUpdatesAreDisabled() =>
private static string GetAdvertisingWorkloadsFilePath(string userProfileDir, SdkFeatureBand featureBand) => Path.Combine(userProfileDir, $".workloadAdvertisingUpdates{featureBand}");
- private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews)
- {
- string packagePath = await _nugetPackageDownloader.DownloadPackageAsync(
- _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand),
- packageSourceLocation: _packageSourceLocation,
- includePreview: includePreviews);
-
- return packagePath;
- }
-
- private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, DirectoryPath? offlineCache = null)
- {
- string packagePath = Directory.GetFiles(offlineCache.Value.Value)
- .Where(path => path.EndsWith(".nupkg"))
- .Where(path =>
- {
- var manifestPackageId = _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand).ToString();
- return Path.GetFileName(path).StartsWith(manifestPackageId, StringComparison.OrdinalIgnoreCase);
- })
- .Max();
-
- return packagePath;
- }
-
- private async Task<(bool, string)> GetManifestPackageUpdate(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews, DirectoryPath? offlineCache = null)
- {
- if (offlineCache == null || !offlineCache.HasValue)
- {
- try
- {
- string packagePath = await GetOnlinePackagePath(sdkFeatureBand, manifestId, includePreviews);
- return (true, packagePath);
- }
- catch (NuGetPackageNotFoundException)
- {
- return (false, null);
- }
- }
- else
- {
- string packagePath = GetOfflinePackagePath(sdkFeatureBand, manifestId, offlineCache);
- return (packagePath != null, packagePath);
- }
- }
-
- private async Task<(bool, NuGetVersion)> GetPackageVersion(PackageId packageId, PackageSourceLocation packageSourceLocation = null, bool includePreview = false)
- {
- try
- {
- var latestVersion = await _nugetPackageDownloader.GetLatestPackageVersion(packageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreview);
- return (true, latestVersion);
- }
- catch (NuGetPackageNotFoundException)
- {
- return (false, null);
- }
-
- }
-
+ private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString());
- private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) =>
- Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString());
+ private record ManifestVersionWithBand(ManifestVersion Version, SdkFeatureBand Band);
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
index 9f8d6f8e2699..720a2aefe836 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
@@ -362,6 +362,11 @@
CESTA
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ UrÄete, jestli by budoucà operace úloh mÄ›ly použÃvat sady úloh nebo volné manifesty.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Jsou k dispozici aktualizace úloh. Pokud chcete zÃskat dalšà informace, spusÅ¥te `dotnet workload list`.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
index 23d61efe36f7..f412e4e21584 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
@@ -362,6 +362,11 @@
PFAD
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Hiermit wird gesteuert, ob zukünftige Workloadvorgänge Workloadsätze oder lose Manifeste verwenden sollen.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Es sind Workloadupdates verfügbar. Um weitere Informationen zu erhalten, führen Sie `dotnet workload list` aus.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
index 056fb8cbeeaf..661379832a29 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
@@ -362,6 +362,11 @@
RUTA DE ACCESO
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Controle si las operaciones de carga de trabajo futuras deben usar conjuntos de cargas de trabajo o manifiestos flexibles.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Hay actualizaciones de carga de trabajo disponibles. Ejecute "dotnet workload list" para obtener más información.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
index 879b2659c9fd..1462fa793077 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
@@ -362,6 +362,11 @@
PATH
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Contrôlez si les futures opérations de charge de travail doivent utiliser des ensembles de charges de travail ou des manifestes lâches.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Des mises à jour de la charge de travail sont disponibles. Exécutez `dotnet workload list` pour plus d’informations.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
index b7ba22c5001c..2f9f807b9fa6 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
@@ -362,6 +362,11 @@
PERCORSO
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Controllare se le operazioni future del carico di lavoro devono usare set di carichi di lavoro o manifesti separati.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Sono disponibili aggiornamenti del carico di lavoro. Per altre informazioni, eseguire `dotnet workload list`.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
index ed50daa56103..93aa4172e61b 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
@@ -362,6 +362,11 @@
パス
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ å°†æ¥ã®ãƒ¯ãƒ¼ã‚¯ãƒãƒ¼ãƒ‰æ“作ã§ãƒ¯ãƒ¼ã‚¯ãƒãƒ¼ãƒ‰ セットを使用ã™ã‚‹ã‹ã€ãƒ«ãƒ¼ã‚º マニフェストを使用ã™ã‚‹ã‹ã‚’制御ã—ã¾ã™ã€‚
+
+
Workload updates are available. Run `dotnet workload list` for more information.
ワークãƒãƒ¼ãƒ‰ã®æ›´æ–°ãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚詳細ã«ã¤ã„ã¦ã¯ã€`dotnet workload list` を実行ã—ã¦ãã ã•ã„。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
index a46019514be2..dcfa49035630 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
@@ -362,6 +362,11 @@
경로
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ 향후 워í¬ë¡œë“œ 작업ì—서 워í¬ë¡œë“œ ì§‘í•©ì„ ì‚¬ìš©í• ì§€, 매니페스트를 ì™„í™”í• ì§€ë¥¼ ì œì–´í•©ë‹ˆë‹¤.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
워í¬ë¡œë“œ ì—…ë°ì´íŠ¸ë¥¼ ì‚¬ìš©í• ìˆ˜ 있습니다. ìžì„¸í•œ ë‚´ìš©ì„ ë³´ë ¤ë©´ `dotnet workload list`ì„ ì‹¤í–‰í•˜ì„¸ìš”.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
index 94697cd0040d..6325acdc4b93 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
@@ -362,6 +362,11 @@
ŚCIEŻKA
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Określ, czy przyszłe operacje związane z obciążeniami powinny wykorzystywać zestawy obciążeń, czy luźne manifesty.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
Dostępne są aktualizacje obciążenia. Uruchom polecenie `dotnet workload list`, aby uzyskać więcej informacji.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
index a3f860528e7e..8c03cca867d1 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
@@ -362,6 +362,11 @@
CAMINHO
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Controle se as operações de carga de trabalho futuras devem usar conjuntos de carga de trabalho ou manifestos flexÃveis.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
As atualizações de carga de trabalho estão disponÃveis. Execute `dotnet workload list` para obter mais informações.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
index bc3ec05f6d96..b4940992ccc1 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
@@ -362,6 +362,11 @@
PATH
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Укажите, должны ли будущие операции рабочей нагрузки иÑпользовать наборы рабочей нагрузки или Ñвободные манифеÑты.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
ДоÑтупны Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ¹ нагрузки. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ñ… Ñведений запуÑтите `dotnet workload list`.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
index c2cd403c021b..3691d8eb871c 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
@@ -362,6 +362,11 @@
YOL
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ Gelecekteki iş yükü işlemlerinin iş yükü kümelerini mi yoksa gevşek bildirimleri mi kullanması gerektiğini kontrol edin.
+
+
Workload updates are available. Run `dotnet workload list` for more information.
İş yükü güncelleştirmeleri var. Daha fazla bilgi için `dotnet workload list` çalıştırın.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
index 58ce764a255c..60dce47d2925 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
@@ -362,6 +362,11 @@
路径
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ 控制未æ¥çš„工作负载æ“ä½œåº”è¯¥ä½¿ç”¨å·¥ä½œè´Ÿè½½é›†è¿˜æ˜¯æ¾æ•£æ¸…å•。
+
+
Workload updates are available. Run `dotnet workload list` for more information.
有å¯ç”¨çš„工作负载更新。有关详细信æ¯ï¼Œè¯·è¿è¡Œ `dotnet workload list`。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
index d1e4bc9f6655..4921d99e1aab 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
@@ -362,6 +362,11 @@
路徑
+
+ Control whether future workload operations should use workload sets or loose manifests.
+ æŽ§åˆ¶æœªä¾†çš„å·¥ä½œè² è¼‰ä½œæ¥æ‡‰è©²ä½¿ç”¨å·¥ä½œè² 載集åˆé‚„是鬆散資訊清單。
+
+
Workload updates are available. Run `dotnet workload list` for more information.
有å¯ç”¨çš„å·¥ä½œè² è¼‰æ›´æ–°ã€‚å¦‚éœ€è©³ç´°è³‡è¨Šï¼Œè«‹åŸ·è¡Œ `dotnet workload list`。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
index 42fb65445dd7..2a3d861b517e 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs
@@ -54,7 +54,7 @@ public WorkloadListCommand(
string userProfileDir1 = userProfileDir ?? CliFolderPathCalculator.DotnetUserProfileFolderPath;
_workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(Reporter,
- _workloadListHelper.WorkloadResolver, PackageDownloader, userProfileDir1, TempDirectoryPath, _workloadListHelper.WorkloadRecordRepo, _workloadListHelper.Installer);
+ _workloadListHelper.WorkloadResolver, PackageDownloader, userProfileDir1, _workloadListHelper.WorkloadRecordRepo, _workloadListHelper.Installer);
}
public override int Execute()
@@ -65,9 +65,9 @@ public override int Execute()
{
_workloadListHelper.CheckTargetSdkVersionIsValid();
- UpdateAvailableEntry[] updateAvailable = GetUpdateAvailable(installedList);
- ListOutput listOutput = new(installedList.Select(id => id.ToString()).ToArray(),
- updateAvailable);
+ var updateAvailable = GetUpdateAvailable(installedList);
+ var installed = installedList.Select(id => id.ToString()).ToArray();
+ ListOutput listOutput = new(installed, updateAvailable.ToArray());
Reporter.WriteLine("==workloadListJsonOutputStart==");
Reporter.WriteLine(
@@ -108,29 +108,23 @@ public override int Execute()
return 0;
}
- internal UpdateAvailableEntry[] GetUpdateAvailable(IEnumerable installedList)
+ internal IEnumerable GetUpdateAvailable(IEnumerable installedList)
{
- HashSet installedWorkloads = installedList.ToHashSet();
_workloadManifestUpdater.UpdateAdvertisingManifestsAsync(_includePreviews).Wait();
- var manifestsToUpdate =
- _workloadManifestUpdater.CalculateManifestUpdates();
+ var manifestsToUpdate = _workloadManifestUpdater.CalculateManifestUpdates();
- List updateList = new();
- foreach ((ManifestVersionUpdate manifestUpdate, Dictionary workloads) in manifestsToUpdate)
+ foreach ((ManifestVersionUpdate manifestUpdate, WorkloadCollection workloads) in manifestsToUpdate)
{
- foreach ((WorkloadId WorkloadId, WorkloadDefinition workloadDefinition) in
- workloads)
+ foreach ((WorkloadId workloadId, WorkloadDefinition workloadDefinition) in workloads)
{
- if (installedWorkloads.Contains(new WorkloadId(WorkloadId.ToString())))
+ if (installedList.Contains(workloadId))
{
- updateList.Add(new UpdateAvailableEntry(manifestUpdate.ExistingVersion.ToString(),
+ yield return new UpdateAvailableEntry(manifestUpdate.ExistingVersion.ToString(),
manifestUpdate.NewVersion.ToString(),
- workloadDefinition.Description, WorkloadId.ToString()));
+ workloadDefinition.Description, workloadId.ToString());
}
}
}
-
- return updateList.ToArray();
}
internal record ListOutput(string[] Installed, UpdateAvailableEntry[] UpdateAvailable);
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx
index 41d8c0db27ee..77709f82d798 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx
@@ -138,6 +138,9 @@
Workload update failed: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+
Include workloads installed with earlier SDK versions in update.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs
index 03930eb5c5b0..a14ad7e4d00b 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs
@@ -19,6 +19,7 @@ internal class WorkloadUpdateCommand : InstallingWorkloadCommand
private readonly bool _adManifestOnlyOption;
private readonly bool _printRollbackDefinitionOnly;
private readonly bool _fromPreviousSdk;
+ private readonly string _workloadSetMode;
public WorkloadUpdateCommand(
ParseResult parseResult,
@@ -36,13 +37,14 @@ public WorkloadUpdateCommand(
_fromPreviousSdk = parseResult.GetValue(WorkloadUpdateCommandParser.FromPreviousSdkOption);
_adManifestOnlyOption = parseResult.GetValue(WorkloadUpdateCommandParser.AdManifestOnlyOption);
_printRollbackDefinitionOnly = parseResult.GetValue(WorkloadUpdateCommandParser.PrintRollbackOption);
+ _workloadSetMode = parseResult.GetValue(InstallingWorkloadCommandParser.WorkloadSetMode);
_workloadInstaller = _workloadInstallerFromConstructor ?? WorkloadInstallerFactory.GetWorkloadInstaller(Reporter,
_sdkFeatureBand, _workloadResolver, Verbosity, _userProfileDir, VerifySignatures, PackageDownloader,
_dotnetPath, TempDirectoryPath, packageSourceLocation: _packageSourceLocation, RestoreActionConfiguration,
elevationRequired: !_printDownloadLinkOnly && !_printRollbackDefinitionOnly && string.IsNullOrWhiteSpace(_downloadToCacheOption));
- _workloadManifestUpdater = _workloadManifestUpdaterFromConstructor ?? new WorkloadManifestUpdater(Reporter, _workloadResolver, PackageDownloader, _userProfileDir, TempDirectoryPath,
+ _workloadManifestUpdater = _workloadManifestUpdaterFromConstructor ?? new WorkloadManifestUpdater(Reporter, _workloadResolver, PackageDownloader, _userProfileDir,
_workloadInstaller.GetWorkloadInstallationRecordRepository(), _workloadInstaller, _packageSourceLocation, sdkFeatureBand: _sdkFeatureBand);
}
@@ -80,6 +82,22 @@ public override int Execute()
Reporter.WriteLine(workloadSet.ToJson());
Reporter.WriteLine("==workloadRollbackDefinitionJsonOutputEnd==");
}
+ else if (!string.IsNullOrWhiteSpace(_workloadSetMode))
+ {
+ if (_workloadSetMode.Equals("workloadset", StringComparison.OrdinalIgnoreCase))
+ {
+ _workloadInstaller.UpdateInstallMode(_sdkFeatureBand, true);
+ }
+ else if (_workloadSetMode.Equals("loosemanifest", StringComparison.OrdinalIgnoreCase) ||
+ _workloadSetMode.Equals("auto", StringComparison.OrdinalIgnoreCase))
+ {
+ _workloadInstaller.UpdateInstallMode(_sdkFeatureBand, false);
+ }
+ else
+ {
+ throw new GracefulException(string.Format(LocalizableStrings.WorkloadSetModeTakesWorkloadSetLooseManifestOrAuto, _workloadSetMode), isUserError: true);
+ }
+ }
else
{
try
@@ -108,7 +126,7 @@ public void UpdateWorkloads(bool includePreviews = false, DirectoryPath? offline
var manifestsToUpdate = useRollback ?
_workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) :
- _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.manifestUpdate);
+ _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate);
UpdateWorkloadsWithInstallRecord(_sdkFeatureBand, manifestsToUpdate, useRollback, offlineCache);
@@ -143,11 +161,9 @@ private void UpdateWorkloadsWithInstallRecord(
transaction.Run(
action: context =>
{
- bool rollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition);
-
foreach (var manifestUpdate in manifestsToUpdate)
{
- _workloadInstaller.InstallWorkloadManifest(manifestUpdate, context, offlineCache, rollback);
+ _workloadInstaller.InstallWorkloadManifest(manifestUpdate, context, offlineCache, useRollback);
}
_workloadResolver.RefreshWorkloadManifests();
@@ -156,7 +172,14 @@ private void UpdateWorkloadsWithInstallRecord(
_workloadInstaller.InstallWorkloads(workloads, sdkFeatureBand, context, offlineCache);
- UpdateInstallState(useRollback, manifestsToUpdate);
+ if (useRollback)
+ {
+ _workloadInstaller.SaveInstallStateManifestVersions(sdkFeatureBand, GetInstallStateContents(manifestsToUpdate));
+ }
+ else
+ {
+ _workloadInstaller.RemoveManifestsFromInstallState(sdkFeatureBand);
+ }
},
rollback: () =>
{
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs
index 496c49697e59..fe068fc74b70 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs
@@ -47,6 +47,7 @@ private static CliCommand ConstructCommand()
command.Options.Add(CommonOptions.VerbosityOption);
command.Options.Add(PrintRollbackOption);
command.Options.Add(WorkloadInstallCommandParser.SkipSignCheckOption);
+ command.Options.Add(InstallingWorkloadCommandParser.WorkloadSetMode);
command.SetAction((parseResult) => new WorkloadUpdateCommand(parseResult).Execute());
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf
index 11b89e60bc76..0d57937d6b65 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf
@@ -52,6 +52,11 @@
Nepovedlo se stáhnout balÃÄky aktualizace úlohy do mezipamÄ›ti: {0}.
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Neplatný argument „{0}“ argumentu --mode pro aktualizaci úlohy dotnet. Jediné podporované režimy jsou workloadset, loosemanifest a auto.
+
+
Successfully updated advertising manifests.
Manifesty reklamy se úspěšně aktualizovaly.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf
index d2a0c6c462bf..7fe3755912f5 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf
@@ -52,6 +52,11 @@
Fehler beim Herunterladen von Paketen zur Workloadaktualisierung in den Cache: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Ungültiges Argument "{0}" zum Argument --mode für das Dotnet Workload-Update. Es werden nur die Modi "workloadset", "loosemanifest" und "auto" unterstützt.
+
+
Successfully updated advertising manifests.
Werbemanifeste wurden erfolgreich aktualisiert.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf
index 8662a5c2a22b..61eacfcb2be0 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf
@@ -52,6 +52,11 @@
No se pudieron descargar los paquetes de actualización de la carga de trabajo en caché: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Argumento "{0}" no válido para el argumento --mode para la actualización de la carga de trabajo de dotnet. Solo los modos admitidos son "workloadset", "loosemanifest" y "auto".
+
+
Successfully updated advertising manifests.
Los manifiestos de publicidad se han actualizado correctamente.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf
index b5a33d8a6244..ffa504cef235 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf
@@ -52,6 +52,11 @@
Échec du téléchargement des packages de mise à jour de charge de travail dans le cache : {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Argument «{0}» non valide à l’argument --mode pour la mise à jour de charge de travail dotnet. Seuls les modes pris en charge sont « workloadset », « loosemanifest » et « auto ».
+
+
Successfully updated advertising manifests.
Les manifestes de publicité ont été mis à jour.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf
index 72fc4b08f96d..0d9403d54fe4 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf
@@ -52,6 +52,11 @@
Non è stato possibile scaricare i pacchetti di aggiornamento del carico di lavoro nella cache: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Argomento non valido "{0}" per l'argomento --mode per l'aggiornamento del carico di lavoro dotnet. Le uniche modalità supportate sono "workloadset", "loosemanifest" e "auto".
+
+
Successfully updated advertising manifests.
I manifesti pubblicitari sono stati aggiornati.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf
index 0fd3558e55b9..a3555167d604 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf
@@ -52,6 +52,11 @@
ワークãƒãƒ¼ãƒ‰æ›´æ–°ãƒ‘ッケージをã‚ャッシュã«ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ .NET ワークãƒãƒ¼ãƒ‰æ›´æ–°ã® --mode 引数ã«å¯¾ã™ã‚‹å¼•æ•° "{0}" ãŒç„¡åйã§ã™ã€‚サãƒãƒ¼ãƒˆã•れã¦ã„るモードã¯ã€"workloadset"ã€"loosemanifest"ã€ãŠã‚ˆã³ "auto" ã®ã¿ã§ã™ã€‚
+
+
Successfully updated advertising manifests.
広告マニフェストをæ£å¸¸ã«æ›´æ–°ã—ã¾ã—ãŸã€‚
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf
index 5496e044b69e..1f9d499812c4 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf
@@ -52,6 +52,11 @@
ìºì‹œí• 워í¬ë¡œë“œ ì—…ë°ì´íЏ 패키지를 다운로드하지 못했습니다. {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ dotnet 워í¬ë¡œë“œ ì—…ë°ì´íŠ¸ì˜ --mode ì¸ìˆ˜ì— 대한 "{0}" ì¸ìˆ˜ê°€ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. "workloadset", "loosemanifest", "auto" 모드만 ì§€ì›ë©ë‹ˆë‹¤.
+
+
Successfully updated advertising manifests.
알림 매니페스트를 ì—…ë°ì´íŠ¸í–ˆìŠµë‹ˆë‹¤.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf
index b1248dedd4f5..222d233a4c10 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf
@@ -52,6 +52,11 @@
Nie można pobrać pakietów aktualizacji pakietów roboczych do pamięci podręcznej: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ NieprawidÅ‚owy argument „{0}†argumentu --mode dla aktualizacji obciążenia dotnet. ObsÅ‚ugiwane tryby to „workloadsetâ€, „loosemanifest†i „autoâ€.
+
+
Successfully updated advertising manifests.
Pomyślnie zaktualizowano manifesty reklam.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf
index 5b743a470c8e..c5db7d56ba3e 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf
@@ -52,6 +52,11 @@
Falha ao baixar pacotes de atualização de carga de trabalho para o cache: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Argumento "{0}" inválido para o argumento --mode para atualização de carga de trabalho dotnet. Os únicos modos com suporte são "workloadset", "loosemanifest" e "auto".
+
+
Successfully updated advertising manifests.
Manifestos de anúncio atualizados com êxito.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf
index 6049ae860032..f65de634659b 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf
@@ -52,6 +52,11 @@
Ðе удалоÑÑŒ Ñкачать пакеты Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ¹ нагрузки в кÑш: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ ÐедопуÑтимый аргумент "{0}" Ð´Ð»Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð° --mode Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ¹ нагрузки dotnet. ПоддерживаютÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ режимы "workloadset", "loosemanifest" и "auto".
+
+
Successfully updated advertising manifests.
МанифеÑты рекламы уÑпешно обновлены.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf
index fe08c6c5f1b8..6c7f77da9566 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf
@@ -52,6 +52,11 @@
İş yükü güncelleştirme paketleri önbelleğe yüklenemedi: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ Dotnet iş yükü güncelleştirmesi için --mod bağımsız değişkeninde geçersiz "{0}" bağımsız değişkeni. Yalnızca "workloadset", "loosemanifest" ve "auto" modları desteklenir.
+
+
Successfully updated advertising manifests.
Reklam bildirimleri başarıyla güncelleştirildi.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf
index 844cad9451c3..3c981c1c23c4 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf
@@ -52,6 +52,11 @@
未能将工作负载更新程åºåŒ…下载到缓å˜: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ dotnet 工作负载更新的 --mode 傿•°çš„傿•°â€œ{0}â€æ— 效。仅支æŒâ€œworkloadsetâ€ã€â€œloosemanifestâ€å’Œâ€œautoâ€æ¨¡å¼ã€‚
+
+
Successfully updated advertising manifests.
æˆåŠŸæ›´æ–°å¹¿å‘Šæ¸…å•。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf
index cbde1607649d..20dab178f25e 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf
@@ -52,6 +52,11 @@
ç„¡æ³•å°‡å·¥ä½œè² è¼‰æ›´æ–°å¥—ä»¶ä¸‹è¼‰åˆ°å¿«å–: {0}
+
+ Invalid argument "{0}" to the --mode argument for dotnet workload update. Only supported modes are "workloadset", "loosemanifest", and "auto".
+ dotnet å·¥ä½œè² è¼‰æ›´æ–°çš„ --mode 引數之引數 "{0}" ç„¡æ•ˆã€‚åƒ…æ”¯æ´ "workloadset"ã€"loosemanifest" å’Œ "auto" 模å¼ã€‚
+
+
Successfully updated advertising manifests.
å·²æˆåŠŸæ›´æ–°å»£å‘Šè³‡è¨Šæ¸…å–®ã€‚
diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj
index 7410ec42262f..de27955804a3 100644
--- a/src/Cli/dotnet/dotnet.csproj
+++ b/src/Cli/dotnet/dotnet.csproj
@@ -44,6 +44,7 @@
+
@@ -124,4 +125,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs b/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs
index 94488fa74339..10ff6c46a554 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs
@@ -31,6 +31,8 @@ internal sealed partial class AuthHandshakeMessageHandler : DelegatingHandler
/// Valid characters for this clientID are in the unicode range 20-7E
///
private const string ClientID = "netsdkcontainers";
+ private const string BasicAuthScheme = "Basic";
+ private const string BearerAuthScheme = "Bearer";
private sealed record AuthInfo(string Realm, string? Service, string? Scope);
@@ -62,22 +64,33 @@ private static bool TryParseAuthenticationInfo(HttpResponseMessage msg, [NotNull
}
AuthenticationHeaderValue header = authenticateHeader.First();
- if (header is { Scheme: "Bearer" or "Basic", Parameter: string bearerArgs })
+
+ if (header.Scheme is not null)
{
scheme = header.Scheme;
- var keyValues = ParseBearerArgs(bearerArgs);
+ var keyValues = ParseBearerArgs(header.Parameter);
+ if (keyValues is null)
+ {
+ return false;
+ }
- var result = scheme switch
+ if (header.Scheme.Equals(BasicAuthScheme, StringComparison.OrdinalIgnoreCase))
{
- "Bearer" => TryParseBearerAuthInfo(keyValues, out bearerAuthInfo),
- "Basic" => TryParseBasicAuthInfo(keyValues, msg.RequestMessage!.RequestUri!, out bearerAuthInfo),
- _ => false
- };
- return result;
+ return TryParseBasicAuthInfo(keyValues, msg.RequestMessage!.RequestUri!, out bearerAuthInfo);
+ }
+ else if (header.Scheme.Equals(BearerAuthScheme, StringComparison.OrdinalIgnoreCase))
+ {
+ return TryParseBearerAuthInfo(keyValues, out bearerAuthInfo);
+ }
+ else
+ {
+ return false;
+ }
}
return false;
- static bool TryParseBearerAuthInfo(Dictionary authValues, [NotNullWhen(true)] out AuthInfo? authInfo) {
+ static bool TryParseBearerAuthInfo(Dictionary authValues, [NotNullWhen(true)] out AuthInfo? authInfo)
+ {
if (authValues.TryGetValue("realm", out string? realm))
{
string? service = null;
@@ -87,19 +100,25 @@ static bool TryParseBearerAuthInfo(Dictionary authValues, [NotNu
authInfo = new AuthInfo(realm, service, scope);
return true;
}
- else {
+ else
+ {
authInfo = null;
return false;
}
}
- static bool TryParseBasicAuthInfo(Dictionary authValues, Uri requestUri, out AuthInfo? authInfo) {
+ static bool TryParseBasicAuthInfo(Dictionary authValues, Uri requestUri, out AuthInfo? authInfo)
+ {
authInfo = null;
return true;
}
- static Dictionary ParseBearerArgs(string bearerHeaderArgs)
+ static Dictionary? ParseBearerArgs(string? bearerHeaderArgs)
{
+ if (bearerHeaderArgs is null)
+ {
+ return null;
+ }
Dictionary keyValues = new();
foreach (Match match in BearerParameterSplitter().Matches(bearerHeaderArgs))
{
@@ -118,8 +137,10 @@ static Dictionary ParseBearerArgs(string bearerHeaderArgs)
private sealed record TokenResponse(string? token, string? access_token, int? expires_in, DateTimeOffset? issued_at)
{
public string ResolvedToken => token ?? access_token ?? throw new ArgumentException(Resource.GetString(nameof(Strings.InvalidTokenResponse)));
- public DateTimeOffset ResolvedExpiration {
- get {
+ public DateTimeOffset ResolvedExpiration
+ {
+ get
+ {
var issueTime = this.issued_at ?? DateTimeOffset.UtcNow; // per spec, if no issued_at use the current time
var validityDuration = this.expires_in ?? 60; // per spec, if no expires_in use 60 seconds
var expirationTime = issueTime.AddSeconds(validityDuration);
@@ -151,12 +172,12 @@ public DateTimeOffset ResolvedExpiration {
privateRepoCreds = await GetLoginCredentials(registry).ConfigureAwait(false);
}
- if (scheme is "Basic")
+ if (scheme.Equals(BasicAuthScheme, StringComparison.OrdinalIgnoreCase))
{
- var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
- return new (authValue, DateTimeOffset.MaxValue);
+ var authValue = new AuthenticationHeaderValue(BasicAuthScheme, Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
+ return new(authValue, DateTimeOffset.MaxValue);
}
- else if (scheme is "Bearer")
+ else if (scheme.Equals(BearerAuthScheme, StringComparison.OrdinalIgnoreCase))
{
Debug.Assert(bearerAuthInfo is not null);
@@ -223,7 +244,7 @@ public DateTimeOffset ResolvedExpiration {
TokenResponse? tokenResponse = JsonSerializer.Deserialize(postResponse.Content.ReadAsStream(cancellationToken));
if (tokenResponse is { } tokenEnvelope)
{
- var authValue = new AuthenticationHeaderValue("Bearer", tokenResponse.ResolvedToken);
+ var authValue = new AuthenticationHeaderValue(BearerAuthScheme, tokenResponse.ResolvedToken);
return (authValue, tokenResponse.ResolvedExpiration);
}
else
@@ -239,39 +260,39 @@ public DateTimeOffset ResolvedExpiration {
///
private async Task<(AuthenticationHeaderValue, DateTimeOffset)?> TryTokenGetAsync(DockerCredentials privateRepoCreds, AuthInfo bearerAuthInfo, CancellationToken cancellationToken)
{
- // this doesn't seem to be called out in the spec, but actual username/password auth information should be converted into Basic auth here,
- // even though the overall Scheme we're authenticating for is Bearer
- var header = privateRepoCreds.Username == ""
- ? new AuthenticationHeaderValue("Bearer", privateRepoCreds.Password)
- : new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
- var builder = new UriBuilder(new Uri(bearerAuthInfo.Realm));
-
- _logger.LogTrace("Attempting to authenticate on {uri} using GET.", bearerAuthInfo.Realm);
- var queryDict = System.Web.HttpUtility.ParseQueryString("");
- if (bearerAuthInfo.Service is string svc)
- {
- queryDict["service"] = svc;
- }
- if (bearerAuthInfo.Scope is string s)
- {
- queryDict["scope"] = s;
- }
- builder.Query = queryDict.ToString();
- var message = new HttpRequestMessage(HttpMethod.Get, builder.ToString());
- message.Headers.Authorization = header;
+ // this doesn't seem to be called out in the spec, but actual username/password auth information should be converted into Basic auth here,
+ // even though the overall Scheme we're authenticating for is Bearer
+ var header = privateRepoCreds.Username == ""
+ ? new AuthenticationHeaderValue(BearerAuthScheme, privateRepoCreds.Password)
+ : new AuthenticationHeaderValue(BasicAuthScheme, Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
+ var builder = new UriBuilder(new Uri(bearerAuthInfo.Realm));
+
+ _logger.LogTrace("Attempting to authenticate on {uri} using GET.", bearerAuthInfo.Realm);
+ var queryDict = System.Web.HttpUtility.ParseQueryString("");
+ if (bearerAuthInfo.Service is string svc)
+ {
+ queryDict["service"] = svc;
+ }
+ if (bearerAuthInfo.Scope is string s)
+ {
+ queryDict["scope"] = s;
+ }
+ builder.Query = queryDict.ToString();
+ var message = new HttpRequestMessage(HttpMethod.Get, builder.ToString());
+ message.Headers.Authorization = header;
- using var tokenResponse = await base.SendAsync(message, cancellationToken).ConfigureAwait(false);
- if (!tokenResponse.IsSuccessStatusCode)
- {
- throw new UnableToAccessRepositoryException(_registryName);
- }
+ using var tokenResponse = await base.SendAsync(message, cancellationToken).ConfigureAwait(false);
+ if (!tokenResponse.IsSuccessStatusCode)
+ {
+ throw new UnableToAccessRepositoryException(_registryName);
+ }
- TokenResponse? token = JsonSerializer.Deserialize(tokenResponse.Content.ReadAsStream(cancellationToken));
- if (token is null)
- {
- throw new ArgumentException(Resource.GetString(nameof(Strings.CouldntDeserializeJsonToken)));
- }
- return (new AuthenticationHeaderValue("Bearer", token.ResolvedToken), token.ResolvedExpiration);
+ TokenResponse? token = JsonSerializer.Deserialize(tokenResponse.Content.ReadAsStream(cancellationToken));
+ if (token is null)
+ {
+ throw new ArgumentException(Resource.GetString(nameof(Strings.CouldntDeserializeJsonToken)));
+ }
+ return (new AuthenticationHeaderValue(BearerAuthScheme, token.ResolvedToken), token.ResolvedExpiration);
}
private static async Task GetLoginCredentials(string registry)
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
index ec546487ed62..4a0ae6848596 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs
@@ -59,11 +59,12 @@ public static async Task ContainerizeAsync(
{
try
{
+ var ridGraphPicker = new RidGraphManifestPicker(ridGraphPath);
imageBuilder = await registry.GetImageManifestAsync(
baseImageName,
baseImageTag,
containerRuntimeIdentifier,
- ridGraphPath,
+ ridGraphPicker,
cancellationToken).ConfigureAwait(false);
}
catch (RepositoryNotFoundException)
diff --git a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
index fc670b38519b..ce76c4060eb2 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/ImageBuilder.cs
@@ -237,13 +237,32 @@ internal void AssignUserFromEnvironment()
///
internal void AssignPortsFromEnvironment()
{
- // asp.net images control port bindings via three environment variables. we should check for those variables and ensure that ports are created for them
+ // asp.net images control port bindings via three environment variables. we should check for those variables and ensure that ports are created for them.
+ // precendence is captured at https://github.com/dotnet/aspnetcore/blob/f49c1c7f7467c184ffb630086afac447772096c6/src/Hosting/Hosting/src/GenericHost/GenericWebHostService.cs#L68-L119
+ // ASPNETCORE_URLS is the most specific and is the only one used if present, followed by ASPNETCORE_HTTPS_PORT and ASPNETCORE_HTTP_PORT together
+ // https://learn.microsoft.com//aspnet/core/fundamentals/host/web-host?view=aspnetcore-8.0#server-urls - the format of ASPNETCORE_URLS has been stable for many years now
+ if (_baseImageConfig.EnvironmentVariables.TryGetValue(EnvironmentVariables.ASPNETCORE_URLS, out string? urls))
+ {
+ foreach (var url in Split(urls))
+ {
+ _logger.LogTrace("Setting ports from ASPNETCORE_URLS environment variable");
+ var match = aspnetPortRegex.Match(url);
+ if (match.Success && int.TryParse(match.Groups["port"].Value, out int port))
+ {
+ _logger.LogTrace("Added port {port}", port);
+ ExposePort(port, PortType.tcp);
+ }
+ }
+ return; // we're done here - ASPNETCORE_URLS is the most specific and overrides the other two
+ }
+
+ // port-specific
// https://learn.microsoft.com/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-8.0#specify-ports-only - new for .NET 8 - allows just changing port(s) easily
if (_baseImageConfig.EnvironmentVariables.TryGetValue(EnvironmentVariables.ASPNETCORE_HTTP_PORTS, out string? httpPorts))
{
_logger.LogTrace("Setting ports from ASPNETCORE_HTTP_PORTS environment variable");
- foreach(var port in Split(httpPorts))
+ foreach (var port in Split(httpPorts))
{
if (int.TryParse(port, out int parsedPort))
{
@@ -260,7 +279,7 @@ internal void AssignPortsFromEnvironment()
if (_baseImageConfig.EnvironmentVariables.TryGetValue(EnvironmentVariables.ASPNETCORE_HTTPS_PORTS, out string? httpsPorts))
{
_logger.LogTrace("Setting ports from ASPNETCORE_HTTPS_PORTS environment variable");
- foreach(var port in Split(httpsPorts))
+ foreach (var port in Split(httpsPorts))
{
if (int.TryParse(port, out int parsedPort))
{
@@ -274,21 +293,6 @@ internal void AssignPortsFromEnvironment()
}
}
- // https://learn.microsoft.com//aspnet/core/fundamentals/host/web-host?view=aspnetcore-8.0#server-urls - the format of ASPNETCORE_URLS has been stable for many years now
- if (_baseImageConfig.EnvironmentVariables.TryGetValue(EnvironmentVariables.ASPNETCORE_URLS, out string? urls))
- {
- foreach(var url in Split(urls))
- {
- _logger.LogTrace("Setting ports from ASPNETCORE_URLS environment variable");
- var match = aspnetPortRegex.Match(url);
- if (match.Success && int.TryParse(match.Groups["port"].Value, out int port))
- {
- _logger.LogTrace("Added port {port}", port);
- ExposePort(port, PortType.tcp);
- }
- }
- }
-
static string[] Split(string input)
{
return input.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
diff --git a/src/Containers/Microsoft.NET.Build.Containers/KnownStrings.cs b/src/Containers/Microsoft.NET.Build.Containers/KnownStrings.cs
index 36fd920dd96e..be334ac7d271 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/KnownStrings.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/KnownStrings.cs
@@ -25,7 +25,6 @@ public static class Properties
public static readonly string ContainerEnvironmentVariable = nameof(ContainerEnvironmentVariable);
public static readonly string ComputeContainerBaseImage = nameof(ComputeContainerBaseImage);
- public static readonly string _ComputeContainerBaseImageTag = nameof(_ComputeContainerBaseImageTag);
public static readonly string ComputeContainerConfig = nameof(ComputeContainerConfig);
public static readonly string AssemblyName = nameof(AssemblyName);
public static readonly string ContainerBaseRegistry = nameof(ContainerBaseRegistry);
@@ -35,6 +34,15 @@ public static class Properties
public static readonly string ContainerGenerateLabels = nameof(ContainerGenerateLabels);
public static readonly string ContainerRuntimeIdentifier = nameof(ContainerRuntimeIdentifier);
+ public static readonly string RuntimeIdentifier = nameof(RuntimeIdentifier);
+ public static readonly string PublishAot = nameof(PublishAot);
+ public static readonly string PublishSelfContained = nameof(PublishSelfContained);
+ public static readonly string InvariantGlobalization = nameof(InvariantGlobalization);
+ }
+
+ public static class Items
+ {
+ public static readonly string FrameworkReference = nameof(FrameworkReference);
}
public static class ErrorCodes
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj b/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj
index 3142bdd1eaf1..6d7041c4c4f5 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj
+++ b/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj
@@ -47,7 +47,7 @@
-
+
diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
index 49ce9afe58c0..89caa7583181 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
+++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt
@@ -21,15 +21,6 @@ override Microsoft.NET.Build.Containers.Port.GetHashCode() -> int
Microsoft.NET.Build.Containers.PortType
Microsoft.NET.Build.Containers.PortType.tcp = 0 -> Microsoft.NET.Build.Containers.PortType
Microsoft.NET.Build.Containers.PortType.udp = 1 -> Microsoft.NET.Build.Containers.PortType
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ComputedBaseImageTag.get -> string?
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ComputeDotnetBaseImageTag() -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ContainerFamily.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ContainerFamily.set -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.SdkVersion.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.SdkVersion.set -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.TargetFrameworkVersion.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.TargetFrameworkVersion.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.get -> string!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.set -> void
@@ -86,7 +77,6 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.RuntimeIdentifierGraphPath.g
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.RuntimeIdentifierGraphPath.set -> void
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.WorkingDirectory.get -> string!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.WorkingDirectory.set -> void
-override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.Execute() -> bool
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ToolName.get -> string!
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateCommandLineCommands() -> string!
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.GenerateFullPathToTool() -> string!
@@ -115,3 +105,23 @@ Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerTag
override Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.Execute() -> bool
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string! input, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string? portNumber, string? portType, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ComputedContainerBaseImage.get -> string?
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.FrameworkReferences.get -> Microsoft.Build.Framework.ITaskItem![]!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.FrameworkReferences.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsAotPublished.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsAotPublished.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ComputeDotnetBaseImageAndTag() -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ContainerFamily.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ContainerFamily.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.set -> void
+override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.Execute() -> bool
\ No newline at end of file
diff --git a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
index 9b5a7e701d2d..3b6d27ca2c1e 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
+++ b/src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net8.0/PublicAPI.Unshipped.txt
@@ -2,7 +2,17 @@
const Microsoft.NET.Build.Containers.KnownLocalRegistryTypes.Podman = "Podman" -> string!
Microsoft.NET.Build.Containers.BaseImageNotFoundException
Microsoft.NET.Build.Containers.Constants
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ComputeDotnetBaseImageTag() -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ComputedContainerBaseImage.get -> string?
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.FrameworkReferences.get -> Microsoft.Build.Framework.ITaskItem![]!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.FrameworkReferences.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsAotPublished.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsAotPublished.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.IsSelfContained.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetRuntimeIdentifier.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.get -> bool
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.UsesInvariantGlobalization.set -> void
static Microsoft.NET.Build.Containers.ContainerBuilder.ContainerizeAsync(System.IO.DirectoryInfo! publishDirectory, string! workingDir, string! baseRegistry, string! baseImageName, string! baseImageTag, string![]! entrypoint, string![]! entrypointArgs, string![]! defaultArgs, string![]! appCommand, string![]! appCommandArgs, string! appCommandInstruction, string! imageName, string![]! imageTags, string? outputRegistry, System.Collections.Generic.Dictionary! labels, Microsoft.NET.Build.Containers.Port[]? exposedPorts, System.Collections.Generic.Dictionary! envVars, string! containerRuntimeIdentifier, string! ridGraphPath, string! localRegistry, string? containerUser, string? archiveOutputPath, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
static readonly Microsoft.NET.Build.Containers.Constants.Version -> string!
Microsoft.NET.Build.Containers.ContainerBuilder
@@ -111,14 +121,15 @@ override Microsoft.NET.Build.Containers.Port.GetHashCode() -> int
Microsoft.NET.Build.Containers.PortType
Microsoft.NET.Build.Containers.PortType.tcp = 0 -> Microsoft.NET.Build.Containers.PortType
Microsoft.NET.Build.Containers.PortType.udp = 1 -> Microsoft.NET.Build.Containers.PortType
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ContainerFamily.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ContainerFamily.set -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.SdkVersion.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.SdkVersion.set -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.TargetFrameworkVersion.get -> string!
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.TargetFrameworkVersion.set -> void
-Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.ComputedBaseImageTag.get -> string?
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ComputeDotnetBaseImageAndTag() -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ContainerFamily.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.ContainerFamily.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.set -> void
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.get -> string!
+Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.set -> void
+override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.Execute() -> bool
Microsoft.NET.Build.Containers.Tasks.CreateNewImage
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.get -> string!
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.set -> void
@@ -202,7 +213,6 @@ Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParseContainerProp
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerImage.get -> string!
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerRegistry.get -> string!
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerTag.get -> string!
-override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageTag.Execute() -> bool
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Execute() -> bool
override Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.Execute() -> bool
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string! input, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultBlobOperations.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultBlobOperations.cs
index fd5898fedd59..d258ebfc0886 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultBlobOperations.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultBlobOperations.cs
@@ -36,7 +36,7 @@ public async Task ExistsAsync(string repositoryName, string digest, Cancel
{
HttpStatusCode.OK => true,
HttpStatusCode.NotFound => false,
- HttpStatusCode.Unauthorized => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
+ HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
_ => await LogAndThrowContainerHttpException(response, cancellationToken).ConfigureAwait(false)
};
}
@@ -68,7 +68,7 @@ private async Task GetAsync(string repositoryName, string d
return response.StatusCode switch
{
HttpStatusCode.OK => response,
- HttpStatusCode.Unauthorized => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
+ HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
_ => await LogAndThrowContainerHttpException(response, cancellationToken).ConfigureAwait(false)
};
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultManifestOperations.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultManifestOperations.cs
index 55cf8bf93b6f..c34426d10af3 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultManifestOperations.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultManifestOperations.cs
@@ -33,7 +33,7 @@ public async Task GetAsync(string repositoryName, string re
{
HttpStatusCode.OK => response,
HttpStatusCode.NotFound => throw new RepositoryNotFoundException(_registryName, repositoryName, reference),
- HttpStatusCode.Unauthorized => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
+ HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden => throw new UnableToAccessRepositoryException(_registryName, repositoryName),
_ => await LogAndThrowContainerHttpException(response, cancellationToken).ConfigureAwait(false)
};
}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs
index e83a4da029b3..22d4d333c4bf 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs
@@ -30,7 +30,10 @@ internal DefaultRegistryAPI(string registryName, Uri baseUri, ILogger logger)
private static HttpClient CreateClient(string registryName, Uri baseUri, ILogger logger, bool isAmazonECRRegistry = false)
{
- var innerHandler = new SocketsHttpHandler();
+ var innerHandler = new SocketsHttpHandler()
+ {
+ UseCookies = false,
+ };
// Ignore certificate for https localhost repository.
if (baseUri.Host == "localhost" && baseUri.Scheme == "https")
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
index d65dd905d9c5..68456fa71bfa 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs
@@ -7,9 +7,49 @@
using System.Diagnostics;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
namespace Microsoft.NET.Build.Containers;
+internal interface IManifestPicker {
+ public PlatformSpecificManifest? PickBestManifestForRid(IReadOnlyDictionary manifestList, string runtimeIdentifier);
+}
+
+internal sealed class RidGraphManifestPicker : IManifestPicker
+{
+ private readonly RuntimeGraph _runtimeGraph;
+
+ public RidGraphManifestPicker(string runtimeIdentifierGraphPath)
+ {
+ _runtimeGraph = GetRuntimeGraphForDotNet(runtimeIdentifierGraphPath);
+ }
+ public PlatformSpecificManifest? PickBestManifestForRid(IReadOnlyDictionary ridManifestDict, string runtimeIdentifier)
+ {
+ var bestManifestRid = GetBestMatchingRid(_runtimeGraph, runtimeIdentifier, ridManifestDict.Keys);
+ if (bestManifestRid is null) {
+ return null;
+ }
+ return ridManifestDict[bestManifestRid];
+ }
+
+ private static string? GetBestMatchingRid(RuntimeGraph runtimeGraph, string runtimeIdentifier, IEnumerable availableRuntimeIdentifiers)
+ {
+ HashSet availableRids = new HashSet(availableRuntimeIdentifiers, StringComparer.Ordinal);
+ foreach (var candidateRuntimeIdentifier in runtimeGraph.ExpandRuntime(runtimeIdentifier))
+ {
+ if (availableRids.Contains(candidateRuntimeIdentifier))
+ {
+ return candidateRuntimeIdentifier;
+ }
+ }
+
+ return null;
+ }
+
+ private static RuntimeGraph GetRuntimeGraphForDotNet(string ridGraphPath) => JsonRuntimeFormat.ReadRuntimeGraph(ridGraphPath);
+
+}
+
internal sealed class Registry
{
private const string DockerHubRegistry1 = "registry-1.docker.io";
@@ -102,7 +142,7 @@ public bool IsGoogleArtifactRegistry {
///
private bool SupportsParallelUploads => !IsAmazonECRRegistry && _settings.ParallelUploadEnabled;
- public async Task GetImageManifestAsync(string repositoryName, string reference, string runtimeIdentifier, string runtimeIdentifierGraphPath, CancellationToken cancellationToken)
+ public async Task GetImageManifestAsync(string repositoryName, string reference, string runtimeIdentifier, IManifestPicker manifestPicker, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using HttpResponseMessage initialManifestResponse = await _registryAPI.Manifest.GetAsync(repositoryName, reference, cancellationToken).ConfigureAwait(false);
@@ -118,7 +158,7 @@ await initialManifestResponse.Content.ReadFromJsonAsync(cancellation
reference,
await initialManifestResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false),
runtimeIdentifier,
- runtimeIdentifierGraphPath,
+ manifestPicker,
cancellationToken).ConfigureAwait(false),
var unknownMediaType => throw new NotImplementedException(Resource.FormatString(
nameof(Strings.UnknownMediaType),
@@ -129,6 +169,18 @@ await initialManifestResponse.Content.ReadFromJsonAsync(cancella
};
}
+ internal async Task GetManifestListAsync(string repositoryName, string reference, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ using HttpResponseMessage initialManifestResponse = await _registryAPI.Manifest.GetAsync(repositoryName, reference, cancellationToken).ConfigureAwait(false);
+
+ return initialManifestResponse.Content.Headers.ContentType?.MediaType switch
+ {
+ SchemaTypes.DockerManifestListV2 => await initialManifestResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false),
+ _ => null
+ };
+ }
+
private async Task ReadSingleImageAsync(string repositoryName, ManifestV2 manifest, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -141,33 +193,8 @@ private async Task ReadSingleImageAsync(string repositoryName, Man
return new ImageBuilder(manifest, new ImageConfig(configDoc), _logger);
}
- private async Task PickBestImageFromManifestListAsync(
- string repositoryName,
- string reference,
- ManifestListV2 manifestList,
- string runtimeIdentifier,
- string runtimeIdentifierGraphPath,
- CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- var runtimeGraph = GetRuntimeGraphForDotNet(runtimeIdentifierGraphPath);
- var ridManifestDict = GetManifestsByRid(manifestList);
- var bestManifestRid = GetBestMatchingRid(runtimeGraph, runtimeIdentifier, ridManifestDict.Keys);
- if (bestManifestRid is null) {
- throw new BaseImageNotFoundException(runtimeIdentifier, repositoryName, reference, ridManifestDict.Keys);
- }
- PlatformSpecificManifest matchingManifest = ridManifestDict[bestManifestRid];
- using HttpResponseMessage manifestResponse = await _registryAPI.Manifest.GetAsync(repositoryName, matchingManifest.digest, cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- return await ReadSingleImageAsync(
- repositoryName,
- await manifestResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false),
- cancellationToken).ConfigureAwait(false);
- }
-
- IReadOnlyDictionary GetManifestsByRid(ManifestListV2 manifestList)
+
+ private static IReadOnlyDictionary GetManifestsByRid(ManifestListV2 manifestList)
{
var ridDict = new Dictionary();
foreach (var manifest in manifestList.manifests) {
@@ -179,21 +206,7 @@ IReadOnlyDictionary GetManifestsByRid(Manifest
return ridDict;
}
-
- private static string? GetBestMatchingRid(RuntimeGraph runtimeGraph, string runtimeIdentifier, IEnumerable availableRuntimeIdentifiers)
- {
- HashSet availableRids = new HashSet(availableRuntimeIdentifiers, StringComparer.Ordinal);
- foreach (var candidateRuntimeIdentifier in runtimeGraph.ExpandRuntime(runtimeIdentifier))
- {
- if (availableRids.Contains(candidateRuntimeIdentifier))
- {
- return candidateRuntimeIdentifier;
- }
- }
-
- return null;
- }
-
+
private static string? CreateRidForPlatform(PlatformInformation platform)
{
// we only support linux and windows containers explicitly, so anything else we should skip past.
@@ -225,7 +238,32 @@ IReadOnlyDictionary GetManifestsByRid(Manifest
return $"{osPart}{versionPart ?? ""}-{platformPart}";
}
- private static RuntimeGraph GetRuntimeGraphForDotNet(string ridGraphPath) => JsonRuntimeFormat.ReadRuntimeGraph(ridGraphPath);
+
+ private async Task PickBestImageFromManifestListAsync(
+ string repositoryName,
+ string reference,
+ ManifestListV2 manifestList,
+ string runtimeIdentifier,
+ IManifestPicker manifestPicker,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var ridManifestDict = GetManifestsByRid(manifestList);
+ if (manifestPicker.PickBestManifestForRid(ridManifestDict, runtimeIdentifier) is PlatformSpecificManifest matchingManifest)
+ {
+ using HttpResponseMessage manifestResponse = await _registryAPI.Manifest.GetAsync(repositoryName, matchingManifest.digest, cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ return await ReadSingleImageAsync(
+ repositoryName,
+ await manifestResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false),
+ cancellationToken).ConfigureAwait(false);
+ } else
+ {
+ throw new BaseImageNotFoundException(runtimeIdentifier, repositoryName, reference, ridManifestDict.Keys);
+ }
+ }
///
/// Ensure a blob associated with from the registry is available locally.
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageAndTag.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageAndTag.cs
new file mode 100644
index 000000000000..f4eab184e1e8
--- /dev/null
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageAndTag.cs
@@ -0,0 +1,248 @@
+// 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.Security.Cryptography;
+using Microsoft.Build.Framework;
+using NuGet.Versioning;
+#if NETFRAMEWORK
+using System.Linq;
+#endif
+
+namespace Microsoft.NET.Build.Containers.Tasks;
+
+///
+/// Computes the base image and Tag for a Microsoft-authored container image based on the project properties and tagging scheme from various SDK versions.
+///
+public sealed class ComputeDotnetBaseImageAndTag : Microsoft.Build.Utilities.Task
+{
+ // starting in .NET 8, the container tagging scheme started incorporating the
+ // 'channel' (rc/preview) and the channel increment (the numeric value after the channel name)
+ // into the container tags.
+ private const int FirstVersionWithNewTaggingScheme = 8;
+
+ ///
+ /// When in preview, this influences which preview image tag is used, since previews can have compatibility problems across versions.
+ ///
+ [Required]
+ public string SdkVersion { get; set; }
+
+ ///
+ /// Used to determine which `tag` of an image should be used by default.
+ ///
+ [Required]
+ public string TargetFrameworkVersion { get; set; }
+
+ ///
+ /// Used to inspect the project to see if it references ASP.Net Core, which causes a change in base image to dotnet/aspnet.
+ ///
+ [Required]
+ public ITaskItem[] FrameworkReferences { get; set; }
+
+ ///
+ /// If this is set to linux-ARCH then we use jammy-chiseled for the AOT/Extra/etc decisions.
+ /// If this is set to linux-musl-ARCH then we need to use `alpine` for all containers, and tag on `aot` or `extra` as necessary.
+ ///
+ [Required]
+ public string TargetRuntimeIdentifier { get; set; }
+
+
+ ///
+ /// If a project is self-contained then it includes a runtime, and so the runtime-deps image should be used.
+ ///
+ public bool IsSelfContained { get; set; }
+
+ ///
+ /// If a project is AOT-published then not only is it self-contained, but it can also remove some other deps - we can use the dotnet/nightly/runtime-deps variant here aot
+ ///
+ public bool IsAotPublished { get; set; }
+
+ ///
+ /// If the project is AOT'd the aot image variant doesn't contain ICU and TZData, so we use this flag to see if we need to use the `-extra` variant that does contain those packages.
+ ///
+ public bool UsesInvariantGlobalization { get; set; }
+
+ ///
+ /// If set, this expresses a preference for a variant of the container image that we infer for a project.
+ /// e.g. 'alpine', or 'jammy-chiseled'
+ ///
+ public string ContainerFamily { get; set; }
+
+ ///
+ /// The final base image computed from the inputs (or explicitly set by the user if IsUsingMicrosoftDefaultImages is true)
+ ///
+ [Output]
+ public string? ComputedContainerBaseImage { get; private set; }
+
+ private bool IsAspNetCoreProject =>
+ FrameworkReferences.Length > 0
+ && FrameworkReferences.Any(x => x.ItemSpec.Equals("Microsoft.AspNetCore.App", StringComparison.OrdinalIgnoreCase));
+
+ private bool IsMuslRid => TargetRuntimeIdentifier.StartsWith("linux-musl", StringComparison.Ordinal);
+ private bool IsBundledRuntime => IsSelfContained;
+ private bool NeedsNightlyImages => IsAotPublished;
+ private bool AllowsExperimentalTagInference => String.IsNullOrEmpty(ContainerFamily);
+
+ public ComputeDotnetBaseImageAndTag()
+ {
+ SdkVersion = "";
+ TargetFrameworkVersion = "";
+ ContainerFamily = "";
+ FrameworkReferences = [];
+ TargetRuntimeIdentifier = "";
+ }
+
+ public override bool Execute()
+ {
+ var defaultRegistry = "mcr.microsoft.com";
+ if (ComputeRepositoryAndTag(out var repository, out var tag))
+ {
+ ComputedContainerBaseImage = $"{defaultRegistry}/{repository}:{tag}";
+ }
+ return !Log.HasLoggedErrors;
+ }
+
+ private bool ComputeRepositoryAndTag([NotNullWhen(true)] out string? repository, [NotNullWhen(true)] out string? tag)
+ {
+ if (ComputeVersionPart() is (string baseVersionPart, bool versionAllowsUsingAOTAndExtrasImages))
+ {
+ Log.LogMessage("Computed base version tag of {0} from TFM {1} and SDK {2}", baseVersionPart, TargetFrameworkVersion, SdkVersion);
+ if (baseVersionPart is null)
+ {
+ repository = null;
+ tag = null;
+ return false;
+ }
+
+ var detectedRepository = (NeedsNightlyImages, IsSelfContained, IsAspNetCoreProject) switch
+ {
+ (true, true, _) when AllowsExperimentalTagInference && versionAllowsUsingAOTAndExtrasImages => "dotnet/nightly/runtime-deps",
+ (_, true, _) => "dotnet/runtime-deps",
+ (_, _, true) => "dotnet/aspnet",
+ (_, _, false) => "dotnet/runtime"
+ };
+ Log.LogMessage("Chose base image repository {0}", detectedRepository);
+ repository = detectedRepository;
+ tag = baseVersionPart;
+
+ if (!string.IsNullOrWhiteSpace(ContainerFamily))
+ {
+ // for the inferred image tags, 'family' aka 'flavor' comes after the 'version' portion (including any preview/rc segments).
+ // so it's safe to just append here
+ tag += $"-{ContainerFamily}";
+ return true;
+ }
+ else
+ {
+ if (!versionAllowsUsingAOTAndExtrasImages)
+ {
+ tag += IsMuslRid switch
+ {
+ true => "-alpine",
+ false => "" // TODO: should we default here to chiseled iamges for < 8 apps?
+ };
+ Log.LogMessage("Selected base image tag {0}", tag);
+ return true;
+ }
+ else
+ {
+ // chose the base OS
+ tag += IsMuslRid switch
+ {
+ true => "-alpine",
+ // default to chiseled for AOT, non-musl Apps
+ false when IsAotPublished => "-jammy-chiseled", // TODO: should we default here to jammy-chiseled for non-musl RIDs?
+ // default to jammy for non-AOT, non-musl Apps
+ false => ""
+ };
+
+ // now choose the variant, if any - if globalization then -extra, else -aot
+ tag += (IsAotPublished, UsesInvariantGlobalization) switch
+ {
+ (true, false) => "-extra",
+ (true, true) => "-aot",
+ _ => ""
+ };
+ Log.LogMessage("Selected base image tag {0}", tag);
+ return true;
+ }
+ }
+ }
+ else
+ {
+ repository = null;
+ tag = null;
+ return false;
+ }
+ }
+
+ private (string, bool)? ComputeVersionPart()
+ {
+ if (SemanticVersion.TryParse(TargetFrameworkVersion, out var tfm) && tfm.Major < FirstVersionWithNewTaggingScheme)
+ {
+ // < 8 TFMs don't support the -aot and -extras images
+ return ($"{tfm.Major}.{tfm.Minor}", false);
+ }
+ else if (SemanticVersion.TryParse(SdkVersion, out var version))
+ {
+ if (ComputeVersionInternal(version, tfm) is string majMinor)
+ {
+ return (majMinor, true);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ Log.LogError(Resources.Strings.InvalidSdkVersion, SdkVersion);
+ return null;
+ }
+ }
+
+ private string? ComputeVersionInternal(SemanticVersion version, SemanticVersion? tfm)
+ {
+ if (tfm != null && (tfm.Major < version.Major || tfm.Minor < version.Minor))
+ {
+ // in this case the TFM is earlier, so we are assumed to be in a stable scenario
+ return $"{tfm.Major}.{tfm.Minor}";
+ }
+ // otherwise if we're in a scenario where we're using the TFM for the given SDK version,
+ // and that SDK version may be a prerelease, so we need to handle
+ var baseImageTag = version switch
+ {
+ // all stable versions or prereleases with majors before the switch get major/minor tags
+ { IsPrerelease: false } or { Major: < FirstVersionWithNewTaggingScheme } => $"{version.Major}.{version.Minor}",
+ // prereleases after the switch for the first SDK version get major/minor-channel.bump tags
+ { IsPrerelease: true, Major: >= FirstVersionWithNewTaggingScheme, Patch: 100 } => DetermineLabelBasedOnChannel(version.Major, version.Minor, version.ReleaseLabels.ToArray()),
+ // prereleases of subsequent SDK versions still get to use the stable tags
+ { IsPrerelease: true, Major: >= FirstVersionWithNewTaggingScheme } => $"{version.Major}.{version.Minor}",
+ };
+ return baseImageTag;
+ }
+
+ private string? DetermineLabelBasedOnChannel(int major, int minor, string[] releaseLabels)
+ {
+ var channel = releaseLabels.Length > 0 ? releaseLabels[0] : null;
+ switch (channel)
+ {
+ case null or "rtm" or "servicing":
+ return $"{major}.{minor}";
+ case "rc" or "preview":
+ if (releaseLabels.Length > 1)
+ {
+ // Per the dotnet-docker team, the major.minor preview tag format is a fluke and the major.minor.0 form
+ // should be used for all previews going forward.
+ return $"{major}.{minor}.0-{channel}.{releaseLabels[1]}";
+ }
+ Log.LogError(Resources.Strings.InvalidSdkPrereleaseVersion, channel);
+ return null;
+ case "alpha" or "dev" or "ci":
+ return $"{major}.{minor}-preview";
+ default:
+ Log.LogError(Resources.Strings.InvalidSdkPrereleaseVersion, channel);
+ return null;
+ };
+ }
+}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageTag.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageTag.cs
deleted file mode 100644
index 04719cf0f22f..000000000000
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/ComputeDotnetBaseImageTag.cs
+++ /dev/null
@@ -1,109 +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 Microsoft.Build.Framework;
-using NuGet.Versioning;
-#if NETFRAMEWORK
-using System.Linq;
-#endif
-
-namespace Microsoft.NET.Build.Containers.Tasks;
-
-///
-/// Computes the base image Tag for a Microsoft-authored container image based on the tagging scheme from various SDK versions.
-///
-public sealed class ComputeDotnetBaseImageTag : Microsoft.Build.Utilities.Task
-{
- // starting in .NET 8, the container tagging scheme started incorporating the
- // 'channel' (rc/preview) and the channel increment (the numeric value after the channel name)
- // into the container tags.
- private const int FirstVersionWithNewTaggingScheme = 8;
-
- [Required]
- public string SdkVersion { get; set; }
-
- [Required]
- public string TargetFrameworkVersion { get; set; }
-
- public string ContainerFamily { get; set; }
-
- [Output]
- public string? ComputedBaseImageTag { get; private set; }
-
- public ComputeDotnetBaseImageTag()
- {
- SdkVersion = "";
- TargetFrameworkVersion = "";
- ContainerFamily = "";
- }
-
- public override bool Execute()
- {
- if (SemanticVersion.TryParse(TargetFrameworkVersion, out var tfm) && tfm.Major < FirstVersionWithNewTaggingScheme)
- {
- ComputedBaseImageTag = $"{tfm.Major}.{tfm.Minor}";
- }
- else if (SemanticVersion.TryParse(SdkVersion, out var version))
- {
- ComputedBaseImageTag = ComputeVersionInternal(version, tfm);
- }
- else
- {
- Log.LogError(Resources.Strings.InvalidSdkVersion, SdkVersion);
- return !Log.HasLoggedErrors;
- }
-
- if (!string.IsNullOrWhiteSpace(ContainerFamily))
- {
- // for the inferred image tags, 'family' aka 'flavor' comes after the 'version' portion (including any preview/rc segments).
- // so it's safe to just append here
- ComputedBaseImageTag += $"-{ContainerFamily}";
- }
- return true;
- }
-
-
- private string? ComputeVersionInternal(SemanticVersion version, SemanticVersion? tfm)
- {
- if (tfm != null && (tfm.Major < version.Major || tfm.Minor < version.Minor))
- {
- // in this case the TFM is earlier, so we are assumed to be in a stable scenario
- return $"{tfm.Major}.{tfm.Minor}";
- }
- // otherwise if we're in a scenario where we're using the TFM for the given SDK version,
- // and that SDK version may be a prerelease, so we need to handle
- var baseImageTag = (version) switch
- {
- // all stable versions or prereleases with majors before the switch get major/minor tags
- { IsPrerelease: false } or { Major: < FirstVersionWithNewTaggingScheme } => $"{version.Major}.{version.Minor}",
- // prereleases after the switch for the first SDK version get major/minor-channel.bump tags
- { IsPrerelease: true, Major: >= FirstVersionWithNewTaggingScheme, Patch: 100 } => DetermineLabelBasedOnChannel(version.Major, version.Minor, version.ReleaseLabels.ToArray()),
- // prereleases of subsequent SDK versions still get to use the stable tags
- { IsPrerelease: true, Major: >= FirstVersionWithNewTaggingScheme } => $"{version.Major}.{version.Minor}",
- };
- return baseImageTag;
- }
-
- private string? DetermineLabelBasedOnChannel(int major, int minor, string[] releaseLabels) {
- var channel = releaseLabels.Length > 0 ? releaseLabels[0] : null;
- switch (channel)
- {
- case null or "rtm" or "servicing":
- return $"{major}.{minor}";
- case "rc" or "preview":
- if (releaseLabels.Length > 1)
- {
- // Per the dotnet-docker team, the major.minor preview tag format is a fluke and the major.minor.0 form
- // should be used for all previews going forward.
- return $"{major}.{minor}.0-{channel}.{releaseLabels[1]}";
- }
- Log.LogError(Resources.Strings.InvalidSdkPrereleaseVersion, channel);
- return null;
- case "alpha" or "dev" or "ci":
- return $"{major}.{minor}-preview";
- default:
- Log.LogError(Resources.Strings.InvalidSdkPrereleaseVersion, channel);
- return null;
- };
- }
-}
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
index 669edba9590e..3ede1d5be098 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs
@@ -31,7 +31,19 @@ public sealed partial class CreateNewImage : Microsoft.Build.Utilities.Task, ICa
public override bool Execute()
{
- return Task.Run(() => ExecuteAsync(_cancellationTokenSource.Token)).GetAwaiter().GetResult();
+ try
+ {
+ Task.Run(() => ExecuteAsync(_cancellationTokenSource.Token)).GetAwaiter().GetResult();
+ }
+ catch (TaskCanceledException ex)
+ {
+ Log.LogWarningFromException(ex);
+ }
+ catch (OperationCanceledException ex)
+ {
+ Log.LogWarningFromException(ex);
+ }
+ return !Log.HasLoggedErrors;
}
internal async Task ExecuteAsync(CancellationToken cancellationToken)
@@ -64,11 +76,12 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken)
{
try
{
+ var picker = new RidGraphManifestPicker(RuntimeIdentifierGraphPath);
imageBuilder = await registry.GetImageManifestAsync(
BaseImageName,
BaseImageTag,
ContainerRuntimeIdentifier,
- RuntimeIdentifierGraphPath,
+ picker,
cancellationToken).ConfigureAwait(false);
}
catch (RepositoryNotFoundException)
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
index d68c6011b18d..6189d3d74d4e 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs
@@ -187,6 +187,11 @@ internal string GenerateCommandLineCommandsInt()
builder.AppendSwitchIfNotNull("--container-user ", ContainerUser);
}
+ if (!string.IsNullOrWhiteSpace(ArchiveOutputPath))
+ {
+ builder.AppendSwitchIfNotNull("--archiveoutputpath ", ArchiveOutputPath);
+ }
+
return builder.ToString();
void AppendSwitchIfNotNullSantized(CommandLineBuilder builder, string commandArgName, string propertyName, ITaskItem[] value)
diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/ParseContainerProperties.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/ParseContainerProperties.cs
index f7bc4c755ceb..91486be048f7 100644
--- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/ParseContainerProperties.cs
+++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/ParseContainerProperties.cs
@@ -100,12 +100,12 @@ public override bool Execute()
Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidTag), nameof(ContainerImageTag), ContainerImageTag);
}
}
- else if (ContainerImageTags.Length != 0 && TryValidateTags(ContainerImageTags, out var valids, out var invalids))
+ else if (ContainerImageTags.Length != 0)
{
- validTags = valids;
- if (invalids.Any())
+ (validTags, var invalidTags) = TryValidateTags(ContainerImageTags);
+ if (invalidTags.Any())
{
- Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidTags), nameof(ContainerImageTags), String.Join(",", invalids));
+ Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidTags), nameof(ContainerImageTags), String.Join(",", invalidTags));
return !Log.HasLoggedErrors;
}
}
@@ -200,7 +200,7 @@ private void ValidateEnvironmentVariables()
}
}
- private static bool TryValidateTags(string[] inputTags, out string[] validTags, out string[] invalidTags)
+ private static (string[] validTags, string[] invalidTags) TryValidateTags(string[] inputTags)
{
var v = new List();
var i = new List();
@@ -215,8 +215,6 @@ private static bool TryValidateTags(string[] inputTags, out string[] validTags,
i.Add(tag);
}
}
- validTags = v.ToArray();
- invalidTags = i.ToArray();
- return invalidTags.Length == 0;
+ return (v.ToArray(), i.ToArray());
}
}
diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.props b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.props
index 669e742dc309..c44684e057f1 100644
--- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.props
+++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.props
@@ -16,5 +16,5 @@
-
+
diff --git a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
index e0dc4c70ac02..ff0b034dba94 100644
--- a/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
+++ b/src/Containers/packaging/build/Microsoft.NET.Build.Containers.targets
@@ -13,6 +13,7 @@
)">true
<_ContainerIsTargetingNet8TFM>false
<_ContainerIsTargetingNet8TFM Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(_TargetFrameworkVersionWithoutV), '8.0'))">true
+ <_ContainerIsSelfContained>false
<_ContainerIsSelfContained Condition="'$(SelfContained)' == 'true' or '$(PublishSelfContained)' == 'true'">true
@@ -30,33 +31,29 @@
-
-
-
-
-
-
+ Returns="$(ContainerBaseImage)">
- <_IsAspNet Condition="@(FrameworkReference->Count()) > 0 and @(FrameworkReference->AnyHaveMetadataValue('Identity', 'Microsoft.AspnetCore.App'))">true
-
-
- <_ContainerBaseRegistry>mcr.microsoft.com
- <_ContainerBaseImageName Condition="'$(_ContainerIsSelfContained)' == 'true'">dotnet/runtime-deps
- <_ContainerBaseImageName Condition="'$(_ContainerBaseImageName)' == '' and '$(_IsAspNet)' == 'true'">dotnet/aspnet
- <_ContainerBaseImageName Condition="'$(_ContainerBaseImageName)' == ''">dotnet/runtime
-
-
- <_ContainerIsUsingMicrosoftDefaultImages Condition="'$(ContainerBaseImage)' != ''">false
+
+ $(RuntimeIdentifier)
+ linux-$(NETCoreSdkPortableRuntimeIdentifier.Split('-')[1])
<_ContainerIsUsingMicrosoftDefaultImages Condition="'$(ContainerBaseImage)' == ''">true
-
- $(_ContainerBaseRegistry)/$(_ContainerBaseImageName):$(_ContainerBaseImageTag)
+ <_ContainerIsUsingMicrosoftDefaultImages Condition="'$(ContainerBaseImage)' != ''">false
+
+
+
+
@@ -91,8 +88,6 @@
- $(RuntimeIdentifier)
- linux-$(NETCoreSdkPortableRuntimeIdentifier.Split('-')[1])
<_ContainerIsTargetingWindows>false
<_ContainerIsTargetingWindows Condition="$(ContainerRuntimeIdentifier.StartsWith('win'))">true
@@ -184,14 +179,27 @@
_ContainerVerifySDKVersion;
- ComputeContainerConfig
+ ComputeContainerConfig;
+ _CheckContainersPackage
-
+
+
+ Microsoft.NET.Build.Containers
+
+
+
+
+
+
+ true
+
+
+
+ Condition="'$(IsPublishable)' == 'true' AND '$(EnableSdkContainerSupport)' == 'true'">
$(NetCoreRoot)
diff --git a/src/Layout/redist/minimumMSBuildVersion b/src/Layout/redist/minimumMSBuildVersion
index 2d573323cace..616f65ff5653 100644
--- a/src/Layout/redist/minimumMSBuildVersion
+++ b/src/Layout/redist/minimumMSBuildVersion
@@ -1 +1 @@
-17.7.0
+17.8.3
diff --git a/src/Layout/redist/redist.csproj b/src/Layout/redist/redist.csproj
index 843561432c6e..ebdfd9e7ef94 100644
--- a/src/Layout/redist/redist.csproj
+++ b/src/Layout/redist/redist.csproj
@@ -51,15 +51,20 @@
+
-
+
+
-
-
+
+
diff --git a/src/Layout/redist/targets/PublishDotnetWatch.targets b/src/Layout/redist/targets/PublishDotnetWatch.targets
index 71c633552f01..be9cf2b38d6f 100644
--- a/src/Layout/redist/targets/PublishDotnetWatch.targets
+++ b/src/Layout/redist/targets/PublishDotnetWatch.targets
@@ -14,12 +14,17 @@
To reduce the size of the SDK, we use the compiler dependencies that are located in the `Roslyn/bincore` location
instead of shipping our own copies in the dotnet-watch tool. These assemblies will be resolved by path in the
dotnet-watch executable.
+
+ We make an exception for the Microsoft.CodeAnalysis binaries deployed with the MSBuildWorkspace BuildHosts, since those don't
+ have any logic to pick up Roslyn from another location. Those can be addressed a different way which tracked in
+ https://github.com/dotnet/roslyn/issues/70945.
-->
<_DotnetWatchInputFile Include="@(_DotnetWatchBuildOutput)"
- Condition="'%(Filename)' != 'Microsoft.CodeAnalysis' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.resources' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp.resources'"/>
+ Condition="('%(Filename)' != 'Microsoft.CodeAnalysis' and
+ '%(Filename)' != 'Microsoft.CodeAnalysis.resources' and
+ '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp' and
+ '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp.resources') or
+ $([MSBuild]::ValueOrDefault('%(FullPath)', '').Contains('BuildHost'))" />
diff --git a/src/Layout/tool_msbuild/tool_msbuild.csproj b/src/Layout/tool_msbuild/tool_msbuild.csproj
index ce1500ce853a..799d057f115c 100644
--- a/src/Layout/tool_msbuild/tool_msbuild.csproj
+++ b/src/Layout/tool_msbuild/tool_msbuild.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Microsoft.DotNet.ApiSymbolExtensions/Microsoft.DotNet.ApiSymbolExtensions.csproj b/src/Microsoft.DotNet.ApiSymbolExtensions/Microsoft.DotNet.ApiSymbolExtensions.csproj
index a014b2499ad7..3f1c9f3ddfb8 100644
--- a/src/Microsoft.DotNet.ApiSymbolExtensions/Microsoft.DotNet.ApiSymbolExtensions.csproj
+++ b/src/Microsoft.DotNet.ApiSymbolExtensions/Microsoft.DotNet.ApiSymbolExtensions.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.Configuration.targets b/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.Configuration.targets
index 162ba3f8416b..8f0fa59ba967 100644
--- a/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.Configuration.targets
+++ b/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.Configuration.targets
@@ -93,8 +93,8 @@ Copyright (c) .NET Foundation. All rights reserved.
- Microsoft.AspNetCore.Mvc.Razor.Extensions
- $(RazorSdkDirectoryRoot)tools\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll
+ Microsoft.CodeAnalysis.Razor.Compiler.Mvc
+ $(RazorSdkDirectoryRoot)tools\Microsoft.CodeAnalysis.Razor.Compiler.Mvc.dll
diff --git a/src/RazorSdk/Tool/DiscoverCommand.cs b/src/RazorSdk/Tool/DiscoverCommand.cs
index 26ffb93d20e8..fd67bdabc4bb 100644
--- a/src/RazorSdk/Tool/DiscoverCommand.cs
+++ b/src/RazorSdk/Tool/DiscoverCommand.cs
@@ -109,8 +109,8 @@ internal static void PatchExtensions(CommandOption extensionNames, CommandOption
var extensionName = extensionNames.Values[i];
var replacementFileName = extensionName switch
{
- "MVC-1.0" or "MVC-1.1" => "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.dll",
- "MVC-2.0" or "MVC-2.1" => "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.dll",
+ "MVC-1.0" or "MVC-1.1" => "Microsoft.CodeAnalysis.Razor.Compiler.Mvc.Version1_X.dll",
+ "MVC-2.0" or "MVC-2.1" => "Microsoft.CodeAnalysis.Razor.Compiler.Mvc.Version2_X.dll",
_ => null,
};
diff --git a/src/RazorSdk/Tool/JsonReaderExtensions.cs b/src/RazorSdk/Tool/JsonReaderExtensions.cs
index b27e3e009d09..af6e17715cfc 100644
--- a/src/RazorSdk/Tool/JsonReaderExtensions.cs
+++ b/src/RazorSdk/Tool/JsonReaderExtensions.cs
@@ -1,56 +1,57 @@
// 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 Newtonsoft.Json;
-namespace Microsoft.CodeAnalysis.Razor.Serialization
+namespace Microsoft.CodeAnalysis.Razor.Serialization;
+
+internal static class JsonReaderExtensions
{
- internal static class JsonReaderExtensions
+ public static bool ReadTokenAndAdvance(this JsonReader reader, JsonToken expectedTokenType, out object value)
{
- public static bool ReadTokenAndAdvance(this JsonReader reader, JsonToken expectedTokenType, out object value)
- {
- value = reader.Value;
- return reader.TokenType == expectedTokenType && reader.Read();
- }
+ value = reader.Value;
+ return reader.TokenType == expectedTokenType && reader.Read();
+ }
- public static void ReadProperties(this JsonReader reader, Action onProperty)
+ public static void ReadProperties(this JsonReader reader, Action onProperty)
+ {
+ while (reader.Read())
{
- while (reader.Read())
+ switch (reader.TokenType)
{
- switch (reader.TokenType)
- {
- case JsonToken.PropertyName:
- var propertyName = reader.Value.ToString();
- onProperty(propertyName);
- break;
- case JsonToken.EndObject:
- return;
- }
+ case JsonToken.PropertyName:
+ var propertyName = reader.Value.ToString();
+ onProperty(propertyName);
+ break;
+ case JsonToken.EndObject:
+ return;
}
}
+ }
- public static string ReadNextStringProperty(this JsonReader reader, string propertyName)
+ public static string ReadNextStringProperty(this JsonReader reader, string propertyName)
+ {
+ while (reader.Read())
{
- while (reader.Read())
+ switch (reader.TokenType)
{
- switch (reader.TokenType)
- {
- case JsonToken.PropertyName:
- Debug.Assert(reader.Value.ToString() == propertyName);
- if (reader.Read())
- {
- var value = (string)reader.Value;
- return value;
- }
- else
- {
- return null;
- }
- }
+ case JsonToken.PropertyName:
+ Debug.Assert(reader.Value.ToString() == propertyName);
+ if (reader.Read())
+ {
+ var value = (string)reader.Value;
+ return value;
+ }
+ else
+ {
+ return null;
+ }
}
-
- throw new JsonSerializationException($"Could not find string property '{propertyName}'.");
}
+
+ throw new JsonSerializationException($"Could not find string property '{propertyName}'.");
}
}
diff --git a/src/RazorSdk/Tool/RazorDiagnosticJsonConverter.cs b/src/RazorSdk/Tool/RazorDiagnosticJsonConverter.cs
index a475dd589c7c..e185dc75c617 100644
--- a/src/RazorSdk/Tool/RazorDiagnosticJsonConverter.cs
+++ b/src/RazorSdk/Tool/RazorDiagnosticJsonConverter.cs
@@ -1,73 +1,73 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace Microsoft.CodeAnalysis.Razor.Serialization
+namespace Microsoft.CodeAnalysis.Razor.Serialization;
+
+internal class RazorDiagnosticJsonConverter : JsonConverter
{
- internal class RazorDiagnosticJsonConverter : JsonConverter
+ public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
+ private const string RazorDiagnosticMessageKey = "Message";
+
+ public override bool CanConvert(Type objectType)
{
- public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
- private const string RazorDiagnosticMessageKey = "Message";
+ return typeof(RazorDiagnostic).IsAssignableFrom(objectType);
+ }
- public override bool CanConvert(Type objectType)
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType != JsonToken.StartObject)
{
- return typeof(RazorDiagnostic).IsAssignableFrom(objectType);
+ return null;
}
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.TokenType != JsonToken.StartObject)
- {
- return null;
- }
-
- var diagnostic = JObject.Load(reader);
- var id = diagnostic[nameof(RazorDiagnostic.Id)].Value();
- var severity = diagnostic[nameof(RazorDiagnostic.Severity)].Value();
- var message = diagnostic[RazorDiagnosticMessageKey].Value();
+ var diagnostic = JObject.Load(reader);
+ var id = diagnostic[nameof(RazorDiagnostic.Id)].Value();
+ var severity = diagnostic[nameof(RazorDiagnostic.Severity)].Value();
+ var message = diagnostic[RazorDiagnosticMessageKey].Value();
- var span = diagnostic[nameof(RazorDiagnostic.Span)].Value();
- var filePath = span[nameof(SourceSpan.FilePath)].Value();
- var absoluteIndex = span[nameof(SourceSpan.AbsoluteIndex)].Value();
- var lineIndex = span[nameof(SourceSpan.LineIndex)].Value();
- var characterIndex = span[nameof(SourceSpan.CharacterIndex)].Value();
- var length = span[nameof(SourceSpan.Length)].Value();
+ var span = diagnostic[nameof(RazorDiagnostic.Span)].Value();
+ var filePath = span[nameof(SourceSpan.FilePath)].Value();
+ var absoluteIndex = span[nameof(SourceSpan.AbsoluteIndex)].Value();
+ var lineIndex = span[nameof(SourceSpan.LineIndex)].Value();
+ var characterIndex = span[nameof(SourceSpan.CharacterIndex)].Value();
+ var length = span[nameof(SourceSpan.Length)].Value();
- var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
- var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
+ var descriptor = new RazorDiagnosticDescriptor(id, message, (RazorDiagnosticSeverity)severity);
+ var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
- return RazorDiagnostic.Create(descriptor, sourceSpan);
- }
+ return RazorDiagnostic.Create(descriptor, sourceSpan);
+ }
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- var diagnostic = (RazorDiagnostic)value;
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var diagnostic = (RazorDiagnostic)value;
- writer.WriteStartObject();
- WriteProperty(writer, nameof(RazorDiagnostic.Id), diagnostic.Id);
- WriteProperty(writer, nameof(RazorDiagnostic.Severity), (int)diagnostic.Severity);
- WriteProperty(writer, RazorDiagnosticMessageKey, diagnostic.GetMessage(CultureInfo.CurrentCulture));
+ writer.WriteStartObject();
+ WriteProperty(writer, nameof(RazorDiagnostic.Id), diagnostic.Id);
+ WriteProperty(writer, nameof(RazorDiagnostic.Severity), (int)diagnostic.Severity);
+ WriteProperty(writer, RazorDiagnosticMessageKey, diagnostic.GetMessage(CultureInfo.CurrentCulture));
- writer.WritePropertyName(nameof(RazorDiagnostic.Span));
- writer.WriteStartObject();
- WriteProperty(writer, nameof(SourceSpan.FilePath), diagnostic.Span.FilePath);
- WriteProperty(writer, nameof(SourceSpan.AbsoluteIndex), diagnostic.Span.AbsoluteIndex);
- WriteProperty(writer, nameof(SourceSpan.LineIndex), diagnostic.Span.LineIndex);
- WriteProperty(writer, nameof(SourceSpan.CharacterIndex), diagnostic.Span.CharacterIndex);
- WriteProperty(writer, nameof(SourceSpan.Length), diagnostic.Span.Length);
- writer.WriteEndObject();
+ writer.WritePropertyName(nameof(RazorDiagnostic.Span));
+ writer.WriteStartObject();
+ WriteProperty(writer, nameof(SourceSpan.FilePath), diagnostic.Span.FilePath);
+ WriteProperty(writer, nameof(SourceSpan.AbsoluteIndex), diagnostic.Span.AbsoluteIndex);
+ WriteProperty(writer, nameof(SourceSpan.LineIndex), diagnostic.Span.LineIndex);
+ WriteProperty(writer, nameof(SourceSpan.CharacterIndex), diagnostic.Span.CharacterIndex);
+ WriteProperty(writer, nameof(SourceSpan.Length), diagnostic.Span.Length);
+ writer.WriteEndObject();
- writer.WriteEndObject();
- }
+ writer.WriteEndObject();
+ }
- private void WriteProperty(JsonWriter writer, string key, T value)
- {
- writer.WritePropertyName(key);
- writer.WriteValue(value);
- }
+ private void WriteProperty(JsonWriter writer, string key, T value)
+ {
+ writer.WritePropertyName(key);
+ writer.WriteValue(value);
}
}
diff --git a/src/RazorSdk/Tool/TagHelperDescriptorJsonConverter.cs b/src/RazorSdk/Tool/TagHelperDescriptorJsonConverter.cs
index f87bdf6683e7..ae0f7acc76e8 100644
--- a/src/RazorSdk/Tool/TagHelperDescriptorJsonConverter.cs
+++ b/src/RazorSdk/Tool/TagHelperDescriptorJsonConverter.cs
@@ -1,850 +1,865 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
-namespace Microsoft.CodeAnalysis.Razor.Serialization
+namespace Microsoft.CodeAnalysis.Razor.Serialization;
+
+internal class TagHelperDescriptorJsonConverter : JsonConverter
{
- internal class TagHelperDescriptorJsonConverter : JsonConverter
+ public static readonly TagHelperDescriptorJsonConverter Instance = new();
+
+ public override bool CanConvert(Type objectType)
{
- public static readonly TagHelperDescriptorJsonConverter Instance = new TagHelperDescriptorJsonConverter();
+ return typeof(TagHelperDescriptor).IsAssignableFrom(objectType);
+ }
- public override bool CanConvert(Type objectType)
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType != JsonToken.StartObject)
{
- return typeof(TagHelperDescriptor).IsAssignableFrom(objectType);
+ return null;
}
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.TokenType != JsonToken.StartObject)
- {
- return null;
- }
-
- // Required tokens (order matters)
- var descriptorKind = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Kind));
- var typeName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Name));
- var assemblyName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.AssemblyName));
- var builder = TagHelperDescriptorBuilder.Create(descriptorKind, typeName, assemblyName);
+ // Required tokens (order matters)
+ var descriptorKind = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Kind));
+ var typeName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Name));
+ var assemblyName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.AssemblyName));
+ using var _ = TagHelperDescriptorBuilder.GetPooledInstance(descriptorKind, typeName, assemblyName, out var builder);
- reader.ReadProperties(propertyName =>
+ reader.ReadProperties(propertyName =>
+ {
+ switch (propertyName)
{
- switch (propertyName)
- {
- case nameof(TagHelperDescriptor.Documentation):
- if (reader.Read())
- {
- var documentation = (string)reader.Value;
- builder.Documentation = documentation;
- }
- break;
- case nameof(TagHelperDescriptor.TagOutputHint):
- if (reader.Read())
- {
- var tagOutputHint = (string)reader.Value;
- builder.TagOutputHint = tagOutputHint;
- }
- break;
- case nameof(TagHelperDescriptor.CaseSensitive):
- if (reader.Read())
- {
- var caseSensitive = (bool)reader.Value;
- builder.CaseSensitive = caseSensitive;
- }
- break;
- case nameof(TagHelperDescriptor.TagMatchingRules):
- ReadTagMatchingRules(reader, builder);
- break;
- case nameof(TagHelperDescriptor.BoundAttributes):
- ReadBoundAttributes(reader, builder);
- break;
- case nameof(TagHelperDescriptor.AllowedChildTags):
- ReadAllowedChildTags(reader, builder);
- break;
- case nameof(TagHelperDescriptor.Diagnostics):
- ReadDiagnostics(reader, builder.Diagnostics);
- break;
- case nameof(TagHelperDescriptor.Metadata):
- ReadMetadata(reader, builder.Metadata);
- break;
- }
- });
+ case nameof(TagHelperDescriptor.Documentation):
+ if (reader.Read())
+ {
+ var documentation = (string)reader.Value;
+ builder.SetDocumentation(documentation);
+ }
+ break;
+ case nameof(TagHelperDescriptor.TagOutputHint):
+ if (reader.Read())
+ {
+ var tagOutputHint = (string)reader.Value;
+ builder.TagOutputHint = tagOutputHint;
+ }
+ break;
+ case nameof(TagHelperDescriptor.CaseSensitive):
+ if (reader.Read())
+ {
+ var caseSensitive = (bool)reader.Value;
+ builder.CaseSensitive = caseSensitive;
+ }
+ break;
+ case nameof(TagHelperDescriptor.TagMatchingRules):
+ ReadTagMatchingRules(reader, builder);
+ break;
+ case nameof(TagHelperDescriptor.BoundAttributes):
+ ReadBoundAttributes(reader, builder);
+ break;
+ case nameof(TagHelperDescriptor.AllowedChildTags):
+ ReadAllowedChildTags(reader, builder);
+ break;
+ case nameof(TagHelperDescriptor.Diagnostics):
+ ReadDiagnostics(reader, builder.Diagnostics);
+ break;
+ case nameof(TagHelperDescriptor.Metadata):
+ ReadMetadata(reader, builder.Metadata);
+ break;
+ }
+ });
+
+ return builder.Build();
+ }
- return builder.Build();
- }
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var tagHelper = (TagHelperDescriptor)value;
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- var tagHelper = (TagHelperDescriptor)value;
+ writer.WriteStartObject();
- writer.WriteStartObject();
+ writer.WritePropertyName(nameof(TagHelperDescriptor.Kind));
+ writer.WriteValue(tagHelper.Kind);
- writer.WritePropertyName(nameof(TagHelperDescriptor.Kind));
- writer.WriteValue(tagHelper.Kind);
+ writer.WritePropertyName(nameof(TagHelperDescriptor.Name));
+ writer.WriteValue(tagHelper.Name);
- writer.WritePropertyName(nameof(TagHelperDescriptor.Name));
- writer.WriteValue(tagHelper.Name);
+ writer.WritePropertyName(nameof(TagHelperDescriptor.AssemblyName));
+ writer.WriteValue(tagHelper.AssemblyName);
- writer.WritePropertyName(nameof(TagHelperDescriptor.AssemblyName));
- writer.WriteValue(tagHelper.AssemblyName);
+ if (tagHelper.Documentation != null)
+ {
+ writer.WritePropertyName(nameof(TagHelperDescriptor.Documentation));
+ writer.WriteValue(tagHelper.Documentation);
+ }
- if (tagHelper.Documentation != null)
- {
- writer.WritePropertyName(nameof(TagHelperDescriptor.Documentation));
- writer.WriteValue(tagHelper.Documentation);
- }
+ if (tagHelper.TagOutputHint != null)
+ {
+ writer.WritePropertyName(nameof(TagHelperDescriptor.TagOutputHint));
+ writer.WriteValue(tagHelper.TagOutputHint);
+ }
- if (tagHelper.TagOutputHint != null)
- {
- writer.WritePropertyName(nameof(TagHelperDescriptor.TagOutputHint));
- writer.WriteValue(tagHelper.TagOutputHint);
- }
+ writer.WritePropertyName(nameof(TagHelperDescriptor.CaseSensitive));
+ writer.WriteValue(tagHelper.CaseSensitive);
- writer.WritePropertyName(nameof(TagHelperDescriptor.CaseSensitive));
- writer.WriteValue(tagHelper.CaseSensitive);
+ writer.WritePropertyName(nameof(TagHelperDescriptor.TagMatchingRules));
+ writer.WriteStartArray();
+ foreach (var ruleDescriptor in tagHelper.TagMatchingRules)
+ {
+ WriteTagMatchingRule(writer, ruleDescriptor, serializer);
+ }
+ writer.WriteEndArray();
- writer.WritePropertyName(nameof(TagHelperDescriptor.TagMatchingRules));
+ if (tagHelper.BoundAttributes != null && tagHelper.BoundAttributes.Length > 0)
+ {
+ writer.WritePropertyName(nameof(TagHelperDescriptor.BoundAttributes));
writer.WriteStartArray();
- foreach (var ruleDescriptor in tagHelper.TagMatchingRules)
+ foreach (var boundAttribute in tagHelper.BoundAttributes)
{
- WriteTagMatchingRule(writer, ruleDescriptor, serializer);
+ WriteBoundAttribute(writer, boundAttribute, serializer);
}
writer.WriteEndArray();
+ }
- if (tagHelper.BoundAttributes != null && tagHelper.BoundAttributes.Count > 0)
- {
- writer.WritePropertyName(nameof(TagHelperDescriptor.BoundAttributes));
- writer.WriteStartArray();
- foreach (var boundAttribute in tagHelper.BoundAttributes)
- {
- WriteBoundAttribute(writer, boundAttribute, serializer);
- }
- writer.WriteEndArray();
- }
-
- if (tagHelper.AllowedChildTags != null && tagHelper.AllowedChildTags.Count > 0)
- {
- writer.WritePropertyName(nameof(TagHelperDescriptor.AllowedChildTags));
- writer.WriteStartArray();
- foreach (var allowedChildTag in tagHelper.AllowedChildTags)
- {
- WriteAllowedChildTags(writer, allowedChildTag, serializer);
- }
- writer.WriteEndArray();
- }
-
- if (tagHelper.Diagnostics != null && tagHelper.Diagnostics.Count > 0)
+ if (tagHelper.AllowedChildTags != null && tagHelper.AllowedChildTags.Length > 0)
+ {
+ writer.WritePropertyName(nameof(TagHelperDescriptor.AllowedChildTags));
+ writer.WriteStartArray();
+ foreach (var allowedChildTag in tagHelper.AllowedChildTags)
{
- writer.WritePropertyName(nameof(TagHelperDescriptor.Diagnostics));
- serializer.Serialize(writer, tagHelper.Diagnostics);
+ WriteAllowedChildTags(writer, allowedChildTag, serializer);
}
-
- writer.WritePropertyName(nameof(TagHelperDescriptor.Metadata));
- WriteMetadata(writer, tagHelper.Metadata);
-
- writer.WriteEndObject();
+ writer.WriteEndArray();
}
- private static void WriteAllowedChildTags(JsonWriter writer, AllowedChildTagDescriptor allowedChildTag, JsonSerializer serializer)
+ if (tagHelper.Diagnostics != null && tagHelper.Diagnostics.Length > 0)
{
- writer.WriteStartObject();
-
- writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Name));
- writer.WriteValue(allowedChildTag.Name);
+ writer.WritePropertyName(nameof(TagHelperDescriptor.Diagnostics));
+ serializer.Serialize(writer, tagHelper.Diagnostics);
+ }
- writer.WritePropertyName(nameof(AllowedChildTagDescriptor.DisplayName));
- writer.WriteValue(allowedChildTag.DisplayName);
+ writer.WritePropertyName(nameof(TagHelperDescriptor.Metadata));
+ WriteMetadata(writer, tagHelper.Metadata);
- writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Diagnostics));
- serializer.Serialize(writer, allowedChildTag.Diagnostics);
+ writer.WriteEndObject();
+ }
- writer.WriteEndObject();
- }
+ private static void WriteAllowedChildTags(JsonWriter writer, AllowedChildTagDescriptor allowedChildTag, JsonSerializer serializer)
+ {
+ writer.WriteStartObject();
- private static void WriteBoundAttribute(JsonWriter writer, BoundAttributeDescriptor boundAttribute, JsonSerializer serializer)
- {
- writer.WriteStartObject();
+ writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Name));
+ writer.WriteValue(allowedChildTag.Name);
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.Kind));
- writer.WriteValue(boundAttribute.Kind);
+ writer.WritePropertyName(nameof(AllowedChildTagDescriptor.DisplayName));
+ writer.WriteValue(allowedChildTag.DisplayName);
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.Name));
- writer.WriteValue(boundAttribute.Name);
+ writer.WritePropertyName(nameof(AllowedChildTagDescriptor.Diagnostics));
+ serializer.Serialize(writer, allowedChildTag.Diagnostics);
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.TypeName));
- writer.WriteValue(boundAttribute.TypeName);
+ writer.WriteEndObject();
+ }
- if (boundAttribute.IsEnum)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEnum));
- writer.WriteValue(boundAttribute.IsEnum);
- }
+ private static void WriteBoundAttribute(JsonWriter writer, BoundAttributeDescriptor boundAttribute, JsonSerializer serializer)
+ {
+ writer.WriteStartObject();
- if (boundAttribute.IndexerNamePrefix != null)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
- writer.WriteValue(boundAttribute.IndexerNamePrefix);
- }
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Kind));
+ writer.WriteValue(boundAttribute.Kind);
- if (boundAttribute.IndexerTypeName != null)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerTypeName));
- writer.WriteValue(boundAttribute.IndexerTypeName);
- }
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Name));
+ writer.WriteValue(boundAttribute.Name);
- if (boundAttribute.Documentation != null)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.Documentation));
- writer.WriteValue(boundAttribute.Documentation);
- }
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.TypeName));
+ writer.WriteValue(boundAttribute.TypeName);
- if (boundAttribute.Diagnostics != null && boundAttribute.Diagnostics.Count > 0)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.Diagnostics));
- serializer.Serialize(writer, boundAttribute.Diagnostics);
- }
+ if (boundAttribute.IsEnum)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEnum));
+ writer.WriteValue(boundAttribute.IsEnum);
+ }
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.Metadata));
- WriteMetadata(writer, boundAttribute.Metadata);
+ if (boundAttribute.IndexerNamePrefix != null)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
+ writer.WriteValue(boundAttribute.IndexerNamePrefix);
+ }
- if (boundAttribute.BoundAttributeParameters != null && boundAttribute.BoundAttributeParameters.Count > 0)
- {
- writer.WritePropertyName(nameof(BoundAttributeDescriptor.BoundAttributeParameters));
- writer.WriteStartArray();
- foreach (var boundAttributeParameter in boundAttribute.BoundAttributeParameters)
- {
- WriteBoundAttributeParameter(writer, boundAttributeParameter, serializer);
- }
- writer.WriteEndArray();
- }
+ if (boundAttribute.IsEditorRequired)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEditorRequired));
+ writer.WriteValue(boundAttribute.IsEditorRequired);
+ }
- writer.WriteEndObject();
+ if (boundAttribute.IndexerTypeName != null)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerTypeName));
+ writer.WriteValue(boundAttribute.IndexerTypeName);
}
- private static void WriteBoundAttributeParameter(JsonWriter writer, BoundAttributeParameterDescriptor boundAttributeParameter, JsonSerializer serializer)
+ if (boundAttribute.Documentation != null)
{
- writer.WriteStartObject();
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Documentation));
+ writer.WriteValue(boundAttribute.Documentation);
+ }
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Name));
- writer.WriteValue(boundAttributeParameter.Name);
+ if (boundAttribute.Diagnostics != null && boundAttribute.Diagnostics.Length > 0)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Diagnostics));
+ serializer.Serialize(writer, boundAttribute.Diagnostics);
+ }
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.TypeName));
- writer.WriteValue(boundAttributeParameter.TypeName);
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Metadata));
+ WriteMetadata(writer, boundAttribute.Metadata);
- if (boundAttributeParameter.IsEnum != default)
+ if (boundAttribute.Parameters != null && boundAttribute.Parameters.Length > 0)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeDescriptor.Parameters));
+ writer.WriteStartArray();
+ foreach (var boundAttributeParameter in boundAttribute.Parameters)
{
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.IsEnum));
- writer.WriteValue(boundAttributeParameter.IsEnum);
+ WriteBoundAttributeParameter(writer, boundAttributeParameter, serializer);
}
+ writer.WriteEndArray();
+ }
- if (boundAttributeParameter.Documentation != null)
- {
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Documentation));
- writer.WriteValue(boundAttributeParameter.Documentation);
- }
+ writer.WriteEndObject();
+ }
- if (boundAttributeParameter.Diagnostics != null && boundAttributeParameter.Diagnostics.Count > 0)
- {
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Diagnostics));
- serializer.Serialize(writer, boundAttributeParameter.Diagnostics);
- }
+ private static void WriteBoundAttributeParameter(JsonWriter writer, BoundAttributeParameterDescriptor boundAttributeParameter, JsonSerializer serializer)
+ {
+ writer.WriteStartObject();
+
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Name));
+ writer.WriteValue(boundAttributeParameter.Name);
- writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Metadata));
- WriteMetadata(writer, boundAttributeParameter.Metadata);
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.TypeName));
+ writer.WriteValue(boundAttributeParameter.TypeName);
- writer.WriteEndObject();
+ if (boundAttributeParameter.IsEnum != default)
+ {
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.IsEnum));
+ writer.WriteValue(boundAttributeParameter.IsEnum);
}
- private static void WriteMetadata(JsonWriter writer, IReadOnlyDictionary metadata)
+ if (boundAttributeParameter.Documentation != null)
{
- writer.WriteStartObject();
- foreach (var kvp in metadata)
- {
- writer.WritePropertyName(kvp.Key);
- writer.WriteValue(kvp.Value);
- }
- writer.WriteEndObject();
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Documentation));
+ writer.WriteValue(boundAttributeParameter.Documentation);
}
- private static void WriteTagMatchingRule(JsonWriter writer, TagMatchingRuleDescriptor ruleDescriptor, JsonSerializer serializer)
+ if (boundAttributeParameter.Diagnostics != null && boundAttributeParameter.Diagnostics.Length > 0)
{
- writer.WriteStartObject();
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Diagnostics));
+ serializer.Serialize(writer, boundAttributeParameter.Diagnostics);
+ }
- writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagName));
- writer.WriteValue(ruleDescriptor.TagName);
+ writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Metadata));
+ WriteMetadata(writer, boundAttributeParameter.Metadata);
- if (ruleDescriptor.ParentTag != null)
- {
- writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.ParentTag));
- writer.WriteValue(ruleDescriptor.ParentTag);
- }
+ writer.WriteEndObject();
+ }
- if (ruleDescriptor.TagStructure != default)
- {
- writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagStructure));
- writer.WriteValue(ruleDescriptor.TagStructure);
- }
+ private static void WriteMetadata(JsonWriter writer, MetadataCollection metadata)
+ {
+ writer.WriteStartObject();
+ foreach (var kvp in metadata)
+ {
+ writer.WritePropertyName(kvp.Key);
+ writer.WriteValue(kvp.Value);
+ }
+ writer.WriteEndObject();
+ }
- if (ruleDescriptor.Attributes != null && ruleDescriptor.Attributes.Count > 0)
- {
- writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Attributes));
- writer.WriteStartArray();
- foreach (var requiredAttribute in ruleDescriptor.Attributes)
- {
- WriteRequiredAttribute(writer, requiredAttribute, serializer);
- }
- writer.WriteEndArray();
- }
+ private static void WriteTagMatchingRule(JsonWriter writer, TagMatchingRuleDescriptor ruleDescriptor, JsonSerializer serializer)
+ {
+ writer.WriteStartObject();
- if (ruleDescriptor.Diagnostics != null && ruleDescriptor.Diagnostics.Count > 0)
- {
- writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Diagnostics));
- serializer.Serialize(writer, ruleDescriptor.Diagnostics);
- }
+ writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagName));
+ writer.WriteValue(ruleDescriptor.TagName);
- writer.WriteEndObject();
+ if (ruleDescriptor.ParentTag != null)
+ {
+ writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.ParentTag));
+ writer.WriteValue(ruleDescriptor.ParentTag);
}
- private static void WriteRequiredAttribute(JsonWriter writer, RequiredAttributeDescriptor requiredAttribute, JsonSerializer serializer)
+ if (ruleDescriptor.TagStructure != default)
{
- writer.WriteStartObject();
-
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Name));
- writer.WriteValue(requiredAttribute.Name);
+ writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagStructure));
+ writer.WriteValue(ruleDescriptor.TagStructure);
+ }
- if (requiredAttribute.NameComparison != default)
+ if (ruleDescriptor.Attributes != null && ruleDescriptor.Attributes.Length > 0)
+ {
+ writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Attributes));
+ writer.WriteStartArray();
+ foreach (var requiredAttribute in ruleDescriptor.Attributes)
{
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.NameComparison));
- writer.WriteValue(requiredAttribute.NameComparison);
+ WriteRequiredAttribute(writer, requiredAttribute, serializer);
}
+ writer.WriteEndArray();
+ }
- if (requiredAttribute.Value != null)
- {
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Value));
- writer.WriteValue(requiredAttribute.Value);
- }
+ if (ruleDescriptor.Diagnostics != null && ruleDescriptor.Diagnostics.Length > 0)
+ {
+ writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Diagnostics));
+ serializer.Serialize(writer, ruleDescriptor.Diagnostics);
+ }
- if (requiredAttribute.ValueComparison != default)
- {
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.ValueComparison));
- writer.WriteValue(requiredAttribute.ValueComparison);
- }
+ writer.WriteEndObject();
+ }
- if (requiredAttribute.Diagnostics != null && requiredAttribute.Diagnostics.Count > 0)
- {
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Diagnostics));
- serializer.Serialize(writer, requiredAttribute.Diagnostics);
- }
+ private static void WriteRequiredAttribute(JsonWriter writer, RequiredAttributeDescriptor requiredAttribute, JsonSerializer serializer)
+ {
+ writer.WriteStartObject();
- if (requiredAttribute.Metadata != null && requiredAttribute.Metadata.Count > 0)
- {
- writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Metadata));
- WriteMetadata(writer, requiredAttribute.Metadata);
- }
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Name));
+ writer.WriteValue(requiredAttribute.Name);
- writer.WriteEndObject();
+ if (requiredAttribute.NameComparison != default)
+ {
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.NameComparison));
+ writer.WriteValue(requiredAttribute.NameComparison);
}
- private static void ReadBoundAttributes(JsonReader reader, TagHelperDescriptorBuilder builder)
+ if (requiredAttribute.Value != null)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
-
- do
- {
- ReadBoundAttribute(reader, builder);
- } while (reader.TokenType != JsonToken.EndArray);
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Value));
+ writer.WriteValue(requiredAttribute.Value);
}
- private static void ReadBoundAttribute(JsonReader reader, TagHelperDescriptorBuilder builder)
+ if (requiredAttribute.ValueComparison != default)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
-
- builder.BindAttribute(attribute =>
- {
- reader.ReadProperties(propertyName =>
- {
- switch (propertyName)
- {
- case nameof(BoundAttributeDescriptor.Name):
- if (reader.Read())
- {
- var name = (string)reader.Value;
- attribute.Name = name;
- }
- break;
- case nameof(BoundAttributeDescriptor.TypeName):
- if (reader.Read())
- {
- var typeName = (string)reader.Value;
- attribute.TypeName = typeName;
- }
- break;
- case nameof(BoundAttributeDescriptor.Documentation):
- if (reader.Read())
- {
- var documentation = (string)reader.Value;
- attribute.Documentation = documentation;
- }
- break;
- case nameof(BoundAttributeDescriptor.IndexerNamePrefix):
- if (reader.Read())
- {
- var indexerNamePrefix = (string)reader.Value;
- if (indexerNamePrefix != null)
- {
- attribute.IsDictionary = true;
- attribute.IndexerAttributeNamePrefix = indexerNamePrefix;
- }
- }
- break;
- case nameof(BoundAttributeDescriptor.IndexerTypeName):
- if (reader.Read())
- {
- var indexerTypeName = (string)reader.Value;
- if (indexerTypeName != null)
- {
- attribute.IsDictionary = true;
- attribute.IndexerValueTypeName = indexerTypeName;
- }
- }
- break;
- case nameof(BoundAttributeDescriptor.IsEnum):
- if (reader.Read())
- {
- var isEnum = (bool)reader.Value;
- attribute.IsEnum = isEnum;
- }
- break;
- case nameof(BoundAttributeDescriptor.BoundAttributeParameters):
- ReadBoundAttributeParameters(reader, attribute);
- break;
- case nameof(BoundAttributeDescriptor.Diagnostics):
- ReadDiagnostics(reader, attribute.Diagnostics);
- break;
- case nameof(BoundAttributeDescriptor.Metadata):
- ReadMetadata(reader, attribute.Metadata);
- break;
- }
- });
- });
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.ValueComparison));
+ writer.WriteValue(requiredAttribute.ValueComparison);
}
- private static void ReadBoundAttributeParameters(JsonReader reader, BoundAttributeDescriptorBuilder builder)
+ if (requiredAttribute.Diagnostics != null && requiredAttribute.Diagnostics.Length > 0)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
-
- do
- {
- ReadBoundAttributeParameter(reader, builder);
- } while (reader.TokenType != JsonToken.EndArray);
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Diagnostics));
+ serializer.Serialize(writer, requiredAttribute.Diagnostics);
}
- private static void ReadBoundAttributeParameter(JsonReader reader, BoundAttributeDescriptorBuilder builder)
+ if (requiredAttribute.Metadata != null && requiredAttribute.Metadata.Count > 0)
{
- if (!reader.Read())
- {
- return;
- }
+ writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Metadata));
+ WriteMetadata(writer, requiredAttribute.Metadata);
+ }
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ writer.WriteEndObject();
+ }
- builder.BindAttributeParameter(parameter =>
- {
- reader.ReadProperties(propertyName =>
- {
- switch (propertyName)
- {
- case nameof(BoundAttributeParameterDescriptor.Name):
- if (reader.Read())
- {
- var name = (string)reader.Value;
- parameter.Name = name;
- }
- break;
- case nameof(BoundAttributeParameterDescriptor.TypeName):
- if (reader.Read())
- {
- var typeName = (string)reader.Value;
- parameter.TypeName = typeName;
- }
- break;
- case nameof(BoundAttributeParameterDescriptor.IsEnum):
- if (reader.Read())
- {
- var isEnum = (bool)reader.Value;
- parameter.IsEnum = isEnum;
- }
- break;
- case nameof(BoundAttributeParameterDescriptor.Documentation):
- if (reader.Read())
- {
- var documentation = (string)reader.Value;
- parameter.Documentation = documentation;
- }
- break;
- case nameof(BoundAttributeParameterDescriptor.Metadata):
- ReadMetadata(reader, parameter.Metadata);
- break;
- case nameof(BoundAttributeParameterDescriptor.Diagnostics):
- ReadDiagnostics(reader, parameter.Diagnostics);
- break;
- }
- });
- });
+ private static void ReadBoundAttributes(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
}
- private static void ReadTagMatchingRules(JsonReader reader, TagHelperDescriptorBuilder builder)
+ if (reader.TokenType != JsonToken.StartArray)
{
- if (!reader.Read())
- {
- return;
- }
+ return;
+ }
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
+ do
+ {
+ ReadBoundAttribute(reader, builder);
+ } while (reader.TokenType != JsonToken.EndArray);
+ }
- do
- {
- ReadTagMatchingRule(reader, builder);
- } while (reader.TokenType != JsonToken.EndArray);
+ private static void ReadBoundAttribute(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
}
- private static void ReadTagMatchingRule(JsonReader reader, TagHelperDescriptorBuilder builder)
+ if (reader.TokenType != JsonToken.StartObject)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ return;
+ }
- builder.TagMatchingRule(rule =>
+ builder.BindAttribute(attribute =>
+ {
+ reader.ReadProperties(propertyName =>
{
- reader.ReadProperties(propertyName =>
+ switch (propertyName)
{
- switch (propertyName)
- {
- case nameof(TagMatchingRuleDescriptor.TagName):
- if (reader.Read())
+ case nameof(BoundAttributeDescriptor.Name):
+ if (reader.Read())
+ {
+ var name = (string)reader.Value;
+ attribute.Name = name;
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.TypeName):
+ if (reader.Read())
+ {
+ var typeName = (string)reader.Value;
+ attribute.TypeName = typeName;
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.Documentation):
+ if (reader.Read())
+ {
+ var documentation = (string)reader.Value;
+ attribute.SetDocumentation(documentation);
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.IndexerNamePrefix):
+ if (reader.Read())
+ {
+ var indexerNamePrefix = (string)reader.Value;
+ if (indexerNamePrefix != null)
{
- var tagName = (string)reader.Value;
- rule.TagName = tagName;
+ attribute.IsDictionary = true;
+ attribute.IndexerAttributeNamePrefix = indexerNamePrefix;
}
- break;
- case nameof(TagMatchingRuleDescriptor.ParentTag):
- if (reader.Read())
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.IndexerTypeName):
+ if (reader.Read())
+ {
+ var indexerTypeName = (string)reader.Value;
+ if (indexerTypeName != null)
{
- var parentTag = (string)reader.Value;
- rule.ParentTag = parentTag;
+ attribute.IsDictionary = true;
+ attribute.IndexerValueTypeName = indexerTypeName;
}
- break;
- case nameof(TagMatchingRuleDescriptor.TagStructure):
- rule.TagStructure = (TagStructure)reader.ReadAsInt32();
- break;
- case nameof(TagMatchingRuleDescriptor.Attributes):
- ReadRequiredAttributeValues(reader, rule);
- break;
- case nameof(TagMatchingRuleDescriptor.Diagnostics):
- ReadDiagnostics(reader, rule.Diagnostics);
- break;
- }
- });
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.IsEnum):
+ if (reader.Read())
+ {
+ var isEnum = (bool)reader.Value;
+ attribute.IsEnum = isEnum;
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.IsEditorRequired):
+ if (reader.Read())
+ {
+ var value = (bool)reader.Value;
+ attribute.IsEditorRequired = value;
+ }
+ break;
+ case nameof(BoundAttributeDescriptor.Parameters):
+ ReadBoundAttributeParameters(reader, attribute);
+ break;
+ case nameof(BoundAttributeDescriptor.Diagnostics):
+ ReadDiagnostics(reader, attribute.Diagnostics);
+ break;
+ case nameof(BoundAttributeDescriptor.Metadata):
+ ReadMetadata(reader, attribute.Metadata);
+ break;
+ }
});
+ });
+ }
+
+ private static void ReadBoundAttributeParameters(JsonReader reader, BoundAttributeDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
}
- private static void ReadRequiredAttributeValues(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
+ if (reader.TokenType != JsonToken.StartArray)
{
- if (!reader.Read())
- {
- return;
- }
+ return;
+ }
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
+ do
+ {
+ ReadBoundAttributeParameter(reader, builder);
+ } while (reader.TokenType != JsonToken.EndArray);
+ }
- do
- {
- ReadRequiredAttribute(reader, builder);
- } while (reader.TokenType != JsonToken.EndArray);
+ private static void ReadBoundAttributeParameter(JsonReader reader, BoundAttributeDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
}
- private static void ReadRequiredAttribute(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
+ if (reader.TokenType != JsonToken.StartObject)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ return;
+ }
- builder.Attribute(attribute =>
+ builder.BindAttributeParameter(parameter =>
+ {
+ reader.ReadProperties(propertyName =>
{
- reader.ReadProperties(propertyName =>
+ switch (propertyName)
{
- switch (propertyName)
- {
- case nameof(RequiredAttributeDescriptor.Name):
- if (reader.Read())
- {
- var name = (string)reader.Value;
- attribute.Name = name;
- }
- break;
- case nameof(RequiredAttributeDescriptor.NameComparison):
- var nameComparison = (RequiredAttributeDescriptor.NameComparisonMode)reader.ReadAsInt32();
- attribute.NameComparisonMode = nameComparison;
- break;
- case nameof(RequiredAttributeDescriptor.Value):
- if (reader.Read())
- {
- var value = (string)reader.Value;
- attribute.Value = value;
- }
- break;
- case nameof(RequiredAttributeDescriptor.ValueComparison):
- var valueComparison = (RequiredAttributeDescriptor.ValueComparisonMode)reader.ReadAsInt32();
- attribute.ValueComparisonMode = valueComparison;
- break;
- case nameof(RequiredAttributeDescriptor.Diagnostics):
- ReadDiagnostics(reader, attribute.Diagnostics);
- break;
- case nameof(RequiredAttributeDescriptor.Metadata):
- ReadMetadata(reader, attribute.Metadata);
- break;
- }
- });
+ case nameof(BoundAttributeParameterDescriptor.Name):
+ if (reader.Read())
+ {
+ var name = (string)reader.Value;
+ parameter.Name = name;
+ }
+ break;
+ case nameof(BoundAttributeParameterDescriptor.TypeName):
+ if (reader.Read())
+ {
+ var typeName = (string)reader.Value;
+ parameter.TypeName = typeName;
+ }
+ break;
+ case nameof(BoundAttributeParameterDescriptor.IsEnum):
+ if (reader.Read())
+ {
+ var isEnum = (bool)reader.Value;
+ parameter.IsEnum = isEnum;
+ }
+ break;
+ case nameof(BoundAttributeParameterDescriptor.Documentation):
+ if (reader.Read())
+ {
+ var documentation = (string)reader.Value;
+ parameter.SetDocumentation(documentation);
+ }
+ break;
+ case nameof(BoundAttributeParameterDescriptor.Metadata):
+ ReadMetadata(reader, parameter.Metadata);
+ break;
+ case nameof(BoundAttributeParameterDescriptor.Diagnostics):
+ ReadDiagnostics(reader, parameter.Diagnostics);
+ break;
+ }
});
- }
+ });
+ }
- private static void ReadAllowedChildTags(JsonReader reader, TagHelperDescriptorBuilder builder)
+ private static void ReadTagMatchingRules(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
-
- do
- {
- ReadAllowedChildTag(reader, builder);
- } while (reader.TokenType != JsonToken.EndArray);
+ return;
}
- private static void ReadAllowedChildTag(JsonReader reader, TagHelperDescriptorBuilder builder)
+ if (reader.TokenType != JsonToken.StartArray)
{
- if (!reader.Read())
- {
- return;
- }
+ return;
+ }
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ do
+ {
+ ReadTagMatchingRule(reader, builder);
+ } while (reader.TokenType != JsonToken.EndArray);
+ }
- builder.AllowChildTag(childTag =>
- {
- reader.ReadProperties(propertyName =>
- {
- switch (propertyName)
- {
- case nameof(AllowedChildTagDescriptor.Name):
- if (reader.Read())
- {
- var name = (string)reader.Value;
- childTag.Name = name;
- }
- break;
- case nameof(AllowedChildTagDescriptor.DisplayName):
- if (reader.Read())
- {
- var displayName = (string)reader.Value;
- childTag.DisplayName = displayName;
- }
- break;
- case nameof(AllowedChildTagDescriptor.Diagnostics):
- ReadDiagnostics(reader, childTag.Diagnostics);
- break;
- }
- });
- });
+ private static void ReadTagMatchingRule(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
}
- private static void ReadMetadata(JsonReader reader, IDictionary metadata)
+ if (reader.TokenType != JsonToken.StartObject)
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ return;
+ }
+ builder.TagMatchingRule(rule =>
+ {
reader.ReadProperties(propertyName =>
{
- if (reader.Read())
+ switch (propertyName)
{
- var value = (string)reader.Value;
- metadata[propertyName] = value;
+ case nameof(TagMatchingRuleDescriptor.TagName):
+ if (reader.Read())
+ {
+ var tagName = (string)reader.Value;
+ rule.TagName = tagName;
+ }
+ break;
+ case nameof(TagMatchingRuleDescriptor.ParentTag):
+ if (reader.Read())
+ {
+ var parentTag = (string)reader.Value;
+ rule.ParentTag = parentTag;
+ }
+ break;
+ case nameof(TagMatchingRuleDescriptor.TagStructure):
+ rule.TagStructure = (TagStructure)reader.ReadAsInt32();
+ break;
+ case nameof(TagMatchingRuleDescriptor.Attributes):
+ ReadRequiredAttributeValues(reader, rule);
+ break;
+ case nameof(TagMatchingRuleDescriptor.Diagnostics):
+ ReadDiagnostics(reader, rule.Diagnostics);
+ break;
}
});
- }
+ });
+ }
- private static void ReadDiagnostics(JsonReader reader, RazorDiagnosticCollection diagnostics)
+ private static void ReadRequiredAttributeValues(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
+ {
+ if (!reader.Read())
{
- if (!reader.Read())
- {
- return;
- }
-
- if (reader.TokenType != JsonToken.StartArray)
- {
- return;
- }
+ return;
+ }
- do
- {
- ReadDiagnostic(reader, diagnostics);
- } while (reader.TokenType != JsonToken.EndArray);
+ if (reader.TokenType != JsonToken.StartArray)
+ {
+ return;
}
- private static void ReadDiagnostic(JsonReader reader, RazorDiagnosticCollection diagnostics)
+ do
{
- if (!reader.Read())
- {
- return;
- }
+ ReadRequiredAttribute(reader, builder);
+ } while (reader.TokenType != JsonToken.EndArray);
+ }
- if (reader.TokenType != JsonToken.StartObject)
- {
- return;
- }
+ private static void ReadRequiredAttribute(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
+ }
- string id = default;
- int severity = default;
- string message = default;
- SourceSpan sourceSpan = default;
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return;
+ }
+ builder.Attribute(attribute =>
+ {
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
- case nameof(RazorDiagnostic.Id):
+ case nameof(RequiredAttributeDescriptor.Name):
if (reader.Read())
{
- id = (string)reader.Value;
+ var name = (string)reader.Value;
+ attribute.Name = name;
}
break;
- case nameof(RazorDiagnostic.Severity):
- severity = reader.ReadAsInt32().Value;
+ case nameof(RequiredAttributeDescriptor.NameComparison):
+ var nameComparison = (RequiredAttributeDescriptor.NameComparisonMode)reader.ReadAsInt32();
+ attribute.NameComparisonMode = nameComparison;
break;
- case "Message":
+ case nameof(RequiredAttributeDescriptor.Value):
if (reader.Read())
{
- message = (string)reader.Value;
+ var value = (string)reader.Value;
+ attribute.Value = value;
}
break;
- case nameof(RazorDiagnostic.Span):
- sourceSpan = ReadSourceSpan(reader);
+ case nameof(RequiredAttributeDescriptor.ValueComparison):
+ var valueComparison = (RequiredAttributeDescriptor.ValueComparisonMode)reader.ReadAsInt32();
+ attribute.ValueComparisonMode = valueComparison;
+ break;
+ case nameof(RequiredAttributeDescriptor.Diagnostics):
+ ReadDiagnostics(reader, attribute.Diagnostics);
+ break;
+ case nameof(RequiredAttributeDescriptor.Metadata):
+ ReadMetadata(reader, attribute.Metadata);
break;
}
});
+ });
+ }
- var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
+ private static void ReadAllowedChildTags(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
+ }
- var diagnostic = RazorDiagnostic.Create(descriptor, sourceSpan);
- diagnostics.Add(diagnostic);
+ if (reader.TokenType != JsonToken.StartArray)
+ {
+ return;
}
- private static SourceSpan ReadSourceSpan(JsonReader reader)
+ do
{
- if (!reader.Read())
- {
- return SourceSpan.Undefined;
- }
+ ReadAllowedChildTag(reader, builder);
+ } while (reader.TokenType != JsonToken.EndArray);
+ }
- if (reader.TokenType != JsonToken.StartObject)
- {
- return SourceSpan.Undefined;
- }
+ private static void ReadAllowedChildTag(JsonReader reader, TagHelperDescriptorBuilder builder)
+ {
+ if (!reader.Read())
+ {
+ return;
+ }
- string filePath = default;
- int absoluteIndex = default;
- int lineIndex = default;
- int characterIndex = default;
- int length = default;
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return;
+ }
+ builder.AllowChildTag(childTag =>
+ {
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
- case nameof(SourceSpan.FilePath):
+ case nameof(AllowedChildTagDescriptor.Name):
if (reader.Read())
{
- filePath = (string)reader.Value;
+ var name = (string)reader.Value;
+ childTag.Name = name;
}
break;
- case nameof(SourceSpan.AbsoluteIndex):
- absoluteIndex = reader.ReadAsInt32().Value;
- break;
- case nameof(SourceSpan.LineIndex):
- lineIndex = reader.ReadAsInt32().Value;
- break;
- case nameof(SourceSpan.CharacterIndex):
- characterIndex = reader.ReadAsInt32().Value;
+ case nameof(AllowedChildTagDescriptor.DisplayName):
+ if (reader.Read())
+ {
+ var displayName = (string)reader.Value;
+ childTag.DisplayName = displayName;
+ }
break;
- case nameof(SourceSpan.Length):
- length = reader.ReadAsInt32().Value;
+ case nameof(AllowedChildTagDescriptor.Diagnostics):
+ ReadDiagnostics(reader, childTag.Diagnostics);
break;
}
});
+ });
+ }
+
+ private static void ReadMetadata(JsonReader reader, IDictionary metadata)
+ {
+ if (!reader.Read())
+ {
+ return;
+ }
- var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
- return sourceSpan;
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return;
+ }
+
+ reader.ReadProperties(propertyName =>
+ {
+ if (reader.Read())
+ {
+ var value = (string)reader.Value;
+ metadata[propertyName] = value;
+ }
+ });
+ }
+
+ private static void ReadDiagnostics(JsonReader reader, IList diagnostics)
+ {
+ if (!reader.Read())
+ {
+ return;
+ }
+
+ if (reader.TokenType != JsonToken.StartArray)
+ {
+ return;
+ }
+
+ do
+ {
+ ReadDiagnostic(reader, diagnostics);
+ }
+ while (reader.TokenType != JsonToken.EndArray);
+ }
+
+ private static void ReadDiagnostic(JsonReader reader, IList diagnostics)
+ {
+ if (!reader.Read())
+ {
+ return;
}
+
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return;
+ }
+
+ string id = default;
+ int severity = default;
+ string message = default;
+ SourceSpan sourceSpan = default;
+
+ reader.ReadProperties(propertyName =>
+ {
+ switch (propertyName)
+ {
+ case nameof(RazorDiagnostic.Id):
+ if (reader.Read())
+ {
+ id = (string)reader.Value;
+ }
+ break;
+ case nameof(RazorDiagnostic.Severity):
+ severity = reader.ReadAsInt32().Value;
+ break;
+ case "Message":
+ if (reader.Read())
+ {
+ message = (string)reader.Value;
+ }
+ break;
+ case nameof(RazorDiagnostic.Span):
+ sourceSpan = ReadSourceSpan(reader);
+ break;
+ }
+ });
+
+ var descriptor = new RazorDiagnosticDescriptor(id, message, (RazorDiagnosticSeverity)severity);
+
+ var diagnostic = RazorDiagnostic.Create(descriptor, sourceSpan);
+ diagnostics.Add(diagnostic);
+ }
+
+ private static SourceSpan ReadSourceSpan(JsonReader reader)
+ {
+ if (!reader.Read())
+ {
+ return SourceSpan.Undefined;
+ }
+
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return SourceSpan.Undefined;
+ }
+
+ string filePath = default;
+ int absoluteIndex = default;
+ int lineIndex = default;
+ int characterIndex = default;
+ int length = default;
+
+ reader.ReadProperties(propertyName =>
+ {
+ switch (propertyName)
+ {
+ case nameof(SourceSpan.FilePath):
+ if (reader.Read())
+ {
+ filePath = (string)reader.Value;
+ }
+ break;
+ case nameof(SourceSpan.AbsoluteIndex):
+ absoluteIndex = reader.ReadAsInt32().Value;
+ break;
+ case nameof(SourceSpan.LineIndex):
+ lineIndex = reader.ReadAsInt32().Value;
+ break;
+ case nameof(SourceSpan.CharacterIndex):
+ characterIndex = reader.ReadAsInt32().Value;
+ break;
+ case nameof(SourceSpan.Length):
+ length = reader.ReadAsInt32().Value;
+ break;
+ }
+ });
+
+ var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
+ return sourceSpan;
}
}
diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj
index 3a4e5841b010..77a9cf79660b 100644
--- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj
+++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj
@@ -1,6 +1,8 @@

+
+ 8.0.100.0
$(ResolverTargetFramework);net472
$(ResolverTargetFramework)
AnyCPU
diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs
index 9b70750cced6..757f738ec7a9 100644
--- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs
+++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/EnvironmentProvider.cs
@@ -12,6 +12,8 @@ namespace Microsoft.DotNet.NativeWrapper
{
public class EnvironmentProvider
{
+ private static readonly char[] s_invalidPathChars = Path.GetInvalidPathChars();
+
private IEnumerable _searchPaths;
private readonly Func _getEnvironmentVariable;
@@ -31,17 +33,12 @@ private IEnumerable SearchPaths
{
get
{
- if (_searchPaths == null)
- {
- var searchPaths = new List();
-
- searchPaths.AddRange(
- _getEnvironmentVariable(Constants.PATH)
- .Split(new char[] { Path.PathSeparator }, options: StringSplitOptions.RemoveEmptyEntries)
- .Select(p => p.Trim('"')));
-
- _searchPaths = searchPaths;
- }
+ _searchPaths ??=
+ _getEnvironmentVariable(Constants.PATH)
+ .Split(new char[] { Path.PathSeparator }, options: StringSplitOptions.RemoveEmptyEntries)
+ .Select(p => p.Trim('"'))
+ .Where(p => p.IndexOfAny(s_invalidPathChars) == -1)
+ .ToList();
return _searchPaths;
}
@@ -51,7 +48,6 @@ public string GetCommandPath(string commandName)
{
var commandNameWithExtension = commandName + Constants.ExeSuffix;
var commandPath = SearchPaths
- .Where(p => !Path.GetInvalidPathChars().Any(c => p.Contains(c)))
.Select(p => Path.Combine(p, commandNameWithExtension))
.FirstOrDefault(File.Exists);
@@ -67,10 +63,15 @@ public string GetDotnetExeDirectory(Action log = null)
return environmentOverride;
}
- string dotnetExe = _getCurrentProcessPath();
+ string dotnetExe;
+#if NETCOREAPP
+ // The dotnet executable is loading only the .NET version of this code so there is no point checking
+ // the current process path on .NET Framework. We are expected to find dotnet on PATH.
+ dotnetExe = _getCurrentProcessPath();
if (string.IsNullOrEmpty(dotnetExe) || !Path.GetFileNameWithoutExtension(dotnetExe)
.Equals(Constants.DotNet, StringComparison.InvariantCultureIgnoreCase))
+#endif
{
string dotnetExeFromPath = GetCommandPath(Constants.DotNet);
@@ -87,6 +88,13 @@ public string GetDotnetExeDirectory(Action log = null)
} else {
log?.Invoke($"GetDotnetExeDirectory: dotnet command path not found. Using current process");
log?.Invoke($"GetDotnetExeDirectory: Path variable: {_getEnvironmentVariable(Constants.PATH)}");
+
+#if !NETCOREAPP
+ // If we failed to find dotnet on PATH, we revert to the old behavior of returning the current process
+ // path. This is really an error state but we're keeping the contract of always returning a non-empty
+ // path for backward compatibility.
+ dotnetExe = _getCurrentProcessPath();
+#endif
}
}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadId.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadId.cs
index 948b8ec928ae..c6adcff13fbc 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadId.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadId.cs
@@ -4,7 +4,7 @@
namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
///
- /// Wraps a workload definition id string to help ensure consistency of behaviour/semantics.
+ /// Wraps a workload definition id string to help ensure consistency of behavior/semantics.
/// Comparisons are case insensitive but ToString() will return the original string for display purposes.
///
public readonly struct WorkloadId : IComparable, IEquatable
diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
index b881ae6ebecf..b2b7abf5bc73 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible a EnableAotAnalyzer nejsou pro cÃlovou architekturu podporovány. Zvažte vÃcenásobné cÃlenà na podporovanou architekturu, abyste umožnili analýzu kompilace pÅ™edem, a nastavte IsAotCompatible pouze pro podporované architektury. NapÅ™Ãklad:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable a EnableTrimAnalyzer nejsou pro cÃlovou architekturu podporovány. Zvažte vÃcenásobné cÃlenà na podporovanou architekturu, která umožňuje oÅ™ÃznutÃ, a nastavte IsTrimmable pouze pro podporované architektury. NapÅ™Ãklad:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf
index abb936f7669b..bfeab68a8dda 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: EnableSingleFileAnalyzer no se admite para la plataforma de destino. Considere la posibilidad de usar varios destinos en un marco compatible para habilitar el análisis de archivos únicos y establezca EnableSingleFileAnalyzer solo para los marcos admitidos. Por ejemplo:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible y EnableAotAnalyzer no se admite para la plataforma de destino. Considere la posibilidad de usar varios destinos en un marco compatible para habilitar el análisis de compilación con antelación y establezca IsAotCompatible solo para los marcos admitidos. Por ejemplo:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable y EnableTrimAnalyzer no se admite para la plataforma de destino. Considere la posibilidad de usar varios destinos en un marco compatible para habilitar el recorte y establezca IsTrimmable solo para los marcos admitidos. Por ejemplo:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf
index 37c8148e6367..86e1d2d8342f 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: EnableSingleFileAnalyzer non è supportato per il framework di destinazione. Prendere in considerazione la multitargeting per un framework supportato per abilitare l'analisi a file singolo e impostare EnableSingleFileAnalyzer solo per i framework supportati. Ad esempio:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible e EnableAotAnalyzer non sono supportati per il framework di destinazione. Prendere in considerazione la multitargeting per un framework supportato per abilitare l'analisi della compilazione in anticipo e impostare IsAotCompatible solo per i framework supportati. Ad esempio:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable e EnableTrimAnalyzer non sono supportati per il framework di destinazione. Prendere in considerazione la multitargeting per un framework supportato per abilitare il taglio e impostare IsTrimmable solo per i framework supportati. Ad esempio:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
index a8155edaf937..58d017d5ca72 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible 㨠EnableAotAnalyzer ã¯ã‚¿ãƒ¼ã‚²ãƒƒãƒˆ フレームワークã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。Ahead of Time コンパイル分æžã‚’有効ã«ã™ã‚‹ã«ã¯ã€ã‚µãƒãƒ¼ãƒˆã•れã¦ã„るフレームワークã«å¯¾ã™ã‚‹ãƒžãƒ«ãƒã‚¿ãƒ¼ã‚²ãƒƒãƒˆã‚’検討ã—ã€ã‚µãƒãƒ¼ãƒˆã•れã¦ã„るフレームワークã«é™ã‚Š IsAotCompatible ã‚’è¨å®šã—ã¦ãã ã•ã„。例:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable 㨠EnableTrimAnalyzer ã¯ã‚¿ãƒ¼ã‚²ãƒƒãƒˆ フレームワークã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。トリミングを有効ã«ã™ã‚‹ã«ã¯ã€ã‚µãƒãƒ¼ãƒˆã•れã¦ã„るフレームワークã«å¯¾ã™ã‚‹ãƒžãƒ«ãƒã‚¿ãƒ¼ã‚²ãƒƒãƒˆã‚’検討ã—ã€ã‚µãƒãƒ¼ãƒˆã•れã¦ã„るフレームワークã«é™ã‚Š IsTrimmable ã‚’è¨å®šã—ã¦ãã ã•ã„。例:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
index f14df578d581..d24efca06c11 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
@@ -402,8 +402,8 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
-<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
+ NETSDK1211: EnableSingleFileAnalyzer nie jest obsługiwany dla struktury docelowej. Rozważ zastosowanie wielu elementów docelowych do obsługiwanej struktury, aby umożliwić analizę pojedynczych plików, i ustaw parametr EnableSingleFileAnalyzer tylko dla obsługiwanych struktur. Na przykład:
+<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', „{0}â€))">wartość prawda</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,15 +572,15 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
-<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
+ NETSDK1210: Elementy IsAotCompatible i EnableAotAnalyzer nie są obsługiwane dla struktury docelowej. Rozważ zastosowanie wielu elementów docelowych do obsługiwanej struktury, aby umożliwić analizę kompilacji z wyprzedzeniem, i ustaw wartość IsAotCompatible tylko dla obsługiwanych struktur. Na przykład:
+<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)„, â€{0}'))">wartość prawda</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
-<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
+ NETSDK1212: Elementy IsTrimmable i EnableTrimAnalyzer nie są obsługiwane dla struktury docelowej. Rozważ zastosowanie wielu elementów docelowych do obsługiwanej struktury, aby włączyć przycinanie, i ustaw właściwość IsTrimmable tylko dla obsługiwanych struktur. Na przykład:
+<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', „{0}â€))">wartość prawda</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
index 39eb2af70807..9f0cd6842b73 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
@@ -402,8 +402,8 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
-<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
+ NETSDK1211: EnableSingleFileAnalyzer não é compatÃvel com a estrutura de destino. Considere o direcionamento múltiplo para uma estrutura com suporte para habilitar a análise de arquivo único e defina EnableSingleFileAnalyzer somente para as estruturas com suporte. Por exemplo:
+<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">verdadeiro</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,15 +572,15 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
-<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
+ NETSDK1210: IsAotCompatible e EnableAotAnalyzer não são compatÃveis com a estrutura de destino. Considere o direcionamento múltiplo para uma estrutura com suporte para permitir a análise de compilação antecipada e defina IsAotCompatible apenas para as estruturas com suporte. Por exemplo:
+<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">verdadeiro</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
-<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
+ NETSDK1212: IsTrimmable e EnableTrimAnalyzer não são compatÃveis com a estrutura de destino. Considere o direcionamento múltiplo para uma estrutura com suporte para habilitar o corte e defina IsTrimmable somente para as estruturas com suporte. Por exemplo:
+<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">verdadeiro</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
index eb934bfde849..561e733e1634 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: EnableSingleFileAnalyzer не поддерживаетÑÑ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð¹ платформой. РаÑÑмотрите возможноÑть иÑпользовать неÑколько целевых верÑий Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ð¾Ð¹ платформы, чтобы включить анализ одного файла, и наÑтройте EnableSingleFileAnalyzer только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ñ… платформ. Ðапример:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible и EnableAotAnalyzer не поддерживаютÑÑ Ð´Ð»Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð¹ платформы. РаÑÑмотрите возможноÑть иÑпользовать неÑколько целевых верÑий Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ð¾Ð¹ платформы, чтобы включить анализ компилÑции AOT, и задайте параметр IsAotCompatible только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ñ… платформ. Пример:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable и EnableTrimAnalyzer не поддерживаютÑÑ Ð´Ð»Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð¹ платформы. РаÑÑмотрите возможноÑть иÑпользовать неÑколько целевых верÑий Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ð¾Ð¹ платформы, чтобы включить обрезку, и задайте параметр IsTrimmable только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ñ… платформ. Пример:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
index dd845ad008bf..08d35b0d704b 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: EnableSingleFileAnalyzer hedef altyapı için desteklenmiyor. Tek dosya analizini etkinleştirmek için desteklenen bir altyapıya çoklu hedeflemeyi kullanabilir ve EnableSingleFileAnalyzer ayarını yalnızca desteklenen altyapılar için yapabilirsiniz. Örneğin:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: IsAotCompatible ve EnableAotAnalyzer hedef altyapı için desteklenmiyor. Önceden derleme analizini etkinleştirmek için desteklenen bir altyapıya çoklu hedefleme uygulayabilir ve IsAotCompatible ayarını yalnızca desteklenen altyapılar için yapabilirsiniz. Örneğin:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: IsTrimmable ve EnableTrimAnalyzer hedef altyapı için desteklenmiyor. Kırpmayı etkinleştirmek için desteklenen bir altyapıya çoklu hedefleme uygulayabilir ve IsTrimmable ayarını yalnızca desteklenen altyapılar için yapabilirsiniz. Örneğin:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
index 1abcb43d4d43..5dd16b79df86 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: ç›®æ ‡æ¡†æž¶ä¸æ”¯æŒ EnableSingleFileAnalyzerã€‚è¯·è€ƒè™‘å¯¹å—æ”¯æŒçš„æ¡†æž¶è¿›è¡Œå¤šç›®æ ‡è®¾å®šæ¥å¯ç”¨å•文件分æžï¼Œå¹¶ä¸”ä»…ä¸ºå—æ”¯æŒçš„æ¡†æž¶è®¾ç½® EnableSingleFileAnalyzer。例如:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: ç›®æ ‡æ¡†æž¶ä¸æ”¯æŒ IsAotCompatible å’Œ EnableAotAnalyzerã€‚è¯·è€ƒè™‘å¯¹å—æ”¯æŒçš„æ¡†æž¶è¿›è¡Œå¤šç›®æ ‡è®¾å®šæ¥å¯ç”¨æå‰ç¼–译分æžï¼Œå¹¶ä¸”ä»…ä¸ºå—æ”¯æŒçš„æ¡†æž¶è®¾ç½® IsAotCompatible。例如:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: ç›®æ ‡æ¡†æž¶ä¸æ”¯æŒ IsTrimmable å’Œ EnableTrimAnalyzerã€‚è¯·è€ƒè™‘å¯¹å—æ”¯æŒçš„æ¡†æž¶è¿›è¡Œå¤šç›®æ ‡è®¾å®šä»¥å¯ç”¨å‰ªè£ï¼Œå¹¶ä¸”ä»…ä¸ºå—æ”¯æŒçš„æ¡†æž¶è®¾ç½® IsTrimmable。例如:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
index 697f7689090e..72dfb5bfac80 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
@@ -402,7 +402,7 @@
NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
- NETSDK1211: EnableSingleFileAnalyzer is not supported for the target framework. Consider multi-targeting to a supported framework to enable single-file analysis, and set EnableSingleFileAnalyzer only for the supported frameworks. For example:
+ NETSDK1211: ç›®æ¨™æž¶æ§‹ä¸æ”¯æ´ EnableSingleFileAnalyzerã€‚è€ƒæ…®å°æ”¯æ´çš„æž¶æ§‹è¨å®šå¤šé‡ç›®æ¨™ï¼Œä»¥å•Ÿç”¨å–®ä¸€æª”案分æžï¼Œä¸¦åƒ…é‡å°æ”¯æ´çš„æž¶æ§‹è¨å®š EnableSingleFileAnalyzer。例如:
<EnableSingleFileAnalyzer Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</EnableSingleFileAnalyzer>
{StrBegin="NETSDK1211: "}
@@ -572,14 +572,14 @@ The following are names of parameters or literal values and should not be transl
NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
- NETSDK1210: IsAotCompatible and EnableAotAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable ahead-of-time compilation analysis, and set IsAotCompatible only for the supported frameworks. For example:
+ NETSDK1210: ç›®æ¨™æž¶æ§‹ä¸æ”¯æ´ IsAotCompatible å’Œ EnableAotAnalyzerã€‚è€ƒæ…®å°æ”¯æ´çš„æž¶æ§‹è¨å®šå¤šé‡ç›®æ¨™ï¼Œä»¥å•Ÿç”¨æå‰ç·¨è¯åˆ†æžï¼Œä¸¦åƒ…é‡å°æ”¯æ´çš„æž¶æ§‹è¨å®š IsAotCompatible。例如:
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsAotCompatible>
{StrBegin="NETSDK1210: "}
NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
- NETSDK1212: IsTrimmable and EnableTrimAnalyzer are not supported for the target framework. Consider multi-targeting to a supported framework to enable trimming, and set IsTrimmable only for the supported frameworks. For example:
+ NETSDK1212: ç›®æ¨™æž¶æ§‹ä¸æ”¯æ´ IsTrimmable å’Œ EnableTrimAnalyzerã€‚è€ƒæ…®å°æ”¯æ´çš„æž¶æ§‹è¨å®šå¤šé‡ç›®æ¨™ï¼Œä»¥å•Ÿç”¨ä¿®å‰ªï¼Œä¸¦åƒ…é‡å°æ”¯æ´çš„æž¶æ§‹è¨å®š IsTrimmable。例如:
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegin="NETSDK1212: "}
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.props b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.props
index 404a3d71f4d2..00c466b9e31d 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.props
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.props
@@ -11,6 +11,13 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
+
+ <_AfterSdkPublishDependsOn Condition="'$(_IsAspNetCoreProject)' == 'true'">AfterPublish
+ <_AfterSdkPublishDependsOn Condition="'$(_IsAspNetCoreProject)' != 'true'">Publish
+
+
+
+
true
-
-
-
-
-
-
- true
-
-
- <_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props
- <_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)'))
- $([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)'))
-
-
-
-
-
- false
-
-
-
-
-
-
- true
- $(MSBuildProjectName)
-
-
-
- $(ArtifactsPath)\obj\$(ArtifactsProjectName)\
- $(ArtifactsPath)\obj\
-
-
-
- <_ArtifactsPathSetEarly>true
+ $(CustomAfterDirectoryBuildProps);$(MSBuildThisFileDirectory)UseArtifactsOutputPath.props
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets
index 136d5d2cb673..a506081a2017 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets
@@ -52,4 +52,15 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+
+
+ <_ContainersTargetsDir Condition=" '$(_ContainersTargetsDir)'=='' ">$(MSBuildThisFileDirectory)..\..\..\Containers\build\
+
+
+
+
+
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/UseArtifactsOutputPath.props b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/UseArtifactsOutputPath.props
new file mode 100644
index 000000000000..1dd11c8d49ad
--- /dev/null
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/UseArtifactsOutputPath.props
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ true
+ $(MSBuildProjectName)
+
+
+
+ $(ArtifactsPath)\obj\$(ArtifactsProjectName)\
+ $(ArtifactsPath)\obj\
+
+
+
+
+ <_ArtifactsPathSetEarly>true
+
+
diff --git a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerNugetCacheTests.cs b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerNugetCacheTests.cs
index 3aedbf9a3a98..263b8d3bba6e 100644
--- a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerNugetCacheTests.cs
+++ b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerNugetCacheTests.cs
@@ -18,7 +18,7 @@ public ToolPackageInstallToManagedLocationInstaller(ITestOutputHelper log) : bas
{
}
- [Theory]
+ [WindowsOnlyTheory]
[InlineData(false)]
[InlineData(true)]
public void GivenNugetConfigInstallSucceeds(bool testMockBehaviorIsInSync)
@@ -61,7 +61,7 @@ public void GivenNugetConfigInstallSucceeds(bool testMockBehaviorIsInSync)
}
}
- [Theory]
+ [WindowsOnlyTheory]
[InlineData(false)]
[InlineData(true)]
public void GivenNugetConfigVersionRangeInstallSucceeds(bool testMockBehaviorIsInSync)
diff --git a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageUninstallerTests.cs b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageUninstallerTests.cs
index 4e5750f81d61..21c0f99477e8 100644
--- a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageUninstallerTests.cs
+++ b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageUninstallerTests.cs
@@ -15,7 +15,7 @@ namespace Microsoft.DotNet.PackageInstall.Tests
{
public class ToolPackageUninstallerTests : SdkTest
{
- [Theory]
+ [WindowsOnlyTheory]
[InlineData(false)]
[InlineData(true)]
public void GivenAnInstalledPackageUninstallRemovesThePackage(bool testMockBehaviorIsInSync)
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
index 0d3ee20642d5..e9fe7898dff5 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs
@@ -152,7 +152,7 @@ public void Tasks_EndToEnd_With_EnvironmentVariable_Validation()
File.WriteAllText(Path.Combine(newProjectDir.FullName, "Program.cs"), $"Console.Write(Environment.GetEnvironmentVariable(\"GoodEnvVar\"));");
- new DotnetCommand(_testOutput, "build", "--configuration", "release", "/p:runtimeidentifier=linux-x64", $"/p:RuntimeFrameworkVersion=8.0.0-preview.3.23174.8")
+ new DotnetCommand(_testOutput, "build", "--configuration", "release", "/p:runtimeidentifier=linux-x64")
.WithWorkingDirectory(newProjectDir.FullName)
.Execute()
.Should().Pass();
@@ -161,7 +161,7 @@ public void Tasks_EndToEnd_With_EnvironmentVariable_Validation()
(IBuildEngine buildEngine, List errors) = SetupBuildEngine();
pcp.BuildEngine = buildEngine;
- pcp.FullyQualifiedBaseImageName = $"mcr.microsoft.com/{DockerRegistryManager.RuntimeBaseImage}:{DockerRegistryManager.Net8PreviewImageTag}";
+ pcp.FullyQualifiedBaseImageName = $"mcr.microsoft.com/{DockerRegistryManager.RuntimeBaseImage}:{DockerRegistryManager.Net8ImageTag}";
pcp.ContainerRegistry = "";
pcp.ContainerRepository = "dotnet/envvarvalidation";
pcp.ContainerImageTag = "latest";
@@ -174,7 +174,7 @@ public void Tasks_EndToEnd_With_EnvironmentVariable_Validation()
Assert.True(pcp.Execute(), FormatBuildMessages(errors));
Assert.Equal("mcr.microsoft.com", pcp.ParsedContainerRegistry);
Assert.Equal("dotnet/runtime", pcp.ParsedContainerImage);
- Assert.Equal(DockerRegistryManager.Net8PreviewImageTag, pcp.ParsedContainerTag);
+ Assert.Equal(DockerRegistryManager.Net8ImageTag, pcp.ParsedContainerTag);
Assert.Single(pcp.NewContainerEnvironmentVariables);
Assert.Equal("Foo", pcp.NewContainerEnvironmentVariables[0].GetMetadata("Value"));
@@ -229,9 +229,9 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage()
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.RuntimeBaseImage,
- DockerRegistryManager.Net8PreviewImageTag,
+ DockerRegistryManager.Net8ImageTag,
"linux-x64",
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);
@@ -239,7 +239,7 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage()
BuiltImage builtImage = imageBuilder.Build();
- var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net8PreviewImageTag);
+ var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net8ImageTag);
var destinationReference = new DestinationImageReference(registry, RootlessBase, new[] { "latest" });
await registry.PushAsync(builtImage, sourceReference, destinationReference, cancellationToken: default).ConfigureAwait(false);
@@ -289,7 +289,7 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage()
AppImage,
"latest",
"linux-x64",
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.Equal(RootlessUser, imageBuilder.BaseImageConfig.GetUser());
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs
index 53954b10427b..534a4a78056d 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs
@@ -3,6 +3,7 @@
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Logging;
+using Xunit.Sdk;
namespace Microsoft.NET.Build.Containers.IntegrationTests;
@@ -13,14 +14,22 @@ public class DockerRegistryManager
public const string BaseImageSource = "mcr.microsoft.com";
public const string Net6ImageTag = "6.0";
public const string Net7ImageTag = "7.0";
- public const string Net8PreviewImageTag = "8.0-preview";
- public const string Net8PreviewWindowsSpecificImageTag = $"{Net8PreviewImageTag}-nanoserver-ltsc2022";
+ public const string Net8ImageTag = "8.0";
+ public const string Net8PreviewWindowsSpecificImageTag = $"{Net8ImageTag}-nanoserver-ltsc2022";
public const string LocalRegistry = "localhost:5010";
- public const string FullyQualifiedBaseImageDefault = $"{BaseImageSource}/{RuntimeBaseImage}:{Net8PreviewImageTag}";
- public const string FullyQualifiedBaseImageAspNet = $"{BaseImageSource}/{AspNetBaseImage}:{Net8PreviewImageTag}";
+ public const string FullyQualifiedBaseImageDefault = $"{BaseImageSource}/{RuntimeBaseImage}:{Net8ImageTag}";
+ public const string FullyQualifiedBaseImageAspNet = $"{BaseImageSource}/{AspNetBaseImage}:{Net8ImageTag}";
private static string? s_registryContainerId;
- public static void StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
+ internal class SameArchManifestPicker : IManifestPicker
+ {
+ public PlatformSpecificManifest? PickBestManifestForRid(IReadOnlyDictionary manifestList, string runtimeIdentifier)
+ {
+ return manifestList.Values.SingleOrDefault(m => m.platform.os == "linux" && m.platform.architecture == "amd64");
+ }
+ }
+
+ public static async Task StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
{
using TestLoggerFactory loggerFactory = new(testOutput);
@@ -34,6 +43,9 @@ public static void StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
int spawnRegistryDelay = 1000; //ms
StringBuilder failureReasons = new();
+ var pullRegistry = new Registry(BaseImageSource, logger);
+ var pushRegistry = new Registry(LocalRegistry, logger);
+
for (int spawnRegistryAttempt = 1; spawnRegistryAttempt <= spawnRegistryMaxRetry; spawnRegistryAttempt++)
{
try
@@ -52,22 +64,18 @@ public static void StartAndPopulateDockerRegistry(ITestOutputHelper testOutput)
EnsureRegistryLoaded(LocalRegistry, s_registryContainerId, logger, testOutput);
- foreach (string? tag in new[] { Net6ImageTag, Net7ImageTag, Net8PreviewImageTag })
+ foreach (string? tag in new[] { Net6ImageTag, Net7ImageTag, Net8ImageTag })
{
logger.LogInformation("Pulling image '{repo}/{image}:{tag}'.", BaseImageSource, RuntimeBaseImage, tag);
- ContainerCli.PullCommand(testOutput, $"{BaseImageSource}/{RuntimeBaseImage}:{tag}")
- .Execute()
- .Should().Pass();
-
- logger.LogInformation("Tagging image '{sourceRepo}/{sourceImage}:{sourceTag}' as '{targetRepo}/{targetImage}:{targetTag}'.",BaseImageSource, RuntimeBaseImage, tag, LocalRegistry, RuntimeBaseImage, tag);
- ContainerCli.TagCommand(testOutput, $"{BaseImageSource}/{RuntimeBaseImage}:{tag}", $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
- .Execute()
- .Should().Pass();
-
- logger.LogInformation("Pushing image '{repo}/{image}:{tag}'.", LocalRegistry, RuntimeBaseImage, tag);
- ContainerCli.PushCommand(testOutput, $"{LocalRegistry}/{RuntimeBaseImage}:{tag}")
- .Execute()
- .Should().Pass();
+ string dotnetdll = System.Reflection.Assembly.GetExecutingAssembly().Location;
+ var ridjson = Path.Combine(Path.GetDirectoryName(dotnetdll)!, "RuntimeIdentifierGraph.json");
+
+ var image = await pullRegistry.GetImageManifestAsync(RuntimeBaseImage, tag, "linux-x64", new SameArchManifestPicker(), CancellationToken.None);
+ var source = new SourceImageReference(pullRegistry, RuntimeBaseImage, tag);
+ var dest = new DestinationImageReference(pushRegistry, RuntimeBaseImage, [tag]);
+ logger.LogInformation($"Pushing image for {BaseImageSource}/{RuntimeBaseImage}:{tag}");
+ await pushRegistry.PushAsync(image.Build(), source, dest, CancellationToken.None);
+ logger.LogInformation($"Pushed image for {BaseImageSource}/{RuntimeBaseImage}:{tag}");
}
return;
}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs
index 4d4f5df614ef..18638ee17b84 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs
@@ -31,7 +31,7 @@ public async Task GetFromRegistry()
DockerRegistryManager.RuntimeBaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
- ridgraphfile,
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(downloadedImage);
@@ -83,7 +83,7 @@ public async Task WriteToPrivateBasicRegistry()
DockerRegistryManager.RuntimeBaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
- ridgraphfile,
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
var image = downloadedImage.Build();
await localAuthed.PushAsync(image, sourceImage, destinationImage, CancellationToken.None);
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs
index 847c22b7d956..c417e23c6184 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerTestsFixture.cs
@@ -12,7 +12,7 @@ public DockerTestsFixture(IMessageSink messageSink)
_diagnosticOutput = new SharedTestOutputHelper(messageSink);
try
{
- DockerRegistryManager.StartAndPopulateDockerRegistry(_diagnosticOutput);
+ DockerRegistryManager.StartAndPopulateDockerRegistry(_diagnosticOutput).GetAwaiter().GetResult();
}
catch
{
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs
index c012fbd153f3..c0050a9ee053 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs
@@ -50,9 +50,9 @@ public async Task ApiEndToEndWithRegistryPushAndPull()
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.RuntimeBaseImage,
- DockerRegistryManager.Net8PreviewImageTag,
+ DockerRegistryManager.Net8ImageTag,
"linux-x64",
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);
@@ -66,7 +66,7 @@ public async Task ApiEndToEndWithRegistryPushAndPull()
BuiltImage builtImage = imageBuilder.Build();
// Push the image back to the local registry
- var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net8PreviewImageTag);
+ var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net8ImageTag);
var destinationReference = new DestinationImageReference(registry, NewImageName(), new[] { "latest", "1.0" });
await registry.PushAsync(builtImage, sourceReference, destinationReference, cancellationToken: default).ConfigureAwait(false);
@@ -97,9 +97,9 @@ public async Task ApiEndToEndWithLocalLoad()
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.RuntimeBaseImage,
- DockerRegistryManager.Net8PreviewImageTag,
+ DockerRegistryManager.Net8ImageTag,
"linux-x64",
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);
@@ -138,9 +138,9 @@ public async Task ApiEndToEndWithArchiveWritingAndLoad()
ImageBuilder imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.RuntimeBaseImage,
- DockerRegistryManager.Net8PreviewImageTag,
+ DockerRegistryManager.Net8ImageTag,
"linux-x64",
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);
@@ -197,11 +197,6 @@ private string BuildLocalApp([CallerMemberName] string testName = "TestName", st
new DotnetCommand(_testOutput, "publish", "-bl", "MinimalTestApp", "-r", rid, "-f", tfm, "-c", "Debug")
.WithWorkingDirectory(workingDirectory);
- if (tfm == ToolsetInfo.CurrentTargetFramework)
- {
- publishCommand.Arguments.AddRange(new[] { "-p", $"RuntimeFrameworkVersion=8.0.0-preview.3.23174.8" });
- }
-
publishCommand.Execute()
.Should().Pass();
@@ -209,6 +204,87 @@ private string BuildLocalApp([CallerMemberName] string testName = "TestName", st
return publishDirectory;
}
+ [DockerAvailableFact]
+ public async Task EndToEnd_MultiProjectSolution()
+ {
+ ILogger logger = _loggerFactory.CreateLogger(nameof(EndToEnd_MultiProjectSolution));
+ DirectoryInfo newSolutionDir = new(Path.Combine(TestSettings.TestArtifactsDirectory, $"CreateNewImageTest_EndToEnd_MultiProjectSolution"));
+
+ if (newSolutionDir.Exists)
+ {
+ newSolutionDir.Delete(recursive: true);
+ }
+
+ newSolutionDir.Create();
+
+ // Create solution with projects
+ new DotnetNewCommand(_testOutput, "sln", "-n", nameof(EndToEnd_MultiProjectSolution))
+ .WithVirtualHive()
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ new DotnetNewCommand(_testOutput, "console", "-n", "ConsoleApp")
+ .WithVirtualHive()
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ new DotnetCommand(_testOutput, "sln", "add", Path.Combine("ConsoleApp", "ConsoleApp.csproj"))
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ new DotnetNewCommand(_testOutput, "web", "-n", "WebApp")
+ .WithVirtualHive()
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ new DotnetCommand(_testOutput, "sln", "add", Path.Combine("WebApp", "WebApp.csproj"))
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute()
+ .Should().Pass();
+
+ // Add 'EnableSdkContainerSupport' property to the ConsoleApp and set TFM
+ using (FileStream stream = File.Open(Path.Join(newSolutionDir.FullName, "ConsoleApp", "ConsoleApp.csproj"), FileMode.Open, FileAccess.ReadWrite))
+ {
+ XDocument document = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
+ document
+ .Descendants()
+ .First(e => e.Name.LocalName == "PropertyGroup")?
+ .Add(new XElement("EnableSdkContainerSupport", "true"));
+ document
+ .Descendants()
+ .First(e => e.Name.LocalName == "TargetFramework")
+ .Value = ToolsetInfo.CurrentTargetFramework;
+
+ stream.SetLength(0);
+ await document.SaveAsync(stream, SaveOptions.None, CancellationToken.None);
+ }
+
+ // Set TFM for WebApp
+ using (FileStream stream = File.Open(Path.Join(newSolutionDir.FullName, "WebApp", "WebApp.csproj"), FileMode.Open, FileAccess.ReadWrite))
+ {
+ XDocument document = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
+ document
+ .Descendants()
+ .First(e => e.Name.LocalName == "TargetFramework")
+ .Value = ToolsetInfo.CurrentTargetFramework;
+
+ stream.SetLength(0);
+ await document.SaveAsync(stream, SaveOptions.None, CancellationToken.None);
+ }
+
+ // Publish
+ CommandResult commandResult = new DotnetCommand(_testOutput, "publish", "/t:PublishContainer")
+ .WithWorkingDirectory(newSolutionDir.FullName)
+ .Execute();
+
+ commandResult.Should().Pass();
+ commandResult.Should().HaveStdOutContaining("Pushed image 'webapp:latest'");
+ commandResult.Should().HaveStdOutContaining("Pushed image 'consoleapp:latest'");
+ }
[DockerAvailableTheory()]
[InlineData("webapi", false)]
@@ -284,7 +360,7 @@ public async Task EndToEnd_NoAPI_ProjectType(string projectType, bool addPackage
$"/p:ContainerRegistry={DockerRegistryManager.LocalRegistry}",
$"/p:ContainerRepository={imageName}",
$"/p:ContainerImageTag={imageTag}",
- $"/p:RuntimeFrameworkVersion=8.0.0-preview.3.23174.8")
+ "/p:UseRazorSourceGenerator=false")
.WithEnvironmentVariable("NUGET_PACKAGES", privateNuGetAssets.FullName)
.WithWorkingDirectory(newProjectDir.FullName)
.Execute();
@@ -442,7 +518,6 @@ public void EndToEnd_NoAPI_Console()
$"/p:ContainerBaseImage={DockerRegistryManager.FullyQualifiedBaseImageAspNet}",
$"/p:ContainerRegistry={DockerRegistryManager.LocalRegistry}",
$"/p:ContainerRepository={imageName}",
- $"/p:RuntimeFrameworkVersion=8.0.0-preview.3.23174.8",
$"/p:ContainerImageTag={imageTag}")
.WithEnvironmentVariable("NUGET_PACKAGES", privateNuGetAssets.FullName)
.WithWorkingDirectory(newProjectDir.FullName)
@@ -483,9 +558,9 @@ public async Task CanPackageForAllSupportedContainerRIDs(string dockerPlatform,
var isWin = rid.StartsWith("win");
ImageBuilder? imageBuilder = await registry.GetImageManifestAsync(
DockerRegistryManager.RuntimeBaseImage,
- isWin ? DockerRegistryManager.Net8PreviewWindowsSpecificImageTag : DockerRegistryManager.Net8PreviewImageTag,
+ isWin ? DockerRegistryManager.Net8PreviewWindowsSpecificImageTag : DockerRegistryManager.Net8ImageTag,
rid,
- ToolsetUtils.GetRuntimeGraphFilePath(),
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(imageBuilder);
@@ -500,7 +575,7 @@ public async Task CanPackageForAllSupportedContainerRIDs(string dockerPlatform,
BuiltImage builtImage = imageBuilder.Build();
// Load the image into the local registry
- var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net7ImageTag);
+ var sourceReference = new SourceImageReference(registry, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net8ImageTag);
var destinationReference = new DestinationImageReference(registry, NewImageName(), new[] { rid });
await new DockerCli(_loggerFactory).LoadAsync(builtImage, sourceReference, destinationReference, default).ConfigureAwait(false);
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ParseContainerPropertiesTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ParseContainerPropertiesTests.cs
index 0a3dbb179aa3..2259ba90ca14 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ParseContainerPropertiesTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ParseContainerPropertiesTests.cs
@@ -8,13 +8,13 @@
namespace Microsoft.NET.Build.Containers.Tasks.IntegrationTests;
-[Collection("Docker tests")]
public class ParseContainerPropertiesTests
{
- [DockerAvailableFact]
+ [Fact]
public void Baseline()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerRepository] = "dotnet/testimage",
@@ -22,7 +22,7 @@ public void Baseline()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.True(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.True(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.Equal("mcr.microsoft.com", instance.GetPropertyValue(ContainerBaseRegistry));
Assert.Equal("dotnet/runtime", instance.GetPropertyValue(ContainerBaseName));
@@ -33,26 +33,28 @@ public void Baseline()
instance.GetItems("ProjectCapability").Select(i => i.EvaluatedInclude).ToArray().Should().BeEquivalentTo(new[] { "NetSdkOCIImageBuild" });
}
- [DockerAvailableFact]
+ [Fact]
public void SpacesGetReplacedWithDashes()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet runtime:7.0",
[ContainerRegistry] = "localhost:5010"
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.True(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.True(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
- Assert.Equal("mcr.microsoft.com",instance.GetPropertyValue(ContainerBaseRegistry));
+ Assert.Equal("mcr.microsoft.com", instance.GetPropertyValue(ContainerBaseRegistry));
Assert.Equal("dotnet-runtime", instance.GetPropertyValue(ContainerBaseName));
Assert.Equal("7.0", instance.GetPropertyValue(ContainerBaseTag));
}
- [DockerAvailableFact]
+ [Fact]
public void RegexCatchesInvalidContainerNames()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerRepository] = "dotnet testimage",
@@ -60,14 +62,15 @@ public void RegexCatchesInvalidContainerNames()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.True(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.True(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.Contains(logs.Messages, m => m.Message?.Contains("'dotnet testimage' was not a valid container image name, it was normalized to 'dotnet-testimage'") == true);
}
- [DockerAvailableFact]
+ [Fact]
public void RegexCatchesInvalidContainerTags()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerRepository] = "dotnet/testimage",
@@ -75,16 +78,17 @@ public void RegexCatchesInvalidContainerTags()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.False(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.False(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.True(logs.Errors.Count > 0);
Assert.Equal(logs.Errors[0].Code, ErrorCodes.CONTAINER2007);
}
- [DockerAvailableFact]
+ [Fact]
public void CanOnlySupplyOneOfTagAndTags()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerRepository] = "dotnet/testimage",
@@ -93,16 +97,34 @@ public void CanOnlySupplyOneOfTagAndTags()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.False(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.False(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.True(logs.Errors.Count > 0);
Assert.Equal(logs.Errors[0].Code, ErrorCodes.CONTAINER2008);
}
- [DockerAvailableFact]
+ [Fact]
+ public void InvalidTagsThrowError()
+ {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
+ [ContainerBaseImage] = "mcr.microsoft.com/dotnet/aspnet:8.0",
+ [ContainerRepository] = "dotnet/testimage",
+ [ContainerImageTags] = "'latest;oldest'"
+ });
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ Assert.False(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
+
+ Assert.True(logs.Errors.Count > 0);
+ Assert.Equal(logs.Errors[0].Code, ErrorCodes.CONTAINER2010);
+ }
+
+ [Fact]
public void FailsOnCompletelyInvalidRepositoryNames()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerImageName] = "㓳㓴㓵㓶㓷㓹㓺㓻",
@@ -110,16 +132,17 @@ public void FailsOnCompletelyInvalidRepositoryNames()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.False(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.False(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.True(logs.Errors.Count > 0);
Assert.Equal(logs.Errors[0].Code, ErrorCodes.CONTAINER2005);
}
- [DockerAvailableFact]
+ [Fact]
public void FailsWhenFirstCharIsAUnicodeLetterButNonLatin()
{
- var (project, logs, d) = ProjectInitializer.InitProject(new () {
+ var (project, logs, d) = ProjectInitializer.InitProject(new()
+ {
[ContainerBaseImage] = "mcr.microsoft.com/dotnet/runtime:7.0",
[ContainerRegistry] = "localhost:5010",
[ContainerImageName] = "㓳but-otherwise-valid",
@@ -127,7 +150,7 @@ public void FailsWhenFirstCharIsAUnicodeLetterButNonLatin()
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- Assert.False(instance.Build(new[]{ComputeContainerConfig}, new [] { logs }, null, out var outputs));
+ Assert.False(instance.Build(new[] { ComputeContainerConfig }, new[] { logs }, null, out var outputs));
Assert.True(logs.Errors.Count > 0);
Assert.Equal(logs.Errors[0].Code, ErrorCodes.CONTAINER2005);
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ProjectInitializer.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ProjectInitializer.cs
index d373bb05bb8c..1e19bbb4f7e0 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ProjectInitializer.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ProjectInitializer.cs
@@ -34,7 +34,7 @@ private static string CombineFiles(string propsFile, string targetsFile)
return tempTargetLocation;
}
- public static (Project, CapturingLogger, IDisposable) InitProject(Dictionary bonusProps, [CallerMemberName]string projectName = "")
+ public static (Project, CapturingLogger, IDisposable) InitProject(Dictionary bonusProps, Dictionary? bonusItems = null, [CallerMemberName] string projectName = "")
{
var props = new Dictionary();
// required parameters
@@ -52,6 +52,7 @@ public static (Project, CapturingLogger, IDisposable) InitProject(Dictionary
{
@@ -68,6 +69,32 @@ public static (Project, CapturingLogger, IDisposable) InitProject(Dictionary ni,
+ [var ni, ..] => ni,
+ [] => null
+ };
+ if (newItem is not null)
+ {
+ // we don't want to copy the MSBuild-reserved metadata, if any,
+ // so only use the custom metadata
+ var customMetadata = item.CloneCustomMetadata();
+ foreach (var key in customMetadata)
+ {
+ newItem.SetMetadataValue((string)key, customMetadata[key] as string);
+ }
+ }
+ }
+ }
+ }
+ return (project, logs, collection);
}
}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs
index 92bede0342c3..0f58b21e98ec 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs
@@ -47,7 +47,7 @@ public async Task CanReadManifestFromRegistry(string fullyQualifiedContainerName
containerName,
containerTag,
"linux-x64",
- ridgraphfile,
+ ToolsetUtils.RidGraphManifestPicker,
cancellationToken: default).ConfigureAwait(false);
Assert.NotNull(downloadedImage);
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/TargetsTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/TargetsTests.cs
index 41bd32f8129a..88aa4498356d 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/TargetsTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/TargetsTests.cs
@@ -30,7 +30,8 @@ public void CanDeferEntrypoint(string selfContainedPropertyName, bool selfContai
}
[Fact]
- public void CanDeferToContainerImageNameWhenPresent() {
+ public void CanDeferToContainerImageNameWhenPresent()
+ {
var customImageName = "my-container-app";
var (project, logger, d) = ProjectInitializer.InitProject(new()
{
@@ -38,7 +39,7 @@ public void CanDeferToContainerImageNameWhenPresent() {
});
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[] { ComputeContainerConfig }, new []{ logger });
+ instance.Build(new[] { ComputeContainerConfig }, new[] { logger });
logger.Warnings.Should().HaveCount(1, "a warning for the use of the old ContainerImageName property should have been created");
logger.Warnings[0].Code.Should().Be(KnownStrings.ErrorCodes.CONTAINER003);
Assert.Equal(customImageName, instance.GetPropertyValue(ContainerRepository));
@@ -58,7 +59,7 @@ public void CanNormalizeInputContainerNames(string projectName, string expectedC
}, projectName: $"{nameof(CanNormalizeInputContainerNames)}_{projectName}_{expectedContainerImageName}_{shouldPass}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[] { ComputeContainerConfig }, new[]{ logger }, null, out var outputs).Should().Be(shouldPass, String.Join(Environment.NewLine, logger.AllMessages));
+ instance.Build(new[] { ComputeContainerConfig }, new[] { logger }, null, out var outputs).Should().Be(shouldPass, String.Join(Environment.NewLine, logger.AllMessages));
Assert.Equal(expectedContainerImageName, instance.GetPropertyValue(ContainerRepository));
}
@@ -78,7 +79,7 @@ public void CanWarnOnInvalidSDKVersions(string sdkVersion, bool isAllowed)
}, projectName: $"{nameof(CanWarnOnInvalidSDKVersions)}_{sdkVersion}_{isAllowed}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[]{"_ContainerVerifySDKVersion"}, new[] { logger }, null, out var outputs).Should().Be(isAllowed);
+ instance.Build(new[] { "_ContainerVerifySDKVersion" }, new[] { logger }, null, out var outputs).Should().Be(isAllowed);
var derivedIsAllowed = Boolean.Parse(project.GetProperty("_IsSDKContainerAllowedVersion").EvaluatedValue);
if (isAllowed)
{
@@ -103,7 +104,8 @@ public void GetsConventionalLabelsByDefault(bool shouldEvaluateLabels)
}, projectName: $"{nameof(GetsConventionalLabelsByDefault)}_{shouldEvaluateLabels}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[] { ComputeContainerConfig }, new [] { logger }, null, out var outputs).Should().BeTrue("Build should have succeeded");
+ var success = instance.Build(new[] { ComputeContainerConfig }, new[] { logger }, null, out var outputs);
+ success.Should().BeTrue("Build should have succeeded");
if (shouldEvaluateLabels)
{
instance.GetItems(ContainerLabel).Should().NotBeEmpty("Should have evaluated some labels by default");
@@ -132,7 +134,7 @@ public void ShouldNotIncludeSourceControlLabelsUnlessUserOptsIn(bool includeSour
}, projectName: $"{nameof(ShouldNotIncludeSourceControlLabelsUnlessUserOptsIn)}_{includeSourceControl}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[] { ComputeContainerConfig }, new [] { logger }, null, out var outputs).Should().BeTrue("Build should have succeeded but failed due to {0}", String.Join("\n", logger.AllMessages));
+ instance.Build(new[] { ComputeContainerConfig }, new[] { logger }, null, out var outputs).Should().BeTrue("Build should have succeeded but failed due to {0}", String.Join("\n", logger.AllMessages));
var labels = instance.GetItems(ContainerLabel);
if (includeSourceControl)
{
@@ -180,12 +182,13 @@ public void CanComputeTagsForSupportedSDKVersions(string sdkVersion, string tfm,
}, projectName: $"{nameof(CanComputeTagsForSupportedSDKVersions)}_{sdkVersion}_{tfm}_{expectedTag}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[]{"_ComputeContainerBaseImageTag"}, new [] { logger }, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
- var computedTag = instance.GetProperty("_ContainerBaseImageTag").EvaluatedValue;
- computedTag.Should().Be(expectedTag);
+ instance.Build(new[] { ComputeContainerBaseImage }, new[] { logger }, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedTag = instance.GetProperty(ContainerBaseImage).EvaluatedValue;
+ computedTag.Should().EndWith(expectedTag);
}
[InlineData("v8.0", "linux-x64", null)]
+ [InlineData("v8.0", "linux-musl-x64", null)]
[InlineData("v8.0", "win-x64", "ContainerUser")]
[InlineData("v7.0", "linux-x64", null)]
[InlineData("v7.0", "win-x64", null)]
@@ -203,7 +206,7 @@ public void CanComputeContainerUser(string tfm, string rid, string expectedUser)
}, projectName: $"{nameof(CanComputeContainerUser)}_{tfm}_{rid}_{expectedUser}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[]{ComputeContainerConfig}, new [] { logger }, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ instance.Build(new[] { ComputeContainerConfig }, new[] { logger }, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
var computedTag = instance.GetProperty("ContainerUser")?.EvaluatedValue;
computedTag.Should().Be(expectedUser);
}
@@ -222,7 +225,7 @@ public void WindowsUsersGetLinuxContainers(string sdkPortableRid, string expecte
}, projectName: $"{nameof(WindowsUsersGetLinuxContainers)}_{sdkPortableRid}_{expectedRid}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[]{ComputeContainerConfig}, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ instance.Build(new[] { ComputeContainerConfig }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
var computedRid = instance.GetProperty(KnownStrings.Properties.ContainerRuntimeIdentifier)?.EvaluatedValue;
computedRid.Should().Be(expectedRid);
}
@@ -244,8 +247,140 @@ public void CanTakeContainerBaseFamilyIntoAccount(string sdkVersion, string tfmM
}, projectName: $"{nameof(CanTakeContainerBaseFamilyIntoAccount)}_{sdkVersion}_{tfmMajMin}_{containerFamily}_{expectedTag}");
using var _ = d;
var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
- instance.Build(new[]{ _ComputeContainerBaseImageTag }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
- var computedBaseImageTag = instance.GetProperty(KnownStrings.Properties._ContainerBaseImageTag)?.EvaluatedValue;
- computedBaseImageTag.Should().Be(expectedTag);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().EndWith(expectedTag);
+ }
+
+ [InlineData("v6.0", "linux-musl-x64", "mcr.microsoft.com/dotnet/runtime:6.0-alpine")]
+ [InlineData("v6.0", "linux-x64", "mcr.microsoft.com/dotnet/runtime:6.0")]
+ [InlineData("v7.0", "linux-musl-x64", "mcr.microsoft.com/dotnet/runtime:7.0-alpine")]
+ [InlineData("v7.0", "linux-x64", "mcr.microsoft.com/dotnet/runtime:7.0")]
+ [InlineData("v8.0", "linux-musl-x64", "mcr.microsoft.com/dotnet/runtime:8.0-alpine")]
+ [InlineData("v8.0", "linux-x64", "mcr.microsoft.com/dotnet/runtime:8.0")]
+ [Theory]
+ public void MuslRidsGetAlpineContainers(string tfm, string rid, string expectedImage)
+ {
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.100",
+ ["TargetFrameworkVersion"] = tfm,
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = rid,
+ }, projectName: $"{nameof(MuslRidsGetAlpineContainers)}_{tfm}_{rid}_{expectedImage}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ [InlineData("linux-musl-x64", "mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-alpine-aot")]
+ [InlineData("linux-x64", "mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-jammy-chiseled-aot")]
+ [Theory]
+ public void AOTAppsGetAOTImages(string rid, string expectedImage)
+ {
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.100",
+ ["TargetFrameworkVersion"] = "v8.0",
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = rid,
+ [KnownStrings.Properties.PublishSelfContained] = true.ToString(),
+ [KnownStrings.Properties.PublishAot] = true.ToString(),
+ [KnownStrings.Properties.InvariantGlobalization] = true.ToString(),
+ }, projectName: $"{nameof(AOTAppsGetAOTImages)}_{rid}_{expectedImage}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ [InlineData("linux-musl-x64", "mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-alpine-extra")]
+ [InlineData("linux-x64", "mcr.microsoft.com/dotnet/nightly/runtime-deps:8.0-jammy-chiseled-extra")]
+ [Theory]
+ public void AOTAppsWithCulturesGetExtraImages(string rid, string expectedImage)
+ {
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.100",
+ ["TargetFrameworkVersion"] = "v8.0",
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = rid,
+ [KnownStrings.Properties.PublishSelfContained] = true.ToString(),
+ [KnownStrings.Properties.PublishAot] = true.ToString(),
+ [KnownStrings.Properties.InvariantGlobalization] = false.ToString()
+ }, projectName: $"{nameof(AOTAppsWithCulturesGetExtraImages)}_{rid}_{expectedImage}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ [InlineData("linux-musl-x64", "mcr.microsoft.com/dotnet/runtime-deps:7.0-alpine")]
+ [InlineData("linux-x64", "mcr.microsoft.com/dotnet/runtime-deps:7.0")]
+ [Theory]
+ public void AOTAppsLessThan8DoNotGetAOTImages(string rid, string expectedImage)
+ {
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.100",
+ ["TargetFrameworkVersion"] = "v7.0",
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = rid,
+ [KnownStrings.Properties.PublishSelfContained] = true.ToString(),
+ [KnownStrings.Properties.PublishAot] = true.ToString(),
+ [KnownStrings.Properties.InvariantGlobalization] = true.ToString(),
+ }, projectName: $"{nameof(AOTAppsLessThan8DoNotGetAOTImages)}_{rid}_{expectedImage}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ [InlineData("linux-musl-x64", "mcr.microsoft.com/dotnet/runtime-deps:7.0-alpine")]
+ [InlineData("linux-x64", "mcr.microsoft.com/dotnet/runtime-deps:7.0")]
+ [Theory]
+ public void AOTAppsLessThan8WithCulturesDoNotGetExtraImages(string rid, string expectedImage)
+ {
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.100",
+ ["TargetFrameworkVersion"] = "v7.0",
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = rid,
+ [KnownStrings.Properties.PublishSelfContained] = true.ToString(),
+ [KnownStrings.Properties.PublishAot] = true.ToString(),
+ [KnownStrings.Properties.InvariantGlobalization] = false.ToString()
+ }, projectName: $"{nameof(AOTAppsWithCulturesGetExtraImages)}_{rid}_{expectedImage}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ [Fact]
+ public void AspNetFDDAppsGetAspNetBaseImage()
+ {
+ var expectedImage = "mcr.microsoft.com/dotnet/aspnet:8.0";
+ var (project, logger, d) = ProjectInitializer.InitProject(new()
+ {
+ ["NetCoreSdkVersion"] = "8.0.200",
+ ["TargetFrameworkVersion"] = "v8.0",
+ [KnownStrings.Properties.ContainerRuntimeIdentifier] = "linux-x64",
+ }, bonusItems: new()
+ {
+ [KnownStrings.Items.FrameworkReference] = KnownFrameworkReferences.WebApp
+ }, projectName: $"{nameof(AspNetFDDAppsGetAspNetBaseImage)}");
+ using var _ = d;
+ var instance = project.CreateProjectInstance(global::Microsoft.Build.Execution.ProjectInstanceSettings.None);
+ instance.Build(new[] { ComputeContainerBaseImage }, null, null, out var outputs).Should().BeTrue(String.Join(Environment.NewLine, logger.Errors));
+ var computedBaseImageTag = instance.GetProperty(ContainerBaseImage)?.EvaluatedValue;
+ computedBaseImageTag.Should().BeEquivalentTo(expectedImage);
+ }
+
+ private static class KnownFrameworkReferences
+ {
+ public static Microsoft.Build.Framework.ITaskItem[] ConsoleApp { get; } = [new Microsoft.Build.Utilities.TaskItem("Microsoft.NETCore.App")];
+ public static Microsoft.Build.Framework.ITaskItem[] WebApp { get; } = [.. ConsoleApp, new Microsoft.Build.Utilities.TaskItem("Microsoft.AspNetCore.App")];
}
}
diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ToolsetUtils.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ToolsetUtils.cs
index 35cf4fb4ddd0..599d95280157 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ToolsetUtils.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/ToolsetUtils.cs
@@ -20,6 +20,8 @@ internal static string GetRuntimeGraphFilePath()
return lastWrittenSdk.GetFiles("RuntimeIdentifierGraph.json").Single().FullName;
}
+ internal static IManifestPicker RidGraphManifestPicker { get; } = new RidGraphManifestPicker(GetRuntimeGraphFilePath());
+
///
/// Gets path to built Microsoft.NET.Build.Containers.*.nupkg prepared for tests.
///
diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageBuilderTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageBuilderTests.cs
index 29c4b35fc6ef..559bd7d33c86 100644
--- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageBuilderTests.cs
+++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/ImageBuilderTests.cs
@@ -221,7 +221,7 @@ public void CanAddPortsToImage()
Assert.Equal(2, resultPorts.Count);
Assert.NotNull(resultPorts["6000/tcp"] as JsonObject);
- Assert.NotNull( resultPorts["6010/udp"] as JsonObject);
+ Assert.NotNull(resultPorts["6010/udp"] as JsonObject);
}
[Fact]
@@ -602,12 +602,56 @@ public void CanSetContainerUserAndOverrideAppUID()
config!["config"]?["User"]?.GetValue().Should().Be(expected: userId, because: "The precedence of SetUser should override inferred user ids");
}
+ [Fact]
+ public void WhenMultipleUrlSourcesAreSetOnlyAspnetcoreUrlsIsUsed()
+ {
+ var builder = FromBaseImageConfig($$"""
+ {
+ "architecture": "amd64",
+ "config": {
+ "Hostname": "",
+ "Domainname": "",
+ "User": "",
+ "Env": [
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+ ],
+ "Cmd": ["bash"],
+ "Image": "sha256:d772d27ebeec80393349a4770dc37f977be2c776a01c88b624d43f93fa369d69",
+ "WorkingDir": ""
+ },
+ "created": "2023-02-04T08:14:52.000901321Z",
+ "os": "linux",
+ "rootfs": {
+ "type": "layers",
+ "diff_ids": [
+ "sha256:bd2fe8b74db65d82ea10db97368d35b92998d4ea0e7e7dc819481fe4a68f64cf",
+ "sha256:94100d1041b650c6f7d7848c550cd98c25d0bdc193d30692e5ea5474d7b3b085",
+ "sha256:53c2a75a33c8f971b4b5036d34764373e134f91ee01d8053b4c3573c42e1cf5d",
+ "sha256:49a61320e585180286535a2545be5722b09e40ad44c7c190b20ec96c9e42e4a3",
+ "sha256:8a379cce2ac272aa71aa029a7bbba85c852ba81711d9f90afaefd3bf5036dc48"
+ ]
+ }
+ }
+ """);
+
+ builder.AddEnvironmentVariable(ImageBuilder.EnvironmentVariables.ASPNETCORE_URLS, "https://*:12345");
+ builder.AddEnvironmentVariable(ImageBuilder.EnvironmentVariables.ASPNETCORE_HTTPS_PORTS, "456");
+ var builtImage = builder.Build();
+ JsonNode? result = JsonNode.Parse(builtImage.Config);
+ Assert.NotNull(result);
+ var portsObject = result["config"]?["ExposedPorts"]?.AsObject();
+ var assignedPorts = portsObject?.AsEnumerable().Select(portString => int.Parse(portString.Key.Split('/')[0])).ToArray();
+ Assert.Equal([12345], assignedPorts);
+ }
+
private ImageBuilder FromBaseImageConfig(string baseImageConfig, [CallerMemberName] string testName = "")
{
- var manifest = new ManifestV2() {
+ var manifest = new ManifestV2()
+ {
SchemaVersion = 2,
MediaType = SchemaTypes.DockerManifestV2,
- Config = new ManifestConfig() {
+ Config = new ManifestConfig()
+ {
mediaType = "",
size = 0,
digest = "sha256:0"
diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs
index 551f60fe656e..2701aa314dd1 100644
--- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs
+++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs
@@ -254,8 +254,8 @@ public void It_restores_only_ridless_tfm()
}
[Theory]
- [InlineData("netcoreapp2.0")]
- [InlineData("netcoreapp2.1")]
+ [InlineData("net6.0")]
+ [InlineData("net7.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_runs_the_app_from_the_output_folder(string targetFramework)
{
@@ -263,41 +263,34 @@ public void It_runs_the_app_from_the_output_folder(string targetFramework)
}
[Theory]
- [InlineData("netcoreapp2.1")]
+ [InlineData("net6.0")]
+ [InlineData("net7.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_runs_a_rid_specific_app_from_the_output_folder(string targetFramework)
- {
+ {
RunAppFromOutputFolder("RunFromOutputFolderWithRID_" + targetFramework, true, false, targetFramework);
}
[Theory]
- [InlineData("netcoreapp2.0")]
+ [InlineData("net6.0")]
+ [InlineData("net7.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_runs_the_app_with_conflicts_from_the_output_folder(string targetFramework)
{
- if (!EnvironmentInfo.SupportsTargetFramework(targetFramework))
- {
- return;
- }
-
RunAppFromOutputFolder("RunFromOutputFolderConflicts_" + targetFramework, false, true, targetFramework);
}
[Theory]
- [InlineData("netcoreapp2.0")]
+ [InlineData("net6.0")]
+ [InlineData("net7.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_runs_a_rid_specific_app_with_conflicts_from_the_output_folder(string targetFramework)
{
- if (!EnvironmentInfo.SupportsTargetFramework(targetFramework))
- {
- return;
- }
-
RunAppFromOutputFolder("RunFromOutputFolderWithRIDConflicts_" + targetFramework, true, true, targetFramework);
}
private void RunAppFromOutputFolder(string testName, bool useRid, bool includeConflicts,
- string targetFramework = "netcoreapp2.0")
+ string targetFramework = ToolsetInfo.CurrentTargetFramework)
{
var runtimeIdentifier = useRid ? EnvironmentInfo.GetCompatibleRid(targetFramework) : null;
diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAFrameworkDependentApp.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAFrameworkDependentApp.cs
index ff31fa3e626b..c85eef8641f2 100644
--- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAFrameworkDependentApp.cs
+++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAFrameworkDependentApp.cs
@@ -15,12 +15,12 @@ public GivenThatWeWantToPublishAFrameworkDependentApp(ITestOutputHelper log) : b
}
[Theory]
- [InlineData(null, "netcoreapp2.1")]
- [InlineData("true", "netcoreapp2.1")]
- [InlineData("false", "netcoreapp2.1")]
- [InlineData(null, "netcoreapp2.2")]
- [InlineData("true", "netcoreapp2.2")]
- [InlineData("false", "netcoreapp2.2")]
+ [InlineData(null, "net6.0")]
+ [InlineData("true", "net6.0")]
+ [InlineData("false", "net6.0")]
+ [InlineData(null, "net7.0")]
+ [InlineData("true", "net7.0")]
+ [InlineData("false", "net7.0")]
[InlineData(null, ToolsetInfo.CurrentTargetFramework)]
[InlineData("true", ToolsetInfo.CurrentTargetFramework)]
[InlineData("false", ToolsetInfo.CurrentTargetFramework)]
@@ -46,14 +46,6 @@ public void It_publishes_with_or_without_apphost(string useAppHost, string targe
msbuildArgs.Add($"/p:UseAppHost={useAppHost}");
}
- if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX) &&
- targetFramework == "netcoreapp2.1")
- {
- // .NET Core 2.1.0 packages don't support latest versions of OS X, so roll forward to the
- // latest patch which does
- msbuildArgs.Add("/p:TargetLatestRuntimePatch=true");
- }
-
var publishCommand = new PublishCommand(testAsset);
publishCommand
.Execute(msbuildArgs.ToArray())
@@ -112,7 +104,7 @@ public void It_errors_when_using_app_host_with_older_target_framework()
.Should()
.Fail()
.And
- .HaveStdOutContaining(Strings.FrameworkDependentAppHostRequiresVersion21.Replace("“", "\"").Replace("”", "\""));
+ .HaveStdOutContaining(Strings.FrameworkDependentAppHostRequiresVersion21.Replace("�", "\"").Replace("�", "\""));
}
}
}
diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs
index 21a1c24b5ed2..57c50ae45b85 100644
--- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs
+++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishASingleFileApp.cs
@@ -150,6 +150,25 @@ public void It_generates_publishing_single_file_with_win7()
.Pass();
}
+ [Fact]
+ public void Target_after_AfterSdkPublish_executes()
+ {
+ var projectChanges = (XDocument doc) =>
+ {
+ var ns = doc.Root.Name.Namespace;
+ var target = new XElement("Target");
+ target.ReplaceAttributes(new XAttribute[] { new XAttribute("Name", "AfterAfterSdkPublish"), new XAttribute("AfterTargets", "AfterSdkPublish") });
+ var message = new XElement("Message");
+ message.ReplaceAttributes(new XAttribute[] { new XAttribute("Importance", "High"), new XAttribute("Text", "Executed AfterAfterSdkPublish") });
+ target.Add(message);
+ doc.Root.Add(target);
+ };
+
+ var publishResults = GetPublishCommand(projectChanges: projectChanges).Execute();
+ publishResults.Should().Pass();
+ publishResults.Should().HaveStdOutContaining("Executed AfterAfterSdkPublish");
+ }
+
[Fact]
public void It_errors_when_publishing_single_file_lib()
{
diff --git a/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs b/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
index 1bd8241383e0..8469b0a9f7d5 100644
--- a/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
+++ b/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
@@ -90,7 +90,7 @@ public void CanOverrideScopeIdentifiers()
var scoped = Path.Combine(intermediateOutputPath, "scopedcss", "Styles", "Pages", "Counter.rz.scp.css");
new FileInfo(scoped).Should().Exist();
new FileInfo(scoped).Should().Contain("b-overriden");
- var generated = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
+ var generated = Path.Combine(intermediateOutputPath, "generated", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
new FileInfo(generated).Should().Exist();
new FileInfo(generated).Should().Contain("b-overriden");
new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
@@ -319,7 +319,7 @@ public void Build_RemovingScopedCssAndBuilding_UpdatesGeneratedCodeAndBundle()
new FileInfo(generatedBundle).Should().Exist();
var generatedProjectBundle = Path.Combine(intermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css");
new FileInfo(generatedProjectBundle).Should().Exist();
- var generatedCounter = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
+ var generatedCounter = Path.Combine(intermediateOutputPath, "generated", "Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
new FileInfo(generatedCounter).Should().Exist();
var componentThumbprint = FileThumbPrint.Create(generatedCounter);
diff --git a/src/Tests/SDDLTests/Program.cs b/src/Tests/SDDLTests/Program.cs
index eed29c834100..f6290d24535d 100644
--- a/src/Tests/SDDLTests/Program.cs
+++ b/src/Tests/SDDLTests/Program.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.Versioning;
using System.Security.AccessControl;
@@ -22,6 +23,21 @@ public class SDDLTests
///
private static readonly string s_programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
+ ///
+ /// Directory under ProgramData to store the install state file.
+ ///
+ private static readonly string s_installStateDirectory = Path.Combine(s_programData, "SDDLTest", "workloads", "8.0.100", "InstallState");
+
+ ///
+ /// The filename and extension of the install state file.
+ ///
+ private static readonly string s_installStateFile = "default.json";
+
+ ///
+ /// The full path of the install state file.
+ ///
+ private static readonly string s_installStateFileAssetPath = Path.Combine(s_installStateDirectory, s_installStateFile);
+
///
/// Directory under the user's %temp% directory where the test asset will be created.
///
@@ -151,8 +167,8 @@ private static string CreateTestAsset()
///
private static void RelocateAndSecureAsset()
{
- MsiPackageCache.CreateSecureDirectory(s_workloadPackCacheDirectory);
- MsiPackageCache.MoveAndSecureFile(s_userTestAssetPath, s_cachedTestAssetPath);
+ SecurityUtils.CreateSecureDirectory(s_workloadPackCacheDirectory);
+ SecurityUtils.MoveAndSecureFile(s_userTestAssetPath, s_cachedTestAssetPath);
}
///
@@ -230,6 +246,25 @@ private static void VerifyDescriptors()
VerifyFileSecurityDescriptor(s_cachedTestAssetPath, "BA", "BA", 4, "A;ID;0x1200a9;;;WD", "A;ID;FA;;;SY", "A;ID;FA;;;BA", "A;ID;0x1200a9;;;BU");
}
+ private static void CreateInstallStateAsset()
+ {
+ SecurityUtils.CreateSecureDirectory(s_installStateDirectory);
+ File.WriteAllLines(s_installStateFileAssetPath, new[] { "line1", "line2" });
+ SecurityUtils.SecureFile(s_installStateFileAssetPath);
+ }
+
+ private static void VerifyInstallStateDescriptors()
+ {
+ // Dump the descriptor of ProgramData since it's useful for analyzing.
+ DirectorySecurity ds = new DirectorySecurity(s_programData, s_accessControlSections);
+ string descriptor = ds.GetSecurityDescriptorSddlForm(s_accessControlSections);
+ Console.WriteLine($" Directory: {s_programData}");
+ Console.WriteLine($"Descriptor: {descriptor}");
+
+ VerifyDirectorySecurityDescriptor(s_installStateDirectory, "BA", "BA", 4, "A;OICIID;0x1200a9;;;WD", "A;OICIID;FA;;;SY", "A;OICIID;FA;;;BA", "A;OICIID;0x1200a9;;;BU");
+ VerifyFileSecurityDescriptor(s_installStateFileAssetPath, "BA", "BA", 4, "A;ID;0x1200a9;;;WD", "A;ID;FA;;;SY", "A;ID;FA;;;BA", "A;ID;0x1200a9;;;BU");
+ }
+
static void Main(string[] args)
{
if (!OperatingSystem.IsWindows())
@@ -251,6 +286,8 @@ static void Main(string[] args)
try
{
RelocateAndSecureAsset();
+
+ CreateInstallStateAsset();
}
catch
{
@@ -268,6 +305,9 @@ static void Main(string[] args)
CreateTestAsset();
RelocateAndSecureAsset();
VerifyDescriptors();
+
+ CreateInstallStateAsset();
+ VerifyInstallStateDescriptors();
}
catch (Exception e)
{
@@ -317,6 +357,7 @@ static void Main(string[] args)
}
VerifyDescriptors();
+ VerifyInstallStateDescriptors();
}
else
{
diff --git a/src/Tests/dotnet-nuget.UnitTests/GivenANuGetCommand.cs b/src/Tests/dotnet-nuget.UnitTests/GivenANuGetCommand.cs
index 6806a7d4e8ee..b405aa74e257 100644
--- a/src/Tests/dotnet-nuget.UnitTests/GivenANuGetCommand.cs
+++ b/src/Tests/dotnet-nuget.UnitTests/GivenANuGetCommand.cs
@@ -65,6 +65,17 @@ public GivenANuGetCommand(ITestOutputHelper log) : base(log)
"--certificate-store-location", "CurrentUser",
"--certificate-subject-name", "CE40881FF5F0AD3E58965DA20A9F57",
"--certificate-password", "PlaceholderPassword"}, 0)]
+ [InlineData(new[] { "package", "search", "nuget"}, 0)]
+ [InlineData(new[] { "package", "search", "nuget",
+ "--source", "https://api.nuget.org/v3/index.json",
+ "--take", "10",
+ "--skip", "5",
+ "--prerelease",
+ "--exact-match",
+ "--interactive",
+ "--verbosity", "detailed",
+ "--format", "json"}, 0)]
+
public void ItPassesCommandIfSupported(string[] inputArgs, int result)
{
// Arrange
diff --git a/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs b/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs
index b619e67e1e3e..476a5f49894c 100644
--- a/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs
+++ b/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs
@@ -104,7 +104,7 @@ public void GivenWorkloadInstallOnFailingRollbackItDisplaysTopLevelError()
var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android"), new WorkloadId("xamarin-android-build") };
var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
var dotnetRoot = Path.Combine(testDirectory, "dotnet");
- var installer = new MockPackWorkloadInstaller(failingWorkload: "xamarin-android-build", failingRollback: true);
+ var installer = new MockPackWorkloadInstaller(dotnetRoot, failingWorkload: "xamarin-android-build", failingRollback: true);
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot);
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "xamarin-android-build", "--skip-manifest-update" });
var workloadResolverFactory = new MockWorkloadResolverFactory(dotnetRoot, "6.0.100", workloadResolver);
@@ -140,7 +140,7 @@ public void GivenWorkloadInstallItWarnsOnGarbageCollectionFailure()
var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android"), new WorkloadId("xamarin-android-build") };
var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
var dotnetRoot = Path.Combine(testDirectory, "dotnet");
- var installer = new MockPackWorkloadInstaller(failingGarbageCollection: true);
+ var installer = new MockPackWorkloadInstaller(dotnetRoot, failingGarbageCollection: true);
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot);
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "xamarin-android-build", "--skip-manifest-update" });
var workloadResolverFactory = new MockWorkloadResolverFactory(dotnetRoot, "6.0.100", workloadResolver);
@@ -169,10 +169,8 @@ public void GivenNoWorkloadsInstalledInfoOptionRemarksOnThat()
// However, we can test a setup where no workloads are installed and --info is provided.
_reporter.Clear();
- var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android"), new WorkloadId("xamarin-android-build") };
var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
var dotnetRoot = Path.Combine(testDirectory, "dotnet");
- var installer = new MockPackWorkloadInstaller();
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot);
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android"});
@@ -207,18 +205,17 @@ public void GivenWorkloadInstallItCanUpdateInstalledManifests(bool userLocal, st
Parser.Instance.Parse(new string[] {"dotnet", "workload", "install", "xamarin-android"});
var featureBand = new SdkFeatureBand(sdkVersion);
var manifestsToUpdate =
- new (ManifestVersionUpdate manifestUpdate, Dictionary Workloads)[]
+ new ManifestUpdateWithWorkloads[]
{
- (new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), featureBand.ToString(), new ManifestVersion("2.0.0"), featureBand.ToString()),
- null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), featureBand.ToString(), new ManifestVersion("2.0.0"), featureBand.ToString()), null),
};
(_, var installManager, var installer, _, _, _) =
GetTestInstallers(parseResult, userLocal, sdkVersion, manifestUpdates: manifestsToUpdate, installedFeatureBand: sdkVersion);
installManager.InstallWorkloads(new List(), false); // Don't actually do any installs, just update manifests
- installer.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].manifestUpdate.ManifestId);
- installer.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].manifestUpdate.NewVersion);
+ installer.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId);
+ installer.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion);
installer.InstalledManifests[0].manifestUpdate.NewFeatureBand.Should().Be(new SdkFeatureBand(sdkVersion).ToString());
installer.InstalledManifests[0].offlineCache.Should().Be(null);
}
@@ -232,11 +229,9 @@ public void GivenWorkloadInstallFromCacheItInstallsCachedManifest(bool userLocal
{
var featureBand = new SdkFeatureBand(sdkVersion);
var manifestsToUpdate =
- new (ManifestVersionUpdate manifestUpdate, Dictionary
- Workloads)[]
+ new ManifestUpdateWithWorkloads[]
{
- (new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), featureBand.ToString(), new ManifestVersion("2.0.0"), featureBand.ToString()),
- null)
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), featureBand.ToString(), new ManifestVersion("2.0.0"), featureBand.ToString()), null)
};
var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: AppendForUserLocal("mockCache_", userLocal) + sdkVersion).Path,
"mockCachePath");
@@ -249,8 +244,8 @@ public void GivenWorkloadInstallFromCacheItInstallsCachedManifest(bool userLocal
installManager.Execute();
- installer.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].manifestUpdate.ManifestId);
- installer.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].manifestUpdate.NewVersion);
+ installer.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId);
+ installer.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion);
installer.InstalledManifests[0].manifestUpdate.NewFeatureBand.Should().Be(new SdkFeatureBand(sdkVersion).ToString());
installer.InstalledManifests[0].offlineCache.Should().Be(new DirectoryPath(cachePath));
}
@@ -264,7 +259,7 @@ public void GivenWorkloadInstallItCanDownloadToOfflineCache(bool userLocal, stri
{
var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: AppendForUserLocal("mockCache_", userLocal) + sdkVersion).Path, "mockCachePath");
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--download-to-cache", cachePath });
- (_, var installManager, var installer, _, var manifestUpdater, var packageDownloader) = GetTestInstallers(parseResult, userLocal, sdkVersion, tempDirManifestPath: _manifestPath, installedFeatureBand: sdkVersion);
+ (_, var installManager, _, _, var manifestUpdater, var packageDownloader) = GetTestInstallers(parseResult, userLocal, sdkVersion, tempDirManifestPath: _manifestPath, installedFeatureBand: sdkVersion);
installManager.Execute();
@@ -324,7 +319,7 @@ public void GivenWorkloadInstallItErrorsOnUnsupportedPlatform()
var manifestPath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "UnsupportedPlatform.json");
var testDirectory = _testAssetsManager.CreateTestDirectory().Path;
var dotnetRoot = Path.Combine(testDirectory, "dotnet");
- var installer = new MockPackWorkloadInstaller();
+ var installer = new MockPackWorkloadInstaller(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), dotnetRoot);
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var manifestUpdater = new MockWorkloadManifestUpdater();
@@ -457,7 +452,7 @@ static void CreateFile(string path)
string sdkVersion,
[CallerMemberName] string testName = "",
string failingWorkload = null,
- IEnumerable<(ManifestVersionUpdate manifestUpdate, Dictionary Workloads)> manifestUpdates = null,
+ IEnumerable manifestUpdates = null,
string tempDirManifestPath = null,
string installedFeatureBand = null)
{
@@ -466,13 +461,13 @@ static void CreateFile(string path)
var dotnetRoot = Path.Combine(testDirectory, "dotnet");
var userProfileDir = Path.Combine(testDirectory, "user-profile");
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot);
- var installer = new MockPackWorkloadInstaller(failingWorkload)
+ var installer = new MockPackWorkloadInstaller(dotnetRoot, failingWorkload)
{
WorkloadResolver = workloadResolver
};
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
- var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates, tempDirManifestPath);
+ var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates);
if (userLocal)
{
WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkVersion);
@@ -593,13 +588,11 @@ public void ShowManifestUpdatesWhenVerbosityIsDetailedOrDiagnostic(string verbos
var parseResult =
Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", verbosityFlag, "xamarin-android" });
var manifestsToUpdate =
- new (ManifestVersionUpdate manifestUpdate, Dictionary
- Workloads)[]
+ new ManifestUpdateWithWorkloads[]
{
- (new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), sdkFeatureBand, new ManifestVersion("2.0.0"), sdkFeatureBand),
- null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), sdkFeatureBand, new ManifestVersion("2.0.0"), sdkFeatureBand), null),
};
- (_, var installManager, var installer, _, _, _) =
+ (_, var installManager, _, _, _, _) =
GetTestInstallers(parseResult, true, sdkFeatureBand, manifestUpdates: manifestsToUpdate);
installManager.InstallWorkloads(new List(), false); // Don't actually do any installs, just update manifests
diff --git a/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs b/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs
index 3a98644e69ee..d352cf05da03 100644
--- a/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs
+++ b/src/Tests/dotnet-workload-install.Tests/GivenFileBasedWorkloadInstall.cs
@@ -12,6 +12,7 @@
using Microsoft.Extensions.EnvironmentAbstractions;
using System.Text.Json;
using Microsoft.TemplateEngine.Edge.Constraints;
+using Microsoft.DotNet.Workloads.Workload;
namespace Microsoft.DotNet.Cli.Workload.Install.Tests
{
@@ -26,6 +27,42 @@ public GivenFileBasedWorkloadInstall(ITestOutputHelper log) : base(log)
_manifestPath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "Sample2.json");
}
+ [Fact]
+ public void InstallStateUpdatesWorkProperly()
+ {
+ (string dotnetRoot, FileBasedInstaller installer, _, _) = GetTestInstaller();
+ var stringFeatureBand = "6.0.300"; // This is hard-coded in the test installer, so if that changes, update this, too.
+ var sdkFeatureBand = new SdkFeatureBand(stringFeatureBand);
+ var path = Path.Combine(dotnetRoot, "metadata", "workloads", stringFeatureBand, "InstallState", "default.json");
+
+ installer.UpdateInstallMode(sdkFeatureBand, true);
+ var installState = InstallStateContents.FromString(File.ReadAllText(path));
+ installState.Manifests.Should().BeNull();
+ installState.UseWorkloadSets.Should().BeTrue();
+
+ installer.SaveInstallStateManifestVersions(sdkFeatureBand, new Dictionary()
+ {
+ { "first", "second" },
+ { "third", "fourth" },
+ });
+
+ installState = InstallStateContents.FromString(File.ReadAllText(path));
+ installState.Manifests.Count.Should().Be(2);
+ installState.Manifests["first"].Should().Be("second");
+ installState.Manifests["third"].Should().Be("fourth");
+ installState.UseWorkloadSets.Should().BeTrue();
+
+ installer.UpdateInstallMode(sdkFeatureBand, false);
+ installState = InstallStateContents.FromString(File.ReadAllText(path));
+ installState.UseWorkloadSets.Should().BeFalse();
+ installState.Manifests.Count.Should().Be(2);
+
+ installer.RemoveManifestsFromInstallState(sdkFeatureBand);
+ installState = InstallStateContents.FromString(File.ReadAllText(path));
+ installState.Manifests.Should().BeNull();
+ installState.UseWorkloadSets.Should().BeFalse();
+ }
+
[Fact]
public void GivenManagedInstallItCanGetFeatureBandsWhenFilesArePresent()
{
diff --git a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs
index b9b9b8eb969d..029d28fd9ee6 100644
--- a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs
+++ b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs
@@ -124,9 +124,9 @@ public void GivenWorkloadManifestUpdateItCanCalculateUpdates()
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, userProfileDir: Path.Combine(testDir, ".dotnet"), testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, userProfileDir: Path.Combine(testDir, ".dotnet"), installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
- var manifestUpdates = manifestUpdater.CalculateManifestUpdates().Select( m => m.manifestUpdate);
+ var manifestUpdates = manifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate);
manifestUpdates.Should().BeEquivalentTo(expectedManifestUpdates);
}
@@ -191,9 +191,9 @@ public void GivenAdvertisedManifestsItCalculatesCorrectUpdates()
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, userProfileDir: Path.Combine(testDir, ".dotnet"), testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, userProfileDir: Path.Combine(testDir, ".dotnet"), installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
- var manifestUpdates = manifestUpdater.CalculateManifestUpdates().Select(m => m.manifestUpdate);
+ var manifestUpdates = manifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate);
manifestUpdates.Should().BeEquivalentTo(expectedManifestUpdates);
}
@@ -233,7 +233,7 @@ public void ItCanFallbackAndAdvertiseCorrectUpdate(bool useOfflineCache)
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
nugetDownloader.PackageIdsToNotFind.Add($"{testManifestName}.Manifest-6.0.300");
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
var offlineCacheDir = "";
if (useOfflineCache)
@@ -310,7 +310,7 @@ public void ItCanFallbackWithNoUpdates(bool useOfflineCache)
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
var offlineCacheDir = "";
if (useOfflineCache)
@@ -377,7 +377,7 @@ public void GivenNoUpdatesAreAvailableAndNoRollbackItGivesAppropriateMessage(boo
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, Path.Combine(testDir, ".dotnet"), installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
var offlineCacheDir = "";
if (useOfflineCache)
@@ -440,7 +440,7 @@ public void GivenWorkloadManifestRollbackItCanCalculateUpdates()
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
var manifestUpdates = manifestUpdater.CalculateManifestRollbacks(rollbackDefPath);
manifestUpdates.Should().BeEquivalentTo(expectedManifestUpdates);
@@ -483,7 +483,7 @@ public void GivenFromRollbackDefinitionItErrorsOnInstalledExtraneousManifestId()
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
manifestUpdater.CalculateManifestRollbacks(rollbackDefPath);
string.Join(" ", _reporter.Lines).Should().Contain(rollbackDefPath);
@@ -525,7 +525,7 @@ public void GivenFromRollbackDefinitionItErrorsOnExtraneousManifestIdInRollbackD
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, new MockPackWorkloadInstaller());
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, new MockPackWorkloadInstaller(dotnetRoot));
manifestUpdater.CalculateManifestRollbacks(rollbackDefPath);
string.Join(" ", _reporter.Lines).Should().Contain(rollbackDefPath);
@@ -556,8 +556,8 @@ public void GivenWorkloadManifestUpdateItChoosesHighestManifestVersionInCache()
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var installer = new MockPackWorkloadInstaller();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, installer);
+ var installer = new MockPackWorkloadInstaller(dotnetRoot);
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, installer);
manifestUpdater.UpdateAdvertisingManifestsAsync(false, new DirectoryPath(offlineCache)).Wait();
// We should have chosen the higher version manifest package to install/ extract
@@ -709,7 +709,7 @@ public void TestSideBySideUpdateChecks()
var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot);
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
var installationRepo = new MockInstallationRecordRepository();
- var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, new MockPackWorkloadInstaller(), getEnvironmentVariable: getEnvironmentVariable);
+ var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, new MockPackWorkloadInstaller(dotnetRoot), getEnvironmentVariable: getEnvironmentVariable);
var sentinelPath = Path.Combine(testDir, _manifestSentinelFileName + featureBand);
return (manifestUpdater, nugetDownloader, sentinelPath);
diff --git a/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs b/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs
index f5863d1e98dd..61d6dc6f24d8 100644
--- a/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs
+++ b/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs
@@ -7,6 +7,7 @@
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.NET.Sdk.WorkloadManifestReader;
using Microsoft.DotNet.ToolPackage;
+using Microsoft.DotNet.Workloads.Workload;
namespace Microsoft.DotNet.Cli.Workload.Install.Tests
{
@@ -22,12 +23,13 @@ internal class MockPackWorkloadInstaller : IInstaller
public bool FailingRollback;
public bool FailingGarbageCollection;
private readonly string FailingPack;
+ private readonly string _dotnetDir;
public IWorkloadResolver WorkloadResolver { get; set; }
public int ExitCode => 0;
- public MockPackWorkloadInstaller(string failingWorkload = null, string failingPack = null, bool failingRollback = false, IList installedWorkloads = null,
+ public MockPackWorkloadInstaller(string dotnetDir, string failingWorkload = null, string failingPack = null, bool failingRollback = false, IList installedWorkloads = null,
IList installedPacks = null, bool failingGarbageCollection = false)
{
InstallationRecordRepository = new MockInstallationRecordRepository(failingWorkload, installedWorkloads);
@@ -35,6 +37,7 @@ public MockPackWorkloadInstaller(string failingWorkload = null, string failingPa
InstalledPacks = installedPacks ?? new List();
FailingPack = failingPack;
FailingGarbageCollection = failingGarbageCollection;
+ _dotnetDir = dotnetDir;
}
IEnumerable GetPacksForWorkloads(IEnumerable workloadIds)
@@ -53,6 +56,11 @@ IEnumerable GetPacksForWorkloads(IEnumerable workloadIds)
}
}
+ public void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode)
+ {
+ throw new NotImplementedException();
+ }
+
public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, ITransactionContext transactionContext, DirectoryPath? offlineCache = null)
{
List packs = new List();
@@ -156,6 +164,26 @@ public void ReplaceWorkloadResolver(IWorkloadResolver workloadResolver)
{
WorkloadResolver = workloadResolver;
}
+
+ public void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, _dotnetDir), "default.json");
+ if (File.Exists(path))
+ {
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.Manifests = null;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
+ }
+
+ public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary manifestContents)
+ {
+ string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, _dotnetDir), "default.json");
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents();
+ installStateContents.Manifests = manifestContents;
+ File.WriteAllText(path, installStateContents.ToString());
+ }
}
internal class MockInstallationRecordRepository : IWorkloadInstallationRecordRepository
diff --git a/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs b/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs
index d3e2bf8a3b6f..6a3d90073c64 100644
--- a/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs
+++ b/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs
@@ -12,16 +12,11 @@ internal class MockWorkloadManifestUpdater : IWorkloadManifestUpdater
public int UpdateAdvertisingManifestsCallCount = 0;
public int CalculateManifestUpdatesCallCount = 0;
public int GetManifestPackageDownloadsCallCount = 0;
- private IEnumerable<(ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)> _manifestUpdates;
- private string _tempDirManifestPath;
+ private readonly IEnumerable _manifestUpdates;
- public MockWorkloadManifestUpdater(IEnumerable<(ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)> manifestUpdates = null, string tempDirManifestPath = null)
+ public MockWorkloadManifestUpdater(IEnumerable manifestUpdates = null)
{
- _manifestUpdates = manifestUpdates ?? new List<(ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)>();
- _tempDirManifestPath = tempDirManifestPath;
+ _manifestUpdates = manifestUpdates ?? new List();
}
public Task UpdateAdvertisingManifestsAsync(bool includePreview, DirectoryPath? cachePath = null)
@@ -30,9 +25,7 @@ public Task UpdateAdvertisingManifestsAsync(bool includePreview, DirectoryPath?
return Task.CompletedTask;
}
- public IEnumerable<(
- ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)> CalculateManifestUpdates()
+ public IEnumerable CalculateManifestUpdates()
{
CalculateManifestUpdatesCallCount++;
return _manifestUpdates;
@@ -49,11 +42,11 @@ public Task> GetManifestPackageDownloadsAsync(bool
public IEnumerable CalculateManifestRollbacks(string rollbackDefinitionFilePath)
{
- return _manifestUpdates.Select(t => t.manifestUpdate);
+ return _manifestUpdates.Select(t => t.ManifestUpdate);
}
- public Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync() => throw new System.NotImplementedException();
- public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable installedWorkloads) => throw new System.NotImplementedException();
+ public Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync() => throw new NotImplementedException();
+ public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable installedWorkloads) => throw new NotImplementedException();
public void DeleteUpdatableWorkloadsFile() { }
}
}
diff --git a/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs b/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs
index 98e467085595..29d942429114 100644
--- a/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs
+++ b/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs
@@ -6,6 +6,7 @@
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Workload.Install.Tests;
using Microsoft.DotNet.Workloads.Workload;
+using Microsoft.DotNet.Workloads.Workload.Install;
using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord;
using Microsoft.DotNet.Workloads.Workload.List;
using Microsoft.NET.Sdk.WorkloadManifestReader;
@@ -22,7 +23,7 @@ public class GivenInstalledWorkloadAndManifestUpdater : SdkTest
private WorkloadListCommand _workloadListCommand;
private string _testDirectory;
- private List<(ManifestVersionUpdate manifestUpdate, Dictionary Workloads)> _mockManifestUpdates;
+ private List _mockManifestUpdates;
private MockNuGetPackageDownloader _nugetDownloader;
private string _dotnetRoot;
@@ -40,14 +41,14 @@ private void Setup(string identifier)
_mockManifestUpdates = new()
{
- (
+ new(
new ManifestVersionUpdate(
new ManifestId("manifest1"),
new ManifestVersion(CurrentSdkVersion),
currentSdkFeatureBand.ToString(),
new ManifestVersion(UpdateAvailableVersion),
currentSdkFeatureBand.ToString()),
- new Dictionary
+ new WorkloadCollection
{
[new WorkloadId(InstallingWorkload)] = new(
new WorkloadId(InstallingWorkload), false, XamarinAndroidDescription,
@@ -56,28 +57,28 @@ private void Setup(string identifier)
new WorkloadId("other"), false, "other description",
WorkloadDefinitionKind.Dev, null, null, null)
}),
- (
+ new(
new ManifestVersionUpdate(
new ManifestId("manifest-other"),
new ManifestVersion(CurrentSdkVersion),
currentSdkFeatureBand.ToString(),
new ManifestVersion("7.0.101"),
currentSdkFeatureBand.ToString()),
- new Dictionary
+ new WorkloadCollection
{
[new WorkloadId("other-manifest-workload")] = new(
new WorkloadId("other-manifest-workload"), false,
"other-manifest-workload description",
WorkloadDefinitionKind.Dev, null, null, null)
}),
- (
+ new(
new ManifestVersionUpdate(
new ManifestId("manifest-older-version"),
new ManifestVersion(CurrentSdkVersion),
currentSdkFeatureBand.ToString(),
new ManifestVersion("6.0.100"),
currentSdkFeatureBand.ToString()),
- new Dictionary
+ new WorkloadCollection
{
[new WorkloadId("other-manifest-workload")] = new(
new WorkloadId("other-manifest-workload"), false,
@@ -107,7 +108,7 @@ public void ItShouldGetAvailableUpdate()
{
Setup(nameof(ItShouldGetAvailableUpdate));
WorkloadListCommand.UpdateAvailableEntry[] result =
- _workloadListCommand.GetUpdateAvailable(new List {new("xamarin-android")});
+ _workloadListCommand.GetUpdateAvailable(new List { new("xamarin-android") }).ToArray();
result.Should().NotBeEmpty();
result[0].WorkloadId.Should().Be(InstallingWorkload, "Only should installed workload");
diff --git a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs
index f9d4738eed6f..896569702333 100644
--- a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs
+++ b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs
@@ -214,7 +214,7 @@ public void GivenWorkloadUpdateItCanDownloadToOfflineCache()
var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android") };
var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: "cachePath").Path, "mockCachePath");
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--download-to-cache", cachePath });
- (_, var command, var installer, _, var manifestUpdater, var packageDownloader) = GetTestInstallers(parseResult, installedWorkloads: mockWorkloadIds, includeInstalledPacks: true, installedFeatureBand: "6.0.100");
+ (_, var command, _, _, var manifestUpdater, var packageDownloader) = GetTestInstallers(parseResult, installedWorkloads: mockWorkloadIds, includeInstalledPacks: true, installedFeatureBand: "6.0.100");
command.Execute();
@@ -326,54 +326,46 @@ public void ApplyRollbackAcrossFeatureBand(string existingSdkFeatureBand, string
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--from-rollback-file", "rollback.json" });
var manifestsToUpdate =
- new (ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)[]
+ new ManifestUpdateWithWorkloads[]
{
- (new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), existingSdkFeatureBand, new ManifestVersion("2.0.0"), newSdkFeatureBand),
- null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), existingSdkFeatureBand, new ManifestVersion("2.0.0"), newSdkFeatureBand), null),
};
-
- (var dotnetPath, var updateCommand, var packInstaller, var workloadResolver, var workloadManifestUpdater, var nuGetPackageDownloader) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", identifier: existingSdkFeatureBand + newSdkFeatureBand, installedFeatureBand: existingSdkFeatureBand);
+ (var dotnetPath, var updateCommand, var packInstaller, _, _, _) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", identifier: existingSdkFeatureBand + newSdkFeatureBand, installedFeatureBand: existingSdkFeatureBand);
updateCommand.UpdateWorkloads();
- packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].manifestUpdate.ManifestId);
- packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].manifestUpdate.NewVersion);
- packInstaller.InstalledManifests[0].manifestUpdate.NewFeatureBand.Should().Be(manifestsToUpdate[0].manifestUpdate.NewFeatureBand);
- packInstaller.InstalledManifests[0].manifestUpdate.ExistingVersion.Should().Be(manifestsToUpdate[0].manifestUpdate.ExistingVersion);
- packInstaller.InstalledManifests[0].manifestUpdate.ExistingFeatureBand.Should().Be(manifestsToUpdate[0].manifestUpdate.ExistingFeatureBand);
+ packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId);
+ packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion);
+ packInstaller.InstalledManifests[0].manifestUpdate.NewFeatureBand.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewFeatureBand);
+ packInstaller.InstalledManifests[0].manifestUpdate.ExistingVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.ExistingVersion);
+ packInstaller.InstalledManifests[0].manifestUpdate.ExistingFeatureBand.Should().Be(manifestsToUpdate[0].ManifestUpdate.ExistingFeatureBand);
packInstaller.InstalledManifests[0].offlineCache.Should().Be(null);
- var defaultJsonPath = Path.Combine(dotnetPath, "dotnet", "metadata", "workloads", "6.0.300", "InstallState", "default.json");
+ var defaultJsonPath = Path.Combine(dotnetPath, "metadata", "workloads", "6.0.300", "InstallState", "default.json");
File.Exists(defaultJsonPath).Should().BeTrue();
var json = JsonDocument.Parse(new FileStream(defaultJsonPath, FileMode.Open, FileAccess.Read), new JsonDocumentOptions() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip });
json.RootElement.Should().NotBeNull();
json.RootElement.GetProperty("manifests").GetProperty("mock-manifest").GetString().Should().Be("2.0.0/" + newSdkFeatureBand);
}
- [Fact]
+ [Fact]
public void ApplyRollbackWithMultipleManifestsAcrossFeatureBand()
{
var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--from-rollback-file", "rollback.json" });
var manifestsToUpdate =
- new (ManifestVersionUpdate manifestUpdate,
- Dictionary Workloads)[]
+ new ManifestUpdateWithWorkloads[]
{
- (new ManifestVersionUpdate(new ManifestId("mock-manifest-1"), new ManifestVersion("1.0.0"), "6.0.300", new ManifestVersion("2.0.0"), "6.0.100"),
- null),
- (new ManifestVersionUpdate(new ManifestId("mock-manifest-2"), new ManifestVersion("1.0.0"), "6.0.100", new ManifestVersion("2.0.0"), "6.0.300"),
- null),
- (new ManifestVersionUpdate(new ManifestId("mock-manifest-3"), new ManifestVersion("1.0.0"), "5.0.100", new ManifestVersion("2.0.0"), "6.0.100"),
- null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest-1"), new ManifestVersion("1.0.0"), "6.0.300", new ManifestVersion("2.0.0"), "6.0.100"), null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest-2"), new ManifestVersion("1.0.0"), "6.0.100", new ManifestVersion("2.0.0"), "6.0.300"), null),
+ new(new ManifestVersionUpdate(new ManifestId("mock-manifest-3"), new ManifestVersion("1.0.0"), "5.0.100", new ManifestVersion("2.0.0"), "6.0.100"), null),
};
-
- (_, var updateCommand, var packInstaller, var workloadResolver, var workloadManifestUpdater, var nuGetPackageDownloader) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", installedFeatureBand: "6.0.300");
+ (_, var updateCommand, var packInstaller, _, _, _) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", installedFeatureBand: "6.0.300");
updateCommand.UpdateWorkloads();
- packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].manifestUpdate.ManifestId);
- packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].manifestUpdate.NewVersion);
+ packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId);
+ packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion);
packInstaller.InstalledManifests[0].manifestUpdate.NewFeatureBand.Should().Be("6.0.100");
packInstaller.InstalledManifests[1].manifestUpdate.NewFeatureBand.Should().Be("6.0.300");
packInstaller.InstalledManifests[2].manifestUpdate.NewFeatureBand.Should().Be("6.0.100");
@@ -418,7 +410,7 @@ public void GivenInvalidVersionInRollbackFileItErrors()
[CallerMemberName] string testName = "",
string failingWorkload = null,
string failingPack = null,
- IEnumerable<(ManifestVersionUpdate manifestUpdate, Dictionary Workloads)> manifestUpdates = null,
+ IEnumerable manifestUpdates = null,
IList installedWorkloads = null,
bool includeInstalledPacks = false,
string sdkVersion = "6.0.100",
@@ -433,8 +425,8 @@ public void GivenInvalidVersionInRollbackFileItErrors()
CreatePackInfo("Xamarin.Android.Framework", "8.2.0", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Framework", "8.2.0"), "Xamarin.Android.Framework")
};
var installer = includeInstalledPacks ?
- new MockPackWorkloadInstaller(failingWorkload, failingPack, installedWorkloads: installedWorkloads, installedPacks: installedPacks) :
- new MockPackWorkloadInstaller(failingWorkload, failingPack, installedWorkloads: installedWorkloads);
+ new MockPackWorkloadInstaller(dotnetRoot, failingWorkload, failingPack, installedWorkloads: installedWorkloads, installedPacks: installedPacks) :
+ new MockPackWorkloadInstaller(dotnetRoot, failingWorkload, failingPack, installedWorkloads: installedWorkloads);
var copiedManifestFolder = Path.Combine(dotnetRoot, "sdk-manifests", new SdkFeatureBand(sdkVersion).ToString(), "SampleManifest");
Directory.CreateDirectory(copiedManifestFolder);
@@ -444,7 +436,7 @@ public void GivenInvalidVersionInRollbackFileItErrors()
var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { copiedManifestFile }), dotnetRoot);
installer.WorkloadResolver = workloadResolver;
var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot);
- var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates, _manifestPath);
+ var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates);
var workloadResolverFactory = new MockWorkloadResolverFactory(dotnetRoot, sdkVersion, workloadResolver, userProfileDir: testDirectory);
@@ -456,7 +448,7 @@ public void GivenInvalidVersionInRollbackFileItErrors()
nugetPackageDownloader: nugetDownloader,
workloadManifestUpdater: manifestUpdater);
- return (testDirectory, installManager, installer, workloadResolver, manifestUpdater, nugetDownloader);
+ return (dotnetRoot, installManager, installer, workloadResolver, manifestUpdater, nugetDownloader);
}
}
}
diff --git a/src/Tests/dotnet.Tests/CommandTests/CompleteCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/CompleteCommandTests.cs
index b4b2160fb370..a895374c1802 100644
--- a/src/Tests/dotnet.Tests/CommandTests/CompleteCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/CompleteCommandTests.cs
@@ -39,6 +39,7 @@ public void GivenOnlyDotnetItSuggestsTopLevelCommandsAndOptions()
"new",
"nuget",
"pack",
+ "package",
"publish",
"remove",
"restore",
diff --git a/src/Tests/dotnet.Tests/CommandTests/ToolInstallGlobalOrToolPathCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/ToolInstallGlobalOrToolPathCommandTests.cs
index e0c4e247d4fb..0d15084cb1f3 100644
--- a/src/Tests/dotnet.Tests/CommandTests/ToolInstallGlobalOrToolPathCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/ToolInstallGlobalOrToolPathCommandTests.cs
@@ -1,21 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.CommandLine;
+using System.Text.Json;
+using Microsoft.DotNet.Cli.NuGetPackageDownloader;
+using Microsoft.DotNet.Cli.ToolPackage;
using Microsoft.DotNet.Cli.Utils;
-using Microsoft.DotNet.Tools;
+using Microsoft.DotNet.ShellShim;
using Microsoft.DotNet.ToolPackage;
-using Microsoft.DotNet.Tools.Tool.Install;
+using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Tests.ComponentMocks;
-using Microsoft.DotNet.ShellShim;
+using Microsoft.DotNet.Tools.Tool.Install;
+using Microsoft.DotNet.Tools.Tool.Update;
using Microsoft.Extensions.DependencyModel.Tests;
using Microsoft.Extensions.EnvironmentAbstractions;
+using CreateShellShimRepository = Microsoft.DotNet.Tools.Tool.Install.CreateShellShimRepository;
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings;
-using System.Text.Json;
-using System.CommandLine;
using Parser = Microsoft.DotNet.Cli.Parser;
-using Microsoft.DotNet.Cli.NuGetPackageDownloader;
-using Microsoft.NET.TestFramework;
-using Microsoft.DotNet.Cli.ToolPackage;
+
namespace Microsoft.DotNet.Tests.Commands.Tool
{
@@ -24,8 +26,10 @@ public class ToolInstallGlobalOrToolPathCommandTests: SdkTest
private readonly IFileSystem _fileSystem;
private readonly IToolPackageStore _toolPackageStore;
private readonly IToolPackageStoreQuery _toolPackageStoreQuery;
+ private readonly ToolPackageUninstallerMock _toolPackageUninstallerMock;
private readonly CreateShellShimRepository _createShellShimRepository;
- private readonly CreateToolPackageStoresAndDownloader _createToolPackageStoresAndDownloader;
+ private readonly CreateToolPackageStoresAndDownloaderAndUninstaller _createToolPackageStoreDownloaderUninstaller;
+ private readonly string _toolsDirectory;
private readonly EnvironmentPathInstructionMock _environmentPathInstructionMock;
private readonly ParseResult _parseResult;
private readonly BufferedReporter _reporter;
@@ -34,6 +38,8 @@ public class ToolInstallGlobalOrToolPathCommandTests: SdkTest
private readonly string _pathToPlacePackages;
private const string PackageId = "global.tool.console.demo";
private const string PackageVersion = "1.0.4";
+ private const string HigherPackageVersion = "2.0.0";
+ private const string LowerPackageVersion = "1.0.0";
private const string ToolCommandName = "SimulatorCommand";
private readonly string UnlistedPackageId = "elemental.sysinfotool";
@@ -42,6 +48,7 @@ public ToolInstallGlobalOrToolPathCommandTests(ITestOutputHelper log): base(log)
_reporter = new BufferedReporter();
_fileSystem = new FileSystemMockBuilder().UseCurrentSystemTemporaryDirectory().Build();
_temporaryDirectory = _fileSystem.Directory.CreateTemporaryDirectory().DirectoryPath;
+ _toolsDirectory = Path.Combine(_temporaryDirectory, "tools");
_pathToPlaceShim = Path.Combine(_temporaryDirectory, "pathToPlace");
_fileSystem.Directory.CreateDirectory(_pathToPlaceShim);
_pathToPlacePackages = _pathToPlaceShim + "Packages";
@@ -57,10 +64,14 @@ public ToolInstallGlobalOrToolPathCommandTests(ITestOutputHelper log): base(log)
filePermissionSetter: new NoOpFilePermissionSetter());
_environmentPathInstructionMock =
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim);
- _createToolPackageStoresAndDownloader = (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, CreateToolPackageDownloader());
+ var store = new ToolPackageStoreMock(
+ new DirectoryPath(_toolsDirectory),
+ _fileSystem);
+ _toolPackageUninstallerMock = new ToolPackageUninstallerMock(_fileSystem, store);
+ _createToolPackageStoreDownloaderUninstaller = (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, CreateToolPackageDownloader(), _toolPackageUninstallerMock);
- _parseResult = Parser.Instance.Parse($"dotnet tool install -g {PackageId}");
+ _parseResult = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --verbosity minimal");
}
[Fact]
@@ -68,7 +79,7 @@ public void WhenRunWithPackageIdItShouldCreateValidShim()
{
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -88,7 +99,7 @@ public void WhenRunFromToolInstallRedirectCommandWithPackageIdItShouldCreateVali
{
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -128,7 +139,7 @@ public void WhenRunWithPackageIdWithSourceItShouldCreateValidShim()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -149,7 +160,7 @@ public void WhenRunWithPackageIdItShouldShowPathInstruction()
{
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -175,7 +186,7 @@ public void WhenRunWithPackageIdPackageFormatIsNotFullySupportedItShouldShowPath
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -197,7 +208,7 @@ public void GivenFailedPackageInstallWhenRunWithPackageIdItShouldFail()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -220,7 +231,7 @@ public void GivenCreateShimItShouldHaveNoBrokenFolderOnDisk()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -244,7 +255,7 @@ public void GivenInCorrectToolConfigurationWhenRunWithPackageIdItShouldFail()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -265,7 +276,7 @@ public void WhenRunWithPackageIdItShouldShowSuccessMessage()
{
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
_parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -282,6 +293,29 @@ public void WhenRunWithPackageIdItShouldShowSuccessMessage()
PackageVersion).Green());
}
+ [Fact]
+ public void WhenRunWithPackageIdWithQuietItShouldShowNoSuccessMessage()
+ {
+ var parseResultQuiet = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --verbosity quiet");
+ var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+ parseResultQuiet,
+ _createToolPackageStoreDownloaderUninstaller,
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .NotContain(string.Format(
+ LocalizableStrings.InstallationSucceeded,
+ ToolCommandName,
+ PackageId,
+ PackageVersion).Green());
+ }
+
[Fact]
public void WhenRunWithInvalidVersionItShouldThrow()
{
@@ -290,7 +324,7 @@ public void WhenRunWithInvalidVersionItShouldThrow()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -307,11 +341,11 @@ public void WhenRunWithInvalidVersionItShouldThrow()
[Fact]
public void WhenRunWithExactVersionItShouldSucceed()
{
- ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion}");
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion} --verbosity minimal");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -328,14 +362,179 @@ public void WhenRunWithExactVersionItShouldSucceed()
PackageVersion).Green());
}
+ [Fact]
+ public void WhenInstallTheSameVersionTwiceItShouldSucceed()
+ {
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+ result,
+ _createToolPackageStoreDownloaderUninstaller,
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ LocalizableStrings.InstallationSucceeded,
+ ToolCommandName,
+ PackageId,
+ PackageVersion).Green());
+ _reporter.Clear();
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings.UpdateSucceededStableVersionNoChange,
+ PackageId,
+ PackageVersion).Green());
+ }
+
+ [Fact]
+ public void WhenInstallWithHigherVersionItShouldUpdate()
+ {
+ IToolPackageDownloader toolToolPackageDownloader = GetToolPackageDownloaderWithHigherVersionInFeed();
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+ result,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ LocalizableStrings.InstallationSucceeded,
+ ToolCommandName,
+ PackageId,
+ PackageVersion).Green());
+ _reporter.Clear();
+
+ ParseResult result2 = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {HigherPackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand2 = new ToolInstallGlobalOrToolPathCommand(
+ result2,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand2.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings.UpdateSucceeded,
+ PackageId,
+ PackageVersion,
+ HigherPackageVersion).Green());
+ }
+
+ [Fact]
+ public void WhenInstallWithLowerVersionWithAllowDowngradeOptionItShouldDowngrade()
+ {
+ IToolPackageDownloader toolToolPackageDownloader = GetToolPackageDownloaderWithLowerVersionInFeed();
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+ result,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ LocalizableStrings.InstallationSucceeded,
+ ToolCommandName,
+ PackageId,
+ PackageVersion).Green());
+ _reporter.Clear();
+
+ ParseResult result2 = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {LowerPackageVersion} --verbosity minimal --allow-downgrade");
+
+ var toolInstallGlobalOrToolPathCommand2 = new ToolInstallGlobalOrToolPathCommand(
+ result2,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand2.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings.UpdateSucceeded,
+ PackageId,
+ PackageVersion,
+ LowerPackageVersion).Green());
+ }
+
+ [Fact]
+ public void WhenInstallWithLowerVersionItShouldFail()
+ {
+ IToolPackageDownloader toolToolPackageDownloader = GetToolPackageDownloaderWithLowerVersionInFeed();
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {PackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
+ result,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ toolInstallGlobalOrToolPathCommand.Execute().Should().Be(0);
+
+ _reporter
+ .Lines
+ .Should()
+ .Equal(string.Format(
+ LocalizableStrings.InstallationSucceeded,
+ ToolCommandName,
+ PackageId,
+ PackageVersion).Green());
+ _reporter.Clear();
+
+ ParseResult result2 = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version {LowerPackageVersion} --verbosity minimal");
+
+ var toolInstallGlobalOrToolPathCommand2 = new ToolInstallGlobalOrToolPathCommand(
+ result2,
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
+ _createShellShimRepository,
+ new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
+ _reporter);
+
+ Action a = () => toolInstallGlobalOrToolPathCommand2.Execute();
+ a.Should().Throw();
+ }
+
[Fact]
public void WhenRunWithValidVersionRangeItShouldSucceed()
{
- ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version [1.0,2.0]");
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version [1.0,2.0] --verbosity minimal");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -404,11 +603,11 @@ public void WhenRunWithPrereleaseItShouldSucceed()
{
IToolPackageDownloader toolToolPackageDownloader = GetToolToolPackageDownloaderWithPreviewInFeed();
- ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --prerelease");
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --prerelease --verbosity minimal");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -434,7 +633,7 @@ public void WhenRunWithPrereleaseAndPackageVersionItShouldThrow()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader),
+ (location, forwardArguments) => (_toolPackageStore, _toolPackageStoreQuery, toolToolPackageDownloader, _toolPackageUninstallerMock),
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -471,6 +670,62 @@ private IToolPackageDownloader GetToolToolPackageDownloaderWithPreviewInFeed()
return toolToolPackageDownloader;
}
+ private IToolPackageDownloader GetToolPackageDownloaderWithLowerVersionInFeed()
+ {
+ var toolToolPackageDownloader = CreateToolPackageDownloader(
+ feeds: new List
+ {
+ new MockFeed
+ {
+ Type = MockFeedType.ImplicitAdditionalFeed,
+ Packages = new List
+ {
+ new MockFeedPackage
+ {
+ PackageId = PackageId,
+ Version = "1.0.4",
+ ToolCommandName = "SimulatorCommand"
+ },
+ new MockFeedPackage
+ {
+ PackageId = PackageId,
+ Version = "1.0.0",
+ ToolCommandName = "SimulatorCommand"
+ }
+ }
+ }
+ });
+ return toolToolPackageDownloader;
+ }
+
+ private IToolPackageDownloader GetToolPackageDownloaderWithHigherVersionInFeed()
+ {
+ var toolToolPackageDownloader = CreateToolPackageDownloader(
+ feeds: new List
+ {
+ new MockFeed
+ {
+ Type = MockFeedType.ImplicitAdditionalFeed,
+ Packages = new List
+ {
+ new MockFeedPackage
+ {
+ PackageId = PackageId,
+ Version = "1.0.4",
+ ToolCommandName = "SimulatorCommand"
+ },
+ new MockFeedPackage
+ {
+ PackageId = PackageId,
+ Version = "2.0.0",
+ ToolCommandName = "SimulatorCommand"
+ }
+ }
+ }
+ });
+ return toolToolPackageDownloader;
+ }
+
[Fact]
public void WhenRunWithoutAMatchingRangeItShouldFail()
{
@@ -478,7 +733,7 @@ public void WhenRunWithoutAMatchingRangeItShouldFail()
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -496,11 +751,11 @@ public void WhenRunWithoutAMatchingRangeItShouldFail()
[Fact]
public void WhenRunWithValidVersionWildcardItShouldSucceed()
{
- ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version 1.0.*");
+ ParseResult result = Parser.Instance.Parse($"dotnet tool install -g {PackageId} --version 1.0.* --verbosity minimal");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim, true),
_reporter);
@@ -524,7 +779,7 @@ public void WhenRunWithPackageIdAndBinPathItShouldNoteHaveEnvironmentPathInstruc
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
result,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim),
_reporter);
@@ -555,7 +810,7 @@ public void AndPackagedShimIsProvidedWhenRunWithPackageIdItCreateShimUsingPackag
fileSystem: _fileSystem,
store: _toolPackageStore,
packagedShimsMap: packagedShimsMap,
- reporter: _reporter)),
+ reporter: _reporter), _toolPackageUninstallerMock),
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, _pathToPlaceShim),
_reporter);
@@ -573,7 +828,7 @@ public void WhenRunWithArchOptionItErrorsOnInvalidRids()
var parseResult = Parser.Instance.Parse($"dotnet tool install -g {PackageId} -a invalid");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter);
@@ -589,7 +844,7 @@ public void WhenRunWithArchOptionItDownloadsAppHostTemplate()
var parseResult = Parser.Instance.Parse($"dotnet tool install -g {PackageId} -a arm64");
var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand(
parseResult,
- _createToolPackageStoresAndDownloader,
+ _createToolPackageStoreDownloaderUninstaller,
_createShellShimRepository,
_environmentPathInstructionMock,
_reporter,
diff --git a/src/Tests/dotnet.Tests/CommandTests/ToolInstallLocalCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/ToolInstallLocalCommandTests.cs
index 898223f16c62..c4f0f2f9751f 100644
--- a/src/Tests/dotnet.Tests/CommandTests/ToolInstallLocalCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/ToolInstallLocalCommandTests.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.
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using FluentAssertions;
using System.CommandLine;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.ToolPackage;
@@ -16,12 +11,9 @@
using Microsoft.DotNet.Tools.Tool.Install;
using Microsoft.Extensions.DependencyModel.Tests;
using Microsoft.Extensions.EnvironmentAbstractions;
-using Xunit;
+using NuGet.Frameworks;
using NuGet.Versioning;
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings;
-using Microsoft.NET.TestFramework.Utilities;
-using System.CommandLine.Parsing;
-using NuGet.Frameworks;
using Parser = Microsoft.DotNet.Cli.Parser;
namespace Microsoft.DotNet.Tests.Commands.Tool
@@ -39,6 +31,7 @@ public class ToolInstallLocalCommandTests:SdkTest
private readonly string _manifestFilePath;
private readonly PackageId _packageIdA = new PackageId("local.tool.console.a");
private readonly NuGetVersion _packageVersionA;
+ private readonly NuGetVersion _packageNewVersionA;
private readonly ToolCommandName _toolCommandNameA = new ToolCommandName("a");
private readonly ToolManifestFinder _toolManifestFinder;
private readonly ToolManifestEditor _toolManifestEditor;
@@ -46,6 +39,7 @@ public class ToolInstallLocalCommandTests:SdkTest
public ToolInstallLocalCommandTests(ITestOutputHelper log):base(log)
{
_packageVersionA = NuGetVersion.Parse("1.0.4");
+ _packageNewVersionA = NuGetVersion.Parse("2.0.0");
_reporter = new BufferedReporter();
_fileSystem = new FileSystemMockBuilder().UseCurrentSystemTemporaryDirectory().Build();
diff --git a/src/Tests/dotnet.Tests/CommandTests/ToolUninstallGlobalOrToolPathCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/ToolUninstallGlobalOrToolPathCommandTests.cs
index 0f6842dc5751..59592ed8b5fb 100644
--- a/src/Tests/dotnet.Tests/CommandTests/ToolUninstallGlobalOrToolPathCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/ToolUninstallGlobalOrToolPathCommandTests.cs
@@ -56,7 +56,7 @@ public void GivenANonExistentPackageItErrors()
[Fact]
public void GivenAPackageItUninstalls()
{
- CreateInstallCommand($"-g {PackageId}").Execute().Should().Be(0);
+ CreateInstallCommand($"-g {PackageId} --verbosity minimal").Execute().Should().Be(0);
_reporter
.Lines
@@ -98,7 +98,7 @@ public void GivenAPackageItUninstalls()
[Fact]
public void GivenAPackageWhenCallFromUninstallRedirectCommandItUninstalls()
{
- CreateInstallCommand($"-g {PackageId}").Execute().Should().Be(0);
+ CreateInstallCommand($"-g {PackageId} --verbosity minimal").Execute().Should().Be(0);
_reporter
.Lines
@@ -168,7 +168,7 @@ var uninstallCommand
[Fact]
public void GivenAFailureToUninstallItLeavesItInstalled()
{
- CreateInstallCommand($"-g {PackageId}").Execute().Should().Be(0);
+ CreateInstallCommand($"-g {PackageId} --verbosity minimal").Execute().Should().Be(0);
_reporter
.Lines
@@ -235,10 +235,11 @@ private ToolInstallGlobalOrToolPathCommand CreateInstallCommand(string options)
fileSystem: _fileSystem,
_reporter
);
+ var toolPackageDownloaderMock = new ToolPackageUninstallerMock(_fileSystem, store);
return new ToolInstallGlobalOrToolPathCommand(
result,
- (location, forwardArguments) => (store, store, packageDownloaderMock),
+ (location, forwardArguments) => (store, store, packageDownloaderMock, toolPackageDownloaderMock),
(_, _) => new ShellShimRepository(
new DirectoryPath(_shimsDirectory),
string.Empty,
diff --git a/src/Tests/dotnet.Tests/CommandTests/ToolUpdateGlobalOrToolPathCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/ToolUpdateGlobalOrToolPathCommandTests.cs
index b5eb57b1ea78..9b1f7e3bdcd8 100644
--- a/src/Tests/dotnet.Tests/CommandTests/ToolUpdateGlobalOrToolPathCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/ToolUpdateGlobalOrToolPathCommandTests.cs
@@ -143,7 +143,7 @@ public void GivenAnExistedLowerversionInstallationWhenCallItCanPrintSuccessMessa
CreateInstallCommand($"-g {_packageId} --version {LowerPackageVersion}").Execute();
_reporter.Lines.Clear();
- var command = CreateUpdateCommand($"-g {_packageId}");
+ var command = CreateUpdateCommand($"-g {_packageId} --verbosity minimal");
command.Execute();
@@ -152,13 +152,43 @@ public void GivenAnExistedLowerversionInstallationWhenCallItCanPrintSuccessMessa
_packageId, LowerPackageVersion, HigherPackageVersion));
}
+ [Fact]
+ public void GivenAnExistedHigherversionInstallationWhenUpdateToLowerVersionItErrors()
+ {
+ CreateInstallCommand($"-g {_packageId} --version {HigherPackageVersion}").Execute();
+ _reporter.Lines.Clear();
+
+ var command = CreateUpdateCommand($"-g {_packageId} --version {LowerPackageVersion} --verbosity minimal");
+
+ Action a = () => command.Execute();
+
+ a.Should().Throw().And.Message
+ .Should().Contain(
+ string.Format(LocalizableStrings.UpdateToLowerVersion, LowerPackageVersion, HigherPackageVersion));
+ }
+
+ [Fact]
+ public void GivenAnExistedHigherversionInstallationWithDowngradeFlagWhenUpdateToLowerVersionItSucceeds()
+ {
+ CreateInstallCommand($"-g {_packageId} --version {HigherPackageVersion}").Execute();
+ _reporter.Lines.Clear();
+
+ var command = CreateUpdateCommand($"-g {_packageId} --version {LowerPackageVersion} --verbosity minimal --allow-downgrade");
+
+ command.Execute();
+
+ _reporter.Lines.First().Should().Contain(string.Format(
+ LocalizableStrings.UpdateSucceeded,
+ _packageId, HigherPackageVersion, LowerPackageVersion));
+ }
+
[Fact]
public void GivenAnExistedLowerversionInstallationWhenCallWithWildCardVersionItCanPrintSuccessMessage()
{
CreateInstallCommand($"-g {_packageId} --version {LowerPackageVersion}").Execute();
_reporter.Lines.Clear();
- var command = CreateUpdateCommand($"-g {_packageId} --version 1.0.5-*");
+ var command = CreateUpdateCommand($"-g {_packageId} --version 1.0.5-* --verbosity minimal");
command.Execute();
@@ -173,7 +203,7 @@ public void GivenAnExistedLowerversionInstallationWhenCallWithPrereleaseVersionI
CreateInstallCommand($"-g {_packageId} --version {LowerPackageVersion}").Execute();
_reporter.Lines.Clear();
- var command = CreateUpdateCommand($"-g {_packageId} --prerelease");
+ var command = CreateUpdateCommand($"-g {_packageId} --prerelease --verbosity minimal");
command.Execute();
@@ -208,7 +238,7 @@ public void GivenAnExistedSameVersionInstallationWhenCallItCanPrintSuccessMessag
CreateInstallCommand($"-g {_packageId} --version {HigherPackageVersion}").Execute();
_reporter.Lines.Clear();
- var command = CreateUpdateCommand($"-g {_packageId}");
+ var command = CreateUpdateCommand($"-g {_packageId} --verbosity minimal");
command.Execute();
@@ -223,7 +253,7 @@ public void GivenAnExistedSameVersionInstallationWhenCallWithPrereleaseItUsesAPr
CreateInstallCommand($"-g {_packageId} --version {HigherPreviewPackageVersion}").Execute();
_reporter.Lines.Clear();
- var command = CreateUpdateCommand($"-g {_packageId} --version {HigherPreviewPackageVersion}");
+ var command = CreateUpdateCommand($"-g {_packageId} --version {HigherPreviewPackageVersion} --verbosity minimal");
command.Execute();
@@ -333,6 +363,9 @@ string ExpectedCommandPath()
private ToolInstallGlobalOrToolPathCommand CreateInstallCommand(string options)
{
ParseResult result = Parser.Instance.Parse("dotnet tool install " + options);
+ var store = new ToolPackageStoreMock(
+ new DirectoryPath(_toolsDirectory),
+ _fileSystem);
return new ToolInstallGlobalOrToolPathCommand(
result,
@@ -341,7 +374,7 @@ private ToolInstallGlobalOrToolPathCommand CreateInstallCommand(string options)
fileSystem: _fileSystem,
_reporter,
_mockFeeds
- )),
+ ), new ToolPackageUninstallerMock(_fileSystem, store)),
(_, _) => GetMockedShellShimRepository(),
_environmentPathInstructionMock,
_reporter);
diff --git a/src/Tests/dotnet.Tests/CommandTests/ToolUpdateLocalCommandTests.cs b/src/Tests/dotnet.Tests/CommandTests/ToolUpdateLocalCommandTests.cs
index 78c4958e1c18..d6742c26f443 100644
--- a/src/Tests/dotnet.Tests/CommandTests/ToolUpdateLocalCommandTests.cs
+++ b/src/Tests/dotnet.Tests/CommandTests/ToolUpdateLocalCommandTests.cs
@@ -305,12 +305,35 @@ public void GivenFeedVersionIsLowerRunPackageIdItShouldThrow()
_reporter.Clear();
Action a = () => _defaultToolUpdateLocalCommand.Execute();
a.Should().Throw().And.Message.Should().Contain(string.Format(
- LocalizableStrings.UpdateLocaToolToLowerVersion,
+ LocalizableStrings.UpdateLocalToolToLowerVersion,
"0.9.0",
_packageOriginalVersionA.ToNormalizedString(),
_manifestFilePath));
}
+ [Fact]
+ public void GivenFeedVersionIsLowerWithDowngradeFlagRunPackageIdItShouldSucceeds()
+ {
+ _reporter.Clear();
+
+ ParseResult parseResult
+ = Parser.Instance.Parse(
+ $"dotnet tool update {_packageIdA.ToString()} --version 0.9.0 --allow-downgrade");
+
+ _toolRestoreCommand.Execute();
+ _mockFeed.Packages.Single().Version = "0.9.0";
+
+ ToolUpdateLocalCommand toolUpdateLocalCommand = new ToolUpdateLocalCommand(
+ parseResult,
+ _toolPackageDownloaderMock,
+ _toolManifestFinder,
+ _toolManifestEditor,
+ _localToolsResolverCache,
+ _reporter);
+
+ toolUpdateLocalCommand.Execute().Should().Be(0);
+ }
+
private void AssertUpdateSuccess(FilePath? manifestFile = null, NuGetVersion packageVersion = null)
{
packageVersion ??= _packageNewVersionA;
diff --git a/src/Tests/dotnet.Tests/GivenThatDotNetRunsCommands.cs b/src/Tests/dotnet.Tests/GivenThatDotNetRunsCommands.cs
index f7115379540a..27289c31db96 100644
--- a/src/Tests/dotnet.Tests/GivenThatDotNetRunsCommands.cs
+++ b/src/Tests/dotnet.Tests/GivenThatDotNetRunsCommands.cs
@@ -27,7 +27,8 @@ public void UnresolvedPlatformReferencesFailAsExpected()
.WithWorkingDirectory(testInstance.Path)
.Execute("crash")
.Should().Fail()
- .And.HaveStdErrContaining(string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-crash"));
+ .And.HaveStdErrContaining(LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage)
+ .And.HaveStdOutContaining(string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-crash"));
}
[Theory]
diff --git a/src/Tests/dotnet.Tests/PackagedCommandTests.cs b/src/Tests/dotnet.Tests/PackagedCommandTests.cs
index 96f4539b6915..771cad76eca0 100644
--- a/src/Tests/dotnet.Tests/PackagedCommandTests.cs
+++ b/src/Tests/dotnet.Tests/PackagedCommandTests.cs
@@ -157,7 +157,9 @@ public void ItShowsErrorWhenToolIsNotRestored()
.WithWorkingDirectory(testInstance.Path)
.Execute("nonexistingtool")
.Should().Fail()
- .And.HaveStdErrContaining(string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-nonexistingtool"));
+ .And.HaveStdErrContaining(LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage)
+ .And.HaveStdOutContaining(
+ string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-nonexistingtool"));
}
[Fact]
@@ -234,9 +236,10 @@ public void TestProjectDependencyIsNotAvailableThroughDriver()
.WithWorkingDirectory(testInstance.Path)
.Execute();
- result.StdErr.Should().Contain(string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-hello"));
-
- result.Should().Fail();
+ result.StdErr.Should().Contain(LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage);
+ result.StdOut.Should().Contain(string.Format(LocalizableStrings.NoExecutableFoundMatchingCommand, "dotnet-hello"));
+
+ result.Should().Fail();
}
private void SetGeneratedPackageName(FileInfo project, string packageName)
diff --git a/src/Tests/dotnet.Tests/ParserTests/ParseResultExtensionsTests.cs b/src/Tests/dotnet.Tests/ParserTests/ParseResultExtensionsTests.cs
index d768e5a31d62..be639fd0b2a6 100644
--- a/src/Tests/dotnet.Tests/ParserTests/ParseResultExtensionsTests.cs
+++ b/src/Tests/dotnet.Tests/ParserTests/ParseResultExtensionsTests.cs
@@ -33,7 +33,7 @@ public void RootSubCommandResultReturnsCorrectSubCommand(string input, string ex
[Theory]
[InlineData(new string[] { "dotnet", "build" }, new string[] { })]
[InlineData(new string[] { "build" }, new string[] { })]
- [InlineData(new string[] { "dotnet", "test", "-d" }, new string[] { })]
+ [InlineData(new string[] { "dotnet", "test", "-d" }, new string[] { "-d" })]
[InlineData(new string[] { "dotnet", "publish", "-o", "foo" }, new string[] { "-o", "foo" })]
[InlineData(new string[] { "publish", "-o", "foo" }, new string[] { "-o", "foo" })]
[InlineData(new string[] { "dotnet", "add", "package", "-h" }, new string[] { "package", "-h" })]
diff --git a/src/Tests/dotnet.Tests/dotnet-msbuild/GivenDotnetOsArchOptions.cs b/src/Tests/dotnet.Tests/dotnet-msbuild/GivenDotnetOsArchOptions.cs
index ed1ea7afefc3..658a5590ab25 100644
--- a/src/Tests/dotnet.Tests/dotnet-msbuild/GivenDotnetOsArchOptions.cs
+++ b/src/Tests/dotnet.Tests/dotnet-msbuild/GivenDotnetOsArchOptions.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 Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using BuildCommand = Microsoft.DotNet.Tools.Build.BuildCommand;
@@ -148,5 +149,53 @@ public void ArchOptionsAMD64toX64()
.StartWith($"{ExpectedPrefix} -restore -consoleloggerparameters:Summary -property:RuntimeIdentifier=os-x64");
});
}
+
+ [Fact]
+ public void ArchOptionIsResolvedFromRidUnderDifferentCulture()
+ {
+ CultureInfo currentCultureBefore = CultureInfo.CurrentCulture;
+ try
+ {
+ CultureInfo.CurrentCulture = new CultureInfo("th");
+ CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () =>
+ {
+ var msbuildPath = "";
+ var command = BuildCommand.FromArgs(new string[] { "--os", "os" }, msbuildPath);
+ var expectedArch = RuntimeInformation.ProcessArchitecture.Equals(Architecture.Arm64) ? "arm64" : Environment.Is64BitOperatingSystem ? "x64" : "x86";
+ command.GetArgumentsToMSBuild()
+ .Should()
+ .StartWith($"{ExpectedPrefix} -restore -consoleloggerparameters:Summary -property:RuntimeIdentifier=os-{expectedArch}");
+ });
+ }
+ finally { CultureInfo.CurrentCulture = currentCultureBefore; }
+ }
+
+ [Fact]
+ public void OsOptionIsResolvedFromRidUnderDifferentCulture()
+ {
+ CultureInfo currentCultureBefore = CultureInfo.CurrentCulture;
+ try
+ {
+ CultureInfo.CurrentCulture = new CultureInfo("th");
+ CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () =>
+ {
+ var msbuildPath = "";
+ var command = BuildCommand.FromArgs(new string[] { "--arch", "arch" }, msbuildPath);
+ var expectedOs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" :
+ RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" :
+ RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx" :
+ null;
+ if (expectedOs == null)
+ {
+ // Not a supported OS for running test
+ return;
+ }
+ command.GetArgumentsToMSBuild()
+ .Should()
+ .StartWith($"{ExpectedPrefix} -restore -consoleloggerparameters:Summary -property:RuntimeIdentifier={expectedOs}-arch");
+ });
+ }
+ finally { CultureInfo.CurrentCulture = currentCultureBefore;}
+ }
}
}
diff --git a/src/Tests/dotnet.Tests/dotnet.Tests.csproj b/src/Tests/dotnet.Tests/dotnet.Tests.csproj
index 9753c7b25f8d..0179861656c2 100644
--- a/src/Tests/dotnet.Tests/dotnet.Tests.csproj
+++ b/src/Tests/dotnet.Tests/dotnet.Tests.csproj
@@ -170,5 +170,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.props b/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.props
index ac284f1689d5..862cdafc5d89 100644
--- a/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.props
+++ b/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.props
@@ -15,7 +15,4 @@ Copyright (c) .NET Foundation. All rights reserved.
true
-
-
diff --git a/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.targets b/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.targets
index f6779faa84a6..be1190acfbf4 100644
--- a/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.targets
+++ b/src/WebSdk/Publish/Targets/Microsoft.NET.Sdk.Publish.targets
@@ -29,7 +29,6 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<_PublishTargetsDir Condition=" '$(_PublishTargetsDir)'=='' ">$(MSBuildThisFileDirectory)PublishTargets\
<_DotNetCLIToolTargetsDir Condition=" '$(_DotNetCLIToolTargetsDir)'=='' ">$(MSBuildThisFileDirectory)DotNetCLIToolTargets\
<_PublishProfilesDir Condition=" '$(_PublishProfilesDir)'=='' ">$(MSBuildThisFileDirectory)PublishProfiles\
- <_ContainersTargetsDir Condition=" '$(_ContainersTargetsDir)'=='' ">$(MSBuildThisFileDirectory)..\..\..\Containers\build\
-
-
-
-