Skip to content

Commit ad91435

Browse files
authored
Check for language server updates every 24 hours (#595)
1 parent 542e6b3 commit ad91435

File tree

2 files changed

+69
-47
lines changed

2 files changed

+69
-47
lines changed

src/extension.ts

+31-18
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ const extensionId = 'hashicorp.terraform';
3939
const appInsightsKey = '885372d2-6f3c-499f-9d25-b8b219983a52';
4040
let reporter: TelemetryReporter;
4141

42-
let extensionPath: string;
42+
let installPath: string;
4343

4444
export async function activate(context: vscode.ExtensionContext): Promise<any> {
4545
const extensionVersion = vscode.extensions.getExtension(extensionId).packageJSON.version;
4646
reporter = new TelemetryReporter(extensionId, extensionVersion, appInsightsKey);
4747
context.subscriptions.push(reporter);
48+
installPath = path.join(context.extensionPath, 'lsp');
4849

49-
extensionPath = context.extensionPath;
5050
// get rid of pre-2.0.0 settings
5151
if (config('terraform').has('languageServer.enabled')) {
5252
try {
@@ -63,7 +63,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
6363
const current = config('terraform').get('languageServer');
6464
await config('terraform').update('languageServer', Object.assign(current, { external: true }), vscode.ConfigurationTarget.Global);
6565
}
66-
return startClients();
66+
return updateLanguageServer();
6767
}),
6868
vscode.commands.registerCommand('terraform.disableLanguageServer', async () => {
6969
if (enabled()) {
@@ -166,6 +166,29 @@ export function deactivate(): Promise<void[]> {
166166
return stopClients();
167167
}
168168

169+
async function updateLanguageServer() {
170+
const delay = 1000 * 60 * 24;
171+
setTimeout(updateLanguageServer, delay); // check for new updates every 24hrs
172+
173+
// skip install if a language server binary path is set
174+
if (!config('terraform').get('languageServer.pathToBinary')) {
175+
const installer = new LanguageServerInstaller(installPath, reporter);
176+
const install = await installer.needsInstall();
177+
if (install) {
178+
await stopClients();
179+
try {
180+
await installer.install();
181+
} catch (err) {
182+
reporter.sendTelemetryException(err);
183+
throw err;
184+
} finally {
185+
await installer.cleanupZips();
186+
}
187+
}
188+
}
189+
return startClients(); // on repeat runs with no install, this will be a no-op
190+
}
191+
169192
async function startClients(folders = prunedFolderNames()) {
170193
console.log('Starting:', folders);
171194
const command = await pathToBinary();
@@ -262,20 +285,10 @@ let _pathToBinaryPromise: Promise<string>;
262285
async function pathToBinary(): Promise<string> {
263286
if (!_pathToBinaryPromise) {
264287
let command: string = config('terraform').get('languageServer.pathToBinary');
265-
if (!command) { // Skip install/upgrade if user has set custom binary path
266-
const installDir = path.join(extensionPath, 'lsp');
267-
const installer = new LanguageServerInstaller(reporter);
268-
try {
269-
await installer.install(installDir);
270-
} catch (err) {
271-
reporter.sendTelemetryException(err);
272-
throw err;
273-
} finally {
274-
await installer.cleanupZips(installDir);
275-
}
276-
command = path.join(installDir, 'terraform-ls');
277-
} else {
288+
if (command) { // Skip install/upgrade if user has set custom binary path
278289
reporter.sendTelemetryEvent('usePathToBinary');
290+
} else {
291+
command = path.join(installPath, 'terraform-ls');
279292
}
280293
_pathToBinaryPromise = Promise.resolve(command);
281294
}
@@ -357,8 +370,8 @@ async function terraformCommand(command: string, languageServerExec = true): Pro
357370
{ value: `terraform ${command}`, prompt: `Run in ${selectedModule}` }
358371
);
359372
if (terraformCommand) {
360-
const terminal = vscode.window.terminals.find(t => t.name == terminalName ) ||
361-
vscode.window.createTerminal({ name: `Terraform ${selectedModule}`, cwd: moduleURI });
373+
const terminal = vscode.window.terminals.find(t => t.name == terminalName) ||
374+
vscode.window.createTerminal({ name: `Terraform ${selectedModule}`, cwd: moduleURI });
362375
terminal.sendText(terraformCommand);
363376
terminal.show();
364377
}

src/languageServerInstaller.ts

+38-29
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,64 @@ import TelemetryReporter from 'vscode-extension-telemetry';
88
import { exec } from './utils';
99
import { Release, getRelease } from '@hashicorp/js-releases';
1010

11+
const extensionVersion = vscode.extensions.getExtension('hashicorp.terraform').packageJSON.version;
12+
1113
export class LanguageServerInstaller {
1214
constructor(
15+
private directory: string,
1316
private reporter: TelemetryReporter
1417
) { }
1518

16-
public async install(directory: string): Promise<void> {
17-
const extensionVersion = vscode.extensions.getExtension('hashicorp.terraform').packageJSON.version;
18-
const userAgent = `Terraform-VSCode/${extensionVersion} VSCode/${vscode.version}`;
19-
let isInstalled = false;
19+
private userAgent = `Terraform-VSCode/${extensionVersion} VSCode/${vscode.version}`;
20+
private release: Release;
21+
22+
public async needsInstall(): Promise<boolean> {
23+
try {
24+
this.release = await getRelease("terraform-ls", "latest", this.userAgent);
25+
} catch (err) {
26+
// if the releases site is inaccessible, report it and skip the install
27+
this.reporter.sendTelemetryException(err);
28+
return false;
29+
}
30+
2031
let installedVersion: string;
2132
try {
22-
installedVersion = await getLsVersion(directory);
23-
isInstalled = true;
33+
installedVersion = await getLsVersion(this.directory);
2434
} catch (err) {
25-
// Most of the time, getLsVersion would produce "ENOENT: no such file or directory"
35+
// Most of the time, getLsVersion will produce "ENOENT: no such file or directory"
2636
// on a fresh installation (unlike upgrade). It’s also possible that the file or directory
2737
// is inaccessible for some other reason, but we catch that separately.
2838
if (err.code !== 'ENOENT') {
2939
this.reporter.sendTelemetryException(err);
3040
throw err;
3141
}
32-
isInstalled = false;
42+
return true; // yes to new install
3343
}
3444

35-
const currentRelease = await getRelease("terraform-ls", "latest", userAgent);
36-
if (isInstalled) {
37-
this.reporter.sendTelemetryEvent('foundLsInstalled', { terraformLsVersion: installedVersion });
38-
if (semver.gt(currentRelease.version, installedVersion, { includePrerelease: true })) {
39-
const selected = await vscode.window.showInformationMessage(`A new language server release is available: ${currentRelease.version}. Install now?`, 'Install', 'Cancel');
40-
if (selected !== 'Install') { // selected is undefined if the user closes the message
41-
return;
42-
}
43-
} else {
44-
return;
45-
}
45+
this.reporter.sendTelemetryEvent('foundLsInstalled', { terraformLsVersion: installedVersion });
46+
const upgrade = semver.gt(this.release.version, installedVersion, { includePrerelease: true });
47+
if (upgrade) {
48+
const selected = await vscode.window.showInformationMessage(`A new language server release is available: ${this.release.version}. Install now?`, 'Install', 'Cancel');
49+
return (selected === "Install");
50+
} else {
51+
return false; // no upgrade available
4652
}
53+
}
4754

55+
public async install(): Promise<void> {
56+
this.reporter.sendTelemetryEvent('installingLs', { terraformLsVersion: this.release.version });
4857
try {
49-
this.reporter.sendTelemetryEvent('installingLs', { terraformLsVersion: currentRelease.version });
50-
await this.installPkg(currentRelease, directory, userAgent);
58+
await this.installPkg(this.release);
5159
} catch (err) {
5260
vscode.window.showErrorMessage(`Unable to install terraform-ls: ${err.message}`);
5361
throw err;
5462
}
5563

56-
this.showChangelog(currentRelease.version);
64+
this.showChangelog(this.release.version);
5765
}
5866

59-
async installPkg(release: Release, installDir: string, userAgent: string): Promise<void> {
67+
async installPkg(release: Release): Promise<void> {
68+
const installDir = this.directory;
6069
const destination = `${installDir}/terraform-ls_v${release.version}.zip`;
6170
fs.mkdirSync(installDir, { recursive: true }); // create install directory if missing
6271

@@ -67,7 +76,7 @@ export class LanguageServerInstaller {
6776
throw new Error(`Install error: no matching terraform-ls binary for ${os}/${arch}`);
6877
}
6978
try {
70-
this.removeOldBinary(installDir);
79+
this.removeOldBinary();
7180
} catch {
7281
// ignore missing binary (new install)
7382
}
@@ -78,20 +87,20 @@ export class LanguageServerInstaller {
7887
title: "Installing terraform-ls"
7988
}, async (progress) => {
8089
progress.report({ increment: 30 });
81-
await release.download(build.url, destination, userAgent);
90+
await release.download(build.url, destination, this.userAgent);
8291
progress.report({ increment: 30 });
8392
await release.verify(destination, build.filename)
8493
progress.report({ increment: 30 });
8594
return release.unpack(installDir, destination)
8695
});
8796
}
8897

89-
removeOldBinary(directory: string): void {
90-
fs.unlinkSync(lsBinPath(directory));
98+
removeOldBinary(): void {
99+
fs.unlinkSync(lsBinPath(this.directory));
91100
}
92101

93-
public async cleanupZips(directory: string): Promise<string[]> {
94-
return del(`${directory}/terraform-ls*.zip`, { force: true });
102+
public async cleanupZips(): Promise<string[]> {
103+
return del(`${this.directory}/terraform-ls*.zip`, { force: true });
95104
}
96105

97106
showChangelog(version: string): void {

0 commit comments

Comments
 (0)