mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-16 09:35:40 -05:00
Fixes #7 - missing spawn-rx dependency (argh!)
This commit is contained in:
@@ -2,15 +2,14 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp';
|
||||
//import {spawnPromise} from 'spawn-rx';
|
||||
const spawnRx = require('./spawn-rx');
|
||||
import {spawnPromise} from 'spawn-rx';
|
||||
|
||||
export * from './gitEnrichment';
|
||||
//export * from './enrichers/blameRegExpEnricher';
|
||||
export * from './enrichers/blameParserEnricher';
|
||||
|
||||
function gitCommand(cwd: string, ...args) {
|
||||
return spawnRx.spawnPromise('git', args, { cwd: cwd })
|
||||
return spawnPromise('git', args, { cwd: cwd })
|
||||
.then(s => {
|
||||
console.log('[GitLens]', 'git', ...args);
|
||||
return s;
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
//import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as net from 'net';
|
||||
import { Observable, Subscription, AsyncSubject } from 'rxjs';
|
||||
import * as sfs from 'fs';
|
||||
|
||||
const omit = require('lodash.omit');
|
||||
|
||||
const spawnOg = require('child_process').spawn;
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// const d = require('debug-electron')('surf:promise-array');
|
||||
|
||||
/**
|
||||
* stat a file but don't throw if it doesn't exist
|
||||
*
|
||||
* @param {string} file The path to a file
|
||||
* @return {Stats} The stats structure
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function statSyncNoException(file) {
|
||||
try {
|
||||
return sfs.statSync(file);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// 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(/[\\\/]/)) {
|
||||
// d('Path has slash in directory, bailing');
|
||||
return exe;
|
||||
}
|
||||
|
||||
let target = path.join('.', exe);
|
||||
if (statSyncNoException(target)) {
|
||||
// d(`Found executable in currect directory: ${target}`);
|
||||
return target;
|
||||
}
|
||||
|
||||
let haystack = process.env.PATH.split(isWindows ? ';' : ':');
|
||||
for (let p of haystack) {
|
||||
let needle = path.join(p, exe);
|
||||
if (statSyncNoException(needle)) return needle;
|
||||
}
|
||||
|
||||
// d('Failed to find executable anywhere in path');
|
||||
return exe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the actual 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.
|
||||
*
|
||||
* @param {string} exe The executable to run
|
||||
* @param {Array<string>} args The arguments to run
|
||||
*
|
||||
* @return {Object} The cmd and args to run
|
||||
* @property {string} cmd The command to pass to spawn
|
||||
* @property {Array<string>} args The arguments to pass to spawn
|
||||
*/
|
||||
export function findActualExecutable(exe, args) {
|
||||
// POSIX can just execute scripts directly, no need for silly goosery
|
||||
if (process.platform !== 'win32') return { cmd: runDownPath(exe), args: args };
|
||||
|
||||
if (!sfs.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 (let ext of possibleExts) {
|
||||
let possibleFullPath = runDownPath(`${exe}${ext}`);
|
||||
|
||||
if (sfs.existsSync(possibleFullPath)) {
|
||||
return findActualExecutable(possibleFullPath, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exe.match(/\.ps1$/i)) {
|
||||
let cmd = path.join(process.env.SYSTEMROOT, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
|
||||
let psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
|
||||
|
||||
return { cmd: cmd, args: psargs.concat(args) };
|
||||
}
|
||||
|
||||
if (exe.match(/\.(bat|cmd)$/i)) {
|
||||
let cmd = path.join(process.env.SYSTEMROOT, 'System32', 'cmd.exe');
|
||||
let cmdArgs = ['/C', `${exe} ${args.join(' ')}`];
|
||||
|
||||
return { cmd: cmd, args: cmdArgs };
|
||||
}
|
||||
|
||||
if (exe.match(/\.(js)$/i)) {
|
||||
let cmd = process.execPath;
|
||||
let nodeArgs = [exe];
|
||||
|
||||
return { cmd: cmd, args: nodeArgs.concat(args) };
|
||||
}
|
||||
|
||||
// Dunno lol
|
||||
return { cmd: exe, args: args };
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a process but detached from the current process. The process is put
|
||||
* into its own Process Group that can be killed by unsubscribing from the
|
||||
* return Observable.
|
||||
*
|
||||
* @param {string} exe The executable to run
|
||||
* @param {Array<string>} params The parameters to pass to the child
|
||||
* @param {Object} opts Options to pass to spawn.
|
||||
*
|
||||
* @return {Observable<string>} Returns an Observable that when subscribed
|
||||
* to, will create a detached process. The
|
||||
* process output will be streamed to this
|
||||
* Observable, and if unsubscribed from, the
|
||||
* process will be terminated early. If the
|
||||
* process terminates with a non-zero value,
|
||||
* the Observable will terminate with onError.
|
||||
*/
|
||||
export function spawnDetached(exe, params, opts=null) {
|
||||
let { cmd, args } = findActualExecutable(exe, params);
|
||||
|
||||
if (!isWindows) return spawn(cmd, args, Object.assign({}, opts || {}, {detached: true }));
|
||||
const newParams = [cmd].concat(args);
|
||||
|
||||
let target = path.join(__dirname, '..', 'vendor', 'jobber', 'jobber.exe');
|
||||
let options = Object.assign({}, opts || {}, { detached: true, jobber: true });
|
||||
|
||||
// d(`spawnDetached: ${target}, ${newParams}`);
|
||||
return spawn(target, newParams, options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawns a process attached as a child of the current process.
|
||||
*
|
||||
* @param {string} exe The executable to run
|
||||
* @param {Array<string>} params The parameters to pass to the child
|
||||
* @param {Object} opts Options to pass to spawn.
|
||||
*
|
||||
* @return {Observable<string>} Returns an Observable that when subscribed
|
||||
* to, will create a child process. The
|
||||
* process output will be streamed to this
|
||||
* Observable, and if unsubscribed from, the
|
||||
* process will be terminated early. If the
|
||||
* process terminates with a non-zero value,
|
||||
* the Observable will terminate with onError.
|
||||
*/
|
||||
export function spawn(exe, params=[], opts=null) {
|
||||
opts = opts || {};
|
||||
let spawnObs = Observable.create((subj) => {
|
||||
let proc = null;
|
||||
|
||||
let { cmd, args } = findActualExecutable(exe, params);
|
||||
// d(`spawning process: ${cmd} ${args.join()}, ${JSON.stringify(opts)}`);
|
||||
proc = spawnOg(cmd, args, omit(opts, 'jobber', 'split'));
|
||||
|
||||
let bufHandler = (source) => (b) => {
|
||||
if (b.length < 1) return;
|
||||
let chunk = "<< String sent back was too long >>";
|
||||
try {
|
||||
chunk = b.toString();
|
||||
} catch (e) {
|
||||
chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
|
||||
}
|
||||
|
||||
subj.next({source: source, text: chunk});
|
||||
};
|
||||
|
||||
let ret = new Subscription();
|
||||
|
||||
if (opts.stdin) {
|
||||
if (proc.stdin) {
|
||||
ret.add(opts.stdin.subscribe(
|
||||
(x) => proc.stdin.write(x),
|
||||
subj.error,
|
||||
() => proc.stdin.end()
|
||||
));
|
||||
} else {
|
||||
subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
|
||||
}
|
||||
}
|
||||
|
||||
let stderrCompleted = null;
|
||||
let stdoutCompleted = null;
|
||||
let noClose = false;
|
||||
|
||||
if (proc.stdout) {
|
||||
stdoutCompleted = new AsyncSubject();
|
||||
proc.stdout.on('data', bufHandler('stdout'));
|
||||
proc.stdout.on('close', () => { stdoutCompleted.next(true); stdoutCompleted.complete(); });
|
||||
} else {
|
||||
stdoutCompleted = Observable.of(true);
|
||||
}
|
||||
|
||||
if (proc.stderr) {
|
||||
stderrCompleted = new AsyncSubject();
|
||||
proc.stderr.on('data', bufHandler('stderr'));
|
||||
proc.stderr.on('close', () => { stderrCompleted.next(true); stderrCompleted.complete(); });
|
||||
} else {
|
||||
stderrCompleted = Observable.of(true);
|
||||
}
|
||||
|
||||
proc.on('error', (e) => {
|
||||
noClose = true;
|
||||
subj.error(e);
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
noClose = true;
|
||||
let pipesClosed = Observable.merge(stdoutCompleted, stderrCompleted)
|
||||
.reduce((acc) => acc, true);
|
||||
|
||||
if (code === 0) {
|
||||
pipesClosed.subscribe(() => subj.complete());
|
||||
} else {
|
||||
pipesClosed.subscribe(() => subj.error(new Error(`Failed with exit code: ${code}`)));
|
||||
}
|
||||
});
|
||||
|
||||
ret.add(new Subscription(() => {
|
||||
if (noClose) return;
|
||||
|
||||
// d(`Killing process: ${cmd} ${args.join()}`);
|
||||
if (opts.jobber) {
|
||||
// NB: Connecting to Jobber's named pipe will kill it
|
||||
net.connect(`\\\\.\\pipe\\jobber-${proc.pid}`);
|
||||
setTimeout(() => proc.kill(), 5*1000);
|
||||
} else {
|
||||
proc.kill();
|
||||
}
|
||||
}));
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
return opts.split ? spawnObs : spawnObs.pluck('text');
|
||||
}
|
||||
|
||||
function wrapObservableInPromise(obs) {
|
||||
return new Promise((res, rej) => {
|
||||
let out = '';
|
||||
|
||||
obs.subscribe(
|
||||
(x) => out += x,
|
||||
(e) => rej(new Error(`${out}\n${e.message}`)),
|
||||
() => res(out));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a process but detached from the current process. The process is put
|
||||
* into its own Process Group.
|
||||
*
|
||||
* @param {string} exe The executable to run
|
||||
* @param {Array<string>} params The parameters to pass to the child
|
||||
* @param {Object} opts Options to pass to spawn.
|
||||
*
|
||||
* @return {Promise<string>} Returns an Promise that represents a detached
|
||||
* process. The value returned is the process
|
||||
* output. If the process terminates with a
|
||||
* non-zero value, the Promise will resolve with
|
||||
* an Error.
|
||||
*/
|
||||
export function spawnDetachedPromise(exe, params, opts=null) {
|
||||
return wrapObservableInPromise(spawnDetached(exe, params, opts));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawns a process as a child process.
|
||||
*
|
||||
* @param {string} exe The executable to run
|
||||
* @param {Array<string>} params The parameters to pass to the child
|
||||
* @param {Object} opts Options to pass to spawn.
|
||||
*
|
||||
* @return {Promise<string>} Returns an Promise that represents a child
|
||||
* process. The value returned is the process
|
||||
* output. If the process terminates with a
|
||||
* non-zero value, the Promise will resolve with
|
||||
* an Error.
|
||||
*/
|
||||
export function spawnPromise(exe, params, opts=null) {
|
||||
return wrapObservableInPromise(spawn(exe, params, opts));
|
||||
}
|
||||
Reference in New Issue
Block a user