Skip to content

Commit b338f57

Browse files
committed
built in extensions control file
1 parent f13654c commit b338f57

6 files changed

Lines changed: 190 additions & 47 deletions

File tree

build/gulpfile.vscode.js

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const BUNDLED_FILE_HEADER = [
9292
' *--------------------------------------------------------*/'
9393
].join('\n');
9494

95-
const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages: []);
95+
const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []);
9696

9797
gulp.task('clean-optimized-vscode', util.rimraf('out-vscode'));
9898
gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({
@@ -409,7 +409,7 @@ gulp.task('vscode-translations-push', function () {
409409
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
410410
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
411411
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
412-
).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
412+
).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
413413
});
414414

415415
gulp.task('vscode-translations-push-test', function () {
@@ -422,7 +422,7 @@ gulp.task('vscode-translations-push-test', function () {
422422
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
423423
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
424424
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
425-
).pipe(vfs.dest('../vscode-transifex-input'));
425+
).pipe(vfs.dest('../vscode-transifex-input'));
426426
});
427427

428428
gulp.task('vscode-translations-pull', function () {
@@ -597,17 +597,3 @@ gulp.task('generate-vscode-configuration', () => {
597597
console.error(e.toString());
598598
});
599599
});
600-
601-
//#region Built-In Extensions
602-
gulp.task('clean-builtin-extensions', util.rimraf('.build/builtInExtensions'));
603-
gulp.task('download-builtin-extensions', ['clean-builtin-extensions'], function () {
604-
const marketplaceExtensions = es.merge(...builtInExtensions.map(extension => {
605-
return ext.fromMarketplace(extension.name, extension.version)
606-
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
607-
}));
608-
609-
return marketplaceExtensions
610-
.pipe(util.setExecutableBit(['**/*.sh']))
611-
.pipe(vfs.dest('.build/builtInExtensions'));
612-
});
613-
//#endregion

build/lib/builtInExtensions.js

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,116 @@
77

88
const fs = require('fs');
99
const path = require('path');
10+
const mkdirp = require('mkdirp');
11+
const rimraf = require('rimraf');
12+
const es = require('event-stream');
13+
const rename = require('gulp-rename');
14+
const vfs = require('vinyl-fs');
15+
const ext = require('./extensions');
16+
const util = require('gulp-util');
17+
1018
const root = path.dirname(path.dirname(__dirname));
19+
const builtInExtensions = require('../builtInExtensions');
20+
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
21+
22+
function getExtensionPath(extension) {
23+
return path.join(root, '.build', 'builtInExtensions', extension.name);
24+
}
1125

1226
function isUpToDate(extension) {
13-
const packagePath = path.join(root, '.build', 'builtInExtensions', extension.name, 'package.json');
27+
const packagePath = path.join(getExtensionPath(extension), 'package.json');
28+
1429
if (!fs.existsSync(packagePath)) {
1530
return false;
1631
}
32+
1733
const packageContents = fs.readFileSync(packagePath);
34+
1835
try {
1936
const diskVersion = JSON.parse(packageContents).version;
2037
return (diskVersion === extension.version);
21-
} catch(err) {
38+
} catch (err) {
2239
return false;
2340
}
2441
}
2542

