diff --git a/changelog.d/+pin-vsc-e2e.internal.md b/changelog.d/+pin-vsc-e2e.internal.md new file mode 100644 index 00000000..ab878b38 --- /dev/null +++ b/changelog.d/+pin-vsc-e2e.internal.md @@ -0,0 +1 @@ +Pin vscode version for e2e, as it fails on latest release diff --git a/changelog.d/62.added.md b/changelog.d/62.added.md new file mode 100644 index 00000000..a02957b8 --- /dev/null +++ b/changelog.d/62.added.md @@ -0,0 +1 @@ +Add option to disable auto-update diff --git a/package-lock.json b/package-lock.json index 44b4c201..e33c1c17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.54.1", "dependencies": { "axios": "^1.4.0", - "semver": "^7.3.7", + "semver": "^7.5.4", "toml": "^3.0.0", "vscode-uri": "^3.0.7", "which": "^3.0.1", diff --git a/package.json b/package.json index dcba02e9..a01d3c4b 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,11 @@ ], "default": null, "description": "Path to local mirrord installation." + }, + "mirrord.autoUpdate": { + "type": ["string", "boolean"], + "default": true, + "description": "Automatically update mirrord binary." } } }, @@ -226,7 +231,7 @@ }, "dependencies": { "axios": "^1.4.0", - "semver": "^7.3.7", + "semver": "^7.5.4", "toml": "^3.0.0", "vscode-uri": "^3.0.7", "which": "^3.0.1", diff --git a/src/binaryManager.ts b/src/binaryManager.ts index d7de5fcb..d4483319 100644 --- a/src/binaryManager.ts +++ b/src/binaryManager.ts @@ -1,4 +1,5 @@ import axios, { AxiosResponse } from 'axios'; +import * as vscode from 'vscode'; import which from 'which'; import { globalContext } from './extension'; import { MirrordAPI } from './api'; @@ -7,6 +8,7 @@ import * as fs from 'node:fs'; import { platform } from 'os'; import { Uri, workspace, window, ProgressLocation, ExtensionMode } from 'vscode'; import { NotificationBuilder } from './notification'; +import * as semver from 'semver'; const mirrordBinaryEndpoint = 'https://version.mirrord.dev/v1/version'; // const binaryCheckInterval = 1000 * 60 * 3; @@ -19,37 +21,43 @@ function getExtensionMirrordPath(): Uri { /** * Tries to find local mirrord in path or in extension storage. + * @param version If specified, then the version of the binary is checked and matched path is returned. + * @returns Path to mirrord binary or null if not found */ export async function getLocalMirrordBinary(version?: string): Promise { try { - const mirrordPath = await which("mirrord"); + const mirrordPath = getExtensionMirrordPath(); + await workspace.fs.stat(mirrordPath); if (version) { - const api = new MirrordAPI(mirrordPath); + const api = new MirrordAPI(mirrordPath.fsPath); const installedVersion = await api.getBinaryVersion(); if (installedVersion === version) { - return mirrordPath; + return mirrordPath.fsPath; } } else { - return mirrordPath; + return mirrordPath.fsPath; } + } catch (e) { - console.debug("couldn't find mirrord in path"); + console.log("couldn't find mirrord in extension storage"); } try { - const mirrordPath = getExtensionMirrordPath(); - await workspace.fs.stat(mirrordPath); + const mirrordPath = await which("mirrord"); if (version) { - const api = new MirrordAPI(mirrordPath.fsPath); + const api = new MirrordAPI(mirrordPath); const installedVersion = await api.getBinaryVersion(); - if (installedVersion === version) { - return mirrordPath.fsPath; + + // we use semver.gte here because installedVersion can be greater than the latest version + // if we are running on the release CI. + if ((process.env.CI_BUILD_PLUGIN === "true" && installedVersion && semver.gte(installedVersion, version)) || + (!process.env.CI_BUILD_PLUGIN && installedVersion === version)) { + return mirrordPath; } } else { - return mirrordPath.fsPath; + return mirrordPath; } - } catch (e) { - console.log("couldn't find mirrord in extension storage"); + console.debug("couldn't find mirrord in path"); } return null; } @@ -75,7 +83,7 @@ async function getConfiguredMirrordBinary(): Promise { let latestVersion; try { - latestVersion = await getLatestSupportedVersion(1000); + latestVersion = await getLatestSupportedVersion(); } catch (err) { new NotificationBuilder() .withMessage(`failed to check latest supported version of mirrord binary, binary specified in settings may be outdated: ${err}`) @@ -93,40 +101,79 @@ async function getConfiguredMirrordBinary(): Promise { } /** - * Downloads mirrord binary (if needed) and returns its path - */ + * Toggles auto-update of mirrord binary. + * Criteria for auto-update: + * - Auto-update is enabled by default + * - if mirrord binary path is mentioned in workspace settings, then that is used + * - if a version is specified, that version is downloaded + * - if auto-update is enabled, then latest supported version is downloaded + * - if auto-update is disabled, any local mirrord binary is used + * + * @param extensionActivate If true, then a global state is set so that any changes to the workspace settings pertaining + * to mirrord binary auto-update will prompt the user to reload the window. + * @returns Path to mirrord binary +*/ export async function getMirrordBinary(): Promise { const configured = await getConfiguredMirrordBinary(); + if (configured) { + vscode.window.showInformationMessage(`Using mirrord binary specified in settings: ${configured}`); return configured; } let foundLocal = await getLocalMirrordBinary(); - // timeout is 1s if we have alternative or 10s if we don't - let timeout = foundLocal ? 1000 : 10000; - const latestVersion = await getLatestSupportedVersion(timeout); - - // See if maybe we have it installed already, in correct version. - const localMirrord = await getLocalMirrordBinary(latestVersion); - if (localMirrord) { - const api = new MirrordAPI(localMirrord); - const installedVersion = await api.getBinaryVersion(); - if (installedVersion === latestVersion) { - return localMirrord; - } + const latestVersion = await getLatestSupportedVersion(); + + const autoUpdateConfigured = vscode.workspace.getConfiguration().get("mirrord.autoUpdate"); + + // values for `mirrord.autoUpdate` can be: + // - true: auto-update is enabled + // - false: auto-update is disabled + // - string: version to download + // example: "mirrord.autoUpdate": "3.70.1" or "mirrord.autoUpdate": false or "mirrord.autoUpdate": true + + const extensionPath = getExtensionMirrordPath().fsPath; + + // check the type can be only null, string or boolean + if (typeof autoUpdateConfigured !== 'boolean' && typeof autoUpdateConfigured !== 'string') { + vscode.window.showErrorMessage(`Invalid autoUpdate setting ${autoUpdateConfigured}: must be a boolean or a string`); + return extensionPath; } - const extensionMirrordPath = getExtensionMirrordPath(); - await downloadMirrordBinary(extensionMirrordPath, latestVersion); + if (typeof autoUpdateConfigured === 'string') { + if (semver.valid(autoUpdateConfigured)) { + const localMirrordBinary = await getLocalMirrordBinary(autoUpdateConfigured); + if (localMirrordBinary) { + return localMirrordBinary; + } + // donot block and download binary, instead download in background + await downloadMirrordBinary(getExtensionMirrordPath(), autoUpdateConfigured); + return extensionPath; + } else { + vscode.window.showErrorMessage(`Invalid version format ${autoUpdateConfigured}: must follow semver format`); + } + } - return extensionMirrordPath.fsPath; + if (autoUpdateConfigured === true) { + const localMirrordBinary = await getLocalMirrordBinary(latestVersion); + if (localMirrordBinary) { + return localMirrordBinary; + } + await downloadMirrordBinary(getExtensionMirrordPath(), latestVersion); + return extensionPath; + } else { + if (foundLocal) { + return foundLocal; + } + return extensionPath; + } } /** * * @returns The latest supported version of mirrord for current extension version */ -async function getLatestSupportedVersion(timeout: number): Promise { +async function getLatestSupportedVersion(): Promise { // commented out logic to avoid checking every X seconds // uncomment if hits performance or too annoying // let lastChecked = globalContext.globalState.get('binaryLastChecked', 0); @@ -173,25 +220,35 @@ function getMirrordDownloadUrl(version: string): string { * @param destPath Path to download the binary to */ async function downloadMirrordBinary(destPath: Uri, version: string): Promise { - fs.mkdirSync(Utils.dirname(destPath).fsPath, { recursive: true }); - const response: AxiosResponse = await window.withProgress({ - location: ProgressLocation.Notification, + await window.withProgress({ + location: ProgressLocation.Window, title: "mirrord", cancellable: false - }, (progress, _) => { - progress.report({ increment: 0, "message": "Downloading mirrord binary..." }); - const p = axios.get( - getMirrordDownloadUrl(version), - { - onDownloadProgress: function (progressEvent) { - progress.report({ increment: progressEvent.progress, "message": "Downloading mirrord binary..." }); - }, - responseType: 'arraybuffer', - }); - - return p; - } - ); - fs.writeFileSync(destPath.fsPath, response.data); - fs.chmodSync(destPath.fsPath, 0o755); + }, async (progress) => { + return new Promise(async (resolve, reject) => { + fs.mkdirSync(Utils.dirname(destPath).fsPath, { recursive: true }); + try { + const response: AxiosResponse = await axios.get( + getMirrordDownloadUrl(version), + { + onDownloadProgress: function (progressEvent) { + progress.report({ increment: progressEvent.progress, "message": "Downloading mirrord binary..." }); + }, + responseType: 'arraybuffer', + }); + + fs.writeFileSync(destPath.fsPath, response.data); + fs.chmodSync(destPath.fsPath, 0o755); + new NotificationBuilder() + .withMessage(`Downloaded mirrord binary version ${version}`) + .info(); + resolve(); + } catch (error) { + new NotificationBuilder() + .withMessage(`Error downloading mirrord binary: ${error}`) + .error(); + reject(error); + } + }); + }); } \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 684aca87..3fc07442 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { ConfigurationProvider } from './debugger'; import { MirrordStatus } from './status'; +import { getMirrordBinary } from './binaryManager'; export let globalContext: vscode.ExtensionContext; @@ -11,6 +12,9 @@ export async function activate(context: vscode.ExtensionContext) { context.workspaceState.update('enabled', false); vscode.debug.registerDebugConfigurationProvider('*', new ConfigurationProvider(), 2); + + // start mirrord binary updates, so that we avoid downloading mid session + await getMirrordBinary(); new MirrordStatus(vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0)) .register() diff --git a/src/tests/e2e.ts b/src/tests/e2e.ts index ffd7d5a2..7af89f19 100644 --- a/src/tests/e2e.ts +++ b/src/tests/e2e.ts @@ -153,4 +153,4 @@ async function startDebugging(configurationFile: string = "Python: Current File" const debugView = await activityBar?.openView() as DebugView; await debugView.selectLaunchConfiguration(configurationFile); await debugView.start(); -} +} \ No newline at end of file diff --git a/src/tests/runTests.ts b/src/tests/runTests.ts index ffe01e25..55de0a4c 100644 --- a/src/tests/runTests.ts +++ b/src/tests/runTests.ts @@ -6,7 +6,7 @@ import { ExTester, ReleaseQuality } from 'vscode-extension-tester'; // https://github.com/redhat-developer/vscode-extension-tester/issues/485#issuecomment-1648050797 is fixed async function main(): Promise { - const version = "latest"; + const version = "1.83.1"; const testPath = path.join(__dirname, 'e2e.js'); const storageFolder = path.join(__dirname, '..', 'storage'); const extFolder = path.join(__dirname, '..', 'extensions');