Skip to content

Commit 5b0bacb

Browse files
authored
Merge pull request #170 from sfoslund/RemoveUpperBound
Add ability to uninstall 5.0
2 parents c2b8626 + ff7ccfc commit 5b0bacb

7 files changed

Lines changed: 147 additions & 222 deletions

File tree

src/dotnet-core-uninstall/Shared/Utils/Regexes.cs

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,17 @@ internal static class Regexes
2222
$@"(?<{MajorGroupName}>\d+)\.(?<{MinorGroupName}>\d+)");
2323
private static readonly Regex _previewVersionNumberRegex = new Regex(
2424
$@"(\d+(\.\d+)*)?");
25-
private static readonly Regex _previewVersionNumberRuntimeDisplayNameRegex = new Regex(
26-
$@"(\s\d+(\.\d+)?)?");
2725
private static readonly Regex _rcVersionNumberRegex = new Regex(
2826
$@"\d+");
2927
private static readonly Regex _buildNumberRegex = new Regex(
3028
$@"(?<{BuildGroupName}>\d+)");
3129
private static readonly Regex _archRegex = new Regex(
32-
$@"(?<{ArchGroupName}>x64|x86)");
30+
$@"(?<{ArchGroupName}>\-?x64|x86)");
3331

3432
private static readonly Regex _previewVersionSdkDisplayNameRegex = new Regex(
35-
$@"(?<{PreviewGroupName}>\s\-\s(preview{_previewVersionNumberRegex.ToString()}|rc{_rcVersionNumberRegex.ToString()}))");
36-
private static readonly Regex _previewVersionRuntimeDisplayNameRegex = new Regex(
37-
$@"(?<{PreviewGroupName}>\s(Preview{_previewVersionNumberRuntimeDisplayNameRegex.ToString()}|Release\sCandidate\s{_rcVersionNumberRegex.ToString()}))");
38-
private static readonly Regex _previewVersionAspNetRuntimeDisplayNameRegex = new Regex(
39-
$@"(?<{PreviewGroupName}>\s(Preview{_previewVersionNumberRuntimeDisplayNameRegex.ToString()}(\sBuild\s{_buildNumberRegex.ToString()}(\.\d+|\-\d+)?)?|Release\sCandidate\s{_rcVersionNumberRegex.ToString()}))");
40-
private static readonly Regex _previewVersionHostingBundleDisplayNameRegex = new Regex(
41-
$@"(?<{PreviewGroupName}>\s(Preview{_previewVersionNumberRuntimeDisplayNameRegex.ToString()}(\s(Build\s{_buildNumberRegex.ToString()}(\.\d+|\-\d+)?|Build\spreview{_previewVersionNumberRegex.ToString()}|{_buildNumberRegex.ToString()}(\s{_buildNumberRegex.ToString()})?(\spreview{_previewVersionNumberRegex.ToString()})?))?|Release\sCandidate\s{_rcVersionNumberRegex.ToString()}))");
33+
$@"(?<{PreviewGroupName}>\s?\-\s?((preview|alpha)\.?{_previewVersionNumberRegex.ToString()}|rc{_rcVersionNumberRegex.ToString()}))");
4234
private static readonly Regex _previewVersionSdkCachePathRegex = new Regex(
43-
$@"(?<{PreviewGroupName}>\-(preview{_previewVersionNumberRegex.ToString()}|rc{_rcVersionNumberRegex.ToString()}(\.\d+)?)\-(?<{BuildGroupName}>\d+))");
35+
$@"(?<{PreviewGroupName}>\-((preview|alpha)\.?{_previewVersionNumberRegex.ToString()}|rc{_rcVersionNumberRegex.ToString()}(\.\d+)?)\-(?<{BuildGroupName}>\d+))");
4436
private static readonly Regex _previewVersionRuntimeCachePathRegex = new Regex(
4537
$@"(?<{PreviewGroupName}>\-(preview{_previewVersionNumberRegex.ToString()}\-{_buildNumberRegex.ToString()}\-\d+|rc{_rcVersionNumberRegex.ToString()}))");
4638
private static readonly Regex _previewVersionAspNetRuntimeCachePathRegex = new Regex(
@@ -56,29 +48,6 @@ internal static class Regexes
5648
$@"(?<{VersionGroupName}>{_notCapturedRuntimeVersionBasicRegexFormat})";
5749
private static readonly string _runtimeAuxVersionBasicRegexFormat =
5850
$@"(?<{AuxVersionGroupName}>{_notCapturedRuntimeVersionBasicRegexFormat})";
59-
private static readonly Regex _sdkVersionDisplayNameRegex = new Regex(string.Format(
60-
_sdkVersionBasicRegexFormat,
61-
_previewVersionSdkDisplayNameRegex.ToString()));
62-
private static readonly Regex _runtimeVersionDisplayNameRegex = new Regex(string.Format(
63-
_runtimeVersionBasicRegexFormat,
64-
_previewVersionRuntimeDisplayNameRegex.ToString()));
65-
private static readonly Regex _aspNetRuntimeVersionDisplayNameRegex = new Regex(string.Format(
66-
_runtimeVersionBasicRegexFormat,
67-
_previewVersionAspNetRuntimeDisplayNameRegex.ToString()));
68-
private static readonly Regex _hostingBundleVersionDisplayNameRegex = new Regex(string.Format(
69-
_runtimeVersionBasicRegexFormat,
70-
_previewVersionHostingBundleDisplayNameRegex.ToString()));
71-
private static readonly Regex _hostingBundleAuxVersionDisplayNameRegex = new Regex(string.Format(
72-
_runtimeAuxVersionBasicRegexFormat,
73-
_previewVersionHostingBundleDisplayNameRegex.ToString()));
74-
private static readonly Regex _sdkDisplayNameRegex = new Regex(
75-
$@"(\.NET\sCore\s(?<{TypeGroupName}>SDK)\s{_sdkVersionDisplayNameRegex.ToString()}|Microsoft\s\.NET\sCore\s(?<{TypeGroupName}>SDK)\s(\-\s)?{_sdkVersionDisplayNameRegex.ToString()})\s\({_archRegex.ToString()}\)");
76-
private static readonly Regex _runtimeDisplayNameRegex = new Regex(
77-
$@"(Microsoft\s\.NET\sCore\s(?<{TypeGroupName}>Runtime)\s\-\s{_runtimeVersionDisplayNameRegex.ToString()}|Microsoft\s\.NET\sCore\s{_runtimeVersionDisplayNameRegex.ToString()}\s\-\s(?<{TypeGroupName}>Runtime))\s\({_archRegex.ToString()}\)");
78-
private static readonly Regex _aspNetRuntimeDisplayNameRegex = new Regex(
79-
$@"Microsoft\s(?<{TypeGroupName}>ASP\.NET)\sCore\s{_aspNetRuntimeVersionDisplayNameRegex.ToString()}\s\-\s(Shared\sFramework|Runtime\sPackage\sStore)");
80-
private static readonly Regex _hostingBundleDisplayNameRegex = new Regex(
81-
$@"Microsoft\s\.NET\sCore\s(({_hostingBundleAuxVersionDisplayNameRegex.ToString()}\s\&\s)?{_hostingBundleVersionDisplayNameRegex.ToString()}|{_previewVersionHostingBundleDisplayNameRegex.ToString()})\s\-\s(?<{TypeGroupName}>Windows\sServer\sHosting)(\s\-\s{_buildNumberRegex.ToString()})?");
8251
private static readonly Regex _sdkVersionCachePathRegex = new Regex(string.Format(
8352
_sdkVersionBasicRegexFormat,
8453
_previewVersionSdkCachePathRegex.ToString()));
@@ -105,12 +74,11 @@ internal static class Regexes
10574
private static readonly Regex _hostingBundleCachePathRegex = new Regex(
10675
$@"\\DotNetCore\.({_hostingBundleAuxVersionCachePathRegex.ToString()}_)?{_hostingBundleVersionCachePathRegex.ToString()}\-(?<{TypeGroupName}>WindowsHosting)\.exe|\\dotnetcore\.{_hostingBundleAuxVersionCachePathRegex.ToString()}_{_hostingBundleVersionCachePathRegex.ToString()}\-(?<{TypeGroupName}>windowshosting)\.exe|\\dotnet\-(?<{TypeGroupName}>hosting)\-{_hostingBundleVersionCachePathRegex.ToString()}\-win\.exe");
10776

108-
public static readonly Regex BundlePublisherRegex = new Regex(
109-
@"^Microsoft\sCorporation$");
77+
public static readonly Regex VersionDisplayNameRegex = new Regex(string.Format(
78+
_sdkVersionBasicRegexFormat,
79+
_previewVersionSdkDisplayNameRegex.ToString()));
11080
public static readonly Regex BundleMajorMinorRegex = new Regex(
11181
$@"^{_majorMinorRegex.ToString()}$");
112-
public static readonly Regex BundleDisplayNameRegex = new Regex(
113-
$@"^({_sdkDisplayNameRegex.ToString()}|{_runtimeDisplayNameRegex.ToString()}|{_aspNetRuntimeDisplayNameRegex.ToString()}|{_hostingBundleDisplayNameRegex.ToString()})$");
11482
public static readonly Regex BundleCachePathRegex = new Regex(
11583
$@"({_sdkCachePathRegex.ToString()}|{_runtimeCachePathRegex.ToString()}|{_aspNetRuntimeCachePathRegex.ToString()}|{_aspNetSharedFrameworkCachePathRegex.ToString()}|{_hostingBundleCachePathRegex.ToString()})$");
11684
}

src/dotnet-core-uninstall/Shared/VSVersioning/VisualStudioSafeVersionsExtractor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Microsoft.DotNet.Tools.Uninstall.Shared.VSVersioning
1313
internal static class VisualStudioSafeVersionsExtractor
1414
{
1515
// The tool should not be used to uninstall any more recent versions of the sdk
16-
public static readonly SemanticVersion UpperLimit = new SemanticVersion(5, 0, 0);
16+
public static readonly SemanticVersion UpperLimit = new SemanticVersion(6, 0, 0);
1717

1818
// Must keep one of each of these divisions to ensure Visual Studio works.
1919
// Pairs are [inclusive, exclusive)

src/dotnet-core-uninstall/Windows/RegistryQuery.cs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using System.Runtime.InteropServices;
8+
using System.Text.RegularExpressions;
89
using Microsoft.DotNet.Tools.Uninstall.MacOs;
910
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
1011
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
@@ -42,7 +43,7 @@ public virtual IEnumerable<Bundle> GetAllInstalledBundles()
4243

4344
var bundles = names
4445
.Select(name => uninstalls.OpenSubKey(name))
45-
.Where(bundle => IsDotNetCoreBundle(bundle));
46+
.Where(bundle => IsNetCoreBundle(bundle));
4647

4748
var wrappedBundles = bundles
4849
.Select(bundle => WrapRegistryKey(bundle))
@@ -52,36 +53,34 @@ public virtual IEnumerable<Bundle> GetAllInstalledBundles()
5253
return wrappedBundles;
5354
}
5455