26-
const builtInExtensions = require('../builtInExtensions');
27-
builtInExtensions.forEach((extension) => {
28-
if (!isUpToDate(extension)) {
29-
process.exit(1);
43+
function syncMarketplaceExtension(extension) {
44+
if (isUpToDate(extension)) {
45+
util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎'));
46+
return es.readArray([]);
47+
}
48+
49+
rimraf.sync(getExtensionPath(extension));
50+
51+
return ext.fromMarketplace(extension.name, extension.version)
52+
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`))
53+
.pipe(vfs.dest('.build/builtInExtensions'))
54+
.on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎')));
55+
}
56+
57+
function syncExtension(extension, controlState) {
58+
switch (controlState) {
59+
case 'disabled':
60+
util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name));
61+
rimraf.sync(getExtensionPath(extension));
62+
return es.readArray([]);
63+
64+
case 'marketplace':
65+
return syncMarketplaceExtension(extension);
66+
67+
default:
68+
if (!fs.existsSync(controlState)) {
69+
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`));
70+
return es.readArray([]);
71+
72+
} else if (!fs.existsSync(path.join(controlState, 'package.json'))) {
73+
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`));
74+
return es.readArray([]);
75+
}
76+
77+
util.log(util.colors.blue('[local]'), `${extension.name}: ${controlState}`, util.colors.green('✔︎'));
78+
return es.readArray([]);
3079
}
31-
});
32-
process.exit(0);
80+
}
81+
82+
function readControlFile() {
83+
try {
84+
return JSON.parse(fs.readFileSync(controlFilePath, 'utf8'));
85+
} catch (err) {
86+
return {};
87+
}
88+
}
89+
90+
function writeControlFile(control) {
91+
mkdirp.sync(path.dirname(controlFilePath));
92+
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
93+
}
94+
95+
function main() {
96+
util.log('Syncronizing built-in extensions...');
97+
util.log('Control file:', controlFilePath);
98+
99+
const control = readControlFile();
100+
const streams = [];
101+
102+
for (const extension of builtInExtensions) {
103+
let controlState = control[extension.name] || 'marketplace';
104+
control[extension.name] = controlState;
105+
106+
streams.push(syncExtension(extension, controlState));
107+
}
108+
109+
writeControlFile(control);
110+
111+
es.merge(streams)
112+
.on('error', err => {
113+
console.error(err);
114+
process.exit(1);
115+
})
116+
.on('end', () => {
117+
util.log(`${streams.length} built-in extensions processed.`);
118+
process.exit(0);
119+
});
120+
}
121+
122+
main();

scripts/code.bat

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ set CODE=".build\electron\%NAMESHORT%"
1717
node build\lib\electron.js
1818
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
1919

20-
:: Get built-in extensions
20+
:: Sync built-in extensions
2121
node build\lib\builtInExtensions.js
22-
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js download-builtin-extensions
2322

2423
:: Build
2524
if not exist out node .\node_modules\gulp\bin\gulp.js compile

scripts/code.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ function code() {
2424
# Get electron
2525
node build/lib/electron.js || ./node_modules/.bin/gulp electron
2626

27-
# Get built-in extensions
28-
node build/lib/builtInExtensions.js || ./node_modules/.bin/gulp download-builtin-extensions
27+
# Sync built-in extensions
28+
node build/lib/builtInExtensions.js
2929

3030
# Build
3131
test -d out || ./node_modules/.bin/gulp compile

src/vs/workbench/services/extensions/electron-browser/extensionPoints.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,25 @@ export class ExtensionScannerInput {
289289
}
290290
}
291291

292+
export interface IExtensionReference {
293+
name: string;
294+
path: string;
295+
}
296+
297+
export interface IExtensionResolver {
298+
resolveExtensions(): TPromise<IExtensionReference[]>;
299+
}
300+
301+
class DefaultExtensionResolver implements IExtensionResolver {
302+
303+
constructor(private root: string) { }
304+
305+
resolveExtensions(): TPromise<IExtensionReference[]> {
306+
return pfs.readDirsInDir(this.root)
307+
.then(folders => folders.map(name => ({ name, path: join(this.root, name) })));
308+
}
309+
}
310+
292311
export class ExtensionScanner {
293312

294313
/**
@@ -318,10 +337,14 @@ export class ExtensionScanner {
318337
/**
319338
* Scan a list of extensions defined in `absoluteFolderPath`
320339
*/
321-
public static async scanExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
340+
public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver?: IExtensionResolver): TPromise<IExtensionDescription[]> {
322341
const absoluteFolderPath = input.absoluteFolderPath;
323342
const isBuiltin = input.isBuiltin;
324343

344+
if (!resolver) {
345+
resolver = new DefaultExtensionResolver(absoluteFolderPath);
346+
}
347+
325348
try {
326349
let obsolete: { [folderName: string]: boolean; } = {};
327350
if (!isBuiltin) {
@@ -333,38 +356,35 @@ export class ExtensionScanner {
333356
}
334357
}
335358

336-
const rawFolders = await pfs.readDirsInDir(absoluteFolderPath);
359+
let refs = await resolver.resolveExtensions();
337360

338361
// Ensure the same extension order
339-
rawFolders.sort();
362+
refs.sort((a, b) => a.name < b.name ? -1 : 1);
340363

341-
let folders: string[] = null;
342-
if (isBuiltin) {
343-
folders = rawFolders;
344-
} else {
364+
if (!isBuiltin) {
345365
// TODO: align with extensionsService
346-
const nonGallery: string[] = [];
347-
const gallery: string[] = [];
366+
const nonGallery: IExtensionReference[] = [];
367+
const gallery: IExtensionReference[] = [];
348368

349-
rawFolders.forEach(folder => {
350-
if (obsolete[folder]) {
369+
refs.forEach(ref => {
370+
if (obsolete[ref.name]) {
351371
return;
352372
}
353373

354-
const { id, version } = getIdAndVersionFromLocalExtensionId(folder);
374+
const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name);
355375

356376
if (!id || !version) {
357-
nonGallery.push(folder);
377+
nonGallery.push(ref);
358378
} else {
359-
gallery.push(folder);
379+
gallery.push(ref);
360380
}
361381
});
362382

363-
folders = [...nonGallery, ...gallery];
383+
refs = [...nonGallery, ...gallery];
364384
}
365385

366386
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
367-
let extensionDescriptions = await TPromise.join(folders.map(f => this.scanExtension(input.ourVersion, log, join(absoluteFolderPath, f), isBuiltin, nlsConfig)));
387+
let extensionDescriptions = await TPromise.join(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, nlsConfig)));
368388
extensionDescriptions = extensionDescriptions.filter(item => item !== null);
369389

370390
if (!isBuiltin) {

src/vs/workbench/services/extensions/electron-browser/extensionService.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService,
1919
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
2020
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
2121
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
22-
import { ExtensionScanner, ILog, ExtensionScannerInput } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
22+
import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
2323
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
2424
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
2525
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
@@ -46,6 +46,42 @@ import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
4646
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
4747
const ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions'));
4848

49+
interface IBuiltInExtension {
50+
name: string;
51+
version: string;
52+
repo: string;
53+
}
54+
55+
interface IBuiltInExtensionControl {
56+
[name: string]: 'marketplace' | 'disabled' | string;
57+
}
58+
59+
class ExtraBuiltInExtensionResolver implements IExtensionResolver {
60+
61+
constructor(private builtInExtensions: IBuiltInExtension[], private control: IBuiltInExtensionControl) { }
62+
63+
resolveExtensions(): TPromise<IExtensionReference[]> {
64+
const result: IExtensionReference[] = [];
65+
66+
for (const ext of this.builtInExtensions) {
67+
const controlState = this.control[ext.name] || 'marketplace';
68+
69+
switch (controlState) {
70+
case 'disabled':
71+
break;
72+
case 'marketplace':
73+
result.push({ name: ext.name, path: path.join(ExtraDevSystemExtensionsRoot, ext.name) });
74+
break;
75+
default:
76+
result.push({ name: ext.name, path: controlState });
77+
break;
78+
}
79+
}
80+
81+
return TPromise.as(result);
82+
}
83+
}
84+
4985
// Enable to see detailed message communication between window and extension host
5086
const logExtensionHostCommunication = false;
5187

@@ -632,7 +668,19 @@ export class ExtensionService extends Disposable implements IExtensionService {
632668
let finalBuiltinExtensions: TPromise<IExtensionDescription[]> = builtinExtensions;
633669

634670
if (devMode) {
635-
const extraBuiltinExtensions = ExtensionScanner.scanExtensions(new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true), log);
671+
const builtInExtensionsFilePath = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'build', 'builtInExtensions.json'));
672+
const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8')
673+
.then<IBuiltInExtension[]>(raw => JSON.parse(raw));
674+
675+
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
676+
const controlFile = pfs.readFile(controlFilePath, 'utf8')
677+
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
678+
679+
const input = new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true);
680+
const extraBuiltinExtensions = TPromise.join([builtInExtensions, controlFile])
681+
.then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control))
682+
.then(resolver => ExtensionScanner.scanExtensions(input, log, resolver));
683+
636684
finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
637685
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
638686
for (let i = 0, len = builtinExtensions.length; i < len; i++) {

0 commit comments

Comments
 (0)