Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/min-versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"comment": "This file is fetched by the AppMap extension from https://raw.githubusercontent.com/getappmap/vscode-appland/develop/.github/min-versions.json.",

"vsCode": "1.89.0",
"extension": "0.118.2"
}
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import CommandRegistry from './commands/commandRegistry';
import AssetService from './assets/assetService';
import clearNavieAiSettings from './commands/clearNavieAiSettings';
import EnvironmentVariableService from './services/environmentVariableService';
import { checkVersions } from './lib/checkVersions';

export async function activate(context: vscode.ExtensionContext): Promise<AppMapService> {
CommandRegistry.setContext(context).addWaitAlias({
Expand All @@ -84,6 +85,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<AppMap
const extensionState = new ExtensionState(context);
context.subscriptions.push(extensionState);

checkVersions(context);

navieConfigurationService(context);

const recommender = new AppMapRecommenderService(extensionState);
Expand Down
53 changes: 53 additions & 0 deletions src/lib/checkVersions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as vscode from 'vscode';
import fetch from 'node-fetch';
import semverCompare from 'semver/functions/compare';
import semverValid from 'semver/functions/valid';

const MIN_VERSIONS_JSON_URL =
'https://raw.githubusercontent.com/getappmap/vscode-appland/develop/.github/min-versions.json';

async function fetchMinVersions(url: string) {
try {
const response = await fetch(url);
return await response.json();
} catch (e) {
/* Do nothing if there is no min versions json. */
}
}

export type MinVersions = {
vsCode?: string;
extension?: string;
};

export async function checkVersions(
context: vscode.ExtensionContext,
minVersions?: MinVersions,
minVersionsJsonUrl: string = MIN_VERSIONS_JSON_URL
): Promise<void> {
if (minVersions == undefined) minVersions = await fetchMinVersions(minVersionsJsonUrl);
if (minVersions == undefined) return;

const vsCodeIsOld =
minVersions.vsCode &&
semverValid(minVersions.vsCode, true) &&
semverCompare(minVersions.vsCode, vscode.version, true) == 1;
const extensionIsOld =
minVersions.extension &&
semverValid(minVersions.extension, true) &&
semverCompare(minVersions.extension, context.extension.packageJSON.version, true) == 1;

const clauses: string[] = [];
if (vsCodeIsOld) clauses.push(`VSCode to v${minVersions.vsCode} or higher`);
if (extensionIsOld) clauses.push(`AppMap extension to v${minVersions.extension} or higher`);
if (clauses.length > 0) {
const choice = await vscode.window.showWarningMessage(
`Update Required: Please update ${clauses.join(' and ')}.`,
'Update Now'
);
if (choice == 'Update Now') {
if (vsCodeIsOld) vscode.commands.executeCommand('update.checkForUpdate');
else vscode.commands.executeCommand('workbench.extensions.action.checkForUpdates');
}
}
}
74 changes: 74 additions & 0 deletions test/integration/lib/checkVersions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import assert from 'assert';
import * as vscode from 'vscode';
import * as semver from 'semver';
import { SinonSandbox, SinonStub, createSandbox } from 'sinon';
import MockExtensionContext from '../../mocks/mockExtensionContext';
import { checkVersions } from '../../../src/lib/checkVersions';

describe('Check VSCode and extension versions', () => {
let sinon: SinonSandbox;
let context: MockExtensionContext;
let showWarningMessageStub: SinonStub;
let executeCommandStub: SinonStub;

beforeEach(() => {
sinon = createSandbox();
context = new MockExtensionContext();

showWarningMessageStub = sinon.stub(vscode.window, 'showWarningMessage');
showWarningMessageStub.resolves('Update Now' as unknown as vscode.MessageItem);
executeCommandStub = sinon.stub(vscode.commands, 'executeCommand');
});

afterEach(() => {
context.dispose();
sinon.restore();
});

it('warning shown when both versions are old', async () => {
// Can't replace vscode.version
// sinon.replaceGetter(vscode, 'version', () => '1.82');
const minVersions = {
vsCode: semver.inc(vscode.version, 'minor') ?? undefined,
extension: '0.118.2',
};
sinon.replace(context.extension.packageJSON, 'version', '0.118.0');
await checkVersions(context, minVersions);
assert.equal(showWarningMessageStub.callCount, 1);
assert.equal(
showWarningMessageStub.firstCall.args[0],
`Update Required: Please update VSCode to v${minVersions.vsCode} or higher` +
` and AppMap extension to v${minVersions.extension} or higher.`
);
assert.equal(executeCommandStub.callCount, 1);
assert.equal(executeCommandStub.firstCall.args[0], 'update.checkForUpdate');
});

it('warning shown when only extension version is old', async () => {
const minVersions = {
vsCode: vscode.version,
extension: '0.118.2',
};
sinon.replace(context.extension.packageJSON, 'version', '0.118.0');
await checkVersions(context, minVersions);
assert.equal(showWarningMessageStub.callCount, 1);
assert.equal(
showWarningMessageStub.firstCall.args[0],
`Update Required: Please update AppMap extension to v${minVersions.extension} or higher.`
);
assert.equal(executeCommandStub.callCount, 1);
assert.equal(
executeCommandStub.firstCall.args[0],
'workbench.extensions.action.checkForUpdates'
);
});

it('no warning when versions are up to date', async () => {
await checkVersions(context, {
vsCode: vscode.version,
extension: context.extension.packageJSON.version,
});
assert(showWarningMessageStub.notCalled);
assert(executeCommandStub.notCalled);
});
});