55-
private static bool IsDotNetCoreBundle(RegistryKey registryKey)
56+
private static bool IsNetCoreBundle(RegistryKey uninstallKey)
5657
{
57-
return IsDotNetCoreBundleDisplayName(registryKey.GetValue("DisplayName") as string)
58-
&& IsDotNetCoreBundlePublisher(registryKey.GetValue("Publisher") as string)
59-
&& IsDotNetCoreBundleUninstaller(registryKey.GetValue("WindowsInstaller") as int?)
60-
&& IsNotVisualStudioDummyVersion(registryKey.GetValue("DisplayName") as string);
61-
}
62-
63-
private static bool IsNotVisualStudioDummyVersion(string displayName)
64-
{
65-
return !displayName.Contains(" from Visual Studio");
66-
}
67-
68-
private static bool IsDotNetCoreBundleDisplayName(string displayName)
69-
{
70-
return displayName == null ?
71-
false :
72-
Regexes.BundleDisplayNameRegex.IsMatch(displayName);
73-
}
58+
if (uninstallKey == null)
59+
{
60+
return false;
61+
}
7462

75-
private static bool IsDotNetCoreBundlePublisher(string publisher)
76-
{
77-
return publisher == null ?
78-
false :
79-
Regexes.BundlePublisherRegex.IsMatch(publisher);
63+
return IsNetCoreBundle(uninstallKey.GetValue("DisplayName") as string,
64+
uninstallKey.GetValue("DisplayVersion") as string,
65+
uninstallKey.GetValue("UninstallString") as string,
66+
uninstallKey.GetValue("BundleVersion") as string);
8067
}
8168

