Skip to content

Commit 6ed87f8

Browse files
committed
Block debugger installation if dotnet cli is too old
This does a version check at the time we verify dotnet is on the path by running dotnet --info. We use semver to compare the cli version to a hard coded minimum cli version. If the check fails we use the same UI we already had for "dotnet not on path" with a different message.
1 parent 2b9b110 commit 6ed87f8

File tree

2 files changed

+98
-59
lines changed

2 files changed

+98
-59
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "csharp",
33
"publisher": "ms-vscode",
4-
"version": "1.4.0-beta7",
4+
"version": "1.4.0-beta8",
55
"description": "C# for Visual Studio Code (powered by OmniSharp).",
66
"displayName": "C#",
77
"author": "Microsoft Corporation",

src/coreclr-debug/activate.ts

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,21 @@ import TelemetryReporter from 'vscode-extension-telemetry';
1212
import { CoreClrDebugUtil } from './util';
1313
import * as debugInstall from './install';
1414
import { Platform, getCurrentPlatform } from './../platform';
15+
import * as semver from 'semver';
16+
17+
const MINIMUM_SUPPORTED_DOTNET_CLI: string = '1.0.0-preview2-003121';
1518

1619
let _reporter: TelemetryReporter = null;
1720
let _channel: vscode.OutputChannel = null;
1821
let _util: CoreClrDebugUtil = null;
1922

23+
class DotnetInfo
24+
{
25+
public Version: string;
26+
public OsVersion: string;
27+
public RuntimeId: string;
28+
}
29+
2030
export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter) {
2131
_reporter = reporter;
2232
_channel = vscode.window.createOutputChannel('coreclr-debug');
@@ -27,22 +37,7 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
2737
return;
2838
}
2939

