Skip to content

Commit

Permalink
feat: create the spawnOptions plugin to allow setting uid / gid
Browse files Browse the repository at this point in the history
… owner for the spawned `git` child processes.
  • Loading branch information
steveukx authored Jun 12, 2021
2 parents 8603971 + 116374d commit cc70220
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 3 deletions.
18 changes: 18 additions & 0 deletions docs/PLUGIN-SPAWN-OPTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Process Owner User / Group

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: { gid: 20 } });

// any command executed will belong to system group 20
await git.pull();
```

```typescript
const git: SimpleGit = simpleGit('/some/path', { spawnOptions: { uid: 1000 } });

// any command executed will belong to system user 1000
await git.pull();
```
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ await git.pull();
- [Progress Events](./docs/PLUGIN-PROGRESS-EVENTS.md)
Receive progress events as `git` works through long-running processes.

- [Spawned Process Ownership](./docs/PLUGIN-SPAWN-OPTIONS.md)
Configure the system `uid` / `gid` to use for spawned `git` processes.

- [Timeout](./docs/PLUGIN-TIMEOUT.md)
Automatically kill the wrapped `git` process after a rolling timeout.

Expand Down
2 changes: 2 additions & 0 deletions src/lib/git-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
errorDetectionPlugin,
PluginStore,
progressMonitorPlugin,
spawnOptionsPlugin,
timeoutPlugin
} from './plugins';
import { createInstanceConfig, folderExists } from './utils';
Expand Down Expand Up @@ -53,6 +54,7 @@ export function gitInstanceFactory(baseDir?: string | Partial<SimpleGitOptions>,

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));
Expand Down
1 change: 1 addition & 0 deletions src/lib/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
6 changes: 5 additions & 1 deletion src/lib/plugins/simple-git-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChildProcess } from 'child_process';
import { ChildProcess, SpawnOptions } from 'child_process';
import { GitExecutorResult } from '../types';

type SimpleGitTaskPluginContext = {
Expand All @@ -11,6 +11,10 @@ export interface SimpleGitPluginTypes {
data: string[];
context: SimpleGitTaskPluginContext & {};
};
'spawn.options': {
data: Partial<SpawnOptions>;
context: SimpleGitTaskPluginContext & {};
};
'spawn.after': {
data: void;
context: SimpleGitTaskPluginContext & {
Expand Down
14 changes: 14 additions & 0 deletions src/lib/plugins/spawn-options-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SpawnOptions } from 'child_process';
import { pick } from '../utils';
import { SimpleGitPlugin } from './simple-git-plugin';

export function spawnOptionsPlugin(spawnOptions: Partial<SpawnOptions>): SimpleGitPlugin<'spawn.options'> {
const options = pick(spawnOptions, ['uid', 'gid']);

return {
type: 'spawn.options',
action(data) {
return {...options, ...data};
},
};
}
4 changes: 2 additions & 2 deletions src/lib/runners/git-executor-chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ export class GitExecutorChain implements SimpleGitExecutor {

private async gitResponse<R>(task: SimpleGitTask<R>, command: string, args: string[], outputHandler: Maybe<outputHandler>, logger: OutputLogger): Promise<GitExecutorResult> {
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[] = [];
Expand Down
4 changes: 4 additions & 0 deletions src/lib/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SpawnOptions } from 'child_process';

import { SimpleGitTask } from './tasks';
import { SimpleGitProgressEvent } from './handlers';

Expand Down Expand Up @@ -85,6 +87,8 @@ export interface SimpleGitPluginConfig {
*/
block: number;
};

spawnOptions: Pick<SpawnOptions, 'uid' | 'gid'>;
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/lib/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,10 @@ export function prefixedArray<T>(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<string, any>, properties: string[]) {
return Object.assign({}, ...properties.map((property) => property in source ? {[property]: source[property]} : {}));
}
4 changes: 4 additions & 0 deletions test/unit/__fixtures__/expectations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

20 changes: 20 additions & 0 deletions test/unit/plugins.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SimpleGit } from '../../typings';
import {
assertChildProcessSpawnOptions,
assertExecutedCommands,
assertExecutedCommandsContainsOnce,
closeWithSuccess,
Expand All @@ -24,6 +25,25 @@ describe('plugins', () => {
assertExecutedCommands('-c', 'a', '-c', 'bcd', 'foo');
});

describe('spawnOptions', () => {
it('allows setting uid and gid', async () => {
git = newSimpleGit({spawnOptions: {uid: 1, gid: 2}});
git.init();

await closeWithSuccess();
assertChildProcessSpawnOptions({uid: 1, gid: 2});
});

it('sets process ids along with environment variables', async () => {
git = newSimpleGit({spawnOptions: {gid: 2}});
git.env({hello: 'world'});
git.raw('rev-parse', '--show-toplevel');

await closeWithSuccess();
assertChildProcessSpawnOptions({gid: 2, env: {hello: 'world'}});
});
});

describe('progress', () => {

it('emits progress events when counting objects', async () => {
Expand Down

0 comments on commit cc70220

Please sign in to comment.