Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

machine-as-script

Package Overview
Dependencies
Maintainers
2
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

machine-as-script - npm Package Compare versions

Comparing version 3.0.2 to 4.0.0-0

91

bin/machine-as-script.js

@@ -17,2 +17,3 @@ #!/usr/bin/env node

var _ = require('lodash');
var chalk = require('chalk');
var asScript = require('../');

@@ -22,10 +23,88 @@

if (_.isUndefined(yargs.argv._[0])) {
console.error('Usage:');
console.error('machine-as-script path/to/the/machine/def/you/want/to/use.js');
console.error(chalk.bold('MACHINE AS SCRIPT')+' -- Run any machine as a script.');
console.error();
console.error();
console.error(chalk.bold('USAGE'));
console.error(' machine-as-script path/to/the/machine/def/you/want/to/use.js');
console.error();
console.error();
console.error(chalk.bold('NOTES'));
console.error(' You can specify the path to a "dry" node machine def (e.g. `machines/foo.js`)');
console.error(' or to a module which exports a "wet" machine (i.e. already instantiated)');
console.error();
return;
}
var pathToMachineDef = path.resolve(yargs.argv._[0]);
asScript({
machine: require(pathToMachineDef)
}).exec();
// Resolve the path.
var absPath = path.resolve(yargs.argv._[0]);
// Load the machine.
var wetOrDryMachine;
try {
wetOrDryMachine = require(absPath);
}
catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
console.error(chalk.bold(chalk.yellow('Could not locate a module at the specified path (`'+absPath+'`).')));
console.error('Details:');
throw e;
}
else {
console.error(chalk.bold(chalk.yellow('Encountered an error when trying to load the module at the specified path (`'+absPath+'`).')));
console.error();
console.error('Error details:');
throw e;
}
}
if (_.isEqual(wetOrDryMachine, {})) {
console.error(chalk.bold(chalk.yellow('The module at the specified path (`'+absPath+'`) does not see to export anything.')));
console.error(chalk.bold(chalk.yellow('Specifically, requiring it yielded an empty dictionary (`{}`).')));
console.error('--------------------------------------------------------------------------');
console.error('TROUBLESHOOTING:');
console.error();
console.error('This could be for any of the following reasons:');
console.error(' => It does not export anything.');
console.error();
console.error(' => It requires a cyclical dependency.');
console.error(' (For more info on that, see: https://nodejs.org/api/modules.html#modules_cycles)');
console.error();
console.error(' => Or (unlikely) it _actually_ exports an empty dictionary (`{}`)');
console.error();
console.error('Check and make sure that the code at the specified path does either:');
console.error('```');
console.error('module.exports = {...<<machine def here>>...};');
console.error('```');
console.error();
console.error('Or:');
console.error('```');
console.error('module.exports = require(\'machine\').build({...<<machine def here>>...});');
console.error('```');
console.error();
console.error('--------------------------------------------------------------------------');
return;
}
// If the module at the specified path exports a wet machine that is already wrapped
// in a call machine-as-script, then fail with an error message. This shouldn't ever
// really happen, since scripts don't normally export anything, but it's possible, so
// we aim to handle this case in a way that helps diagnose the problem as quickly as
// possible.
if (wetOrDryMachine._telltale === 'machine-as-script') {
console.error(chalk.bold(chalk.yellow('The script at the specified path ran. But...')));
console.error('It looks like the module at `'+absPath+'` is using machine-as-script internally.');
console.error('-----------------------------------------------------------------------------------------------------------------------');
console.error('First of all, the `machine-as-script` command-line tool is designed to be used with "dry" node machine definitions,');
console.error('or with modules which export "wet" node machine instances. It should not be called on scripts.');
console.error('Secondly, it shouldn\'t even be possible for me to know this!');
console.error('Modules which use `require(\'machine-as-script\')` should never export anything-- they\'re designed to be run in-place.');
console.error('-----------------------------------------------------------------------------------------------------------------------');
return;
}
// Otherwise, wrap it with machine-as-script, then exec() it.
else {
asScript({
machine: wetOrDryMachine
}).exec();
}