82-
private static bool IsDotNetCoreBundleUninstaller(int? windowsInstaller)
69+
internal static bool IsNetCoreBundle(string displayName, string displayVersion, string uninstallString, string bundleVersion)
8370
{
84-
return windowsInstaller == null;
71+
return (!String.IsNullOrEmpty(displayName)) &&
72+
(displayName.IndexOf("Visual Studio", StringComparison.OrdinalIgnoreCase) < 0) &&
73+
(displayName.IndexOf("VS 2015", StringComparison.OrdinalIgnoreCase) < 0) &&
74+
(displayName.IndexOf("Local Feed", StringComparison.OrdinalIgnoreCase) < 0) &&
75+
((displayName.IndexOf(".NET Core", StringComparison.OrdinalIgnoreCase) >= 0) ||
76+
(displayName.IndexOf(".NET Runtime", StringComparison.OrdinalIgnoreCase) >= 0) ||
77+
(displayName.IndexOf(".NET SDK", StringComparison.OrdinalIgnoreCase) >= 0) ||
78+
(displayName.IndexOf("Dotnet Shared Framework for Windows Desktop", StringComparison.OrdinalIgnoreCase) >= 0)) &&
79+
(!String.IsNullOrEmpty(uninstallString)) &&
80+
(uninstallString.IndexOf(".exe", StringComparison.OrdinalIgnoreCase) >= 0) &&
81+
(uninstallString.IndexOf("msiexec", StringComparison.OrdinalIgnoreCase) < 0) &&
82+
(!String.IsNullOrEmpty(displayVersion)) &&
83+
(!String.IsNullOrEmpty(bundleVersion));
8584
}
8685

8786
private static Bundle WrapRegistryKey(RegistryKey registryKey)
@@ -90,7 +89,8 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
9089
var uninstallCommand = registryKey.GetValue("QuietUninstallString") as string;
9190
var bundleCachePath = registryKey.GetValue("BundleCachePath") as string;
9291

93-
ParseVersionAndArch(registryKey, displayName, bundleCachePath, out var version, out var arch);
92+
var version = GetBundleVersion(displayName, uninstallCommand, bundleCachePath);
93+
var arch = GetBundleArch(displayName, bundleCachePath);
9494

9595
if (version == null)
9696
{
@@ -100,41 +100,59 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
100100
return Bundle.From(version, arch, uninstallCommand, displayName);
101101
}
102102

103-
private static void ParseVersionAndArch(RegistryKey registryKey, string displayName, string bundleCachePath, out BundleVersion version, out BundleArch arch)
103+
private static BundleVersion GetBundleVersion(string displayName, string uninstallString, string bundleCachePath)
104104
{
105-
var match = Regexes.BundleDisplayNameRegex.Match(displayName);
105+
var versionString = Regexes.VersionDisplayNameRegex.Match(displayName)?.Value ?? string.Empty;
106106
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
107-
var archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value ?? string.Empty;
108-
var versionFromCachePath = cachePathMatch.Groups[Regexes.VersionGroupName].Value;
109-
// Note: ASP.NET Core runtimes do not include version in the cache path, need to get version from registry:
110-
var versionFromRegistry = string.Join('.', (registryKey.GetValue("DisplayVersion") as string).Split('.').Take(3));
111-
var versionString = string.IsNullOrEmpty(versionFromCachePath) ? versionFromRegistry : versionFromCachePath;
112107
var hasAuxVersion = cachePathMatch.Groups[Regexes.AuxVersionGroupName].Success;
113108
var footnote = hasAuxVersion ?
114109
string.Format(LocalizableStrings.HostingBundleFootnoteFormat, displayName, versionString) :
115110
null;
116111

117-
if (string.IsNullOrEmpty(displayName) || string.IsNullOrEmpty(versionString))
112+
// Classify the bundle type
113+
if (displayName.IndexOf("Windows Server", StringComparison.OrdinalIgnoreCase) >= 0)
118114
{
119-
version = null;
120-
arch = BundleArch.X64 | BundleArch.X86;
121-
return;
115+
return new HostingBundleVersion(versionString, footnote);
122116
}
117+
else if (displayName.IndexOf("ASP.NET", StringComparison.OrdinalIgnoreCase) >= 0)
118+
{
119+
return new AspNetRuntimeVersion(versionString);
120+
}
121+
else if ((displayName.IndexOf(".NET Core SDK", StringComparison.OrdinalIgnoreCase) >= 0) ||
122+
(displayName.IndexOf("Microsoft .NET SDK", StringComparison.OrdinalIgnoreCase) >= 0) ||
123+
uninstallString.IndexOf("dotnet-dev-win") >= 0)
124+
{
125+
return new SdkVersion(versionString);
126+
}
127+
else if (displayName.IndexOf(".NET Core Runtime", StringComparison.OrdinalIgnoreCase) >= 0 || Regex.IsMatch(displayName, @".*\.NET Core.*Runtime") ||
128+
displayName.IndexOf(".NET Runtime", StringComparison.OrdinalIgnoreCase) >= 0)
129+
{
130+
return new RuntimeVersion(versionString);
131+
}
132+
else {
133+
return null;
134+
}
135+
}
123136

