Skip to content

Commit 751bc22

Browse files
authored
Feat: add sagemaker-extensions-sync
Feat: add sagemaker-extensions-sync
2 parents 34728b6 + 51794a2 commit 751bc22

File tree

17 files changed

+887
-54
lines changed

17 files changed

+887
-54
lines changed

patched-vscode/build/gulpfile.extensions.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ const compilations = [
6262
'extensions/simple-browser/tsconfig.json',
6363
'extensions/sagemaker-extension/tsconfig.json',
6464
'extensions/sagemaker-idle-extension/tsconfig.json',
65+
'extensions/sagemaker-extensions-sync/tsconfig.json',
6566
'extensions/sagemaker-terminal-crash-mitigation/tsconfig.json',
6667
'extensions/sagemaker-open-notebook-extension/tsconfig.json',
6768
'extensions/sagemaker-ui-dark-theme/tsconfig.json',
68-
'extensions/post-startup-notifications/tsconfig.json',
69+
'extensions/post-startup-notifications/tsconfig.json',
6970
'extensions/tunnel-forwarding/tsconfig.json',
7071
'extensions/typescript-language-features/test-workspace/tsconfig.json',
7172
'extensions/typescript-language-features/web/tsconfig.json',

patched-vscode/build/npm/dirs.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ const dirs = [
4040
'extensions/php-language-features',
4141
'extensions/references-view',
4242
'extensions/sagemaker-extension',
43+
'extensions/sagemaker-extensions-sync',
4344
'extensions/sagemaker-idle-extension',
4445
'extensions/sagemaker-terminal-crash-mitigation',
4546
'extensions/sagemaker-open-notebook-extension',
4647
'extensions/sagemaker-ui-dark-theme',
47-
'extensions/post-startup-notifications',
48+
'extensions/post-startup-notifications',
4849
'extensions/search-result',
4950
'extensions/simple-browser',
5051
'extensions/tunnel-forwarding',
Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
{
2-
// See http://go.microsoft.com/fwlink/?LinkId=827846
3-
// for the documentation about the extensions.json format
4-
"recommendations": [
5-
"dbaeumer.vscode-eslint",
6-
"amodio.tsl-problem-matcher",
7-
"ms-vscode.extension-test-runner"
8-
]
9-
}
2+
// See http://go.microsoft.com/fwlink/?LinkId=827846
3+
// for the documentation about the extensions.json format
4+
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher", "ms-vscode.extension-test-runner"]
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.vscode/**
2+
.vscode-test/**
3+
out/test/**
4+
out/**
5+
test/**
6+
src/**
7+
tsconfig.json
8+
out/test/**
9+
out/**
10+
cgmanifest.json
11+
yarn.lock
12+
preview-src/**
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SageMaker Code Editor Extensions Sync
2+
3+
Notifies users if the extensions directory is missing pre-packaged extensions from SageMaker Distribution and give them the option to sync them.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright Amazon.com Inc. or its affiliates. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
//@ts-check
7+
8+
'use strict';
9+
10+
const withBrowserDefaults = require('../shared.webpack.config').browser;
11+
12+
module.exports = withBrowserDefaults({
13+
context: __dirname,
14+
entry: {
15+
extension: './src/extension.ts'
16+
},
17+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright Amazon.com Inc. or its affiliates. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
//@ts-check
7+
8+
'use strict';
9+
10+
const withDefaults = require('../shared.webpack.config');
11+
12+
module.exports = withDefaults({
13+
context: __dirname,
14+
resolve: {
15+
mainFields: ['module', 'main']
16+
},
17+
entry: {
18+
extension: './src/extension.ts',
19+
}
20+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "sagemaker-extensions-sync",
3+
"displayName": "SageMaker Extensions Sync",
4+
"description": "Sync pre-packaged extensions from SageMaker Distribution",
5+
"extensionKind": [
6+
"workspace"
7+
],
8+
"version": "1.0.0",
9+
"publisher": "sagemaker",
10+
"license": "MIT",
11+
"engines": {
12+
"vscode": "^1.70.0"
13+
},
14+
"main": "./out/extension",
15+
"categories": [
16+
"Other"
17+
],
18+
"activationEvents": [
19+
"*"
20+
],
21+
"capabilities": {
22+
"virtualWorkspaces": true,
23+
"untrustedWorkspaces": {
24+
"supported": true
25+
}
26+
},
27+
"contributes": {
28+
"commands": [
29+
{
30+
"command": "extensions-sync.syncExtensions",
31+
"title": "Sync Extensions from SageMaker Distribution",
32+
"category": "Extensions Sync"
33+
}
34+
]
35+
},
36+
"scripts": {
37+
"compile": "gulp compile-extension:sagemaker-extensions-sync",
38+
"watch": "npm run build-preview && gulp watch-extension:sagemaker-extensions-sync",
39+
"vscode:prepublish": "npm run build-ext",
40+
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:sagemaker-idle-extension ./tsconfig.json"
41+
},
42+
"dependencies": {},
43+
"repository": {}
44+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// constants
2+
export const PERSISTENT_VOLUME_EXTENSIONS_DIR = "/home/sagemaker-user/sagemaker-code-editor-server-data/extensions";
3+
export const IMAGE_EXTENSIONS_DIR = "/opt/amazon/sagemaker/sagemaker-code-editor-server-data/extensions";
4+
export const LOG_PREFIX = "[sagemaker-extensions-sync]";
5+
6+
export class ExtensionInfo {
7+
constructor(
8+
public name: string,
9+
public publisher: string,
10+
public version: string,
11+
public path: string | null
12+
) {}
13+
14+
get identifier(): string {
15+
return `${this.publisher}.${this.name}@${this.version}`;
16+
}
17+
18+
toString(): string {
19+
return `ExtensionInfo: ${this.identifier} (${this.path})`;
20+
}
21+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import * as process from "process";
2+
import * as vscode from 'vscode';
3+
4+
import {
5+
ExtensionInfo,
6+
IMAGE_EXTENSIONS_DIR,
7+
LOG_PREFIX,
8+
PERSISTENT_VOLUME_EXTENSIONS_DIR,
9+
} from "./constants"
10+
11+
import {
12+
getExtensionsFromDirectory,
13+
getInstalledExtensions,
14+
installExtension,
15+
refreshExtensionsMetadata } from "./utils"
16+
17+
export async function activate() {
18+
19+
// this extension will only activate within a sagemaker app
20+
const isSageMakerApp = !!process.env?.SAGEMAKER_APP_TYPE_LOWERCASE;
21+
if (!isSageMakerApp) {
22+
return;
23+
}
24+
25+
// get installed extensions. this could be different from pvExtensions b/c vscode sometimes doesn't delete the assets
26+
// for an old extension when uninstalling or changing versions
27+
const installedExtensions = new Set(await getInstalledExtensions());
28+
console.log(`${LOG_PREFIX} Found installed extensions: `, Array.from(installedExtensions));
29+
30+
const prePackagedExtensions: ExtensionInfo[] = await getExtensionsFromDirectory(IMAGE_EXTENSIONS_DIR);
31+
const prePackagedExtensionsById: Record<string, ExtensionInfo> = {};
32+
prePackagedExtensions.forEach(extension => {
33+
prePackagedExtensionsById[extension.identifier] = extension;
34+
});
35+
36+
console.log(`${LOG_PREFIX} Found pre-packaged extensions: `, prePackagedExtensions);
37+
38+
const pvExtensions = await getExtensionsFromDirectory(PERSISTENT_VOLUME_EXTENSIONS_DIR);
39+
const pvExtensionsByName: Record<string, ExtensionInfo> = {};
40+
const pvExtensionsById: Record<string, ExtensionInfo> = {};
41+
pvExtensions.forEach(extension => {
42+
if (installedExtensions.has(extension.identifier)) { // only index extensions that are installed
43+
pvExtensionsByName[extension.name] = extension;
44+
pvExtensionsById[extension.identifier] = extension;
45+
}
46+
});
47+
console.log(`${LOG_PREFIX} Found installed extensions in persistent volume: `, pvExtensionsById);
48+
49+
// check each pre-packaged extension, record if it is not in installed extensions or version mismatch
50+
// store unsynced extensions as {identifier pre-packaged ext: currently installed version}
51+
const unsyncedExtensions: Record<string, string | null> = {}
52+
prePackagedExtensions.forEach(extension => {
53+
const id = extension.identifier;
54+
if (!(installedExtensions.has(id))){
55+
unsyncedExtensions[id] = pvExtensionsByName[extension.name]?.version ?? null;
56+
}
57+
});
58+
console.log(`${LOG_PREFIX} Unsynced extensions: `, unsyncedExtensions);
59+
60+
if (Object.keys(unsyncedExtensions).length !== 0) {
61+
const selection = await vscode.window.showWarningMessage(
62+
'Warning: You have unsynchronized extensions from SageMaker Distribution \
63+
which could result in incompatibilities with Code Editor. Do you want to install them?',
64+
"Synchronize Extensions", "Dismiss");
65+
66+
if (selection === "Synchronize Extensions") {
67+
const quickPick = vscode.window.createQuickPick();
68+
quickPick.items = Object.keys(unsyncedExtensions).map(extensionId => ({
69+
label: extensionId,
70+
description: unsyncedExtensions[extensionId] ? `Currently installed version: ${unsyncedExtensions[extensionId]}` : undefined,
71+
}));
72+
quickPick.placeholder = 'Select extensions to install';
73+
quickPick.canSelectMany = true;
74+
quickPick.ignoreFocusOut = true;
75+
76+
quickPick.onDidAccept(async () => {
77+
const selectedExtensions = quickPick.selectedItems.map(item => item.label);
78+
79+
for (const extensionId of selectedExtensions) {
80+
const extensionName = prePackagedExtensionsById[extensionId].name;
81+
await installExtension(prePackagedExtensionsById[extensionId], pvExtensionsByName[extensionName]);
82+
}
83+
await refreshExtensionsMetadata();
84+
85+
quickPick.hide();
86+
await vscode.window.showInformationMessage(
87+
'Extensions have been installed. \nWould you like to reload the window?',
88+
{ modal: true },
89+
'Reload'
90+
).then(selection => {
91+
if (selection === 'Reload') {
92+
vscode.commands.executeCommand('workbench.action.reloadWindow');
93+
}
94+
});
95+
});
96+
97+
quickPick.show();
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)