<commit_before>'use strict';
const path = require('path');
const Generator = require('yeoman-generator');
const yosay = require('yosay');
const chalk = require('chalk');
const isWin = process.platform === 'win32';
const homedir = (isWin) ? process.env.HOMEPATH : process.env.HOME;
const fs = require('fs');
const mkdirp = require('mkdirp');
const GruntfileEditor = require('gruntfile-editor');

function getDirectories(srcpath) {
  return fs.readdirSync(srcpath).filter(function(file) {
    return fs.statSync(path.join(srcpath, file)).isDirectory() && file !== 'zips';
  });
}

module.exports = class extends Generator {
  initializing() {
    this.gruntfile = new GruntfileEditor();
  }

  prompting() {
    var done = this.async();
    var self = this;

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the ArcGIS Web AppBuilder generator!'));
    this.log(chalk.yellow('These generators should be run in the root folder of your project.'));

    var prompts = [{
      type: 'list',
      choices: [
        {
        value: 'is2d',
        name: '2D'
        },
        {
        value: 'is3d',
        name: '3D'
        }
      ],
      name: 'widgetsType',
      message: 'Type of widget(s) to be generated:'
    }, {
      name: 'wabRoot',
      message: 'Web AppBuilder install root:',
      'default': function(currentAnswers) {
        return path.join(homedir, 'WebAppBuilderForArcGIS');
      },
      validate: function(wabPath) {
        // make sure input directory and the following paths exist:
        // * server
        // * client
        var paths = [wabPath, path.join(wabPath, 'server'), path.join(wabPath, 'client')];
        try {
          paths.forEach(function(path) {
            fs.accessSync(path, fs.F_OK);
          });
          return true;
        } catch (e) {
          // It isn't accessible
          return 'Invalid path. Please ensure this is a valid path to your WAB root.';
        }
      }
    }, {
      when: function(currentAnswers) {
        try {
          var appsPath = path.join(currentAnswers.wabRoot, 'server', 'apps');
          var appsDirectories = getDirectories(appsPath);
          if (appsDirectories.length > 0) {
            return true;
          } else {
            this.log(chalk.red('You do not have any WAB apps setup yet. After you create an app, please see the Gruntfile for a todo, or run this generator again.'));
          }
        } catch (e) {
          this.log(chalk.red('You do not have any WAB apps setup yet. After you create an app, please see the Gruntfile for a todo, or run this generator again.'));
        }

      }.bind(this),
      name: 'appDirId',
      type: 'list',
      message: 'Web AppBuilder application:',
      choices: function(currentAnswers) {
        // Always include option for "None"
        var retArray = [{
          name: 'None',
          value: 'None',
          short: 'N'
        }];
        var appsPath = path.join(currentAnswers.wabRoot, 'server', 'apps');
        var appsDirectories = getDirectories(appsPath);
        appsDirectories.forEach(function(appDirectory) {
          try {
            // get the config file, convert to JSON, and read the title property
            var configPath = path.join(currentAnswers.wabRoot, 'server', 'apps', appDirectory, 'config.json');
            var configJson = JSON.parse(fs.readFileSync(configPath, 'utf8'));
            if (configJson.hasOwnProperty('title') && configJson.title !== '') {
              retArray.push({
                name: configJson.title,
                value: appDirectory,
                short: appDirectory
              });
            } else {
              // does not have title property or is empty. Use the app folder name (number) as the name.
              retArray.push({
                name: appDirectory,
                value: appDirectory,
                short: appDirectory
              });
            }
          } catch (e) {
            // Cannot open the config file. Just use the app folder name (number) as the name
            retArray.push({
              name: appDirectory,
              value: appDirectory,
              short: appDirectory
            });
          }
        });
        return retArray;
      }
    }, {
      type: 'confirm',
      message: 'Would you like to use SASS for CSS preprocessing?',
      name: 'useSass'
    }, {
      name: 'jsVersion',
      type: 'list',
      message: 'Which JavaScript  syntax version would you like to develop in?',
      choices: ['ES5', 'ES2015', 'TypeScript']
    }];

    this.prompt(prompts).then(function(props) {
      this.wabRoot = props.wabRoot;
      this.widgetsType = props.widgetsType;
      this.useSass = props.useSass;
      this.jsVersion = props.jsVersion;
      if (props.appDirId && props.appDirId !== 'None') {
        this.appDirId = props.appDirId;
      } else {
        this.appDirId = false;
      }
      done();
    }.bind(this));
  }

  writing() {

      mkdirp('widgets');
      this.config.set('widgetsType', this.widgetsType);
      this.config.set('useSass', this.useSass);
    this.config.set('jsVersion', this.jsVersion);

    // gruntConfig:

      // Setting up the stemappDir and appDir Gruntfile variables:
      var stemappDir;
      if (this.widgetsType === 'is3d') {
        stemappDir = path.join(this.wabRoot, 'client', 'stemapp3d');
      } else {
        stemappDir = path.join(this.wabRoot, 'client', 'stemapp');
      }
      this.appDir = false;
      if (this.appDirId) {
        this.appDir = path.join(this.wabRoot, 'server', 'apps', this.appDirId);
      }
      if (isWin) {
        // this hack is needed to ensure paths are not escaped when injected into Gruntfile
        stemappDir = stemappDir.replace(/\\/g, '/');
        if (this.appDir) {
          this.appDir = this.appDir.replace(/\\/g, '/');
        }
      }
      this.gruntfile.insertVariable('stemappDir', '"' + stemappDir + '"');
      if (this.appDir) {
        this.gruntfile.insertVariable('appDir', '"' + this.appDir + '"');
      } else {
        this.gruntfile.insertVariable('appDir', '"TODO - AFTER CREATING AN APP, PLEASE PUT PATH HERE AND INSERT ENTRY IN SYNC.MAIN.FILES BELOW."');
      }

      // SYNC CONFIG
      var syncConfig = '{ main: { verbose: true, files: [';
      var filesPrefix = '{cwd: \'dist/\', src: \'**\', dest: ';
      syncConfig = syncConfig + filesPrefix + 'stemappDir }';
      if (this.appDir) {
        syncConfig = syncConfig + ',' + filesPrefix + 'appDir }';
      }
      syncConfig = syncConfig + ']';
      syncConfig = syncConfig + '} }';

      this.gruntfile.insertConfig('sync', syncConfig);

    if(this.jsVersion === 'TypeScript') {
      // TS CONFIG
      var tsConfig = {
        default: {
          tsconfig: {
            passThrough: true
          }
        }
      };
      this.gruntfile.insertConfig('ts', JSON.stringify(tsConfig));
    } else {
      // BABEL CONFIG
      var babelConfig = {
        options: {
          sourceMap: true
        },
        main: {
          files: [{
            expand: true,
            src: [
                'widgets/*.js',
				'widgets/**/*.js',
				'widgets/**/**/*.js',
				'widgets/!**/**/nls/*.js',
				'themes/*.js',
				'themes/**/*.js',
				'themes/**/**/*.js',
				'themes/!**/**/nls/*.js'
            ],
            dest: 'dist/'
          }]
        }
      };
      this.gruntfile.insertConfig('babel', JSON.stringify(babelConfig));
    }

      // WATCH CONFIG
      this.gruntfile.insertConfig('watch', `{
        main: {
          files: ['widgets/**', 'themes/**'],
        tasks: ['clean', ${(this.useSass ? '\'sass\', ' : '')}${(this.jsVersion === 'TypeScript' ? '\'ts\', ' : '\'babel\', ')} 'copy', 'sync'],
          options: {
            spawn: false,
          atBegin: true,
          livereload: true
          }
        }
      }`);

      // COPY CONFIG
      this.gruntfile.insertConfig('copy', JSON.stringify({
        main: {
          src: [
            'widgets/**/**.html',
            'widgets/**/**.json',
            'widgets/**/**.css',
            'widgets/**/images/**',
            'widgets/**/nls/**',
            'themes/**/**.html',
            'themes/**/**.json',
            'themes/**/**.css',
            'themes/**/images/**',
            'themes/**/nls/**'
          ],
          dest: 'dist/',
          expand: true
        }
      }));

      // CLEAN CONFIG
      this.gruntfile.insertConfig('clean', JSON.stringify({
        dist: {
          src: 'dist/*'
        }
      }));

      // SASS CONFIG
      if(this.useSass) {

        // must require in the "sass" variable so it can be used on the "implementation" line below:
        this.gruntfile.prependJavaScript('const sass = require(\'node-sass\');');

        this.gruntfile.insertConfig('sass', `{
          dist: {
            options: {
              implementation: sass,
              sourceMap: true,
            },

            files: [{
              expand: true,
              src: ['widgets/**/*.scss'],
              rename: function(dest, src) {
                return src.replace('scss', 'css')
              }
            }]
          }
        }`);
        this.gruntfile.loadNpmTasks('node-sass');
        this.gruntfile.loadNpmTasks('grunt-sass');
      }

      // load tasks
    if(this.jsVersion === 'TypeScript') {
      this.gruntfile.loadNpmTasks('grunt-ts');
    } else {
      this.gruntfile.loadNpmTasks('grunt-babel');
    }
      this.gruntfile.loadNpmTasks('grunt-contrib-clean');
      this.gruntfile.loadNpmTasks('grunt-contrib-copy');
      this.gruntfile.loadNpmTasks('grunt-contrib-watch');
      this.gruntfile.loadNpmTasks('grunt-sync');

      // register tasks
      this.gruntfile.registerTask('default', ['watch']);

    // projectFiles:

      this.fs.copyTpl(
        this.templatePath('editorconfig'),
        this.destinationPath('.editorconfig')
      );

    if(this.jsVersion === 'TypeScript') {
      this.fs.copyTpl(
        this.templatePath('tsconfig'),
        this.destinationPath('tsconfig.json')
      );
    } else {
      this.fs.copyTpl(
        this.templatePath('babelrc'),
        this.destinationPath('.babelrc')
      );
    }
      const buildObj = {
        "skip-test": true,
        "skip-main": true,
      }

      if (this.appDir && this.widgetsType !== 'is3d'){
        buildObj.scripts = {
          "build": `esri-wab-build ${this.appDir}`
        };
      }

      this.composeWith(require.resolve('generator-npm-init/app'),buildObj);

      fs.writeFileSync('Gruntfile.js', this.gruntfile.toString());
    }

  install() {
    if (this.options['skip-install']) {
      return;
    }

    // we install different sets of packages depending on TypeScript or not:
    var dependencies = [
      'grunt',
      'grunt-contrib-clean',
      'grunt-contrib-copy',
      'node-sass',
      'grunt-sass@3.0.1',
      'grunt-sync',
      'grunt-contrib-watch',
      'esri-wab-build@1.0.1'
    ];

    if(this.jsVersion === 'TypeScript') {
      dependencies = dependencies.concat([
        'dojo-typings',
        'grunt-contrib-connect',
        'grunt-ts',
        'typescript@3.0.3'
      ]);
      // 3D vs 2D we need to install a different declarations file:
      if (this.widgetsType === 'is3d') {
        dependencies.push('@types/arcgis-js-api@4.6.0');
      } else {
        dependencies.push('@types/arcgis-js-api@3.23.0');
      }
    } else {
      dependencies = dependencies.concat([
      'babel-plugin-transform-es2015-modules-simple-amd',
      'babel-preset-es2015-without-strict',
        'babel-preset-stage-0',
        'grunt-babel',
        'babel-core'
      ]);
    }

    this.npmInstall(dependencies, {
      'saveDev': true
    });
  }
}
<commit_msg>package versions<commit_after>'use strict';
const path = require('path');
const Generator = require('yeoman-generator');
const yosay = require('yosay');
const chalk = require('chalk');
const isWin = process.platform === 'win32';
const homedir = (isWin) ? process.env.HOMEPATH : process.env.HOME;
const fs = require('fs');
const mkdirp = require('mkdirp');
const GruntfileEditor = require('gruntfile-editor');

