Skip to content

Commit

Permalink
Add function to force and forbid respawning
Browse files Browse the repository at this point in the history
  • Loading branch information
sttk committed Dec 10, 2017
1 parent 2e79265 commit 78000dc
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 2 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ flaggedRespawn(v8flags, process.argv, function (ready, child) {

```


## API

### <u>flaggedRespawn(flags, argv, [ forcedFlags, ] callback) : Void</u>

Respawns the script itself when *argv* has special flag contained in *flags* and/or *forcedFlags* is not empty. Because members of *flags* and *forcedFlags* are passed to `node` command, each of them needs to be a node flag or a V8 flag.

#### Forbid respawning

If `--no-respawning` flag is given in *argv*, this function does not respawned even if *argv* contains members of flags or *forcedFlags* is not empty. (This flag is also used internally to prevent from respawning more than once).

#### Parameter:

| Parameter | Type | Description |
|:--------------|:------:|:----------------------------------------------------|
| *flags* | Array | An array of node flags and V8 flags which are available when present in *argv*. |
| *argv* | Array | Command line arguments to respawn. |
| *forcedFlags* | Array | An array of node flags and V8 flags for respawning forcely. |
| *callback* | function | A called function when not respawning or after respawned. |

* **<u><i>callback</i>(ready, proc, argv) : Void</u>**

*callback* function is called both when respawned or not, and it can be distinguished by callback's argument: *ready*. (*ready* indicates whether a process spawned its child process (false) or not (true), but it does not indicate whether a process is a spawned child process or not. *ready* for a spawned child process is true.)

*argv* is an array of command line arguments which is respawned (when *ready* is false) or is passed current process except flags within *flags* and `--no-respawning` (when *ready* is true).

**Parameter:**

| Parameter | Type | Description |
|:----------|:-------:|:--------------------------|
| *ready* | boolean | True, if not respawning and is ready to execute main function. |
| *proc* | object | Child process object if respawned, otherwise current process object. |
| *argv* | Array | An array of command line arguments. |

## Release History

* 2016-03-22 - v0.3.2 - fix issue with v8 flags values being dropped
Expand Down
32 changes: 30 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
const reorder = require('./lib/reorder');
const respawn = require('./lib/respawn');
const remover = require('./lib/remover');

module.exports = function (flags, argv, execute) {
const FORBID_RESPAWNING_FLAG = '--no-respawning';

module.exports = function (flags, argv, forcedFlags, execute) {
if (!flags) {
throw new Error('You must specify flags to respawn with.');
}
if (!argv) {
throw new Error('You must specify an argv array.');
}

if (typeof forcedFlags === 'function') {
execute = forcedFlags;
forcedFlags = [];
} else if (!Array.isArray(forcedFlags)) {
forcedFlags = [];
}

var index = argv.indexOf(FORBID_RESPAWNING_FLAG);
if (index >= 0) {
argv = remover([FORBID_RESPAWNING_FLAG], argv);
argv = remover(flags, argv);
execute(true, process, argv);
return;
}

var proc = process;
var reordered = reorder(flags, argv);
var ready = JSON.stringify(argv) === JSON.stringify(reordered);

if (forcedFlags.length) {
reordered = reordered.slice(0, 1)
.concat(forcedFlags)
.concat(reordered.slice(1));
ready = false;
}

if (!ready) {
reordered.push(FORBID_RESPAWNING_FLAG);
proc = respawn(reordered);
}
execute(ready, proc);
execute(ready, proc, reordered);
};
11 changes: 11 additions & 0 deletions lib/remover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = function(flags, argv) {
var args = argv.slice(0, 1);
for (var i = 1, n = argv.length; i < n; i++) {
var arg = argv[i];
var flag = arg.split('=')[0];
if (flags.indexOf(flag) < 0) {
args.push(arg);
}
}
return args;
};
26 changes: 26 additions & 0 deletions test/bin/forbid-respawning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env node

const flaggedRespawn = require('../../');
const v8flags = require('v8flags');

// get a list of all possible v8 flags for the running version of node
v8flags(function(err, flags) {
if (err) {
console.error(err);
return;
}

var argv = process.argv.concat('--no-respawning');

flaggedRespawn(flags, argv, function (ready, child) {
if (ready) {
console.log('Running!');
} else {
console.log('Special flags found, respawning.');
}
if (child.pid !== process.pid) {
console.log('Respawned to PID:', child.pid);
}
});
});

23 changes: 23 additions & 0 deletions test/bin/force-and-forbid-respawning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node

const flaggedRespawn = require('../../');
const v8flags = require('v8flags');

// get a list of all possible v8 flags for the running version of node
v8flags(function(err, flags) {
if (err) {
console.error(err);
return;
}

var argv = process.argv.concat('--no-respawning');

flaggedRespawn(flags, argv, ['--trace-warnings'], function (ready, child) {
if (ready) {
console.log('Running!');
} else {
console.log('Respawning!');
}
});
});

21 changes: 21 additions & 0 deletions test/bin/force-respawning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env node

const flaggedRespawn = require('../../');
const v8flags = require('v8flags');

// get a list of all possible v8 flags for the running version of node
v8flags(function(err, flags) {
if (err) {
console.error(err);
return;
}

flaggedRespawn(flags, process.argv, ['--trace-warnings'], function (ready, child) {
if (ready) {
console.log('Running!');
} else {
console.log('Respawning!');
}
});
});

26 changes: 26 additions & 0 deletions test/bin/print-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env node

const flaggedRespawn = require('../..');
const v8flags = require('v8flags');
const path = require('path');

v8flags(function(err, flags) {
if (err) {
console.error(err);
return;
}

flaggedRespawn(flags, process.argv, [
'--trace-warnings',
'--require',
'v8flags',
'--v8-pool-size=2',
], function (ready, child, args) {
if (ready) {
console.log('cli args passed to app:', args.join(' '));
} else {
console.log('Respawning!');
}
});
});

164 changes: 164 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const expect = require('chai').expect;
const exec = require('child_process').exec;
const os = require('os');
const path = require('path');

const reorder = require('../lib/reorder');
const isV8flags = require('../lib/is-v8flags');
const remover = require('../lib/remover');
const flaggedRespawn = require('../');

describe('flaggedRespawn', function () {
Expand Down Expand Up @@ -57,6 +59,29 @@ describe('flaggedRespawn', function () {

});

describe('remover', function() {
it('should remove args included in flags', function() {
var needsRespawn = ['node', 'file.js', '--flag', '--harmony', 'command'];
var noRespawnNeeded = ['node', 'bin/flagged-respawn', 'thing'];
expect(remover(flags, needsRespawn))
.to.deep.equal(['node', 'file.js', '--flag', 'command']);
expect(reorder(flags, noRespawnNeeded))
.to.deep.equal(noRespawnNeeded);
});

it('should remove a arg even when the arg has value', function() {
var args = ['node', 'file.js', '--stack_size=2048'];
var expected = ['node', 'file.js'];
expect(remover(flags, args)).to.deep.equal(expected);
});

it('should remove special flags when they are in the correct position', function () {
var args = ['node', '--harmony', 'file.js', '--flag'];
var expected = ['node', 'file.js', '--flag'];
expect(reorder(flags, remover(flags, args))).to.deep.equal(expected);
});
});

describe('execute', function () {

it('should throw if no flags are specified', function () {
Expand Down Expand Up @@ -140,6 +165,134 @@ describe('flaggedRespawn', function () {

});

describe('force and forbid respawning', function() {
it('forbid respawning with --no-respawning flag', function(done) {
var cmd = [
'node',
path.resolve(__dirname, 'bin/respawner.js'),
'--harmony',
'--no-respawning',
].join(' ');;

exec(cmd, function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal('Running!\n');
done();
});
});

it('always forbid respawning with inner --no-respawning', function(done) {
var cmd = [
'node',
path.resolve(__dirname, 'bin/forbid-respawning.js'),
'--harmony',
].join(' ');;

exec(cmd, function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal('Running!\n');
done();
});
});

it('should force respawning with node flags', function(done) {
var cmd = [
'node',
path.resolve(__dirname, 'bin/force-respawning.js'),
].join(' ');;

exec(cmd, function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal('Respawning!\nRunning!\n');
done();
});
});

it('should take priority to forbidding than forcinge', function(done) {
exec('node ./test/bin/force-and-forbid-respawning.js', cb);

function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal('Running!\n');
done();
}
});
});

describe('cli args which are passed to app', function() {
it('should pass args except v8flags, forced node flags, --no-respawning when respawned', function(done) {
var script = path.resolve(__dirname, 'bin/print-args.js');
var cmd = [
'node',
script,
'aaa',
'--harmony',
'-q',
'1234',
'--cwd',
'bbb/ccc/ddd',
'--experimental_extras',
'-V',
].join(' ');

exec(cmd, function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal('Respawning!\n' +
'cli args passed to app: ' + [
process.argv[0],
script,
'aaa',
'-q',
'1234',
'--cwd',
'bbb/ccc/ddd',
'-V',
].join(' ') + '\n');
done();
});
});

it('should pass args except v8flags, forced node flags, --no-respawning when not respawned', function(done) {
var script = path.resolve(__dirname, 'bin/print-args.js');
var cmd = [
'node',
script,
'aaa',
'--harmony',
'-q',
'1234',
'--cwd',
'bbb/ccc/ddd',
'--experimental_extras',
'-V',
'--no-respawning',
].join(' ');;

exec(cmd, function cb(err, stdout, stderr) {
expect(err).to.equal(null);
expect(stderr).to.equal('');
expect(stdout).to.equal(
'cli args passed to app: ' + [
process.argv[0],
script,
'aaa',
'-q',
'1234',
'--cwd',
'bbb/ccc/ddd',
'-V',
].join(' ') + '\n');
done();
});
});

});

describe('parameter checks', function() {

it('should throw an error when flags is nullish', function() {
Expand All @@ -153,6 +306,17 @@ describe('flaggedRespawn', function () {
expect(function() {
flaggedRespawn(flags, undefined, exec);
}).throws(Error);

});

it('should not respawn if forced flags is not an array', function(done) {
var argv = ['node', './test/bin/respawner'];

flaggedRespawn(flags, argv, '--harmony', function(ready, child) {
expect(ready).to.be.true;
expect(child.pid).to.equal(process.pid);
done();
});
});
});

Expand Down

0 comments on commit 78000dc

Please sign in to comment.