mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
vscode: add nightly extension installation logic
This commit is contained in:
parent
6d2d753677
commit
601fc9d1ab
1 changed files with 131 additions and 0 deletions
131
editors/code/src/installation/extension.ts
Normal file
131
editors/code/src/installation/extension.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
import { vscodeReinstallExtension, vscodeReloadWindow, log, vscodeInstallExtensionFromVsix, assert, notReentrant } from "../util";
|
||||
import { Config, UpdatesChannel } from "../config";
|
||||
import { ArtifactReleaseInfo } from "./interfaces";
|
||||
import { downloadArtifactWithProgressUi } from "./downloads";
|
||||
import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info";
|
||||
|
||||
const HEURISTIC_NIGHTLY_RELEASE_PERIOD_IN_HOURS = 25;
|
||||
|
||||
/**
|
||||
* Installs `stable` or latest `nightly` version or does nothing if the current
|
||||
* extension version is what's needed according to `desiredUpdateChannel`.
|
||||
*/
|
||||
export async function ensureProperExtensionVersion(config: Config): Promise<never | void> {
|
||||
const currentUpdChannel = config.installedExtensionUpdateChannel;
|
||||
const desiredUpdChannel = config.updatesChannel;
|
||||
|
||||
if (currentUpdChannel === UpdatesChannel.Stable) {
|
||||
// Release date is present only when we are on nightly
|
||||
config.installedNightlyExtensionReleaseDate.set(null);
|
||||
}
|
||||
|
||||
// User has built lsp server from sources, she should manage updates manually
|
||||
if (currentUpdChannel === null) return;
|
||||
|
||||
if (desiredUpdChannel === UpdatesChannel.Stable) {
|
||||
// VSCode should handle updates for stable channel
|
||||
if (currentUpdChannel === UpdatesChannel.Stable) return;
|
||||
|
||||
if (!await askToDownloadProperExtensionVersion(config)) return;
|
||||
|
||||
await vscodeReinstallExtension(config.extensionId);
|
||||
await vscodeReloadWindow(); // never returns
|
||||
}
|
||||
|
||||
if (currentUpdChannel === UpdatesChannel.Stable) {
|
||||
if (!await askToDownloadProperExtensionVersion(config)) return;
|
||||
|
||||
return await tryDownloadNightlyExtension(config);
|
||||
}
|
||||
|
||||
const currentExtReleaseDate = config.installedNightlyExtensionReleaseDate.get();
|
||||
|
||||
assert(currentExtReleaseDate !== null, "nightly release date must've been set during installation");
|
||||
|
||||
const hoursSinceLastUpdate = diffInHours(currentExtReleaseDate, new Date());
|
||||
log.debug(`Current rust-analyzer nightly was downloaded ${hoursSinceLastUpdate} hours ago`);
|
||||
|
||||
if (hoursSinceLastUpdate < HEURISTIC_NIGHTLY_RELEASE_PERIOD_IN_HOURS) {
|
||||
return;
|
||||
}
|
||||
if (!await askToDownloadProperExtensionVersion(config, "The installed nightly version is most likely outdated. ")) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tryDownloadNightlyExtension(config, releaseInfo => {
|
||||
assert(
|
||||
currentExtReleaseDate === config.installedNightlyExtensionReleaseDate.get(),
|
||||
"Other active VSCode instance has reinstalled the extension"
|
||||
);
|
||||
|
||||
if (releaseInfo.releaseDate === currentExtReleaseDate) {
|
||||
vscode.window.showInformationMessage(
|
||||
"Whoops, it appears that your nightly version is up-to-date. " +
|
||||
"There might be some problems with the upcomming nightly release " +
|
||||
"or you traveled too far into the future. Sorry for that 😅! "
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async function askToDownloadProperExtensionVersion(config: Config, reason = "") {
|
||||
if (!config.askBeforeDownload) return true;
|
||||
|
||||
const stableOrNightly = config.updatesChannel === UpdatesChannel.Stable ? "stable" : "latest nightly";
|
||||
|
||||
// In case of reentering this function and showing the same info message
|
||||
// (e.g. after we had shown this message, the user changed the config)
|
||||
// vscode will dismiss the already shown one (i.e. return undefined).
|
||||
// This behaviour is what we want, but likely it is not documented
|
||||
|
||||
const userResponse = await vscode.window.showInformationMessage(
|
||||
reason + `Do you want to download the ${stableOrNightly} rust-analyzer extension ` +
|
||||
`version and reload the window now?`,
|
||||
"Download now", "Cancel"
|
||||
);
|
||||
log.debug("Response: ", userResponse);
|
||||
return userResponse === "Download now";
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the process in case of success (i.e. reloads the window) or throws an error.
|
||||
*/
|
||||
const tryDownloadNightlyExtension = notReentrant(async function tryDownloadNightlyExtension(
|
||||
config: Config,
|
||||
shouldDownload: (releaseInfo: ArtifactReleaseInfo) => boolean = () => true
|
||||
): Promise<never | void> {
|
||||
const vsixSource = config.nightlyVsixSource;
|
||||
try {
|
||||
const releaseInfo = await fetchArtifactReleaseInfo(vsixSource.repo, vsixSource.file, vsixSource.tag);
|
||||
|
||||
if (!shouldDownload(releaseInfo)) return;
|
||||
|
||||
await downloadArtifactWithProgressUi(releaseInfo, vsixSource.file, vsixSource.dir, "nightly extension");
|
||||
|
||||
const vsixPath = path.join(vsixSource.dir, vsixSource.file);
|
||||
|
||||
await vscodeInstallExtensionFromVsix(vsixPath)
|
||||
await config.installedNightlyExtensionReleaseDate.set(releaseInfo.releaseDate);
|
||||
await fs.unlink(vsixPath);
|
||||
|
||||
await vscodeReloadWindow(); // never returns
|
||||
} catch (err) {
|
||||
log.downloadError(err, "nightly extension", vsixSource.repo.name);
|
||||
}
|
||||
});
|
||||
|
||||
function diffInHours(a: Date, b: Date): number {
|
||||
// Discard the time and time-zone information (to abstract from daylight saving time bugs)
|
||||
// https://stackoverflow.com/a/15289883/9259330
|
||||
|
||||
const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
||||
const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
||||
|
||||
return (utcA - utcB) / (1000 * 60 * 60);
|
||||
}
|
Loading…
Reference in a new issue