function getDirectories(srcpath) {
  return fs.readdirSync(srcpath).filter(function(file) {
    return fs.statSync(path.join(srcpath, file)).isDirectory() && file !== 'zips';
  });
}

module.exports = class extends Generator {
  initializing() {
    this.gruntfile = new GruntfileEditor();
  }

  prompting() {
    var done = this.async();
    var self = this;

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the ArcGIS Web AppBuilder generator!'));
    this.log(chalk.yellow('These generators should be run in the root folder of your project.'));

    var prompts = [{
      type: 'list',
      choices: [
        {
        value: 'is2d',
        name: '2D'
        },
        {
        value: 'is3d',
        name: '3D'
        }
      ],
      name: 'widgetsType',
      message: 'Type of widget(s) to be generated:'
    }, {
      name: 'wabRoot',
      message: 'Web AppBuilder install root:',
      'default': function(currentAnswers) {
        return path.join(homedir, 'WebAppBuilderForArcGIS');
      },
      validate: function(wabPath) {
        // make sure input directory and the following paths exist:
        // * server
        // * client
        var paths = [wabPath, path.join(wabPath, 'server'), path.join(wabPath, 'client')];
        try {
          paths.forEach(function(path) {
            fs.accessSync(path, fs.F_OK);
          });
          return true;
        } catch (e) {
          // It isn't accessible
          return 'Invalid path. Please ensure this is a valid path to your WAB root.';
        }
      }
    }, {
      when: function(currentAnswers) {
        try {
          var appsPath = path.join(currentAnswers.wabRoot, 'server', 'apps');
          var appsDirectories = getDirectories(appsPath);
          if (appsDirectories.length > 0) {
            return true;
          } else {
            this.log(chalk.red('You do not have any WAB apps setup yet. After you create an app, please see the Gruntfile for a todo, or run this generator again.'));
          }
        } catch (e) {
          this.log(chalk.red('You do not have any WAB apps setup yet. After you create an app, please see the Gruntfile for a todo, or run this generator again.'));
        }

      }.bind(this),
      name: 'appDirId',
      type: 'list',
      message: 'Web AppBuilder application:',
      choices: function(currentAnswers) {
        // Always include option for "None"
        var retArray = [{
          name: 'None',
          value: 'None',
          short: 'N'
        }];
        var appsPath = path.join(currentAnswers.wabRoot, 'server', 'apps');
        var appsDirectories = getDirectories(appsPath);
        appsDirectories.forEach(function(appDirectory) {
          try {
            // get the config file, convert to JSON, and read the title property
            var configPath = path.join(currentAnswers.wabRoot, 'server', 'apps', appDirectory, 'config.json');
            var configJson = JSON.parse(fs.readFileSync(configPath, 'utf8'));
            if (configJson.hasOwnProperty('title') && configJson.title !== '') {
              retArray.push({
                name: configJson.title,
                value: appDirectory,
                short: appDirectory
              });
            } else {
              // does not have title property or is empty. Use the app folder name (number) as the name.
              retArray.push({
                name: appDirectory,
                value: appDirectory,
                short: appDirectory
              });
            }
          } catch (e) {
            // Cannot open the config file. Just use the app folder name (number) as the name
            retArray.push({
              name: appDirectory,
              value: appDirectory,
              short: appDirectory
            });
          }
        });
        return retArray;
      }
    }, {
      type: 'confirm',
      message: 'Would you like to use SASS for CSS preprocessing?',
      name: 'useSass'
    }, {
      name: 'jsVersion',
      type: 'list',
      message: 'Which JavaScript  syntax version would you like to develop in?',
      choices: ['ES5', 'ES2015', 'TypeScript']
    }];

    this.prompt(prompts).then(function(props) {
      this.wabRoot = props.wabRoot;
      this.widgetsType = props.widgetsType;
      this.useSass = props.useSass;
      this.jsVersion = props.jsVersion;
      if (props.appDirId && props.appDirId !== 'None') {
        this.appDirId = props.appDirId;
      } else {
        this.appDirId = false;
      }
      done();
    }.bind(this));
  }

  writing() {

      mkdirp('widgets');
      this.config.set('widgetsType', this.widgetsType);
      this.config.set('useSass', this.useSass);
    this.config.set('jsVersion', this.jsVersion);

    // gruntConfig:

      // Setting up the stemappDir and appDir Gruntfile variables:
      var stemappDir;
      if (this.widgetsType === 'is3d') {
        stemappDir = path.join(this.wabRoot, 'client', 'stemapp3d');
      } else {
        stemappDir = path.join(this.wabRoot, 'client', 'stemapp');
      }
      this.appDir = false;
      if (this.appDirId) {
        this.appDir = path.join(this.wabRoot, 'server', 'apps', this.appDirId);
      }
      if (isWin) {
        // this hack is needed to ensure paths are not escaped when injected into Gruntfile
        stemappDir = stemappDir.replace(/\\/g, '/');
        if (this.appDir) {
          this.appDir = this.appDir.replace(/\\/g, '/');
        }
      }
      this.gruntfile.insertVariable('stemappDir', '"' + stemappDir + '"');
      if (this.appDir) {
        this.gruntfile.insertVariable('appDir', '"' + this.appDir + '"');
      } else {
        this.gruntfile.insertVariable('appDir', '"TODO - AFTER CREATING AN APP, PLEASE PUT PATH HERE AND INSERT ENTRY IN SYNC.MAIN.FILES BELOW."');
      }

      // SYNC CONFIG
      var syncConfig = '{ main: { verbose: true, files: [';
      var filesPrefix = '{cwd: \'dist/\', src: \'**\', dest: ';
      syncConfig = syncConfig + filesPrefix + 'stemappDir }';
      if (this.appDir) {
        syncConfig = syncConfig + ',' + filesPrefix + 'appDir }';
      }
      syncConfig = syncConfig + ']';
      syncConfig = syncConfig + '} }';

      this.gruntfile.insertConfig('sync', syncConfig);

    if(this.jsVersion === 'TypeScript') {
      // TS CONFIG
      var tsConfig = {
        default: {
          tsconfig: {
            passThrough: true
          }
        }
      };
      this.gruntfile.insertConfig('ts', JSON.stringify(tsConfig));
    } else {
      // BABEL CONFIG
      var babelConfig = {
        options: {
          sourceMap: true
        },
        main: {
          files: [{
            expand: true,
            src: [
                'widgets/*.js',
				'widgets/**/*.js',
				'widgets/**/**/*.js',
				'widgets/!**/**/nls/*.js',
				'themes/*.js',
				'themes/**/*.js',
				'themes/**/**/*.js',
				'themes/!**/**/nls/*.js'
            ],
            dest: 'dist/'
          }]
        }
      };
      this.gruntfile.insertConfig('babel', JSON.stringify(babelConfig));
    }

      // WATCH CONFIG
      this.gruntfile.insertConfig('watch', `{
        main: {
          files: ['widgets/**', 'themes/**'],
        tasks: ['clean', ${(this.useSass ? '\'sass\', ' : '')}${(this.jsVersion === 'TypeScript' ? '\'ts\', ' : '\'babel\', ')} 'copy', 'sync'],
          options: {
            spawn: false,
          atBegin: true,
          livereload: true
          }
        }
      }`);

      // COPY CONFIG
      this.gruntfile.insertConfig('copy', JSON.stringify({
        main: {
          src: [
            'widgets/**/**.html',
            'widgets/**/**.json',
            'widgets/**/**.css',
            'widgets/**/images/**',
            'widgets/**/nls/**',
            'themes/**/**.html',
            'themes/**/**.json',
            'themes/**/**.css',
            'themes/**/images/**',
            'themes/**/nls/**'
          ],
          dest: 'dist/',
          expand: true
        }
      }));

      // CLEAN CONFIG
      this.gruntfile.insertConfig('clean', JSON.stringify({
        dist: {
          src: 'dist/*'
        }
      }));

      // SASS CONFIG
      if(this.useSass) {

        // must require in the "sass" variable so it can be used on the "implementation" line below:
        this.gruntfile.prependJavaScript('const sass = require(\'node-sass\');');

        this.gruntfile.insertConfig('sass', `{
          dist: {
            options: {
              implementation: sass,
              sourceMap: true,
            },

            files: [{
              expand: true,
              src: ['widgets/**/*.scss'],
              rename: function(dest, src) {
                return src.replace('scss', 'css')
              }
            }]
          }
        }`);
        this.gruntfile.loadNpmTasks('node-sass');
        this.gruntfile.loadNpmTasks('grunt-sass');
      }

      // load tasks
    if(this.jsVersion === 'TypeScript') {
      this.gruntfile.loadNpmTasks('grunt-ts');
    } else {
      this.gruntfile.loadNpmTasks('grunt-babel');
    }
      this.gruntfile.loadNpmTasks('grunt-contrib-clean');
      this.gruntfile.loadNpmTasks('grunt-contrib-copy');
      this.gruntfile.loadNpmTasks('grunt-contrib-watch');
      this.gruntfile.loadNpmTasks('grunt-sync');

      // register tasks
      this.gruntfile.registerTask('default', ['watch']);

    // projectFiles:

      this.fs.copyTpl(
        this.templatePath('editorconfig'),
        this.destinationPath('.editorconfig')
      );

    if(this.jsVersion === 'TypeScript') {
      this.fs.copyTpl(
        this.templatePath('tsconfig'),
        this.destinationPath('tsconfig.json')
      );
    } else {
      this.fs.copyTpl(
        this.templatePath('babelrc'),
        this.destinationPath('.babelrc')
      );
    }
      const buildObj = {
        "skip-test": true,
        "skip-main": true,
      }

      if (this.appDir && this.widgetsType !== 'is3d'){
        buildObj.scripts = {
          "build": `esri-wab-build ${this.appDir}`
        };
      }

      this.composeWith(require.resolve('generator-npm-init/app'),buildObj);

      fs.writeFileSync('Gruntfile.js', this.gruntfile.toString());
    }

  install() {
    if (this.options['skip-install']) {
      return;
    }

    // we install different sets of packages depending on TypeScript or not:
    var dependencies = [
      'grunt',
      'grunt-contrib-clean',
      'grunt-contrib-copy',
      'node-sass',
      'grunt-sass@3.0.1',
      'grunt-sync@^0.8.0',
      'grunt-contrib-watch',
      'esri-wab-build@1.0.1'
    ];

    if(this.jsVersion === 'TypeScript') {
      dependencies = dependencies.concat([
        'dojo-typings',
        'grunt-contrib-connect',
        'grunt-ts',
        'typescript@3.0.3'
      ]);
      // 3D vs 2D we need to install a different declarations file:
      if (this.widgetsType === 'is3d') {
        dependencies.push('@types/arcgis-js-api@4.8.0');
      } else {
        dependencies.push('@types/arcgis-js-api@3.25.0');
      }
    } else {
      dependencies = dependencies.concat([
      'babel-plugin-transform-es2015-modules-simple-amd',
      'babel-preset-es2015-without-strict',
        'babel-preset-stage-0',
        'grunt-babel',
        'babel-core'
      ]);
    }

    this.npmInstall(dependencies, {
      'saveDev': true
    });
  }
}
<|endoftext|>


