diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 0cc8025125d..e8c301c18be 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -57,7 +57,7 @@ jobs: - run: npm run testCompile - run: npm run lint - jscpd: + lint-duplicate-code: needs: lint-commits if: ${{ github.event_name == 'pull_request'}} runs-on: ubuntu-latest diff --git a/buildspec/release/70checkmarketplace.yml b/buildspec/release/70checkmarketplace.yml new file mode 100644 index 00000000000..c4c2314b98f --- /dev/null +++ b/buildspec/release/70checkmarketplace.yml @@ -0,0 +1,48 @@ +version: 0.2 + +phases: + install: + runtime-versions: + nodejs: 16 + + commands: + - apt update + - apt install -y wget gpg + - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg + - install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/ + - sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list' + - apt update + - apt install -y code + + pre_build: + commands: + # Check for implicit env vars passed from the release pipeline. + - test -n "${TARGET_EXTENSION}" + + build: + commands: + - VERSION=$(node -e "console.log(require('./packages/${TARGET_EXTENSION}/package.json').version);") + # get extension name + - | + if [ "${TARGET_EXTENSION}" = "amazonq" ]; then + extension_name="amazonwebservices.amazon-q-vscode" + elif [ "${TARGET_EXTENSION}" = "toolkit" ]; then + extension_name="amazonwebservices.aws-toolkit-vscode" + else + echo checkmarketplace: "Unknown TARGET_EXTENSION: ${TARGET_EXTENSION}" + exit 1 + fi + # keep reinstalling the extension until the desired version is updated. Otherwise fail on codebuild timeout (1 hour). + - | + while true; do + code --uninstall-extension "${extension_name}" --no-sandbox --user-data-dir /tmp/vscode + code --install-extension ${extension_name} --no-sandbox --user-data-dir /tmp/vscode + cur_version=$(code --list-extensions --show-versions --no-sandbox --user-data-dir /tmp/vscode | grep ${extension_name} | cut -d'@' -f2) + if [ "${cur_version}" = "${VERSION}" ]; then + echo "checkmarketplace: Extension ${extension_name} is updated to version '${cur_version}.'" + break + else + echo "checkmarketplace: Current version '${cur_version}' does not match expected version '${VERSION}'. Retrying..." + fi + sleep 120 # Wait for 2 minutes before retrying + done diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts index a087abf395c..72d0746cdcf 100644 --- a/packages/amazonq/src/lsp/lspInstaller.ts +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -12,6 +12,7 @@ import { fs, LspResolution, getNodeExecutableName, + cleanLspDownloads, } from 'aws-core-vscode/shared' import path from 'path' @@ -46,7 +47,7 @@ export class AmazonQLSPResolver implements LspResolver { const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`) await fs.chmod(nodePath, 0o755) - // TODO Cleanup old versions of language servers + await cleanLspDownloads(manifest.versions, path.dirname(installationResult.assetDirectory)) return { ...installationResult, resourcePaths: { diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index bdccd0c9b6d..8f5de139912 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -20,7 +20,7 @@ "AWS.configuration.description.suppressPrompts": "Prompts which ask for confirmation. Checking an item suppresses the prompt.", "AWS.configuration.enableCodeLenses": "Enable SAM hints in source code and template.yaml files", "AWS.configuration.description.resources.enabledResources": "AWS resources to display in the 'Resources' portion of the explorer.", - "AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component.\n * `ec2RemoteConnect` - Allows interfacing with EC2 instances with options to start, stop, and establish remote connections. Remote connections are done over SSM and can be through a terminal or a remote VSCode window.", + "AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component.", "AWS.stepFunctions.asl.format.enable.desc": "Enables the default formatter used with Amazon States Language files", "AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", "AWS.configuration.description.awssam.debug.api": "API Gateway configuration", @@ -295,6 +295,7 @@ "AWS.codewhisperer.customization.notification.new_customizations.learn_more": "Learn More", "AWS.amazonq.title": "Amazon Q", "AWS.amazonq.chat": "Chat", + "AWS.amazonq.chat.workspacecontext.enable.message": "Amazon Q: Workspace index is now enabled. You can disable it from Amazon Q settings.", "AWS.amazonq.security": "Code Issues", "AWS.amazonq.login": "Login", "AWS.amazonq.learnMore": "Learn More About Amazon Q", @@ -404,6 +405,7 @@ "AWS.amazonq.doc.pillText.reject": "Reject", "AWS.amazonq.doc.pillText.makeChanges": "Make changes", "AWS.amazonq.inline.invokeChat": "Inline chat", + "AWS.amazonq.opensettings:": "Open settings", "AWS.toolkit.lambda.walkthrough.quickpickTitle": "Application Builder Walkthrough", "AWS.toolkit.lambda.walkthrough.title": "Get started building your application", "AWS.toolkit.lambda.walkthrough.description": "Your quick guide to build an application visually, iterate locally, and deploy to the cloud!", diff --git a/packages/core/src/amazonq/lsp/workspaceInstaller.ts b/packages/core/src/amazonq/lsp/workspaceInstaller.ts index 6c5d869a70a..c4c688d7bc1 100644 --- a/packages/core/src/amazonq/lsp/workspaceInstaller.ts +++ b/packages/core/src/amazonq/lsp/workspaceInstaller.ts @@ -10,6 +10,7 @@ import { LanguageServerResolver } from '../../shared/lsp/lspResolver' import { Range } from 'semver' import { getNodeExecutableName } from '../../shared/lsp/utils/platform' import { fs } from '../../shared/fs/fs' +import { cleanLspDownloads } from '../../shared' const manifestUrl = 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json' // this LSP client in Q extension is only going to work with these LSP server versions @@ -30,7 +31,7 @@ export class WorkspaceLSPResolver implements LspResolver { const nodePath = path.join(installationResult.assetDirectory, nodeName) await fs.chmod(nodePath, 0o755) - // TODO Cleanup old versions of language servers + await cleanLspDownloads(manifest.versions, path.basename(installationResult.assetDirectory)) return { ...installationResult, resourcePaths: { diff --git a/packages/core/src/awsexplorer/regionNode.ts b/packages/core/src/awsexplorer/regionNode.ts index a5c5bafe104..575da091d04 100644 --- a/packages/core/src/awsexplorer/regionNode.ts +++ b/packages/core/src/awsexplorer/regionNode.ts @@ -30,7 +30,6 @@ import { getEcsRootNode } from '../awsService/ecs/model' import { compareTreeItems, TreeShim } from '../shared/treeview/utils' import { Ec2ParentNode } from '../awsService/ec2/explorer/ec2ParentNode' import { Ec2Client } from '../shared/clients/ec2Client' -import { Experiments } from '../shared/settings' interface ServiceNode { allRegions?: boolean @@ -64,7 +63,6 @@ const serviceCandidates: ServiceNode[] = [ }, { serviceId: 'ec2', - when: () => Experiments.instance.isExperimentEnabled('ec2RemoteConnect'), createFn: (regionCode: string, partitionId: string) => new Ec2ParentNode(regionCode, partitionId, new Ec2Client(regionCode)), }, diff --git a/packages/core/src/shared/clients/s3Client.ts b/packages/core/src/shared/clients/s3Client.ts index ae530a68fa3..f4375cc6755 100644 --- a/packages/core/src/shared/clients/s3Client.ts +++ b/packages/core/src/shared/clients/s3Client.ts @@ -465,7 +465,7 @@ export class DefaultS3Client { * Set '' as the default prefix to ensure that the bucket's content will be displayed * when the user has at least list access to the root of the bucket. * https://github.com/aws/aws-toolkit-vscode/issues/4643 - * @default '' + * @default '' */ Prefix: request.folderPath ?? defaultPrefix, ContinuationToken: request.continuationToken, diff --git a/packages/core/src/shared/featureConfig.ts b/packages/core/src/shared/featureConfig.ts index 73eea42bbf3..1ef07968198 100644 --- a/packages/core/src/shared/featureConfig.ts +++ b/packages/core/src/shared/featureConfig.ts @@ -10,6 +10,7 @@ import { ListFeatureEvaluationsResponse, } from '../codewhisperer/client/codewhispereruserclient' import * as vscode from 'vscode' +import * as nls from 'vscode-nls' import { codeWhispererClient as client } from '../codewhisperer/client/codewhisperer' import { AuthUtil } from '../codewhisperer/util/authUtil' import { getLogger } from './logger' @@ -19,7 +20,9 @@ import globals from './extensionGlobals' import { getClientId, getOperatingSystem } from './telemetry/util' import { extensionVersion } from './vscode/env' import { telemetry } from './telemetry' -import { Auth } from '../auth' +import { Commands } from './vscode/commands2' + +const localize = nls.loadMessageBundle() export class FeatureContext { constructor( @@ -35,6 +38,7 @@ export const Features = { customizationArnOverride: 'customizationArnOverride', dataCollectionFeature: 'IDEProjectContextDataCollection', projectContextFeature: 'ProjectContextV2', + workspaceContextFeature: 'WorkspaceContext', test: 'testFeature', } as const @@ -83,6 +87,21 @@ export class FeatureConfigProvider { } } + getWorkspaceContextGroup(): 'control' | 'treatment' { + const variation = this.featureConfigs.get(Features.projectContextFeature)?.variation + + switch (variation) { + case 'CONTROL': + return 'control' + + case 'TREATMENT': + return 'treatment' + + default: + return 'control' + } + } + public async listFeatureEvaluations(): Promise { const request: ListFeatureEvaluationsRequest = { userContext: { @@ -154,12 +173,26 @@ export class FeatureConfigProvider { await vscode.commands.executeCommand('aws.amazonq.refreshStatusBar') } } - if (Auth.instance.isInternalAmazonUser()) { + if (this.getWorkspaceContextGroup() === 'treatment') { // Enable local workspace index by default only once, for Amzn users. const isSet = globals.globalState.get('aws.amazonq.workspaceIndexToggleOn') || false if (!isSet) { await CodeWhispererSettings.instance.enableLocalIndex() globals.globalState.tryUpdate('aws.amazonq.workspaceIndexToggleOn', true) + + await vscode.window + .showInformationMessage( + localize( + 'AWS.amazonq.chat.workspacecontext.enable.message', + 'Amazon Q: Workspace index is now enabled. You can disable it from Amazon Q settings.' + ), + localize('AWS.amazonq.opensettings', 'Open settings') + ) + .then((r) => { + if (r === 'Open settings') { + void Commands.tryExecute('aws.amazonq.configure').then() + } + }) } } } catch (e) { diff --git a/packages/core/src/shared/index.ts b/packages/core/src/shared/index.ts index 7cdf3ad12f0..236badac4be 100644 --- a/packages/core/src/shared/index.ts +++ b/packages/core/src/shared/index.ts @@ -63,6 +63,7 @@ export { TabTypeDataMap } from '../amazonq/webview/ui/tabs/constants' export * from './lsp/manifestResolver' export * from './lsp/lspResolver' export * from './lsp/types' +export * from './lsp/utils/cleanup' export { default as request } from './request' export * from './lsp/utils/platform' export * as processUtils from './utilities/processUtils' diff --git a/packages/core/src/shared/lsp/lspResolver.ts b/packages/core/src/shared/lsp/lspResolver.ts index 4959f675986..5a907b96a02 100644 --- a/packages/core/src/shared/lsp/lspResolver.ts +++ b/packages/core/src/shared/lsp/lspResolver.ts @@ -338,9 +338,13 @@ export class LanguageServerResolver { return version.targets.find((x) => x.arch === arch && x.platform === platform) } + // lazy calls to `getApplicationSupportFolder()` to avoid failure on windows. + public static get defaultDir() { + return path.join(getApplicationSupportFolder(), `aws/toolkits/language-servers`) + } + defaultDownloadFolder() { - const applicationSupportFolder = getApplicationSupportFolder() - return path.join(applicationSupportFolder, `aws/toolkits/language-servers/${this.lsName}`) + return path.join(LanguageServerResolver.defaultDir, `${this.lsName}`) } private getDownloadDirectory(version: string) { diff --git a/packages/core/src/shared/lsp/utils/cleanup.ts b/packages/core/src/shared/lsp/utils/cleanup.ts new file mode 100644 index 00000000000..874f56e46ff --- /dev/null +++ b/packages/core/src/shared/lsp/utils/cleanup.ts @@ -0,0 +1,41 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from 'path' +import { LspVersion } from '../types' +import { fs } from '../../../shared/fs/fs' +import { partition } from '../../../shared/utilities/tsUtils' +import { sort } from 'semver' + +async function getDownloadedVersions(installLocation: string) { + return (await fs.readdir(installLocation)).map(([f, _], __) => f) +} + +function isDelisted(manifestVersions: LspVersion[], targetVersion: string): boolean { + return manifestVersions.find((v) => v.serverVersion === targetVersion)?.isDelisted ?? false +} + +/** + * Delete all delisted versions and keep the two newest versions that remain + * @param manifest + * @param downloadDirectory + */ +export async function cleanLspDownloads(manifestVersions: LspVersion[], downloadDirectory: string): Promise { + const downloadedVersions = await getDownloadedVersions(downloadDirectory) + const [delistedVersions, remainingVersions] = partition(downloadedVersions, (v: string) => + isDelisted(manifestVersions, v) + ) + for (const v of delistedVersions) { + await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true }) + } + + if (remainingVersions.length <= 2) { + return + } + + for (const v of sort(remainingVersions).slice(0, -2)) { + await fs.delete(path.join(downloadDirectory, v), { force: true, recursive: true }) + } +} diff --git a/packages/core/src/shared/settings-toolkit.gen.ts b/packages/core/src/shared/settings-toolkit.gen.ts index 9b493670266..8e4cc453d03 100644 --- a/packages/core/src/shared/settings-toolkit.gen.ts +++ b/packages/core/src/shared/settings-toolkit.gen.ts @@ -42,8 +42,7 @@ export const toolkitSettings = { }, "aws.experiments": { "jsonResourceModification": {}, - "amazonqLSP": {}, - "ec2RemoteConnect": {} + "amazonqLSP": {} }, "aws.resources.enabledResources": {}, "aws.lambda.recentlyUploaded": {}, diff --git a/packages/core/src/shared/utilities/tsUtils.ts b/packages/core/src/shared/utilities/tsUtils.ts index e4fbf5a2b3f..2e4838c5b7a 100644 --- a/packages/core/src/shared/utilities/tsUtils.ts +++ b/packages/core/src/shared/utilities/tsUtils.ts @@ -94,6 +94,21 @@ export function createFactoryFunction any>(cto return (...args) => new ctor(...args) } +/** + * Split a list into two sublists based on the result of a predicate. + * @param lst list to split + * @param pred predicate to apply to each element + * @returns two nested lists, where for all items x in the left sublist, pred(x) returns true. The remaining elements are in the right sublist. + */ +export function partition(lst: T[], pred: (arg: T) => boolean): [T[], T[]] { + return lst.reduce( + ([leftAcc, rightAcc], item) => { + return pred(item) ? [[...leftAcc, item], rightAcc] : [leftAcc, [...rightAcc, item]] + }, + [[], []] as [T[], T[]] + ) +} + type NoSymbols = { [Property in keyof T]: Property extends symbol ? never : Property }[keyof T] export type InterfaceNoSymbol = Pick> /** diff --git a/packages/core/src/test/shared/lsp/utils/cleanup.test.ts b/packages/core/src/test/shared/lsp/utils/cleanup.test.ts new file mode 100644 index 00000000000..377039566de --- /dev/null +++ b/packages/core/src/test/shared/lsp/utils/cleanup.test.ts @@ -0,0 +1,93 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Uri } from 'vscode' +import { cleanLspDownloads, fs } from '../../../../shared' +import { createTestWorkspaceFolder } from '../../../testUtil' +import path from 'path' +import assert from 'assert' + +async function fakeInstallVersion(version: string, installationDir: string): Promise { + const versionDir = path.join(installationDir, version) + await fs.mkdir(versionDir) + await fs.writeFile(path.join(versionDir, 'file.txt'), 'content') +} + +async function fakeInstallVersions(versions: string[], installationDir: string): Promise { + for (const v of versions) { + await fakeInstallVersion(v, installationDir) + } +} + +describe('cleanLSPDownloads', function () { + let installationDir: Uri + + before(async function () { + installationDir = (await createTestWorkspaceFolder()).uri + }) + + afterEach(async function () { + const files = await fs.readdir(installationDir.fsPath) + for (const [name, _type] of files) { + await fs.delete(path.join(installationDir.fsPath, name), { force: true, recursive: true }) + } + }) + + after(async function () { + await fs.delete(installationDir, { force: true, recursive: true }) + }) + + it('keeps two newest versions', async function () { + await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath) + await cleanLspDownloads([], installationDir.fsPath) + + const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename) + assert.strictEqual(result.length, 2) + assert.ok(result.includes('2.1.1')) + assert.ok(result.includes('1.1.1')) + }) + + it('deletes delisted versions', async function () { + await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath) + await cleanLspDownloads([{ serverVersion: '1.1.1', isDelisted: true, targets: [] }], installationDir.fsPath) + + const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename) + assert.strictEqual(result.length, 2) + assert.ok(result.includes('2.1.1')) + assert.ok(result.includes('1.0.1')) + }) + + it('handles case where less than 2 versions are not delisted', async function () { + await fakeInstallVersions(['1.0.0', '1.0.1', '1.1.1', '2.1.1'], installationDir.fsPath) + await cleanLspDownloads( + [ + { serverVersion: '1.1.1', isDelisted: true, targets: [] }, + { serverVersion: '2.1.1', isDelisted: true, targets: [] }, + { serverVersion: '1.0.0', isDelisted: true, targets: [] }, + ], + installationDir.fsPath + ) + + const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename) + assert.strictEqual(result.length, 1) + assert.ok(result.includes('1.0.1')) + }) + + it('handles case where less than 2 versions exist', async function () { + await fakeInstallVersions(['1.0.0'], installationDir.fsPath) + await cleanLspDownloads([], installationDir.fsPath) + + const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename) + assert.strictEqual(result.length, 1) + }) + + it('does not install delisted version when no other option exists', async function () { + await fakeInstallVersions(['1.0.0'], installationDir.fsPath) + await cleanLspDownloads([{ serverVersion: '1.0.0', isDelisted: true, targets: [] }], installationDir.fsPath) + + const result = (await fs.readdir(installationDir.fsPath)).map(([filename, _filetype], _index) => filename) + assert.strictEqual(result.length, 0) + }) +}) diff --git a/packages/core/src/test/shared/utilities/tsUtils.test.ts b/packages/core/src/test/shared/utilities/tsUtils.test.ts new file mode 100644 index 00000000000..eb04da035e5 --- /dev/null +++ b/packages/core/src/test/shared/utilities/tsUtils.test.ts @@ -0,0 +1,16 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { partition } from '../../../shared/utilities/tsUtils' +import assert from 'assert' + +describe('partition', function () { + it('should split the list according to predicate', function () { + const items = [1, 2, 3, 4, 5, 6, 7, 8] + const [even, odd] = partition(items, (i) => i % 2 === 0) + assert.deepStrictEqual(even, [2, 4, 6, 8]) + assert.deepStrictEqual(odd, [1, 3, 5, 7]) + }) +}) diff --git a/packages/toolkit/.changes/next-release/Feature-a7fb8317-77bc-4459-a278-2bc98fd0a5cf.json b/packages/toolkit/.changes/next-release/Feature-a7fb8317-77bc-4459-a278-2bc98fd0a5cf.json new file mode 100644 index 00000000000..ea12f9b0fe3 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-a7fb8317-77bc-4459-a278-2bc98fd0a5cf.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "EC2 is now available in AWS Explorer:\n\n1. Remote-connect VSCode to your EC2 instances.\n2. Open terminal to your EC2 instances.\n3. Start, stop, and visit the Launch page." +} diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 3086b679f47..9b51d043d61 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -240,8 +240,7 @@ "type": "object", "markdownDescription": "%AWS.configuration.description.experiments%", "default": { - "jsonResourceModification": false, - "ec2RemoteConnect": false + "jsonResourceModification": false }, "properties": { "jsonResourceModification": { @@ -251,10 +250,6 @@ "amazonqLSP": { "type": "boolean", "default": false - }, - "ec2RemoteConnect": { - "type": "boolean", - "default": false } }, "additionalProperties": false @@ -1210,30 +1205,6 @@ { "command": "aws.toolkit.auth.manageConnections" }, - { - "command": "aws.ec2.openRemoteConnection", - "when": "config.aws.experiments.ec2RemoteConnect" - }, - { - "command": "aws.ec2.openTerminal", - "when": "config.aws.experiments.ec2RemoteConnect" - }, - { - "command": "aws.ec2.linkToLaunch", - "when": "config.aws.experiments.ec2RemoteConnect" - }, - { - "command": "aws.ec2.startInstance", - "when": "config.aws.experiments.ec2RemoteConnect" - }, - { - "command": "aws.ec2.stopInstance", - "when": "config.aws.experiments.ec2RemoteConnect" - }, - { - "command": "aws.ec2.rebootInstance", - "when": "config.aws.experiments.ec2RemoteConnect" - }, { "command": "aws.dev.openMenu", "when": "aws.isDevMode || isCloud9" @@ -1457,66 +1428,66 @@ { "command": "aws.ec2.openTerminal", "group": "0@1", - "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/" }, { "command": "aws.ec2.openTerminal", "group": "inline@1", - "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/" }, { "command": "aws.ec2.linkToLaunch", "group": "0@0", - "when": "viewItem =~ /^(awsEc2ParentNode)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2ParentNode)$/" }, { "command": "aws.ec2.linkToLaunch", "group": "inline@0", - "when": "viewItem =~ /^(awsEc2ParentNode)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2ParentNode)$/" }, { "command": "aws.ec2.openRemoteConnection", "group": "0@1", - "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/" }, { "command": "aws.ec2.openRemoteConnection", "group": "inline@1", - "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/ && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem =~ /^(awsEc2(Parent|Running)Node)$/" }, { "command": "aws.ec2.startInstance", "group": "0@1", - "when": "viewItem == awsEc2StoppedNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2StoppedNode" }, { "command": "aws.ec2.startInstance", "group": "inline@1", - "when": "viewItem == awsEc2StoppedNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2StoppedNode" }, { "command": "aws.ec2.stopInstance", "group": "0@1", - "when": "viewItem == awsEc2RunningNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2RunningNode" }, { "command": "aws.ec2.stopInstance", "group": "inline@1", - "when": "viewItem == awsEc2RunningNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2RunningNode" }, { "command": "aws.ec2.rebootInstance", "group": "0@1", - "when": "viewItem == awsEc2RunningNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2RunningNode" }, { "command": "aws.ec2.rebootInstance", "group": "inline@1", - "when": "viewItem == awsEc2RunningNode && config.aws.experiments.ec2RemoteConnect" + "when": "viewItem == awsEc2RunningNode" }, { "command": "aws.ec2.copyInstanceId", - "when": "view == aws.explorer && viewItem =~ /^(awsEc2(Running|Stopped|Pending)Node)$/ && config.aws.experiments.ec2RemoteConnect", + "when": "view == aws.explorer && viewItem =~ /^(awsEc2(Running|Stopped|Pending)Node)$/", "group": "2@0" }, {