diff --git a/package-lock.json b/package-lock.json index eafcd51..7a26dc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,19 +16,13 @@ "integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=", "dev": true, "requires": { - "@types/node": "8.5.1" + "@types/node": "8.5.2" } }, - "@types/mocha": { - "version": "2.2.44", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.44.tgz", - "integrity": "sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw==", - "dev": true - }, "@types/node": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.1.tgz", - "integrity": "sha512-SrmAO+NhnsuG/6TychSl2VdxBZiw/d6V+8j+DFo8O3PwFi+QeYXWHhAw+b170aSc6zYab6/PjEWRZHIDN9mNUw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz", + "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==", "dev": true }, "@types/tmp": { @@ -140,16 +134,6 @@ } } }, - "applicationinsights": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.21.0.tgz", - "integrity": "sha1-Ng9JIrg7wHhMb3TfBjZ+QlOJGWM=", - "requires": { - "diagnostic-channel": "0.1.0", - "diagnostic-channel-publishers": "0.1.3", - "zone.js": "0.7.6" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -566,7 +550,7 @@ "requires": { "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" + "supports-color": "4.5.0" } }, "chokidar": { @@ -717,9 +701,9 @@ } }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", "dev": true }, "commondir": { @@ -920,9 +904,10 @@ "dev": true }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -958,23 +943,10 @@ "minimalistic-assert": "1.0.0" } }, - "diagnostic-channel": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.1.0.tgz", - "integrity": "sha1-emrYrVBmusVE2go3m6F0ujYqV88=", - "requires": { - "semver": "5.4.1" - } - }, - "diagnostic-channel-publishers": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.1.3.tgz", - "integrity": "sha1-y9bdEK8e7M1JWsDtZ69fi2hvAKM=" - }, "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", "dev": true }, "diffie-hellman": { @@ -2948,7 +2920,7 @@ "dev": true, "requires": { "chalk": "1.1.3", - "commander": "2.11.0", + "commander": "2.12.2", "is-my-json-valid": "2.17.1", "pinkie-promise": "2.0.1" }, @@ -3565,11 +3537,6 @@ "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -3584,11 +3551,6 @@ "lodash._root": "3.0.1" } }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -3604,7 +3566,8 @@ "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true }, "lodash.keys": { "version": "3.1.2", @@ -3886,13 +3849,25 @@ "supports-color": "4.4.0" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { - "ms": "2.0.0" + "has-flag": "2.0.0" } } } @@ -3914,7 +3889,8 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "multimatch": { "version": "2.1.0", @@ -4753,14 +4729,6 @@ "aproba": "1.2.0" } }, - "rxjs": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.5.tgz", - "integrity": "sha512-D/MfQnPMBk8P8gfwGxvCkuaWBcG58W7dUMT//URPoYzIbDEKT0GezdirkK5whMgKFBATfCoTpxO8bJQGJ04W5A==", - "requires": { - "symbol-observable": "1.0.1" - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -4779,7 +4747,8 @@ "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true }, "serialize-javascript": { "version": "1.4.0", @@ -4872,16 +4841,6 @@ "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", "dev": true }, - "spawn-rx": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz", - "integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==", - "requires": { - "debug": "2.6.9", - "lodash.assign": "4.2.0", - "rxjs": "5.5.5" - } - }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", @@ -5103,19 +5062,14 @@ "dev": true }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { "has-flag": "2.0.0" } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, "sync-exec": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz", @@ -5238,8 +5192,8 @@ "babel-code-frame": "6.26.0", "builtin-modules": "1.1.1", "chalk": "2.3.0", - "commander": "2.11.0", - "diff": "3.3.1", + "commander": "2.12.2", + "diff": "3.4.0", "glob": "7.1.2", "minimatch": "3.0.4", "resolve": "1.5.0", @@ -5296,14 +5250,6 @@ "requires": { "commander": "2.12.2", "source-map": "0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true - } } }, "uglify-to-browserify": { @@ -5624,7 +5570,7 @@ "mkdirp": "0.5.1", "node-libs-browser": "2.1.0", "source-map": "0.5.7", - "supports-color": "4.4.0", + "supports-color": "4.5.0", "tapable": "0.2.8", "uglifyjs-webpack-plugin": "0.4.6", "watchpack": "1.4.0", @@ -5864,11 +5810,6 @@ "requires": { "buffer-crc32": "0.2.13" } - }, - "zone.js": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz", - "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=" } } } diff --git a/package.json b/package.json index 455c5af..155cffa 100644 --- a/package.json +++ b/package.json @@ -2935,26 +2935,20 @@ "prepush": "npm run compile" }, "dependencies": { - "applicationinsights": "0.21.0", "copy-paste": "1.3.0", "date-fns": "1.29.0", "iconv-lite": "0.4.19", "lodash.debounce": "4.0.8", - "lodash.escaperegexp": "4.1.2", - "lodash.isequal": "4.5.0", "lodash.once": "4.1.1", - "spawn-rx": "2.0.12", "tmp": "0.0.33", "tslib": "^1.8.1" }, "devDependencies": { "@types/copy-paste": "1.1.30", "@types/iconv-lite": "0.0.1", - "@types/mocha": "2.2.44", - "@types/node": "8.5.1", + "@types/node": "8.5.2", "@types/tmp": "0.0.33", "husky": "^0.14.3", - "mocha": "4.0.1", "ts-loader": "^3.2.0", "tslint": "5.8.0", "typescript": "2.6.2", diff --git a/src/commands/common.ts b/src/commands/common.ts index 2d023ea..3d13192 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -3,7 +3,7 @@ import { commands, Disposable, SourceControlResourceGroup, SourceControlResource import { ExplorerNode, ExplorerRefNode } from '../views/explorerNodes'; import { GitBranch, GitCommit, GitRemote, GitUri } from '../gitService'; import { Logger } from '../logger'; -import { Telemetry } from '../telemetry'; +// import { Telemetry } from '../telemetry'; export enum Commands { ClearFileAnnotations = 'gitlens.clearFileAnnotations', @@ -162,7 +162,7 @@ export abstract class Command extends Disposable { abstract execute(...args: any[]): any; protected _execute(command: string, ...args: any[]): any { - Telemetry.trackEvent(command); + // Telemetry.trackEvent(command); const [context, rest] = Command.parseContext(command, this.contextParsingOptions, ...args); return this.preExecute(context, ...rest); @@ -282,7 +282,7 @@ export abstract class EditorCommand extends Disposable { } private executeCore(command: string, editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any { - Telemetry.trackEvent(command); + // Telemetry.trackEvent(command); return this.execute(editor, edit, ...args); } diff --git a/src/extension.ts b/src/extension.ts index 8cc2f2e..f0a9f2b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,7 @@ import { Objects } from './system'; import { ConfigurationTarget, ExtensionContext, extensions, languages, window, workspace } from 'vscode'; import { AnnotationController } from './annotations/annotationController'; import { configuration, Configuration, IConfig } from './configuration'; -import { ApplicationInsightsKey, CommandContext, ExtensionKey, GlobalState, QualifiedExtensionId, setCommandContext } from './constants'; +import { CommandContext, ExtensionKey, GlobalState, QualifiedExtensionId, setCommandContext } from './constants'; import { CodeLensController } from './codeLensController'; import { configureCommands } from './commands'; import { CurrentLineController } from './currentLineController'; @@ -16,7 +16,7 @@ import { Keyboard } from './keyboard'; import { Logger } from './logger'; import { Messages, SuppressedMessages } from './messages'; import { ResultsExplorer } from './views/resultsExplorer'; -import { Telemetry } from './telemetry'; +// import { Telemetry } from './telemetry'; // this method is called when your extension is activated export async function activate(context: ExtensionContext) { @@ -44,12 +44,12 @@ export async function activate(context: ExtensionContext) { const gitVersion = GitService.getGitVersion(); - Telemetry.configure(ApplicationInsightsKey); + // Telemetry.configure(ApplicationInsightsKey); - const telemetryContext: { [id: string]: any } = Object.create(null); - telemetryContext.version = gitlensVersion; - telemetryContext['git.version'] = gitVersion; - Telemetry.setContext(telemetryContext); + // const telemetryContext: { [id: string]: any } = Object.create(null); + // telemetryContext.version = gitlensVersion; + // telemetryContext['git.version'] = gitVersion; + // Telemetry.setContext(telemetryContext); const previousVersion = context.globalState.get(GlobalState.GitLensVersion); @@ -90,8 +90,6 @@ export async function activate(context: ExtensionContext) { // Constantly over my data cap so stop collecting initialized event // Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true)); - // setCommandContext(CommandContext.ResultsExplorer, false); - // Slightly delay enabling the explorer to not stop the rest of GitLens from being usable setTimeout(() => setCommandContext(CommandContext.GitExplorer, true), 1000); diff --git a/src/git/git.ts b/src/git/git.ts index c9023c6..dc50679 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -1,10 +1,8 @@ 'use strict'; import { Strings } from '../system'; -import { SpawnOptions } from 'child_process'; import { findGitPath, IGit } from './gitLocator'; import { Logger } from '../logger'; -import { Observable } from 'rxjs'; -import { spawnPromise } from 'spawn-rx'; +import { CommandOptions, runCommand } from './shell'; import * as fs from 'fs'; import * as iconv from 'iconv-lite'; import * as path from 'path'; @@ -39,17 +37,7 @@ const GitWarnings = [ /ambiguous argument '.*?': unknown revision or path not in the working tree/ ]; -interface GitCommandOptions { - cwd: string; - env?: any; - encoding?: string; - stdin?: Observable | undefined; - willHandleErrors?: boolean; -} - -async function gitCommand(options: GitCommandOptions, ...args: any[]): Promise { - if (options.willHandleErrors) return gitCommandCore(options, ...args); - +async function gitCommand(options: CommandOptions, ...args: any[]): Promise { try { return await gitCommandCore(options, ...args); } @@ -61,27 +49,26 @@ async function gitCommand(options: GitCommandOptions, ...args: any[]): Promise> = new Map(); -async function gitCommandCore(options: GitCommandOptions, ...args: any[]): Promise { +async function gitCommandCore(options: CommandOptions, ...args: any[]): Promise { // Fixes https://github.com/eamodio/vscode-gitlens/issues/73 & https://github.com/eamodio/vscode-gitlens/issues/161 // See https://stackoverflow.com/questions/4144417/how-to-handle-asian-characters-in-file-names-in-git-on-os-x args.splice(0, 0, '-c', 'core.quotepath=false', '-c', 'color.ui=false'); - const opts = { encoding: 'utf8', ...options }; + const encoding = options.encoding || 'utf8'; + const opts = { + ...options, + encoding: encoding === 'utf8' ? 'utf8' : 'binary', + // Adds GCM environment variables to avoid any possible credential issues -- from https://github.com/Microsoft/vscode/issues/26573#issuecomment-338686581 + // Shouldn't *really* be needed but better safe than sorry + env: { ...(options.env || process.env), GCM_INTERACTIVE: 'NEVER', GCM_PRESERVE_CREDS: 'TRUE' } + } as CommandOptions; - const command = `(${options.cwd}): git ` + args.join(' '); + const command = `(${opts.cwd}): git ${args.join(' ')}`; let promise = pendingCommands.get(command); if (promise === undefined) { - Logger.log(`Spawning${command}`); - - promise = spawnPromise(git.path, args, { - cwd: options.cwd, - // Adds GCM environment variables to avoid any possible credential issues -- from https://github.com/Microsoft/vscode/issues/26573#issuecomment-338686581 - // Shouldn't *really* be needed but better safe than sorry - env: { ...(options.env || process.env), GCM_INTERACTIVE: 'NEVER', GCM_PRESERVE_CREDS: 'TRUE' }, - encoding: (opts.encoding === 'utf8') ? 'utf8' : 'binary', - stdin: opts.stdin - } as SpawnOptions); + Logger.log(`Running${command}`); + promise = runCommand(git.path, args, opts); pendingCommands.set(command, promise); } @@ -98,12 +85,12 @@ async function gitCommandCore(options: GitCommandOptions, ...args: any[]): Promi Logger.log(`Completed${command}`); } - if (opts.encoding === 'utf8' || opts.encoding === 'binary') return data; + if (encoding === 'utf8' || encoding === 'binary') return data; - return iconv.decode(Buffer.from(data, 'binary'), opts.encoding); + return iconv.decode(Buffer.from(data, 'binary'), encoding); } -function gitCommandDefaultErrorHandler(ex: Error, options: GitCommandOptions, ...args: any[]): string { +function gitCommandDefaultErrorHandler(ex: Error, options: CommandOptions, ...args: any[]): string { const msg = ex && ex.toString(); if (msg) { for (const warning of GitWarnings) { @@ -253,7 +240,8 @@ export class Git { params.push(`-L ${options.startLine},${options.endLine}`); } - let stdin: Observable | undefined; + // let stdin: Observable | undefined; + let stdin: string | undefined; if (sha) { if (Git.isStagedUncommitted(sha)) { // Pipe the blame contents to stdin @@ -261,7 +249,7 @@ export class Git { params.push('-'); // Get the file contents for the staged version using `:` - stdin = Observable.from(Git.show(repoPath, fileName, ':') as any); + stdin = await Git.show(repoPath, fileName, ':'); } else { params.push(sha); @@ -288,7 +276,7 @@ export class Git { static async config_get(key: string, repoPath?: string) { try { - const data = await gitCommand({ cwd: repoPath || '', willHandleErrors: true }, `config`, `--get`, key); + const data = await gitCommandCore({ cwd: repoPath || '' }, `config`, `--get`, key); return data.trim(); } catch { @@ -305,7 +293,8 @@ export class Git { params.push(Git.isStagedUncommitted(sha2) ? '--staged' : sha2); } - return gitCommand({ cwd: repoPath, encoding: options.encoding || 'utf8' }, ...params, '--', fileName); + const encoding: BufferEncoding = options.encoding === 'utf8' ? 'utf8' : 'binary'; + return gitCommand({ cwd: repoPath, encoding: encoding }, ...params, '--', fileName); } static diff_nameStatus(repoPath: string, sha1?: string, sha2?: string, options: { filter?: string } = {}) { @@ -408,7 +397,7 @@ export class Git { static async log_resolve(repoPath: string, fileName: string, ref: string) { try { - const data = await gitCommand({ cwd: repoPath, willHandleErrors: true }, `log`, `--full-history`, `-M`, `-n1`, `--no-merges`, `--format=%H`, ref, `--`, fileName); + const data = await gitCommandCore({ cwd: repoPath }, `log`, `--full-history`, `-M`, `-n1`, `--no-merges`, `--format=%H`, ref, `--`, fileName); return data.trim(); } catch { @@ -440,7 +429,7 @@ export class Git { } try { - const data = await gitCommand({ cwd: repoPath, willHandleErrors: true }, ...params, fileName); + const data = await gitCommandCore({ cwd: repoPath }, ...params, fileName); return data.trim(); } catch { @@ -458,10 +447,10 @@ export class Git { static async revparse(repoPath: string, ref: string): Promise { try { - const data = await gitCommand({ cwd: repoPath, willHandleErrors: true }, `rev-parse`, ref); + const data = await gitCommandCore({ cwd: repoPath }, `rev-parse`, ref); return data.trim(); } - catch (ex) { + catch { return undefined; } } @@ -469,9 +458,9 @@ export class Git { static async revparse_currentBranch(repoPath: string): Promise { const params = [`rev-parse`, `--abbrev-ref`, `--symbolic-full-name`, `@`, `@{u}`]; - const opts = { cwd: repoPath, willHandleErrors: true } as GitCommandOptions; + const opts = { cwd: repoPath } as CommandOptions; try { - const data = await gitCommand(opts, ...params); + const data = await gitCommandCore(opts, ...params); return data; } catch (ex) { @@ -482,7 +471,7 @@ export class Git { if (/ambiguous argument '.*?': unknown revision or path not in the working tree/.test(msg)) { try { const params = [`symbolic-ref`, `-q`, `--short`, `HEAD`]; - const data = await gitCommand(opts, ...params); + const data = await gitCommandCore(opts, ...params); return data; } catch { @@ -496,7 +485,7 @@ export class Git { static async revparse_toplevel(cwd: string): Promise { try { - const data = await gitCommand({ cwd: cwd, willHandleErrors: true }, 'rev-parse', '--show-toplevel'); + const data = await gitCommandCore({ cwd: cwd }, 'rev-parse', '--show-toplevel'); return data.trim(); } catch { @@ -512,13 +501,13 @@ export class Git { } if (Git.isUncommitted(ref)) throw new Error(`sha=${ref} is uncommitted`); - const opts = { cwd: root, encoding: options.encoding || 'utf8', willHandleErrors: true } as GitCommandOptions; + const opts = { cwd: root, encoding: options.encoding || 'utf8' } as CommandOptions; const args = ref.endsWith(':') ? `${ref}./${file}` : `${ref}:./${file}`; try { - const data = await gitCommand(opts, 'show', args); + const data = await gitCommandCore(opts, 'show', args); return data; } catch (ex) { diff --git a/src/git/gitLocator.ts b/src/git/gitLocator.ts index 5039c2d..0d1b4c7 100644 --- a/src/git/gitLocator.ts +++ b/src/git/gitLocator.ts @@ -1,5 +1,6 @@ 'use strict'; -import { findActualExecutable, spawnPromise } from 'spawn-rx'; +// import { findActualExecutable, spawnPromise } from 'spawn-rx'; +import { findExecutable, runCommand } from './shell'; import * as path from 'path'; export interface IGit { @@ -12,10 +13,10 @@ function parseVersion(raw: string): string { } async function findSpecificGit(path: string): Promise { - const version = await spawnPromise(path, ['--version']); + const version = await runCommand(path, ['--version']); // If needed, let's update our path to avoid the search on every command if (!path || path === 'git') { - path = findActualExecutable(path, ['--version']).cmd; + path = findExecutable(path, ['--version']).cmd; } return { @@ -26,7 +27,7 @@ async function findSpecificGit(path: string): Promise { async function findGitDarwin(): Promise { try { - let path = await spawnPromise('which', ['git']); + let path = await runCommand('which', ['git']); path = path.replace(/^\s+|\s+$/g, ''); if (path !== '/usr/bin/git') { @@ -34,7 +35,7 @@ async function findGitDarwin(): Promise { } try { - await spawnPromise('xcode-select', ['-p']); + await runCommand('xcode-select', ['-p']); return findSpecificGit(path); } catch (ex) { diff --git a/src/git/shell.ts b/src/git/shell.ts new file mode 100644 index 0000000..fc63e30 --- /dev/null +++ b/src/git/shell.ts @@ -0,0 +1,148 @@ +'use strict'; +import { execFile } from 'child_process'; +import { Logger } from '../logger'; +import * as fs from 'fs'; +import * as path from 'path'; + +const isWindows = process.platform === 'win32'; + +/** + * Search PATH to see if a file exists in any of the path folders. + * + * @param {string} exe The file to search for + * @return {string} A fully qualified path, or the original path if nothing + * is found + * + * @private + */ +function runDownPath(exe: string): string { + // NB: Windows won't search PATH looking for executables in spawn like + // Posix does + + // Files with any directory path don't get this applied + if (exe.match(/[\\\/]/)) return exe; + + const target = path.join('.', exe); + try { + if (fs.statSync(target) ) return target; + } + catch { } + + const haystack = process.env.PATH!.split(isWindows ? ';' : ':'); + for (const p of haystack) { + const needle = path.join(p, exe); + try { + if (fs.statSync(needle)) return needle; + } + catch { } + } + + return exe; +} + +/** + * Finds the executable and parameters to run on Windows. This method + * mimics the POSIX behavior of being able to run scripts as executables by + * replacing the passed-in executable with the script runner, for PowerShell, + * CMD, and node scripts. + * + * This method also does the work of running down PATH, which spawn on Windows + * also doesn't do, unlike on POSIX. + */ +export function findExecutable(exe: string, args: string[]): { cmd: string; args: string[] } { + // POSIX can just execute scripts directly, no need for silly goosery + if (!isWindows) return { cmd: runDownPath(exe), args: args }; + + if (!fs.existsSync(exe)) { + // NB: When you write something like `surf-client ... -- surf-build` on Windows, + // a shell would normally convert that to surf-build.cmd, but since it's passed + // in as an argument, it doesn't happen + const possibleExts = ['.exe', '.bat', '.cmd', '.ps1']; + for (const ext of possibleExts) { + const possibleFullPath = runDownPath(`${exe}${ext}`); + + if (fs.existsSync(possibleFullPath)) return findExecutable(possibleFullPath, args); + } + } + + if (exe.match(/\.ps1$/i)) { + const cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe'); + const psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe]; + + return { cmd: cmd, args: psargs.concat(args) }; + } + + if (exe.match(/\.(bat|cmd)$/i)) { + const cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'cmd.exe'); + const cmdArgs = ['/C', exe, ...args]; + + return { cmd: cmd, args: cmdArgs }; + } + + if (exe.match(/\.(js)$/i)) { + const cmd = process.execPath; + const nodeArgs = [exe]; + + return { cmd: cmd, args: nodeArgs.concat(args) }; + } + + return { cmd: exe, args: args }; +} + +export interface CommandOptions { + readonly cwd?: string; + readonly env?: Object; + readonly encoding?: BufferEncoding; + /** + * The size the output buffer to allocate to the spawned process. Set this + * if you are anticipating a large amount of output. + * + * If not specified, this will be 10MB (10485760 bytes) which should be + * enough for most Git operations. + */ + readonly maxBuffer?: number; + /** + * An optional string or buffer which will be written to + * the child process stdin stream immediately immediately + * after spawning the process. + */ + readonly stdin?: string | Buffer; + /** + * The encoding to use when writing to stdin, if the stdin + * parameter is a string. + */ + readonly stdinEncoding?: string; +} + +export function runCommand(command: string, args: any[], options: CommandOptions = {}) { + const { stdin, stdinEncoding, ...opts } = { maxBuffer: 10 * 1024 * 1024, ...options } as CommandOptions; + + return new Promise((resolve, reject) => { + const proc = execFile( + command, + args, + opts, + (err: Error & { code?: string | number } | null, stdout, stderr) => { + if (!err) { + if (stderr) { + Logger.warn(`Warning(${command} ${args.join(' ')}): ${stderr}`); + } + resolve(stdout); + + return; + } + + if (err.message === 'stdout maxBuffer exceeded') { + reject(new Error(`Command output exceeded the allocated stdout buffer. Set 'options.maxBuffer' to a larger value than ${opts.maxBuffer} bytes`)); + } + + Logger.warn(`Error(${command} ${args.join(' ')}): (${err.code}) ${stderr}`); + reject(err); + } + ); + + if (stdin) { + proc.stdin.end(stdin, stdinEncoding || 'utf8'); + } + }); +} diff --git a/src/logger.ts b/src/logger.ts index 3b1a36c..c340030 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -2,7 +2,7 @@ import { ConfigurationChangeEvent, ExtensionContext, OutputChannel, window } from 'vscode'; import { configuration } from './configuration'; import { ExtensionOutputChannelName } from './constants'; -import { Telemetry } from './telemetry'; +// import { Telemetry } from './telemetry'; const ConsolePrefix = `[${ExtensionOutputChannelName}]`; @@ -66,7 +66,7 @@ export class Logger { this.output.appendLine((this.debug ? [this.timestamp, classOrMethod, ex, ...params] : [classOrMethod, ex, ...params]).join(' ')); } - Telemetry.trackException(ex); + // Telemetry.trackException(ex); } static warn(message?: any, ...params: any[]): void { diff --git a/src/system/object.ts b/src/system/object.ts index 18479b4..f655002 100644 --- a/src/system/object.ts +++ b/src/system/object.ts @@ -1,11 +1,6 @@ 'use strict'; -const _isEqual = require('lodash.isequal'); export namespace Objects { - export function areEquivalent(first: any, second: any): boolean { - return _isEqual(first, second); - } - export function entries(o: { [key: string]: T }): IterableIterator<[string, T]>; export function entries(o: { [key: number]: T }): IterableIterator<[string, T]>; export function* entries(o: any): IterableIterator<[string, T]> { diff --git a/src/system/string.ts b/src/system/string.ts index 85ea261..4813532 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -1,12 +1,7 @@ 'use strict'; -const _escapeRegExp = require('lodash.escaperegexp'); import * as crypto from 'crypto'; export namespace Strings { - export function escapeRegExp(s: string): string { - return _escapeRegExp(s); - } - const TokenRegex = /\$\{([^|]*?)(?:\|(\d+)(\-|\?)?)?\}/g; const TokenSanitizeRegex = /\$\{(\w*?)(?:\W|\d)*?\}/g; diff --git a/src/telemetry.ts b/src/telemetry.ts index fcdd9bb..543c5ea 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -1,113 +1,113 @@ -'use strict'; -import { Disposable, env, version, workspace } from 'vscode'; -import { configuration } from './configuration'; -import { Logger } from './logger'; -import * as os from 'os'; - -let _reporter: TelemetryReporter; - -export class Telemetry extends Disposable { - - static configure(key: string) { - if (!configuration.get(configuration.name('advanced')('telemetry')('enabled').value) || - !workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { - return; - } - - const start = process.hrtime(); - - _reporter = new TelemetryReporter(key); - - const duration = process.hrtime(start); - Logger.log(`Telemetry.configure took ${(duration[0] * 1000) + Math.floor(duration[1] / 1000000)} ms`); - } - - static setContext(context?: { [key: string]: string }) { - if (_reporter === undefined) return; - - _reporter.setContext(context); - } - - static trackEvent(name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number; }) { - if (_reporter === undefined) return; - - _reporter.trackEvent(name, properties, measurements); - } - - static trackException(ex: Error) { - if (_reporter === undefined) return; - - _reporter.trackException(ex); - } -} - -export class TelemetryReporter { - - private appInsights: ApplicationInsights; - private _client: Client; - private _context: { [key: string]: string }; - - constructor(key: string) { - const diagChannelState = process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL']; - (process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] as any) = true; - this.appInsights = require('applicationinsights') as ApplicationInsights; - process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = diagChannelState; - - if (this.appInsights.client) { - this._client = this.appInsights.getClient(key); - // no other way to enable offline mode - this._client.channel.setOfflineMode(true); - } - else { - this._client = this.appInsights.setup(key) - .setAutoCollectRequests(false) - .setAutoCollectPerformance(false) - .setAutoCollectExceptions(false) - .setAutoCollectDependencies(false) - .setAutoCollectConsole(false) - .setAutoDependencyCorrelation(false) - .setOfflineMode(true) - .start() - .client; - } - - this.setContext(); - this.stripPII(this._client); - } - - setContext(context?: { [key: string]: string }) { - if (!this._context) { - this._context = Object.create(null); - - // Add vscode properties - this._context['code.language'] = env.language; - this._context['code.version'] = version; - this._context[this._client.context.keys.sessionId] = env.sessionId; - - // Add os properties - this._context['os.platform'] = os.platform(); - this._context['os.version'] = os.release(); - } - - if (context) { - Object.assign(this._context, context); - } - - Object.assign(this._client.commonProperties, this._context); - } - - trackEvent(name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number; }) { - this._client.trackEvent(name, properties, measurements); - } - - trackException(ex: Error) { - this._client.trackException(ex); - } - - private stripPII(client: Client) { - if (client && client.context && client.context.keys && client.context.tags) { - const machineNameKey = client.context.keys.deviceMachineName; - client.context.tags[machineNameKey] = ''; - } - } -} \ No newline at end of file +// 'use strict'; +// import { env, version, workspace } from 'vscode'; +// import { configuration } from './configuration'; +// import { Logger } from './logger'; +// import * as os from 'os'; + +// let _reporter: TelemetryReporter | undefined; + +// export class Telemetry { + +// static configure(key: string) { +// if (!configuration.get(configuration.name('advanced')('telemetry')('enabled').value) || +// !workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { +// return; +// } + +// const start = process.hrtime(); + +// _reporter = new TelemetryReporter(key); + +// const duration = process.hrtime(start); +// Logger.log(`Telemetry.configure took ${(duration[0] * 1000) + Math.floor(duration[1] / 1000000)} ms`); +// } + +// static setContext(context?: { [key: string]: string }) { +// if (_reporter === undefined) return; + +// _reporter.setContext(context); +// } + +// static trackEvent(name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number; }) { +// if (_reporter === undefined) return; + +// _reporter.trackEvent(name, properties, measurements); +// } + +// static trackException(ex: Error) { +// if (_reporter === undefined) return; + +// _reporter.trackException(ex); +// } +// } + +// export class TelemetryReporter { + +// private appInsights: ApplicationInsights; +// private _client: Client; +// private _context: { [key: string]: string }; + +// constructor(key: string) { +// const diagChannelState = process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL']; +// (process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] as any) = true; +// this.appInsights = require('applicationinsights') as ApplicationInsights; +// process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = diagChannelState; + +// if (this.appInsights.client) { +// this._client = this.appInsights.getClient(key); +// // no other way to enable offline mode +// this._client.channel.setOfflineMode(true); +// } +// else { +// this._client = this.appInsights.setup(key) +// .setAutoCollectRequests(false) +// .setAutoCollectPerformance(false) +// .setAutoCollectExceptions(false) +// .setAutoCollectDependencies(false) +// .setAutoCollectConsole(false) +// .setAutoDependencyCorrelation(false) +// .setOfflineMode(true) +// .start() +// .client; +// } + +// this.setContext(); +// this.stripPII(this._client); +// } + +// setContext(context?: { [key: string]: string }) { +// if (!this._context) { +// this._context = Object.create(null); + +// // Add vscode properties +// this._context['code.language'] = env.language; +// this._context['code.version'] = version; +// this._context[this._client.context.keys.sessionId] = env.sessionId; + +// // Add os properties +// this._context['os.platform'] = os.platform(); +// this._context['os.version'] = os.release(); +// } + +// if (context) { +// Object.assign(this._context, context); +// } + +// Object.assign(this._client.commonProperties, this._context); +// } + +// trackEvent(name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number; }) { +// this._client.trackEvent(name, properties, measurements); +// } + +// trackException(ex: Error) { +// this._client.trackException(ex); +// } + +// private stripPII(client: Client) { +// if (client && client.context && client.context.keys && client.context.tags) { +// const machineNameKey = client.context.keys.deviceMachineName; +// client.context.tags[machineNameKey] = ''; +// } +// } +// } \ No newline at end of file