<commit_before>"use strict";

require('colors');
const AWS = require('aws-sdk');
const fs = require('fs');
const fsx = require('../helpers/fs-additions');
const path = require('path');
const Promise = require('bluebird');
const _ = require('lodash');

function uploadAssets(context) {
    const S3 = new AWS.S3({
        params: {
            Bucket: context.project.bucket
        }
    });

    return new Promise(function(resolve) {
        console.log('\nUploading stack assets');
        resolve(context);
    }).then(function(ctx) {
        return Promise.mapSeries(ctx.lambdas, function(lambda) {
            return new Promise(function(resolve, reject) {
                if (!lambda.zip) {
                    return reject(new Error('No zipped Lambda to upload'));
                }

                process.stdout.write(`\tUploading ${path.basename(lambda.zip)}`);
                S3.upload({
                    Key: [context.project.timestamp, path.basename(lambda.zip)].join('/'),
                    Body: fs.createReadStream(lambda.zip)
                }, function(err, result) {
                    if (err) {
                        console.log(' ✖'.red);
                        return reject(err);
                    }

                    console.log(' ✔'.green);
                    resolve(result);
                });
            });
        });
    }).then(function(results) {
        const ctx = _.clone(context);
        ctx.lambdas = context.lambdas.map(function(lambda, idx) {
            return _.assign({}, lambda, {
                s3: results[idx].Location
            });
        });

        return ctx;
    }).then(function(ctx) {
        // Upload the configuration from the context
        return new Promise(function(resolve, reject) {
            process.stdout.write(`\tUploading stack configuration`);

            S3.upload({
                Key: [context.project.timestamp, path.basename(context.stack.configuration)].join('/'),
                Body: fs.createReadStream(context.stack.configuration)
            }, function(err) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve(ctx);
            });
        });
    });
}

function updateStack(context) {
    const CF = new AWS.CloudFormation({ apiVersion: '2010-05-15' });

    const stack = context.stack;
    const formationFile = stack.configuration;
    const existingStack = stack.stack;
    const stackName = stack.name;

    const stackCF = fsx.readJSONFileSync(formationFile);
    const parameters = [
        {
            ParameterKey: "aaStage",
            ParameterValue: context.project.stage
        },
        {
            ParameterKey: "aaProjectName",
            ParameterValue: context.project.name
        },
        {
            ParameterKey: "aaRegion",
            ParameterValue: context.project.region
        }
    ];

    let promise;

    console.log('\nUpdating CF stack');

    // Depending on whether there is a stack or not, either update one or
    // create a new stack instance
    if (existingStack) {
        promise = new Promise(function(resolve, reject) {
            process.stdout.write('\tFound existing stack, updating');

            CF.updateStack({
                StackName: existingStack.StackId,
                TemplateBody: JSON.stringify(stackCF),
                Parameters: parameters,
                Capabilities: [
                    'CAPABILITY_IAM'
                ]
            }, function(err, result) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve({
                    id: result.StackId,
                    pendingStates: [
                        'UPDATE_IN_PROGRESS',
                        'UPDATE_ROLLBACK_IN_PROGRESS',
                        'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS',
                        'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'
                    ],
                    acceptedStates: ['UPDATE_COMPLETE']
                });
            });
        });
    } else {
        promise = new Promise(function(resolve, reject) {
            process.stdout.write('\tCreating a new stack');

            CF.createStack({
                StackName: stackName,
                TemplateBody: JSON.stringify(stackCF),
                Parameters: parameters,
                Capabilities: [
                    'CAPABILITY_IAM'
                ],
                OnFailure: 'ROLLBACK'
            }, function(err, result) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve({
                    id: result.StackId,
                    pendingStates: [
                        'CREATE_IN_PROGRESS'
                    ],
                    acceptedStates: ['CREATE_COMPLETE']
                });
            });
        });
    }

    // Wait until stack returns to an acceptedState (or until it is in an
    // unacceptable state)
    return promise.then(function(state) {
        return new Promise(function(resolve, reject) {
            process.stdout.write('\tWaiting for stack to transition to state: ' + state.acceptedStates);

            const interval = setInterval(function() {
                CF.describeStacks({
                    StackName: state.id
                }, function(err, result) {
                    if (err) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        return reject(err);
                    }

                    const stacks = result.Stacks;
                    if (!stacks || stacks.length == 0) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        return reject(new Error('Stacks dissapeared'));
                    }

                    const currentState = stacks[0].StackStatus;
                    if (state.acceptedStates.indexOf(currentState) !== -1) {
                        clearInterval(interval);
                        console.log(' ✔'.green);

                        const newCtx = _.assign({}, context);
                        newCtx.stack.id = state.id;
                        resolve(newCtx);
                    } else if (state.pendingStates.indexOf(currentState) === -1) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        reject(new Error('Stack changed to an unacceptable state'));
                    }
                });
            }, 3000);
        });
    });
}

//
//  Step for updating the CF based stack, including uploading all
//  assets to S3 (Lambdas + configuration files)
//
//  Once completed, the stack is stable and updated
//
module.exports = function(context) {
    return uploadAssets(context).then(updateStack);
};
<commit_msg>Make sure to upload the API definition before updating the stack<commit_after>"use strict";

require('colors');
const AWS = require('aws-sdk');
const fs = require('fs');
const fsx = require('../helpers/fs-additions');
const path = require('path');
const Promise = require('bluebird');
const _ = require('lodash');

function uploadAssets(context) {
    const S3 = new AWS.S3({
        params: {
            Bucket: context.project.bucket
        }
    });

    return new Promise(function(resolve) {
        console.log('\nUploading stack assets');
        resolve(context);
    }).then(function(ctx) {
        return Promise.mapSeries(ctx.lambdas, function(lambda) {
            return new Promise(function(resolve, reject) {
                if (!lambda.zip) {
                    return reject(new Error('No zipped Lambda to upload'));
                }

                process.stdout.write(`\tUploading ${path.basename(lambda.zip)}`);
                S3.upload({
                    Key: [context.project.timestamp, path.basename(lambda.zip)].join('/'),
                    Body: fs.createReadStream(lambda.zip)
                }, function(err, result) {
                    if (err) {
                        console.log(' ✖'.red);
                        return reject(err);
                    }

                    console.log(' ✔'.green);
                    resolve(result);
                });
            });
        });
    }).then(function(results) {
        const ctx = _.clone(context);
        ctx.lambdas = context.lambdas.map(function(lambda, idx) {
            return _.assign({}, lambda, {
                s3: results[idx].Location
            });
        });

        return ctx;
    }).then(function(ctx) {
        // Upload the configuration from the context
        return new Promise(function(resolve, reject) {
            process.stdout.write(`\tUploading stack configuration`);

            S3.upload({
                Key: [context.project.timestamp, path.basename(context.stack.configuration)].join('/'),
                Body: fs.createReadStream(context.stack.configuration)
            }, function(err) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve(ctx);
            });
        });
    }).then(function(ctx) {
        if (!ctx.api.skip && ctx.api.configuration) {
            return new Promise(function(resolve, reject) {
                process.stdout.write(`\tUploading API definition`);

                S3.upload({
                    Key: [ctx.project.timestamp, path.basename(ctx.api.configuration)].join('/'),
                    Body: fs.createReadStream(ctx.api.configuration)
                }, function(err) {
                    if (err) {
                        console.log(' ✖'.red);
                        return reject(err);
                    }

                    console.log(' ✔'.green);
                    resolve(ctx);
                });
            });
        }

        return ctx;
    });
}

function updateStack(context) {
    const CF = new AWS.CloudFormation({ apiVersion: '2010-05-15' });

    const stack = context.stack;
    const formationFile = stack.configuration;
    const existingStack = stack.stack;
    const stackName = stack.name;

    const stackCF = fsx.readJSONFileSync(formationFile);
    const parameters = [
        {
            ParameterKey: "aaStage",
            ParameterValue: context.project.stage
        },
        {
            ParameterKey: "aaProjectName",
            ParameterValue: context.project.name
        },
        {
            ParameterKey: "aaRegion",
            ParameterValue: context.project.region
        }
    ];

    let promise;

    console.log('\nUpdating CF stack');

    // Depending on whether there is a stack or not, either update one or
    // create a new stack instance
    if (existingStack) {
        promise = new Promise(function(resolve, reject) {
            process.stdout.write('\tFound existing stack, updating');

            CF.updateStack({
                StackName: existingStack.StackId,
                TemplateBody: JSON.stringify(stackCF),
                Parameters: parameters,
                Capabilities: [
                    'CAPABILITY_IAM'
                ]
            }, function(err, result) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve({
                    id: result.StackId,
                    pendingStates: [
                        'UPDATE_IN_PROGRESS',
                        'UPDATE_ROLLBACK_IN_PROGRESS',
                        'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS',
                        'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'
                    ],
                    acceptedStates: ['UPDATE_COMPLETE']
                });
            });
        });
    } else {
        promise = new Promise(function(resolve, reject) {
            process.stdout.write('\tCreating a new stack');

            CF.createStack({
                StackName: stackName,
                TemplateBody: JSON.stringify(stackCF),
                Parameters: parameters,
                Capabilities: [
                    'CAPABILITY_IAM'
                ],
                OnFailure: 'ROLLBACK'
            }, function(err, result) {
                if (err) {
                    console.log(' ✖'.red);
                    return reject(err);
                }

                console.log(' ✔'.green);
                resolve({
                    id: result.StackId,
                    pendingStates: [
                        'CREATE_IN_PROGRESS'
                    ],
                    acceptedStates: ['CREATE_COMPLETE']
                });
            });
        });
    }

    // Wait until stack returns to an acceptedState (or until it is in an
    // unacceptable state)
    return promise.then(function(state) {
        return new Promise(function(resolve, reject) {
            process.stdout.write('\tWaiting for stack to transition to state: ' + state.acceptedStates);

            const interval = setInterval(function() {
                CF.describeStacks({
                    StackName: state.id
                }, function(err, result) {
                    if (err) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        return reject(err);
                    }

                    const stacks = result.Stacks;
                    if (!stacks || stacks.length == 0) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        return reject(new Error('Stacks dissapeared'));
                    }

                    const currentState = stacks[0].StackStatus;
                    if (state.acceptedStates.indexOf(currentState) !== -1) {
                        clearInterval(interval);
                        console.log(' ✔'.green);

                        const newCtx = _.assign({}, context);
                        newCtx.stack.id = state.id;
                        resolve(newCtx);
                    } else if (state.pendingStates.indexOf(currentState) === -1) {
                        clearInterval(interval);
                        console.log(' ✖'.red);

                        reject(new Error('Stack changed to an unacceptable state'));
                    }
                });
            }, 3000);
        });
    });
}

