From b952cede45c54105bbde253b5dfefb500f037d9e Mon Sep 17 00:00:00 2001 From: Daniel Luxemburg Date: Thu, 10 Jun 2021 15:58:14 -0400 Subject: [PATCH] add spawnOptions plugin --- readme.md | 10 ++++++++++ src/lib/git-factory.ts | 2 ++ src/lib/plugins/index.ts | 1 + src/lib/plugins/simple-git-plugin.ts | 6 +++++- src/lib/plugins/spawn-options-plugin.ts | 14 ++++++++++++++ src/lib/runners/git-executor-chain.ts | 4 ++-- src/lib/types/index.ts | 4 ++++ src/lib/utils/util.ts | 7 +++++++ test/unit/__fixtures__/expectations.ts | 4 ++++ test/unit/plugins.spec.ts | 9 +++++++++ 10 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/lib/plugins/spawn-options-plugin.ts diff --git a/readme.md b/readme.md index d6255bb2..e9511d23 100644 --- a/readme.md +++ b/readme.md @@ -77,6 +77,16 @@ const git: SimpleGit = simpleGit('/some/path', { config: ['http.proxy=someproxy' await git.pull(); ``` +To set the user identity or group identity of the spawned git commands to something other than the owner of +the current Node process, supply a `spawnOptions` option with a `uid`, a `gid`, or both: + +```typescript +const git: SimpleGit = simpleGit('/some/path', { spawnOptions: { uid: 1000 } }); + +// any command executed will belong to system user 1000 +await git.pull(); +``` + ## Configuring Plugins - [Error Detection](./docs/PLUGIN-ERRORS.md) diff --git a/src/lib/git-factory.ts b/src/lib/git-factory.ts index 8216b3ce..de1f89c1 100644 --- a/src/lib/git-factory.ts +++ b/src/lib/git-factory.ts @@ -7,6 +7,7 @@ import { errorDetectionPlugin, PluginStore, progressMonitorPlugin, + spawnOptionsPlugin, timeoutPlugin } from './plugins'; import { createInstanceConfig, folderExists } from './utils'; @@ -53,6 +54,7 @@ export function gitInstanceFactory(baseDir?: string | Partial, config.progress && plugins.add(progressMonitorPlugin(config.progress)); config.timeout && plugins.add(timeoutPlugin(config.timeout)); + config.spawnOptions && plugins.add(spawnOptionsPlugin(config.spawnOptions)); plugins.add(errorDetectionPlugin(errorDetectionHandler(true))); config.errors && plugins.add(errorDetectionPlugin(config.errors)); diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index f005ffeb..b290bce8 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -3,4 +3,5 @@ export * from './error-detection.plugin'; export * from './plugin-store'; export * from './progress-monitor-plugin'; export * from './simple-git-plugin'; +export * from './spawn-options-plugin'; export * from './timout-plugin'; diff --git a/src/lib/plugins/simple-git-plugin.ts b/src/lib/plugins/simple-git-plugin.ts index 7e564a05..84362f37 100644 --- a/src/lib/plugins/simple-git-plugin.ts +++ b/src/lib/plugins/simple-git-plugin.ts @@ -1,4 +1,4 @@ -import { ChildProcess } from 'child_process'; +import { ChildProcess, SpawnOptions } from 'child_process'; import { GitExecutorResult } from '../types'; type SimpleGitTaskPluginContext = { @@ -11,6 +11,10 @@ export interface SimpleGitPluginTypes { data: string[]; context: SimpleGitTaskPluginContext & {}; }; + 'spawn.options': { + data: Partial; + context: SimpleGitTaskPluginContext & {}; + }; 'spawn.after': { data: void; context: SimpleGitTaskPluginContext & { diff --git a/src/lib/plugins/spawn-options-plugin.ts b/src/lib/plugins/spawn-options-plugin.ts new file mode 100644 index 00000000..ab389d60 --- /dev/null +++ b/src/lib/plugins/spawn-options-plugin.ts @@ -0,0 +1,14 @@ +import { SpawnOptions } from 'child_process'; +import { pick } from '../utils'; +import { SimpleGitPlugin } from './simple-git-plugin'; + +export function spawnOptionsPlugin(spawnOptions: Partial): SimpleGitPlugin<'spawn.options'> { + const options = pick(spawnOptions, ['uid', 'gid']); + + return { + type: 'spawn.options', + action(data) { + return {...options, ...data}; + }, + }; +} diff --git a/src/lib/runners/git-executor-chain.ts b/src/lib/runners/git-executor-chain.ts index 6a9d31e0..5a235616 100644 --- a/src/lib/runners/git-executor-chain.ts +++ b/src/lib/runners/git-executor-chain.ts @@ -150,11 +150,11 @@ export class GitExecutorChain implements SimpleGitExecutor { private async gitResponse(task: SimpleGitTask, command: string, args: string[], outputHandler: Maybe, logger: OutputLogger): Promise { const outputLogger = logger.sibling('output'); - const spawnOptions: SpawnOptions = { + const spawnOptions: SpawnOptions = this._plugins.exec('spawn.options', { cwd: this.cwd, env: this.env, windowsHide: true, - }; + }, pluginContext(task, task.commands)); return new Promise((done) => { const stdOut: Buffer[] = []; diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 1aa3c77e..c7206fab 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -1,3 +1,5 @@ +import { SpawnOptions } from 'child_process'; + import { SimpleGitTask } from './tasks'; import { SimpleGitProgressEvent } from './handlers'; @@ -85,6 +87,8 @@ export interface SimpleGitPluginConfig { */ block: number; }; + + spawnOptions: Pick; } /** diff --git a/src/lib/utils/util.ts b/src/lib/utils/util.ts index 2684f306..94c18b52 100644 --- a/src/lib/utils/util.ts +++ b/src/lib/utils/util.ts @@ -141,3 +141,10 @@ export function prefixedArray(input: T[], prefix: T): T[] { export function bufferToString (input: Buffer | Buffer[]): string { return (Array.isArray(input) ? Buffer.concat(input) : input).toString('utf-8'); } + +/** + * Get a new object from a source object with only the listed properties. + */ +export function pick (source: Record, properties: string[]) { + return Object.assign({}, ...properties.map((property) => property in source ? {[property]: source[property]} : {})); +} diff --git a/test/unit/__fixtures__/expectations.ts b/test/unit/__fixtures__/expectations.ts index 088aee15..58aff8ca 100644 --- a/test/unit/__fixtures__/expectations.ts +++ b/test/unit/__fixtures__/expectations.ts @@ -35,3 +35,7 @@ export function assertChildProcessEnvironmentVariables(env: any) { expect(mockChildProcessModule.$mostRecent()).toHaveProperty('$env', env); } +export function assertChildProcessSpawnOptions(options: any) { + expect(mockChildProcessModule.$mostRecent().$options).toMatchObject(options); +} + diff --git a/test/unit/plugins.spec.ts b/test/unit/plugins.spec.ts index cdef975e..8dfb0b30 100644 --- a/test/unit/plugins.spec.ts +++ b/test/unit/plugins.spec.ts @@ -1,5 +1,6 @@ import { SimpleGit } from '../../typings'; import { + assertChildProcessSpawnOptions, assertExecutedCommands, assertExecutedCommandsContainsOnce, closeWithSuccess, @@ -24,6 +25,14 @@ describe('plugins', () => { assertExecutedCommands('-c', 'a', '-c', 'bcd', 'foo'); }); + it('allows setting uid and gid', async () => { + git = newSimpleGit({spawnOptions: {uid: 1, gid: 2}}); + git.init(); + + await closeWithSuccess(); + assertChildProcessSpawnOptions({uid: 1, gid: 2}); + }); + describe('progress', () => { it('emits progress events when counting objects', async () => {