<commit_before>'use strict';

var init      = exports;
var node_path = require('path');
var fs        = require('fs');
var fse       = require('fs-extra');
var asks      = require('asks');
var spawn     = require('child_process').spawn;
var prompts   = require('cortex-init-prompts');
var generator = require('cortex-scaffold-generator');
var neuron    = require('neuronjs');

// @param {Object} options see 'lib/option/adduser.js' for details
init.run = function(options, callback) {
  var self = this;

  this.logger.info([
    'This utility helps to create the basic scaffold for your cortex project.',
    'It will walk you through creating a cortex.json and some other files.',
    '',
    'Use `cortex install <pkg> --save` afterwards to install a package',
    '  and save it as a dependency in the package.json file.',
    '',
    'Press {{cyan ^C}} at any time to quit.\n',

  ].join('\n'));

  this._check_cwd(options.cwd, function (err, empty) {
    if (err) {
      return callback(err);
    }

    options.empty = empty;

    if (empty) {
      options._force = 'overriding';
      return self._run(options, callback);
    }

    if (options._force) {
      return self._run(options, callback);
    }

    // If the dir is not empty, ask
    self._ask_about_forcing(options, function () {
      self._run(options, callback);
    });
  });
};

init._run = function (options, callback) {
  switch(options._force){
    case 'none':
      this.logger.info('Cancelled by user.');
      return callback(null);

    // case 'updating':
    //   return this._fulfill_cortex_json(options, callback);

    // 'overriding'
    // 'skipping'
    default:
      return this._new(options, callback);
  }
};

init._check_cwd = function (cwd, callback) {
  fs.readdir(cwd, function (err, files) {
    if (err) {
      return callback({
        code: 'EREADDIR',
        message: 'Error reading "' + cwd + '": ' + err.stack,
        data: {
          dir: dir,
          error: err
        }
      });
    }

    callback(null, !files.length);
  });
};

var SKIPPED_MAP = {
  username: 'author_name',
  email: 'author_email'
};
init._skipped = function () {
  var profile = this.profile;
  var skip = {};
  Object.keys(SKIPPED_MAP).forEach(function (key) {
    var value = profile.get(key);
    if (value) {
      skip[SKIPPED_MAP[key]] = value;
    }
  });

  return skip;
};

// Creates new projects
init._new = function (options, callback) {
  this.logger.info(
    '\n' +
    '{{underline To create the scaffold, Cortex wants to ask you some questions:}}'
  );

  var self = this;
  prompts({
    licenses: generator.availableLicenses(),
    skip: this._skipped()

  }, function (results) {
    options.pkg = results;

    self._confirm_data(options, function (confirm) {
      if (confirm) {
        return self._generate(options, callback);
      }

      self.logger.info('{{red|bold Aborted!}}');
      callback(null);
    });
  });
};

init._confirm_data = function (options, callback) {
  this.logger.info('\n');
  this.logger.info( JSON.stringify(options.pkg, null, 2) );
  this.logger.info('\n');

  asks.prompt([
    {
      name: 'confirm',
      default: true,
      message: 'About to write files. Is this ok?',
      type: 'confirm'
    }
  ], function (result) {
    callback(result.confirm);
  });
};

// Generates the current project.
init._generate = function (options, callback) {
  var self = this;
  generator(options.pkg, {
    override: options._force === 'overriding',
    cwd: options.cwd
    neuron_version: neuron.version()

  }, callback);
};

var SCHEMA = [{
  name: 'choice',
  message: 'For the current directory is not empty, what should cortex do?',
  type: 'list',
  choices: [
    'Skip: Skip existing files, and initialize other files.',
    'Override: override existing files.',
    // 'Update: only help me update and fulfill cortex.json file.',
    'Cancel: cancel initializing.'
  ]
}];

var CHOICES = {
  U: 'updating',
  C: 'none',
  O: 'overriding',
  S: 'Skipping'
};

init._ask_about_forcing = function (options, callback) {
  asks.prompt(SCHEMA, function (result) {
    var choice = CHOICES[result.choice.slice(0, 1)];
    options._force = choice;
    callback();
  });
};
<commit_msg>command/init: fixes a syntax bug; #402<commit_after>'use strict';

var init      = exports;
var node_path = require('path');
var fs        = require('fs');
var fse       = require('fs-extra');
var asks      = require('asks');
var spawn     = require('child_process').spawn;
var prompts   = require('cortex-init-prompts');
var generator = require('cortex-scaffold-generator');
var neuron    = require('neuronjs');

// @param {Object} options see 'lib/option/adduser.js' for details
init.run = function(options, callback) {
  var self = this;

  this.logger.info([
    'This utility helps to create the basic scaffold for your cortex project.',
    'It will walk you through creating a cortex.json and some other files.',
    '',
    'Use `cortex install <pkg> --save` afterwards to install a package',
    '  and save it as a dependency in the package.json file.',
    '',
    'Press {{cyan ^C}} at any time to quit.\n',

  ].join('\n'));

  this._check_cwd(options.cwd, function (err, empty) {
    if (err) {
      return callback(err);
    }

    options.empty = empty;

    if (empty) {
      options._force = 'overriding';
      return self._run(options, callback);
    }

    if (options._force) {
      return self._run(options, callback);
    }

    // If the dir is not empty, ask
    self._ask_about_forcing(options, function () {
      self._run(options, callback);
    });
  });
};

init._run = function (options, callback) {
  switch(options._force){
    case 'none':
      this.logger.info('Cancelled by user.');
      return callback(null);

    // case 'updating':
    //   return this._fulfill_cortex_json(options, callback);

    // 'overriding'
    // 'skipping'
    default:
      return this._new(options, callback);
  }
};

init._check_cwd = function (cwd, callback) {
  fs.readdir(cwd, function (err, files) {
    if (err) {
      return callback({
        code: 'EREADDIR',
        message: 'Error reading "' + cwd + '": ' + err.stack,
        data: {
          dir: dir,
          error: err
        }
      });
    }

    callback(null, !files.length);
  });
};

var SKIPPED_MAP = {
  username: 'author_name',
  email: 'author_email'
};
init._skipped = function () {
  var profile = this.profile;
  var skip = {};
  Object.keys(SKIPPED_MAP).forEach(function (key) {
    var value = profile.get(key);
    if (value) {
      skip[SKIPPED_MAP[key]] = value;
    }
  });

  return skip;
};

// Creates new projects
init._new = function (options, callback) {
  this.logger.info(
    '\n' +
    '{{underline To create the scaffold, Cortex wants to ask you some questions:}}'
  );

  var self = this;
  prompts({
    licenses: generator.availableLicenses(),
    skip: this._skipped()

  }, function (results) {
    options.pkg = results;

    self._confirm_data(options, function (confirm) {
      if (confirm) {
        return self._generate(options, callback);
      }

      self.logger.info('{{red|bold Aborted!}}');
      callback(null);
    });
  });
};

init._confirm_data = function (options, callback) {
  this.logger.info('\n');
  this.logger.info( JSON.stringify(options.pkg, null, 2) );
  this.logger.info('\n');

  asks.prompt([
    {
      name: 'confirm',
      default: true,
      message: 'About to write files. Is this ok?',
      type: 'confirm'
    }
  ], function (result) {
    callback(result.confirm);
  });
};

// Generates the current project.
init._generate = function (options, callback) {
  var self = this;
  generator(options.pkg, {
    override: options._force === 'overriding',
    cwd: options.cwd,
    neuron_version: neuron.version()

  }, callback);
};

var SCHEMA = [{
  name: 'choice',
  message: 'For the current directory is not empty, what should cortex do?',
  type: 'list',
  choices: [
    'Skip: Skip existing files, and initialize other files.',
    'Override: override existing files.',
    // 'Update: only help me update and fulfill cortex.json file.',
    'Cancel: cancel initializing.'
  ]
}];

var CHOICES = {
  U: 'updating',
  C: 'none',
  O: 'overriding',
  S: 'Skipping'
};

init._ask_about_forcing = function (options, callback) {
  asks.prompt(SCHEMA, function (result) {
    var choice = CHOICES[result.choice.slice(0, 1)];
    options._force = choice;
    callback();
  });
};
<|endoftext|>


<commit_before>"use strict";

var jub = require("./jub.js");
var helper = require("./helper.js");
var logging = require("./logging.js");
var taskModule = require("./task.js");
var tempdir = require("./tempdir.js");
var tools = require("./tools.js");
var fs = require("fs");
var pathModule = require("path");

var HEADER = "Jub: Task runner for Node.";

var USAGE = "Usage\n" +
            "  jub [options] [taskName ...]\n\n" +

            "Options\n" +
            "  -f path, --jubfile path                input jubfile path.\n" +
            "  -h     , --help                        show this message\n" +
            "  -t     , --tasks                       print task names.\n" +
            "  -T     , --all-tasks                   print all task names.\n" +
            "  -x     , --exclude plugin[,plugin...]  prevent auto loading plugins.\n" +
            "           --version                     print Jub version.";

var MIN_USAGE = "Usage: jub [-f path | -t | -T | -h] [tasks...]";

exports.main = function() {
    var cl = new CommandLine(process.argv.slice(2));
    cl.parseArgs();

    if(cl.versionFlag) {
        console.log(jub.version.join("."));
        return;
    }
    if(cl.helpFlag) {
        console.log(HEADER);
        console.log("");
        console.log(USAGE);
        return;
    }

    cl.setup();

    if(cl.taskSearchKeyword) {
        cl.taskSearch(cl.taskSearchKeyword);
        return;
    }
    if(cl.allTaskListFlag) {
        cl.printTasks(true);
        return;
    }
    if(cl.taskListFlag) {
        cl.printTasks(false);
        return;
    }

    if(cl.tasks.length === 0) {
        if("default" in taskModule.Task.all) {
            tools.before("default");
        } else {
            logging.error("Task name required.");
            console.error(MIN_USAGE);
            process.exit(1);
        }
    }

    taskModule.runMain(cl.tasks, mainErrorHandler);
};

function mainErrorHandler(mainTaskError, afterTaskError) {
    printTaskError(mainTaskError);
    printTaskError(afterTaskError);
    if(mainTaskError || afterTaskError) {
        process.exit(8);
    }
}

function printTaskError(err) {
    if(! err) { return; }

    var stack = err.stack;
    var info = helper.getTraceInfo(stack, 0);
    var idx = stack.indexOf("\n");
    var msg = stack.slice(0, idx);
    var stackTrace = stack.slice(idx + 1);

    logging.error(msg);
    printErroredSourceCode(info);
    console.error(stackTrace);
}

function printErroredSourceCode(info) {
    // skip if source is NativeModule
    if(/[\\/]/.test(info.src) === false) { return; }

    try {
        var source = fs.readFileSync(info.src, "utf8");
        var lines = source.split("\n");
        var line = lines[parseInt(info.line) - 1];
        console.error(line);
        var cursor = helper.spaces(parseInt(info.col) - 1) + "^";
        console.error(cursor);
    } catch(_) {
        // source has gone
    }
}

/**
 * CommandLine class
 */

function CommandLine(args) {
    this.args = args;
    this.tasks = [];
    this.excludePlugins = Object.create(null);
    this.jubfile = null;
    this.versionFlag = false;
    this.helpFlag = false;
    this.taskListFlag = false;
    this.allTaskListFlag = false;
    this.taskSearchKeyword = null;
}

exports.CommandLine = CommandLine;

CommandLine.prototype.parseArgs = function () {
    var args = this.args;
    for(var i = 0; i < args.length; i++) {
        var arg = args[i];
        var eq = args[i].indexOf("=");

        if(arg === "--version") {
            this.versionFlag = true;

        } else if(arg === "-h" || arg === "--help") {
            this.helpFlag = true;

        } else if(arg === "-f" || arg === "--jubfile") {
            i++;
            this.jubfile = args[i];

        } else if(arg === "-x" || arg === "--exclude") {
            i++;
            this.addExcludePlugins(args[i].split(","));

        } else if(arg === "-t" || arg === "--tasks") {
            this.taskListFlag = true;

        } else if(arg === "-T" || arg === "--all-tasks") {
            this.allTaskListFlag = true;
            if(i + 1 < args.length) {
                this.taskSearchKeyword = args[i + 1];
                i++;
            }

        } else if(arg[0] === "-") {
            logging.error("Unknown option '%s'", arg);
            process.exit(1);

        } else if(eq !== -1) {
            this.setEnv(arg, eq);

        } else {
            this.tasks.push(args[i]);
        }
    }
};

CommandLine.prototype.addExcludePlugins = function(plugins) {
    for(var i = 0; i < plugins.length; i++) {
        var plug = plugins[i];
        if(/^jub-/.test(plug) === false) {
            plug = "jub-" + plug;
        }
        this.excludePlugins[plug] = true;
    }
};

CommandLine.prototype.setup = function() {
    this.loadPackageJson();
    this.findJubfiles();
    this.addBuiltinTask();
    taskModule.nextMode(); // taskmode: builtin -> plugin
    tools._extendsNativeObject();

    var loader = new PluginLoader(this.excludePlugins);
    loader.load();

    taskModule.nextMode(); // taskmode: plugin -> user
    this.loadJubfile();
};

CommandLine.prototype.loadPackageJson = function() {
    try {
        jub.pkg = JSON.parse(tools.readFile("$root/package.json", "utf8"));
        jub.env.pkg = jub.pkg;
    } catch(_) {
        // Not found package.json or Syntax Error
    }
};

CommandLine.prototype.addBuiltinTask = function() {
    tools.task("clearTempfiles", function() {
        tempdir.clearTempfiles();
    });

    tools.after("_saveConfigFiles");
    tools.task("_saveConfigFiles", function() {
        tempdir.saveConfigFiles();
    });

    var server;
    tools.asyncTask("StaticServer", function(done) {
        server = tools.StaticServer({ logging: true, listenCallback: done });
    });

    tools.asyncTask("closeStaticServer", function(done) {
        if(server) {
            server.close(done);
            server = null;
        }
    });
};

CommandLine.prototype.findJubfiles = function() {
    if(this.jubfile) {
        jub.jubfilePaths = [pathModule.resolve(this.jubfile)];
    } else {
        jub.jubfilePaths = tools.glob("$root/jubfile*");
    }
};

CommandLine.prototype.printTasks = function(printBuiltinTasks) {
    if(printBuiltinTasks) {
        this.printTaskWithMode("Builtin task", taskModule.Task.BUILTIN);
        this.printTaskWithMode("Plugin task", taskModule.Task.PLUGIN);
    }
    this.printTaskWithMode("User task", taskModule.Task.USER);
};

CommandLine.prototype.printTaskWithMode = function(title, mode) {
    var allTasks = taskModule.Task.getAll();
    var tasks = allTasks.filter(function(task) {
        return task.mode === mode && task.name[0] !== "_";
    });
    if(tasks.length === 0) {
        return;
    }

    tasks.sort(function (a, b) { return a.name > b.name });
    console.log(title);
    tasks.forEach(function(task) {
        printTask(task, "    ");
    });
};

CommandLine.prototype.taskSearch = function(keyword) {
    var regex = new RegExp(keyword, "i");
    var allTasks = taskModule.Task.getAll();
    allTasks.sort(function (a, b) { return a.name > b.name });
    allTasks.forEach(function(task) {
        if(regex.test(task.name)) {
            printTask(task, "");
        }
    });
};

CommandLine.prototype.setEnv = function(pair, index) {
    var name = pair.slice(0, index);
    var value = pair.slice(index + 1);
    process.env[name] = value;
    jub.env[name] = value;
};

CommandLine.prototype.loadJubfile = function() {
    var filename = jub.jubfilePaths[0];
    if(filename === undefined) {
        return;
    }

    if(tools.exists(filename) === false) {
        logging.error("No jubfile found '%s'", (this.jubfile || filename));
        process.exit(1);
    }

    var jubfileHeader = this.generateJubfileHeader();
    var Module = require("module");
    var _wrap = Module.wrap;
    Module.wrap = function(script) {
        Module.wrap = _wrap;
        return _wrap(jubfileHeader + script);
    };

    try {
        jub.jubfile = require(filename);
    } finally {
        Module.wrap = _wrap;
    }
};

CommandLine.prototype.generateJubfileHeader = function() {
    var names = Object.keys(jub.tools);
    var header = "var jub=require('jub'), tools=jub.tools, ";

    var tools = [];
    names.forEach(function(name) {
        if(name[0] !== "_") {
            tools.push(name + "=tools." + name);
        }
    });
    header += tools.join(", ") + ";";
    header += " jub.jubfile=module.exports;";
    return header;
};

function printTask(task, indent) {
    var output = indent + task.name;
    if(task.description) {
        output += " " + helper.spaces(24 - output.length);
        output += task.description;
    }
    console.log(output);
}

/**
 * PluginLoader class
 */

function PluginLoader(excludePlugins) {
    this.exclude = excludePlugins;
}

PluginLoader.prototype.load = function() {
    var exclude = this.exclude;
    this.searchPluginNames().forEach(function(name) {
        if(exclude[name] === undefined) {
            require(name);
        }
    });
};

PluginLoader.prototype.searchPluginNames = function() {
    var libs = tools.glob("$root/node_modules/*");
    var pdir = tools.expand("$0:dir:dir", __dirname);
    if(pathModule.basename(pdir) === "node_modules") {
        libs.push.apply(libs, tools.glob(pdir + "/*"));
    }

    return libs.filter(function(lib) {
        return /^jub-/.test(pathModule.basename(lib));
    });
};
<commit_msg>Bug fix<commit_after>"use strict";

var jub = require("./jub.js");
var helper = require("./helper.js");
var logging = require("./logging.js");
var taskModule = require("./task.js");
var tempdir = require("./tempdir.js");
var tools = require("./tools.js");
var fs = require("fs");
var pathModule = require("path");

var HEADER = "Jub: Task runner for Node.";

var USAGE = "Usage\n" +
            "  jub [options] [taskName ...]\n\n" +

            "Options\n" +
            "  -f path, --jubfile path                input jubfile path.\n" +
            "  -h     , --help                        show this message\n" +
            "  -t     , --tasks                       print task names.\n" +
            "  -T     , --all-tasks                   print all task names.\n" +
            "  -x     , --exclude plugin[,plugin...]  prevent auto loading plugins.\n" +
            "           --version                     print Jub version.";

var MIN_USAGE = "Usage: jub [-f path | -t | -T | -h] [tasks...]";

exports.main = function() {
    var cl = new CommandLine(process.argv.slice(2));
    cl.parseArgs();

    if(cl.versionFlag) {
        console.log(jub.version.join("."));
        return;
    }
    if(cl.helpFlag) {
        console.log(HEADER);
        console.log("");
        console.log(USAGE);
        return;
    }

    cl.setup();

    if(cl.taskSearchKeyword) {
        cl.taskSearch(cl.taskSearchKeyword);
        return;
    }
    if(cl.allTaskListFlag) {
        cl.printTasks(true);
        return;
    }
    if(cl.taskListFlag) {
        cl.printTasks(false);
        return;
    }

    if(cl.tasks.length === 0) {
        if("default" in taskModule.Task.all) {
            tools.before("default");
        } else {
            logging.error("Task name required.");
            console.error(MIN_USAGE);
            process.exit(1);
        }
    }

    taskModule.runMain(cl.tasks, mainErrorHandler);
};

function mainErrorHandler(mainTaskError, afterTaskError) {
    printTaskError(mainTaskError);
    printTaskError(afterTaskError);
    if(mainTaskError || afterTaskError) {
        process.exit(8);
    }
}

function printTaskError(err) {
    if(! err) { return; }

    var stack = err.stack;
    var info = helper.getTraceInfo(stack, 0);
    var idx = stack.indexOf("\n");
    var msg = stack.slice(0, idx);
    var stackTrace = stack.slice(idx + 1);

    logging.error(msg);
    printErroredSourceCode(info);
    console.error(stackTrace);
}

function printErroredSourceCode(info) {
    // skip if source is NativeModule
    if(/[\\/]/.test(info.src) === false) { return; }

    try {
        var source = fs.readFileSync(info.src, "utf8");
        var lines = source.split("\n");
        var line = lines[parseInt(info.line) - 1];
        console.error(line);
        var cursor = helper.spaces(parseInt(info.col) - 1) + "^";
        console.error(cursor);
    } catch(_) {
        // source has gone
    }
}

/**
 * CommandLine class
 */

function CommandLine(args) {
    this.args = args;
    this.tasks = [];
    this.excludePlugins = Object.create(null);
    this.jubfile = null;
    this.versionFlag = false;
    this.helpFlag = false;
    this.taskListFlag = false;
    this.allTaskListFlag = false;
    this.taskSearchKeyword = null;
}

exports.CommandLine = CommandLine;

CommandLine.prototype.parseArgs = function () {
    var args = this.args;
    for(var i = 0; i < args.length; i++) {
        var arg = args[i];
        var eq = args[i].indexOf("=");

        if(arg === "--version") {
            this.versionFlag = true;

        } else if(arg === "-h" || arg === "--help") {
            this.helpFlag = true;

        } else if(arg === "-f" || arg === "--jubfile") {
            i++;
            this.jubfile = args[i];

        } else if(arg === "-x" || arg === "--exclude") {
            i++;
            this.addExcludePlugins(args[i].split(","));

        } else if(arg === "-t" || arg === "--tasks") {
            this.taskListFlag = true;

        } else if(arg === "-T" || arg === "--all-tasks") {
            this.allTaskListFlag = true;
            console.log(args[i + 1]);
            if(args[i + 1] && args[i + 1][0] !== "-") {
                this.taskSearchKeyword = args[i + 1];
                i++;
            }

        } else if(arg[0] === "-") {
            logging.error("Unknown option '%s'", arg);
            process.exit(1);

        } else if(eq !== -1) {
            this.setEnv(arg, eq);

        } else {
            this.tasks.push(args[i]);
        }
    }
};

CommandLine.prototype.addExcludePlugins = function(plugins) {
    for(var i = 0; i < plugins.length; i++) {
        var plug = plugins[i];
        if(/^jub-/.test(plug) === false) {
            plug = "jub-" + plug;
        }
        this.excludePlugins[plug] = true;
    }
};

CommandLine.prototype.setup = function() {
    this.loadPackageJson();
    this.findJubfiles();
    this.addBuiltinTask();
    taskModule.nextMode(); // taskmode: builtin -> plugin
    tools._extendsNativeObject();

    var loader = new PluginLoader(this.excludePlugins);
    loader.load();

    taskModule.nextMode(); // taskmode: plugin -> user
    this.loadJubfile();
};

CommandLine.prototype.loadPackageJson = function() {
    try {
        jub.pkg = JSON.parse(tools.readFile("$root/package.json", "utf8"));
        jub.env.pkg = jub.pkg;
    } catch(_) {
        // Not found package.json or Syntax Error
    }
};

CommandLine.prototype.addBuiltinTask = function() {
    tools.task("clearTempfiles", function() {
        tempdir.clearTempfiles();
    });

    tools.after("_saveConfigFiles");
    tools.task("_saveConfigFiles", function() {
        tempdir.saveConfigFiles();
    });

    var server;
    tools.asyncTask("StaticServer", function(done) {
        server = tools.StaticServer({ logging: true, listenCallback: done });
    });

    tools.asyncTask("closeStaticServer", function(done) {
        if(server) {
            server.close(done);
            server = null;
        }
    });
};

CommandLine.prototype.findJubfiles = function() {
    if(this.jubfile) {
        jub.jubfilePaths = [pathModule.resolve(this.jubfile)];
    } else {
        var paths = helper.readdirWithDirname(tools.env.root);
        jub.jubfilePaths = paths.filter(function(path) {
             return /^jubfile/i.test(pathModule.basename(path));
        });
    }
};

CommandLine.prototype.printTasks = function(printBuiltinTasks) {
    if(printBuiltinTasks) {
        this.printTaskWithMode("Builtin task", taskModule.Task.BUILTIN);
        this.printTaskWithMode("Plugin task", taskModule.Task.PLUGIN);
    }
    this.printTaskWithMode("User task", taskModule.Task.USER);
};

CommandLine.prototype.printTaskWithMode = function(title, mode) {
    var allTasks = taskModule.Task.getAll();
    var tasks = allTasks.filter(function(task) {
        return task.mode === mode && task.name[0] !== "_";
    });
    if(tasks.length === 0) {
        return;
    }

    tasks.sort(function (a, b) { return a.name > b.name });
    console.log(title);
    tasks.forEach(function(task) {
        printTask(task, "    ");
    });
};

CommandLine.prototype.taskSearch = function(keyword) {
    var regex = new RegExp(keyword, "i");
    var allTasks = taskModule.Task.getAll();
    allTasks.sort(function (a, b) { return a.name > b.name });
    allTasks.forEach(function(task) {
        if(regex.test(task.name)) {
            printTask(task, "");
        }
    });
};

CommandLine.prototype.setEnv = function(pair, index) {
    var name = pair.slice(0, index);
    var value = pair.slice(index + 1);
    process.env[name] = value;
    jub.env[name] = value;
};

CommandLine.prototype.loadJubfile = function() {
    var filename = jub.jubfilePaths[0];
    if(filename === undefined) {
        return;
    }

    if(tools.exists(filename) === false) {
        logging.error("No jubfile found '%s'", (this.jubfile || filename));
        process.exit(1);
    }

    var jubfileHeader = this.generateJubfileHeader();
    var Module = require("module");
    var _wrap = Module.wrap;
    Module.wrap = function(script) {
        Module.wrap = _wrap;
        return _wrap(jubfileHeader + script);
    };

    try {
        jub.jubfile = require(filename);
    } finally {
        Module.wrap = _wrap;
    }
};

CommandLine.prototype.generateJubfileHeader = function() {
    var names = Object.keys(jub.tools);
    var header = "var jub=require('jub'), tools=jub.tools, ";

    var tools = [];
    names.forEach(function(name) {
        if(name[0] !== "_") {
            tools.push(name + "=tools." + name);
        }
    });
    header += tools.join(", ") + ";";
    header += " jub.jubfile=module.exports;";
    return header;
};

function printTask(task, indent) {
    var output = indent + task.name;
    if(task.description) {
        output += " " + helper.spaces(24 - output.length);
        output += task.description;
    }
    console.log(output);
}

/**
 * PluginLoader class
 */

function PluginLoader(excludePlugins) {
    this.exclude = excludePlugins;
}

PluginLoader.prototype.load = function() {
    var exclude = this.exclude;
    this.searchPluginNames().forEach(function(name) {
        if(exclude[name] === undefined) {
            require(name);
        }
    });
};

PluginLoader.prototype.searchPluginNames = function() {
    var libs = tools.glob("$root/node_modules/*");
    var pdir = tools.expand("$0:dir:dir", __dirname);
    if(pathModule.basename(pdir) === "node_modules") {
        libs.push.apply(libs, tools.glob(pdir + "/*"));
    }

    return libs.filter(function(lib) {
        return /^jub-/.test(pathModule.basename(lib));
    });
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const util = require('util');
const async = require('async');
const csv = require('csv-parse');
const cli = require('cli');
const YAML = require('yaml-js');
const moment = require('moment');
const chalk = require('chalk');
const defaultOptions = require('rc')('artillery');
const validate = require('artillery-core').validate;
const debug = require('debug')('commands:run');

const createRunner = require('../runner');

module.exports = run;

function run(scriptPath, options) {
  let logfile;

  // is the destination a directory that exists?
  let isDir = false;
  if (options.output && options.output !== defaultOptions.output) {
    try {
      isDir = fs.statSync(options.output).isDirectory();
    } catch (err) {
      // ENOENT, don't need to do anything
    }
  }

  const defaultFormat = '[artillery_report_]YMMDD_HHmmSS[.json]';
  if (!isDir && options.output) {
    // -o is set with a filename (existing or not)
    logfile = options.output;
  } else if (!isDir && !options.output) {
    // no -o set
    logfile = moment().format(defaultOptions.output ||
                              defaultFormat);
  } else {
    // -o is set with a directory
    logfile = path.join(options.output, moment().format(defaultOptions.output ||
                                                        defaultFormat));
  }

  function log() {
    if (options.quiet) { return; }
    console.log.apply(console, arguments);
  }

  async.waterfall([
    function readScript(callback) {

      fs.readFile(scriptPath, 'utf-8', function(err, data) {

        if (err) {
          const msg = util.format('File not found: %s', scriptPath);
          return callback(new Error(msg), null);
        }

        let script;
        let fileformat;
        try {
          if (/\.ya?ml$/.test(path.extname(scriptPath))) {
            fileformat = 'YAML';
            script = YAML.load(data);
          } else {
            fileformat = 'JSON';
            script = JSON.parse(data);
          }
        } catch (e) {
          const msg2 = `File ${scriptPath} does not appear to be valid ${fileformat}: (${e.message})`;
          return callback(new Error(msg2), null);
        }

        if (options.target && script.config) {
          script.config.target = options.target;
        }

        if (!script.config.target && !options.environment) {
          const msg4 = 'No target specified and no environment chosen';
          return callback(new Error(msg4), null);
        }

        let validation = validate(script);
        if (!validation.valid) {
          log(validation.errors);
          return callback(new Error('Test script validation error'), null);
        }

        return callback(null, {script: script});
      });
    },
    function readPayload(context, callback) {

      if (context.script.config.payload && _.isArray(context.script.config.payload)) {
        async.map(context.script.config.payload,
          function(item, callback2) {
            let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
            let payloadFile = path.resolve(path.dirname(absoluteScriptPath), item.path);

            let data = fs.readFileSync(payloadFile, 'utf-8');
            csv(data, function(err, parsedData) {
              item.data = parsedData;
              return callback2(err, item);
            });
          },
          function(err, results) {
            if (err) {
              return callback(err, null);
            }
            context.payload = results;
            return callback(null, context);
          });
      } else if (context.script.config.payload &&
                 (_.isObject(context.script.config.payload) ||
                 options.payload)) {

        //use config if path set else use command line
        let csvdata;
        if (context.script.config.payload.path) {
          let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
          let payloadFile = path.resolve(path.dirname(absoluteScriptPath), context.script.config.payload.path);
          csvdata = fs.readFileSync(payloadFile, 'utf-8');
        } else {
          csvdata = fs.readFileSync(options.payload, 'utf-8');
        }
        csv(csvdata, function(err, payload) {

          if (err) {
            const msg3 = util.format(
              'File %s does not appear to be valid CSV', options.payload);
            return callback(new Error(msg3), null);
          }

          context.payload = payload;

          return callback(null, context);
        });
      } else {
        if (context.script.config.payload) {
          log(
            'WARNING: payload file not set, but payload is configured in %s\n',
            scriptPath);
        }

        return callback(null, context);
      }
    },
    function checkTimersBug(context, callback) {
      // Node.js versions 6.8.1 - 7.2.0 have a bug which can cause the process
      // to hang when rampTo is used.
      // Ref: https://github.com/shoreditch-ops/artillery/issues/210
      //
      // TODO: Add a check for upper end of the affected range once Node.js
      // with the fix is out.
      // Ref: https://github.com/nodejs/node/issues/9756

      // 6.8.1 and 7.0.0 and 7.2.0 but NOT 6.8.0
      let nv = process.version;
      let mj = parseInt(nv.substring(1, 2), 10);
      let mn = parseInt(nv.substring(3, 4), 10);
      let pt = parseInt(nv.substring(5, 6), 10);
      if (mj === 7 ||
          (mj === 6 && mn > 8) ||
          (mj === 6 && mn === 8 && pt > 0)) {
        let usesRampTo = _.some(context.script.config.phases, function(phase) {
          return !(_.isUndefined(phase.rampTo));
        });

        if (usesRampTo) {
          console.error(chalk.bold.red(`
You are running Node.js ${nv} which is affected by a bug that can cause
Artillery to hang indefinitely when a rampTo arrival phase is used.

Use Node.js v6.8.0 or lower to avoid the issue.

For more details see: https://github.com/nodejs/node/issues/9756
`));
        }
      }

      return callback(null, context);
    },
    function checkIfXPathIsUsed(context, callback) {
      // FIXME: This should probably be in core.
      let xmlInstalled = null;
      try {
        xmlInstalled = require('artillery-xml-capture');
      } catch (e) {
      }

      let usesXPathCapture = false;
      context.script.scenarios.forEach(function(scenario) {
        scenario.flow.forEach(function(step) {
          let params = step[_.keys(step)[0]];
          if ((params.capture && params.capture.xpath) ||
              (params.match && params.match.xpath)) {
            usesXPathCapture = true;
          }
        });
      });
      if (usesXPathCapture && !xmlInstalled) {
        console.log(chalk.bold.red('Warning: '), chalk.bold.yellow('your test script is using XPath capture, but artillery-xml-capture does not seem to be installed.'));
        console.log(chalk.bold.yellow('Install it with: npm install -g artillery-xml-capture'));
      }
      return callback(null, context);
    }
    ],
    function(err, result) {

      if (err) {
        log(err.message);
        process.exit(1);
      }

      if (options.insecure) {
        if (result.script.config.tls) {
          if (result.script.config.tls.rejectUnauthorized) {
            log('WARNING: TLS certificate validation enabled in the ' +
                        'test script, but explicitly disabled with ' +
                        '-k/--insecure.');
          }
          result.script.config.tls.rejectUnauthorized = false;
        } else {
          result.script.config.tls = {rejectUnauthorized: false};
        }
      }

      result.script.config.statsInterval = result.script.config.statsInterval || 10;

      log('Log file: %s', logfile);

      var spinnerOn = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('');
        }
      };
      var spinnerOff = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('', true);
        }
      };

      let runner = createRunner(result.script,
                                result.payload,
                                {
                                  environment: options.environment,
                                  // This is used in the worker to resolve
                                  // the path to the processor module
                                  scriptPath: scriptPath
                                });
      let intermediates = [];

      runner.events.on('phaseStarted', function(phase) {
        spinnerOff();
        log(
          'Phase %s%s started - duration: %ss',
          phase.index,
          (phase.name ? ' (' + phase.name + ')' : ''),
          phase.duration || phase.think);
        spinnerOn();
      });

      runner.events.on('stats', function(stats) {
        let report = stats.report();
        intermediates.push(report);
        spinnerOff();
        log('Report for the previous %ss @ %s',
            result.script.config.statsInterval,
            report.timestamp);
        printReport(report);
        spinnerOn();
      });

      runner.events.on('done', function(allStats) {
        let report = allStats.report();
        delete report.concurrency; // doesn't make sense in this context
        delete report.pendingRequests;

        spinnerOff();
        log('all scenarios completed');
        log('Complete report @ %s', report.timestamp);
        printReport(report, { showScenarioCounts: true });
        report.phases = _.get(result, 'script.config.phases', []);
        delete report.latencies;

        fs.writeFileSync(
          logfile, JSON.stringify({
            aggregate: report,
            intermediate: intermediates
          }, null, 2),
          {flag: 'w'});

        if (result.script.config.ensure) {
          const latency = report.latency;
          _.each(result.script.config.ensure, function(max, k) {

            let bucket = k === 'p50' ? 'median' : k;
            if (latency[bucket]) {
              if (latency[bucket] > max) {
                const msg = util.format(
                  'ensure condition failed: ensure.%s < %s', bucket, max);
                log(msg);
                process.exit(1);
              }
            }
          });
        }
      });

      spinnerOn();
      runner.run();

      let shuttingDown = false;
      process.once('SIGINT', gracefulShutdown);
      process.once('SIGTERM', gracefulShutdown);

      function gracefulShutdown() {
        if (shuttingDown) {
          return;
        }

        shuttingDown = true;
        debug('Graceful shutdown initated');
        spinnerOff();
        console.error('Artillery shutting down...');
        runner.shutdown(function() {
          process.exit(0);
        });
      }
    });
}

function printReport(report, opts) {
  opts = opts || {};

  console.log('  Scenarios launched:  %s', report.scenariosCreated);
  console.log('  Scenarios completed: %s', report.scenariosCompleted);
  console.log('  Requests completed:  %s', report.requestsCompleted);

  // Final report does not have concurrency
  if (report.concurrency) {
    console.log('  Concurrent users:    %s', report.concurrency);
  }

  console.log('  RPS sent: %s', report.rps.mean);
  console.log('  Request latency:');
  console.log('    min: %s', report.latency.min);
  console.log('    max: %s', report.latency.max);
  console.log('    median: %s', report.latency.median);
  console.log('    p95: %s', report.latency.p95);
  console.log('    p99: %s', report.latency.p99);

  console.log('  Scenario duration:');
  console.log('    min: %s', report.scenarioDuration.min);
  console.log('    max: %s', report.scenarioDuration.max);
  console.log('    median: %s', report.scenarioDuration.median);
  console.log('    p95: %s', report.scenarioDuration.p95);
  console.log('    p99: %s', report.scenarioDuration.p99);

  // We only want to show this for the aggregate report
  if (opts.showScenarioCounts && report.scenarioCounts) {
    console.log('  Scenario counts:');
    _.each(report.scenarioCounts, function(count, name) {
      let percentage = Math.round(count / report.scenariosCompleted * 100 * 1000) / 1000;
      console.log('    %s: %s (%s\%)', name, count, percentage);
    });
  }

  if (_.size(report.customStats) > 0) {
    console.log('Custom stats:');
    _.each(report.customStats, function(r, n) {
      console.log('  %s:', n);
      console.log('    min: %s', r.min);
      console.log('    max: %s', r.max);
      console.log('    median: %s', r.median);
      console.log('    p95: %s', r.p95);
      console.log('    p99: %s', r.p99);
    });
  }

  if (_.keys(report.codes).length !== 0) {
    console.log('  Codes:');
    _.each(report.codes, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
  if (_.keys(report.errors).length !== 0) {
    console.log('  Errors:');
    _.each(report.errors, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
}
<commit_msg>Warn about rampTo only for Node <7.3.0<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const util = require('util');
const async = require('async');
const csv = require('csv-parse');
const cli = require('cli');
const YAML = require('yaml-js');
const moment = require('moment');
const chalk = require('chalk');
const defaultOptions = require('rc')('artillery');
const validate = require('artillery-core').validate;
const debug = require('debug')('commands:run');

const createRunner = require('../runner');

module.exports = run;

function run(scriptPath, options) {
  let logfile;

  // is the destination a directory that exists?
  let isDir = false;
  if (options.output && options.output !== defaultOptions.output) {
    try {
      isDir = fs.statSync(options.output).isDirectory();
    } catch (err) {
      // ENOENT, don't need to do anything
    }
  }

  const defaultFormat = '[artillery_report_]YMMDD_HHmmSS[.json]';
  if (!isDir && options.output) {
    // -o is set with a filename (existing or not)
    logfile = options.output;
  } else if (!isDir && !options.output) {
    // no -o set
    logfile = moment().format(defaultOptions.output ||
                              defaultFormat);
  } else {
    // -o is set with a directory
    logfile = path.join(options.output, moment().format(defaultOptions.output ||
                                                        defaultFormat));
  }

  function log() {
    if (options.quiet) { return; }
    console.log.apply(console, arguments);
  }

  async.waterfall([
    function readScript(callback) {

      fs.readFile(scriptPath, 'utf-8', function(err, data) {

        if (err) {
          const msg = util.format('File not found: %s', scriptPath);
          return callback(new Error(msg), null);
        }

        let script;
        let fileformat;
        try {
          if (/\.ya?ml$/.test(path.extname(scriptPath))) {
            fileformat = 'YAML';
            script = YAML.load(data);
          } else {
            fileformat = 'JSON';
            script = JSON.parse(data);
          }
        } catch (e) {
          const msg2 = `File ${scriptPath} does not appear to be valid ${fileformat}: (${e.message})`;
          return callback(new Error(msg2), null);
        }

        if (options.target && script.config) {
          script.config.target = options.target;
        }

        if (!script.config.target && !options.environment) {
          const msg4 = 'No target specified and no environment chosen';
          return callback(new Error(msg4), null);
        }

        let validation = validate(script);
        if (!validation.valid) {
          log(validation.errors);
          return callback(new Error('Test script validation error'), null);
        }

        return callback(null, {script: script});
      });
    },
    function readPayload(context, callback) {

      if (context.script.config.payload && _.isArray(context.script.config.payload)) {
        async.map(context.script.config.payload,
          function(item, callback2) {
            let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
            let payloadFile = path.resolve(path.dirname(absoluteScriptPath), item.path);

            let data = fs.readFileSync(payloadFile, 'utf-8');
            csv(data, function(err, parsedData) {
              item.data = parsedData;
              return callback2(err, item);
            });
          },
          function(err, results) {
            if (err) {
              return callback(err, null);
            }
            context.payload = results;
            return callback(null, context);
          });
      } else if (context.script.config.payload &&
                 (_.isObject(context.script.config.payload) ||
                 options.payload)) {

        //use config if path set else use command line
        let csvdata;
        if (context.script.config.payload.path) {
          let absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
          let payloadFile = path.resolve(path.dirname(absoluteScriptPath), context.script.config.payload.path);
          csvdata = fs.readFileSync(payloadFile, 'utf-8');
        } else {
          csvdata = fs.readFileSync(options.payload, 'utf-8');
        }
        csv(csvdata, function(err, payload) {

          if (err) {
            const msg3 = util.format(
              'File %s does not appear to be valid CSV', options.payload);
            return callback(new Error(msg3), null);
          }

          context.payload = payload;

          return callback(null, context);
        });
      } else {
        if (context.script.config.payload) {
          log(
            'WARNING: payload file not set, but payload is configured in %s\n',
            scriptPath);
        }

        return callback(null, context);
      }
    },
    function checkTimersBug(context, callback) {
      // Node.js versions 6.8.1 - 7.2.0 have a bug which can cause the process
      // to hang when rampTo is used.
      // Ref: https://github.com/shoreditch-ops/artillery/issues/210

      // 6.8.1 and 7.0.0 and 7.2.0 but NOT 6.8.0
      let nv = process.version;
      let mj = parseInt(nv.substring(1, 2), 10);
      let mn = parseInt(nv.substring(3, 4), 10);
      let pt = parseInt(nv.substring(5, 6), 10);
      if (mj === 7 && mn < 3 ||
          (mj === 6 && mn > 8) ||
          (mj === 6 && mn === 8 && pt > 0)) {
        let usesRampTo = _.some(context.script.config.phases, function(phase) {
          return !(_.isUndefined(phase.rampTo));
        });

        if (usesRampTo) {
          console.error(chalk.bold.red(`
You are running Node.js ${nv} which is affected by a bug that can cause
Artillery to hang indefinitely when a rampTo arrival phase is used.

Use Node.js v6.8.0 or lower to avoid the issue.

For more details see: https://github.com/nodejs/node/issues/9756
`));
        }
      }

      return callback(null, context);
    },
    function checkIfXPathIsUsed(context, callback) {
      // FIXME: This should probably be in core.
      let xmlInstalled = null;
      try {
        xmlInstalled = require('artillery-xml-capture');
      } catch (e) {
      }

      let usesXPathCapture = false;
      context.script.scenarios.forEach(function(scenario) {
        scenario.flow.forEach(function(step) {
          let params = step[_.keys(step)[0]];
          if ((params.capture && params.capture.xpath) ||
              (params.match && params.match.xpath)) {
            usesXPathCapture = true;
          }
        });
      });
      if (usesXPathCapture && !xmlInstalled) {
        console.log(chalk.bold.red('Warning: '), chalk.bold.yellow('your test script is using XPath capture, but artillery-xml-capture does not seem to be installed.'));
        console.log(chalk.bold.yellow('Install it with: npm install -g artillery-xml-capture'));
      }
      return callback(null, context);
    }
    ],
    function(err, result) {

      if (err) {
        log(err.message);
        process.exit(1);
      }

      if (options.insecure) {
        if (result.script.config.tls) {
          if (result.script.config.tls.rejectUnauthorized) {
            log('WARNING: TLS certificate validation enabled in the ' +
                        'test script, but explicitly disabled with ' +
                        '-k/--insecure.');
          }
          result.script.config.tls.rejectUnauthorized = false;
        } else {
          result.script.config.tls = {rejectUnauthorized: false};
        }
      }

      result.script.config.statsInterval = result.script.config.statsInterval || 10;

      log('Log file: %s', logfile);

      var spinnerOn = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('');
        }
      };
      var spinnerOff = function() {
        if (!options.quiet && process.stdout.isTTY) {
          cli.spinner('', true);
        }
      };

      let runner = createRunner(result.script,
                                result.payload,
                                {
                                  environment: options.environment,
                                  // This is used in the worker to resolve
                                  // the path to the processor module
                                  scriptPath: scriptPath
                                });
      let intermediates = [];

      runner.events.on('phaseStarted', function(phase) {
        spinnerOff();
        log(
          'Phase %s%s started - duration: %ss',
          phase.index,
          (phase.name ? ' (' + phase.name + ')' : ''),
          phase.duration || phase.think);
        spinnerOn();
      });

      runner.events.on('stats', function(stats) {
        let report = stats.report();
        intermediates.push(report);
        spinnerOff();
        log('Report for the previous %ss @ %s',
            result.script.config.statsInterval,
            report.timestamp);
        printReport(report);
        spinnerOn();
      });

      runner.events.on('done', function(allStats) {
        let report = allStats.report();
        delete report.concurrency; // doesn't make sense in this context
        delete report.pendingRequests;

        spinnerOff();
        log('all scenarios completed');
        log('Complete report @ %s', report.timestamp);
        printReport(report, { showScenarioCounts: true });
        report.phases = _.get(result, 'script.config.phases', []);
        delete report.latencies;

        fs.writeFileSync(
          logfile, JSON.stringify({
            aggregate: report,
            intermediate: intermediates
          }, null, 2),
          {flag: 'w'});

        if (result.script.config.ensure) {
          const latency = report.latency;
          _.each(result.script.config.ensure, function(max, k) {

            let bucket = k === 'p50' ? 'median' : k;
            if (latency[bucket]) {
              if (latency[bucket] > max) {
                const msg = util.format(
                  'ensure condition failed: ensure.%s < %s', bucket, max);
                log(msg);
                process.exit(1);
              }
            }
          });
        }
      });

      spinnerOn();
      runner.run();

      let shuttingDown = false;
      process.once('SIGINT', gracefulShutdown);
      process.once('SIGTERM', gracefulShutdown);

      function gracefulShutdown() {
        if (shuttingDown) {
          return;
        }

        shuttingDown = true;
        debug('Graceful shutdown initated');
        spinnerOff();
        console.error('Artillery shutting down...');
        runner.shutdown(function() {
          process.exit(0);
        });
      }
    });
}

function printReport(report, opts) {
  opts = opts || {};

  console.log('  Scenarios launched:  %s', report.scenariosCreated);
  console.log('  Scenarios completed: %s', report.scenariosCompleted);
  console.log('  Requests completed:  %s', report.requestsCompleted);

  // Final report does not have concurrency
  if (report.concurrency) {
    console.log('  Concurrent users:    %s', report.concurrency);
  }

  console.log('  RPS sent: %s', report.rps.mean);
  console.log('  Request latency:');
  console.log('    min: %s', report.latency.min);
  console.log('    max: %s', report.latency.max);
  console.log('    median: %s', report.latency.median);
  console.log('    p95: %s', report.latency.p95);
  console.log('    p99: %s', report.latency.p99);

  console.log('  Scenario duration:');
  console.log('    min: %s', report.scenarioDuration.min);
  console.log('    max: %s', report.scenarioDuration.max);
  console.log('    median: %s', report.scenarioDuration.median);
  console.log('    p95: %s', report.scenarioDuration.p95);
  console.log('    p99: %s', report.scenarioDuration.p99);

  // We only want to show this for the aggregate report
  if (opts.showScenarioCounts && report.scenarioCounts) {
    console.log('  Scenario counts:');
    _.each(report.scenarioCounts, function(count, name) {
      let percentage = Math.round(count / report.scenariosCompleted * 100 * 1000) / 1000;
      console.log('    %s: %s (%s\%)', name, count, percentage);
    });
  }

  if (_.size(report.customStats) > 0) {
    console.log('Custom stats:');
    _.each(report.customStats, function(r, n) {
      console.log('  %s:', n);
      console.log('    min: %s', r.min);
      console.log('    max: %s', r.max);
      console.log('    median: %s', r.median);
      console.log('    p95: %s', r.p95);
      console.log('    p99: %s', r.p99);
    });
  }

  if (_.keys(report.codes).length !== 0) {
    console.log('  Codes:');
    _.each(report.codes, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
  if (_.keys(report.errors).length !== 0) {
    console.log('  Errors:');
    _.each(report.errors, function(count, code) {
      console.log('    %s: %s', code, count);
    });
  }
}
<|endoftext|>


<commit_before>const sift = require('sift');

function preprocessQueryObj(queryObj) {
    if (typeof queryObj === 'object' && queryObj) {
        if (queryObj.constructor === Object) {
            const result = {};
            for (const key of Object.keys(queryObj)) {
                result[key] = preprocessQueryObj(queryObj[key]);
            }
            return result;
        } else {
            if (queryObj.id) {
                // Assume Asset or Relation instance
                return { id: { $eq: queryObj.id } };
            } else {
                // Maybe RegExp
                return queryObj;
            }
        }
    } else if (Array.isArray(queryObj)) {
        return queryObj.map(preprocessQueryObj);
    } else {
        // Primitive value
        return queryObj;
    }
}

module.exports = function compileQuery(queryObj = {}) {
    return sift(preprocessQueryObj(queryObj));
};
<commit_msg>Reintroduce regexp shorthand<commit_after>const sift = require('sift');
const _ = require('lodash');

function preprocessQueryObj(queryObj) {
    if (typeof queryObj === 'object' && queryObj) {
        if (queryObj.constructor === Object) {
            const result = {};
            for (const key of Object.keys(queryObj)) {
                if (key === '$regex') {
                    result[key] = queryObj[key];
                } else {
                    result[key] = preprocessQueryObj(queryObj[key]);
                }
            }
            return result;
        } else {
            if (queryObj.id) {
                // Assume Asset or Relation instance
                return { id: { $eq: queryObj.id } };
            } else if (_.isRegExp(queryObj)) {
                return { $regex: queryObj };
            } else {
                return queryObj;
            }
        }
    } else if (Array.isArray(queryObj)) {
        return queryObj.map(preprocessQueryObj);
    } else {
        // Primitive value
        return queryObj;
    }
}

module.exports = function compileQuery(queryObj = {}) {
    return sift(preprocessQueryObj(queryObj));
};
<|endoftext|>


<commit_before>"use strict";

/**
 * CONNECT-LOKI
 * A Loki.js session store for Connect/Express
 * MIT Licensed
 */

var loki = require('lokijs'),
	 Promise = require('bluebird'),
	 util = require('util'),
	 _ = require('lodash');

module.exports = (session) => {

	/**
	 * Express's session Store.
	 */

	var Store = session.Store;

	/**
	 * Initialize LokiStore with the given `options`.
	 *
	 * @param {Object} options
	 * @api public
	 */

	function LokiStore (options) {

		if (!(this instanceof LokiStore)) {
			throw new TypeError('Cannot call LokiStore constructor as a function');
		}

		var self = this;

		// Parse options

		options = options || {};
		Store.call(this, options);

		this.autosave = options.autosave || true;
		this.storePath = options.path || './session-store.db';
		this.ttl = options.ttl || 1209600;
		if(this.ttl === 0) { this.ttl = null; }

		// Initialize Loki.js

		this.client = new loki(this.storePath, {
			env: 'NODEJS',
			autosave: self.autosave,
			autosaveInterval: 5000
		});

		// Setup error logging

		if(options.logErrors){
			if(typeof options.logErrors != 'function'){
				options.logErrors = function (err) {
					console.error('Warning: connect-loki reported a client error: ' + err);
				};
			}
			this.logger = options.logErrors;
		} else {
			this.logger = _.noop;
		}

		// Get / Create collection

		this.client.loadDatabase({}, () => {
			self.collection = self.client.getCollection('Sessions');
			if(_.isNil(self.collection)) {
				self.collection = self.client.addCollection('Sessions', {
					indices: ['sid'],
					ttlInterval: self.ttl
				});
			}
			self.collection.on('error', (err) => {
				return self.logger(err);
			});
			self.emit('connect');
		});

	}

	/**
	 * Inherit from `Store`.
	 */

	util.inherits(LokiStore, Store);

	/**
	 * Attempt to fetch session by the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.get = function (sid, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].content) {
			fn(null, s[0].content);
		} else {
			fn(null);
		}

	};

	/**
	 * Commit the given `sess` object associated with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Session} sess
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.set = function (sid, sess, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].content) {
			s[0].content = sess;
			s[0].updatedAt = new Date();
			this.collection.update(s[0]);
		} else {
			this.collection.insert({
				sid,
				content: sess,
				updatedAt: new Date()
			});
		}

		fn(null);

	};

	/**
	 * Destroy the session associated with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.destroy = function (sid, fn) {
		if(!fn) { fn = _.noop; }
		this.collection.remove({ sid });
		fn(null);
	};

	/**
	 * Clear all sessions in the store
	 *
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.clear = function (fn) {
		if(!fn) { fn = _.noop; }
		this.collection.clear();
		fn(null);
	};

	/**
	 * Count of all sessions in the store
	 *
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.length = function (fn) {
		if(!fn) { fn = _.noop; }
		let c = this.collection.count();
		fn(null, c);
	};

	/**
	 * Refresh the time-to-live for the session with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Session} sess
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.touch = function (sid, sess, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].updatedAt) {
			s[0].updatedAt = new Date();
			this.collection.update(s[0]);
		}

		return fn();
	};

	return LokiStore;

};<commit_msg>fix autosave option<commit_after>"use strict";

/**
 * CONNECT-LOKI
 * A Loki.js session store for Connect/Express
 * MIT Licensed
 */

var loki = require('lokijs'),
	 Promise = require('bluebird'),
	 util = require('util'),
	 _ = require('lodash');

module.exports = (session) => {

	/**
	 * Express's session Store.
	 */

	var Store = session.Store;

	/**
	 * Initialize LokiStore with the given `options`.
	 *
	 * @param {Object} options
	 * @api public
	 */

	function LokiStore (options) {

		if (!(this instanceof LokiStore)) {
			throw new TypeError('Cannot call LokiStore constructor as a function');
		}

		var self = this;

		// Parse options

		options = options || {};
		Store.call(this, options);

		this.autosave = options.autosave !== false;
		this.storePath = options.path || './session-store.db';
		this.ttl = options.ttl || 1209600;
		if(this.ttl === 0) { this.ttl = null; }

		// Initialize Loki.js

		this.client = new loki(this.storePath, {
			env: 'NODEJS',
			autosave: self.autosave,
			autosaveInterval: 5000
		});

		// Setup error logging

		if(options.logErrors){
			if(typeof options.logErrors != 'function'){
				options.logErrors = function (err) {
					console.error('Warning: connect-loki reported a client error: ' + err);
				};
			}
			this.logger = options.logErrors;
		} else {
			this.logger = _.noop;
		}

		// Get / Create collection

		this.client.loadDatabase({}, () => {
			self.collection = self.client.getCollection('Sessions');
			if(_.isNil(self.collection)) {
				self.collection = self.client.addCollection('Sessions', {
					indices: ['sid'],
					ttlInterval: self.ttl
				});
			}
			self.collection.on('error', (err) => {
				return self.logger(err);
			});
			self.emit('connect');
		});

	}

	/**
	 * Inherit from `Store`.
	 */

	util.inherits(LokiStore, Store);

	/**
	 * Attempt to fetch session by the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.get = function (sid, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].content) {
			fn(null, s[0].content);
		} else {
			fn(null);
		}

	};

	/**
	 * Commit the given `sess` object associated with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Session} sess
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.set = function (sid, sess, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].content) {
			s[0].content = sess;
			s[0].updatedAt = new Date();
			this.collection.update(s[0]);
		} else {
			this.collection.insert({
				sid,
				content: sess,
				updatedAt: new Date()
			});
		}

		fn(null);

	};

	/**
	 * Destroy the session associated with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.destroy = function (sid, fn) {
		if(!fn) { fn = _.noop; }
		this.collection.remove({ sid });
		fn(null);
	};

	/**
	 * Clear all sessions in the store
	 *
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.clear = function (fn) {
		if(!fn) { fn = _.noop; }
		this.collection.clear();
		fn(null);
	};

	/**
	 * Count of all sessions in the store
	 *
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.length = function (fn) {
		if(!fn) { fn = _.noop; }
		let c = this.collection.count();
		fn(null, c);
	};

	/**
	 * Refresh the time-to-live for the session with the given `sid`.
	 *
	 * @param {String} sid
	 * @param {Session} sess
	 * @param {Function} fn
	 * @api public
	 */

	LokiStore.prototype.touch = function (sid, sess, fn) {
		if(!fn) { fn = _.noop; }

		let s = this.collection.find({ sid });
		if(s[0] && s[0].updatedAt) {
			s[0].updatedAt = new Date();
			this.collection.update(s[0]);
		}

		return fn();
	};

	return LokiStore;

};
<|endoftext|>


<commit_before>"use strict";

var _ = require("./lodash"),
    addLabel = require("./label/add-label"),
    addSubnodes = require("./add-subnodes"),
    util = require("./util"),
    d3 = require("./d3");

module.exports = createNodes;

function createNodes(selection, g, shapes) {
  var simpleNodes = g.nodes().filter(function(v) { return !util.isSubgraph(g, v); });
  var svgNodes = selection.selectAll("g.node")
    .data(simpleNodes, function(v) { return v; })
    .classed("update", true);

  svgNodes.selectAll("*").remove();
  svgNodes.enter()
    .append("g")
      .attr("class", "node")
      .style("opacity", 0);
  svgNodes.each(function(v) {
    var subnodeGroup, subnodeDom;
    var subnodeBBox = {width: 0, height: 0};
    var node = g.node(v),
        thisGroup = d3.select(this),
        labelGroup = thisGroup.append("g").attr("class", "label"),
        labelDom = addLabel(labelGroup, node),
        shape = shapes[node.shape],
        bbox = _.pick(labelDom.node().getBBox(), "width", "height");

    node.elem = this;

    // If the node has subnodes, add them to the node and modify the bbox.
    if (node.subnodes && node.subnodes.length) {
      subnodeGroup = addSubnodes(thisGroup, node);

      // Calculate the box surrounding the subnode elements. Adjust bbox to hold them.
      subnodeBBox = _.pick(subnodeGroup.node().getBBox(), "width", "height");
      bbox.height += subnodeBBox.height;
      bbox.width = Math.max(bbox.width, subnodeBBox.width);

      // If the tooltip is open, render it
      var hoveredSubnode = _(node.subnodes).find(subnode => subnode.tooltip);
      if (hoveredSubnode) {
        tooltipGroup = thisGroup.append("g").attr("class", "tooltip"),
        tooltipGroup.append("rect")
          .attr("x", 0)
          .attr("y", "20")
          .attr("width", 100)
          .attr("height", 30);
        tooltipGroup.append("text")
          .text(hoveredSubnode.ref['@type'][0])
          .attr("x", 0)
          .attr("y", "30");
      }
    } else {
      // The node doesn't have subnodes.
      subnodeGroup = subnodeDom = null;
      subnodeBBox.width = subnodeBBox.height = 0;
    }

    if (node.id) { thisGroup.attr("id", node.id); }
    if (node.labelId) { labelGroup.attr("id", node.labelId); }
    util.applyClass(thisGroup, node["class"],
      (thisGroup.classed("update") ? "update " : "") + "node");

    if (_.has(node, "width")) { bbox.width = node.width; }
    if (_.has(node, "height")) { bbox.height = node.height; }

    bbox.width += node.paddingLeft + node.paddingRight;
    bbox.height += node.paddingTop + node.paddingBottom;
    labelGroup.attr("transform", "translate(" +
      ((node.paddingLeft - node.paddingRight) / 2) + "," +
      ((node.paddingTop - node.paddingBottom - subnodeBBox.height) / 2) + ")");
    if (subnodeGroup) {
      subnodeGroup.attr("transform", "translate(" +
        -((subnodeBBox.width / 2) - 10) + "," +
        ((node.paddingTop - node.paddingBottom + subnodeBBox.height) / 2) + ")");
    }

    var shapeSvg = shape(d3.select(this), bbox, node);
    util.applyStyle(shapeSvg, node.style);

    var shapeBBox = shapeSvg.node().getBBox();
    node.width = shapeBBox.width;
    node.height = shapeBBox.height;
  });

  util.applyTransition(svgNodes.exit(), g)
    .style("opacity", 0)
    .remove();

  return svgNodes;
}
<commit_msg>Fix typo<commit_after>"use strict";

var _ = require("./lodash"),
    addLabel = require("./label/add-label"),
    addSubnodes = require("./add-subnodes"),
    util = require("./util"),
    d3 = require("./d3");

module.exports = createNodes;

function createNodes(selection, g, shapes) {
  var simpleNodes = g.nodes().filter(function(v) { return !util.isSubgraph(g, v); });
  var svgNodes = selection.selectAll("g.node")
    .data(simpleNodes, function(v) { return v; })
    .classed("update", true);

  svgNodes.selectAll("*").remove();
  svgNodes.enter()
    .append("g")
      .attr("class", "node")
      .style("opacity", 0);
  svgNodes.each(function(v) {
    var subnodeGroup, subnodeDom;
    var subnodeBBox = {width: 0, height: 0};
    var node = g.node(v),
        thisGroup = d3.select(this),
        labelGroup = thisGroup.append("g").attr("class", "label"),
        labelDom = addLabel(labelGroup, node),
        shape = shapes[node.shape],
        bbox = _.pick(labelDom.node().getBBox(), "width", "height");

    node.elem = this;

    // If the node has subnodes, add them to the node and modify the bbox.
    if (node.subnodes && node.subnodes.length) {
      subnodeGroup = addSubnodes(thisGroup, node);

      // Calculate the box surrounding the subnode elements. Adjust bbox to hold them.
      subnodeBBox = _.pick(subnodeGroup.node().getBBox(), "width", "height");
      bbox.height += subnodeBBox.height;
      bbox.width = Math.max(bbox.width, subnodeBBox.width);

      // If the tooltip is open, render it
      var hoveredSubnode = _(node.subnodes).find(subnode => subnode.tooltip);
      if (hoveredSubnode) {
        var tooltipGroup = thisGroup.append("g").attr("class", "tooltip");
        tooltipGroup.append("rect")
          .attr("x", 0)
          .attr("y", "20")
          .attr("width", 100)
          .attr("height", 30);
        tooltipGroup.append("text")
          .text(hoveredSubnode.ref['@type'][0])
          .attr("x", 0)
          .attr("y", "30");
      }
    } else {
      // The node doesn't have subnodes.
      subnodeGroup = subnodeDom = null;
      subnodeBBox.width = subnodeBBox.height = 0;
    }

    if (node.id) { thisGroup.attr("id", node.id); }
    if (node.labelId) { labelGroup.attr("id", node.labelId); }
    util.applyClass(thisGroup, node["class"],
      (thisGroup.classed("update") ? "update " : "") + "node");

    if (_.has(node, "width")) { bbox.width = node.width; }
    if (_.has(node, "height")) { bbox.height = node.height; }

    bbox.width += node.paddingLeft + node.paddingRight;
    bbox.height += node.paddingTop + node.paddingBottom;
    labelGroup.attr("transform", "translate(" +
      ((node.paddingLeft - node.paddingRight) / 2) + "," +
      ((node.paddingTop - node.paddingBottom - subnodeBBox.height) / 2) + ")");
    if (subnodeGroup) {
      subnodeGroup.attr("transform", "translate(" +
        -((subnodeBBox.width / 2) - 10) + "," +
        ((node.paddingTop - node.paddingBottom + subnodeBBox.height) / 2) + ")");
    }

    var shapeSvg = shape(d3.select(this), bbox, node);
    util.applyStyle(shapeSvg, node.style);

    var shapeBBox = shapeSvg.node().getBBox();
    node.width = shapeBBox.width;
    node.height = shapeBBox.height;
  });

  util.applyTransition(svgNodes.exit(), g)
    .style("opacity", 0)
    .remove();

  return svgNodes;
}
<|endoftext|>


<commit_before>const path = require('path')
const pify = require('pify')
const co = require('co')
const tmp = pify(require('tmp'), { include: ['file'] })
const fs = pify(require('fs'), { include: ['readFile', 'writeFile', 'unlink'] })
const Cucumber = {
  Cli: require('cucumber/lib/cli').default,
  TestCaseRunner: require('cucumber/lib/runtime/test_case_runner').default,
  UserCodeRunner: require('cucumber/lib/user_code_runner').default,
  statuses: require('cucumber/lib/status').default
}

tmp.setGracefulCleanup()

module.exports = class CucumberAPI {
  addAttachmentProvider (attachmentProvider) {
    this.attachmentProvider = attachmentProvider
    this.injectAttachmentHandler()
  }

  * handleAttachments (testCase, stepResult, attach) {
    if (
      !this.attachmentProvider ||
        stepResult.status !== Cucumber.statuses.FAILED ||
        stepResult._handledByNightwatchCucumber
    ) return

    stepResult._handledByNightwatchCucumber = true

    try {
      const feature = path.basename(testCase.uri, path.extname(testCase.uri))
      const scenario = testCase.pickle.name
      const attachment = yield * this.attachmentProvider(feature, scenario)

      if (attachment) {
        attach(attachment)
      }
    } catch (err) {
      console.error(err.stack)
    }
  }

  injectAttachmentHandler () {
    const self = this
    const originalTestCaseRunnerInvokeStep = Cucumber.TestCaseRunner.prototype.invokeStep
    Cucumber.TestCaseRunner.prototype.invokeStep = co.wrap(function * (step, stepDefinition) {
      const result = yield originalTestCaseRunnerInvokeStep.apply(this, arguments)

      yield * self.handleAttachments(
        this.testCase,
        result,
        ({ data, mimeType }) => {
          this.emit('test-step-attachment', {
            index: this.testStepIndex,
            data,
            media: {
              type: mimeType
            }
          })
        }
      )

      return result
    })
  }

  injectTimeoutHandler () {
    const originalUserCodeRunnerRun = Cucumber.UserCodeRunner.run
    Cucumber.UserCodeRunner.run = function (props) {
      props.timeoutInMilliseconds = -1
      return originalUserCodeRunnerRun.call(this, props)
    }
  }

  * run (args) {
    const cli = new Cucumber.Cli({
      argv: process.argv.slice(0, 2).concat(args),
      cwd: process.cwd(),
      stdout: process.stdout
    })

    return yield cli.run()
  }

  getJSONReportName (args) {
    const pattern = /^json:(.*)$/
    let i = 0
    while (i < args.length - 2 && !(args[i] === '--format' && pattern.test(args[i + 1]))) {
      i++
    }
    if (i < args.length - 2) {
      return args[i + 1].match(pattern)[1]
    }
  }

  isBooleanArg (args, index) {
    const nextIndex = index + 1
    return args[nextIndex].startsWith('--') || nextIndex === args.length - 1
  }

  getFeatureFiles (args) {
    const featureFiles = []
    let i = 0
    while (i < args.length) {
      if (args[i].startsWith('--')) {
        i += this.isBooleanArg(args, i) ? 1 : 2
      } else {
        featureFiles.push(args[i])
        i++
      }
    }
    return featureFiles
  }

  getAdjustedArgs ({args, featureFiles, jsonReport, tags, skipTags}) {
    const result = []
    let i = 0
    while (i < args.length) {
      if (args[i] === '--format' && args[i + 1].startsWith('json:')) {
        result.push('--format')
        result.push(`json:${jsonReport}`)
        i += 2
      } else if (args[i].startsWith('--')) {
        result.push(args[i])
        if (this.isBooleanArg(args, i)) {
          i++
        } else {
          result.push(args[i + 1])
          i += 2
        }
      } else {
        i++
      }
    }

    if (tags.length || skipTags.length) {
      result.push('--tags')
      const joinedTags = tags.join(' or ')
      const joinedSkipTags = skipTags.map(tag => `(not ${tag})`).join(' and ')
      if (joinedTags && joinedSkipTags) {
        result.push(`(${joinedTags}) and ${joinedSkipTags}`)
      } else if (joinedTags) {
        result.push(`${joinedTags}`)
      } else if (joinedSkipTags) {
        result.push(`${joinedSkipTags}`)
      }
    }

    featureFiles.forEach((featureFile) => {
      result.push(featureFile)
    })

    return result
  }

  * mergeCucumberJsonReports (reports, target) {
    let sumReport = []
    for (let i = 0; i < reports.length; i++) {
      try {
        const report = JSON.parse(yield fs.readFile(reports[i]))
        sumReport = sumReport.concat(report)
      } catch (e) {}
      try {
        yield fs.unlink(reports[i])
      } catch (e) {}
    }
    yield fs.writeFile(target, JSON.stringify(sumReport, null, 2))
  }
}
<commit_msg>Custom json formatters that pass file location of formatter do not work in parallel mode. This is due to the regular expression being used. (#409)<commit_after>const path = require('path')
const pify = require('pify')
const co = require('co')
const tmp = pify(require('tmp'), { include: ['file'] })
const fs = pify(require('fs'), { include: ['readFile', 'writeFile', 'unlink'] })
const Cucumber = {
  Cli: require('cucumber/lib/cli').default,
  TestCaseRunner: require('cucumber/lib/runtime/test_case_runner').default,
  UserCodeRunner: require('cucumber/lib/user_code_runner').default,
  statuses: require('cucumber/lib/status').default
}

tmp.setGracefulCleanup()

module.exports = class CucumberAPI {
  addAttachmentProvider (attachmentProvider) {
    this.attachmentProvider = attachmentProvider
    this.injectAttachmentHandler()
  }

  * handleAttachments (testCase, stepResult, attach) {
    if (
      !this.attachmentProvider ||
        stepResult.status !== Cucumber.statuses.FAILED ||
        stepResult._handledByNightwatchCucumber
    ) return

    stepResult._handledByNightwatchCucumber = true

    try {
      const feature = path.basename(testCase.uri, path.extname(testCase.uri))
      const scenario = testCase.pickle.name
      const attachment = yield * this.attachmentProvider(feature, scenario)

      if (attachment) {
        attach(attachment)
      }
    } catch (err) {
      console.error(err.stack)
    }
  }

  injectAttachmentHandler () {
    const self = this
    const originalTestCaseRunnerInvokeStep = Cucumber.TestCaseRunner.prototype.invokeStep
    Cucumber.TestCaseRunner.prototype.invokeStep = co.wrap(function * (step, stepDefinition) {
      const result = yield originalTestCaseRunnerInvokeStep.apply(this, arguments)

      yield * self.handleAttachments(
        this.testCase,
        result,
        ({ data, mimeType }) => {
          this.emit('test-step-attachment', {
            index: this.testStepIndex,
            data,
            media: {
              type: mimeType
            }
          })
        }
      )

      return result
    })
  }

  injectTimeoutHandler () {
    const originalUserCodeRunnerRun = Cucumber.UserCodeRunner.run
    Cucumber.UserCodeRunner.run = function (props) {
      props.timeoutInMilliseconds = -1
      return originalUserCodeRunnerRun.call(this, props)
    }
  }

  * run (args) {
    const cli = new Cucumber.Cli({
      argv: process.argv.slice(0, 2).concat(args),
      cwd: process.cwd(),
      stdout: process.stdout
    })

    return yield cli.run()
  }

  getJSONReportName (args) {
    const pattern = /^(.*)json:(.*)$/
    let i = 0
    while (i < args.length - 2 && !(args[i] === '--format' && pattern.test(args[i + 1]))) {
      i++
    }
    if (i < args.length - 2) {
      return args[i + 1].match(pattern)[2]
    }
  }

  isBooleanArg (args, index) {
    const nextIndex = index + 1
    return args[nextIndex].startsWith('--') || nextIndex === args.length - 1
  }

  getFeatureFiles (args) {
    const featureFiles = []
    let i = 0
    while (i < args.length) {
      if (args[i].startsWith('--')) {
        i += this.isBooleanArg(args, i) ? 1 : 2
      } else {
        featureFiles.push(args[i])
        i++
      }
    }
    return featureFiles
  }

  getAdjustedArgs ({args, featureFiles, jsonReport, tags, skipTags}) {
    const result = []
    let i = 0
    while (i < args.length) {
      if (args[i] === '--format' && args[i + 1].startsWith('json:')) {
        result.push('--format')
        result.push(`json:${jsonReport}`)
        i += 2
      } else if (args[i].startsWith('--')) {
        result.push(args[i])
        if (this.isBooleanArg(args, i)) {
          i++
        } else {
          result.push(args[i + 1])
          i += 2
        }
      } else {
        i++
      }
    }

    if (tags.length || skipTags.length) {
      result.push('--tags')
      const joinedTags = tags.join(' or ')
      const joinedSkipTags = skipTags.map(tag => `(not ${tag})`).join(' and ')
      if (joinedTags && joinedSkipTags) {
        result.push(`(${joinedTags}) and ${joinedSkipTags}`)
      } else if (joinedTags) {
        result.push(`${joinedTags}`)
      } else if (joinedSkipTags) {
        result.push(`${joinedSkipTags}`)
      }
    }

    featureFiles.forEach((featureFile) => {
      result.push(featureFile)
    })

    return result
  }

  * mergeCucumberJsonReports (reports, target) {
    let sumReport = []
    for (let i = 0; i < reports.length; i++) {
      try {
        const report = JSON.parse(yield fs.readFile(reports[i]))
        sumReport = sumReport.concat(report)
      } catch (e) {}
      try {
        yield fs.unlink(reports[i])
      } catch (e) {}
    }
    yield fs.writeFile(target, JSON.stringify(sumReport, null, 2))
  }
}
<|endoftext|>


<commit_before>'use strict';

var RSVP = require('rsvp');
var logger = require('../logger');
var api = require('../api');
var chalk = require('chalk');
var _ = require('lodash');
var getProjectId = require('../getProjectId');
var utils = require('../utils');
var FirebaseError = require('../error');
var track = require('../track');

var TARGETS = {
  hosting: require('./hosting'),
  database: require('./database'),
  functions: require('./functions'),
  storage: require('./storage')
};

var _chain = function(fns, context, options, payload) {
  var latest = (fns.shift() || RSVP.resolve)(context, options, payload);
  if (fns.length) {
    return latest.then(function() {
      return _chain(fns, context, options, payload);
    });
  }

  return latest;
};

/**
 * The `deploy()` function runs through a three step deploy process for a listed
 * number of deploy targets. This allows deploys to be done all together or
 * for individual deployable elements to be deployed as such.
 */
var deploy = function(targetNames, options) {
  var projectId = getProjectId(options);

  // --- TEMPORARY CHECKS FOR FUNCTION RULE COMPILATION
  var deployingFunctions = _.includes(targetNames, 'functions');
  var deployingRules = _.includes(targetNames, 'database');
  var warnText = chalk.yellow.bold('\n>--- ALPHA WARNING! ---<\n');
  if (deployingFunctions && !deployingRules) {
    console.log(warnText);
    utils.logWarning('Deploying functions without rules, triggers will not be updated.');
  }
  if (deployingRules && options.config._noRulesProvided) {
    console.log(warnText);
    utils.logWarning('Deploy will overwrite rules, but none are provided. Must specify "rules" in firebase.json.');
  }
  // --- END TEMPORARY CHECKS SECTION

  var payload = {};
  // a shared context object for deploy targets to decorate as needed
  var context = {projectId: projectId};
  var prepares = [];
  var deploys = [];
  var releases = [];

  for (var i = 0; i < targetNames.length; i++) {
    var targetName = targetNames[i];
    var target = TARGETS[targetName];

    if (!target) {
      return RSVP.reject(new FirebaseError(chalk.bold(targetName) + ' is not a valid deploy target', {exit: 1}));
    }

    if (target.prepare) {
      prepares.push(target.prepare);
    }
    if (target.deploy) {
      deploys.push(target.deploy);
    }
    if (target.release) {
      releases.push(target.release);
    }
  }

  logger.info();
  logger.info(chalk.bold(chalk.gray('===') + ' Deploying to \'' + projectId +  '\'...'));
  logger.info();

  var activeTargets = targetNames.filter(function(t) {
    return options.config.has(t);
  });
  utils.logBullet('deploying ' + chalk.bold(activeTargets.join(', ')));

  return _chain(prepares, context, options, payload).then(function() {
    return _chain(deploys, context, options, payload);
  }).then(function() {
    utils.logBullet('starting release process (may take several minutes)...');
    return _chain(releases, context, options, payload);
  }).then(function() {
    console.log(JSON.stringify(payload, null, 2));
    return api.request('POST', '/v1/projects/' + encodeURIComponent(projectId) + '/releases', {
      data: payload,
      auth: true,
      origin: api.deployOrigin
    });
  }).then(function(res) {
    if (_.has(options, 'config.notes.databaseRules')) {
      track('Rules Deploy', options.config.notes.databaseRules);
    }

    logger.info();
    utils.logSuccess(chalk.underline.bold('Deploy complete!'));
    logger.info();
    var deployedHosting = _.includes(targetNames, 'hosting');
    if (deployedHosting) {
      logger.info(chalk.bold('URL:'), utils.addSubdomain(api.hostingOrigin, options.instance));
    }
    logger.info(chalk.bold('Dashboard:'), utils.addSubdomain(api.realtimeOrigin, options.instance));
    if (deployedHosting) {
      logger.info();
      logger.info('Visit the URL above or run', chalk.bold('firebase open'));
    }
    return res.body;
  });
};

module.exports = deploy;
<commit_msg>Stray console.log<commit_after>'use strict';

var RSVP = require('rsvp');
var logger = require('../logger');
var api = require('../api');
var chalk = require('chalk');
var _ = require('lodash');
var getProjectId = require('../getProjectId');
var utils = require('../utils');
var FirebaseError = require('../error');
var track = require('../track');

var TARGETS = {
  hosting: require('./hosting'),
  database: require('./database'),
  functions: require('./functions'),
  storage: require('./storage')
};

var _chain = function(fns, context, options, payload) {
  var latest = (fns.shift() || RSVP.resolve)(context, options, payload);
  if (fns.length) {
    return latest.then(function() {
      return _chain(fns, context, options, payload);
    });
  }

  return latest;
};

/**
 * The `deploy()` function runs through a three step deploy process for a listed
 * number of deploy targets. This allows deploys to be done all together or
 * for individual deployable elements to be deployed as such.
 */
var deploy = function(targetNames, options) {
  var projectId = getProjectId(options);

  // --- TEMPORARY CHECKS FOR FUNCTION RULE COMPILATION
  var deployingFunctions = _.includes(targetNames, 'functions');
  var deployingRules = _.includes(targetNames, 'database');
  var warnText = chalk.yellow.bold('\n>--- ALPHA WARNING! ---<\n');
  if (deployingFunctions && !deployingRules) {
    console.log(warnText);
    utils.logWarning('Deploying functions without rules, triggers will not be updated.');
  }
  if (deployingRules && options.config._noRulesProvided) {
    console.log(warnText);
    utils.logWarning('Deploy will overwrite rules, but none are provided. Must specify "rules" in firebase.json.');
  }
  // --- END TEMPORARY CHECKS SECTION

  var payload = {};
  // a shared context object for deploy targets to decorate as needed
  var context = {projectId: projectId};
  var prepares = [];
  var deploys = [];
  var releases = [];

  for (var i = 0; i < targetNames.length; i++) {
    var targetName = targetNames[i];
    var target = TARGETS[targetName];

    if (!target) {
      return RSVP.reject(new FirebaseError(chalk.bold(targetName) + ' is not a valid deploy target', {exit: 1}));
    }

    if (target.prepare) {
      prepares.push(target.prepare);
    }
    if (target.deploy) {
      deploys.push(target.deploy);
    }
    if (target.release) {
      releases.push(target.release);
    }
  }

  logger.info();
  logger.info(chalk.bold(chalk.gray('===') + ' Deploying to \'' + projectId +  '\'...'));
  logger.info();

  var activeTargets = targetNames.filter(function(t) {
    return options.config.has(t);
  });
  utils.logBullet('deploying ' + chalk.bold(activeTargets.join(', ')));

  return _chain(prepares, context, options, payload).then(function() {
    return _chain(deploys, context, options, payload);
  }).then(function() {
    utils.logBullet('starting release process (may take several minutes)...');
    return _chain(releases, context, options, payload);
  }).then(function() {
    return api.request('POST', '/v1/projects/' + encodeURIComponent(projectId) + '/releases', {
      data: payload,
      auth: true,
      origin: api.deployOrigin
    });
  }).then(function(res) {
    if (_.has(options, 'config.notes.databaseRules')) {
      track('Rules Deploy', options.config.notes.databaseRules);
    }

    logger.info();
    utils.logSuccess(chalk.underline.bold('Deploy complete!'));
    logger.info();
    var deployedHosting = _.includes(targetNames, 'hosting');
    if (deployedHosting) {
      logger.info(chalk.bold('URL:'), utils.addSubdomain(api.hostingOrigin, options.instance));
    }
    logger.info(chalk.bold('Dashboard:'), utils.addSubdomain(api.realtimeOrigin, options.instance));
    if (deployedHosting) {
      logger.info();
      logger.info('Visit the URL above or run', chalk.bold('firebase open'));
    }
    return res.body;
  });
};

module.exports = deploy;
<|endoftext|>


<commit_before>var util = require('util');
var moment = require('moment');
var mysql = require('mysql');
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var MysqlDriver = Base.extend({
  init: function(connection) {
    this._super();
    this.connection = connection;
  },

  mapDataType: function(spec) {
    var len;
    switch(spec.type) {
      case type.TEXT:
        len = parseInt(spec.length) || 1000;
        if(len > 16777216) {
          return 'LONGTEXT';
        }
        if(len > 65536) {
          return 'MEDIUMTEXT';
        }
        if(len > 256) {
          return 'TEXT';
        }
        return 'TINYTEXT';
      case type.DATE_TIME:
        return 'DATETIME';
      case type.BLOB:
        len = parseInt(spec.length) || 1000;
        if(len > 16777216) {
          return 'LONGBLOB';
        }
        if(len > 65536) {
          return 'MEDIUMBLOB';
        }
        if(len > 256) {
          return 'BLOB';
        }
        return 'TINYBLOB';
      case type.BOOLEAN:
        return 'TINYINT(1)';
    }
    return this._super(str);
  },

  createColumnDef: function(name, spec, options) {
    var t = this.mapDataType(spec);
    var len;
    if(spec.type !== type.TEXT && spec.type !== type.BLOB) {
      len = spec.length ? util.format('(%s)', spec.length) : '';
      if (t === 'VARCHAR' && len === '') {
        len = '(255)';
      }
    }
    var constraint = this.createColumnConstraint(spec, options);
    return [name, t, len, constraint].join(' ');
  },

  createColumnConstraint: function(spec, options) {
    var constraint = [];
    if (spec.unsigned) {
      constraint.push('UNSIGNED');
    }

    if (spec.primaryKey && options.emitPrimaryKey) {
      constraint.push('PRIMARY KEY');
      if (spec.autoIncrement) {
        constraint.push('AUTO_INCREMENT');
      }
    }

    if (spec.notNull) {
      constraint.push('NOT NULL');
    }

    if (spec.unique) {
      constraint.push('UNIQUE');
    }

    if (spec.null) {
      constraint.push('NULL');
    }

    if (spec.defaultValue) {
      constraint.push('DEFAULT');

      if (typeof spec.defaultValue === 'string'){
        constraint.push("'" + spec.defaultValue + "'");
      } else {
        constraint.push(spec.defaultValue);
      }
    }

    return constraint.join(' ');
  },

  renameTable: function(tableName, newTableName, callback) {
    var sql = util.format('RENAME TABLE %s TO %s', tableName, newTableName);
    this.runSql(sql, callback);
  },

  removeColumn: function(tableName, columnName, callback) {
    var sql = util.format('ALTER TABLE %s DROP COLUMN %s', tableName, columnName);
    this.runSql(sql, callback);
  },

  removeIndex: function(tableName, indexName, callback) {
    // tableName is optional for other drivers, but required for mySql. So, check the args to ensure they are valid
    if (arguments.length === 2 && typeof(indexName) === 'function') {
      callback = indexName;
      process.nextTick(function () {
        callback(new Error('Illegal arguments, must provide "tableName" and "indexName"'));
      });

      return;
    }

    var sql = util.format('DROP INDEX %s ON %s', indexName, tableName);
    this.runSql(sql, callback);
  },

  renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
    var self = this, columnTypeSql = util.format("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '%s' AND COLUMN_NAME = '%s'", tableName, oldColumnName);
    this.all(columnTypeSql, function(err, result) {
      var columnType = result[0].COLUMN_TYPE;
      var alterSql = util.format("ALTER TABLE %s CHANGE %s %s %s", tableName, oldColumnName, newColumnName, columnType);
      self.runSql(alterSql, callback);
    });
  },

  changeColumn: function(tableName, columnName, columnSpec, callback) {
    var constraint = this.createColumnDef(columnName, columnSpec);
    var sql = util.format('ALTER TABLE %s CHANGE COLUMN %s %s', tableName, columnName, constraint);
    this.runSql(sql, callback);
  },

  addMigrationRecord: function (name, callback) {
    var formattedDate = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
    this.runSql('INSERT INTO migrations (name, run_on) VALUES (?, ?)', [name, formattedDate], callback);
  },

  runSql: function() {
    var callback = arguments[arguments.length - 1];
    log.sql.apply(null, arguments);
    if(global.dryRun) {
      return callback();
    }
    return this.connection.query.apply(this.connection, arguments);
  },

  all: function() {
    return this.connection.query.apply(this.connection, arguments);
  },

  close: function() {
    this.connection.end();
  }

});

exports.connect = function(config, callback) {
  var db;
  if (typeof(mysql.createConnection) === 'undefined') {
    db = config.db || new mysql.createClient(config);
  } else {
    db = config.db || new mysql.createConnection(config);
  }
  callback(null, new MysqlDriver(db));
};
<commit_msg>Fix calling super.mapDataType<commit_after>var util = require('util');
var moment = require('moment');
var mysql = require('mysql');
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var MysqlDriver = Base.extend({
  init: function(connection) {
    this._super();
    this.connection = connection;
  },

  mapDataType: function(spec) {
    var len;
    switch(spec.type) {
      case type.TEXT:
        len = parseInt(spec.length) || 1000;
        if(len > 16777216) {
          return 'LONGTEXT';
        }
        if(len > 65536) {
          return 'MEDIUMTEXT';
        }
        if(len > 256) {
          return 'TEXT';
        }
        return 'TINYTEXT';
      case type.DATE_TIME:
        return 'DATETIME';
      case type.BLOB:
        len = parseInt(spec.length) || 1000;
        if(len > 16777216) {
          return 'LONGBLOB';
        }
        if(len > 65536) {
          return 'MEDIUMBLOB';
        }
        if(len > 256) {
          return 'BLOB';
        }
        return 'TINYBLOB';
      case type.BOOLEAN:
        return 'TINYINT(1)';
    }
    return this._super(spec.type);
  },

  createColumnDef: function(name, spec, options) {
    var t = this.mapDataType(spec);
    var len;
    if(spec.type !== type.TEXT && spec.type !== type.BLOB) {
      len = spec.length ? util.format('(%s)', spec.length) : '';
      if (t === 'VARCHAR' && len === '') {
        len = '(255)';
      }
    }
    var constraint = this.createColumnConstraint(spec, options);
    return [name, t, len, constraint].join(' ');
  },

  createColumnConstraint: function(spec, options) {
    var constraint = [];
    if (spec.unsigned) {
      constraint.push('UNSIGNED');
    }

    if (spec.primaryKey && options.emitPrimaryKey) {
      constraint.push('PRIMARY KEY');
      if (spec.autoIncrement) {
        constraint.push('AUTO_INCREMENT');
      }
    }

    if (spec.notNull) {
      constraint.push('NOT NULL');
    }

    if (spec.unique) {
      constraint.push('UNIQUE');
    }

    if (spec.null) {
      constraint.push('NULL');
    }

    if (spec.defaultValue) {
      constraint.push('DEFAULT');

      if (typeof spec.defaultValue === 'string'){
        constraint.push("'" + spec.defaultValue + "'");
      } else {
        constraint.push(spec.defaultValue);
      }
    }

    return constraint.join(' ');
  },

  renameTable: function(tableName, newTableName, callback) {
    var sql = util.format('RENAME TABLE %s TO %s', tableName, newTableName);
    this.runSql(sql, callback);
  },

  removeColumn: function(tableName, columnName, callback) {
    var sql = util.format('ALTER TABLE %s DROP COLUMN %s', tableName, columnName);
    this.runSql(sql, callback);
  },

  removeIndex: function(tableName, indexName, callback) {
    // tableName is optional for other drivers, but required for mySql. So, check the args to ensure they are valid
    if (arguments.length === 2 && typeof(indexName) === 'function') {
      callback = indexName;
      process.nextTick(function () {
        callback(new Error('Illegal arguments, must provide "tableName" and "indexName"'));
      });

      return;
    }

    var sql = util.format('DROP INDEX %s ON %s', indexName, tableName);
    this.runSql(sql, callback);
  },

  renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
    var self = this, columnTypeSql = util.format("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '%s' AND COLUMN_NAME = '%s'", tableName, oldColumnName);
    this.all(columnTypeSql, function(err, result) {
      var columnType = result[0].COLUMN_TYPE;
      var alterSql = util.format("ALTER TABLE %s CHANGE %s %s %s", tableName, oldColumnName, newColumnName, columnType);
      self.runSql(alterSql, callback);
    });
  },

  changeColumn: function(tableName, columnName, columnSpec, callback) {
    var constraint = this.createColumnDef(columnName, columnSpec);
    var sql = util.format('ALTER TABLE %s CHANGE COLUMN %s %s', tableName, columnName, constraint);
    this.runSql(sql, callback);
  },

  addMigrationRecord: function (name, callback) {
    var formattedDate = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
    this.runSql('INSERT INTO migrations (name, run_on) VALUES (?, ?)', [name, formattedDate], callback);
  },

  runSql: function() {
    var callback = arguments[arguments.length - 1];
    log.sql.apply(null, arguments);
    if(global.dryRun) {
      return callback();
    }
    return this.connection.query.apply(this.connection, arguments);
  },

  all: function() {
    return this.connection.query.apply(this.connection, arguments);
  },

  close: function() {
    this.connection.end();
  }

});

exports.connect = function(config, callback) {
  var db;
  if (typeof(mysql.createConnection) === 'undefined') {
    db = config.db || new mysql.createClient(config);
  } else {
    db = config.db || new mysql.createConnection(config);
  }
  callback(null, new MysqlDriver(db));
};
<|endoftext|>