30-
let dotnetVersion: string = '';
31-
let osVersion: string = '';
32-
let osRID: string = '';
33-
_util.spawnChildProcess('dotnet', ['--info'], _util.coreClrDebugDir(), (data: Buffer) => {
34-
var lines: string[] = data.toString().replace(/\r/mg, '').split('\n');
35-
lines.forEach(line => {
36-
let match: RegExpMatchArray;
37-
if (match = /^\ Version:\s*([^\s].*)$/.exec(line)) {
38-
dotnetVersion = match[1];
39-
} else if (match = /^\ OS Version:\s*([^\s].*)$/.exec(line)) {
40-
osVersion = match[1];
41-
} else if (match = /^\ RID:\s*([\w\-\.]+)$/.exec(line)) {
42-
osRID = match[1];
43-
}
44-
});
45-
}).then(() => {
40+
checkForDotnetTools().then((dotnetInfo: DotnetInfo) => {
4641
let installer = new debugInstall.DebugInstaller(_util);
4742
_util.createInstallLog();
4843

@@ -61,54 +56,98 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
6156
statusBarMessage.dispose();
6257
vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.');
6358
}).catch((error: debugInstall.InstallError) => {
64-
const viewLogMessage = "View Log";
65-
vscode.window.showErrorMessage('Error while installing .NET Core Debugger.', viewLogMessage).then(value => {
66-
if (value === viewLogMessage) {
67-
_channel.show(vscode.ViewColumn.Three);
68-
}
69-
});
70-
statusBarMessage.dispose();
71-
72-
installStage = error.installStage;
73-
installError = error.errorMessage;
74-
moreErrors = error.hasMoreErrors ? 'true' : 'false';
75-
}).then(() => {
76-
// log telemetry and delete install begin file
77-
logTelemetry('Acquisition', {
78-
installStage: installStage,
79-
installError: installError,
80-
moreErrors: moreErrors,
81-
dotnetVersion: dotnetVersion,
82-
osVersion: osVersion,
83-
osRID: osRID
84-
});
85-
try {
86-
deleteInstallBeginFile();
87-
} catch (err) {
88-
// if this throws there's really nothing we can do
59+
const viewLogMessage = "View Log";
60+
vscode.window.showErrorMessage('Error while installing .NET Core Debugger.', viewLogMessage).then(value => {
61+
if (value === viewLogMessage) {
62+
_channel.show(vscode.ViewColumn.Three);
8963
}
90-
_util.closeInstallLog();
9164
});
92-
}).catch(() => {
93-
const config = vscode.workspace.getConfiguration('csharp');
94-
if (!config.get('suppressDotnetInstallWarning', false)) {
95-
const getDotNetMessage = 'Get .NET CLI tools';
96-
const goToSettingsMessage = 'Disable this message in user settings';
97-
// Buttons are shown in right-to-left order, with a close button to the right of everything;
98-
// getDotNetMessage will be the first button, then goToSettingsMessage, then the close button.
99-
vscode.window.showErrorMessage('The .NET CLI tools cannot be located. .NET Core debugging will not be enabled. Make sure .NET CLI tools are installed and are on the path.',
100-
goToSettingsMessage, getDotNetMessage).then(value => {
101-
if (value === getDotNetMessage) {
102-
let open = require('open');
103-
open('https://www.microsoft.com/net/core');
104-
} else if (value === goToSettingsMessage) {
105-
vscode.commands.executeCommand('workbench.action.openGlobalSettings');
106-
}
107-
});
65+
statusBarMessage.dispose();
66+
67+
installStage = error.installStage;
68+
installError = error.errorMessage;
69+
moreErrors = error.hasMoreErrors ? 'true' : 'false';
70+
}).then(() => {
71+
// log telemetry and delete install begin file
72+
logTelemetry('Acquisition', {
73+
installStage: installStage,
74+
installError: installError,
75+
moreErrors: moreErrors,
76+
dotnetVersion: dotnetInfo.Version,
77+
osVersion: dotnetInfo.OsVersion,
78+
osRID: dotnetInfo.RuntimeId
79+
});
80+
try {
81+
deleteInstallBeginFile();
82+
} catch (err) {
83+
// if this throws there's really nothing we can do
84+
}
85+
_util.closeInstallLog();
86+
});
87+
}).catch((error) => {
88+
// log errors from checkForDotnetTools
89+
_util.log(error.message);
90+
});
91+
}
92+
93+
// This function checks for the presence of dotnet on the path and ensures the Version
94+
// is new enough for us. Any error UI that needs to be displayed is handled by this function.
95+
// Returns: a promise that returns a DotnetInfo class
96+
// Throws: An Error() from the return promise if either dotnet does not exist or is too old.
97+
function checkForDotnetTools() : Promise<DotnetInfo>
98+
{
99+
let dotnetInfo = new DotnetInfo();
100+
101+
return _util.spawnChildProcess('dotnet', ['--info'], _util.coreClrDebugDir(), (data: Buffer) => {
102+
var lines: string[] = data.toString().replace(/\r/mg, '').split('\n');
103+
lines.forEach(line => {
104+
let match: RegExpMatchArray;
105+
if (match = /^\ Version:\s*([^\s].*)$/.exec(line)) {
106+
dotnetInfo.Version = match[1];
107+
} else if (match = /^\ OS Version:\s*([^\s].*)$/.exec(line)) {
108+
dotnetInfo.OsVersion = match[1];
109+
} else if (match = /^\ RID:\s*([\w\-\.]+)$/.exec(line)) {
110+
dotnetInfo.RuntimeId = match[1];
111+
}
112+
});
113+
}).catch((error) => {
114+
// something went wrong with spawning 'dotnet --info'
115+
let message = 'The .NET CLI tools cannot be located. .NET Core debugging will not be enabled. Make sure .NET CLI tools are installed and are on the path.';
116+
showDotnetToolsWarning(message);
117+
throw new Error("Failed to spawn 'dotnet --info'");
118+
}).then(() => {
119+
// succesfully spawned 'dotnet --info', check the Version
120+
if (semver.lt(dotnetInfo.Version, MINIMUM_SUPPORTED_DOTNET_CLI))
121+
{
122+
let message = 'The .NET CLI tools on the path are too old. .NET Core debugging will not be enabled. The minimum supported version is ' + MINIMUM_SUPPORTED_DOTNET_CLI + '.';
123+
showDotnetToolsWarning(message);
124+
throw new Error("dotnet cli is too old");
108125
}
126+
127+
return dotnetInfo;
109128
});
110129
}
111130

131+
function showDotnetToolsWarning(message: string) : void
132+
{
133+
const config = vscode.workspace.getConfiguration('csharp');
134+
if (!config.get('suppressDotnetInstallWarning', false)) {
135+
const getDotNetMessage = 'Get .NET CLI tools';
136+
const goToSettingsMessage = 'Disable this message in user settings';
137+
// Buttons are shown in right-to-left order, with a close button to the right of everything;
138+
// getDotNetMessage will be the first button, then goToSettingsMessage, then the close button.
139+
vscode.window.showErrorMessage(message,
140+
goToSettingsMessage, getDotNetMessage).then(value => {
141+
if (value === getDotNetMessage) {
142+
let open = require('open');
143+
open('https://www.microsoft.com/net/core');
144+
} else if (value === goToSettingsMessage) {
145+
vscode.commands.executeCommand('workbench.action.openGlobalSettings');
146+
}
147+
});
148+
}
149+
}
150+
112151
function logTelemetry(eventName: string, properties?: {[prop: string]: string}): void {
113152
if (_reporter !== null) {
114153
_reporter.sendTelemetryEvent('coreclr-debug/' + eventName, properties);

0 commit comments

Comments
 (0)