src/goEnvironmentStatus.ts: notify user of available Go updates
Get the latest go versions from golang.org/dl and notify the user if a newer version than the one they are currently usingis available. The latest versions are cached for 24 hours. Notifications are turned on and off using the go.useGoProxyToCheckForToolUpdates setting. 'Don't show again' will keep users from being shown notifications for the specified versions. Updates golang/vscode-go#483 Change-Id: I41033cbf995f76be5c08c3ed9f4d1b81a4fd2aca Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/248184 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Родитель
1e4dbe2d65
Коммит
7f493bf283
|
@ -227,7 +227,7 @@ Complete functions with their parameter signature, excluding the variable types
|
|||
|
||||
### `go.useGoProxyToCheckForToolUpdates`
|
||||
|
||||
When enabled, the extension automatically checks the Go proxy if there are updates available for the Go tools (at present, only gopls) it depends on and prompts the user accordingly
|
||||
When enabled, the extension automatically checks the Go proxy if there are updates available for Go and the Go tools (at present, only gopls) it depends on and prompts the user accordingly
|
||||
|
||||
### `go.useLanguageServer`
|
||||
|
||||
|
|
|
@ -1560,7 +1560,7 @@
|
|||
"go.useGoProxyToCheckForToolUpdates": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When enabled, the extension automatically checks the Go proxy if there are updates available for the Go tools (at present, only gopls) it depends on and prompts the user accordingly"
|
||||
"description": "When enabled, the extension automatically checks the Go proxy if there are updates available for Go and the Go tools (at present, only gopls) it depends on and prompts the user accordingly"
|
||||
},
|
||||
"go.gotoSymbol.includeImports": {
|
||||
"type": "boolean",
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
|
||||
import cp = require('child_process');
|
||||
import fs = require('fs');
|
||||
import moment = require('moment');
|
||||
import os = require('os');
|
||||
import path = require('path');
|
||||
import { promisify } from 'util';
|
||||
import vscode = require('vscode');
|
||||
import WebRequest = require('web-request');
|
||||
import { toolInstallationEnvironment } from './goEnv';
|
||||
import { outputChannel } from './goStatus';
|
||||
import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils';
|
||||
import { getBinPath, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
|
||||
import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
|
||||
import { getFromGlobalState, getFromWorkspaceState, updateGlobalState, updateWorkspaceState } from './stateUtils';
|
||||
import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, GoVersion, rmdirRecursive } from './util';
|
||||
import { correctBinname, getBinPathFromEnvVar, getCurrentGoRoot, pathExists } from './utils/goPath';
|
||||
|
||||
export class GoEnvironmentOption {
|
||||
|
@ -451,3 +452,110 @@ async function fetchDownloadableGoVersions(): Promise<GoEnvironmentOption[]> {
|
|||
return [...opts, new GoEnvironmentOption(dlPath, label)];
|
||||
}, []);
|
||||
}
|
||||
|
||||
export const latestGoVersionKey = 'latestGoVersions';
|
||||
const oneday = 60 * 60 * 24 * 1000; // 24 hours in milliseconds
|
||||
|
||||
export async function getLatestGoVersions(): Promise<GoEnvironmentOption[]> {
|
||||
const timeout = oneday;
|
||||
const now = moment.now();
|
||||
|
||||
let results: GoEnvironmentOption[];
|
||||
|
||||
// Check if we can use cached results
|
||||
const cachedResults = getFromGlobalState(latestGoVersionKey);
|
||||
if (cachedResults && now - cachedResults.timestamp < timeout) {
|
||||
results = cachedResults.goVersions;
|
||||
} else {
|
||||
// fetch the latest supported Go versions
|
||||
try {
|
||||
// fetch the latest Go versions and cache the results
|
||||
results = await fetchDownloadableGoVersions();
|
||||
await updateGlobalState(latestGoVersionKey, {
|
||||
timestamp: now,
|
||||
goVersions: results,
|
||||
});
|
||||
} catch (e) {
|
||||
// hardcode the latest versions of Go in case golang.dl is unavailable
|
||||
results = [
|
||||
new GoEnvironmentOption('go get golang.org/dl/go1.15', 'Go 1.15'),
|
||||
new GoEnvironmentOption('go get golang.org/dl/go1.14.7', 'Go 1.14.7'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
const dismissedGoVersionUpdatesKey = 'dismissedGoVersionUpdates';
|
||||
|
||||
export async function offerToInstallLatestGoVersion() {
|
||||
const goConfig = getGoConfig();
|
||||
if (!goConfig['useGoProxyToCheckForToolUpdates']) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = await getLatestGoVersions();
|
||||
|
||||
// filter out Go versions the user has already dismissed
|
||||
let dismissedOptions: GoEnvironmentOption[];
|
||||
dismissedOptions = await getFromGlobalState(dismissedGoVersionUpdatesKey);
|
||||
if (!!dismissedOptions) {
|
||||
options = options.filter((version) => !dismissedOptions.find((x) => x.label === version.label));
|
||||
}
|
||||
|
||||
// compare to current go version.
|
||||
const currentVersion = await getGoVersion();
|
||||
if (!!currentVersion) {
|
||||
options = options.filter((version) => currentVersion.lt(version.label));
|
||||
}
|
||||
|
||||
// notify user that there is a newer version of Go available
|
||||
if (options.length > 0) {
|
||||
showGoStatus('Go Update Available', 'go.promptforgoinstall', 'A newer version of Go is available');
|
||||
vscode.commands.registerCommand('go.promptforgoinstall', () => {
|
||||
const download = {
|
||||
title: 'Download',
|
||||
async command() {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(`https://golang.org/dl/`));
|
||||
}
|
||||
};
|
||||
|
||||
const neverAgain = {
|
||||
title: `Don't Show Again`,
|
||||
async command() {
|
||||
// mark these versions as seen
|
||||
dismissedOptions = await getFromGlobalState(dismissedGoVersionUpdatesKey);
|
||||
if (!dismissedOptions) {
|
||||
dismissedOptions = [];
|
||||
}
|
||||
options.forEach((version) => {
|
||||
dismissedOptions.push(version);
|
||||
});
|
||||
await updateGlobalState(dismissedGoVersionUpdatesKey, dismissedOptions);
|
||||
}
|
||||
};
|
||||
|
||||
let versionsText: string;
|
||||
if (options.length > 1) {
|
||||
versionsText = `${options.map((x) => x.label)
|
||||
.reduce((prev, next) => {
|
||||
return prev + ' and ' + next;
|
||||
})} are available`;
|
||||
} else {
|
||||
versionsText = `${options[0].label} is available`;
|
||||
}
|
||||
|
||||
vscode.window
|
||||
.showInformationMessage(
|
||||
`${versionsText}. You are currently using ${formatGoVersion(currentVersion)}.`,
|
||||
download,
|
||||
neverAgain
|
||||
)
|
||||
.then((selection) => {
|
||||
hideGoStatus();
|
||||
selection.command();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { GoDebugConfigurationProvider } from './goDebugConfiguration';
|
||||
import { extractFunction, extractVariable } from './goDoctor';
|
||||
import { toolExecutionEnvironment } from './goEnv';
|
||||
import { chooseGoEnvironment, disposeGoStatusBar, setEnvironmentVariableCollection } from './goEnvironmentStatus';
|
||||
import { chooseGoEnvironment, disposeGoStatusBar, offerToInstallLatestGoVersion, setEnvironmentVariableCollection } from './goEnvironmentStatus';
|
||||
import { runFillStruct } from './goFillStruct';
|
||||
import * as goGenerateTests from './goGenerateTests';
|
||||
import { goGetPackage } from './goGetPackage';
|
||||
|
@ -84,6 +84,7 @@ export function activate(ctx: vscode.ExtensionContext) {
|
|||
|
||||
updateGoVarsFromConfig().then(async () => {
|
||||
suggestUpdates(ctx);
|
||||
offerToInstallLatestGoVersion();
|
||||
offerToInstallTools();
|
||||
configureLanguageServer(ctx);
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ export function setGlobalState(state: vscode.Memento) {
|
|||
globalState = state;
|
||||
}
|
||||
|
||||
export function getGlobalState() {
|
||||
return globalState;
|
||||
}
|
||||
|
||||
export function getFromWorkspaceState(key: string, defaultValue?: any) {
|
||||
if (!workspaceState) {
|
||||
return defaultValue;
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright 2020 The Go Authors. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { describe, it } from 'mocha';
|
||||
import * as sinon from 'sinon';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import moment = require('moment');
|
||||
import semver = require('semver');
|
||||
import WebRequest = require('web-request');
|
||||
|
||||
import {
|
||||
getLatestGoVersions,
|
||||
GoEnvironmentOption,
|
||||
latestGoVersionKey,
|
||||
} from '../../src/goEnvironmentStatus';
|
||||
import {
|
||||
getGlobalState,
|
||||
setGlobalState,
|
||||
updateGlobalState,
|
||||
} from '../../src/stateUtils';
|
||||
import { MockMemento } from '../mocks/MockMemento';
|
||||
|
||||
describe('#getLatestGoVersion()', function () {
|
||||
this.timeout(40000);
|
||||
let sandbox: sinon.SinonSandbox | undefined;
|
||||
let defaultMemento: vscode.Memento;
|
||||
const webrequest = sinon.mock(WebRequest);
|
||||
const mmnt = sinon.mock(moment);
|
||||
|
||||
const now = 100000000;
|
||||
const oneday = 60 * 60 * 24 * 1000; // 24 hours in milliseconds
|
||||
|
||||
this.beforeAll(async () => {
|
||||
defaultMemento = getGlobalState();
|
||||
});
|
||||
this.afterAll(async () => {
|
||||
setGlobalState(defaultMemento);
|
||||
});
|
||||
|
||||
this.beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
setGlobalState(new MockMemento());
|
||||
|
||||
webrequest.expects('json')
|
||||
.withArgs('https://golang.org/dl/?mode=json')
|
||||
.returns([
|
||||
{
|
||||
version: 'go1.15.1',
|
||||
stable: true,
|
||||
},
|
||||
{
|
||||
version: 'go1.14.2',
|
||||
stable: true,
|
||||
},
|
||||
]);
|
||||
|
||||
mmnt.expects('now')
|
||||
.returns(now);
|
||||
|
||||
});
|
||||
|
||||
this.afterEach(async () => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('should get latest go versions from golang.org/dl with empty cache', async () => {
|
||||
const results = await getLatestGoVersions();
|
||||
const want = [
|
||||
{label: 'Go 1.15.1', binpath: 'go get golang.org/dl/go1.15.1'},
|
||||
{label: 'Go 1.14.2', binpath: 'go get golang.org/dl/go1.14.2'},
|
||||
];
|
||||
|
||||
assert(results.length === want.length);
|
||||
for (let i = 0; i < results.length; i ++) {
|
||||
assert(results[i].label === want[i].label);
|
||||
assert(results[i].binpath === want[i].binpath);
|
||||
}
|
||||
});
|
||||
|
||||
const cacheVersions = [
|
||||
new GoEnvironmentOption('go get golang.org/dl/go1.14.7', 'Go 1.14.7'),
|
||||
new GoEnvironmentOption('go get golang.org/dl/go1.13.2', 'Go 1.13.2'),
|
||||
];
|
||||
|
||||
it('should get latest go versions from golang.org/dl with timed out cache', async () => {
|
||||
// add a timed out cache entry
|
||||
await updateGlobalState(latestGoVersionKey, {
|
||||
timestamp: now - (oneday + 1), // more than one day ago
|
||||
goVersions: cacheVersions,
|
||||
});
|
||||
|
||||
// run test
|
||||
const results = await getLatestGoVersions();
|
||||
const want = [
|
||||
{label: 'Go 1.15.1', binpath: 'go get golang.org/dl/go1.15.1'},
|
||||
{label: 'Go 1.14.2', binpath: 'go get golang.org/dl/go1.14.2'},
|
||||
];
|
||||
|
||||
// check results
|
||||
assert(results.length === want.length);
|
||||
for (let i = 0; i < results.length; i ++) {
|
||||
assert(results[i].label === want[i].label);
|
||||
assert(results[i].binpath === want[i].binpath);
|
||||
}
|
||||
});
|
||||
|
||||
it('should get latest go versions from cache', async () => {
|
||||
// add a valid cache entry
|
||||
await updateGlobalState(latestGoVersionKey, {
|
||||
timestamp: now - (oneday - 100), // less than one day ago
|
||||
goVersions: cacheVersions,
|
||||
});
|
||||
|
||||
// run test
|
||||
const results = await getLatestGoVersions();
|
||||
const want = [
|
||||
{label: 'Go 1.14.7', binpath: 'go get golang.org/dl/go1.14.7'},
|
||||
{label: 'Go 1.13.2', binpath: 'go get golang.org/dl/go1.13.2'},
|
||||
];
|
||||
|
||||
// check results
|
||||
assert(results.length === want.length);
|
||||
for (let i = 0; i < results.length; i ++) {
|
||||
assert(results[i].label === want[i].label);
|
||||
assert(results[i].binpath === want[i].binpath);
|
||||
}
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче