diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index b465ef9a095..0b9a362c075 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -59,6 +59,21 @@ steps: node build/lib/builtInExtensions.js displayName: Install distro dependencies and extensions +- script: | + set -e + cd .. + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + npm install + ./node_modules/typescript/bin/tsc + npm run setup-extension-repos + node ./out/cli-extract.js --sourceDir ../s --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources/ --outputDir . --applyEndpoints --includeIsMeasurement + mv declarations-resolved.json ../s/src/telemetry-core.json + mv declarations-extensions-resolved.json ../s/src/telemetry-extensions.json + echo 'Moved Files' + displayName: Extract Telemetry + - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index fa26c26dd1c..210798a4776 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -60,6 +60,21 @@ steps: node build/lib/builtInExtensions.js displayName: Install distro dependencies and extensions +- script: | + set -e + cd .. + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + npm install + ./node_modules/typescript/bin/tsc + npm run setup-extension-repos + node ./out/cli-extract.js --sourceDir ../s --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources/ --outputDir . --applyEndpoints --includeIsMeasurement + mv declarations-resolved.json ../s/src/telemetry-core.json + mv declarations-extensions-resolved.json ../s/src/telemetry-extensions.json + echo 'Moved Files' + displayName: Extract Telemetry + - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 3fe19e4b847..31ff866b76a 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -65,6 +65,23 @@ steps: exec { node build/lib/builtInExtensions.js } displayName: Install distro dependencies and extensions +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + cd .. + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + npm install + npm install -g typescript + tsc + npm run setup-extension-repos + node ./out/cli-extract.js --sourceDir ../s --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources/ --outputDir . --applyEndpoints --includeIsMeasurement + mv declarations-resolved.json ../s/src/telemetry-core.json + mv declarations-extensions-resolved.json ../s/src/telemetry-extensions.json + echo 'Moved Files' + displayName: Extract Telemetry + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 9c32df0bf81..4fc0a7ff414 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -62,6 +62,8 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-window.js', 'out-build/paths.js', + 'out-build/telemetry-core.json', + 'out-build/telemetry-extensions.json', 'out-build/vs/**/*.{svg,png,cur,html}', '!out-build/vs/code/browser/**/*.html', 'out-build/vs/base/common/performance.js', @@ -327,7 +329,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); let all = es.merge( - packageJsonStream, + packageJsonStream, productJsonStream, license, api, diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 8c4415ac59f..054ab351e91 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -14,6 +14,7 @@ arguments=( '--user-data-dir[specify the directory that user data is kept in]:directory:_directories' '(- *)'{-v,--version}'[print version]' '(- *)'{-h,--help}'[print usage]' + '(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]' '--extensions-dir[set the root path for extensions]:root path:_directories' '--list-extensions[list the installed extensions]' '--show-versions[show versions of installed extensions, when using --list-extension]' diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 45f4ff7d670..978ecdd42b8 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -25,7 +25,8 @@ function shouldSpawnCliProcess(argv: ParsedArgs): boolean { || !!argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension'] - || !!argv['locate-extension']; + || !!argv['locate-extension'] + || !!argv['telemetry']; } interface IMainCli { diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dafebd1c91f..5caccaf03b9 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -41,6 +41,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { Schemas } from 'vs/base/common/network'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { buildTelemetryMessage } from 'vs/platform/environment/node/argv'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -94,6 +95,8 @@ export class Main { const arg = argv['locate-extension']; const ids: string[] = typeof arg === 'string' ? [arg] : arg; await this.locateExtension(ids); + } else if (argv['telemetry']) { + console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath)); } } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 443e430fcd0..dc75380ba18 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -13,6 +13,7 @@ export interface ParsedArgs { _urls?: string[]; help?: boolean; version?: boolean; + telemetry?: boolean; status?: boolean; wait?: boolean; waitMarkerFilePath?: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index b53a29f370a..a8715ddccae 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -8,7 +8,8 @@ import * as os from 'os'; import { localize } from 'vs/nls'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { join } from 'vs/base/common/path'; -import { writeFileSync } from 'vs/base/node/pfs'; +import { statSync, readFileSync } from 'fs'; +import { writeFileSync, readdirSync } from 'vs/base/node/pfs'; /** * This code is also used by standalone cli's. Avoid adding any other dependencies. @@ -41,6 +42,7 @@ export const options: Option[] = [ { id: 'user-data-dir', type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") }, { id: 'version', type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") }, { id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") }, + { id: 'telemetry', type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, { id: 'folder-uri', type: 'string', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, { id: 'file-uri', type: 'string', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, @@ -217,6 +219,38 @@ export function buildVersionMessage(version: string | undefined, commit: string return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; } +export function buildTelemetryMessage(appRoot: string, extensionsPath: string): string { + try { + // Gets all the directories inside the extension directory + const dirs = readdirSync(extensionsPath).filter(files => statSync(join(extensionsPath, files)).isDirectory()); + const telemetryJsonFolders: string[] = []; + dirs.forEach((dir) => { + const files = readdirSync(join(extensionsPath, dir)).filter(file => file === 'telemetry.json'); + // We know it contains a telemetry.json file so we add it to the list of folders which have one + if (files.length === 1) { + telemetryJsonFolders.push(dir); + } + }); + const mergedTelemetry = Object.create(null); + // Simple function to merge the telemetry into one json object + const mergeTelemetry = (contents: string, dirName: string) => { + const telemetryData = JSON.parse(contents); + mergedTelemetry[dirName] = telemetryData; + }; + telemetryJsonFolders.forEach((folder) => { + const contents = readFileSync(join(extensionsPath, folder, 'telemetry.json')).toString(); + mergeTelemetry(contents, folder); + }); + let contents = readFileSync(join(appRoot, 'out/', 'telemetry-core.json')).toString(); + mergeTelemetry(contents, 'vscode-core'); + contents = readFileSync(join(appRoot, 'out/', 'telemetry-extensions.json')).toString(); + mergeTelemetry(contents, 'vscode-extensions'); + return JSON.stringify(mergedTelemetry, null, 4); + } catch (err) { + return 'Unable to read VS Code telemetry events!'; + } +} + /** * Converts an argument into an array * @param arg a argument value. Can be undefined, an entry or an array