vscode: amend server installation logic to account for nightlies

This commit is contained in:
Veetaha 2020-03-09 19:57:55 +02:00
parent 601fc9d1ab
commit 1e73811fbe
3 changed files with 57 additions and 52 deletions

View file

@ -5,7 +5,7 @@ import { spawnSync } from 'child_process';
export function serverVersion(ctx: Ctx): Cmd { export function serverVersion(ctx: Ctx): Cmd {
return async () => { return async () => {
const binaryPath = await ensureServerBinary(ctx.config.serverSource); const binaryPath = await ensureServerBinary(ctx.config);
if (binaryPath == null) { if (binaryPath == null) {
throw new Error( throw new Error(
@ -18,4 +18,3 @@ export function serverVersion(ctx: Ctx): Cmd {
vscode.window.showInformationMessage('rust-analyzer version : ' + version); vscode.window.showInformationMessage('rust-analyzer version : ' + version);
}; };
} }

View file

@ -1,14 +1,16 @@
import * as vscode from "vscode"; import * as vscode from "vscode";
import * as path from "path"; import * as path from "path";
import { promises as dns } from "dns";
import { spawnSync } from "child_process"; import { spawnSync } from "child_process";
import { ArtifactSource } from "./interfaces"; import { ArtifactSource } from "./interfaces";
import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info";
import { downloadArtifact } from "./download_artifact"; import { downloadArtifactWithProgressUi } from "./downloads";
import { log, assert } from "../util"; import { log, assert } from "../util";
import { Config, NIGHTLY_TAG } from "../config";
export async function ensureServerBinary(config: Config): Promise<null | string> {
const source = config.serverSource;
export async function ensureServerBinary(source: null | ArtifactSource): Promise<null | string> {
if (!source) { if (!source) {
vscode.window.showErrorMessage( vscode.window.showErrorMessage(
"Unfortunately we don't ship binaries for your platform yet. " + "Unfortunately we don't ship binaries for your platform yet. " +
@ -35,18 +37,11 @@ export async function ensureServerBinary(source: null | ArtifactSource): Promise
return null; return null;
} }
case ArtifactSource.Type.GithubRelease: { case ArtifactSource.Type.GithubRelease: {
const prebuiltBinaryPath = path.join(source.dir, source.file); if (!shouldDownloadServer(source, config)) {
return path.join(source.dir, source.file);
const installedVersion: null | string = getServerVersion(source.storage);
const requiredVersion: string = source.tag;
log.debug("Installed version:", installedVersion, "required:", requiredVersion);
if (isBinaryAvailable(prebuiltBinaryPath) && installedVersion === requiredVersion) {
return prebuiltBinaryPath;
} }
if (source.askBeforeDownload) { if (config.askBeforeDownload) {
const userResponse = await vscode.window.showInformationMessage( const userResponse = await vscode.window.showInformationMessage(
`Language server version ${source.tag} for rust-analyzer is not installed. ` + `Language server version ${source.tag} for rust-analyzer is not installed. ` +
"Do you want to download it now?", "Do you want to download it now?",
@ -55,38 +50,53 @@ export async function ensureServerBinary(source: null | ArtifactSource): Promise
if (userResponse !== "Download now") return null; if (userResponse !== "Download now") return null;
} }
if (!await downloadServer(source)) return null; return await downloadServer(source, config);
return prebuiltBinaryPath;
} }
} }
} }
async function downloadServer(source: ArtifactSource.GithubRelease): Promise<boolean> { function shouldDownloadServer(
source: ArtifactSource.GithubRelease,
config: Config
): boolean {
if (!isBinaryAvailable(path.join(source.dir, source.file))) return true;
const installed = {
tag: config.serverReleaseTag.get(),
date: config.serverReleaseDate.get()
};
const required = {
tag: source.tag,
date: config.installedNightlyExtensionReleaseDate.get()
};
log.debug("Installed server:", installed, "required:", required);
if (required.tag !== NIGHTLY_TAG || installed.tag !== NIGHTLY_TAG) {
return required.tag !== installed.tag;
}
assert(required.date !== null, "Extension release date should have been saved during its installation");
assert(installed.date !== null, "Server release date should have been saved during its installation");
return installed.date.getTime() !== required.date.getTime();
}
async function downloadServer(
source: ArtifactSource.GithubRelease,
config: Config,
): Promise<null | string> {
try { try {
const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.tag); const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.tag);
await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); await downloadArtifactWithProgressUi(releaseInfo, source.file, source.dir, "language server");
await setServerVersion(source.storage, releaseInfo.releaseName); await Promise.all([
config.serverReleaseTag.set(releaseInfo.releaseName),
config.serverReleaseDate.set(releaseInfo.releaseDate)
]);
} catch (err) { } catch (err) {
vscode.window.showErrorMessage( log.downloadError(err, "language server", source.repo.name);
`Failed to download language server from ${source.repo.name} ` + return null;
`GitHub repository: ${err.message}`
);
log.error(err);
dns.resolve('example.com').then(
addrs => log.debug("DNS resolution for example.com was successful", addrs),
err => {
log.error(
"DNS resolution for example.com failed, " +
"there might be an issue with Internet availability"
);
log.error(err);
}
);
return false;
} }
const binaryPath = path.join(source.dir, source.file); const binaryPath = path.join(source.dir, source.file);
@ -101,7 +111,7 @@ async function downloadServer(source: ArtifactSource.GithubRelease): Promise<boo
"Rust analyzer language server was successfully installed 🦀" "Rust analyzer language server was successfully installed 🦀"
); );
return true; return binaryPath;
} }
function isBinaryAvailable(binaryPath: string): boolean { function isBinaryAvailable(binaryPath: string): boolean {
@ -115,14 +125,3 @@ function isBinaryAvailable(binaryPath: string): boolean {
return res.status === 0; return res.status === 0;
} }
function getServerVersion(storage: vscode.Memento): null | string {
const version = storage.get<null | string>("server-version", null);
log.debug("Get server-version:", version);
return version;
}
async function setServerVersion(storage: vscode.Memento, version: string): Promise<void> {
log.debug("Set server-version:", version);
await storage.update("server-version", version.toString());
}

View file

@ -8,6 +8,7 @@ import { activateHighlighting } from './highlighting';
import { ensureServerBinary } from './installation/server'; import { ensureServerBinary } from './installation/server';
import { Config } from './config'; import { Config } from './config';
import { log } from './util'; import { log } from './util';
import { ensureProperExtensionVersion } from './installation/extension';
let ctx: Ctx | undefined; let ctx: Ctx | undefined;
@ -34,7 +35,13 @@ export async function activate(context: vscode.ExtensionContext) {
const config = new Config(context); const config = new Config(context);
const serverPath = await ensureServerBinary(config.serverSource); vscode.workspace.onDidChangeConfiguration(() => ensureProperExtensionVersion(config));
// Don't await the user response here, otherwise we will block the lsp server bootstrap
void ensureProperExtensionVersion(config);
const serverPath = await ensureServerBinary(config);
if (serverPath == null) { if (serverPath == null) {
throw new Error( throw new Error(
"Rust Analyzer Language Server is not available. " + "Rust Analyzer Language Server is not available. " +