built in extensions control file
This commit is contained in:
parent
f13654c5a5
commit
b338f57e76
|
@ -597,17 +597,3 @@ gulp.task('generate-vscode-configuration', () => {
|
||||||
console.error(e.toString());
|
console.error(e.toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region Built-In Extensions
|
|
||||||
gulp.task('clean-builtin-extensions', util.rimraf('.build/builtInExtensions'));
|
|
||||||
gulp.task('download-builtin-extensions', ['clean-builtin-extensions'], function () {
|
|
||||||
const marketplaceExtensions = es.merge(...builtInExtensions.map(extension => {
|
|
||||||
return ext.fromMarketplace(extension.name, extension.version)
|
|
||||||
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
|
|
||||||
}));
|
|
||||||
|
|
||||||
return marketplaceExtensions
|
|
||||||
.pipe(util.setExecutableBit(['**/*.sh']))
|
|
||||||
.pipe(vfs.dest('.build/builtInExtensions'));
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
|
|
|
@ -7,14 +7,31 @@
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const mkdirp = require('mkdirp');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const es = require('event-stream');
|
||||||
|
const rename = require('gulp-rename');
|
||||||
|
const vfs = require('vinyl-fs');
|
||||||
|
const ext = require('./extensions');
|
||||||
|
const util = require('gulp-util');
|
||||||
|
|
||||||
const root = path.dirname(path.dirname(__dirname));
|
const root = path.dirname(path.dirname(__dirname));
|
||||||
|
const builtInExtensions = require('../builtInExtensions');
|
||||||
|
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
|
||||||
|
|
||||||
|
function getExtensionPath(extension) {
|
||||||
|
return path.join(root, '.build', 'builtInExtensions', extension.name);
|
||||||
|
}
|
||||||
|
|
||||||
function isUpToDate(extension) {
|
function isUpToDate(extension) {
|
||||||
const packagePath = path.join(root, '.build', 'builtInExtensions', extension.name, 'package.json');
|
const packagePath = path.join(getExtensionPath(extension), 'package.json');
|
||||||
|
|
||||||
if (!fs.existsSync(packagePath)) {
|
if (!fs.existsSync(packagePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageContents = fs.readFileSync(packagePath);
|
const packageContents = fs.readFileSync(packagePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const diskVersion = JSON.parse(packageContents).version;
|
const diskVersion = JSON.parse(packageContents).version;
|
||||||
return (diskVersion === extension.version);
|
return (diskVersion === extension.version);
|
||||||
|
@ -23,10 +40,83 @@ function isUpToDate(extension) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const builtInExtensions = require('../builtInExtensions');
|
function syncMarketplaceExtension(extension) {
|
||||||
builtInExtensions.forEach((extension) => {
|
if (isUpToDate(extension)) {
|
||||||
if (!isUpToDate(extension)) {
|
util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎'));
|
||||||
process.exit(1);
|
return es.readArray([]);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
rimraf.sync(getExtensionPath(extension));
|
||||||
|
|
||||||
|
return ext.fromMarketplace(extension.name, extension.version)
|
||||||
|
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`))
|
||||||
|
.pipe(vfs.dest('.build/builtInExtensions'))
|
||||||
|
.on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncExtension(extension, controlState) {
|
||||||
|
switch (controlState) {
|
||||||
|
case 'disabled':
|
||||||
|
util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name));
|
||||||
|
rimraf.sync(getExtensionPath(extension));
|
||||||
|
return es.readArray([]);
|
||||||
|
|
||||||
|
case 'marketplace':
|
||||||
|
return syncMarketplaceExtension(extension);
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!fs.existsSync(controlState)) {
|
||||||
|
util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`));
|
||||||
|
return es.readArray([]);
|
||||||
|
|
||||||
|
} else if (!fs.existsSync(path.join(controlState, 'package.json'))) {
|
||||||
|
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.`));
|
||||||
|
return es.readArray([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
util.log(util.colors.blue('[local]'), `${extension.name}: ${controlState}`, util.colors.green('✔︎'));
|
||||||
|
return es.readArray([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readControlFile() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(fs.readFileSync(controlFilePath, 'utf8'));
|
||||||
|
} catch (err) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeControlFile(control) {
|
||||||
|
mkdirp.sync(path.dirname(controlFilePath));
|
||||||
|
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
util.log('Syncronizing built-in extensions...');
|
||||||
|
util.log('Control file:', controlFilePath);
|
||||||
|
|
||||||
|
const control = readControlFile();
|
||||||
|
const streams = [];
|
||||||
|
|
||||||
|
for (const extension of builtInExtensions) {
|
||||||
|
let controlState = control[extension.name] || 'marketplace';
|
||||||
|
control[extension.name] = controlState;
|
||||||
|
|
||||||
|
streams.push(syncExtension(extension, controlState));
|
||||||
|
}
|
||||||
|
|
||||||
|
writeControlFile(control);
|
||||||
|
|
||||||
|
es.merge(streams)
|
||||||
|
.on('error', err => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.on('end', () => {
|
||||||
|
util.log(`${streams.length} built-in extensions processed.`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
|
@ -17,9 +17,8 @@ set CODE=".build\electron\%NAMESHORT%"
|
||||||
node build\lib\electron.js
|
node build\lib\electron.js
|
||||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
|
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
|
||||||
|
|
||||||
:: Get built-in extensions
|
:: Sync built-in extensions
|
||||||
node build\lib\builtInExtensions.js
|
node build\lib\builtInExtensions.js
|
||||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js download-builtin-extensions
|
|
||||||
|
|
||||||
:: Build
|
:: Build
|
||||||
if not exist out node .\node_modules\gulp\bin\gulp.js compile
|
if not exist out node .\node_modules\gulp\bin\gulp.js compile
|
||||||
|
|
|
@ -24,8 +24,8 @@ function code() {
|
||||||
# Get electron
|
# Get electron
|
||||||
node build/lib/electron.js || ./node_modules/.bin/gulp electron
|
node build/lib/electron.js || ./node_modules/.bin/gulp electron
|
||||||
|
|
||||||
# Get built-in extensions
|
# Sync built-in extensions
|
||||||
node build/lib/builtInExtensions.js || ./node_modules/.bin/gulp download-builtin-extensions
|
node build/lib/builtInExtensions.js
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
test -d out || ./node_modules/.bin/gulp compile
|
test -d out || ./node_modules/.bin/gulp compile
|
||||||
|
|
|
@ -289,6 +289,25 @@ export class ExtensionScannerInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IExtensionReference {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IExtensionResolver {
|
||||||
|
resolveExtensions(): TPromise<IExtensionReference[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultExtensionResolver implements IExtensionResolver {
|
||||||
|
|
||||||
|
constructor(private root: string) { }
|
||||||
|
|
||||||
|
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||||
|
return pfs.readDirsInDir(this.root)
|
||||||
|
.then(folders => folders.map(name => ({ name, path: join(this.root, name) })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ExtensionScanner {
|
export class ExtensionScanner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -318,10 +337,14 @@ export class ExtensionScanner {
|
||||||
/**
|
/**
|
||||||
* Scan a list of extensions defined in `absoluteFolderPath`
|
* Scan a list of extensions defined in `absoluteFolderPath`
|
||||||
*/
|
*/
|
||||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver?: IExtensionResolver): TPromise<IExtensionDescription[]> {
|
||||||
const absoluteFolderPath = input.absoluteFolderPath;
|
const absoluteFolderPath = input.absoluteFolderPath;
|
||||||
const isBuiltin = input.isBuiltin;
|
const isBuiltin = input.isBuiltin;
|
||||||
|
|
||||||
|
if (!resolver) {
|
||||||
|
resolver = new DefaultExtensionResolver(absoluteFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let obsolete: { [folderName: string]: boolean; } = {};
|
let obsolete: { [folderName: string]: boolean; } = {};
|
||||||
if (!isBuiltin) {
|
if (!isBuiltin) {
|
||||||
|
@ -333,38 +356,35 @@ export class ExtensionScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawFolders = await pfs.readDirsInDir(absoluteFolderPath);
|
let refs = await resolver.resolveExtensions();
|
||||||
|
|
||||||
// Ensure the same extension order
|
// Ensure the same extension order
|
||||||
rawFolders.sort();
|
refs.sort((a, b) => a.name < b.name ? -1 : 1);
|
||||||
|
|
||||||
let folders: string[] = null;
|
if (!isBuiltin) {
|
||||||
if (isBuiltin) {
|
|
||||||
folders = rawFolders;
|
|
||||||
} else {
|
|
||||||
// TODO: align with extensionsService
|
// TODO: align with extensionsService
|
||||||
const nonGallery: string[] = [];
|
const nonGallery: IExtensionReference[] = [];
|
||||||
const gallery: string[] = [];
|
const gallery: IExtensionReference[] = [];
|
||||||
|
|
||||||
rawFolders.forEach(folder => {
|
refs.forEach(ref => {
|
||||||
if (obsolete[folder]) {
|
if (obsolete[ref.name]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, version } = getIdAndVersionFromLocalExtensionId(folder);
|
const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name);
|
||||||
|
|
||||||
if (!id || !version) {
|
if (!id || !version) {
|
||||||
nonGallery.push(folder);
|
nonGallery.push(ref);
|
||||||
} else {
|
} else {
|
||||||
gallery.push(folder);
|
gallery.push(ref);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
folders = [...nonGallery, ...gallery];
|
refs = [...nonGallery, ...gallery];
|
||||||
}
|
}
|
||||||
|
|
||||||
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
|
||||||
let extensionDescriptions = await TPromise.join(folders.map(f => this.scanExtension(input.ourVersion, log, join(absoluteFolderPath, f), isBuiltin, nlsConfig)));
|
let extensionDescriptions = await TPromise.join(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, nlsConfig)));
|
||||||
extensionDescriptions = extensionDescriptions.filter(item => item !== null);
|
extensionDescriptions = extensionDescriptions.filter(item => item !== null);
|
||||||
|
|
||||||
if (!isBuiltin) {
|
if (!isBuiltin) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService,
|
||||||
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||||
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
|
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||||
import { ExtensionScanner, ILog, ExtensionScannerInput } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
|
import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
|
||||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
|
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
|
||||||
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
|
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';
|
||||||
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||||
const ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions'));
|
const ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions'));
|
||||||
|
|
||||||
|
interface IBuiltInExtension {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
repo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBuiltInExtensionControl {
|
||||||
|
[name: string]: 'marketplace' | 'disabled' | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtraBuiltInExtensionResolver implements IExtensionResolver {
|
||||||
|
|
||||||
|
constructor(private builtInExtensions: IBuiltInExtension[], private control: IBuiltInExtensionControl) { }
|
||||||
|
|
||||||
|
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||||
|
const result: IExtensionReference[] = [];
|
||||||
|
|
||||||
|
for (const ext of this.builtInExtensions) {
|
||||||
|
const controlState = this.control[ext.name] || 'marketplace';
|
||||||
|
|
||||||
|
switch (controlState) {
|
||||||
|
case 'disabled':
|
||||||
|
break;
|
||||||
|
case 'marketplace':
|
||||||
|
result.push({ name: ext.name, path: path.join(ExtraDevSystemExtensionsRoot, ext.name) });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.push({ name: ext.name, path: controlState });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TPromise.as(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enable to see detailed message communication between window and extension host
|
// Enable to see detailed message communication between window and extension host
|
||||||
const logExtensionHostCommunication = false;
|
const logExtensionHostCommunication = false;
|
||||||
|
|
||||||
|
@ -632,7 +668,19 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||||
let finalBuiltinExtensions: TPromise<IExtensionDescription[]> = builtinExtensions;
|
let finalBuiltinExtensions: TPromise<IExtensionDescription[]> = builtinExtensions;
|
||||||
|
|
||||||
if (devMode) {
|
if (devMode) {
|
||||||
const extraBuiltinExtensions = ExtensionScanner.scanExtensions(new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true), log);
|
const builtInExtensionsFilePath = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'build', 'builtInExtensions.json'));
|
||||||
|
const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8')
|
||||||
|
.then<IBuiltInExtension[]>(raw => JSON.parse(raw));
|
||||||
|
|
||||||
|
const controlFilePath = path.join(process.env['HOME'], '.vscode-oss-dev', 'extensions', 'control.json');
|
||||||
|
const controlFile = pfs.readFile(controlFilePath, 'utf8')
|
||||||
|
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
|
||||||
|
|
||||||
|
const input = new ExtensionScannerInput(version, commit, locale, devMode, ExtraDevSystemExtensionsRoot, true);
|
||||||
|
const extraBuiltinExtensions = TPromise.join([builtInExtensions, controlFile])
|
||||||
|
.then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control))
|
||||||
|
.then(resolver => ExtensionScanner.scanExtensions(input, log, resolver));
|
||||||
|
|
||||||
finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
|
finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
|
||||||
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
|
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
|
||||||
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
|
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
|
||||||
|
|
Loading…
Reference in a new issue