Skip to content

Commit

Permalink
ensure appropriate version of dotnet-interactive is installed
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo committed May 9, 2020
1 parent 8916ff4 commit 061495d
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 12 deletions.
25 changes: 19 additions & 6 deletions src/dotnet-interactive-vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/dotnet-interactive-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
"onNotebookEditor:dotnet-interactive"
],
"main": "./out/extension.js",
"extensionDependencies": [
"ms-dotnettools.vscode-dotnet-runtime"
],
"contributes": {
"notebookProvider": [
{
Expand Down Expand Up @@ -76,5 +79,8 @@
"typescript": "3.8.3",
"vsce": "1.75.0",
"vscode-test": "1.3.0"
},
"dependencies": {
"compare-versions": "3.6.0"
}
}
67 changes: 65 additions & 2 deletions src/dotnet-interactive-vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import * as vscode from 'vscode';
import * as cp from 'child_process';
import { ClientMapper } from './clientMapper';
import { DotNetInteractiveNotebookContentProvider } from './vscode/notebookProvider';
import { StdioKernelTransport } from './stdioKernelTransport';
import { registerLanguageProviders } from './vscode/languageProvider';
import { registerCommands } from './vscode/commands';
import { IDotnetAcquireResult } from './interfaces/dotnet';

export function activate(context: vscode.ExtensionContext) {
let clientMapper = new ClientMapper(() => new StdioKernelTransport());
import compareVersions = require("../node_modules/compare-versions");

export async function activate(context: vscode.ExtensionContext) {
const dotnet = await getDotnetPath();
await ensureDotnetInteractive(dotnet);
const clientMapper = new ClientMapper(() => new StdioKernelTransport(dotnet));
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('dotnet-interactive', new DotNetInteractiveNotebookContentProvider(clientMapper)));
context.subscriptions.push(vscode.notebook.onDidCloseNotebookDocument(notebookDocument => clientMapper.closeClient(notebookDocument.uri)));
context.subscriptions.push(registerLanguageProviders(clientMapper));
Expand All @@ -15,3 +21,60 @@ export function activate(context: vscode.ExtensionContext) {

export function deactivate() {
}

async function getDotnetPath(): Promise<string> {
// ensure valid `dotnet`
const validDotnetVersion: RegExp = /^(3|5)\./; // 3.x and 5.x
const { code, output } = await processExitCodeAndOutput('dotnet', ['--version']);
let dotnetPath: string;
if (code === 0 && validDotnetVersion.test(output)) {
// global `dotnet` is present and good
dotnetPath = 'dotnet';
} else {
// need to acquire one
const commandResult = await vscode.commands.executeCommand<IDotnetAcquireResult>('dotnet.acquire', { version: '3.1' });
if (commandResult) {
dotnetPath = commandResult.dotnetPath;
} else {
throw new Error('Unable to find or acquire appropriate version of the `dotnet` CLI.');
}
}

return dotnetPath;
}

async function ensureDotnetInteractive(dotnetPath: string): Promise<void> {
// ensure valid `dotnet-interactive`
const minRequiredVersion = '1.0.125402';
const validDotnetInteractiveDevVersion: RegExp = /^.*-dev$/; // e.g., 1.0.0-dev. Locally produced versions are always assumed to be good.
const { code, output } = await processExitCodeAndOutput(dotnetPath, ['interactive', '--version']);
if (code === 0) {
if (validDotnetInteractiveDevVersion.test(output)) {
// locally built versions are always assumed to be valid
return;
}

// otherwise we have to check the version
if (compareVersions.compare(output, minRequiredVersion, '<')) {
// TODO: acquire it for them
await vscode.window.showErrorMessage(`Unsupported \`dotnet-interactive\` version, '${output}'. Minimum required is '${minRequiredVersion}'.`);
throw new Error('aborting');
}

// good to go
}
}

function processExitCodeAndOutput(command: string, args?: Array<string> | undefined): Promise<{ code: number, output: string }> {
return new Promise<{ code: number, output: string }>((resolve, reject) => {
let output = '';
let childProcess = cp.spawn(command, args || []);
childProcess.stdout.on('data', data => output += data);
childProcess.on('exit', (code: number, _signal: string) => {
resolve({
code: code,
output: output.trim()
});
});
});
}
5 changes: 5 additions & 0 deletions src/dotnet-interactive-vscode/src/interfaces/dotnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// interface acquired from https://github.com/dotnet/vscode-dotnet-runtime/blob/6331b67af2689ba353a1a793eeb1e00131cec46b/vscode-dotnet-runtime-library/src/IDotnetAcquireResult.ts

export interface IDotnetAcquireResult {
dotnetPath: string;
}
6 changes: 3 additions & 3 deletions src/dotnet-interactive-vscode/src/stdioKernelTransport.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as cp from 'child_process';
import { Writable } from 'stream';
import { DisposableSubscription, KernelCommand, KernelCommandType, KernelEventEnvelope, KernelEventEnvelopeObserver } from "./contracts";


export class StdioKernelTransport {
private buffer: string = '';
private childProcess: cp.ChildProcessWithoutNullStreams;
private subscribers: Array<KernelEventEnvelopeObserver> = [];

constructor() {
this.childProcess = cp.spawn('dotnet', ['interactive', 'stdio']);
constructor(readonly dotnetPath: string) {
this.childProcess = cp.spawn(this.dotnetPath, ['interactive', 'stdio']);
this.childProcess.on('exit', (code: number, _signal: string) => {
//
let x = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CellOutput, CellOutputKind } from '../../../interfaces/vscode';

suite('Extension Test Suite', () => {
test('Execute against real kernel', async () => {
let clientMapper = new ClientMapper(() => new StdioKernelTransport());
let clientMapper = new ClientMapper(() => new StdioKernelTransport('dotnet')); // assume it's globally installed for this test
let client = clientMapper.getOrAddClient({ path: 'some/path' });
let code = '1+1';
let result: Array<CellOutput> = [];
Expand Down

0 comments on commit 061495d

Please sign in to comment.