Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Commit

Permalink
Implement nightly updates for rust-analyzer
Browse files Browse the repository at this point in the history
Also make the binary detection more robust, i.e. wrt. "rls.restart" commands.
  • Loading branch information
Xanewok committed Aug 26, 2020
1 parent 5a4dde0 commit bc3e29a
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 57 deletions.
31 changes: 14 additions & 17 deletions src/rust-analyzer/persistent_state.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import * as vscode from 'vscode';

export interface Release {
/**
* ID of a release. Used to disambiguate between different releases under *moving* tags.
*/
id: number;
tag: string;
}

export class PersistentState {
constructor(private readonly globalState: vscode.Memento) {
const { lastCheck, releaseId, releaseTag: serverVersion } = this;
console.info('PersistentState:', { lastCheck, releaseId, serverVersion });
const { lastCheck, installedRelease } = this;
console.info('PersistentState:', { lastCheck, installedRelease });
}

/**
Expand All @@ -16,25 +24,14 @@ export class PersistentState {
await this.globalState.update('lastCheck', value);
}

/**
* Release id of the *nightly* extension.
* Used to check if we should update.
*/
get releaseId(): number | undefined {
return this.globalState.get('releaseId');
}
async updateReleaseId(value: number) {
await this.globalState.update('releaseId', value);
}

/**
* Release tag of the installed server.
* Used to check if we need to update the server.
*/
get releaseTag(): string | undefined {
return this.globalState.get('releaseTag');
get installedRelease(): Release | undefined {
return this.globalState.get('installedRelease');
}
async updateReleaseTag(value: string | undefined) {
await this.globalState.update('releaseTag', value);
async updateInstalledRelease(value: Release | undefined) {
return this.globalState.update('installedRelease', value);
}
}
96 changes: 56 additions & 40 deletions src/rust-analyzer/rustAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ function installDir(): string | undefined {
return undefined;
}

function ensureDir(path: string) {
return !!path && stat(path).catch(() => mkdir(path, { recursive: true }));
}

interface RustAnalyzerConfig {
askBeforeDownload?: boolean;
package: {
Expand Down Expand Up @@ -80,38 +76,39 @@ export async function getServer(
'about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we ' +
'will consider it.',
);
return undefined;
return;
}

const dir = installDir();
if (!dir) {
return;
} else {
await stat(dir).catch(() => mkdir(dir, { recursive: true }));
}
await ensureDir(dir);

const dest = path.join(dir, binaryName);
const exists = await stat(dest).catch(() => false);

if (!exists) {
await state.updateReleaseTag(undefined);
} else if (state.releaseTag === config.package.releaseTag) {
return dest;
await state.updateInstalledRelease(undefined);
}

if (config.askBeforeDownload) {
const userResponse = await vs.window.showInformationMessage(
`${
state.releaseTag && state.releaseTag !== config.package.releaseTag
? `You seem to have installed release \`${state.releaseTag}\` but requested a different one.`
: ''
}
Release \`${
config.package.releaseTag
}\` of rust-analyzer is not installed.\n
Install to ${dir}?`,
'Download',
);
if (userResponse !== 'Download') {
const now = Date.now();
if (state.installedRelease?.tag == config.package.releaseTag) {
// Release tags that are *moving* - these are expected to point to different
// commits and update as the time goes on. Make sure to poll the GitHub API
// (at most once per hour) to see if we need to update.
const MOVING_TAGS = ['nightly'];
const POLL_INTERVAL = 60 * 60 * 1000;

const shouldCheckForNewRelease = MOVING_TAGS.includes(
config.package.releaseTag,
)
? state.installedRelease === undefined ||
now - (state.lastCheck ?? 0) > POLL_INTERVAL
: false;

if (!shouldCheckForNewRelease) {
return dest;
}
}
Expand All @@ -121,19 +118,47 @@ export async function getServer(
'rust-analyzer',
config.package.releaseTag,
);

if (state.installedRelease?.id === release.id) {
return dest;
}

const artifact = release.assets.find(asset => asset.name === binaryName);
if (!artifact) {
throw new Error(`Bad release: ${JSON.stringify(release)}`);
}

if (config.askBeforeDownload) {
const userResponse = await vs.window.showInformationMessage(
`${
state.installedRelease &&
state.installedRelease.tag !== config.package.releaseTag
? `You seem to have installed release \`${state.installedRelease?.tag}\` but requested a different one.`
: ''
}
Release \`${config.package.releaseTag}\` of rust-analyzer ${
!state.installedRelease ? 'is not installed' : 'can be updated'
}.\n
Install to ${dir}?`,
'Download',
);
if (userResponse !== 'Download') {
return exists ? dest : undefined;
}
}

await download({
url: artifact.browser_download_url,
dest,
progressTitle: 'Downloading rust-analyzer server',
mode: 0o755,
});

await state.updateReleaseTag(config.package.releaseTag);
await state.updateLastCheck(now);
await state.updateInstalledRelease({
id: release.id,
tag: config.package.releaseTag,
});

return dest;
}
Expand Down Expand Up @@ -167,34 +192,25 @@ export async function createLanguageClient(
await rustup.ensureComponents(config.rustup, REQUIRED_COMPONENTS);
}

if (!config.rustAnalyzer.path) {
await getServer(
const binPath =
config.rustAnalyzer.path ||
(await getServer(
{
askBeforeDownload: true,
package: { releaseTag: config.rustAnalyzer.releaseTag },
},
new PersistentState(state),
);
));

if (!binPath) {
throw new Error("Couldn't fetch Rust Analyzer binary");
}

if (INSTANCE) {
return INSTANCE;
}

const serverOptions: lc.ServerOptions = async () => {
const binPath =
config.rustAnalyzer.path ||
(await getServer(
{
package: { releaseTag: config.rustAnalyzer.releaseTag },
},
new PersistentState(state),
));

if (!binPath) {
throw new Error("Couldn't fetch Rust Analyzer binary");
}

const childProcess = child_process.exec(binPath);
if (config.logToFile) {
const logPath = path.join(folder.uri.fsPath, `ra-${Date.now()}.log`);
Expand Down

0 comments on commit bc3e29a

Please sign in to comment.