@@ -16,14 +16,19 @@ /**

/**
* asScript()
*
* @param {Dictionary|Machine} opts
* @property {Dictionary|Machine} opts.machine
* @property {Array} opts.args
* @property {Array} opts.envVarNamespace
* (see readme for more information)
* (See README.md for more information.)
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* @param {Dictionary|Machine} optsOrMachineDef
* @property {Dictionary?} machine
* @property {Array?} args
* @property {Array?} envVarNamespace
* @property {SailsApp?} sails
*
* @return {Machine} [a machine instance]
* @return {Machine}
* A live machine instance, but warped to accept CLI args/opts & env vars.
* Also granted other special abilities.
*/
module.exports = function runMachineAsScript(opts){
module.exports = function runMachineAsScript(optsOrMachineDef){
opts = opts||{};
optsOrMachineDef = optsOrMachineDef||{};

@@ -35,12 +40,16 @@ // Use either `opts` or `opts.machine` as the machine definition

var machineDef;
if (!opts.machine) {
machineDef = opts;
var opts;
var MISC_OPTIONS = ['args', 'envVarNamespace', 'sails'];
if (!optsOrMachineDef.machine) {
machineDef = optsOrMachineDef;
opts = _.pick(optsOrMachineDef, MISC_OPTIONS);
}
else {
machineDef = opts.machine;
delete opts.machine;
machineDef = optsOrMachineDef.machine;
opts = _.pick(optsOrMachineDef, MISC_OPTIONS);
}
// Tolerate if no machine was provided (this is just for backwards compatibility-- should be deprecated.)
machineDef = machineDef || {};
if (!_.isObject(machineDef)) {
throw new Error('Consistency violation: Machine definition must be provided as a dictionary.');
}

@@ -80,3 +89,3 @@ // Set up namespace for environment variables.

fn: function (inputs, exits){
exits.error(new Error('Not implemented yet!'));
exits.error(new Error('Not implemented yet! (This is a default `fn` injected by `machine-as-script`.)'));
}

@@ -87,2 +96,33 @@ },machineDef));

// Finally, before moving on, we check the `habitat` and potentially provide
// access to `env.sails`.
var sailsApp;
if (wetMachine.habitat === 'request') {
throw new Error('The target machine defintion declares a dependency on the `request` habitat, which cannot be provided via the command-line interface. This machine cannot be run using machine-as-script.');
}
else if (wetMachine.habitat === 'sails') {
// If the machine depends on the Sails habitat, then we'll attempt to use the provided version of `sails`.
if (opts.sails) {
if (!_.isObject(opts.sails) || opts.sails.constructor.name !== 'Sails') {
throw new Error('The supposed Sails app instance provided as `sails` seems a little sketchy. Make sure you are doing `sails: require(\'sails\')`.');
}
// Down below, we'll attempt to load (but not lift) the Sails app in the current working directory.
// If it works, then we'll run the script, providing it with `env.sails`. After that, regardless of
// how the script exits, we'll call `sails.lower()` to clean up.
sailsApp = opts.sails;
}
// If no `sails` was provided to machine-as-script, then we'll throw an error.
else {
throw new Error('The target machine defintion declares a dependency on the `sails` habitat, but no `sails` app instance was provided as a top-level option to machine-as-script. Make sure this script module is doing: `sails: require(\'sails\')`');
}
}
// ======================================================================
// Now we'll put together the configuration for our wet machine instance.
// (using CLI opts, serial CLI args, and/or env vars)
// ======================================================================
// Configure CLI usage helptext and set up commander

@@ -191,34 +231,130 @@ program.usage('[options]');

