Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: select path for runner when installing #1238

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module.exports = {

extends: '@lars-reimann/svelte',
rules: {
'@typescript-eslint/no-throw-literal': 'off',
'@typescript-eslint/only-throw-error': 'error',
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
'svelte/valid-compile': 'off',
Expand Down
55 changes: 35 additions & 20 deletions packages/safe-ds-vscode/src/extension/actions/installRunner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import vscode, { ExtensionContext, Uri } from 'vscode';
import vscode, { Uri } from 'vscode';
import child_process from 'node:child_process';
import semver from 'semver';
import { dependencies, rpc } from '@safe-ds/lang';
Expand All @@ -11,24 +11,40 @@ const LOWEST_SUPPORTED_PYTHON_VERSION = '3.11.0';
const LOWEST_UNSUPPORTED_PYTHON_VERSION = '3.13.0';
const npmVersionRange = `>=${LOWEST_SUPPORTED_PYTHON_VERSION} <${LOWEST_UNSUPPORTED_PYTHON_VERSION}`;

export const installRunner = (context: ExtensionContext, client: LanguageClient) => {
export const installRunner = (client: LanguageClient) => {
return async () => {
// If the runner is already started, do nothing
if (await client.sendRequest(rpc.IsRunnerReadyRequest.type)) {
vscode.window.showInformationMessage('The runner is already installed and running.');
return;
}

// Ask the user where the virtual environment should be created
const runnerVirtualEnvironmentUris = await vscode.window.showOpenDialog({
canSelectFolders: true,
canSelectMany: false,
title: 'Location for the runner installation',
});

if (!runnerVirtualEnvironmentUris || runnerVirtualEnvironmentUris.length === 0) {
return;
}
const runnerVirtualEnvironmentUri = runnerVirtualEnvironmentUris[0]!;

// Install the runner if it is not already installed
const success = await doInstallRunner(context);
const success = await doInstallRunner(runnerVirtualEnvironmentUri);
if (!success) {
return;
}

// Set the runner command in the configuration
await vscode.workspace
.getConfiguration()
.update('safe-ds.runner.command', getRunnerCommand(context), vscode.ConfigurationTarget.Global);
.update(
'safe-ds.runner.command',
getRunnerCommand(runnerVirtualEnvironmentUri),
vscode.ConfigurationTarget.Global,
);

// Start the runner (needed if the configuration did not change, so no event is fired)
await client.sendNotification(rpc.StartRunnerNotification.type);
Expand All @@ -41,7 +57,7 @@ export const installRunner = (context: ExtensionContext, client: LanguageClient)
/**
* Installs the runner in a virtual environment. Returns true if the installation was successful.
*/
const doInstallRunner = async (context: ExtensionContext): Promise<boolean> => {
const doInstallRunner = async (runnerVirtualEnvironmentUri: Uri): Promise<boolean> => {
// Check if a matching Python interpreter is available
const pythonCommand = await getPythonCommand();
if (!pythonCommand) {
Expand All @@ -58,7 +74,7 @@ const doInstallRunner = async (context: ExtensionContext): Promise<boolean> => {
},
async () => {
try {
await createRunnerVirtualEnvironment(context, pythonCommand);
await createRunnerVirtualEnvironment(runnerVirtualEnvironmentUri, pythonCommand);
return true;
} catch (error) {
vscode.window.showErrorMessage('Failed to create a virtual environment.');
Expand All @@ -79,7 +95,7 @@ const doInstallRunner = async (context: ExtensionContext): Promise<boolean> => {
},
async () => {
try {
await installRunnerInVirtualEnvironment(getPipCommand(context));
await installRunnerInVirtualEnvironment(getPipCommand(runnerVirtualEnvironmentUri));
return true;
} catch (error) {
vscode.window.showErrorMessage('Failed to install the runner.');
Expand Down Expand Up @@ -116,9 +132,12 @@ const isMatchingPython = async (pythonCommand: string): Promise<boolean> => {
});
};

const createRunnerVirtualEnvironment = async (context: ExtensionContext, pythonCommand: string): Promise<void> => {
const createRunnerVirtualEnvironment = async (
runnerVirtualEnvironmentUri: Uri,
pythonCommand: string,
): Promise<void> => {
return new Promise((resolve, reject) => {
child_process.exec(`${pythonCommand} -m venv ${runnerVirtualEnvironmentUri(context).fsPath}`, (error) => {
child_process.exec(`"${pythonCommand}" -m venv "${runnerVirtualEnvironmentUri.fsPath}"`, (error) => {
if (error) {
reject(error);
} else {
Expand All @@ -130,7 +149,7 @@ const createRunnerVirtualEnvironment = async (context: ExtensionContext, pythonC

export const installRunnerInVirtualEnvironment = async (pipCommand: string): Promise<void> => {
return new Promise((resolve, reject) => {
const installCommand = `${pipCommand} install --upgrade "safe-ds-runner${dependencies['safe-ds-runner'].pipVersionRange}"`;
const installCommand = `"${pipCommand}" install --upgrade "safe-ds-runner${dependencies['safe-ds-runner'].pipVersionRange}"`;
const process = child_process.spawn(installCommand, { shell: true });

process.stdout.on('data', (data: Buffer) => {
Expand All @@ -153,22 +172,18 @@ export const installRunnerInVirtualEnvironment = async (pipCommand: string): Pro
});
};

const getPipCommand = (context: ExtensionContext): string => {
const getPipCommand = (runnerVirtualEnvironmentUri: Uri): string => {
if (process.platform === 'win32') {
return `${runnerVirtualEnvironmentUri(context).fsPath}\\Scripts\\pip.exe`;
return `${runnerVirtualEnvironmentUri.fsPath}\\Scripts\\pip.exe`;
} else {
return `${runnerVirtualEnvironmentUri(context).fsPath}/bin/pip`;
return `${runnerVirtualEnvironmentUri.fsPath}/bin/pip`;
}
};

const getRunnerCommand = (context: ExtensionContext): string => {
const getRunnerCommand = (runnerVirtualEnvironmentUri: Uri): string => {
if (process.platform === 'win32') {
return `${runnerVirtualEnvironmentUri(context).fsPath}\\Scripts\\safe-ds-runner.exe`;
return `${runnerVirtualEnvironmentUri.fsPath}\\Scripts\\safe-ds-runner.exe`;
} else {
return `${runnerVirtualEnvironmentUri(context).fsPath}/bin/safe-ds-runner`;
return `${runnerVirtualEnvironmentUri.fsPath}/bin/safe-ds-runner`;
}
};

const runnerVirtualEnvironmentUri = (context: ExtensionContext): Uri => {
return vscode.Uri.joinPath(context.globalStorageUri, 'runnerVenv');
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const updateRunner = (context: ExtensionContext, client: LanguageClient)

// If the runner executable cannot be found at all, install it from scratch
if (!fs.existsSync(await getRunnerCommand())) {
await installRunner(context, client)();
await installRunner(client)();
return;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/safe-ds-vscode/src/extension/mainClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const createLanguageClient = function (context: vscode.ExtensionContext): Langua
const registerNotificationListeners = function (context: vscode.ExtensionContext) {
context.subscriptions.push(
client.onNotification(rpc.InstallRunnerNotification.type, async () => {
await installRunner(context, client)();
await installRunner(client)();
}),
client.onNotification(rpc.RunnerStartedNotification.type, async ({ port }: rpc.RunnerStartedParams) => {
await services.runtime.PythonServer.connectToPort(port);
Expand All @@ -114,7 +114,7 @@ const registerCommands = function (context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('safe-ds.dumpDiagnostics', dumpDiagnostics(context)),
vscode.commands.registerCommand('safe-ds.exploreTable', exploreTable(context)),
vscode.commands.registerCommand('safe-ds.installRunner', installRunner(context, client)),
vscode.commands.registerCommand('safe-ds.installRunner', installRunner(client)),
vscode.commands.registerCommand('safe-ds.openDiagnosticsDumps', openDiagnosticsDumps(context)),
vscode.commands.registerCommand('safe-ds.refreshWebview', refreshWebview(context)),
vscode.commands.registerCommand('safe-ds.updateRunner', updateRunner(context, client)),
Expand Down