diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 63f7a1f591f66ffda625f4b9f43957817314cd23..ef0ddaf6291f1deab7f95ef89d083b589d9cb1aa 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -7,6 +7,8 @@ const gulp = require('gulp'); const fs = require('fs'); +const os = require('os'); +const cp = require('child_process'); const path = require('path'); const es = require('event-stream'); const azure = require('gulp-azure-storage'); @@ -443,3 +445,52 @@ gulp.task('upload-vscode-sourcemaps', ['minify-vscode'], () => { prefix: commit + '/' })); }); + +const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); +gulp.task('upload-vscode-configuration', ['generate-vscode-configuration'], () => { + if (!fs.existsSync(allConfigDetailsPath)) { + console.error(`configuration file at ${allConfigDetailsPath} does not exist`); + return; + } + + return gulp.src(allConfigDetailsPath) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: 'configuration', + prefix: `${packageJson.version}/${commit}/` + })); +}); + +gulp.task('generate-vscode-configuration', () => { + return new Promise((resolve, reject) => { + const buildDir = process.env['AGENT_BUILDDIRECTORY']; + if (!buildDir) { + return reject(new Error('$AGENT_BUILDDIRECTORY not set')); + } + + const appPath = path.join(buildDir, 'VSCode-darwin/Visual\\ Studio\\ Code\\ -\\ Insiders.app/Contents/Resources/app/bin/code'); + const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait`); + + const timer = setTimeout(() => { + codeProc.kill(); + reject(new Error('export-default-configuration process timed out')); + }, 10 * 1000); + + codeProc.stdout.on('data', d => console.log(d.toString())); + codeProc.stderr.on('data', d => console.log(d.toString())); + + codeProc.on('exit', () => { + clearTimeout(timer); + resolve(); + }); + + codeProc.on('error', err => { + clearTimeout(timer); + reject(err); + }); + }).catch(e => { + // Don't fail the build + console.error(e.toString()); + }); +}); diff --git a/build/tfs/darwin/build.sh b/build/tfs/darwin/build.sh index acbb849d4b3caaf5ef65a7b9137c032429d005e3..b13aaa78820dfcc93e0baedc5540d381e9eb21d3 100755 --- a/build/tfs/darwin/build.sh +++ b/build/tfs/darwin/build.sh @@ -39,3 +39,6 @@ step "Run integration tests" \ step "Publish release" \ ./build/tfs/darwin/release.sh + +step "Generate and upload configuration.json" \ + npm run gulp -- upload-vscode-configuration diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 540f812d271e4b9f992d9aac247645850bfaeffb..0741cac9892bea20976eef0c1c6baef85e873e91 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -41,6 +41,7 @@ export interface ParsedArgs { 'open-url'?: string | string[]; 'skip-getting-started'?: boolean; 'sticky-quickopen'?: boolean; + 'export-default-configuration'?: string; } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 6eb3036e94a8fc9b009eacbe74469602deb75db1..1bcf77bddbe93ce2eb9fe037c12a7726dc079157 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -26,7 +26,8 @@ const options: minimist.Opts = { 'debugSearch', 'debugBrkSearch', 'open-url', - 'enable-proposed-api' + 'enable-proposed-api', + 'export-default-configuration' ], boolean: [ 'help', diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index b2a080aa45a97b3e7e2207d24bd58e0e5b19a5b8..8133a94c1b872e5c9b2bac83761b0b70cb570009 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -46,7 +46,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configuration'; import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; @@ -622,6 +622,8 @@ export class Workbench implements IPartService { Registry.as(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService); Registry.as(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService); Registry.as(EditorExtensions.Editors).setInstantiationService(this.instantiationService); + + this.instantiationService.createInstance(DefaultConfigurationExportHelper); } private initSettings(): void { diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index e3142ff18e3ece8d48716c74264fe225a53373a2..09e3bd87848e082a2c411e3cec4555136273d907 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -15,7 +15,7 @@ import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { readFile, stat } from 'vs/base/node/pfs'; +import { readFile, stat, writeFile } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import * as extfs from 'vs/base/node/extfs'; import { IWorkspaceContextService, IWorkspace, Workspace, WorkbenchState, WorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; @@ -36,6 +36,10 @@ import { createHash } from 'crypto'; import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; interface IStat { resource: URI; @@ -870,3 +874,88 @@ export class Configuration extends BaseConfiguration { return true; } } + +interface IExportedConfigurationNode { + name: string; + description: string; + default: any; + type: string | string[]; + enum?: any[]; + enumDescriptions?: string[]; +} + +interface IConfigurationExport { + settings: IExportedConfigurationNode[]; + buildTime: number; + commit: string; + version: string; +} + +export class DefaultConfigurationExportHelper { + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IExtensionService private extensionService: IExtensionService, + @ICommandService private commandService: ICommandService) { + if (environmentService.args['export-default-configuration']) { + this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']); + } + } + + private writeConfigModelAndQuit(targetPath: string): TPromise { + return this.extensionService.onReady() + .then(() => this.writeConfigModel(targetPath)) + .then(() => this.commandService.executeCommand('workbench.action.quit')) + .then(() => { }); + } + + private writeConfigModel(targetPath: string): TPromise { + const config = this.getConfigModel(); + + const resultString = JSON.stringify(config, undefined, ' '); + return writeFile(targetPath, resultString); + } + + private getConfigModel(): IConfigurationExport { + const configurations = Registry.as(Extensions.Configuration).getConfigurations().slice(); + const settings: IExportedConfigurationNode[] = []; + const processConfig = (config: IConfigurationNode) => { + if (config.properties) { + for (let name in config.properties) { + const prop = config.properties[name]; + const propDetails: IExportedConfigurationNode = { + name, + description: prop.description, + default: prop.default, + type: prop.type + }; + + if (prop.enum) { + propDetails.enum = prop.enum; + } + + if (prop.enumDescriptions) { + propDetails.enumDescriptions = prop.enumDescriptions; + } + + settings.push(propDetails); + } + } + + if (config.allOf) { + config.allOf.forEach(processConfig); + } + }; + + configurations.forEach(processConfig); + + const result: IConfigurationExport = { + settings: settings.sort((a, b) => a.name.localeCompare(b.name)), + buildTime: Date.now(), + commit: product.commit, + version: pkg.version + }; + + return result; + } +}