Skip to content

Commit

Permalink
Add option to disable auto-update (#80)
Browse files Browse the repository at this point in the history
* Add option to disable auto-update

* unneeded file

* revert

* ..

* ..

* changelog

* CR

* ..

* wip

* ..

* ..

* ..

* bugfixes

* CR

* .

* Background binary download with progress

* Tests

* Tests

* Tests

* Tests

* Tests

* ...

* ..

* changelog
  • Loading branch information
infiniteregrets authored Nov 18, 2023
1 parent 338f1f2 commit e2de5d3
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 55 deletions.
1 change: 1 addition & 0 deletions changelog.d/+pin-vsc-e2e.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pin vscode version for e2e, as it fails on latest release
1 change: 1 addition & 0 deletions changelog.d/62.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add option to disable auto-update
2 changes: 1 addition & 1 deletion package-lock.json

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

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@
],
"default": null,
"description": "Path to local mirrord installation."
},
"mirrord.autoUpdate": {
"type": ["string", "boolean"],
"default": true,
"description": "Automatically update mirrord binary."
}
}
},
Expand Down Expand Up @@ -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",
Expand Down
159 changes: 108 additions & 51 deletions src/binaryManager.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand All @@ -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<string | null> {
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;
}
Expand All @@ -75,7 +83,7 @@ async function getConfiguredMirrordBinary(): Promise<string | null> {

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}`)
Expand All @@ -93,40 +101,79 @@ async function getConfiguredMirrordBinary(): Promise<string | null> {
}

/**
* 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<string> {
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<string> {
async function getLatestSupportedVersion(): Promise<string> {
// commented out logic to avoid checking every X seconds
// uncomment if hits performance or too annoying
// let lastChecked = globalContext.globalState.get('binaryLastChecked', 0);
Expand Down Expand Up @@ -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<void> {
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<void>(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);
}
});
});
}
4 changes: 4 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/tests/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
2 changes: 1 addition & 1 deletion src/tests/runTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
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');
Expand Down

0 comments on commit e2de5d3

Please sign in to comment.