124-
switch (match.Groups[Regexes.TypeGroupName].Value)
137+
private static BundleArch GetBundleArch(string displayName, string bundleCachePath)
138+
{
139+
const string x64String = "x64";
140+
const string x86String = "x86";
141+
142+
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
143+
144+
var archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value;
145+
146+
if (string.IsNullOrEmpty(archString))
125147
{
126-
case "SDK": version = new SdkVersion(versionString); break;
127-
case "Runtime": version = new RuntimeVersion(versionString); break;
128-
case "ASP.NET": version = new AspNetRuntimeVersion(versionString); break;
129-
case "Windows Server Hosting": version = new HostingBundleVersion(versionString, footnote); break;
130-
default: throw new ArgumentException();
148+
archString = displayName.Contains(x64String) ? x64String : displayName.Contains(x86String) ? x86String : string.Empty;
131149
}
132150

133151
switch (archString)
134152
{
135-
case "x64": arch = BundleArch.X64; break;
136-
case "x86": arch = BundleArch.X86; break;
137-
case "": arch = BundleArch.X64 | BundleArch.X86; break;
153+
case x64String: return BundleArch.X64;
154+
case x86String: return BundleArch.X86;
155+
case "": return BundleArch.X64 | BundleArch.X86;
138156
default: throw new ArgumentException();
139157
}
140158
}

test/dotnet-core-uninstall.Tests/Shared/Commands/CommandBundleFilterTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.DotNet.Tools.Uninstall.Tests.Shared.Commands
1919
{
2020
public class CommandBundleFilterTests
2121
{
22-
private static readonly string[] versions = { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100", "2.2.200", "5.0.0", "5.0.1", "10.10.10" };
22+
private static readonly string[] versions = { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100", "2.2.200", "5.0.0", "7.0.1", "10.10.10" };
2323
private Dictionary<string, BundleArch> versionsWithArch = new Dictionary<string, BundleArch>
2424
{
2525
{ "3.0.0", BundleArch.X64 },
@@ -70,7 +70,7 @@ internal void TestRequiredUninstallableWithOptionsWindows(string command, string
7070
}
7171

7272
[MacOsOnlyTheory]
73-
[InlineData("remove --all-below 5.0.0 --sdk", new string[] { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100" }, new string[] { })]
73+
[InlineData("remove --all-below 5.0.0 --sdk", new string[] { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100", "2.2.200" }, new string[] { })]
7474
[InlineData("remove --all-below 5.0.0 --sdk --force", new string[] { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100", "2.2.200" }, new string[] { })]
7575
[InlineData("remove --all-below 5.0.0 --runtime", new string[] { }, new string[] { "1.0.0", "2.1.0", "2.1.500", "2.2.100" })]
7676
[InlineData("remove --all-below 5.0.0 --runtime --force", new string[] { }, new string[] { "1.0.0", "1.0.1", "1.1.0", "2.1.0", "2.1.500", "2.1.600", "2.2.100", "2.2.200" })]
@@ -122,10 +122,10 @@ internal void TestRequiredUninstallableWhenExplicitlyAdded(IEnumerable<Bundle> b
122122
}
123123

124124
[Theory]
125-
[InlineData("remove {0} 5.0.0")]
125+
[InlineData("remove {0} 7.0.1")]
126126
[InlineData("remove {0} 10.10.10")]
127127
[InlineData("remove {0} --all --force")]
128-
[InlineData("remove {0} 1.0.0 1.0.1 1.1.0 2.1.0 2.1.500 2.1.600 2.2.100 2.2.200 5.0.0 5.0.1 10.10.10")]
128+
[InlineData("remove {0} 1.0.0 1.0.1 1.1.0 2.1.0 2.1.500 2.1.600 2.2.100 2.2.200 5.0.0 7.0.1 10.10.10")]
129129
internal void TestUpperLimitAlwaysRequired(string command)
130130
{
131131
var sdkBundles = new List<Bundle<SdkVersion>>();

0 commit comments

Comments
 (0)