//
//  Step for updating the CF based stack, including uploading all
//  assets to S3 (Lambdas + configuration files)
//
//  Once completed, the stack is stable and updated
//
module.exports = function(context) {
    return uploadAssets(context).then(updateStack);
};
<|endoftext|>


<commit_before>//main.js
/*var flights = [[{dest: "Poland", price: "242", carrier: "Ryan Air"},
                    {dest: "Poland", price: "60", carrier: "British Airways"},
                    {dest: "Poland", price: "260", carrier: "Joe's Hardware"}],
            [ {dest: "Bristol", price: "160", carrier: "British Airways"},
                    {dest: "Bristol", price: "20", carrier: "Joe's Hardware"}]];
*/
var flights;
var ready = false;
//get flight data
$.get( "/top_flights", function( data ) {
    flights = JSON.parse(data);
    populate_data();
});
$(document).ready( function () {

    $('#chooser a').click(function (e) {
	e.preventDefault();
	$(this).tab('show');
    if(!$("#tab-main").hasClass("active")){ $("#tab-main").removeClass("hide");}
        else { $("#tab-main").addClass("hide"); }
    if(!$("#tab-friends").hasClass("active")){ $("#tab-friends").removeClass("hide");}
        else { $("#tab-friends").addClass("hide"); }
    });

    $(".btn-modal").on("click", function () {
        //set modal to loading!
        if(ready) {
            reset_modal();
            $('#myModal').modal('toggle');
            //populate the modal here!!!
            populate_modal($(this).attr("location"));
        }
    });

    $(".friendlist tr").on("click", function() {
        $(this).toggleClass("selected");
        var q = $(this).children(":last").children(".friend-location").text();
        $(".friend-location").each(function () {
            if($(this).text().toLowerCase().indexOf(q.toLowerCase()) != -1) {
                $(this).parent().parent().show();
            } else {
                $(this).parent().parent().hide();
            }
        });
        console.log(q);
    });
    $('#search').on("keyup", function () {
        var q = $('#search').val();
        $(".friend-name").each(function () {
            if ($(this).text().toLowerCase().indexOf(q.toLowerCase()) != -1) {
                $(this).parent().parent().show();
            }
            else {
                $(this).parent().parent().hide();
            }
        });
    });
});
function reset_modal() {
    //resets the modal to a loading screen
    $("#modalTitle").text("Loading...");
    $("#flight-data tr").remove();
    $("#flight-data").html("
Date	Agent	Fly Now!
");
}
function populate_modal(city_id) {
    //this gets all relevant data for the modal
    var data = flights[city_id];
    $("#modalTitle").text(data.name[0]+", "+data.name[1]+", "+data.name[2]);
    $("#skyscanner-url").attr('href', flights[city_id]['url']);
    $("#flight-data tr:last").after(prepare_flight(data.cheapest_quote));
    data.quotes.forEach(function (entry) {
        $("#flight-data tr:last").after(prepare_flight(entry));
    });
    $(".date-tooltip").tooltip();
}
function populate_data() {
    ready = true;
    $("#Loading-Content").addClass("hidden");
    $("#first-location h1").html(flights[0].name[1] + "
£"+flights[0].cheapest_quote.MinPrice);
    $("#first-location p").html("Go and see "+flights[0].friends[0].name+", "+flights[0].friends[1].name+" and "+(flights[0].friends.length - 2)+" others in "+flights[0].name[1]+", "+flights[0].name[2]);
    $("#first-location button").attr("location", 0);
    $("#first-location").removeClass("hidden");
    $("#second-location h1").html(flights[1].name[1] + "
£"+flights[1].cheapest_quote.MinPrice);
    $("#second-location p").html("You have "+flights[1].friends.length+" friends near "+flights[1].name[1]+", including "+flights[1].friends[0].name+"");
    $("#second-location button").attr("location", 1);
    $("#second-location").removeClass("hidden");
    $("#third-location h1").html(flights[2].name[1] + "
£"+flights[2].cheapest_quote.MinPrice);
    $("#third-location p").html("Flights available to go and see "+flights[2].friends[0].name+" and "+(flights[2].friends.length -1)+" others in "+flights[2].name[1]);
    $("#third-location button").attr("location", 2);
    $("#third-location").removeClass("hidden");
}
function prepare_flight(dest) {
    var date = moment(dest.InboundLeg.DepartureDate).format('ll');
    var rel_date = moment(dest.InboundLeg.DepartureDate).fromNow();
    //add a 'time until' event using .fromNow() as a tooltip
    var r = "
";
    r += "

"+date+"	";
    r += ""+dest.InboundLeg.Carrier+"	";
    r += "£"+dest.MinPrice+"	";
    r += "
";
    return r;
}
<commit_msg>add deselect code<commit_after>//main.js
/*var flights = [[{dest: "Poland", price: "242", carrier: "Ryan Air"},
                    {dest: "Poland", price: "60", carrier: "British Airways"},
                    {dest: "Poland", price: "260", carrier: "Joe's Hardware"}],
            [ {dest: "Bristol", price: "160", carrier: "British Airways"},
                    {dest: "Bristol", price: "20", carrier: "Joe's Hardware"}]];
*/
var flights;
var ready = false;
//get flight data
$.get( "/top_flights", function( data ) {
    flights = JSON.parse(data);
    populate_data();
});
$(document).ready( function () {

    $('#chooser a').click(function (e) {
	e.preventDefault();
	$(this).tab('show');
    if(!$("#tab-main").hasClass("active")){ $("#tab-main").removeClass("hide");}
        else { $("#tab-main").addClass("hide"); }
    if(!$("#tab-friends").hasClass("active")){ $("#tab-friends").removeClass("hide");}
        else { $("#tab-friends").addClass("hide"); }
    });

    $(".btn-modal").on("click", function () {
        //set modal to loading!
        if(ready) {
            reset_modal();
            $('#myModal').modal('toggle');
            //populate the modal here!!!
            populate_modal($(this).attr("location"));
        }
    });

    $(".friendlist tr").on("click", function() {
        $(this).toggleClass("selected");
        var any_selected = false;
        var q = $(this).children(":last").children(".friend-location").text();
        $(".friend-location").each(function () {
            if($(this).hasClass("selected")) {any_selected = true; }
            if($(this).text().toLowerCase().indexOf(q.toLowerCase()) != -1) {
                $(this).parent().parent().show();
            } else {
                $(this).parent().parent().hide();
            }
        });
        if(any_selected) {
            $(".friend-location").each(function () {
                $(this).parent().parent().show();
            });
    });
    $('#search').on("keyup", function () {
        var q = $('#search').val();
        $(".friend-name").each(function () {
            if ($(this).text().toLowerCase().indexOf(q.toLowerCase()) != -1) {
                $(this).parent().parent().show();
            }
            else {
                $(this).parent().parent().hide();
            }
        });
    });
});
function reset_modal() {
    //resets the modal to a loading screen
    $("#modalTitle").text("Loading...");
    $("#flight-data tr").remove();
    $("#flight-data").html("
Date	Agent	Fly Now!
");
}
function populate_modal(city_id) {
    //this gets all relevant data for the modal
    var data = flights[city_id];
    $("#modalTitle").text(data.name[0]+", "+data.name[1]+", "+data.name[2]);
    $("#skyscanner-url").attr('href', flights[city_id]['url']);
    $("#flight-data tr:last").after(prepare_flight(data.cheapest_quote));
    data.quotes.forEach(function (entry) {
        $("#flight-data tr:last").after(prepare_flight(entry));
    });
    $(".date-tooltip").tooltip();
}
function populate_data() {
    ready = true;
    $("#Loading-Content").addClass("hidden");
    $("#first-location h1").html(flights[0].name[1] + "
£"+flights[0].cheapest_quote.MinPrice);
    $("#first-location p").html("Go and see "+flights[0].friends[0].name+", "+flights[0].friends[1].name+" and "+(flights[0].friends.length - 2)+" others in "+flights[0].name[1]+", "+flights[0].name[2]);
    $("#first-location button").attr("location", 0);
    $("#first-location").removeClass("hidden");
    $("#second-location h1").html(flights[1].name[1] + "
£"+flights[1].cheapest_quote.MinPrice);
    $("#second-location p").html("You have "+flights[1].friends.length+" friends near "+flights[1].name[1]+", including "+flights[1].friends[0].name+"");
    $("#second-location button").attr("location", 1);
    $("#second-location").removeClass("hidden");
    $("#third-location h1").html(flights[2].name[1] + "
£"+flights[2].cheapest_quote.MinPrice);
    $("#third-location p").html("Flights available to go and see "+flights[2].friends[0].name+" and "+(flights[2].friends.length -1)+" others in "+flights[2].name[1]);
    $("#third-location button").attr("location", 2);
    $("#third-location").removeClass("hidden");
}
function prepare_flight(dest) {
    var date = moment(dest.InboundLeg.DepartureDate).format('ll');
    var rel_date = moment(dest.InboundLeg.DepartureDate).fromNow();
    //add a 'time until' event using .fromNow() as a tooltip
    var r = "
";
    r += "

"+date+"	";
    r += ""+dest.InboundLeg.Carrier+"	";
    r += "£"+dest.MinPrice+"	";
    r += "
";
    return r;
}
<|endoftext|>


<commit_before>var gulp   = require('gulp')
var babel  = require('gulp-babel')
var concat = require('gulp-concat')
var rjs    = require('requirejs')
var fs     = require('fs')
var packg  = require('./package.json')

gulp.task('babel', function () {
  return gulp.src('src/**/*')
    .pipe(babel({ modules: 'ignore' }))
    .pipe(gulp.dest('build/babel/'))
})

gulp.task('rjs', [ 'babel' ], function (done) {
  var bower = '../../bower_components/'
  var npm = '../../node_modules/'
  packg.builtAt = Date.now()
  var packgString = JSON.stringify(packg, null, 2)
  delete packg.builtAt
  rjs.optimize({
    baseUrl: 'build/babel/',
    name: 'extplug/boot',
    include: [
      'extplug/ExtPlug',
      'extplug/Module'
    ],
    paths: {
      // plug-modules defines, these are defined at runtime
      // so the r.js optimizer can't find them
      plug: 'empty:',
      backbone: 'empty:',
      jquery: 'empty:',
      underscore: 'empty:',
      meld: bower + 'meld/meld',
      sistyl: bower + 'sistyl/lib/sistyl',
      'plug-modules': npm + 'plug-modules/plug-modules'
    },
    rawText: {
      'extplug/package': 'define(' + packgString + ')'
    },
    optimize: 'none',
    out: function (text) {
      fs.writeFile('build/build.rjs.js', text, done)
    }
  })
})

gulp.task('build', [ 'rjs' ], function () {
  return gulp.src([ 'build/build.rjs.js'
                  , 'build/babel/plugins/*'
                  , 'build/babel/extplug/plugdj.user.js' ])
    .pipe(concat('build.full.js'))
    .pipe(gulp.dest('build/'))
})
<commit_msg>output Babel-transpiled files in lib/ instead of build/babel/<commit_after>var gulp   = require('gulp')
var babel  = require('gulp-babel')
var concat = require('gulp-concat')
var rjs    = require('requirejs')
var fs     = require('fs')
var packg  = require('./package.json')

gulp.task('babel', function () {
  return gulp.src('src/**/*')
    .pipe(babel({ modules: 'ignore' }))
    .pipe(gulp.dest('lib/'))
})

gulp.task('rjs', [ 'babel' ], function (done) {
  var bower = '../bower_components/'
  var npm = '../node_modules/'
  packg.builtAt = Date.now()
  var packgString = JSON.stringify(packg, null, 2)
  delete packg.builtAt
  rjs.optimize({
    baseUrl: 'lib/',
    name: 'extplug/boot',
    include: [
      'extplug/ExtPlug',
      'extplug/Module'
    ],
    paths: {
      // plug-modules defines, these are defined at runtime
      // so the r.js optimizer can't find them
      plug: 'empty:',
      backbone: 'empty:',
      jquery: 'empty:',
      underscore: 'empty:',
      meld: bower + 'meld/meld',
      sistyl: bower + 'sistyl/lib/sistyl',
      'plug-modules': npm + 'plug-modules/plug-modules'
    },
    rawText: {
      'extplug/package': 'define(' + packgString + ')'
    },
    optimize: 'none',
    out: function (text) {
      fs.writeFile('build/build.rjs.js', text, done)
    }
  })
})

gulp.task('build', [ 'rjs' ], function () {
  return gulp.src([ 'build/build.rjs.js'
                  , 'lib/plugins/*'
                  , 'lib/extplug/plugdj.user.js' ])
    .pipe(concat('build.full.js'))
    .pipe(gulp.dest('build/'))
})
<|endoftext|>


<commit_before>import React from 'react';
import {mount} from 'enzyme';

import {cloneRepository, buildRepository} from '../helpers';
import {expectRelayQuery} from '../../lib/relay-network-layer-manager';
import {issueishDetailContainerProps} from '../fixtures/props/issueish-pane-props';
import {createPullRequestDetailResult} from '../fixtures/factories/pull-request-result';
import GithubLoginModel from '../../lib/models/github-login-model';
import {InMemoryStrategy, UNAUTHENTICATED} from '../../lib/shared/keytar-strategy';
import IssueishDetailContainer from '../../lib/containers/issueish-detail-container';

describe('IssueishDetailContainer', function() {
  let loginModel, repository;

  beforeEach(async function() {
    loginModel = new GithubLoginModel(InMemoryStrategy);

    const workDir = await cloneRepository();
    repository = await buildRepository(workDir);
  });

  function useResult() {
    return expectRelayQuery({
      name: 'issueishDetailContainerQuery',
      variables: {
        repoOwner: 'owner',
        repoName: 'repo',
        issueishNumber: 1,
        timelineCount: 100,
        timelineCursor: null,
        commitCount: 100,
        commitCursor: null,
      },
    }, {
      repository: {
        id: 'repository0',
        name: 'repo',
        owner: {
          __typename: 'User',
          login: 'owner',
          id: 'user0',
        },
        issueish: createPullRequestDetailResult(),
      },
    });
  }

  function buildApp(overrideProps = {}) {
    return <IssueishDetailContainer {...issueishDetailContainerProps({loginModel, repository, ...overrideProps})} />;
  }

  it('renders a spinner while the token is being fetched', function() {
    const wrapper = mount(buildApp());
    assert.isTrue(wrapper.find('LoadingView').exists());
  });

  it('renders a login prompt if the user is unauthenticated', async function() {
    const wrapper = mount(buildApp());
    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
  });

  it("renders a login prompt if the user's token has insufficient scopes", async function() {
    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(['not-enough']);

    const wrapper = mount(buildApp());
    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
  });

  it('re-renders on login', async function() {
    useResult();
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());

    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
    wrapper.find('GithubLoginView').prop('onLogin')('4321');

    await assert.async.isTrue(wrapper.update().find('ReactRelayQueryRenderer').exists());
  });

  it('renders a spinner while the GraphQL query is being performed', async function() {
    useResult();

    loginModel.setToken('https://api.github.com', '1234');
    sinon.spy(loginModel, 'getToken');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    await loginModel.getToken.returnValues[0];

    assert.isTrue(wrapper.update().find('LoadingView').exists());
  });

  it('renders an error view if the GraphQL query fails', async function() {
    const {reject, promise} = useResult();

    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    const e = new Error('wat');
    e.rawStack = e.stack;
    reject(e);
    await promise.catch(() => {});

    await assert.async.isTrue(wrapper.update().find('QueryErrorView').exists());
    const ev = wrapper.find('QueryErrorView');
    assert.strictEqual(ev.prop('error'), e);

    assert.strictEqual(await loginModel.getToken('https://api.github.com'), '1234');
    await ev.prop('logout')();
    assert.strictEqual(await loginModel.getToken('https://api.github.com'), UNAUTHENTICATED);
  });

  it('passes GraphQL query results to an IssueishDetailController', async function() {
    const {resolve} = useResult();
    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    resolve();

    await assert.async.isTrue(wrapper.update().find('BareIssueishDetailController').exists());

    const controller = wrapper.find('BareIssueishDetailController');
    assert.isDefined(controller.prop('repository'));
    assert.strictEqual(controller.prop('issueishNumber'), 1);
  });
});
<commit_msg>fix `IssueishDetailContainer` tests<commit_after>import React from 'react';
import {mount} from 'enzyme';

import {cloneRepository, buildRepository} from '../helpers';
import {expectRelayQuery} from '../../lib/relay-network-layer-manager';
import {issueishDetailContainerProps} from '../fixtures/props/issueish-pane-props';
import {createPullRequestDetailResult} from '../fixtures/factories/pull-request-result';
import GithubLoginModel from '../../lib/models/github-login-model';
import {InMemoryStrategy, UNAUTHENTICATED} from '../../lib/shared/keytar-strategy';
import IssueishDetailContainer from '../../lib/containers/issueish-detail-container';

describe('IssueishDetailContainer', function() {
  let loginModel, repository;

  beforeEach(async function() {
    loginModel = new GithubLoginModel(InMemoryStrategy);

    const workDir = await cloneRepository();
    repository = await buildRepository(workDir);
  });

  function useResult() {
    return expectRelayQuery({
      name: 'issueishDetailContainerQuery',
      variables: {
        repoOwner: 'owner',
        repoName: 'repo',
        issueishNumber: 1,
        timelineCount: 100,
        timelineCursor: null,
        commitCount: 100,
        commitCursor: null,
        commentCount: 100,
        commentCursor: null,
      },
    }, {
      repository: {
        id: 'repository0',
        name: 'repo',
        owner: {
          __typename: 'User',
          login: 'owner',
          id: 'user0',
        },
        issueish: createPullRequestDetailResult(),
      },
    });
  }

  function buildApp(overrideProps = {}) {
    return <IssueishDetailContainer {...issueishDetailContainerProps({loginModel, repository, ...overrideProps})} />;
  }

  it('renders a spinner while the token is being fetched', function() {
    const wrapper = mount(buildApp());
    assert.isTrue(wrapper.find('LoadingView').exists());
  });

  it('renders a login prompt if the user is unauthenticated', async function() {
    const wrapper = mount(buildApp());
    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
  });

  it("renders a login prompt if the user's token has insufficient scopes", async function() {
    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(['not-enough']);

    const wrapper = mount(buildApp());
    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
  });

  it('re-renders on login', async function() {
    useResult();
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());

    await assert.async.isTrue(wrapper.update().find('GithubLoginView').exists());
    wrapper.find('GithubLoginView').prop('onLogin')('4321');

    await assert.async.isTrue(wrapper.update().find('ReactRelayQueryRenderer').exists());
  });

  it('renders a spinner while the GraphQL query is being performed', async function() {
    useResult();

    loginModel.setToken('https://api.github.com', '1234');
    sinon.spy(loginModel, 'getToken');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    await loginModel.getToken.returnValues[0];

    assert.isTrue(wrapper.update().find('LoadingView').exists());
  });

  it('renders an error view if the GraphQL query fails', async function() {
    const {reject, promise} = useResult();

    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    const e = new Error('wat');
    e.rawStack = e.stack;
    reject(e);
    await promise.catch(() => {});

    await assert.async.isTrue(wrapper.update().find('QueryErrorView').exists());
    const ev = wrapper.find('QueryErrorView');
    assert.strictEqual(ev.prop('error'), e);

    assert.strictEqual(await loginModel.getToken('https://api.github.com'), '1234');
    await ev.prop('logout')();
    assert.strictEqual(await loginModel.getToken('https://api.github.com'), UNAUTHENTICATED);
  });

  it('passes GraphQL query results to an IssueishDetailController', async function() {
    const {resolve} = useResult();
    loginModel.setToken('https://api.github.com', '1234');
    sinon.stub(loginModel, 'getScopes').resolves(GithubLoginModel.REQUIRED_SCOPES);

    const wrapper = mount(buildApp());
    resolve();

    await assert.async.isTrue(wrapper.update().find('BareIssueishDetailController').exists());

    const controller = wrapper.find('BareIssueishDetailController');
    assert.isDefined(controller.prop('repository'));
    assert.strictEqual(controller.prop('issueishNumber'), 1);
  });
});
<|endoftext|>


<commit_before>
/*
 * Called as the user scrolls/zooms around to aniplate hrefs of the
 * view tab and various other links
 */
function updatelinks(lon,lat,zoom,layers,minlon,minlat,maxlon,maxlat,objtype,objid) {
  var decimals = Math.pow(10, Math.floor(zoom/3));
  var node;

  lat = Math.round(lat * decimals) / decimals;
  lon = Math.round(lon * decimals) / decimals;

  node = $("permalinkanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    if (objtype && objid) {
      args[objtype] = objid;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("viewanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("exportanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("editanchor");
  if (node) {
    if (zoom >= 13) {
      var args = new Object();
      args.lat = lat;
      args.lon = lon;
      args.zoom = zoom;
      if (objtype && objid) {
        args[objtype] = objid;
      }
      node.href = setArgs("/edit", args);
      node.title = i18n("javascripts.site.edit_tooltip");
      node.removeClassName("disabled");
    } else {
      node.href = 'javascript:alert(i18n("javascripts.site.edit_zoom_alert"));';
      node.title = i18n("javascripts.site.edit_disabled_tooltip");
      node.addClassName("disabled");
    }
  }

  node = $("historyanchor");
  if (node) {
    if (zoom >= 11) {
      var args = new Object();
      //set bbox param from 'extents' object
      if (typeof minlon == "number" &&
	  typeof minlat == "number" &&
	  typeof maxlon == "number" &&
	  typeof maxlat == "number") {

        minlon = Math.round(minlon * decimals) / decimals;
        minlat = Math.round(minlat * decimals) / decimals;
        maxlon = Math.round(maxlon * decimals) / decimals;
        maxlat = Math.round(maxlat * decimals) / decimals;
        args.bbox = minlon + "," + minlat + "," + maxlon + "," + maxlat;
      }

      node.href = setArgs("/history", args);
      node.title = i18n("javascripts.site.history_tooltip");
      node.removeClassName("disabled");
    } else {
      node.href = 'javascript:alert(i18n("javascripts.site.history_zoom_alert"));';
      node.title = i18n("javascripts.site.history_disabled_tooltip");
      node.addClassName("disabled");
    }
  }

  node = $("shortlinkanchor");
  if (node) {
    var args = getArgs(node.href);
    var code = makeShortCode(lat, lon, zoom);
    var prefix = shortlinkPrefix();

    // Add ?{node,way,relation}=id to the arguments
    if (objtype && objid) {
      args[objtype] = objid;
    }

    // This is a hack to omit the default mapnik layer (B000FTF) from
    // the shortlink. B000FTFT is then the "Object" layer which we get
    // on /?{node,way,relation}=id
    if (layers && (layers != "B000FTF") && (layers != "B000FTFT")) {
      args["layers"] = layers;
    }
    else {
      delete args["layers"];
    }

    // Here we're assuming that all parameters but ?layers= and
    // ?{node,way,relation}= can be safely omitted from the shortlink
    // which encodes lat/lon/zoom. If new URL parameters are added to
    // the main slippy map this needs to be changed.
    if (args["layers"] || args[objtype]) {
      node.href = setArgs(prefix + "/go/" + code, args);
    } else {
      node.href = prefix + "/go/" + code;
    }
  }
}

/*
 * Get the URL prefix to use for a short link
 */
function shortlinkPrefix() {
  if (window.location.hostname.match(/^www\.openstreetmap\.org/i)) {
    return "http://osm.org";
  } else {
    return "";
  }
}

/*
 * Called to get the arguments from a URL as a hash.
 */
function getArgs(url) {
  var args = new Object();
  var querystart = url.indexOf("?");

  if (querystart >= 0) {
     var querystring = url.substring(querystart + 1);
     var queryitems = querystring.split("&");

     for (var i = 0; i < queryitems.length; i++) {
        if (match = queryitems[i].match(/^(.*)=(.*)$/)) {
           args[unescape(match[1])] = unescape(match[2]);
        } else {
           args[unescape(queryitems[i])] = null
        }
     }
  }

  return args;
}

/*
 * Called to set the arguments on a URL from the given hash.
 */
function setArgs(url, args) {
   var queryitems = new Array();

   for (arg in args)
   {
      if (args[arg] == null) {
         queryitems.push(escape(arg));
      } else {
         queryitems.push(escape(arg) + "=" + escape(args[arg]));
      }
   }

   return url.replace(/\?.*$/, "") + "?" + queryitems.join("&");
}

/*
 * Called to get a CSS property for an element.
 */
function getStyle(el, property) {
  var style;

  if (el.currentStyle) {
    style = el.currentStyle[property];
  } else if( window.getComputedStyle ) {
    style = document.defaultView.getComputedStyle(el,null).getPropertyValue(property);
  } else {
    style = el.style[property];
  }

  return style;
}

/*
 * Called to interpolate JavaScript variables in strings using a
 * similar syntax to rails I18n string interpolation - the only
 * difference is that [[foo]] is the placeholder syntax instead
 * of {{foo}} which allows the same string to be processed by both
 * rails and then later by javascript.
 */
function i18n(string, keys) {
  string = i18n_strings[string] || string

  for (var key in keys) {
    var re_key = '\\[\\[' + key + '\\]\\]';
    var re = new RegExp(re_key, "g");

    string = string.replace(re, keys[key]);
  }

  return string;
}

function makeShortCode(lat, lon, zoom) {
    char_array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
    var x = Math.round((lon + 180.0) * ((1 << 30) / 90.0));
    var y = Math.round((lat +  90.0) * ((1 << 30) / 45.0));
    // hack around the fact that JS apparently only allows 53-bit integers?!?
    // note that, although this reduces the accuracy of the process, it's fine for
    // z18 so we don't need to care for now.
    var c1 = 0, c2 = 0;
    for (var i = 31; i > 16; --i) {
	c1 = (c1 << 1) | ((x >> i) & 1);
	c1 = (c1 << 1) | ((y >> i) & 1);
    }
    for (var i = 16; i > 1; --i) {
	c2 = (c2 << 1) | ((x >> i) & 1);
	c2 = (c2 << 1) | ((y >> i) & 1);
    }
    var str = "";
    for (var i = 0; i < Math.ceil((zoom + 8) / 3.0) && i < 5; ++i) {
	digit = (c1 >> (24 - 6 * i)) & 0x3f;
	str += char_array.charAt(digit);
    }
    for (var i = 5; i < Math.ceil((zoom + 8) / 3.0); ++i) {
	digit = (c2 >> (24 - 6 * (i - 5))) & 0x3f;
	str += char_array.charAt(digit);
    }
    for (var i = 0; i < ((zoom + 8) % 3); ++i) {
	str += "-";
    }
    return str;
}
<commit_msg>Fix short links to drop the layers param for the default layer<commit_after>
/*
 * Called as the user scrolls/zooms around to aniplate hrefs of the
 * view tab and various other links
 */
function updatelinks(lon,lat,zoom,layers,minlon,minlat,maxlon,maxlat,objtype,objid) {
  var decimals = Math.pow(10, Math.floor(zoom/3));
  var node;

  lat = Math.round(lat * decimals) / decimals;
  lon = Math.round(lon * decimals) / decimals;

  node = $("permalinkanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    if (objtype && objid) {
      args[objtype] = objid;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("viewanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("exportanchor");
  if (node) {
    var args = getArgs(node.href);
    args["lat"] = lat;
    args["lon"] = lon;
    args["zoom"] = zoom;
    if (layers) {
      args["layers"] = layers;
    }
    node.href = setArgs(node.href, args);
  }

  node = $("editanchor");
  if (node) {
    if (zoom >= 13) {
      var args = new Object();
      args.lat = lat;
      args.lon = lon;
      args.zoom = zoom;
      if (objtype && objid) {
        args[objtype] = objid;
      }
      node.href = setArgs("/edit", args);
      node.title = i18n("javascripts.site.edit_tooltip");
      node.removeClassName("disabled");
    } else {
      node.href = 'javascript:alert(i18n("javascripts.site.edit_zoom_alert"));';
      node.title = i18n("javascripts.site.edit_disabled_tooltip");
      node.addClassName("disabled");
    }
  }

  node = $("historyanchor");
  if (node) {
    if (zoom >= 11) {
      var args = new Object();
      //set bbox param from 'extents' object
      if (typeof minlon == "number" &&
	  typeof minlat == "number" &&
	  typeof maxlon == "number" &&
	  typeof maxlat == "number") {

        minlon = Math.round(minlon * decimals) / decimals;
        minlat = Math.round(minlat * decimals) / decimals;
        maxlon = Math.round(maxlon * decimals) / decimals;
        maxlat = Math.round(maxlat * decimals) / decimals;
        args.bbox = minlon + "," + minlat + "," + maxlon + "," + maxlat;
      }

      node.href = setArgs("/history", args);
      node.title = i18n("javascripts.site.history_tooltip");
      node.removeClassName("disabled");
    } else {
      node.href = 'javascript:alert(i18n("javascripts.site.history_zoom_alert"));';
      node.title = i18n("javascripts.site.history_disabled_tooltip");
      node.addClassName("disabled");
    }
  }

  node = $("shortlinkanchor");
  if (node) {
    var args = getArgs(node.href);
    var code = makeShortCode(lat, lon, zoom);
    var prefix = shortlinkPrefix();

    // Add ?{node,way,relation}=id to the arguments
    if (objtype && objid) {
      args[objtype] = objid;
    }

    // This is a hack to omit the default mapnik layer from the shortlink.
    if (layers && layers != "M") {
      args["layers"] = layers;
    }
    else {
      delete args["layers"];
    }

    // Here we're assuming that all parameters but ?layers= and
    // ?{node,way,relation}= can be safely omitted from the shortlink
    // which encodes lat/lon/zoom. If new URL parameters are added to
    // the main slippy map this needs to be changed.
    if (args["layers"] || args[objtype]) {
      node.href = setArgs(prefix + "/go/" + code, args);
    } else {
      node.href = prefix + "/go/" + code;
    }
  }
}

/*
 * Get the URL prefix to use for a short link
 */
function shortlinkPrefix() {
  if (window.location.hostname.match(/^www\.openstreetmap\.org/i)) {
    return "http://osm.org";
  } else {
    return "";
  }
}

/*
 * Called to get the arguments from a URL as a hash.
 */
function getArgs(url) {
  var args = new Object();
  var querystart = url.indexOf("?");

  if (querystart >= 0) {
     var querystring = url.substring(querystart + 1);
     var queryitems = querystring.split("&");

     for (var i = 0; i < queryitems.length; i++) {
        if (match = queryitems[i].match(/^(.*)=(.*)$/)) {
           args[unescape(match[1])] = unescape(match[2]);
        } else {
           args[unescape(queryitems[i])] = null
        }
     }
  }

  return args;
}

/*
 * Called to set the arguments on a URL from the given hash.
 */
function setArgs(url, args) {
   var queryitems = new Array();

   for (arg in args)
   {
      if (args[arg] == null) {
         queryitems.push(escape(arg));
      } else {
         queryitems.push(escape(arg) + "=" + escape(args[arg]));
      }
   }

   return url.replace(/\?.*$/, "") + "?" + queryitems.join("&");
}

/*
 * Called to get a CSS property for an element.
 */
function getStyle(el, property) {
  var style;

  if (el.currentStyle) {
    style = el.currentStyle[property];
  } else if( window.getComputedStyle ) {
    style = document.defaultView.getComputedStyle(el,null).getPropertyValue(property);
  } else {
    style = el.style[property];
  }

  return style;
}

/*
 * Called to interpolate JavaScript variables in strings using a
 * similar syntax to rails I18n string interpolation - the only
 * difference is that [[foo]] is the placeholder syntax instead
 * of {{foo}} which allows the same string to be processed by both
 * rails and then later by javascript.
 */
function i18n(string, keys) {
  string = i18n_strings[string] || string

  for (var key in keys) {
    var re_key = '\\[\\[' + key + '\\]\\]';
    var re = new RegExp(re_key, "g");

    string = string.replace(re, keys[key]);
  }

  return string;
}

function makeShortCode(lat, lon, zoom) {
    char_array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
    var x = Math.round((lon + 180.0) * ((1 << 30) / 90.0));
    var y = Math.round((lat +  90.0) * ((1 << 30) / 45.0));
    // hack around the fact that JS apparently only allows 53-bit integers?!?
    // note that, although this reduces the accuracy of the process, it's fine for
    // z18 so we don't need to care for now.
    var c1 = 0, c2 = 0;
    for (var i = 31; i > 16; --i) {
	c1 = (c1 << 1) | ((x >> i) & 1);
	c1 = (c1 << 1) | ((y >> i) & 1);
    }
    for (var i = 16; i > 1; --i) {
	c2 = (c2 << 1) | ((x >> i) & 1);
	c2 = (c2 << 1) | ((y >> i) & 1);
    }
    var str = "";
    for (var i = 0; i < Math.ceil((zoom + 8) / 3.0) && i < 5; ++i) {
	digit = (c1 >> (24 - 6 * i)) & 0x3f;
	str += char_array.charAt(digit);
    }
    for (var i = 5; i < Math.ceil((zoom + 8) / 3.0); ++i) {
	digit = (c2 >> (24 - 6 * (i - 5))) & 0x3f;
	str += char_array.charAt(digit);
    }
    for (var i = 0; i < ((zoom + 8) % 3); ++i) {
	str += "-";
    }
    return str;
}
<|endoftext|>


<commit_before>'use strict';
var util = require('util'),
    path = require('path'),
    chalk = require('chalk'),
    sh = require('execSync'),
    generators = require('yeoman-generator'),
    file = require('yeoman-generator').file,
    yosay = require('yosay');

module.exports = generators.Base.extend({
  contructor: function() {
    var defaultSettings = {
      vampdHome: process.env.PWD,
      machineId: 'example',
      gitHost: 'github.com',
      gitURI: 'https://github.com/drupal/drupal.git',
      gitRevision: '7.33',
      drupalDocroot: "",
      drupalProfile: 'standard',
      actions: '["deploy,"install"]',
      dbFile: '',
      drupalVersion: "7.x",
      drupalSiteSettings: 'sites/default/settings.php',
      drupalSiteFiles: 'site/default/files'
    }

    this.config.defaults(defaultSettings);
  },

  // Initialize by asking product name
  vampdInit: function () {
    var done = this.async(),
        prompts = [],
        locationTense = 'should';
    // Set location tense if a new role
    if (this.options['new-role']) {
      locationTense = 'does'
    }
    this.vampdHome = this.config.get('vampdHome');
    prompts.push({
      type:'string',
      name: 'vampdHome',
      default: this.config.get('vampdHome'),
      message: 'Where ' + locationTense + ' vampd live?',
    });

    this.machineId = this.config.get('machineId');
    prompts.push({
      type: 'string',
      name: 'machineId',
      message: 'What is the machine name of your site?' + chalk.red('(Required)'),
      validate: function (answer) {
        if ( answer === '' ) {
          return 'You must provide a Machine Name';
        }
        return true;
      }
    });

    prompts.push({
      type: 'confirm',
      name: 'git',
      message: 'Do you have a git repo for your site?',
      default: true,
    });

    // Bind the initial prompts
    this.prompt(prompts, function (answers) {
      this.vampdHome = answers.vampdHome;
      this.machineId = answers.machineId;
      this.git = answers.git;
      done();
    }.bind(this));
  },

  // Get the git information
  vampdGit: function () {
    var done = this.async(),
        prompts = [];

    this.gitHost = this.config.get('gitHost');
    this.gitURI = this.config.get('gitURI');
    this.gitRevision = this.config.get('gitRevision');

    if (this.git) {
      // Ask gor the githost
      prompts.push({
        type: 'string',
        name: 'gitURI',
        message: 'What is the git host?',
        default: this.config.get('gitHost')
      });
      // Ask for uri
      prompts.push({
        type: 'string',
        name: 'gitHost',
        message: 'What is the git uri?' + chalk.red('(Required)'),
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a git uri';
          }
          return true;
        }
      });
      // What revision would you like to use
      prompts.push({
        type: 'string',
        name: 'gitRevision',
        message: 'What is the revision?',
        default: 'master',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a revision, tag, branch, etc.';
          }
          return true;
        }
      });
    } else {
      prompts.push({
        type: 'confirm',
        name: 'gitDrupal',
        message: 'Do you want to pull down a clean drupal site?',
        default: true
      });
    }
    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.gitHost = answers.gitHost;
        this.gitURI = answers.gitURI;
        this.gitRevision = answers.gitRevision;
      } else {
        this.gitDrupal = answers.gitDrupal;
      }
      done();
    }.bind(this));
  },

  // If there is no git and they don't want drupal fail gracefully.
  vampdGitFail: function () {
    if ( !this.git && !this.gitDrupal ) {
      this.log('Sorry, a git project is required or you can choose to run drupal.');
      return false
    }
  },

  //Actions
  vampdActions: function () {
    var done = this.async(),
        prompts = [];

    this.log("Select an action to complete for your site.");
    this.log(chalk.yellow("[deploy]") + " -- places your code.");
    this.log(chalk.yellow("[install]") + " -- runs a clean site install on your code.");
    if (this.git) {
      this.log(chalk.yellow("[import]") + " -- brings in an existing db file");
      this.log(chalk.yellow("[update]") + " -- runs update.php.")
    }
    this.log("If you aren\'t sure what to select, go with the defaults.")
    this.log("Everything is editable later. ");

    var actionChoices = [
      {
        name: "deploy",
        checked: true
      },
      {
        name: "install",
        checked: true
      },
    ];
    if (this.git) {
      var actionChoices = [
        {
          name: "deploy",
          checked: true
        },
        {
          name: "install",
          checked: true
        },
      ]
      actionChoices.push({
        name: "import"
      },
      {
        name: "update"
      });
    }
    prompts.push({
      type: "checkbox",
      message: "Select Actions",
      name: "actions",
      choices: actionChoices,
      validate: function( answer ) {
        if ( answer.length < 1 ) {
          return "You must choose at least one topping.";
        }
        return true;
      }
    });

    this.prompt(prompts, function (answers) {
      var actionsJSON = JSON.stringify(answers.actions, null, "");
      this.actions = answers.actions;
      this.actionsJSON = actionsJSON;
      done();
    }.bind(this));
  },

  // If install is true
  vampdInstall: function () {
    var done = this.async(),
        prompts = [];
    this.drupalProfile = this.config.get('drupalProfile');

    if ( this.actions.indexOf('install') >= 0 ) {
      prompts.push({
        type: 'string',
        name: 'drupalProfile',
        message: 'What install profile would you like to use?' + chalk.red('(Required)'),
        default: 'standard',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a drupal install profile';
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      this.drupalProfile = answers.drupalProfile;
      done();
    }.bind(this));
  },

  // If import is true, load up the file
  vampdImport: function () {
    var done = this.async(),
        prompts = [];
    this.dbFile = this.config.get('dbFile');
    if ( this.actions.indexOf('import') >= 0 ) {
      prompts.push({
        type: 'string',
        name: 'dbFile',
        message: 'Where is the db file located? Example: /vagrant/sites/db_file.sql' + chalk.red('(Required)'),
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a db file location.';
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      this.dbFile = answers.dbFile;
      done();
    }.bind(this));
  },

  // Get the drupal version
  vampdDrupalVersion: function () {
    var done = this.async(),
        prompts = [];

    this.drupalVersion = this.config.get('drupalVersion');

    if ( this.git ) {
      prompts.push({
        type: "checkbox",
        message: "What major version of Drupal are you using?",
        name: "drupalVersion",
        choices: [
          {
            name: "6.x"
          },
          {
            name: "7.x",
            checked: true
          }
        ],
        validate: function( answer ) {
          if ( answer.length < 1 ) {
            return "You must choose a version.";
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalVersion = answers.drupalVersion;
      }
      done();
    }.bind(this));
  },

  // See if a Docroot is needed
  vampdDrupalDocrootInit: function () {
    var done = this.async(),
        prompts = [];
    if ( this.git ) {
      prompts.push({
        type: 'confirm',
        name: 'drupalDocrootInit',
        message: 'Are the files in a docroot besides the base? Example: "htdocs", "docroot"',
        default: false
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalDocrootInit = answers.drupalDocrootInit;
      }
      done();
    }.bind(this));
  },

  // Get the Docroot, if it is needed
  vampdDrupalDocroot: function () {
    var done = this.async(),
        prompts = [];

    this.drupalDocroot = this.config.get('drupalDocroot');

    if ( this.drupalDocrootInit ) {
      prompts.push({
        type: 'string',
        name: 'drupalDocroot',
        message: 'What is the docroot? Example: "htdocs", "docroot"' + chalk.red('(Required)'),
        default: false,
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a docroot location.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      this.drupalDocroot = answers.drupalDocroot;
      done();
    }.bind(this));
  },

  // Site files
  vampdDrupalSiteFiles: function () {
    var done = this.async(),
        prompts = [];
    this.drupalSiteFiles = this.config.get('drupalSiteFiles');
    if ( this.git ) {
      prompts.push({
        type: 'string',
        name: 'drupalSiteFiles',
        message: 'Where do your site files live, relative to the docroot?' + chalk.red('(Required)'),
        default: 'sites/default/files',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'Your files must live somewhere. Please give them a home.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalSiteFiles = answers.drupalSiteFiles;
      }
      done();
    }.bind(this));
  },

  // Site settings.php
  vampdDrupalSiteSettings: function () {
    var done = this.async(),
        prompts = [];

    this.drupalSiteSettings = this.config.get('drupalSiteSettings');

    if ( this.git ) {
      prompts.push({
        type: 'string',
        name: 'drupalSiteSettings',
        message: 'Where does your sites settings.php live, relative to docroot?' + chalk.red('(Required)'),
        default: 'sites/default/settings.php',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'Your files must live somewhere. Please give them a home.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalSiteSettings = answers.drupalSiteSettings
      }
      done();
    }.bind(this));
  },
  //
  // Pipe this to JSON
  vampdSettingsToJSON: function () {

    this.log("Thank you so much! Your site role file will generate in a few moments");
    var mID = this.machineId;
    if ( !this.options['new-role'] ) {
        sh.run('git clone https://github.com/vampd/vampd.git ' + this.vampdHome);
    }
    // Set the destination root to where we specified.
    this.destinationRoot(this.vampdHome);

    this.template('role.json', this.vampdHome + '/chef/roles/' + mID +'.json');

  },
});
<commit_msg>Added helpful messages and check for the IP address.<commit_after>'use strict';
var util = require('util'),
    path = require('path'),
    chalk = require('chalk'),
    sh = require('execSync'),
    generators = require('yeoman-generator'),
    file = require('yeoman-generator').file,
    yosay = require('yosay');

module.exports = generators.Base.extend({
  contructor: function() {
    var defaultSettings = {
      vampdHome: process.env.PWD,
      machineId: 'example',
      gitHost: 'github.com',
      gitURI: 'https://github.com/drupal/drupal.git',
      gitRevision: '7.33',
      drupalDocroot: "",
      drupalProfile: 'standard',
      actions: '["deploy,"install"]',
      dbFile: '',
      drupalVersion: "7.x",
      drupalSiteSettings: 'sites/default/settings.php',
      drupalSiteFiles: 'site/default/files',
      vagrantIP: '192.168.50.5'
    }

    this.config.defaults(defaultSettings);
  },

  // Initialize by asking product name
  vampdInit: function () {
    var done = this.async(),
        prompts = [],
        locationTense = 'should';
    // Set location tense if a new role
    if (this.options['new-role']) {
      locationTense = 'does'
    }
    this.vampdHome = this.config.get('vampdHome');
    prompts.push({
      type:'string',
      name: 'vampdHome',
      default: this.config.get('vampdHome'),
      message: 'Where ' + locationTense + ' vampd live?',
    });
    if (this.options['new-role']) {
      this.vagrantIP = this.config.get('vagrantIP');
      this.log('Your private vagrant IP can be found in your Vagrantfile');
      this.log("If you didn't touch this, just hit enter");
      prompts.push({
        type:'string',
        name: 'vagrantIP',
        default: this.config.get('vagrantIP'),
        message: 'What is the private vagrant IP?',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'Your site must have an IP';
          }
          return true;
        }
      });
    }
    // Get the Machine ID
    this.machineId = this.config.get('machineId');
    prompts.push({
      type: 'string',
      name: 'machineId',
      message: 'What is the machine name of your site?' + chalk.red('(Required)'),
      validate: function (answer) {
        if ( answer === '' ) {
          return 'You must provide a Machine Name';
        }
        return true;
      }
    });

    prompts.push({
      type: 'confirm',
      name: 'git',
      message: 'Do you have a git repo for your site?',
      default: true,
    });

    // Bind the initial prompts
    this.prompt(prompts, function (answers) {
      this.vampdHome = answers.vampdHome;
      this.machineId = answers.machineId;
      this.git = answers.git;
      done();
    }.bind(this));
  },

  // Get the git information
  vampdGit: function () {
    var done = this.async(),
        prompts = [];

    this.gitHost = this.config.get('gitHost');
    this.gitURI = this.config.get('gitURI');
    this.gitRevision = this.config.get('gitRevision');

    if (this.git) {
      // Ask gor the githost
      prompts.push({
        type: 'string',
        name: 'gitURI',
        message: 'What is the git host?',
        default: this.config.get('gitHost')
      });
      // Ask for uri
      prompts.push({
        type: 'string',
        name: 'gitHost',
        message: 'What is the git uri?' + chalk.red('(Required)'),
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a git uri';
          }
          return true;
        }
      });
      // What revision would you like to use
      prompts.push({
        type: 'string',
        name: 'gitRevision',
        message: 'What is the revision?',
        default: 'master',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a revision, tag, branch, etc.';
          }
          return true;
        }
      });
    } else {
      prompts.push({
        type: 'confirm',
        name: 'gitDrupal',
        message: 'Do you want to pull down a clean drupal site?',
        default: true
      });
    }
    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.gitHost = answers.gitHost;
        this.gitURI = answers.gitURI;
        this.gitRevision = answers.gitRevision;
      } else {
        this.gitDrupal = answers.gitDrupal;
      }
      done();
    }.bind(this));
  },

  // If there is no git and they don't want drupal fail gracefully.
  vampdGitFail: function () {
    if ( !this.git && !this.gitDrupal ) {
      this.log('Sorry, a git project is required or you can choose to run drupal.');
      return false
    }
  },

  //Actions
  vampdActions: function () {
    var done = this.async(),
        prompts = [];

    this.log("Select an action to complete for your site.");
    this.log(chalk.yellow("[deploy]") + " -- places your code.");
    this.log(chalk.yellow("[install]") + " -- runs a clean site install on your code.");
    if (this.git) {
      this.log(chalk.yellow("[import]") + " -- brings in an existing db file");
      this.log(chalk.yellow("[update]") + " -- runs update.php.")
    }
    this.log("If you aren\'t sure what to select, go with the defaults.")
    this.log("Everything is editable later. ");

    var actionChoices = [
      {
        name: "deploy",
        checked: true
      },
      {
        name: "install",
        checked: true
      },
    ];
    if (this.git) {
      var actionChoices = [
        {
          name: "deploy",
          checked: true
        },
        {
          name: "install",
          checked: true
        },
      ]
      actionChoices.push({
        name: "import"
      },
      {
        name: "update"
      });
    }
    prompts.push({
      type: "checkbox",
      message: "Select Actions",
      name: "actions",
      choices: actionChoices,
      validate: function( answer ) {
        if ( answer.length < 1 ) {
          return "You must choose at least one topping.";
        }
        return true;
      }
    });

    this.prompt(prompts, function (answers) {
      var actionsJSON = JSON.stringify(answers.actions, null, "");
      this.actions = answers.actions;
      this.actionsJSON = actionsJSON;
      done();
    }.bind(this));
  },

  // If install is true
  vampdInstall: function () {
    var done = this.async(),
        prompts = [];
    this.drupalProfile = this.config.get('drupalProfile');

    if ( this.actions.indexOf('install') >= 0 ) {
      prompts.push({
        type: 'string',
        name: 'drupalProfile',
        message: 'What install profile would you like to use?' + chalk.red('(Required)'),
        default: 'standard',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a drupal install profile';
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      this.drupalProfile = answers.drupalProfile;
      done();
    }.bind(this));
  },

  // If import is true, load up the file
  vampdImport: function () {
    var done = this.async(),
        prompts = [];
    this.dbFile = this.config.get('dbFile');
    if ( this.actions.indexOf('import') >= 0 ) {
      prompts.push({
        type: 'string',
        name: 'dbFile',
        message: 'Where is the db file located? Example: /vagrant/sites/db_file.sql' + chalk.red('(Required)'),
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a db file location.';
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      this.dbFile = answers.dbFile;
      done();
    }.bind(this));
  },

  // Get the drupal version
  vampdDrupalVersion: function () {
    var done = this.async(),
        prompts = [];

    this.drupalVersion = this.config.get('drupalVersion');

    if ( this.git ) {
      prompts.push({
        type: "checkbox",
        message: "What major version of Drupal are you using?",
        name: "drupalVersion",
        choices: [
          {
            name: "6.x"
          },
          {
            name: "7.x",
            checked: true
          }
        ],
        validate: function( answer ) {
          if ( answer.length < 1 ) {
            return "You must choose a version.";
          }
          return true;
        }
      });
    }
    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalVersion = answers.drupalVersion;
      }
      done();
    }.bind(this));
  },

  // See if a Docroot is needed
  vampdDrupalDocrootInit: function () {
    var done = this.async(),
        prompts = [];
    if ( this.git ) {
      prompts.push({
        type: 'confirm',
        name: 'drupalDocrootInit',
        message: 'Are the files in a docroot besides the base? Example: "htdocs", "docroot"',
        default: false
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalDocrootInit = answers.drupalDocrootInit;
      }
      done();
    }.bind(this));
  },

  // Get the Docroot, if it is needed
  vampdDrupalDocroot: function () {
    var done = this.async(),
        prompts = [];

    this.drupalDocroot = this.config.get('drupalDocroot');

    if ( this.drupalDocrootInit ) {
      prompts.push({
        type: 'string',
        name: 'drupalDocroot',
        message: 'What is the docroot? Example: "htdocs", "docroot"' + chalk.red('(Required)'),
        default: false,
        validate: function (answer) {
          if ( answer === '' ) {
            return 'You must provide a docroot location.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      this.drupalDocroot = answers.drupalDocroot;
      done();
    }.bind(this));
  },

  // Site files
  vampdDrupalSiteFiles: function () {
    var done = this.async(),
        prompts = [];
    this.drupalSiteFiles = this.config.get('drupalSiteFiles');
    if ( this.git ) {
      prompts.push({
        type: 'string',
        name: 'drupalSiteFiles',
        message: 'Where do your site files live, relative to the docroot?' + chalk.red('(Required)'),
        default: 'sites/default/files',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'Your files must live somewhere. Please give them a home.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalSiteFiles = answers.drupalSiteFiles;
      }
      done();
    }.bind(this));
  },

  // Site settings.php
  vampdDrupalSiteSettings: function () {
    var done = this.async(),
        prompts = [];

    this.drupalSiteSettings = this.config.get('drupalSiteSettings');

    if ( this.git ) {
      prompts.push({
        type: 'string',
        name: 'drupalSiteSettings',
        message: 'Where does your sites settings.php live, relative to docroot?' + chalk.red('(Required)'),
        default: 'sites/default/settings.php',
        validate: function (answer) {
          if ( answer === '' ) {
            return 'Your files must live somewhere. Please give them a home.';
          }
          return true;
        }
      });
    }

    this.prompt(prompts, function (answers) {
      if ( this.git ) {
        this.drupalSiteSettings = answers.drupalSiteSettings
      }
      done();
    }.bind(this));
  },
  //
  // Pipe this to JSON
  vampdSettingsToJSON: function () {

    this.log("Thank you so much! Your site role file will generate in a few moments");
    var mID = this.machineId;
    if ( !this.options['new-role'] ) {
      sh.run('git clone https://github.com/vampd/vampd.git ' + this.vampdHome);
    }
    // Set the destination root to where we specified.
    this.destinationRoot(this.vampdHome);

    this.template('role.json', this.vampdHome + '/chef/roles/' + mID +'.json');

    // Add the ip to the /etc/hosts
    sh.run('sudo echo ' + this.vagrantIP + '   ' + mID+ '.local'+ ' >> /etc/hosts');

    this.log("------");
    if ( this.options['new-role'] ) {
      this.log(chalk.green('Your new role is created and ready to use.'));
      this.log(chalk.magenta('If your site is ready to provision...'));
      this.log(chalk.cyan('vagrant provision') + ' in your working vampd directory');
      this.log('and add the following line to your ' + chalk.cyan('/etc/hosts'));
      this.log(chalk.cyan(this.vagrantIP + '   ' + mID + '.local'));
    } else {
      this.log(chalk.green('Your installation is complete'));
      this.log(chalk.green('Navigate to your project directory:'));
      this.log(chalk.cyan('cd' + this.vampdHome));
      this.log(chalk.green('and run the following command'));
      this.log(chalk.cyan('vagrant up'));
      t
    }
    this.log("------");
  },
});
<|endoftext|>