// Set some default exit handlers
liveMachine.setExits({
error: function(err) {
// console.error(chalk.red('Unexpected error occurred:\n'), err);
console.log(chalk.red('Something went wrong:'));
console.error(err.stack ? chalk.gray(err.stack) : err);
},
success: function(output) {
// If output is expected, then log it.
if (!_.isUndefined(output)) {
try {
if (
!_.isUndefined(liveMachine.exits.success.example) ||
_.isFunction(liveMachine.exits.success.getExample) ||
!_.isUndefined(liveMachine.exits.success.like) ||
!_.isUndefined(liveMachine.exits.success.itemOf)
) {
// TODO: support json-encoded output vs colors
console.log(util.inspect(output, {depth: null, colors: true}));
// Now build up a default handler callback for each exit.
// (Note that these can be overridden though!)
var callbacks = {};
// We use a local variable (`alreadyExited`) as a spinlock.
var alreadyExited;
_.each(_.keys(wetMachine.exits), function builtExitCallback(exitCodeName){
// Build a callback for this exit that sends the appropriate response.
callbacks[exitCodeName] = function respondApropos(output){
// This spinlock protects against the machine calling more than one
// exit, or the same exit twice.
if (alreadyExited) { return; }
alreadyExited = true;
if (exitCodeName === 'error') {
console.error(chalk.red('Unexpected error occurred:\n'), output);
console.error(output.stack ? chalk.gray(output.stack) : output);
return;
}
else if (exitCodeName === 'success') {
if (_.isUndefined(output)) {
try {
if (
!_.isUndefined(liveMachine.exits.success.example) ||
_.isFunction(liveMachine.exits.success.getExample) ||
!_.isUndefined(liveMachine.exits.success.like) ||
!_.isUndefined(liveMachine.exits.success.itemOf)
) {
// TODO: support json-encoded output vs colors
console.log(util.inspect(output, {depth: null, colors: true}));
}
}
catch (e) { /* fail silently if anything goes awry */ }
}
catch (e) { /* fail silently if anything goes awry */ }
return;
// Otherwise, output is expected. So log it.
else {
console.log(chalk.green('OK.'));
}
}
// Miscellaneous exit.
else {
console.log(chalk.cyan('Something went wrong:'));
console.error(output.stack ? chalk.gray(output.stack) : output);
}
};//</callback definition>
});//</each exit>
// Otherwise, log a generic message.
console.log(chalk.green('OK.'));
// Now intercept `.exec()` to take care of sails.lower(), if relevant.
// (we have to do this because any of the callbacks above _could_ be overridden!)
var _originalExecBeforeItWasChangedForUseByMachineAsScript = liveMachine.exec;
liveMachine.exec = function () {
var args = Array.prototype.slice.call(arguments);
// If we're not managing a Sails app instance for this script, then just do the normal thing.
if (_.isUndefined(sailsApp)) {
if (_.isObject(args[0])) {
var combinedCbs = _.extend({}, callbacks, args[0]);
_originalExecBeforeItWasChangedForUseByMachineAsScript.apply(liveMachine, [combinedCbs]);
}
else if (_.isFunction(args[0])) {
_originalExecBeforeItWasChangedForUseByMachineAsScript.apply(liveMachine, [args[0]]);
}
else {
_originalExecBeforeItWasChangedForUseByMachineAsScript.apply(liveMachine, [callbacks]);
}
return;
}
});
// --• Otherwise, we need to load Sails first, then lower it afterwards.
// Load the Sails app.
sailsApp.load(function (err){
if (err) {
throw new Error('This script relies on access to Sails, but when attempting to load this Sails app automatically, an error occurred. Details: '+err.stack);
}
// Run underlying .exec(), but intercept it to tear down the Sails app.
_originalExecBeforeItWasChangedForUseByMachineAsScript.apply(liveMachine, [function (sbErr, successResult){
sailsApp.lower(function (sailsLowerErr) {
if (sailsLowerErr) {
console.warn('This script relies on access to Sails, but when attempting to lower this Sails app automatically after running the script, an error occurred. Details:',sailsLowerErr.stack);
console.warn('Continuing to run the appropriate exit callback anyway...');
}
// Success
if (!sbErr) {
if (_.isObject(args[0])) {
if (args[0].success) { args[0].success(successResult); }
else { callbacks.success(successResult); }
}
else if (_.isFunction(args[0])) {
args[0](undefined, successResult);
}
else { callbacks.success(successResult); }
}
// Some other exit (or catchall error)
else {
if (_.isObject(args[0]) && _.contains(_.keys(args[0]), sbErr.exit)) {
args[0][sbErr.exit](sbErr.output);
}
else if (_.isFunction(args[0])) {
args[0](sbErr);
}
else if (_.contains(_.keys(callbacks), sbErr.exit)) {
callbacks[sbErr.exit](sbErr.output);
}
else { callbacks.error(sbErr); }
}
});//</after sails.lower()>
}]);//</after calling underlying .exec()>
});//</after sails.load()>
};//</definition of our .exec() override>
// If we're managing a Sails app instance for this script, then pass through `env.sails`.
if (!_.isUndefined(sailsApp)) {
liveMachine.setEnv({ sails: sailsApp });
}
// Set a telltale property to allow `bin/machine-as-script` to be more
// intelligent about catching wet machine instances which are already wrapped
// in a call to machine-as-script. Realistically, this rarely matters since
// script modules don't normally export anything, but it's here just in case.
liveMachine._telltale = 'machine-as-script';
// Return the ready-to-exec machine.

@@ -225,0 +361,0 @@ return liveMachine;

4

package.json
{
"name": "machine-as-script",
"version": "3.0.2",
"version": "4.0.0-0",
"description": "Run a machine as a command-line script.",

@@ -22,3 +22,3 @@ "scripts": {

"lodash": "3.10.1",
"machine": "^12.3.0",
"machine": "^13.0.0-0",
"rttc": "^9.7.0",

@@ -25,0 +25,0 @@ "yargs": "3.4.5"

@@ -5,4 +5,5 @@ # machine-as-script

Useful for running jobs (cron, Heroku scheduler), automating repetitive tasks (Grunt, gulp), writing one-off scripts (NPM, Chef), and building production-ready tools with command-line interfaces (e.g. `treeline`, `machinepack`). Suppots _serial command-line arguments_, command-line opts (`--`), and environment variables.
Useful for running jobs (cron, Heroku scheduler), automating repetitive tasks (Grunt, gulp), writing one-off scripts (NPM, Chef), and building production-ready tools with command-line interfaces (e.g. `treeline`, `machinepack`). Supports _serial command-line arguments_, command-line opts (`--`), and environment variables.
```sh

@@ -41,2 +42,3 @@ $ npm install machine-as-script --save

##### Assorted examples

@@ -53,3 +55,15 @@

## Available Options
Aside from the [normal properties that go into a Node Machine definition](http://node-machine.org/spec), the following additional options are supported:
| Option | Type | Description |
|:------------------|-----------------|:-------------------------------------------------------|
| `machine` | ((dictionary?)) | If specified, `machine-as-script` will use this as the machine definition. Otherwise by default, it expects the machine definition to be passed in at the top-level. In that case, the non-standard (machine-as-script-specific) options are omitted when the machine is built).
| `args` | ((array?)) | The names of inputs, in order, to use for handling serial CLI arguments (more on that [below](#using-serial-cli-arguments)).
| `envVarNamespace` | ((string?)) | The namespace to use when mapping environment variables to runtime arguments for particular inputs (more on that [below](#using-environment-variables)).
| `sails` | ((SailsApp?)) | Only relevant if the machine def declares `habitat: 'sails'`. This is the Sails app instance that will be provided to this machine as `env.sails`. In most cases, if you are using this, you'll want to set it to `require('sails'). The Sails app instance will be automatically loaded before running the machine, and automatically lowered as soon as the machine exits.
## Using serial CLI arguments

@@ -83,2 +97,3 @@

> Note: In a future release, this will likely change to rely on either something like `env.args` or to use a new option to assign an input for this purpose (similar to machine-as-action's `urlWildcardSuffix`)

@@ -250,2 +265,2 @@

MIT &copy; 2015 Mike McNeil
MIT &copy; 2015-2016 Mike McNeil, The Treeline Co.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc