CleverStack/cleverstack-cli

View on GitHub
bin/clever-setup

Summary

Maintainability
Test Coverage
#!/usr/bin/env node

var path      = require('path')
  , lib       = GLOBAL.lib = require(path.join(__dirname, '..', 'lib'))
  , program   = GLOBAL.program = require('commander')
  , async     = require('async')
  , spawn     = require('child_process').spawn
  , readline  = require('readline')
  , fs        = require('fs')
  , os        = require('os')
  , isWin     = /^win32/.test(os.platform())
  , _         = require('lodash');

/**  Define CLI Usage
================================*/
program
  .option('-A, --allow-root', 'allow root for bower')
  .option('-S, --skip-protractor', 'skips installing protractor (Frontend only)')
  .option('-B, --bootstrap', 'will run `grunt bootstrap build` as part of the setup')
  .option('-v, --verbose', 'verbose output useful for debugging')
  .version(lib.pkg.version);

/**  Define CLI Help
================================*/
program.on('--help', function() {
  console.log('  Description:');
  console.log('');
  console.log('    Installs all NPM and Bower components for each module as well as building bundleDependencies.');
  console.log('    This command will also install Protractor unless explicitly skipping.');
  console.log('');
  console.log('  Examples:');
  console.log('');
  console.log('    clever setup');
  console.log('');
});

/**  Parse CLI Arguments
================================*/
program.parse(process.argv);

/**  Start Setup
================================*/
async.waterfall(
  [
    function startBar(callback) {
      lib.utils.initBar();
      lib.utils.installing('Modules');
      lib.utils.startBar(callback);
    },

    function findSeedsToTarget(callback) {
      lib
        .util.locations
        .get()
        .then(function(projects) {
          lib.utils.expandProgress(projects.length * 2);

          var backend = _.find(projects, function(p) {
            return p.name === 'backend';
          });

          var frontend = _.find(projects, function(p) {
            return p.name === 'frontend';
          });

          if (frontend) {
            lib.utils.expandProgress(1);
            if (!!program.bootstrap) {
              lib.utils.expandProgress(1);
            }

            if (!program.skipProtractor) {
              lib.utils.expandProgress(2);
            }
          }

          // @todo remove this patch when npm/npm#6435 is fixed
          if (backend) {
            lib.utils.expandProgress(2);
          }

          if (typeof backend === 'undefined' && typeof frontend === 'undefined') {
            callback('Couldn\'t find a backend or frontend seed for CleverStack within', process.cwd());
          } else {
            callback(null, projects);
          }
        })
        .catch(callback);
    },

    function eachSeed(projects, seedsComplete) {
      async.eachSeries(
        projects,
        function setupSeed(seed, seedComplete) {
          var _path       = seed.moduleDir;

          async.waterfall(
            [
              function npmInstallProject(callback) {
                var args = ['install']
                  , opts = { cwd: _path, env: process.env };

                lib
                  .utils
                  .info('  Installing NPM modules for ' + seed.name + '...')
                  .running('  Installing NPM modules for ' + seed.name + '...');

                if (!program.verbose) {
                  args.push('--silent');
                }

                var posixProc = spawn(!isWin ? 'npm' : 'npm.cmd', args, opts)
                  , exitHdlr  = process.kill.bind(process, posixProc)
                  , error     = '';

                if (!!program.verbose) {
                  readline
                    .createInterface({
                      input    : posixProc.stdout,
                      terminal : false
                    })
                    .on('line', function(line) {
                      console.log('        ' + line);
                    });
                }

                process.on('SIGTERM', exitHdlr);

                posixProc.on('error', function(err) {
                  error += err;
                });

                posixProc.on('close', function(code) {
                  process.removeListener('SIGTERM', exitHdlr);

                  if (code !== 0) {
                    return lib.utils.fail(error);
                  }

                  lib.utils.progress(1);
                  callback(null);
                });
              },

              function modulesNpmInstall(callback) {
                var modulesFolder = path.join(seed.moduleDir, seed.modulePath)
                  , modules = []
                  , folders = [];

                if (fs.existsSync(modulesFolder)) {
                  folders = fs.readdirSync(modulesFolder);

                  folders.forEach(function(folder) {
                    if (!/^\./ig.test(folder)) {
                      var modulesPackageJsonPath = path.join(seed.moduleDir, seed.modulePath, folder)
                        , stats = fs.statSync(modulesPackageJsonPath)
                        , modulesPackageJson = path.join(modulesPackageJsonPath, 'package.json');

                      if (stats.isDirectory() && fs.existsSync(modulesPackageJsonPath) && fs.existsSync(modulesPackageJson)) {
                        modulesPackageJson = require(modulesPackageJson);
                      } else {
                        modulesPackageJson = {
                          version: 'default',
                          dependencies: {}
                        };
                      }

                      modules.push({
                        name: folder,
                        version: modulesPackageJson.version,
                        dependencies: _.merge({}, modulesPackageJson.dependencies, modulesPackageJson.devDependencies)
                      });
                    }
                  });

                  lib.utils.warn('  Installing ' + seed.name + ' modules...');
                  lib.utils.running('  Installing ' + seed.name + ' modules...');
                }

                // Expand the progress bar for each module we need to install
                lib.utils.expandProgress(modules.length);
                
                async.eachSeries(
                  modules,
                  function (m, next) {
                    lib.utils.warn('  Installing ' + m.name + '@' + m.version + ' module...');
                    lib.utils.running('  Installing ' + m.name + '@' + m.version + ' module for ' + seed.name + '...');

                    if (program.verbose) {
                      Object.keys(m.dependencies).forEach(function(depName) {
                        lib.utils.info('    Installing ' + depName + '@' + m.dependencies[ depName ] + '...');
                      });
                    }

                    lib.project
                      .installModule({
                        moduleDir: seed.moduleDir,
                        modulePath: path.join('modules', m.name)
                      }, path.resolve(path.join(seed.moduleDir, seed.modulePath, m.name)))
                      .then(function() {
                        lib.utils.progress(1);
                        next();
                      })
                      .catch(next);
                  },
                  callback
               );
              },

              function bowerInstall(callback) {
                var bowerPath = path.join(seed.moduleDir, 'bower.json');

                lib.utils.progress(1);

                if (seed.name === 'backend' || !fs.existsSync(bowerPath)) {
                  return callback(null);
                }

                lib.utils.info('  Installing ' + seed.name + ' bower components...');
                lib.utils.running('  Installing ' + seed.name + ' bower components...');
                lib.project
                  .installBowerComponents(seed)
                  .then(callback)
                  .catch(callback);
              },

              function bootstrapFrontend(callback) {
                if (seed.name === 'frontend' && program.bootstrap) {
                  lib.utils.progress(1);
                  lib.utils.info('  Building frontend...');
                  lib.utils.running('  Building frontend...');

                  var proc = spawn('grunt bootstrap build', { cwd: _path }, function(err, stdout, stderr) {
                    lib.utils.progress(1);

                    (!!err || stderr.trim() !== '') ? callback(err || stderr.trim()) : callback(null);
                  });

                  // Pipe the output of spawn if verbose has been specified
                  if (program.verbose) {
                    proc.stdout.pipe(process.stdout);
                    proc.stderr.pipe(process.stdout);
                  }
                } else {
                  callback(null);
                }
              },

              function setupProtractor(callback) {
                if (seed.name === 'frontend') {
                  if (program.skipProtractor) {
                    lib.utils.warn('  Skipping installation of protractor...');
                    return callback(null);
                  }
                  lib.utils.warn('  Installing protractor...');
                  lib.utils.running('  Installing protractor components...');

                  var proc = spawn('npm run-script setup-protractor', { cwd: _path }, function(err, stdout, stderr) {
                    lib.utils.progress(2);

                    (!!err || stderr.trim() !== '') ? callback(err || stderr.trim()) : callback(null);
                  });

                  // Pipe the output of spawn if verbose has been specified
                  if (program.verbose) {
                    proc.stdout.pipe(process.stdout);
                    proc.stderr.pipe(process.stdout);
                  }
                } else {
                  callback(null);
                }
              }
            ],
            seedComplete
         );
        },
        seedsComplete
     );
    }
  ],
  function setupComplete(err) {
    if (!!err) {
      lib.utils.fail(err);
      process.exit(1);
    }

    lib.utils
      .installing('CleverStack')
      .running('Setup has completed.')
      .finishProgress()
      .success('Successfully setup all projects modules and components.');

    process.exit(0);
  }
);