diff --git a/.gitignore b/.gitignore index 4baeb3b6..8bb2ef85 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,5 @@ test/temp/ vendor/ composer.phar *.pyc - .vimrc - +.browserData diff --git a/.travis.yml b/.travis.yml index b0142cb1..46f9fca8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,50 @@ sudo: required dist: trusty language: generic +cache: + directories: + - $HOME/.rvm + - $HOME/.nvm + - $TRAVIS_BUILD_DIR/vendor + - $TRAVIS_BUILD_DIR/node_modules addons: hosts: - example.com - local.example.com - production.example.com - www.example.com + apt: + packages: + - xvfb + - libgconf2-dev +env: + global: + # DEBUG: "nightmare:*,electron:*" + - DISPLAY: ":99.0" + matrix: + # ANSIBLE_VERSION: "2.0.2" + - ANSIBLE_VERSION: "2.1.3" + - ANSIBLE_VERSION: "2.2.0" before_install: - # node setup (for yeoman/bower) - - nvm install 0.10 - - node --version - - npm --version - - nvm --version - - npm install -g bower - - npm install # ruby setup (for capistrano) + - rvm install 1.9.3 - rvm use 1.9.3 - gem install bundler - ruby --version - rvm --version - gem --version - bundle --version + # node setup (for yeoman) + - nvm install 4.0 + - node --version + - npm --version + - nvm --version + - npm install -g yo@1.7.1 + - yo --version + - npm install # python setup (for ansible) - - sudo pip install ansible==1.9.4 + - sudo pip install urllib3 pyopenssl ndg-httpsclient pyasn1 + - sudo pip install ansible==$ANSIBLE_VERSION - python --version - pip --version - ansible --version @@ -31,14 +52,12 @@ before_install: - $PWD/bin/mock - sudo mv $PWD/temp /vagrant - ln -s /vagrant $PWD/temp - # copy our local evolution-wordpress as bower dependency - - rm -rf /vagrant/bower_components/evolution-wordpress - - cp -RP $PWD /vagrant/bower_components/evolution-wordpress/ - # invoke local provision (as default user with elevated permissions) + # copy our local evolution-wordpress as npm dependency + - mkdir -p /vagrant/node_modules + - cp -RP $PWD /vagrant/node_modules/evolution-wordpress + # set up local provisioner - sudo cp $PWD/.travis.provision.bin $PWD/bin/provision - sudo chmod +x $PWD/bin/provision - - sudo $PWD/bin/provision - - sudo chown -Rf $USER:$USER $HOME/.ansible # mock vagrant binary (for parity with local/remote stages) - sudo cp $PWD/.travis.vagrant.bin /bin/vagrant - sudo chmod +x /bin/vagrant @@ -46,11 +65,15 @@ before_install: - ssh-keyscan -H production.example.com >> $HOME/.ssh/known_hosts - ssh-keyscan -H www.example.com >> $HOME/.ssh/known_hosts - ssh-keyscan -H example.com >> $HOME/.ssh/known_hosts - # install composer (for phpunit) - - curl -sS https://getcomposer.org/installer | php + # run xvfb as background service + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1024x768x24" install: + - echo "Provision locally and fix permissions afterward" + - sudo $PWD/bin/provision + - sudo chown -Rf $USER:$USER $HOME/.ansible - echo "Install Composer dependencies" - - php composer.phar install --dev + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install -n --dev -vv script: - ./vendor/bin/phpunit - - npm test + - ./bin/test diff --git a/README.md b/README.md index d55f2e44..569229cc 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,9 @@ Evolution lets you generate an entirely versioned, multi-environment Wordpress s ## Project Status -Evolution is _largely_ stable and usable at this point, but features are still being added and bugs being fixed. This documentation is also a work in progress. +Evolution is stable and usable in a production environment, but features are still being added and bugs being fixed. Suggestions, bug reports, and pull requests are welcome. + +This and [futher documentation](./docs/) are a constant work in progress. Contributions to documentation are _always_ welcome. ## Quick Start @@ -37,15 +39,15 @@ You will need: * [Vagrant](https://www.vagrantup.com/downloads.html) 1.8+ * [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 5+ * [Hostmanager for Vagrant](https://github.com/smdahlen/vagrant-hostmanager#installation) -* [npm](https://docs.npmjs.com/getting-started/installing-node) +* [Node](https://nodejs.org/en/download/) 4.0+ * [Bundler](http://bundler.io/) -* [Ansible](http://docs.ansible.com/intro_installation.html) 1.6+ +* [Ansible](http://docs.ansible.com/intro_installation.html) 2.0+ * [sshpass](https://gist.github.com/arunoda/7790979) -You can then use npm to install Bower and the Yeoman generator: +You can then use npm to install the Yeoman generator: ``` -npm install -g bower yo generator-evolve +npm install -g yo generator-evolve ``` ### Common Workflows diff --git a/bin/import b/bin/import index 3b30a241..c3e9f460 100755 --- a/bin/import +++ b/bin/import @@ -1,7 +1,8 @@ #!/usr/bin/env node -var fs = require('fs'); -var fsx = require('fs-extra'); +'use strict' + +var fs = require('fs-extra'); var os = require('os'); var path = require('path'); var cp = require('child_process'); @@ -9,7 +10,7 @@ var util = require('util'); var chalk = require('chalk'); var deasync = require('deasync'); var inquirer = require('inquirer'); -var replace = require("replace"); +var replace = require('replace'); var yauzl = require('yauzl'); // Evolution site working directory @@ -36,7 +37,7 @@ catch(e) { // extract zip to tmpdir var extractedPath = path.join(os.tmpdir(), path.basename(answers.zipfile, '.zip')); -fsx.emptyDirSync(extractedPath); +fs.emptyDirSync(extractedPath); console.log('Extracting', chalk.yellow(answers.zipfile), 'to', chalk.green(extractedPath)); var extractedFiles = 0, @@ -52,7 +53,7 @@ yauzl.open(answers.zipfile, {lazyEntries: true}, function(err, zipfile) { if (/\/$/.test(entry.fileName)) { // create directories (names ending in '/') extractedDirs++; - fsx.mkdirs(extractedName, function(err) { + fs.mkdirs(extractedName, function(err) { if (err) throw err; zipfile.readEntry(); }); @@ -62,7 +63,7 @@ yauzl.open(answers.zipfile, {lazyEntries: true}, function(err, zipfile) { zipfile.openReadStream(entry, function(err, readStream) { if (err) throw err; // ensure parent directory exists - fsx.mkdirs(path.dirname(extractedName), function(err) { + fs.mkdirs(path.dirname(extractedName), function(err) { if (err) throw err; readStream.pipe(fs.createWriteStream(extractedName)); readStream.on('end', function() { @@ -163,21 +164,20 @@ function zipPostProcessing() { } // remove known host, only as necessary - var cpExec = deasync(cp.exec); var execMaxBuffer = 1024*1024*1024; var cpCmd = '(ssh-keygen -F ' + siteDomain + ' && ssh-keygen -R ' + siteDomain + ') || :'; console.log('checking for (and removing) any existing known host:', chalk.cyan(cpCmd)); - cpExec(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); + cp.execSync(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); // run sql against db cpCmd = 'bundle exec cap local evolve:db:exec[' + path.join(extractedPath, 'wordpress.sql') + ']'; console.log('importing sql via:', chalk.cyan(cpCmd)); - cpExec(cpCmd, {cwd: siteDirectory, env: util._extend(process.env, {evolution_non_interactive: 1}), stdio: 'inherit', maxBuffer: execMaxBuffer}); + cp.execSync(cpCmd, {cwd: siteDirectory, env: util._extend(process.env, {evolution_non_interactive: 1}), stdio: 'inherit', maxBuffer: execMaxBuffer}); // list all users via wp-cli cpCmd = 'bundle exec cap local wp:user:list:--fields=ID,user_login,roles:--format=json'; console.log('listing users via:', chalk.cyan(cpCmd)); - var capOut = cpExec(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); + var capOut = cp.execSync(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); // prompt for role of any users lacking one var existingUsers = capOut.match(/\s+(\[\{"ID":[^\r\n]+)/); @@ -206,7 +206,7 @@ function zipPostProcessing() { cpCmd = 'bundle exec cap local wp:user:add-role:'+user.ID+':'+answers.role; console.log('assigning user role via:', chalk.cyan(cpCmd)); - cpExec(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); + cp.execSync(cpCmd, {cwd: siteDirectory, env: process.env, stdio: 'inherit', maxBuffer: execMaxBuffer}); } }); } @@ -214,18 +214,18 @@ function zipPostProcessing() { // Copying Themes / Plugins / Uploads var pluginsPath = path.join(extractedPath, 'plugins'); console.log('installing plugins from', chalk.cyan(pluginsPath)); - fsx.copySync(pluginsPath, path.join(siteDirectory, 'web', 'wp-content', 'plugins'), {clobber: true}); + fs.copySync(pluginsPath, path.join(siteDirectory, 'web', 'wp-content', 'plugins'), {clobber: true}); var themesPath = path.join(extractedPath, 'themes'); console.log('installing themes from', chalk.cyan(themesPath)); - fsx.copySync(themesPath, path.join(siteDirectory, 'web', 'wp-content', 'themes'), {clobber: true}); + fs.copySync(themesPath, path.join(siteDirectory, 'web', 'wp-content', 'themes'), {clobber: true}); var uploadsPath = path.join(extractedPath, 'uploads'); if (manifest.is_multisite) { uploadsPath = path.join(uploadsPath, 'sites', manifest.blogid); } console.log('installing uploads from', chalk.cyan(uploadsPath)); - fsx.copySync(uploadsPath, path.join(siteDirectory, 'web', 'wp-content', 'uploads'), {clobber: true}); + fs.copySync(uploadsPath, path.join(siteDirectory, 'web', 'wp-content', 'uploads'), {clobber: true}); console.log(chalk.green('looks like we\'re done here!')); } diff --git a/bin/mock b/bin/mock index b566066a..f2fce179 100755 --- a/bin/mock +++ b/bin/mock @@ -1,11 +1,14 @@ #!/usr/bin/env node +'use strict' + var fs = require('fs-extra'); -var exec = require('child_process').exec; -var helpers = require('yeoman-generator').test; +var cp = require('child_process'); +var helpers = require('yeoman-test'); var path = require('path'); -var outputDir = path.join(__dirname, '..', 'temp'); +var libDir = path.normalize(path.join(__dirname, '..')); +var outputDir = path.join(libDir, 'temp'); var privatePath = path.join(outputDir, '/lib/ansible/files/ssh/id_rsa'); var privateKey = fs.existsSync(privatePath) ? fs.readFileSync(privatePath) : null; var publicPath = path.join(outputDir, '/lib/ansible/files/ssh/id_rsa.pub'); @@ -30,14 +33,14 @@ helpers.testDirectory(outputDir, function(err) { } else { var prep = [ - 'git clone git@github.com:evolution/wordpress-example.git ' + outputDir, + 'git clone https://github.com/evolution/wordpress-example.git ' + outputDir, 'cd ' + outputDir, 'git rm -rf .', ]; } console.log('Executing `%s`', prep.join(' && ')); - exec(prep.join(' && '), function(err, stdout, stderr) { + cp.exec(prep.join(' && '), function(err, stdout, stderr) { if (err) { throw err; } @@ -111,6 +114,26 @@ helpers.testDirectory(outputDir, function(err) { fs.writeFileSync(outputDir + '/Vagrantfile', vagrantFile); + var committish; + if (isCI) { + committish = process.env.TRAVIS_COMMIT; + } else { + committish = cp.execSync('git rev-parse --verify HEAD', { cwd: libDir }) + .toString().trim(); + } + + var packageJson = fs.readFileSync(outputDir + '/package.json', 'utf8') + .replace( + new RegExp('("evolution-wordpress":[ ]*)"[^"]+"'), + [ + '$1', + `"evolution/wordpress#${committish}"`, + ].join('') + ) + ; + + fs.writeFileSync(outputDir + '/package.json', packageJson); + var wrap = [ 'git add .', ]; @@ -118,12 +141,12 @@ helpers.testDirectory(outputDir, function(err) { if (isCI) { Array.prototype.push.apply(wrap, [ 'git commit -m "Something to deploy"', - 'git remote add origin ' + outputDir + '/.git', + 'git remote add origin /vagrant/.git', ]); } console.log('Executing `%s`', wrap.join(' && ')); - exec(wrap.join(' && '), { cwd: outputDir }); + cp.exec(wrap.join(' && '), { cwd: outputDir }); }); }); }); diff --git a/bin/postinstall b/bin/postinstall new file mode 100644 index 00000000..539b63d3 --- /dev/null +++ b/bin/postinstall @@ -0,0 +1,155 @@ +#!/usr/bin/env node + +'use strict' + +var fs = require('fs-extra'); +var glob = require('glob'); +var inWords = require('in-words').en; +var path = require('path'); +var request = require('request'); +var util = require('util'); +var yauzl = require('yauzl'); + +console.log('Evolution WordPress - npm `postinstall` Script\n'); + +// our point of reference on the filesystem +var siteDir = process.cwd(); + +// wordpress to install, by argument or groupvar +var wpVersion = process.argv[2]; +if (!wpVersion) { + var groupVars = fs.readFileSync(path.join(siteDir, 'lib/ansible/group_vars/all')).toString(); + var matched = groupVars.match(/^wp_version\s*:\s*['"]?([\d.]+)/m); + + if (!matched) { + throw new Error('Couldn\'t find wordpress version to install'); + } + + wpVersion = matched[1]; +} + +// ensure fresh install +var installDir = path.join(siteDir, 'web/wp'); +fs.removeSync(installDir); + +// install from github +console.log('Downloading wordpress `%s` to `%s`...', wpVersion, installDir); +extract('wordpress', 'wordpress', wpVersion, installDir, function (err) { + if (err) throw err; + + var toThemeDir = path.join(siteDir, 'web/wp-content/themes'); + var fromThemeDir = path.join(siteDir, 'web/wp/wp-content/themes'); + var latestTheme = null; + var themeExists = function (parents, name) { + var exists = false; + parents.forEach(function (directory) { + try { + if (fs.statSync(path.join(directory, name)).isDirectory()) { + exists = true; + } + } catch (e) {}; + }); + return exists; + }; + + fs.ensureDirSync(toThemeDir); + + for (var testYear = new Date().getFullYear(); testYear > 2009; testYear--) { + var testName = inWords(testYear.toString().substr(0, 2)) + + inWords(testYear.toString().substr(2, 2)) + ; + + if (themeExists([toThemeDir, fromThemeDir], testName)) { + latestTheme = testName; + break; + } + else { + console.log('* No theme ' + testName); + } + } + + if (latestTheme === null) { + if (themeExists([fromThemeDir], 'default')) { + latestTheme = 'default'; + } + else { + throw new Error(util.format('Could not find WordPress core theme in `%s`', fromThemeDir)); + } + } + + if (themeExists([toThemeDir], latestTheme)) { + console.log('* Existing core theme ' + latestTheme); + } + else { + console.log('* Found theme ' + latestTheme); + + var fromTheme = path.join(fromThemeDir, latestTheme); + var toTheme = path.join(toThemeDir, latestTheme); + + console.log('* Moving theme `%s` to `%s`...', path.relative(siteDir, fromTheme), path.relative(siteDir, toTheme)); + fs.renameSync(fromTheme, toTheme); + } +}); + +function extract(username, repo, version, directory, cb) { + var zipName = path.join(siteDir, [username, repo, version].join('-')); + request({ + url: 'https://github.com/' + username + '/' + repo + '/archive/' + version + '.zip', + headers: { 'user-agent': 'https://github.com/request/request' } + }) + .on('error', function (err) { cb(err); }) + .pipe(fs.createWriteStream(zipName+'.zip')) + .on('close', function () { + console.log('Extracting '+zipName+'.zip to', zipName); + var extractedFiles = 0, + extractedDirs = 0; + fs.emptyDirSync(zipName); + yauzl.open(zipName+'.zip', {lazyEntries: true}, function(err, zipfile) { + if (err) throw err; + + zipfile.readEntry(); + zipfile.on('entry', function(entry) { + var extractedName = path.join(zipName, entry.fileName); + process.stdout.write("Extracted " + extractedFiles + " files and " + extractedDirs + " directories\r"); + + if (/\/$/.test(entry.fileName)) { + // create directories (names ending in '/') + extractedDirs++; + fs.mkdirs(extractedName, function(err) { + if (err) throw err; + zipfile.readEntry(); + }); + } else { + // write files via streams + extractedFiles++; + zipfile.openReadStream(entry, function(err, readStream) { + if (err) throw err; + // ensure parent directory exists + fs.mkdirs(path.dirname(extractedName), function(err) { + if (err) throw err; + readStream.pipe(fs.createWriteStream(extractedName)); + readStream.on('end', function() { + zipfile.readEntry(); + }); + }); + }); + } + }); + zipfile.on('end', function (err) { + if (err) console.log('Zip ended with an error:', err); + console.log('Extracted zip!'); + // gjve it a second to finish writing the last entry... + setTimeout(function() { + fs.unlinkSync(zipName+'.zip'); + + var entryDir = glob.sync(path.join(zipName, '*/'))[0]; + fs.copySync(entryDir, directory, {clobber:true}); + fs.removeSync(zipName); + + cb(); + }, 1000); + }) + + }); + }); +}; diff --git a/bin/test b/bin/test index bba165a5..bb64f73d 100755 --- a/bin/test +++ b/bin/test @@ -1,5 +1,8 @@ #!/usr/bin/env node +'use strict' + +var dns = require('dns'); var glob = require('glob'); var Mocha = require('mocha'); var path = require('path'); @@ -14,10 +17,14 @@ glob.sync('**/*.js', { mocha.addFile(path.join(testDir, file)); }); -mocha - .bail(true) - .reporter('spec') - .timeout(0) - .ui('bdd') - .run(process.exit) -; +dns.lookup('example.com', function(err, address, family) { + process.env.EXAMPLE_COM = address; + + mocha + .bail(true) + .reporter('spec') + .timeout(0) + .ui('bdd') + .run(process.exit) + ; +}); diff --git a/bower.json b/bower.json index d11b10b5..bec10bb2 100644 --- a/bower.json +++ b/bower.json @@ -3,6 +3,7 @@ "version": "1.6.2", "homepage": "https://github.com/evolution/wordpress", "authors": [ + "Evan Kaufman ", "Eric Clemmons " ], "description": "Libraries for a multi-staged WordPress workflow with Vagrant", diff --git a/docs/REF-generator-prompts.md b/docs/REF-generator-prompts.md index a4b597c8..f6a1aae7 100644 --- a/docs/REF-generator-prompts.md +++ b/docs/REF-generator-prompts.md @@ -23,7 +23,7 @@ If you opt for a *public* project, the generator will configure your [`.gitignor **This is ideal if you plan to open-source your site on a free public repository (we recommend Github for this).** -If you opt for *private*, it will configure your [`bower.json`](https://github.com/bower/bower.json-spec#private) to prevent publication through Bower, but *your keys and certificates will be versioned*. +If you opt for *private*, it will configure your [`package.json`](https://docs.npmjs.com/files/package.json) to prevent publication through npm, but *your keys and certificates will be versioned*. **In this case, you should use a paid [private repository on Github](https://help.github.com/articles/making-a-public-repository-private/), or a free [private repository on Gitlab](http://doc.gitlab.com/ce/gitlab-basics/create-project.html).** @@ -95,7 +95,7 @@ This installs some command line utilities that are useful for monitoring process ### Wordpress version -This determines which version of Wordpress will be fetched from Github and installed as a bower dependency. **The default for this is the latest tagged version.** +This determines which version of Wordpress will be fetched and installed from Github. **The default for this is the latest tagged version.** ### Wordpress automatic updates diff --git a/docs/TUTORIAL-CLONE.md b/docs/TUTORIAL-CLONE.md index 18460094..539cd95c 100644 --- a/docs/TUTORIAL-CLONE.md +++ b/docs/TUTORIAL-CLONE.md @@ -17,9 +17,9 @@ If you don't already have the site running locally (eg, someone else set it up), During generation of a new or existing site, the generator does this step for you. Manually installing dependencies is still quite simple, however: bundle install - bower install + npm install -Note that bower will automatically pull in non-breaking changes from the latest patch version of Evolution (eg, `v1.0.15` to `v1.0.16`). +Note that npm's postinstall will automatically pull in non-breaking changes from the latest patch version of Evolution (eg, `v1.0.15` to `v1.0.16`). ### Bringing up local diff --git a/docs/TUTORIAL-UPGRADE.md b/docs/TUTORIAL-UPGRADE.md index f305fe73..95035d24 100644 --- a/docs/TUTORIAL-UPGRADE.md +++ b/docs/TUTORIAL-UPGRADE.md @@ -2,7 +2,7 @@ ### Why regenerate your site? -Non-breaking changes to Evolution are added across **patch versions** (eg, `v1.0.15` to `v1.0.16`), and are automatically pulled down on `bower install` or deployment to a remote stage. +Non-breaking changes to Evolution are added across **patch versions** (eg, `v1.0.15` to `v1.0.16`), and are automatically pulled down on `npm install` or deployment to a remote stage. Breaking changes are added across **minor versions** (eg `v1.0.16` to `v1.1.0`), and require running the generator against your existing site codebase @@ -15,7 +15,7 @@ Bring up your local copy of the site, or [follow the guide](./TUTORIAL-ClONE.md# ### Regenerating -Now, run the generator and follow [the prompts](./REF-generator-prompts.md) -- it should pre-select the choices for which it was already configured, and install bundler and bower dependencies automatically: +Now, run the generator and follow [the prompts](./REF-generator-prompts.md) -- it should pre-select the choices for which it was already configured, and install bundler and npm dependencies automatically: yo evolve wordpress git status diff --git a/lib/ansible/lookup_plugins/ansible1_nested_dict.py b/lib/ansible/lookup_plugins/ansible1_nested_dict.py deleted file mode 100644 index 8f15eae7..00000000 --- a/lib/ansible/lookup_plugins/ansible1_nested_dict.py +++ /dev/null @@ -1,47 +0,0 @@ -# (c) 2014, Evan Kaufman -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from ansible.utils import safe_eval -import ansible.utils as utils -import ansible.errors as errors - -def flatten_nested_hash_to_list(terms, result=None, stack=None, level=None): - result = [] if result is None else result - stack = {} if stack is None else stack - level = 0 if level is None else level - - for key, val in terms.iteritems(): - stack['key_%s' % level] = key - if type(val) is dict: - flatten_nested_hash_to_list(terms=val, result=result, stack=stack.copy(), level=level+1) - else: - stack['value'] = val - result.append(stack.copy()) - return result - -class LookupModule(object): - - def __init__(self, basedir=None, **kwargs): - self.basedir = basedir - - def run(self, terms, inject=None, **kwargs): - terms = utils.listify_lookup_plugin_terms(terms, self.basedir, inject) - - if not isinstance(terms, dict): - raise errors.AnsibleError("with_nested_dict expects a dict") - - return flatten_nested_hash_to_list(terms) diff --git a/lib/ansible/lookup_plugins/ansible2_nested_dict.py b/lib/ansible/lookup_plugins/ansible2_nested_dict.py deleted file mode 100644 index af9d30ff..00000000 --- a/lib/ansible/lookup_plugins/ansible2_nested_dict.py +++ /dev/null @@ -1,84 +0,0 @@ -# (c) 2016, Evan Kaufman -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible.errors import AnsibleError -from ansible.plugins.lookup import LookupBase - -""" -Lookup plugin to flatten nested dictionaries for linear iteration -================================================================= - -For example, a collection of holidays nested by year, month, and day: - - { - '2016': { - 'January': { - '1': 'New Years Day', - '19': 'Martin Luther King Day', - }, - 'February': { - '14': 'Valentines Day', - '16': 'Presidents Day', - }, - ... - }, - ... - } - -Would be flattened into an array of shallow dicts, with each original key denoted by its nesting level: - - [ - { 'key_0': '2016', 'key_1': 'January', 'key_2': '1', 'value': 'New Years Day' }, - { 'key_0': '2016', 'key_1': 'January', 'key_2': '19', 'value': 'Martin Luther King Day' }, - { 'key_0': '2016', 'key_1': 'February', 'key_2': '14', 'value': 'Valentines Day' }, - { 'key_0': '2016', 'key_1': 'February', 'key_2': '16', 'value': 'Presidents Day' }, - ... - ] - -The nested keys and value of each are then exposed to an iterative task: - - - name: "Remind us what holidays are this month" - debug: - msg: "Remember {{value}} on {{key_1}} {{'%02d'|format(key_2)}} in the year {{key_0}}" - when: key_1 == current_month - with_nested_dict: holidays_by_year_month_day - -""" -class LookupModule(LookupBase): - - @staticmethod - def _flatten_nested_hash_to_list(terms, result=None, stack=None, level=None): - result = [] if result is None else result - stack = {} if stack is None else stack - level = 0 if level is None else level - - for key, val in terms.iteritems(): - stack['key_%s' % level] = key - if type(val) is dict: - LookupModule._flatten_nested_hash_to_list(terms=val, result=result, stack=stack.copy(), level=level+1) - else: - stack['value'] = val - result.append(stack.copy()) - return result - - def run(self, terms, variables=None, **kwargs): - if not isinstance(terms, dict): - raise AnsibleError("with_nested_dict expects a dict") - - return self._flatten_nested_hash_to_list(terms) diff --git a/lib/ansible/lookup_plugins/nested_dict.py b/lib/ansible/lookup_plugins/nested_dict.py index 6a417e8c..40030189 100644 --- a/lib/ansible/lookup_plugins/nested_dict.py +++ b/lib/ansible/lookup_plugins/nested_dict.py @@ -1,11 +1,84 @@ -# add current dir to import path -import sys -import os -sys.path.append(os.path.dirname(__file__)) - -# import the right plugin for the right ansible major version -from ansible import __version__ as ansible_version -if ansible_version.startswith('2.'): - from ansible2_nested_dict import LookupModule -elif ansible_version.startswith('1.'): - from ansible1_nested_dict import LookupModule +# (c) 2016, Evan Kaufman +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase + +""" +Lookup plugin to flatten nested dictionaries for linear iteration +================================================================= + +For example, a collection of holidays nested by year, month, and day: + + { + '2016': { + 'January': { + '1': 'New Years Day', + '19': 'Martin Luther King Day', + }, + 'February': { + '14': 'Valentines Day', + '16': 'Presidents Day', + }, + ... + }, + ... + } + +Would be flattened into an array of shallow dicts, with each original key denoted by its nesting level: + + [ + { 'key_0': '2016', 'key_1': 'January', 'key_2': '1', 'value': 'New Years Day' }, + { 'key_0': '2016', 'key_1': 'January', 'key_2': '19', 'value': 'Martin Luther King Day' }, + { 'key_0': '2016', 'key_1': 'February', 'key_2': '14', 'value': 'Valentines Day' }, + { 'key_0': '2016', 'key_1': 'February', 'key_2': '16', 'value': 'Presidents Day' }, + ... + ] + +The nested keys and value of each are then exposed to an iterative task: + + - name: "Remind us what holidays are this month" + debug: + msg: "Remember {{value}} on {{key_1}} {{'%02d'|format(key_2)}} in the year {{key_0}}" + when: key_1 == current_month + with_nested_dict: holidays_by_year_month_day + +""" +class LookupModule(LookupBase): + + @staticmethod + def _flatten_nested_hash_to_list(terms, result=None, stack=None, level=None): + result = [] if result is None else result + stack = {} if stack is None else stack + level = 0 if level is None else level + + for key, val in terms.iteritems(): + stack['key_%s' % level] = key + if type(val) is dict: + LookupModule._flatten_nested_hash_to_list(terms=val, result=result, stack=stack.copy(), level=level+1) + else: + stack['value'] = val + result.append(stack.copy()) + return result + + def run(self, terms, variables=None, **kwargs): + if not isinstance(terms, dict): + raise AnsibleError("with_nested_dict expects a dict") + + return self._flatten_nested_hash_to_list(terms) diff --git a/lib/ansible/roles/apache-prefork/tasks/main.yml b/lib/ansible/roles/apache-prefork/tasks/main.yml index 0e061711..b147d030 100644 --- a/lib/ansible/roles/apache-prefork/tasks/main.yml +++ b/lib/ansible/roles/apache-prefork/tasks/main.yml @@ -1,15 +1,9 @@ --- -- name: Create custom apache prefork config (2.2) - template: src=prefork.conf dest=/etc/apache2/prefork.conf mode=0644 - when: apache_version.stdout == '2.2' - sudo: yes - -- name: Remove default apache prefork config (2.2) - replace: dest=/etc/apache2/apache2.conf backup=yes regexp='^(?:#[^\n]+\n)*[^<]+?<\/IfModule>' replace='Include prefork.conf' validate='/usr/sbin/apache2ctl -f %s -t' - when: apache_version.stdout == '2.2' - sudo: yes - -- name: Override apache prefork module config (2.4) - template: src=prefork.conf dest=/etc/apache2/mods-available/mpm_prefork.conf backup=yes mode=0644 - when: apache_version.stdout == '2.4' - sudo: yes +- name: Override apache prefork module config + template: + src: prefork.conf + dest: /etc/apache2/mods-available/mpm_prefork.conf + backup: yes + mode: '0644' + when: apache_version.stdout == '2.4' + become: true diff --git a/lib/ansible/roles/apache/handlers/main.yml b/lib/ansible/roles/apache/handlers/main.yml index c30bee84..bfc5e044 100644 --- a/lib/ansible/roles/apache/handlers/main.yml +++ b/lib/ansible/roles/apache/handlers/main.yml @@ -1,4 +1,6 @@ --- -- name: restart apache - service: name=apache2 state=restarted - sudo: true +- name: restart apache + service: + name: apache2 + state: restarted + become: true diff --git a/lib/ansible/roles/apache/library/restore b/lib/ansible/roles/apache/library/restore index 181f5dcf..e79dc182 100644 --- a/lib/ansible/roles/apache/library/restore +++ b/lib/ansible/roles/apache/library/restore @@ -56,10 +56,16 @@ options: EXAMPLES = """ # Restore oldest available backup of apache mod config -- restore: path=/etc/apache2/mods-available/mpm_prefork.conf backup=no which=oldest +- restore: + path: /etc/apache2/mods-available/mpm_prefork.conf + backup: no + which: oldest # Restore newest available backup of service config, whilst backing up current file -- restore: path=/etc/default/varnish backup=yes which=newest +- restore: + path: /etc/default/varnish + backup: yes + which: newest """ def main(): diff --git a/lib/ansible/roles/apache/tasks/main.yml b/lib/ansible/roles/apache/tasks/main.yml index 23df5ec2..45c58d86 100644 --- a/lib/ansible/roles/apache/tasks/main.yml +++ b/lib/ansible/roles/apache/tasks/main.yml @@ -1,77 +1,96 @@ --- -- name: Install Apache packages - apt: pkg={{ item }} state=present - with_items: "{{ apache_packages }}" - sudo: yes - -- name: Enable Apache modules - apache2_module: name={{ item }} state=present - with_items: "{{ apache_modules }}" - sudo: yes - notify: restart apache - -- name: Disable Apache modules - apache2_module: name={{ item }} state=absent - with_items: "{{ apache_disabled_modules }}" - sudo: yes - notify: restart apache - -- name: Register Apache major/minor version - shell: "apache2 -v | grep -i apache | perl -pe 's|^.*apache/([0-9]+[.][0-9]+)[.].*$|$1|i'" - register: apache_version - -- debug: msg={{ apache_version.stdout }} - -- name: Configure apache log rotation - copy: src=logrotate-apache dest=/etc/logrotate.d/apache2 owner=root group=root mode=644 - sudo: yes - -- name: Configure Apache canonical server name (2.2) - template: src=canonical dest=/etc/apache2/conf.d/canonical-servername.conf mode=0644 - notify: restart apache - when: apache_version.stdout == '2.2' - sudo: true - -- name: Configure Apache canonical server name (2.4) - template: src=canonical dest=/etc/apache2/conf-available/canonical-servername.conf mode=0644 - when: apache_version.stdout == '2.4' - sudo: true - -- name: Enable canonical config (2.4) - command: a2enconf canonical-servername - notify: restart apache - when: apache_version.stdout == '2.4' - sudo: yes - -- name: Ensure Apache `NameVirtualHost` is 80 (2.2) - lineinfile: regexp='^NameVirtualHost' line='NameVirtualHost *:80' dest=/etc/apache2/ports.conf backup=yes - notify: restart apache - when: apache_version.stdout == '2.2' - sudo: yes - -- name: Ensure Apache `Listen` is 80 - lineinfile: regexp='^Listen' line='Listen 80' dest=/etc/apache2/ports.conf backup=yes - notify: restart apache - sudo: yes - -- name: Ensure existing Varnish installs won't cause port conflict on boot - lineinfile: regexp='^START=' line='START=no' dest=/etc/default/varnish backup=no - ignore_errors: true - sudo: yes - -- name: Ensure pristine mpm_prefork directives - restore: path=/etc/apache2/mods-available/mpm_prefork.conf which=oldest backup=no fail_on_missing=no - when: apache_version.stdout == '2.4' - sudo: yes - -- name: Change ownership of /var/www to deploy - file: path=/var/www state=directory owner=deploy group=deploy - sudo: yes - -- shell: (cd /etc/apache2/sites-enabled/ && ls -1 000-default*) - register: apache_default_vhost - ignore_errors: true - sudo: yes +- name: Install Apache packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ apache_packages }}" + become: true + +- name: Enable Apache modules + apache2_module: + name: "{{ item }}" + state: present + with_items: "{{ apache_modules }}" + become: true + notify: restart apache + +- name: Disable Apache modules + apache2_module: + name: "{{ item }}" + state: absent + with_items: "{{ apache_disabled_modules }}" + become: true + notify: restart apache + +- name: Register Apache major/minor version + shell: "apache2 -v | grep -i apache | perl -pe 's|^.*apache/([0-9]+[.][0-9]+)[.].*$|$1|i'" + register: apache_version + +- debug: + msg: "{{ apache_version.stdout }}" + +- name: Configure apache log rotation + copy: + src: logrotate-apache + dest: /etc/logrotate.d/apache2 + owner: root + group: root + mode: '0644' + become: true + +- name: Configure Apache canonical server name + template: + src: canonical + dest: /etc/apache2/conf-available/canonical-servername.conf + mode: '0644' + when: apache_version.stdout == '2.4' + become: true + +- name: Enable canonical config + command: a2enconf canonical-servername + notify: restart apache + when: apache_version.stdout == '2.4' + become: true + +- name: Ensure Apache `Listen` is 80 + lineinfile: + regexp: '^Listen' + line: 'Listen 80' + dest: /etc/apache2/ports.conf + backup: yes + notify: restart apache + become: true + +- name: Ensure existing Varnish installs won't cause port conflict on boot + lineinfile: + regexp: '^START=' + line: 'START=no' + dest: /etc/default/varnish + backup: no + ignore_errors: true + become: true + +- name: Ensure pristine mpm_prefork directives + restore: + path: /etc/apache2/mods-available/mpm_prefork.conf + which: oldest + backup: no + fail_on_missing: no + when: apache_version.stdout == '2.4' + become: true + +- name: Change ownership of /var/www to deploy + file: + path: /var/www + state: directory + owner: deploy + group: deploy + become: true + +- shell: (cd /etc/apache2/sites-enabled/ && ls -1 000-default*) + register: apache_default_vhost + ignore_errors: true + become: true - set_fact: wordpress__xmlrpc_allow: false @@ -81,22 +100,27 @@ wordpress__xmlrpc_whitelist: false when: wordpress__xmlrpc_whitelist is undefined -- name: Disable default apache site - command: a2dissite {{ apache_default_vhost.stdout }} removes=/etc/apache2/sites-enabled/{{ apache_default_vhost.stdout }} - notify: restart apache - when: apache_default_vhost.stdout != '' - sudo: yes - -- name: Create apache vhosts - template: src=virtualhost dest=/etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf mode=0644 +- name: Disable default apache site + command: a2dissite {{ apache_default_vhost.stdout }} + args: + removes: /etc/apache2/sites-enabled/{{ apache_default_vhost.stdout }} + notify: restart apache + when: apache_default_vhost.stdout != '' + become: true + +- name: Create apache vhosts + template: + src: virtualhost + dest: /etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf + mode: '0644' with_nested_dict: "{{ apache_vhosts }}" # see ansible/ansible#9563 - when: item.key_0 == stage - notify: restart apache - sudo: true + when: item.key_0 == stage + notify: restart apache + become: true -- name: Enable apache vhosts - command: a2ensite {{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf +- name: Enable apache vhosts + command: a2ensite {{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf with_nested_dict: "{{ apache_vhosts }}" - when: item.key_0 == stage - notify: restart apache - sudo: true + when: item.key_0 == stage + notify: restart apache + become: true diff --git a/lib/ansible/roles/apache/templates/virtualhost b/lib/ansible/roles/apache/templates/virtualhost index cc395d12..05b430e0 100644 --- a/lib/ansible/roles/apache/templates/virtualhost +++ b/lib/ansible/roles/apache/templates/virtualhost @@ -44,10 +44,7 @@ {% endif %} Options FollowSymLinks AllowOverride All -{% if apache_version.stdout == '2.2' %} - Order allow,deny - Allow from all -{% elif apache_version.stdout == '2.4' %} +{% if apache_version.stdout == '2.4' %} Require all granted {% endif %} @@ -153,7 +150,6 @@ # Compress all output labeled with one of the following media types. - {% if apache_version.stdout != '2.2' %} # # (!) For Apache versions below version 2.3.7 you don't need to @@ -163,7 +159,6 @@ # # https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype - {% endif %} AddOutputFilterByType DEFLATE "application/atom+xml" \ "application/javascript" \ "application/json" \ @@ -196,8 +191,7 @@ "text/x-component" \ "text/x-cross-domain-policy" \ "text/xml" - {% if apache_version.stdout != '2.2' %} - {% endif %} + # Map the following filename extensions to the specified # encoding type in order to make Apache serve the file types diff --git a/lib/ansible/roles/awstats/tasks/main.yml b/lib/ansible/roles/awstats/tasks/main.yml index 7acea934..31b77a9b 100644 --- a/lib/ansible/roles/awstats/tasks/main.yml +++ b/lib/ansible/roles/awstats/tasks/main.yml @@ -1,3 +1,3 @@ --- -- include: vagrant.yml - when: has_vagrant.stdout +- include: vagrant.yml + when: has_vagrant.stdout diff --git a/lib/ansible/roles/awstats/tasks/vagrant.yml b/lib/ansible/roles/awstats/tasks/vagrant.yml index 134801f5..975559e5 100644 --- a/lib/ansible/roles/awstats/tasks/vagrant.yml +++ b/lib/ansible/roles/awstats/tasks/vagrant.yml @@ -1,32 +1,51 @@ --- -- name: Install awstats - apt: pkg=awstats state=present - sudo: yes +- name: Install awstats + apt: + pkg: awstats + state: present + become: true -- name: Use logfile synced down from remote - lineinfile: regexp='^LogFile=' line='LogFile="/tmp/apache2-remote-access.log"' dest=/etc/awstats/awstats.conf - sudo: yes +- name: Use logfile synced down from remote + lineinfile: + regexp: '^LogFile=' + line: 'LogFile="/tmp/apache2-remote-access.log"' + dest: /etc/awstats/awstats.conf + become: true -- name: Use combined log format - lineinfile: regexp='^LogFormat=' line='LogFormat=1' dest=/etc/awstats/awstats.conf - sudo: yes +- name: Use combined log format + lineinfile: + regexp: '^LogFormat=' + line: 'LogFormat=1' + dest: /etc/awstats/awstats.conf + become: true -- name: Set primary site domain - lineinfile: regexp='^SiteDomain=' line='SiteDomain="{{ domain }}"' dest=/etc/awstats/awstats.conf - sudo: yes +- name: Set primary site domain + lineinfile: + regexp: '^SiteDomain=' + line: 'SiteDomain="{{ domain }}"' + dest: /etc/awstats/awstats.conf + become: true -- name: Set host aliases - lineinfile: regexp='^HostAliases=' line='HostAliases="localhost 127.0.0.1 REGEX[^.*\.{{ domain | replace(".", "\\.") }}$]' dest=/etc/awstats/awstats.conf - sudo: yes +- name: Set host aliases + lineinfile: + regexp: '^HostAliases=' + line: 'HostAliases="localhost 127.0.0.1 REGEX[^.*\.{{ domain | replace(".", "\\.") }}$]' + dest: /etc/awstats/awstats.conf + become: true -- name: Enable Apache cgi module - apache2_module: name=cgi state=present - sudo: yes - notify: restart apache +- name: Enable Apache cgi module + apache2_module: + name: cgi + state: present + become: true + notify: restart apache -- name: Update vhosts for awstats web ui - replace: dest=/etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf regexp='^([ \t]+)(LogLevel warn)' replace='\1\2\n\n\1Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch\n\1Alias /awstatsclasses "/usr/share/awstats/lib/"\n\1Alias /awstats-icon "/usr/share/awstats/icon/"\n\1Alias /awstatscss "/usr/share/doc/awstats/examples/css"\n\1ScriptAlias /awstats/ /usr/lib/cgi-bin/' +- name: Update vhosts for awstats web ui + replace: + dest: /etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf + regexp: '^([ \t]+)(LogLevel warn)' + replace: '\1\2\n\n\1Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch\n\1Alias /awstatsclasses "/usr/share/awstats/lib/"\n\1Alias /awstats-icon "/usr/share/awstats/icon/"\n\1Alias /awstatscss "/usr/share/doc/awstats/examples/css"\n\1ScriptAlias /awstats/ /usr/lib/cgi-bin/' with_nested_dict: "{{ apache_vhosts }}" # see ansible/ansible#9563 - when: item.key_0 == stage - notify: restart apache - sudo: true + when: item.key_0 == stage + notify: restart apache + become: true diff --git a/lib/ansible/roles/cleanup/tasks/main.yml b/lib/ansible/roles/cleanup/tasks/main.yml index 6e0c5638..ce6799f7 100644 --- a/lib/ansible/roles/cleanup/tasks/main.yml +++ b/lib/ansible/roles/cleanup/tasks/main.yml @@ -1,47 +1,57 @@ --- -- name: Ensure logrotate runs hourly - command: mv /etc/cron.daily/logrotate /etc/cron.hourly/logrotate +- name: Ensure logrotate runs hourly + command: mv /etc/cron.daily/logrotate /etc/cron.hourly/logrotate args: - creates: /etc/cron.hourly/logrotate - removes: /etc/cron.daily/logrotate - sudo: yes + creates: /etc/cron.hourly/logrotate + removes: /etc/cron.daily/logrotate + become: true -- name: Set up wpcron via user-level crontab +- name: Set up wpcron via user-level crontab cron: args: - name: "wpcron for {{stage}}" - user: deploy - job: "cd {{item.value}} && /usr/local/bin/wp core is-installed --path=$PWD --url='http://{{stage}}.{{domain}}/' && /usr/local/bin/wp cron event run --all --quiet --path=$PWD --url='http://{{stage}}.{{domain}}/'" - with_dict: "{{ wp_cron_path }}" - when: item.key == stage - sudo: yes + name: "wpcron for {{stage}}" + user: deploy + job: "cd {{item.value}} && /usr/local/bin/wp core is-installed --path=$PWD --url='http://{{stage}}.{{domain}}/' && /usr/local/bin/wp cron event run --all --quiet --path=$PWD --url='http://{{stage}}.{{domain}}/'" + with_dict: "{{ wp_cron_path }}" + when: item.key == stage + become: true -- name: Configure varnish health-checking binary - copy: src=varnish-health.sh dest=/usr/local/bin/varnish-health.sh owner=deploy group=deploy mode=0755 - when: role_varnish is defined - sudo: yes +- name: Configure varnish health-checking binary + copy: + src: varnish-health.sh + dest: /usr/local/bin/varnish-health.sh + owner: deploy + group: deploy + mode: '0755' + when: role_varnish is defined + become: true -- name: Disable varnish health checking (as necessary) via user-level crontab +- name: Disable varnish health checking (as necessary) via user-level crontab cron: args: - name: "varnish health check for {{stage}}" - user: deploy - state: absent - when: role_varnish is not defined - sudo: yes + name: "varnish health check for {{stage}}" + user: deploy + state: absent + when: role_varnish is not defined + become: true -- name: Set up varnish health checking via user-level crontab +- name: Set up varnish health checking via user-level crontab cron: args: - name: "varnish health check for {{stage}}" - user: deploy - job: "/usr/local/bin/varnish-health.sh {{stage}}.{{domain}}" - when: role_varnish is defined - sudo: yes + name: "varnish health check for {{stage}}" + user: deploy + job: "/usr/local/bin/varnish-health.sh {{stage}}.{{domain}}" + when: role_varnish is defined + become: true -- name: Configure wordpress autoupdate binary - copy: src=update.sh dest=/usr/local/bin/wp-update owner=deploy group=deploy mode=0755 - sudo: yes +- name: Configure wordpress autoupdate binary + copy: + src: update.sh + dest: /usr/local/bin/wp-update + owner: deploy + group: deploy + mode: '0755' + become: true - set_fact: wordpress__autoupdate: false @@ -55,28 +65,31 @@ wordpress__autoupdate_skip_themes: false when: wordpress__autoupdate_skip_themes is undefined -- name: Disable wordpress core and cli updates (as necessary) via user-level crontab +- name: Disable wordpress core and cli updates (as necessary) via user-level crontab cron: args: - name: "daily wp auto updates for {{stage}}" - user: deploy - state: absent - with_dict: "{{ wp_cron_path }}" - when: wordpress__autoupdate == false and item.key == stage - sudo: yes + name: "daily wp auto updates for {{stage}}" + user: deploy + state: absent + with_dict: "{{ wp_cron_path }}" + when: wordpress__autoupdate == false and item.key == stage + become: true -- name: Set up wordpress core and cli updates via user-level crontab +- name: Set up wordpress core and cli updates via user-level crontab cron: args: - name: "daily wp auto updates for {{stage}}" - user: deploy - job: "/usr/local/bin/wp-update {{ '--major' if wordpress__autoupdate == 'major' else '' }} {{ '--skip-plugins' if wordpress__autoupdate_skip_plugins else '' }} {{ '--skip-themes' if wordpress__autoupdate_skip_themes else '' }} {{ item.value }} http://{{stage}}.{{domain}}/" - hour: 0 - minute: "{{ 0 if item.key == 'staging' else 10 if item.key == 'production' else 20 }}" - with_dict: "{{ wp_cron_path }}" - when: wordpress__autoupdate in ['major','minor'] and item.key == stage - sudo: yes + name: "daily wp auto updates for {{stage}}" + user: deploy + job: "/usr/local/bin/wp-update {{ '--major' if wordpress__autoupdate == 'major' else '' }} {{ '--skip-plugins' if wordpress__autoupdate_skip_plugins else '' }} {{ '--skip-themes' if wordpress__autoupdate_skip_themes else '' }} {{ item.value }} http://{{stage}}.{{domain}}/" + hour: 0 + minute: "{{ 0 if item.key == 'staging' else 10 if item.key == 'production' else 20 }}" + with_dict: "{{ wp_cron_path }}" + when: wordpress__autoupdate in ['major','minor'] and item.key == stage + become: true -- name: Generate init.d for installed evolution services - template: src=evolution-init-d dest=/etc/init.d/evolution-wordpress mode=0755 - sudo: yes +- name: Generate init.d for installed evolution services + template: + src: evolution-init-d + dest: /etc/init.d/evolution-wordpress + mode: '0755' + become: true diff --git a/lib/ansible/roles/common/tasks/main.yml b/lib/ansible/roles/common/tasks/main.yml index 06fe1bb2..2845ba0b 100644 --- a/lib/ansible/roles/common/tasks/main.yml +++ b/lib/ansible/roles/common/tasks/main.yml @@ -1,80 +1,116 @@ --- -- name: Domain - debug: var=domain - -- name: Stage - debug: var=stage - -- name: New Hostname - debug: var=new_hostname - -- name: Determine static or dynamic resolv.conf - shell: cat /etc/resolv.conf | grep "DO NOT EDIT THIS FILE BY HAND" - ignore_errors: yes - register: dynamic_resolv - -- name: Update nameservers in resolv.conf - replace: dest=/etc/resolv.conf regexp="(nameserver [\w\d.:]+\n?)+" replace="nameserver 8.8.8.8\nnameserver 8.8.4.4\n\1" - sudo: yes - -- name: Update nameservers in dhclient.conf - lineinfile: dest=/etc/dhcp/dhclient.conf regexp="^#?prepend domain-name-servers " line="prepend domain-name-servers 8.8.8.8, 8.8.4.4;" - sudo: yes - when: dynamic_resolv.stdout != "" +- name: Domain + debug: + var: domain + +- name: Stage + debug: + var: stage + +- name: New Hostname + debug: + var: new_hostname + +- name: Determine static or dynamic resolv.conf + shell: cat /etc/resolv.conf | grep "DO NOT EDIT THIS FILE BY HAND" + ignore_errors: yes + register: dynamic_resolv + +- name: Update nameservers in resolv.conf + replace: + dest: /etc/resolv.conf + regexp: '(nameserver [\w\d.:]+\n?)+' + replace: 'nameserver 8.8.8.8\nnameserver 8.8.4.4\n\1' + become: true + +- name: Update nameservers in dhclient.conf + lineinfile: + dest: /etc/dhcp/dhclient.conf + regexp: '^#?prepend domain-name-servers ' + line: 'prepend domain-name-servers 8.8.8.8, 8.8.4.4;' + become: true + when: dynamic_resolv.stdout != "" # see evolution/wordpress#126 -- name: Fetch kernel purging script (on control machine) - local_action: get_url dest=/tmp/purge-kernels.py url=https://raw.githubusercontent.com/EvanK/ubuntu-purge-kernels/master/purge-kernels.py force=yes - -- name: Copy purging script to remote - copy: src=/tmp/purge-kernels.py dest=/tmp/purge-kernels.py force=yes mode=0755 - sudo: yes - -- name: Purge kernels as necessary - command: /tmp/purge-kernels.py - register: purgekernels_result - ignore_errors: yes - sudo: yes - when: not lookup('env','CI') - -- name: Show purged kernels - debug: var=purgekernels_result - -- name: Update apt cache - apt: update_cache=yes cache_valid_time="{{ 60 * 60 * 24 }}" - sudo: yes - -- name: Autoremove unused packages - shell: DEBIAN_FRONTEND=noninteractive apt-get -y autoremove - sudo: yes - -- name: Install common packages - apt: pkg={{ item }} state=present - with_items: "{{ common_packages }}" - sudo: yes - -- name: Update `hostname` - hostname: name="{{ new_hostname }}" - sudo: yes - when: old_hostname != new_hostname - -- name: Update /etc/hosts - replace: dest=/etc/hosts backup=yes regexp={{ old_hostname | replace(".", "[.]") }} replace={{ new_hostname }} - sudo: yes - when: old_hostname != new_hostname - -- name: Configure logrotate for evolution logs - copy: src=logrotate dest=/etc/logrotate.d/evolution mode=0644 - sudo: yes - -- name: Test for git protocol (git://) connectivity - command: curl -v -m 10 http://github.com:9418/ - register: git_protocol_test - ignore_errors: true - -- name: Bypass git protocol if necessary - command: git config --global url."https://".insteadOf git:// - when: "'connect() timed out' in git_protocol_test.stderr" - -- include: swap.yml - when: ansible_swaptotal_mb == 0 +- name: Fetch kernel purging script (from control) + local_action: + module: get_url + dest: /tmp/purge-kernels.py + url: https://raw.githubusercontent.com/EvanK/ubuntu-purge-kernels/master/purge-kernels.py + force: yes + +- name: Copy purging script to remote + copy: + src: /tmp/purge-kernels.py + dest: /tmp/purge-kernels.py + force: yes + mode: '0755' + become: true + +- name: Purge kernels as necessary + command: /tmp/purge-kernels.py + register: purgekernels_result + ignore_errors: yes + become: true + when: not lookup('env','CI') + +- name: Remove kernel purging script in tmp (from control) + local_action: + module: file + dest: /tmp/purge-kernels.py + state: absent + +- name: Show purged kernels + debug: + var: purgekernels_result + +- name: Update apt cache + apt: + update_cache: yes + cache_valid_time: "{{ 60 * 60 * 24 }}" + become: true + +- name: Autoremove unused packages + shell: DEBIAN_FRONTEND=noninteractive apt-get -y autoremove + become: true + +- name: Install common packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ common_packages }}" + become: true + +- name: Update `hostname` + hostname: + name: "{{ new_hostname }}" + become: true + when: old_hostname != new_hostname + +- name: Update /etc/hosts + replace: + dest: /etc/hosts + backup: yes + regexp: '{{ old_hostname | replace(".", "[.]") }}' + replace: '{{ new_hostname }}' + become: true + when: old_hostname != new_hostname + +- name: Configure logrotate for evolution logs + copy: + src: logrotate + dest: /etc/logrotate.d/evolution + mode: '0644' + become: true + +- name: Test for git protocol (git://) connectivity + command: curl -v -m 10 http://github.com:9418/ + register: git_protocol_test + ignore_errors: true + +- name: Bypass git protocol if necessary + command: git config --global url."https://".insteadOf git:// + when: "'connect() timed out' in git_protocol_test.stderr" + +- include: swap.yml + when: ansible_swaptotal_mb == 0 diff --git a/lib/ansible/roles/common/tasks/swap.yml b/lib/ansible/roles/common/tasks/swap.yml index 77f2c8b2..2e77569c 100644 --- a/lib/ansible/roles/common/tasks/swap.yml +++ b/lib/ansible/roles/common/tasks/swap.yml @@ -1,49 +1,69 @@ --- -- shell: "df {{ swap__path | dirname }} | awk '!/^Filesystem/ {print $NF}'" - register: swapfile_mountpoint +- shell: "df {{ swap__path | dirname }} | awk '!/^Filesystem/ {print $NF}'" + register: swapfile_mountpoint -- debug: var=swapfile_mountpoint +- debug: + var: swapfile_mountpoint -- name: Calculate ideal swapfile size - template: src=swapsize.j2 dest=/tmp/swapsize +- name: Calculate ideal swapfile size + template: + src: swapsize.j2 + dest: /tmp/swapsize -- name: Fetch swapfile size - shell: "cat /tmp/swapsize | tr -d '[:space:]'" - register: swapfile_size_mb +- name: Fetch swapfile size + shell: "cat /tmp/swapsize | tr -d '[:space:]'" + register: swapfile_size_mb -- debug: var=swapfile_size_mb.stdout +- debug: + var: swapfile_size_mb.stdout -- fail: msg="Could not determine swapfile size with available diskspace" - when: swapfile_size_mb.stdout == '' +- fail: + msg: "Could not determine swapfile size with available diskspace" + when: swapfile_size_mb.stdout == '' -- name: Write swapfile - command: fallocate -l {{swapfile_size_mb.stdout}}M {{swap__path}} - sudo: yes +- name: Write swapfile + command: fallocate -l {{swapfile_size_mb.stdout}}M {{swap__path}} + become: true -- name: Set permissions - file: path={{swap__path}} mode=600 owner=root group=root - sudo: yes +- name: Set permissions + file: + path: "{{swap__path}}" + mode: '0600' + owner: root + group: root + become: true -- name: Create swapfile - command: mkswap {{swap__path}} - sudo: yes +- name: Create swapfile + command: mkswap {{swap__path}} + become: true -- name: Enable swapfile - command: swapon {{swap__path}} - sudo: yes +- name: Enable swapfile + command: swapon {{swap__path}} + become: true -- name: Add swapfile to /etc/fstab - lineinfile: dest=/etc/fstab state=present line="{{ swap__path }} none swap sw 0 0" - sudo: yes +- name: Add swapfile to /etc/fstab + lineinfile: + dest: /etc/fstab + state: present + line: "{{ swap__path }} none swap sw 0 0" + become: true -- name: Configure vm.swappiness - lineinfile: dest=/etc/sysctl.conf line="vm.swappiness = {{ swap__swappiness }}" regexp="^vm.swappiness\s*?=" state=present - sudo: yes +- name: Configure vm.swappiness + lineinfile: + dest: /etc/sysctl.conf + line: 'vm.swappiness = {{ swap__swappiness }}' + regexp: '^vm.swappiness\s*?=' + state: present + become: true -- name: Configure vm.vfs_cache_pressure - lineinfile: dest=/etc/sysctl.conf line="vm.vfs_cache_pressure = {{ swap__vfs_cache_pressure }}" regexp="^vm.vfs_cache_pressure\s*?=" state=present - sudo: yes +- name: Configure vm.vfs_cache_pressure + lineinfile: + dest: /etc/sysctl.conf + line: 'vm.vfs_cache_pressure = {{ swap__vfs_cache_pressure }}' + regexp: '^vm.vfs_cache_pressure\s*?=' + state: present + become: true -- name: Reload sysctl - command: sysctl -p - sudo: yes +- name: Reload sysctl + command: sysctl -p + become: true diff --git a/lib/ansible/roles/debug/tasks/main.yml b/lib/ansible/roles/debug/tasks/main.yml index a5befbc3..db2eb70f 100644 --- a/lib/ansible/roles/debug/tasks/main.yml +++ b/lib/ansible/roles/debug/tasks/main.yml @@ -1,5 +1,7 @@ --- -- name: Install debug packages - apt: pkg={{ item }} state=present - with_items: "{{ debug_packages }}" - sudo: yes +- name: Install debug packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ debug_packages }}" + become: true diff --git a/lib/ansible/roles/firewall/handlers/main.yml b/lib/ansible/roles/firewall/handlers/main.yml index 9402e4c0..0cde7661 100644 --- a/lib/ansible/roles/firewall/handlers/main.yml +++ b/lib/ansible/roles/firewall/handlers/main.yml @@ -1,4 +1,6 @@ --- -- name: restart firewall - service: name=iptables-persistent state=restarted - sudo: yes +- name: restart firewall + service: + name: iptables-persistent + state: restarted + become: true diff --git a/lib/ansible/roles/firewall/tasks/main.yml b/lib/ansible/roles/firewall/tasks/main.yml index e1b1015b..5ec578f7 100644 --- a/lib/ansible/roles/firewall/tasks/main.yml +++ b/lib/ansible/roles/firewall/tasks/main.yml @@ -1,37 +1,54 @@ --- -- name: Attempt to install vanilla iptables-persistent - apt: pkg=iptables-persistent state=latest update_cache=yes - register: iptables_attempted - ignore_errors: yes - sudo: yes +- name: Attempt to install vanilla iptables-persistent + apt: + pkg: iptables-persistent + state: latest + update_cache: yes + register: iptables_attempted + ignore_errors: yes + become: true # See https://forum.linode.com/viewtopic.php?t=9070&p=58732 -- name: Postinst workaround for iptables-persistent - replace: dest=/var/lib/dpkg/info/iptables-persistent.postinst regexp='^(\s*?modprobe -q ip6?table_filter\s*?)$' replace='\1 || true' - when: iptables_attempted | failed - sudo: yes +- name: Postinst workaround for iptables-persistent + replace: + dest: /var/lib/dpkg/info/iptables-persistent.postinst + regexp: '^(\s*?modprobe -q ip6?table_filter\s*?)$' + replace: '\1 || true' + when: iptables_attempted | failed + become: true -- name: Install firewall packages - apt: pkg={{ item }} state=present - with_items: "{{ firewall_packages }}" - sudo: yes +- name: Install firewall packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ firewall_packages }}" + become: true -- name: Install iptables init script + rulesets - copy: src={{ item }} dest=/etc/{{ item }} mode=0644 backup=yes - sudo: yes +- name: Install iptables init script + rulesets + copy: + src: "{{ item }}" + dest: /etc/{{ item }} + mode: '0644' + backup: yes + become: true with_items: - init.d/iptables-persistent - iptables/rules.v4 - iptables/rules.v6 -- name: Create iptables + fail2ban configs - template: src={{ item }} dest=/etc/{{ item }} mode=0644 - sudo: yes +- name: Create iptables + fail2ban configs + template: + src: "{{ item }}" + dest: /etc/{{ item }} + mode: '0644' + become: true with_items: - default/iptables-persistent.conf - fail2ban/jail.local -- name: Ensure iptables init script is executable - file: path=/etc/init.d/iptables-persistent mode=0755 - notify: restart firewall - sudo: yes +- name: Ensure iptables init script is executable + file: + path: /etc/init.d/iptables-persistent + mode: '0755' + notify: restart firewall + become: true diff --git a/lib/ansible/roles/mail/tasks/main.yml b/lib/ansible/roles/mail/tasks/main.yml index 8b63ccfb..be9d09f0 100644 --- a/lib/ansible/roles/mail/tasks/main.yml +++ b/lib/ansible/roles/mail/tasks/main.yml @@ -1,12 +1,17 @@ --- -- name: Install mail package - apt: pkg=postfix state=present - sudo: yes +- name: Install mail package + apt: + pkg: postfix + state: present + become: true -- name: Generate /etc/aliases - template: src=aliases dest=/etc/aliases mode=0644 - sudo: yes +- name: Generate /etc/aliases + template: + src: aliases + dest: /etc/aliases + mode: '0644' + become: true -- name: Update mail aliases - command: newaliases - sudo: yes +- name: Update mail aliases + command: newaliases + become: true diff --git a/lib/ansible/roles/mysql/tasks/main.yml b/lib/ansible/roles/mysql/tasks/main.yml index 1b81d781..cd39e379 100644 --- a/lib/ansible/roles/mysql/tasks/main.yml +++ b/lib/ansible/roles/mysql/tasks/main.yml @@ -1,21 +1,35 @@ --- -- name: Install MySQL packages - apt: pkg={{ item }} state=present - with_items: "{{ mysql_packages }}" - sudo: yes +- name: Install MySQL packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ mysql_packages }}" + become: true -- name: Update my.conf's bind-address - lineinfile: dest=/etc/mysql/my.cnf backup=yes regexp=^bind-address line='bind-address = 0.0.0.0' - sudo: yes +- name: Update my.conf's bind-address + lineinfile: + dest: /etc/mysql/my.cnf + backup: yes + regexp: '^bind-address' + line: 'bind-address = 0.0.0.0' + become: true -- name: Restart mysql - service: name=mysql state=restarted - sudo: yes +- name: Restart mysql + service: + name: mysql + state: restarted + become: true -- name: Create MySQL database - mysql_db: name="{{ mysql.name }}_{{ stage }}" - sudo: yes +- name: Create MySQL database + mysql_db: + name: "{{ mysql.name }}_{{ stage }}" + become: true -- name: Create MySQL user - mysql_user: name={{ mysql.user }} host={{ mysql.host }} password={{ mysql.password }} priv="{{ mysql.name }}_{{ stage }}.*:GRANT,ALL" append_privs=yes - sudo: yes +- name: Create MySQL user + mysql_user: + name: "{{ mysql.user }}" + host: "{{ mysql.host }}" + password: "{{ mysql.password }}" + priv: "{{ mysql.name }}_{{ stage }}.*:GRANT,ALL" + append_privs: yes + become: true diff --git a/lib/ansible/roles/newrelic/handlers/main.yml b/lib/ansible/roles/newrelic/handlers/main.yml index d1bb76d9..203a7db0 100644 --- a/lib/ansible/roles/newrelic/handlers/main.yml +++ b/lib/ansible/roles/newrelic/handlers/main.yml @@ -1,4 +1,6 @@ --- -- name: restart newrelic-sysmond - service: name=newrelic-sysmond state=restarted - sudo: true +- name: restart newrelic-sysmond + service: + name: newrelic-sysmond + state: restarted + become: true diff --git a/lib/ansible/roles/newrelic/tasks/main.yml b/lib/ansible/roles/newrelic/tasks/main.yml index 4d936dfb..7d9ed6eb 100644 --- a/lib/ansible/roles/newrelic/tasks/main.yml +++ b/lib/ansible/roles/newrelic/tasks/main.yml @@ -1,28 +1,38 @@ --- -- name: Install repository key - apt_key: url=https://download.newrelic.com/548C16BF.gpg state=present - sudo: yes +- name: Install repository key + apt_key: + url: https://download.newrelic.com/548C16BF.gpg + state: present + become: true -- name: Configure repository source list - apt_repository: repo='deb http://apt.newrelic.com/debian/ newrelic non-free' state=present - sudo: yes +- name: Configure repository source list + apt_repository: + repo: 'deb http://apt.newrelic.com/debian/ newrelic non-free' + state: present + become: true -- name: Install PHP agent and sys monitor - apt: name={{ item }} state=latest update_cache=yes - sudo: yes +- name: Install PHP agent and sys monitor + apt: + name: "{{ item }}" + state: latest + update_cache: yes + become: true with_items: - newrelic-php5 - newrelic-sysmond -- name: Configure PHP agent license key and app name - lineinfile: dest={{ php_conf_path }}/newrelic.ini regexp="^{{ item.key }}" line="{{ item.key }} = '{{ item.value }}'" - sudo: yes +- name: Configure PHP agent license key and app name + lineinfile: + dest: "{{ php_conf_path }}/newrelic.ini" + regexp: '^{{ item.key }}' + line: '{{ item.key }} = ''{{ item.value }}''' + become: true with_dict: newrelic.appname: "{{ domain }} ({{ stage }})" - newrelic.license: "{{ monitoring.newrelic }}" - notify: restart apache + newrelic.license: "{{ newrelic_api_key }}" + notify: restart apache -- name: Configure and start sys monitor - command: nrsysmond-config --set license_key={{ monitoring.newrelic }} - sudo: yes - notify: restart newrelic-sysmond +- name: Configure and start sys monitor + command: nrsysmond-config --set license_key={{ newrelic_api_key }} + become: true + notify: restart newrelic-sysmond diff --git a/lib/ansible/roles/node/tasks/main.yml b/lib/ansible/roles/node/tasks/main.yml index 0fd3fb60..fb9ece8b 100644 --- a/lib/ansible/roles/node/tasks/main.yml +++ b/lib/ansible/roles/node/tasks/main.yml @@ -1,13 +1,66 @@ --- -- name: Add Node apt-repository - apt_repository: repo='ppa:chris-lea/node.js' state=present update_cache=yes - sudo: yes - -- name: Install node - apt: pkg=nodejs state=present - sudo: yes - -- name: Install global node modules - command: npm install -g {{ item }} - with_items: "{{ node_modules }}" - sudo: yes +- name: Remove old node repository if necessary + apt_repository: + repo: 'ppa:chris-lea/node.js' + state: absent + register: old_node + become: true + +- name: Uninstall old node if necessary + apt: + pkg: nodejs + state: absent + become: true + when: old_node.changed + +- name: Find old node source lists to remove + shell: ls -1 /etc/apt/sources.list.d/*chris?lea?node_js*.list + register: old_source_lists + ignore_errors: yes + become: true + when: old_node.changed + +- name: Ensure removal of old node source lists + file: + path: "{{ item }}" + state: absent + with_items: "{{ old_source_lists.stdout_lines | default([]) }}" + become: true + +- name: Fetch Nodesource apt key (from control) + local_action: + module: get_url + dest: /tmp/ns4x.gpg.key + url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key + force: yes + +- name: Add Nodesource apt key + apt_key: + data: "{{ lookup('file', '/tmp/ns4x.gpg.key') }}" + state: present + become: true + +- name: Remove Nodesource apt key in tmp (from control) + local_action: + module: file + dest: /tmp/ns4x.gpg.key + state: absent + +- name: Add Nodesource 4.x repos + apt_repository: + repo: "{{item}}" + state: present + update_cache: yes + with_items: "{{ node_repos }}" + become: true + +- name: Install node + apt: + pkg: nodejs + state: present + become: true + +- name: Install global node modules + command: npm install -g {{ item }} + with_items: "{{ node_modules }}" + become: true diff --git a/lib/ansible/roles/node/vars/main.yml b/lib/ansible/roles/node/vars/main.yml index 0e5ab3f3..95713498 100644 --- a/lib/ansible/roles/node/vars/main.yml +++ b/lib/ansible/roles/node/vars/main.yml @@ -1,3 +1,6 @@ --- node_modules: - bower +node_repos: + - deb https://deb.nodesource.com/node_4.x trusty main + - deb-src https://deb.nodesource.com/node_4.x trusty main diff --git a/lib/ansible/roles/php-hardened/tasks/build.yml b/lib/ansible/roles/php-hardened/tasks/build.yml index 37e33755..0877931b 100644 --- a/lib/ansible/roles/php-hardened/tasks/build.yml +++ b/lib/ansible/roles/php-hardened/tasks/build.yml @@ -1,37 +1,48 @@ --- -- name: Fetch stable source - get_url: url={{ suhosin.url }} dest=/tmp/src-suhosin.tar.gz validate_certs=False - sudo: yes - -- name: Hash source for checksum - shell: sha1sum /tmp/src-suhosin.tar.gz - register: suhosin_shahash - sudo: yes - -- debug: var=suhosin_shahash.stdout - -- fail: msg="Suhosin source failed SHA1 hash" - when: suhosin_shahash.stdout.find('{{ suhosin.sha }}') == -1 - -- name: Expand source package - unarchive: src=/tmp/src-suhosin.tar.gz dest=/tmp/ copy=no - sudo: yes - -- shell: ls -1d /tmp/suhosin* - register: suhosin_srcdir - sudo: yes - -- debug: var=suhosin_srcdir.stdout - -- name: Compile and install extension - command: "{{ item }} chdir={{ suhosin_srcdir.stdout }}" - sudo: yes +- name: Fetch stable source + get_url: + url: "{{ suhosin.url }}" + dest: /tmp/src-suhosin.tar.gz + validate_certs: False + become: true + +- name: Hash source for checksum + shell: sha1sum /tmp/src-suhosin.tar.gz + register: suhosin_shahash + become: true + +- debug: + var: suhosin_shahash.stdout + +- fail: + msg: "Suhosin source failed SHA1 hash" + when: suhosin_shahash.stdout.find('{{ suhosin.sha }}') == -1 + +- name: Expand source package + unarchive: + src: /tmp/src-suhosin.tar.gz + dest: /tmp/ + copy: no + become: true + +- shell: ls -1d /tmp/suhosin* + register: suhosin_srcdir + become: true + +- debug: + var: suhosin_srcdir.stdout + +- name: Compile and install extension + command: "{{ item }}" + args: + chdir: "{{ suhosin_srcdir.stdout }}" + become: true with_items: - phpize - ./configure - make - make install -- name: Clean up after ourselves - shell: rm -rf /tmp/*suhosin* - sudo: yes +- name: Clean up after ourselves + shell: rm -rf /tmp/*suhosin* + become: true diff --git a/lib/ansible/roles/php-hardened/tasks/main.yml b/lib/ansible/roles/php-hardened/tasks/main.yml index e402abd6..d7ecab7e 100644 --- a/lib/ansible/roles/php-hardened/tasks/main.yml +++ b/lib/ansible/roles/php-hardened/tasks/main.yml @@ -1,6 +1,10 @@ --- -- name: Disable allow_url_fopen - lineinfile: dest=/etc/php5/apache2/php.ini backup=yes regexp='^[;# ]*allow_url_fopen' line='allow_url_fopen = Off' - sudo: yes +- name: Disable allow_url_fopen + lineinfile: + dest: /etc/php5/apache2/php.ini + backup: yes + regexp: '^[;# ]*allow_url_fopen' + line: 'allow_url_fopen = Off' + become: true -- include: suhosin.yml +- include: suhosin.yml diff --git a/lib/ansible/roles/php-hardened/tasks/suhosin.yml b/lib/ansible/roles/php-hardened/tasks/suhosin.yml index e58fbe2a..e3e65494 100644 --- a/lib/ansible/roles/php-hardened/tasks/suhosin.yml +++ b/lib/ansible/roles/php-hardened/tasks/suhosin.yml @@ -1,34 +1,41 @@ --- -- name: Install apg (for cryptkeys) - apt: name=apg state=present - sudo: yes - -- name: Generate session cryptkey - shell: apg -m 32 | sed 's/[^a-zA-Z0-9]//g' - register: generated_session_cryptkey - -- debug: var=generated_session_cryptkey - -- name: Is suhosin already installed? - stat: path={{ suhosin.ini }} - register: suhosin_installed - -- name: Find existing session cryptkey - shell: cat {{ suhosin.ini }} | sed -n -e 's/^suhosin\.session\.cryptkey = //p' - register: existing_session_cryptkey - when: suhosin_installed.stat.exists == True - -- debug: var=existing_session_cryptkey - when: suhosin_installed.stat.exists == True - -- include: build.yml - when: suhosin_installed.stat.exists == False - -- name: Generate ini config for extension - template: src=suhosin.ini.j2 dest={{ suhosin.ini }} - sudo: yes - -- name: Enable extension - command: php5enmod suhosin - sudo: yes - notify: restart apache +- name: Install apg (for cryptkeys) + apt: + name: apg + state: present + become: true + +- name: Generate session cryptkey + shell: apg -m 32 | sed 's/[^a-zA-Z0-9]//g' + register: generated_session_cryptkey + +- debug: + var: generated_session_cryptkey + +- name: Is suhosin already installed? + stat: + path: "{{ suhosin.ini }}" + register: suhosin_installed + +- name: Find existing session cryptkey + shell: cat {{ suhosin.ini }} | sed -n -e 's/^suhosin\.session\.cryptkey = //p' + register: existing_session_cryptkey + when: suhosin_installed.stat.exists == True + +- debug: + var: existing_session_cryptkey + when: suhosin_installed.stat.exists == True + +- include: build.yml + when: suhosin_installed.stat.exists == False + +- name: Generate ini config for extension + template: + src: suhosin.ini.j2 + dest: "{{ suhosin.ini }}" + become: true + +- name: Enable extension + command: php5enmod suhosin + become: true + notify: restart apache diff --git a/lib/ansible/roles/php/tasks/main.yml b/lib/ansible/roles/php/tasks/main.yml index 5903c062..fecae4d9 100644 --- a/lib/ansible/roles/php/tasks/main.yml +++ b/lib/ansible/roles/php/tasks/main.yml @@ -1,35 +1,51 @@ --- -- name: Install PHP packages - apt: pkg={{ item }} - with_items: "{{ php_packages }}" - sudo: yes +- name: Install PHP packages + apt: + pkg: "{{ item }}" + with_items: "{{ php_packages }}" + become: true -- name: Install PECL packages - command: pecl install {{ item if item is string else item.name + '-' + item.version }} creates={{ php_conf_path }}/{{ item if item is string else item.name }}.ini - with_items: "{{ pecl_packages }}" - sudo: yes +- name: Install PECL packages + command: pecl install {{ item if item is string else item.name + '-' + item.version }} + args: + creates: "{{ php_conf_path }}/{{ item if item is string else item.name }}.ini" + with_items: "{{ pecl_packages }}" + become: true -- name: Enable PECL packages - lineinfile: line="extension={{ item if item is string else item.name }}.so" - dest="{{ php_conf_path }}/{{ item if item is string else item.name }}.ini" - create=yes - with_items: "{{ pecl_packages }}" - sudo: yes +- name: Enable PECL packages + lineinfile: + line: "extension={{ item if item is string else item.name }}.so" + dest: "{{ php_conf_path }}/{{ item if item is string else item.name }}.ini" + create: yes + with_items: "{{ pecl_packages }}" + become: true -- name: Calculate PHP memory_limit - command: echo "{{ 256 if ansible_memtotal_mb > 2048 else 128 }}" - register: calc_php_memory_limit - ignore_errors: true - when: php__memory_limit is undefined +- name: Calculate PHP memory_limit + command: echo "{{ 256 if ansible_memtotal_mb > 2048 else 128 }}" + register: calc_php_memory_limit + ignore_errors: true + when: php__memory_limit is undefined -- name: Update php.ini's date.timezone - lineinfile: dest=/etc/php5/apache2/php.ini backup=yes regexp='^[;# ]*date\.timezone' line='date.timezone = "America/Chicago"' - sudo: yes +- name: Update php.ini's date.timezone + lineinfile: + dest: /etc/php5/apache2/php.ini + backup: yes + regexp: '^[;# ]*date\.timezone' + line: 'date.timezone = "America/Chicago"' + become: true -- name: Update php.ini's memory_limit - lineinfile: dest=/etc/php5/apache2/php.ini backup=yes regexp='^[;# ]*memory_limit' line='memory_limit = {{ calc_php_memory_limit.stdout if calc_php_memory_limit.stdout is defined else php__memory_limit }}M' - sudo: yes +- name: Update php.ini's memory_limit + lineinfile: + dest: /etc/php5/apache2/php.ini + backup: yes + regexp: '^[;# ]*memory_limit' + line: 'memory_limit = {{ calc_php_memory_limit.stdout if calc_php_memory_limit.stdout is defined else php__memory_limit }}M' + become: true -- name: Update php.ini's upload_max_filesize - lineinfile: dest=/etc/php5/apache2/php.ini backup=yes regexp='^[;# ]*upload_max_filesize' line='upload_max_filesize = 16M' - sudo: yes +- name: Update php.ini's upload_max_filesize + lineinfile: + dest: /etc/php5/apache2/php.ini + backup: yes + regexp: '^[;# ]*upload_max_filesize' + line: 'upload_max_filesize = 16M' + become: true diff --git a/lib/ansible/roles/pound/handlers/main.yml b/lib/ansible/roles/pound/handlers/main.yml index e1a41b2a..1238de9a 100644 --- a/lib/ansible/roles/pound/handlers/main.yml +++ b/lib/ansible/roles/pound/handlers/main.yml @@ -1,4 +1,6 @@ --- -- name: restart pound - service: name=pound state=restarted - sudo: true +- name: restart pound + service: + name: pound + state: restarted + become: true diff --git a/lib/ansible/roles/pound/tasks/main.yml b/lib/ansible/roles/pound/tasks/main.yml index c2c79e3d..6c38d154 100644 --- a/lib/ansible/roles/pound/tasks/main.yml +++ b/lib/ansible/roles/pound/tasks/main.yml @@ -1,45 +1,65 @@ --- # https://launchpad.net/~unleashedtech/+archive/ubuntu/pound-2.7 -- name: Register pound 2.7 ppa - apt_repository: repo='ppa:unleashedtech/pound-2.7' - sudo: true +- name: Register pound 2.7 ppa + apt_repository: + repo: 'ppa:unleashedtech/pound-2.7' + become: true -- name: Install pound package - apt: pkg=pound state=latest update_cache=yes - sudo: true +- name: Install pound package + apt: + pkg: pound + state: latest + update_cache: yes + become: true -- name: Copy pound configuration file - template: src=pound.cfg dest=/etc/pound/pound.cfg mode=0644 - notify: restart pound - sudo: true +- name: Copy pound configuration file + template: + src: pound.cfg + dest: /etc/pound/pound.cfg + mode: '0644' + notify: restart pound + become: true -- name: Copy SSL certificates - copy: src=./files/ssl/{{ item }}.{{ domain }}.pem dest=/etc/pound/{{ item }}.{{ domain }}.pem mode=0644 +- name: Copy SSL certificates + copy: + src: ./files/ssl/{{ item }}.{{ domain }}.pem + dest: /etc/pound/{{ item }}.{{ domain }}.pem + mode: '0644' with_items: - local - staging - production - notify: restart pound - sudo: true + notify: restart pound + become: true -- name: Enable pound - lineinfile: regexp='^startup=0' line='startup=1' dest=/etc/default/pound backup=yes - notify: restart pound - sudo: yes +- name: Enable pound + lineinfile: + regexp: '^startup=0' + line: 'startup=1' + dest: /etc/default/pound + backup: yes + notify: restart pound + become: true -- name: Configure HTTPS Forwarded Proto detection in Apache (2.2) - copy: content="SetEnvIf X-Forwarded-Proto ^https$ HTTPS=on\n" dest=/etc/apache2/conf.d/https-forwarded-proto.conf mode=0644 - notify: restart apache - when: apache_version.stdout == '2.2' - sudo: true +- name: Configure HTTPS Forwarded Proto detection in Apache (2.2) + copy: + content: "SetEnvIf X-Forwarded-Proto ^https$ HTTPS=on\n" + dest: /etc/apache2/conf.d/https-forwarded-proto.conf + mode: '0644' + notify: restart apache + when: apache_version.stdout == '2.2' + become: true -- name: Configure HTTPS Forwarded Proto detection in Apache (2.4) - copy: content="SetEnvIf X-Forwarded-Proto ^https$ HTTPS=on\n" dest=/etc/apache2/conf-available/https-forwarded-proto.conf mode=0644 - when: apache_version.stdout == '2.4' - sudo: true +- name: Configure HTTPS Forwarded Proto detection in Apache (2.4) + copy: + content: "SetEnvIf X-Forwarded-Proto ^https$ HTTPS=on\n" + dest: /etc/apache2/conf-available/https-forwarded-proto.conf + mode: '0644' + when: apache_version.stdout == '2.4' + become: true -- name: Enable canonical config (2.4) - command: a2enconf https-forwarded-proto - notify: restart apache - when: apache_version.stdout == '2.4' - sudo: yes +- name: Enable canonical config (2.4) + command: a2enconf https-forwarded-proto + notify: restart apache + when: apache_version.stdout == '2.4' + become: true diff --git a/lib/ansible/roles/prepare/tasks/main.yml b/lib/ansible/roles/prepare/tasks/main.yml index 5e4e53f4..71a48309 100644 --- a/lib/ansible/roles/prepare/tasks/main.yml +++ b/lib/ansible/roles/prepare/tasks/main.yml @@ -1,13 +1,17 @@ --- -- name: Test distro and version, as we only support specifically Ubuntu 14 - fail: msg="{{ansible_distribution}} {{ansible_distribution_version}} is not supported" - when: ansible_distribution != "Ubuntu" or ansible_distribution_major_version != "14" +- name: Test distro and version, as we only support specifically Ubuntu 14 + fail: + msg: "{{ansible_distribution}} {{ansible_distribution_version}} is not supported" + when: ansible_distribution != "Ubuntu" or ansible_distribution_major_version != "14" -- name: Test existance of evolution init script - stat: path=/etc/init.d/evolution-wordpress - register: evo_init +- name: Test existance of evolution init script + stat: + path: /etc/init.d/evolution-wordpress + register: evo_init -- name: Stop any existing evolution services - service: name=evolution-wordpress state=stopped - sudo: yes - when: evo_init.stat.exists == True +- name: Stop any existing evolution services + service: + name: evolution-wordpress + state: stopped + become: true + when: evo_init.stat.exists == True diff --git a/lib/ansible/roles/user/tasks/main.yml b/lib/ansible/roles/user/tasks/main.yml index d5d844e6..bac97265 100644 --- a/lib/ansible/roles/user/tasks/main.yml +++ b/lib/ansible/roles/user/tasks/main.yml @@ -1,42 +1,81 @@ --- -- name: Check for vagrant user - command: id -u vagrant - register: has_vagrant - ignore_errors: true - -- name: Add vagrant user to www-data - user: name=vagrant append=yes groups=www-data - when: has_vagrant.stdout - sudo: yes - -- name: Create deploy group - group: name=deploy state=present system=no - sudo: yes - -- name: Create deploy user - user: name=deploy append=yes group=deploy groups=www-data shell=/bin/bash comment="Created by Evolution WordPress" - sudo: yes - -- name: Grant sudo access to deploy user - copy: content="%deploy ALL=(ALL) NOPASSWD:ALL" dest=/etc/sudoers.d/deploy mode=0440 force=no - sudo: yes - -- name: Add www-data user to deploy - user: name=www-data append=yes groups=deploy - sudo: yes - -- name: Create /home/deploy/.ssh - file: path=/home/deploy/.ssh state=directory mode=0755 owner=deploy group=deploy - sudo: yes - -- name: Copy deploy private key - copy: src=./files/ssh/id_rsa dest=/home/deploy/.ssh/id_rsa owner=deploy group=deploy mode=0600 - sudo: yes - -- name: Copy deploy public key - copy: src=./files/ssh/id_rsa.pub dest=/home/deploy/.ssh/id_rsa.pub owner=deploy group=deploy mode=0600 - sudo: yes - -- name: Set deploy key as authorized key - copy: src=./files/ssh/id_rsa.pub dest=/home/deploy/.ssh/authorized_keys owner=deploy group=deploy mode=0600 - sudo: yes +- name: Check for vagrant user + command: id -u vagrant + register: has_vagrant + ignore_errors: true + +- name: Add vagrant user to www-data + user: + name: vagrant + append: yes + groups: www-data + when: has_vagrant.stdout + become: true + +- name: Create deploy group + group: + name: deploy + state: present + system: no + become: true + +- name: Create deploy user + user: + name: deploy + append: yes + group: deploy + groups: www-data + shell: /bin/bash + comment: "Created by Evolution WordPress" + become: true + +- name: Grant sudo access to deploy user + copy: + content: "%deploy ALL=(ALL) NOPASSWD:ALL" + dest: /etc/sudoers.d/deploy + mode: '0440' + force: no + become: true + +- name: Add www-data user to deploy + user: + name: www-data + append: yes + groups: deploy + become: true + +- name: Create /home/deploy/.ssh + file: + path: /home/deploy/.ssh + state: directory + mode: '0755' + owner: deploy + group: deploy + become: true + +- name: Copy deploy private key + copy: + src: ./files/ssh/id_rsa + dest: /home/deploy/.ssh/id_rsa + owner: deploy + group: deploy + mode: '0600' + become: true + +- name: Copy deploy public key + copy: + src: ./files/ssh/id_rsa.pub + dest: /home/deploy/.ssh/id_rsa.pub + owner: deploy + group: deploy + mode: '0600' + become: true + +- name: Set deploy key as authorized key + copy: + src: ./files/ssh/id_rsa.pub + dest: /home/deploy/.ssh/authorized_keys + owner: deploy + group: deploy + mode: '0600' + become: true diff --git a/lib/ansible/roles/varnish/handlers/main.yml b/lib/ansible/roles/varnish/handlers/main.yml index 441801a9..03535a55 100644 --- a/lib/ansible/roles/varnish/handlers/main.yml +++ b/lib/ansible/roles/varnish/handlers/main.yml @@ -1,4 +1,6 @@ --- -- name: restart varnish - service: name=varnish state=restarted - sudo: true +- name: restart varnish + service: + name: varnish + state: restarted + become: true diff --git a/lib/ansible/roles/varnish/tasks/main.yml b/lib/ansible/roles/varnish/tasks/main.yml index 43f70487..00445398 100644 --- a/lib/ansible/roles/varnish/tasks/main.yml +++ b/lib/ansible/roles/varnish/tasks/main.yml @@ -1,21 +1,29 @@ --- -- name: Install Varnish packages - apt: pkg={{ item }} state=present - with_items: "{{ varnish_packages }}" - sudo: yes +- name: Install Varnish packages + apt: + pkg: "{{ item }}" + state: present + with_items: "{{ varnish_packages }}" + become: true -- name: Create Varnish directory structure - file: path={{ item }} state=directory mode=0644 - sudo: yes +- name: Create Varnish directory structure + file: + path: "{{ item }}" + state: directory + mode: '0644' + become: true with_items: - /etc/varnish - /etc/varnish/conf.d - /etc/varnish/conf.d/fetch - /etc/varnish/conf.d/receive -- name: Copy Varnish configuration files - copy: src=etc-varnish/{{ item }} dest=/etc/varnish/{{ item }} mode=0644 - sudo: yes +- name: Copy Varnish configuration files + copy: + src: etc-varnish/{{ item }} + dest: /etc/varnish/{{ item }} + mode: '0644' + become: true with_items: - conf.d/fetch/wordpress.vcl - conf.d/receive/wordpress.vcl @@ -23,51 +31,69 @@ - custom.backend.vcl - production.vcl -- name: Copy Varnish script - copy: src=etc-default-varnish dest=/etc/default/varnish mode=0644 - sudo: yes +- name: Copy Varnish script + copy: + src: etc-default-varnish + dest: /etc/default/varnish + mode: '0644' + become: true notify: - restart apache - restart varnish -- stat: path=/etc/apache2/mods-enabled/rpaf.load - register: rpaf_load +- stat: + path: /etc/apache2/mods-enabled/rpaf.load + register: rpaf_load -- name: Disable apache module "rpaf" (if installed) - command: a2dismod rpaf - when: rpaf_load.stat.exists - sudo: yes - notify: restart apache +- name: Disable apache module "rpaf" (if installed) + command: a2dismod rpaf + when: rpaf_load.stat.exists + become: true + notify: restart apache -- name: Configure apache module "remoteip" - copy: src=remoteip.conf dest=/etc/apache2/mods-available/remoteip.conf mode=0644 - sudo: yes +- name: Configure apache module "remoteip" + copy: + src: remoteip.conf + dest: /etc/apache2/mods-available/remoteip.conf + mode: '0644' + become: true -- name: Enable apache module "remoteip" - command: a2enmod remoteip - sudo: yes - notify: restart apache +- name: Enable apache module "remoteip" + command: a2enmod remoteip + become: true + notify: restart apache -- name: Change Apache NameVirtualHost 80 => 8080 (2.2) - lineinfile: regexp='^NameVirtualHost' line='NameVirtualHost *:8080' dest=/etc/apache2/ports.conf backup=yes +- name: Change Apache NameVirtualHost 80 => 8080 (2.2) + lineinfile: + regexp: '^NameVirtualHost' + line: 'NameVirtualHost *:8080' + dest: /etc/apache2/ports.conf + backup: yes notify: - restart apache - restart varnish - when: apache_version.stdout == '2.2' - sudo: yes + when: apache_version.stdout == '2.2' + become: true -- name: Change Apache Listen 80 => 8080 - lineinfile: regexp='^Listen' line='Listen 8080' dest=/etc/apache2/ports.conf backup=yes +- name: Change Apache Listen 80 => 8080 + lineinfile: + regexp: '^Listen' + line: 'Listen 8080' + dest: /etc/apache2/ports.conf + backup: yes notify: - restart apache - restart varnish - sudo: yes + become: true -- name: Change VirtualHosts from 80 => 8080 - lineinfile: regexp='^' line='' dest=/etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf +- name: Change VirtualHosts from 80 => 8080 + lineinfile: + regexp: '^' + line: '' + dest: /etc/apache2/sites-available/{{ "%03d" | format(item.key_1) }}-{{ item.value }}.{{ domain }}.conf with_nested_dict: "{{ apache_vhosts }}" - when: item.key_0 == stage + when: item.key_0 == stage notify: - restart apache - restart varnish - sudo: true + become: true diff --git a/lib/ansible/roles/wp-cli/tasks/main.yml b/lib/ansible/roles/wp-cli/tasks/main.yml index 969a7e76..c4d87894 100644 --- a/lib/ansible/roles/wp-cli/tasks/main.yml +++ b/lib/ansible/roles/wp-cli/tasks/main.yml @@ -1,11 +1,30 @@ # see evolution/wordpress#126 -- name: Fetch latest wp-cli (on control machine) - local_action: get_url dest=/tmp/wp-cli url=https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar force=yes +- name: Fetch latest wp-cli (from control) + local_action: + module: get_url + dest: /tmp/wp-cli + url: https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar + force: yes -- name: Copy latest wp-cli to remote - copy: src=/tmp/wp-cli dest=/usr/local/bin/wp force=yes mode=0755 owner=deploy group=deploy - sudo: yes +- name: Copy latest wp-cli to remote + copy: + src: /tmp/wp-cli + dest: /usr/local/bin/wp + force: yes + mode: '0755' + owner: deploy + group: deploy + become: true -- name: Ensure deploy can both write and execute wp-cli - file: path=/usr/local/bin group=deploy mode=0775 - sudo: yes +- name: Ensure deploy can both write and execute wp-cli + file: + path: /usr/local/bin + group: deploy + mode: '0775' + become: true + +- name: Remove latest wp-cli in tmp (from control) + local_action: + module: file + dest: /tmp/wp-cli + state: absent diff --git a/lib/node/nightmare.js b/lib/node/nightmare.js new file mode 100644 index 00000000..fe434248 --- /dev/null +++ b/lib/node/nightmare.js @@ -0,0 +1,26 @@ +var fs = require('fs-extra'); +var Nightmare = require('nightmare'); + +module.exports = function (options) { + // define a data path and ensure an empty dir + var dataPath = `${process.cwd()}/.browserData/`; + fs.emptyDirSync(dataPath); + + // specify empty user data path, unless otherwise specified + if (!options) { + options = { + paths: { + userData: dataPath + } + }; + } else if (!options.hasOwnProperty('paths')) { + options.paths = { + userData: dataPath + }; + } else if (!options.paths.hasOwnProperty('userData')) { + options.paths.userData = dataPath; + } + + // instantiate and return with given options + return new Nightmare(options); +} diff --git a/lib/yeoman/bin/postinstall b/lib/yeoman/bin/postinstall deleted file mode 100755 index 2449977b..00000000 --- a/lib/yeoman/bin/postinstall +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env node - -var fs = require('fs'); -var path = require('path'); -var util = require('util'); - -var cwd = process.cwd(); -var from = path.join(cwd, 'bower_components', 'wordpress'); -var to = path.join(cwd, 'web', 'wp'); - -console.log('Evolution WordPress - Bower `postinstall` Script\n'); - -// Ensure `bower_components/wordpress` has been installed -if (!fs.existsSync(from)) { - throw new Error(util.format('WordPress has not been installed to `%s`', from)); -} - -var toThemeDir = path.join(cwd, 'web', 'wp-content', 'themes'); - -if (!fs.existsSync(toThemeDir)) { - var parts = path.relative(cwd, toThemeDir).split(path.sep); - var toThemePartDir = cwd; - - while (parts.length) { - toThemePartDir = path.join(toThemePartDir, parts.shift()); - - console.info('Creating `%s`...', toThemePartDir); - fs.mkdirSync(toThemePartDir); - } -} - -var fromThemeDir = path.join(from, 'wp-content', 'themes'); -var latestTheme = null; -var themeExists = function (parents, name) { - var exists = false; - parents.forEach (function (directory) { - if (fs.existsSync( path.join(directory, name) )) { - exists = true; - } - }); - return exists; -}; -var words = function (num) { - var ones = ['','one','two','three','four','five','six','seven','eight','nine']; - var tens = ['','','twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety']; - var teens = ['ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen']; - - if (num == 0) return 'zero'; - else if (num < 10) return ones[num]; - else if (num >= 10 && num < 20) return teens[ num - 10 ]; - else { - return tens[ Math.floor(num/10) ] + ones[ num % 10 ]; - } -} - -for (var testYear = new Date().getFullYear(); testYear > 2009; testYear--) { - var testName = words(testYear.toString().substr(0,2)) - + words(testYear.toString().substr(2,2)) - ; - - if (themeExists([toThemeDir,fromThemeDir], testName)) { - latestTheme = testName; - break; - } - else { - console.log('No theme ' + testName); - } -} - -if (latestTheme === null) { - if (themeExists([fromThemeDir], 'default')) { - latestTheme = 'default'; - } - else { - throw new Error(util.format('Could not find WordPress core theme in `%s`', fromThemeDir)); - } -} - -if (themeExists([toThemeDir], latestTheme)) { - console.log('Existing core theme ' + latestTheme); -} -else { - console.log('Found theme ' + latestTheme); - - var fromTheme = path.join(fromThemeDir, latestTheme); - var toTheme = path.join(toThemeDir, latestTheme); - - console.log('Moving theme `%s` to `%s`...', path.relative(cwd, fromTheme), path.relative(cwd, toTheme)); - fs.renameSync(fromTheme, toTheme); -} - -// Remove existing `web/wp` folder -if (fs.existsSync(to)) { - console.info('Removing `%s`...', to); - - var removeDir = function(dir) { - var nodes = fs.readdirSync(dir).map(function(node) { - return path.join(dir, node); - }); - - nodes.forEach(function(node) { - var stats = fs.lstatSync(node); - - if (stats.isDirectory()) { - removeDir(node); - } else { - fs.unlinkSync(node); - } - }); - - fs.rmdirSync(dir); - }; - - removeDir(to); -} - -console.info('Renaming `%s` to `%s`...', path.relative(cwd, from), path.relative(cwd, to)); -fs.renameSync(from, to); - -console.log('Done!'); diff --git a/lib/yeoman/index.js b/lib/yeoman/index.js index 634bf456..37223564 100644 --- a/lib/yeoman/index.js +++ b/lib/yeoman/index.js @@ -1,607 +1,675 @@ -'use strict'; +'use strict' var chalk = require('chalk'); var crypto = require('crypto'); var fs = require('fs-extra'); var glob = require('glob'); -var path = require('path'); var keygen = require('ssh-keygen'); +var path = require('path'); var request = require('request'); -var yeoman = require('yeoman-generator'); +var semver = require('semver'); var util = require('util'); +var yaml = require('js-yaml'); +var yeoman = require('yeoman-generator'); -var WordpressGenerator = yeoman.generators.Base.extend({ - init: function() { - this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../../package.json'))); - this.prompts = []; - - this.option('dev', { - desc: 'Dev-mode for localized development of the generator', - defaults: false, - }); - - this.on('end', function() { - this.installDependencies({ - bower: true, - npm: false, - skipInstall: this.options['skip-install'], - skipMessage: true, - callback: function() { - this.log.write(); - this.log.ok('All done! Run ' + chalk.yellow('vagrant up') + ' to get started!'); - }.bind(this) +var WordpressGenerator = yeoman.Base.extend({ + initializing: { + init: function() { + this.pkg = this.fs.readJSON(path.join(__dirname, '../../package.json')); + this.prompts = []; + + this.option('dev', { + desc: 'Dev-mode for localized development of the generator', + defaults: false, }); - }); - this.sourceRoot(path.join(__dirname, 'templates')); - }, + this.on('end', function() { + this.log.write(); + this.log.ok('All done! Run ' + chalk.yellow('vagrant up') + ' to get started!'); + }); - welcome: function() { - var message = this.readFileAsString(path.join(__dirname, 'welcome.txt')); + this.sourceRoot(path.join(__dirname, 'templates')); + }, - message = message.replace(/./g, function(match) { - return /\w/.test(match) ? chalk.yellow(match) : chalk.cyan(match); - }); + welcome: function() { + var message = this.fs.read(path.join(__dirname, 'welcome.txt')); - this.log.writeln(message); - }, + message = message.replace(/./g, function(match) { + return /\w/.test(match) ? chalk.yellow(match) : chalk.cyan(match); + }); - checkDeps: function() { - if(process.env.CI) { - return true; - } + this.log.writeln(message); + }, - var done = this.async(); - var deps = ['sshpass', 'bundler', 'ansible', 'bower', 'npm']; - var missing = []; - var exited = function(dependency, err) { - deps.pop(); + upgradeNotice: function() { + var upgradeVersion; - if (err) { missing.push(dependency); } + // get existing version from group vars (if available) + try { + var groupVars = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all'))); - if (!deps.length) { - if (missing.length) { - err = new Error('Missing prerequisites'); - this.log.error('Could not find dependencies:' + chalk.red("\n\t- "+missing.join("\n\t- ")) ); + if (groupVars.hasOwnProperty('evolve_version')) { + upgradeVersion = groupVars.evolve_version; } - done(err); - } - } + } catch(e) {}; - this.log.info('Checking for installed prerequisites...'); - deps.forEach(function (dependency) { - this.emit('dependencyCheck'+dependency); + // failing that, get existing version from bower json (if available) + if (!upgradeVersion) { + try { + var bowerJson = this.fs.readJSON(path.join(this.env.cwd, 'bower.json')); - this - .spawnCommand('which', [dependency], {cwd: this.env.cwd, env: process.env}) - .on('error', exited.bind(this, dependency)) - .on('exit', this.emit.bind(this, 'dependencyCheck'+dependency+':end')) - .on('exit', exited.bind(this, dependency)) - ; - }.bind(this)); - }, + if (bowerJson.dependencies.hasOwnProperty('evolution-wordpress')) { + upgradeVersion = bowerContents.dependencies['evolution-wordpress']; + } + } catch (e) {}; + } - promptForPrivate: function() { - var bowerFile = path.join(this.env.cwd, 'bower.json'); - var choices = [ - { - name: 'Private', - value: true, - }, - { - name: 'Public', - value: false, - }, - ]; - var existing = function() { - try { - var bower = JSON.parse(this.readFileAsString(bowerFile)); + // show upgrade notice for evolution 1.x sites + if (upgradeVersion && !semver.satisfies(upgradeVersion, '2.x')) { + this.log([ + chalk.bgRed.bold.underline('!!! Evolution 2.0 Upgrade Notice !!!'), + chalk.green('You are regenerating an Evolution 1.0 site with ' + chalk.bold('significant and potentially breaking changes') + '...'), + chalk.yellow.bold('After regenerating this site, ' + chalk.underline('you MUST re-provision any existing servers before deployment') + ' or they may not continue to function!'), + '', + ].join("\n")); + } + }, - return !bower.private; - } catch(e) {}; - }.bind(this); + checkDeps: function() { + if(process.env.CI) { + return true; + } - if(existing()) { - choices.reverse(); - } + // binaries, args, and callbacks for determining dependencies + var deps = [ + { bin: 'ansible', args: ['--version'], test: function (out) { var ver = out.match(/ansible ((?:\d+[.]){2}\d+)/)[1]; return semver.satisfies(ver, '>=2.0'); } }, + { bin: 'bundler', args: ['--version'] }, + { bin: 'npm', args: ['--version'] }, + { bin: 'sshpass', args: ['-V'] }, + { bin: 'yo', args: ['--version'], test: function (out) { var ver = out.match(/((?:\d+[.]){2}\d+)/)[1]; return semver.satisfies(ver, '>=1.7.1'); } }, + ]; + + var done = this.async(); + var missing = []; + var exited = function(dependency, proc, err) { + deps.pop(); + + // on error, note missing dep + if (err) { + missing.push('Could not find ' + dependency.bin); + } + // run test, when provided, against stdout/err + else { + if (dependency.hasOwnProperty('test')) { + var output = proc.stdout.read() + proc.stderr.read(); + output = output.replace(/\s+/g, ' '); + + // warn (rather than bail) on dep with insufficient version + if (!dependency.test(output)) { + this.log.skip('Warning: found ' + chalk.green(dependency.bin) + ', with insufficient version: ' + chalk.red(output)); + } + } + } - this.prompts.push({ - type: 'list', - name: 'private', - message: 'This repository is', - choices: choices, - }); - }, + if (!deps.length) { + if (missing.length) { + err = new Error('Unsatisfied dependencies'); + this.log.error('Could not satisfy dependencies:' + chalk.red("\n\t- "+missing.join("\n\t- ")) ); + } + done(err); + } + } - promptForSsl: function() { - var defaultSsl = true; + this.log.info('Checking for installed prerequisites...'); + deps.forEach(function (dependency) { + this.emit('dependencyCheck'+dependency.bin); + + var proc = this.spawnCommand(dependency.bin, dependency.args, {cwd: this.env.cwd, env: process.env, stdio: 'pipe'}); + proc + .on('error', exited.bind(this, dependency, null)) + .on('exit', this.emit.bind(this, 'dependencyCheck'+dependency.bin+':end')) + .on('exit', exited.bind(this, dependency, proc)) + ; + }.bind(this)); + }, + }, + + prompting: { + promptForPrivate: function() { + var choices = [ + { + name: 'Private', + value: true, + }, + { + name: 'Public', + value: false, + }, + ]; + var existing = function() { + try { + var contents = this.fs.read(path.join(this.env.cwd, '.gitignore')); - try { - var provisionYml = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'provision.yml')); - defaultSsl = !!provisionYml.match(/^\s*-\s+pound/m); - } catch(e) {}; - - var choices = [ - { - name: 'Yes, HTTPS (SSL)', - value: true, - }, - { - name: 'No, HTTP-only', - value: false, - }, - ]; - - if(!defaultSsl) { - choices.reverse(); - } + return contents.match(/lib\/ansible\/files\/ss(?:h|l)/); + } catch(e) {}; + }.bind(this); - this.prompts.push({ - type: 'list', - name: 'ssl', - message: 'Will this site serve HTTPS (SSL) traffic?', - choices: choices - }); + if(existing()) { + choices.reverse(); + } - if(glob.sync('lib/ansible/files/ssl/*.pem').length) { this.prompts.push({ type: 'list', - name: 'sslOverwrite', - message: 'Overwrite existing SSL certificates?', - when: function (props) { - return props.ssl; - }, - choices: [ - { - name: 'Do not overwrite', - value: false, - }, - { - name: 'Overwrite', - value: true, - }, - ] + name: 'private', + message: 'This repository is', + choices: choices, }); - } - }, + }, - promptForSSHOverwrite: function() { - if(glob.sync('lib/ansible/files/ssh/id_rsa*').length) { - this.prompts.push({ - type: 'list', - name: 'sshOverwrite', - message: 'Overwrite existing SSH keys?', - choices: [ - { - name: 'Do not overwrite', - value: false, - }, - { - name: 'Overwrite', - value: true, - }, - ] - }); - } - }, + promptForSsl: function() { + var defaultSsl = true; - promptForName: function() { - var existing = function() { try { - var bower = JSON.parse(this.readFileAsString(path.join(this.env.cwd, 'bower.json'))); - - return bower.name; + var provisionYml = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'provision.yml'))); + if (provisionYml[0].roles.indexOf('pound') == -1) { + if (!provisionYml[0].roles.find(function(el) { return el.hasOwnProperty('role') && el.role == 'pound'; })) { + defaultSsl = false; + } + } } catch(e) {}; - }.bind(this); - - this.prompts.push({ - required: true, - type: 'text', - name: 'name', - message: 'Repository name (e.g. MySite)', - default: function() { - return existing() || path.basename(this.env.cwd); - }.bind(this) - }); - }, - promptForDomain: function() { - var existing = function() { - try { - var match = this.readFileAsString(path.join(this.env.cwd, 'Vagrantfile')) - .match(/box.vm.hostname[ ]*=[ ]*"local.([^"]+)"/); + var choices = [ + { + name: 'Yes, HTTPS (SSL)', + value: true, + }, + { + name: 'No, HTTP-only', + value: false, + }, + ]; - return match[1]; - } catch(e) {}; - }.bind(this); - - this.prompts.push({ - required: true, - type: 'text', - name: 'domain', - message: 'Domain name (e.g. mysite.com)', - default: function () { - return existing() || path.basename(this.env.cwd).toLowerCase(); - }.bind(this), - validate: function(input) { - if (/^[\w-]+\.\w+(?:\.\w{2,3})?$/.test(input)) { - return true; - } else if (!input) { - return "Domain is required"; - } + if(!defaultSsl) { + choices.reverse(); + } + + this.prompts.push({ + type: 'list', + name: 'ssl', + message: 'Will this site serve HTTPS (SSL) traffic?', + choices: choices + }); - return chalk.yellow(input) + ' does not appear to be a domain'; + if(glob.sync('lib/ansible/files/ssl/*.pem').length) { + this.prompts.push({ + type: 'list', + name: 'sslOverwrite', + message: 'Overwrite existing SSL certificates?', + when: function (props) { + return props.ssl; + }, + choices: [ + { + name: 'Do not overwrite', + value: false, + }, + { + name: 'Overwrite', + value: true, + }, + ] + }); } - }); - }, + }, + + promptForSSHOverwrite: function() { + if(glob.sync('lib/ansible/files/ssh/id_rsa*').length) { + this.prompts.push({ + type: 'list', + name: 'sshOverwrite', + message: 'Overwrite existing SSH keys?', + choices: [ + { + name: 'Do not overwrite', + value: false, + }, + { + name: 'Overwrite', + value: true, + }, + ] + }); + } + }, - promptForWww: function() { - var choices = [ - { - name: 'No, just the domain', - value: false, - }, - { - name: 'Yes, www is preferred', - value: true, - }, - ]; - var existing = function() { - try { - var match = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all')) - .match(/www[ ]*:[ ]*(true)/i); + promptForName: function() { + var existing = function() { + try { + var match = this.fs.read(path.join(this.env.cwd, 'lib/capistrano/deploy.rb')) + .match(/set :application,[ ]*["']([^"']+)/); - return match[1]; - } catch(e) {}; - }.bind(this); + return match[1]; + } catch(e) {}; + }.bind(this); - if(existing()) { - choices.reverse(); - } + this.prompts.push({ + required: true, + type: 'text', + name: 'name', + message: 'Repository name (e.g. MySite)', + default: function() { + return existing() || path.basename(this.env.cwd); + }.bind(this) + }); + }, - this.prompts.push({ - required: true, - type: 'list', - name: 'www', - message: 'Should this site enforce a www subdomain in production? (e.g. www.mysite.com)', - choices: choices - }); - }, + promptForDomain: function() { + var existing = function() { + try { + var match = this.fs.read(path.join(this.env.cwd, 'Vagrantfile')) + .match(/box.vm.hostname[ ]*=[ ]*"local.([^"]+)"/); - promptForNewrelic: function() { - var existing = function() { - try { - var match = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all')) - .match(/newrelic[ ]*:[ ]*(?:'|")?([^'"\s]+)/i); + return match[1]; + } catch(e) {}; + }.bind(this); - return match[1]; - } catch(e) {}; - }.bind(this); - - this.prompts.push({ - required: true, - type: 'text', - name: 'newrelic', - message: 'New Relic license key (leave blank to disable)', - default: function () { - return existing() || ''; - }.bind(this) - }); - }, + this.prompts.push({ + required: true, + type: 'text', + name: 'domain', + message: 'Domain name (e.g. mysite.com)', + default: function () { + return existing() || path.basename(this.env.cwd).toLowerCase(); + }.bind(this), + validate: function(input) { + if (/^[\w-]+\.\w+(?:\.\w{2,3})?$/.test(input)) { + return true; + } else if (!input) { + return "Domain is required"; + } - promptForDatadog: function() { - var existing = function() { - try { - var match = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all')) - .match(/datadog_api_key[ ]*:[ ]*(?:'|")?([^'"\s]+)/i); + return chalk.yellow(input) + ' does not appear to be a domain'; + } + }); + }, - return match[1]; - } catch(e) {}; - }.bind(this); - - this.prompts.push({ - required: true, - type: 'text', - name: 'datadog', - message: 'Datadog license key (leave blank to disable)', - default: function () { - return existing() || ''; - }.bind(this) - }); - }, + promptForWww: function() { + var choices = [ + { + name: 'No, just the domain', + value: false, + }, + { + name: 'Yes, www is preferred', + value: true, + }, + ]; + var existing = function() { + try { + var groupVars = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all'))); - promptForRoles: function() { - try { - var provisionYml = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'provision.yml')); + return groupVars.hasOwnProperty('www') && !!groupVars.www; + } catch(e) {}; + }.bind(this); - WordpressGenerator.roles.forEach(function (role, index) { - var regex = new RegExp('^\\s*-\\s+' + role.value.split(' ')[0], 'm'); + if(existing()) { + choices.reverse(); + } - if (!regex.exec(provisionYml)) { - WordpressGenerator.roles[index].checked = false; - } + this.prompts.push({ + required: true, + type: 'list', + name: 'www', + message: 'Should this site enforce a www subdomain in production? (e.g. www.mysite.com)', + choices: choices }); - } catch(e) {}; - - this.prompts.push({ - required: true, - type: 'checkbox', - name: 'roles', - message: 'Optional features', - choices: WordpressGenerator.roles, - }); - }, + }, - promptForWordPress: function() { - var existing = function() { - try { - var file = this.readFileAsString(path.join(this.env.cwd, 'web', 'wp', 'wp-includes', 'version.php')); - var version = file.match(/\$wp_version\s=\s['"]([^'"]+)/); + promptForNewrelic: function() { + var existing = function() { + try { + var groupVars = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all'))); - if (version.length) { - return version[1]; - } - } catch(e) {} - }.bind(this); + if (groupVars.hasOwnProperty('monitoring') && groupVars.monitoring.hasOwnProperty('newrelic')) { + return groupVars.monitoring.newrelic; + } - var done = this.async(); + if (groupVars.hasOwnProperty('newrelic_api_key')) { + return groupVars.newrelic_api_key; + } + } catch(e) {}; + }.bind(this); - WordpressGenerator.latest('wordpress', 'wordpress', function(err, tag) { this.prompts.push({ + required: true, type: 'text', - name: 'wordpress', - message: 'WordPress version', - default: function() { - return existing() || tag || '4.7'; - } + name: 'newrelic', + message: 'New Relic license key (leave blank to disable)', + default: function () { + return existing() || ''; + }.bind(this) }); + }, + + promptForDatadog: function() { + var existing = function() { + try { + var groupVars = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all'))); + if (groupVars.hasOwnProperty('datadog_api_key')) { + return groupVars.datadog_api_key; + } + } catch(e) {}; + }.bind(this); - done(); - }.bind(this)); - }, + this.prompts.push({ + required: true, + type: 'text', + name: 'datadog', + message: 'Datadog license key (leave blank to disable)', + default: function () { + return existing() || ''; + }.bind(this) + }); + }, - promptForAutoUpdate: function() { - var choices = [ - { - name: 'No', - value: false, - }, - { - name: 'Yes, but only minor releases (eg, 4.6.1 => 4.6.3)', - value: 'minor', - }, - { - name: 'Yes, including major releases (eg, 4.6.1 => 4.7.1)', - value: 'major', - }, - ]; - - var existing = function() { + promptForRoles: function() { try { - var match = this.readFileAsString(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all')) - .match(/wordpress__autoupdate[ ]*:[ ]*(?:'|")?(minor|major)/i); - return match[1]; + var provisionYml = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'provision.yml'))); + + WordpressGenerator.roles.forEach(function (role, index) { + var roleName = role.value.split(' ')[0]; + if (provisionYml[0].roles.indexOf(roleName) != -1) { + WordpressGenerator.roles[index].checked = true; + } + else if (provisionYml[0].roles.find(function(el) { return el.hasOwnProperty('role') && el.role == roleName; })) { + WordpressGenerator.roles[index].checked = true; + } + else { + WordpressGenerator.roles[index].checked = false; + } + }); } catch(e) {}; - }.bind(this); - var majority = existing(); - if (majority == 'major') { - choices[0] = choices.splice(2, 1, choices[0])[0]; - } else if (majority == 'minor') { - choices.push(choices.shift()); - } + this.prompts.push({ + required: true, + type: 'checkbox', + name: 'roles', + message: 'Optional features', + choices: WordpressGenerator.roles, + }); + }, - this.prompts.push({ - required: true, - type: 'list', - name: 'wp_autoupdate', - message: 'Autuomatically apply (and commit to git) WordPress updates, including plugins and themes?', - choices: choices - }); - }, + promptForWordPress: function() { + var existing = function() { + try { + var file = this.fs.read(path.join(this.env.cwd, 'web', 'wp', 'wp-includes', 'version.php')); + var version = file.match(/\$wp_version\s=\s['"]([^'"]+)/); - promptForTablePrefix: function() { - var existing = function() { - try { - var config = this.readFileAsString(path.join(this.env.cwd, 'web', 'wp-config.php')); - var prefix = config.match(/\$table_prefix\s*=\s*['"]([^'"]+)/); + if (version.length) { + return version[1]; + } + } catch(e) {} + }.bind(this); + + var done = this.async(); + + WordpressGenerator.latest('wordpress', 'wordpress', function(err, tag) { + this.prompts.push({ + type: 'text', + name: 'wordpress', + message: 'WordPress version', + default: function() { + return existing() || tag || '4.7'; + } + }); - if (prefix.length) { - return prefix[1]; - } - } catch(e) {} - }.bind(this); - - this.prompts.push({ - type: 'text', - name: 'prefix', - message: 'WordPress table prefix', - default: function() { - return existing() || 'wp_'; - } - }); - }, + done(); + }.bind(this)); + }, - promptForDatabase: function() { - var done = this.async(); - var existing = function(constant) { - try { - var config = this.readFileAsString(path.join(this.env.cwd, 'web', 'wp-config.php')); - var regex = new RegExp(constant + '[\'"],\\s*[\\w:(]*[\'"]([^\'"]+)'); - var matches = regex.exec(config); + promptForAutoUpdate: function() { + var choices = [ + { + name: 'No', + value: false, + }, + { + name: 'Yes, but only minor releases (eg, 4.6.1 => 4.6.3)', + value: 'minor', + }, + { + name: 'Yes, including major releases (eg, 4.6.1 => 4.7.1)', + value: 'major', + }, + ]; - return matches && matches[1]; - } catch(e) {} - }.bind(this); + var existing = function() { + try { + var groupVars = yaml.safeLoad(this.fs.read(path.join(this.env.cwd, 'lib', 'ansible', 'group_vars', 'all'))); + if (groupVars.hasOwnProperty('wordpress__autoupdate') && groupVars.wordpress__autoupdate.match('/^(major|minor)$/')) { + return groupVars.wordpress__autoupdate; + } + } catch(e) {}; + }.bind(this); + + var majority = existing(); + if (majority == 'major') { + choices[0] = choices.splice(2, 1, choices[0])[0]; + } else if (majority == 'minor') { + choices.push(choices.shift()); + } - crypto.randomBytes(12, function(err, buffer) { this.prompts.push({ - type: 'text', - name: 'DB_NAME', - message: 'Database name', - default: function() { return existing('DB_NAME') || 'wordpress'; } + required: true, + type: 'list', + name: 'wp_autoupdate', + message: 'Autuomatically apply (and commit to git) WordPress updates, including plugins and themes?', + choices: choices }); + }, - this.prompts.push({ - type: 'text', - name: 'DB_USER', - message: 'Database user', - default: function() { return existing('DB_USER') || 'wordpress'; } - }); + promptForTablePrefix: function() { + var existing = function() { + try { + var config = this.fs.read(path.join(this.env.cwd, 'web', 'wp-config.php')); + var prefix = config.match(/\$table_prefix\s*=\s*['"]([^'"]+)/); - this.prompts.push({ - type: 'text', - name: 'DB_PASSWORD', - message: 'Database password', - default: function() { return existing('DB_PASSWORD') || buffer.toString('base64'); } - }); + if (prefix.length) { + return prefix[1]; + } + } catch(e) {} + }.bind(this); this.prompts.push({ type: 'text', - name: 'DB_HOST', - message: 'Database host', - default: function() { return existing('DB_HOST') || '127.0.0.1'; } + name: 'prefix', + message: 'WordPress table prefix', + default: function() { + return existing() || 'wp_'; + } }); + }, + + promptForDatabase: function() { + var done = this.async(); + var existing = function(constant) { + try { + var config = this.fs.read(path.join(this.env.cwd, 'web', 'wp-config.php')); + var regex = new RegExp(constant + '[\'"],\\s*[\\w:(]*[\'"]([^\'"]+)'); + var matches = regex.exec(config); + + return matches && matches[1]; + } catch(e) {} + }.bind(this); + + crypto.randomBytes(12, function(err, buffer) { + this.prompts.push({ + type: 'text', + name: 'DB_NAME', + message: 'Database name', + default: function() { return existing('DB_NAME') || 'wordpress'; } + }); - done(); - }.bind(this)); - }, + this.prompts.push({ + type: 'text', + name: 'DB_USER', + message: 'Database user', + default: function() { return existing('DB_USER') || 'wordpress'; } + }); + + this.prompts.push({ + type: 'text', + name: 'DB_PASSWORD', + message: 'Database password', + default: function() { return existing('DB_PASSWORD') || buffer.toString('base64'); } + }); + + this.prompts.push({ + type: 'text', + name: 'DB_HOST', + message: 'Database host', + default: function() { return existing('DB_HOST') || '127.0.0.1'; } + }); + + done(); + }.bind(this)); + }, - promptForIp: function() { - // Private IP blocks - var blocks = [ - ['192.168.0.0', '192.168.255.255'], - ['172.16.0.0', '172.31.255.255'], - ['10.0.0.0', '10.255.255.255'] - ]; + promptForIp: function() { + // Private IP blocks + var blocks = [ + ['192.168.0.0', '192.168.255.255'], + ['172.16.0.0', '172.31.255.255'], + ['10.0.0.0', '10.255.255.255'] + ]; - // Long IP ranges - var ranges = blocks.map(function(block) { - return block.map(function(ip) { - var parts = ip.split('.'); + // Long IP ranges + var ranges = blocks.map(function(block) { + return block.map(function(ip) { + var parts = ip.split('.'); - return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3] >>> 0; + return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3] >>> 0; + }); }); - }); - - // Randomize IP addresses - var ips = ranges.map(function(range) { - return Math.random() * (range[1] - range[0]) + range[0]; - }).map(function(ip) { - return [ - (ip & (0xff << 24)) >>> 24, - (ip & (0xff << 16)) >>> 16, - (ip & (0xff << 8)) >>> 8, - (ip & (0xff << 0)) >>> 0 - ].join('.'); - }); - try { - var vagrant = this.readFileAsString('Vagrantfile').match(/ip:\s['"]([\d\.]+)['"]/); + // Randomize IP addresses + var ips = ranges.map(function(range) { + return Math.random() * (range[1] - range[0]) + range[0]; + }).map(function(ip) { + return [ + (ip & (0xff << 24)) >>> 24, + (ip & (0xff << 16)) >>> 16, + (ip & (0xff << 8)) >>> 8, + (ip & (0xff << 0)) >>> 0 + ].join('.'); + }); - if (vagrant.length) { - ips.unshift(vagrant[1]); - } - } catch(e) {} - - this.prompts.push({ - required: true, - type: 'list', - name: 'ip', - message: 'Vagrant IP', - pattern: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, - choices: ips - }); - }, + try { + var vagrant = this.fs.read('Vagrantfile').match(/ip:\s['"]([\d\.]+)['"]/); - ask: function() { - var done = this.async(); + if (vagrant.length) { + ips.unshift(vagrant[1]); + } + } catch(e) {} - this.prompt(this.prompts, function(props) { - this.props = props; + this.prompts.push({ + required: true, + type: 'list', + name: 'ip', + message: 'Vagrant IP', + pattern: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, + choices: ips + }); + }, - done(); - }.bind(this)); + ask: function() { + return this.prompt(this.prompts).then(function(props) { + this.props = props; + }.bind(this)); + }, }, - prepareReadme: function() { - try { - this.readmeFile = this.readFileAsString(path.join(this.env.cwd, 'README.md')); - this.readmeFile = this.readmeFile - .replace(/^(?:\[[^\]\r\n]+\]){1,2}(?:\([^\)\r\n]+\))?[\r\n]+=+[\r\n]+> Powered by \[(?:Genesis|Evolution)[^\r\n]+[\r\n]+/i, '') - .replace(/[\r\n]\[[^\]\r\n]+\]:\s*http[^\r\n]+[\r\n]+\[(?:genesis|evolution)-wordpress\]:\s*http[^\r\n]+[\r\n]*$/i, '') - ; - } catch(e) { - this.readmeFile = ''; - } - }, + configuring: { + prepareReadme: function() { + try { + this.readmeFile = this.fs.read(path.join(this.env.cwd, 'README.md')); + this.readmeFile = this.readmeFile + .replace(/^(?:\[[^\]\r\n]+\]){1,2}(?:\([^\)\r\n]+\))?[\r\n]+=+[\r\n]+> Powered by \[(?:Genesis|Evolution)[^\r\n]+[\r\n]+/i, '') + .replace(/(?:^|[\r\n])\[[^\]\r\n]+\]:\s*http[^\r\n]+[\r\n]+\[(?:genesis|evolution)-wordpress\]:\s*http[^\r\n]+[\r\n]*$/i, '') + ; + } catch(e) { + this.readmeFile = ''; + } + }, - prepareHtaccess: function() { - try { - this.htaccessFile = this.readFileAsString(path.join(this.env.cwd, 'web', '.htaccess')); - this.htaccessFile = this.htaccessFile.replace(/# BEGIN Evolution WordPress(?:.|[\r\n]+)+?# END Evolution WordPress[\r\n]*/i, '').trim(); + prepareHtaccess: function() { + try { + this.htaccessFile = this.fs.read(path.join(this.env.cwd, 'web', '.htaccess')); + this.htaccessFile = this.htaccessFile.replace(/# BEGIN Evolution WordPress(?:.|[\r\n]+)+?# END Evolution WordPress[\r\n]*/i, '').trim(); - this.htaccessWpBlock = !!this.htaccessFile.match(/# BEGIN WordPress(?:.|[\r\n]+)+?# END WordPress[\r\n]*/i); - } catch(e) { - this.htaccessFile = ''; - this.htaccessWpBlock = false; - } - }, + this.htaccessWpBlock = !!this.htaccessFile.match(/# BEGIN WordPress(?:.|[\r\n]+)+?# END WordPress[\r\n]*/i); + } catch(e) { + this.htaccessFile = ''; + this.htaccessWpBlock = false; + } + }, - prepareGitignore: function () { - try { - this.gitignoreFile = this.readFileAsString(path.join(this.env.cwd, '.gitignore')); - this.gitignoreFile = this.gitignoreFile.match(/[\r\n]+# Generated file, don't edit above this line[\r\n]*([\S\s]*?)[\r\n]*$/i)[1]; - } catch(e) { - this.gitignoreFile = ''; - } - }, + prepareGitignore: function () { + try { + this.gitignoreFile = this.fs.read(path.join(this.env.cwd, '.gitignore')); + this.gitignoreFile = this.gitignoreFile.match(/[\r\n]+# Generated file, don't edit above this line[\r\n]*([\S\s]*?)[\r\n]*$/i)[1]; + } catch(e) { + this.gitignoreFile = ''; + } + }, - prepareRoles: function() { - this.props.roles = WordpressGenerator.roles.map(function(role) { - role.checked = !!~this.props.roles.indexOf(role.value); + prepareRoles: function() { + this.props.roles = WordpressGenerator.roles.map(function(role) { + role.checked = !!~this.props.roles.indexOf(role.value); - return role; - }.bind(this)); - }, + return role; + }.bind(this)); + }, - prepareSalts: function() { - var done = this.async(); + prepareSalts: function() { + var done = this.async(); - request('https://api.wordpress.org/secret-key/1.1/salt/', function(err, response, salts) { - if (err) { - throw err; - } + request('https://api.wordpress.org/secret-key/1.1/salt/', function(err, response, salts) { + if (err) { + throw err; + } - this.props.salts = salts; - done(); - }.bind(this)); - }, + this.props.salts = salts; + done(); + }.bind(this)); + }, - prepareSshKeys: function() { - if (typeof(this.props.sshOverwrite) !== 'undefined' && this.props.sshOverwrite !== true) { - this.log.skip('SSH keys exist, skipping keygen...'); - return false; - } + prepareSshKeys: function() { + if (typeof(this.props.sshOverwrite) !== 'undefined' && this.props.sshOverwrite !== true) { + this.log.skip('SSH keys exist, skipping keygen...'); + return false; + } - var done = this.async(); - var location = path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssh', 'id_rsa'); + var done = this.async(); + var location = path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssh', 'id_rsa'); - this.log.info('Creating SSH keys...'); + this.log.info('Creating SSH keys...'); - this.mkdir(path.dirname(location)); + fs.mkdirsSync(path.dirname(location)); - keygen({ - location: location, - comment: 'deploy@' + this.props.domain, - read: false - }, done); - }, + keygen({ + location: location, + comment: 'deploy@' + this.props.domain, + read: false + }, done); + }, - prepareTemplates: function() { - this.templates = this.expandFiles('**/*', { - cwd: this.sourceRoot(), - }); + prepareTemplates: function() { + this.templates = glob.sync('**/*', { + cwd: this.sourceRoot(), + nodir: true, + }); + }, }, ready: function() { @@ -609,6 +677,60 @@ var WordpressGenerator = yeoman.generators.Base.extend({ this.log.info(chalk.green('Here we go!')); }, + cleanupBower: function () { + // remove old bower postinstall + try { + var rcfile = path.join(this.env.cwd, '.bowerrc'); + var rcContents = this.fs.readJSON(rcfile); + if (rcContents.scripts.postinstall == './bower_components/evolution-wordpress/lib/yeoman/bin/postinstall') { + delete rcContents.scripts.postinstall; + + if (!Object.keys(rcContents.scripts).length) { + delete rcContents.scripts; + } + + if (!Object.keys(rcContents).length) { + fs.unlinkSync(rcfile); + } + else { + fs.writeFileSync(rcfile, JSON.stringify(rcContents, null, 2)); + } + } + } catch (e) {}; + + // remove old bower deps for evolution + try { + var bowerfile = path.join(this.env.cwd, 'bower.json'); + var bowerContents = this.fs.readJSON(bowerfile); + var isChanged = false; + if (bowerContents.dependencies.hasOwnProperty('evolution-wordpress')) { + delete bowerContents.dependencies['evolution-wordpress']; + isChanged = true; + } + if (bowerContents.dependencies.hasOwnProperty('wordpress')) { + delete bowerContents.dependencies.wordpress; + isChanged = true; + } + if (isChanged) { + fs.writeFileSync(bowerfile, JSON.stringify(bowerContents, null, 2)); + } + } catch (e) {}; + + // remove old bower json, if no deps remain + try { + var bowerfile = path.join(this.env.cwd, 'bower.json'); + var bowerContents = this.fs.readJSON(bowerfile); + if (!bowerContents.hasOwnProperty('dependencies') || Object.keys(bowerContents.dependencies).length < 1) { + fs.unlinkSync(bowerfile); + } + } catch (e) {}; + + // remove old bower components + try { + fs.removeSync(path.join(this.env.cwd, 'bower_components/evolution-wordpress')); + } catch (e) {}; + }, + symlinkEvolutionWordPress: function() { if (!this.options.dev) { return false; @@ -616,45 +738,31 @@ var WordpressGenerator = yeoman.generators.Base.extend({ this.log.info(chalk.green('Symlinking local Evolution WordPress as dependency...')); - var srcpath = '../../../wordpress'; - var dstpath = path.join(this.env.cwd, 'bower_components', 'evolution-wordpress'); + var srcpath = path.join(__dirname, '../../../wordpress'); + var dstpath = path.join(this.env.cwd, 'node_modules', 'evolution-wordpress'); - this.mkdir(path.dirname(dstpath)); + this.log.info(chalk.red(srcpath), '~>', chalk.blue(dstpath)); - fs.symlinkSync(srcpath, dstpath); + fs.mkdirsSync(path.dirname(dstpath)); + fs.ensureSymlinkSync(srcpath, dstpath); }, - /** - * Specify project dependencies (e.g. WordPress, Evolution, etc.) via `bower.json` - */ - copyBower: function() { - this.template('bower.json', 'bower.json'); - this.template('_bowerrc', '.bowerrc'); - }, - - /** - * Install project dependencies (e.g. WordPress, Evolution) for later usage by the generator - */ - runBower: function() { + postInstallWordpress: function () { var done = this.async(); + var file = path.join(__dirname, '../../bin/postinstall'); - this.log.info(chalk.green('Installing project dependencies...')); - - this.bowerInstall(null, null, done); - }, - - /** - * Bower's `postInstall` can only be ran after all other dependencies have been installed - */ - copyBowerrc: function() { - this.template('_bowerrc', '.bowerrc'); + this + .spawnCommand('node', [file, this.props.wordpress], {cwd: this.env.cwd, env: process.env}) + .on('error', done) + .on('exit', done) + ; }, prepareWpConfig: function() { - this.wpConfigFile = this.readFileAsString(path.join(this.env.cwd, 'web', 'wp', 'wp-config-sample.php')); + this.wpConfigFile = this.fs.read(path.join(this.env.cwd, 'web', 'wp', 'wp-config-sample.php')); }, - scaffold: function() { + writing: function() { this.log.info(chalk.green('Scaffolding...')); this.templates.forEach(function(file) { @@ -662,106 +770,97 @@ var WordpressGenerator = yeoman.generators.Base.extend({ }.bind(this)); }, - generateSslCert: function() { - if (!this.props.ssl) { - return false; - } + install: { + generateSslCerts: function() { + if (!this.props.ssl) { + return false; + } - if (typeof(this.props.sslOverwrite) !== 'undefined' && this.props.sslOverwrite !== true) { - this.log.skip('SSL certificates exist, skipping creation...'); - return false; - } + if (typeof(this.props.sslOverwrite) !== 'undefined' && this.props.sslOverwrite !== true) { + this.log.skip('SSL certificates exist, skipping creation...'); + return false; + } - var done = this.async(); - var location = path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssl'); - var stages = ['local', 'staging', 'production']; - var exited = function (err) { - stages.pop(); + var done = this.async(); + var location = path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssl'); + var stages = ['local', 'staging', 'production']; + var exited = function (err) { + stages.pop(); - if (err || !stages.length) { - done(err); - } - }; + if (err || !stages.length) { + done(err); + } + }; + + this.log.info('Creating self-signed SSL certificate...'); + + fs.mkdirsSync(path.dirname(location)); + + stages.forEach(function (stage) { + this.emit('sslInstall'+stage); + + var cert = path.join(location, stage + '.' + this.props.domain + '.pem'); + var cfg = path.join(location, stage + '.cfg'); + + this + .spawnCommand('openssl', [ + 'req', + '-x509', + '-nodes', + '-days', + '365', + '-newkey', + 'rsa:2048', + '-keyout', + cert, + '-out', + cert, + '-config', + cfg + ], { + cwd: this.env.cwd, + }) + .on('error', exited) + .on('exit', this.emit.bind(this, 'sslInstall'+stage+':end')) + .on('exit', function (err) { + if (err === 127) { + this.log.error('Could not generate SSL certificate for '+stage); + } + + exited(err); + }.bind(this)) + ; + }.bind(this)); + }, - this.log.info('Creating self-signed SSL certificate...'); + fixPermissions: function() { + fs.chmodSync(path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssh', 'id_rsa'), '600'); + }, - this.mkdir(path.dirname(location)); + installGems: function() { + var done = this.async(); + var installer = 'bundle'; - stages.forEach(function (stage) { - this.emit('sslInstall'+stage); + this.log.info(chalk.green('Installing Gems...')); - var cert = path.join(location, stage + '.' + this.props.domain + '.pem'); - var cfg = path.join(location, stage + '.cfg'); + this.emit(installer + 'Install'); this - .spawnCommand('openssl', [ - 'req', - '-x509', - '-nodes', - '-days', - '365', - '-newkey', - 'rsa:2048', - '-keyout', - cert, - '-out', - cert, - '-config', - cfg - ], { - cwd: this.env.cwd, - }) - .on('error', exited) - .on('exit', this.emit.bind(this, 'sslInstall'+stage+':end')) + .spawnCommand(installer, ['install']) + .on('error', done) + .on('exit', this.emit.bind(this, installer + 'Install:end')) .on('exit', function (err) { if (err === 127) { - this.log.error('Could not generate SSL certificate for '+stage); + this.log.error('Could not run bundler. Please install with `sudo ' + installer + ' install`.'); } - exited(err); + done(err); }.bind(this)) ; - }.bind(this)); - }, - - symlinkBowerComponents: function() { - var bowerPath = path.join(this.env.cwd, 'web', 'bower_components'); - - if (fs.existsSync(bowerPath)) { - this.log.info(chalk.yellow('Removing `bower_components` symlink from `web`...')); - - fs.unlinkSync(bowerPath); + }, + installNpm: function() { + this.npmInstall(); } - - this.log.info(chalk.green('Symlinking `bower_components` into `web`...')); - - fs.symlinkSync('../bower_components', bowerPath); - }, - - fixPermissions: function() { - fs.chmodSync(path.join(this.env.cwd, 'lib', 'ansible', 'files', 'ssh', 'id_rsa'), '600'); - }, - - installGems: function() { - var done = this.async(); - var installer = 'bundle'; - - this.log.info(chalk.green('Installing Gems...')); - - this.emit(installer + 'Install'); - - this - .spawnCommand(installer, ['install'], done) - .on('error', done) - .on('exit', this.emit.bind(this, installer + 'Install:end')) - .on('exit', function (err) { - if (err === 127) { - this.log.error('Could not run bundler. Please install with `sudo ' + installer + ' install`.'); - } - - done(err); - }.bind(this)) - ; }, }); @@ -812,42 +911,32 @@ WordpressGenerator.latest = function(username, repo, fn) { return fn(new Error(res.headers.status)); try { - var fuzzyver = /^\s*v?(\d+)\.(\d+)(?:\.(\d+))?\s*$/; var tags = JSON.parse(body) .map(function(item){ - return item.ref.split('/').pop(); + // fake two segment versions (X.Y) as semvers (X.Y.0) + var tag = item.ref.split('/').pop(); + if (/^\s*[v=]?\d+\.\d+\s*$/.test(tag)) { + return {raw: tag, sem: tag.trim()+'.0'}; + } + return {sem: tag}; + }) + // filter out anything that's not a valid semver + .filter(function (item) { + return semver.valid(item.sem); }) - .filter(RegExp.prototype.test.bind(fuzzyver)) + // sort semvers from high to low .sort(function (a, b) { - var ma = a.match(fuzzyver); - var mb = b.match(fuzzyver); - - if (ma[1] == mb[1]) { - if (ma[2] == mb[2]) { - ma[3] = ma[3] || '0'; - mb[3] = mb[3] || '0'; - if (ma[3] == mb[3]) - return 0; - else if (parseInt(ma[3]) > parseInt(mb[3])) - return 1; - else - return -1; - } - else if (parseInt(ma[2]) > parseInt(mb[2])) - return 1; - else - return -1; - } - else if (parseInt(ma[1]) > parseInt(mb[1])) - return 1; - else - return -1; + return semver.rcompare(a.sem, b.sem); }); } catch (e) { return fn(e); } - fn(null, tags.pop()); + if (!tags.length) { + return fn(new Error('Could not find latest matching version')); + } + + return fn(null, tags[0].hasOwnProperty('raw') ? tags[0].raw : tags[0].sem); }); }; diff --git a/lib/yeoman/templates/Capfile b/lib/yeoman/templates/Capfile index 4cb85371..2ed26e15 100644 --- a/lib/yeoman/templates/Capfile +++ b/lib/yeoman/templates/Capfile @@ -25,7 +25,7 @@ require 'capistrano/deploy' # require 'capistrano/rails/migrations' # Load Evolution WordPress' tasks -load File.expand_path(File.dirname(__FILE__)) + '/bower_components/evolution-wordpress/lib/capistrano/bootstrap.rake' +load File.expand_path(File.dirname(__FILE__)) + '/node_modules/evolution-wordpress/lib/capistrano/bootstrap.rake' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } diff --git a/lib/yeoman/templates/Evolution.php b/lib/yeoman/templates/Evolution.php index 6cae73c8..64edd92f 100644 --- a/lib/yeoman/templates/Evolution.php +++ b/lib/yeoman/templates/Evolution.php @@ -2,7 +2,7 @@ class Evolution { - const DOMAIN = '<%= props.domain %>'; + const DOMAIN = '<%- props.domain %>'; public static $hostname; public static $env; diff --git a/lib/yeoman/templates/README.md b/lib/yeoman/templates/README.md index c10cacc5..eaf664fc 100644 --- a/lib/yeoman/templates/README.md +++ b/lib/yeoman/templates/README.md @@ -1,8 +1,8 @@ -[<%= props.name %>][<%= props.domain %>] -<%= new Array(props.name.length + props.domain.length + 5).join('=') %> +[<%- props.name %>][<%- props.domain %>] +<%- new Array(props.name.length + props.domain.length + 5).join('=') %> -> Powered by [Evolution WordPress][evolution-wordpress] *v<%= pkg.version %>* +> Powered by [Evolution WordPress][evolution-wordpress] *v<%- pkg.version %>* -<%= readmeFile %> -[<%= props.domain %>]: http://<%= props.www ? 'www.' : '' %><%= props.domain %>/ +<%- readmeFile %> +[<%- props.domain %>]: http://<%- props.www ? 'www.' : '' %><%- props.domain %>/ [evolution-wordpress]: https://github.com/evolution/wordpress/ diff --git a/lib/yeoman/templates/Vagrantfile b/lib/yeoman/templates/Vagrantfile index 4387a864..ec643f36 100644 --- a/lib/yeoman/templates/Vagrantfile +++ b/lib/yeoman/templates/Vagrantfile @@ -58,19 +58,19 @@ Vagrant.configure("2") do |config| end config.vm.define :local do |box| - box.vm.hostname = "local.<%= props.domain %>" + box.vm.hostname = "local.<%- props.domain %>" # Static IP for testing. - box.vm.network :private_network, ip: "<%= props.ip %>" - box.vm.network :forwarded_port, guest: 22, id: 'ssh', host: <%= props.ip.split('.').join('').split('0').join('').slice(-4) %>, auto_correct: true + box.vm.network :private_network, ip: "<%- props.ip %>" + box.vm.network :forwarded_port, guest: 22, id: 'ssh', host: <%- props.ip.split('.').join('').split('0').join('').slice(-4) %>, auto_correct: true # Remount the default shared folder as NFS for caching and speed box.vm.synced_folder ".", "/vagrant", :nfs => true # Remove any known hosts when (re)provisioning - box.vm.provision "ssh-cleanup", type: "local_shell", command: "(ssh-keygen -R local.<%= props.domain %> && ssh-keygen -R <%= props.ip %>) || :" + box.vm.provision "ssh-cleanup", type: "local_shell", command: "(ssh-keygen -R local.<%- props.domain %> && ssh-keygen -R <%- props.ip %>) || :" - # Provision local.<%= props.domain %> + # Provision local.<%- props.domain %> box.vm.provision :ansible do |ansible| galaxy_reqs = "lib/ansible/galaxy.yml" if File.exists?(galaxy_reqs) diff --git a/lib/yeoman/templates/_bowerrc b/lib/yeoman/templates/_bowerrc deleted file mode 100644 index aa9fbc08..00000000 --- a/lib/yeoman/templates/_bowerrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "scripts": { - "postinstall": "./bower_components/evolution-wordpress/lib/yeoman/bin/postinstall" - } -} diff --git a/lib/yeoman/templates/_gitignore b/lib/yeoman/templates/_gitignore index 970ad645..635f2946 100644 --- a/lib/yeoman/templates/_gitignore +++ b/lib/yeoman/templates/_gitignore @@ -56,4 +56,4 @@ web/wp lib/ansible/roles # Generated file, don't edit above this line -<%= gitignoreFile %> +<%- gitignoreFile %> diff --git a/lib/yeoman/templates/ansible.cfg b/lib/yeoman/templates/ansible.cfg index aaf4b0bd..ace461e3 100644 --- a/lib/yeoman/templates/ansible.cfg +++ b/lib/yeoman/templates/ansible.cfg @@ -2,8 +2,8 @@ hostfile = ./lib/ansible/hosts remote_user = deploy -roles_path = ./bower_components/evolution-wordpress/lib/ansible/roles -lookup_plugins = ./bower_components/evolution-wordpress/lib/ansible/lookup_plugins +roles_path = ./node_modules/evolution-wordpress/lib/ansible/roles +lookup_plugins = ./node_modules/evolution-wordpress/lib/ansible/lookup_plugins [ssh_connection] control_path = %(directory)s/%%h-%%p-%%r diff --git a/lib/yeoman/templates/bin/import b/lib/yeoman/templates/bin/import index 799e2c7e..46af5abc 100644 --- a/lib/yeoman/templates/bin/import +++ b/lib/yeoman/templates/bin/import @@ -1,12 +1,10 @@ #!/usr/bin/env bash -IMPORTER_PATH="$( dirname "$0" )/../bower_components/evolution-wordpress" +IMPORTER_PATH="$( dirname "$0" )/../node_modules/evolution-wordpress" IMPORTER_SCRIPT="$IMPORTER_PATH/bin/import" chmod +x "$IMPORTER_SCRIPT" -(cd "$IMPORTER_PATH"; npm install) - VAGRANT_STATUS=$(vagrant status) if [[ "$VAGRANT_STATUS" == *"running ("* ]]; then diff --git a/lib/yeoman/templates/bower.json b/lib/yeoman/templates/bower.json deleted file mode 100644 index 9e14db28..00000000 --- a/lib/yeoman/templates/bower.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": <%= props.private ? 'true' : 'false' %>, - "name": "<%= props.name %>", - "version": "1.0.0", - "dependencies": { - "evolution-wordpress": "<%= options.dev ? 'master' : '~' + pkg.version %>", - "wordpress": "<%= props.wordpress %>" - } -} diff --git a/lib/yeoman/templates/lib/ansible/files/ssl/local.cfg b/lib/yeoman/templates/lib/ansible/files/ssl/local.cfg index 226e67ca..c4db4ac2 100644 --- a/lib/yeoman/templates/lib/ansible/files/ssl/local.cfg +++ b/lib/yeoman/templates/lib/ansible/files/ssl/local.cfg @@ -11,7 +11,7 @@ C=US ST=Texas L=Houston O=IT -CN=local.<%= props.domain %> +CN=local.<%- props.domain %> [ v3_req ] basicConstraints = CA:FALSE diff --git a/lib/yeoman/templates/lib/ansible/files/ssl/production.cfg b/lib/yeoman/templates/lib/ansible/files/ssl/production.cfg index 7b724cb7..e52ccec6 100644 --- a/lib/yeoman/templates/lib/ansible/files/ssl/production.cfg +++ b/lib/yeoman/templates/lib/ansible/files/ssl/production.cfg @@ -11,7 +11,7 @@ C=US ST=Texas L=Houston O=IT -CN=<%= props.domain %> +CN=<%- props.domain %> [ v3_req ] basicConstraints = CA:FALSE @@ -19,5 +19,5 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] -DNS.1 = www.<%= props.domain %> -DNS.2 = production.<%= props.domain %> +DNS.1 = www.<%- props.domain %> +DNS.2 = production.<%- props.domain %> diff --git a/lib/yeoman/templates/lib/ansible/files/ssl/staging.cfg b/lib/yeoman/templates/lib/ansible/files/ssl/staging.cfg index a7e9d062..413aca62 100644 --- a/lib/yeoman/templates/lib/ansible/files/ssl/staging.cfg +++ b/lib/yeoman/templates/lib/ansible/files/ssl/staging.cfg @@ -11,7 +11,7 @@ C=US ST=Texas L=Houston O=IT -CN=staging.<%= props.domain %> +CN=staging.<%- props.domain %> [ v3_req ] basicConstraints = CA:FALSE @@ -19,4 +19,4 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] -DNS.1 = *.staging.<%= props.domain %> +DNS.1 = *.staging.<%- props.domain %> diff --git a/lib/yeoman/templates/lib/ansible/group_vars/all b/lib/yeoman/templates/lib/ansible/group_vars/all index 99143f1b..79cf0c04 100644 --- a/lib/yeoman/templates/lib/ansible/group_vars/all +++ b/lib/yeoman/templates/lib/ansible/group_vars/all @@ -1,19 +1,18 @@ --- -evolve_version: <%= pkg.version %> -domain: <%= props.domain %> -www: <%= props.www %> - -monitoring: - newrelic: '<%= props.newrelic %>' +evolve_version: <%- pkg.version %> +wp_version: <%- props.wordpress %> +domain: <%- props.domain %> +www: <%- props.www %> mysql: - name: <%= props.DB_NAME %> - user: <%= props.DB_USER %> - password: <%= props.DB_PASSWORD %> - host: <%= props.DB_HOST %> - -<% if (props.datadog) { %>datadog_api_key: '<%= props.datadog %>'<% } %> - + name: <%- props.DB_NAME %> + user: <%- props.DB_USER %> + password: <%- props.DB_PASSWORD %> + host: <%- props.DB_HOST %> + +<% if (props.newrelic) { %>newrelic_api_key: '<%- props.newrelic %>' +<% } %><% if (props.datadog) { %>datadog_api_key: '<%- props.datadog %>' +<% } %> # php__memory_limit: # apache__start_servers: @@ -33,7 +32,7 @@ mysql: # wordpress__xmlrpc_whitelist: # - "local" -mail__postmaster: no-reply@<%= props.domain %> +mail__postmaster: no-reply@<%- props.domain %> # iptables__ipv6: 0 diff --git a/lib/yeoman/templates/lib/ansible/hosts b/lib/yeoman/templates/lib/ansible/hosts index 1dc71f2b..1df75e8d 100644 --- a/lib/yeoman/templates/lib/ansible/hosts +++ b/lib/yeoman/templates/lib/ansible/hosts @@ -1,8 +1,8 @@ [local] -local.<%= props.domain %> stage=local +local.<%- props.domain %> stage=local [production] -production.<%= props.domain %> stage=production +production.<%- props.domain %> stage=production [staging] -staging.<%= props.domain %> stage=staging +staging.<%- props.domain %> stage=staging diff --git a/lib/yeoman/templates/lib/ansible/provision.yml b/lib/yeoman/templates/lib/ansible/provision.yml index 3c03eb21..58e4442a 100644 --- a/lib/yeoman/templates/lib/ansible/provision.yml +++ b/lib/yeoman/templates/lib/ansible/provision.yml @@ -10,14 +10,14 @@ - apache # (Required) Web server (Soon: Nginx) - mysql # (Required) Database server (Soon: Maria) - php # (Required) PHP 5.3 - - node # (Required) Tools for deployment (e.g. `bower`) + - node # (Required) Tools for deployment + dependencies (e.g. `npm`) - wp-cli # (Required) CLI for managing WordPress # Optional Features <% if (props.ssl) { %>- pound # (Optional) SSL support & decryption<% } %><% props.roles.map(function(role) { %> - <%= role.checked ? '-' : '#' %> <%= role.value %><% }) %> -<% if (props.newrelic) { %> - { role: newrelic, sudo: yes, when: stage == 'production' } # (Optional) New Relic application/server monitoring -<% } %><% if (props.datadog) { %> - { role: Datadog.datadog, sudo: yes, when: stage == 'production' } # (Optional) Datadog monitoring support + <%- role.checked ? '-' : '#' %> <%- role.value %><% }) %> +<% if (props.newrelic) { %> - { role: newrelic, become: true, when: stage == 'production' } # (Optional) New Relic application/server monitoring +<% } %><% if (props.datadog) { %> - { role: Datadog.datadog, become: true, when: stage == 'production' } # (Optional) Datadog monitoring support <% } %> # /Optional Features - cleanup # (Required) Generate init.d for installed evolution services diff --git a/lib/yeoman/templates/lib/capistrano/deploy.rb b/lib/yeoman/templates/lib/capistrano/deploy.rb index f46208ba..e2128f31 100644 --- a/lib/yeoman/templates/lib/capistrano/deploy.rb +++ b/lib/yeoman/templates/lib/capistrano/deploy.rb @@ -10,17 +10,25 @@ end # Repository name -set :application, "<%= props.name %>" -set :domain, "<%= props.domain %>" +set :application, "<%- props.name %>" +set :domain, "<%- props.domain %>" set :deploy_to, "/var/www/#{fetch(:domain)}/#{fetch(:stage)}/#{fetch(:branch)}" set :linked_dirs, %w{web/wp-content/uploads} set :wp_path, "#{release_path}/web/wp" -set :www, <%= props.www ? 'true' : 'false' %> +set :www, <%- props.www ? 'true' : 'false' %> namespace :deploy do + after :updated, :npm_install do + on roles(:web) do + execute "cd #{release_path} && npm install --production" + end + end after :updated, :bower_install do on roles(:web) do - execute "cd #{release_path} && bower install --config.interactive=false" + bower_exists = test "[ -f #{release_path}/bower.json ]" + if bower_exists + execute "cd #{release_path} && bower install" + end end end after :finished, :launch_browser do diff --git a/lib/yeoman/templates/lib/capistrano/deploy/local.rb b/lib/yeoman/templates/lib/capistrano/deploy/local.rb index 381b0c6c..22e94486 100644 --- a/lib/yeoman/templates/lib/capistrano/deploy/local.rb +++ b/lib/yeoman/templates/lib/capistrano/deploy/local.rb @@ -1,4 +1,4 @@ -server 'local.<%= props.domain %>', +server 'local.<%- props.domain %>', roles: %w{db web}, user: fetch(:user), ssh_options: fetch(:ssh_options) diff --git a/lib/yeoman/templates/lib/capistrano/deploy/production.rb b/lib/yeoman/templates/lib/capistrano/deploy/production.rb index df047e7a..79a2e4bd 100644 --- a/lib/yeoman/templates/lib/capistrano/deploy/production.rb +++ b/lib/yeoman/templates/lib/capistrano/deploy/production.rb @@ -1,4 +1,4 @@ -server 'production.<%= props.domain %>', +server 'production.<%- props.domain %>', roles: %w{db web}, user: fetch(:user), ssh_options: fetch(:ssh_options) diff --git a/lib/yeoman/templates/lib/capistrano/deploy/staging.rb b/lib/yeoman/templates/lib/capistrano/deploy/staging.rb index cfb86674..ac520b04 100644 --- a/lib/yeoman/templates/lib/capistrano/deploy/staging.rb +++ b/lib/yeoman/templates/lib/capistrano/deploy/staging.rb @@ -1,4 +1,4 @@ -server 'staging.<%= props.domain %>', +server 'staging.<%- props.domain %>', roles: %w{db web}, user: fetch(:user), ssh_options: fetch(:ssh_options) diff --git a/lib/yeoman/templates/package.json b/lib/yeoman/templates/package.json new file mode 100644 index 00000000..6dbbd332 --- /dev/null +++ b/lib/yeoman/templates/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "name": "<%- props.name %>", + "engines": { + "node": ">=4" + }, + "dependencies": { + "evolution-wordpress": "~2.0.0-alpha" + }, + "scripts": { + "postinstall": "node node_modules/evolution-wordpress/bin/postinstall" + } +} diff --git a/lib/yeoman/templates/web/_htaccess b/lib/yeoman/templates/web/_htaccess index c50b858b..293e7dd6 100644 --- a/lib/yeoman/templates/web/_htaccess +++ b/lib/yeoman/templates/web/_htaccess @@ -3,13 +3,13 @@ RewriteEngine On RewriteBase / - # Force <%= props.www ? '' : 'non-' %>www for production - RewriteCond %{HTTP_HOST} ^<%= props.www ? '' : 'www\\.' %><%= props.domain.replace('.', '\\.') %>$ [NC] + # Force <%- props.www ? '' : 'non-' %>www for production + RewriteCond %{HTTP_HOST} ^<%- props.www ? '' : 'www\\.' %><%- props.domain.replace('.', '\\.') %>$ [NC] RewriteCond %{HTTP:X-Forwarded-Proto} ^http(s)| [NC] - RewriteRule ^(.*)$ http%1://<%= props.www ? 'www.' : '' %><%= props.domain %>/$1 [L,R=301,NC] + RewriteRule ^(.*)$ http%1://<%- props.www ? 'www.' : '' %><%- props.domain %>/$1 [L,R=301,NC] # Prevent spidering of non-canonical URLs, such as `[*.]staging.*` and `production.*`. - RewriteCond %{HTTP_HOST} (^local|staging|^production)\.<%= props.domain.replace('.', '\\.') %>$ [NC] + RewriteCond %{HTTP_HOST} (^local|staging|^production)\.<%- props.domain.replace('.', '\\.') %>$ [NC] RewriteRule ^robots.txt$ no_robots.txt [L] ## SECURITY @@ -41,13 +41,13 @@ # Rules to help reduce spam RewriteCond %{REQUEST_METHOD} POST RewriteCond %{REQUEST_URI} ^(.*)wp-comments-post\.php* - RewriteCond %{HTTP_REFERER} !^(.*)<%= props.domain %>.* + RewriteCond %{HTTP_REFERER} !^(.*)<%- props.domain %>.* RewriteCond %{HTTP_REFERER} !^http://jetpack\.wordpress\.com/jetpack-comment/ [OR] RewriteCond %{HTTP_USER_AGENT} ^$ RewriteRule ^(.*)$ - [F] # END Evolution WordPress -<%= htaccessFile %> +<%- htaccessFile %> <% if (!htaccessWpBlock) { %># BEGIN WordPress RewriteEngine On diff --git a/lib/yeoman/templates/web/robots.txt b/lib/yeoman/templates/web/robots.txt index 03275bf8..dd9e59f6 100644 --- a/lib/yeoman/templates/web/robots.txt +++ b/lib/yeoman/templates/web/robots.txt @@ -12,4 +12,4 @@ Disallow: /wp/trackback Disallow: /wp/xmlrpc.php Disallow: ?wptheme= -# Sitemap: http://<%= props.domain %>/sitemap.xml +# Sitemap: http://<%- props.domain %>/sitemap.xml diff --git a/lib/yeoman/templates/web/wp-config.php b/lib/yeoman/templates/web/wp-config.php index 9820f233..f0bb5c05 100644 --- a/lib/yeoman/templates/web/wp-config.php +++ b/lib/yeoman/templates/web/wp-config.php @@ -2,7 +2,7 @@ /* Include Evolution to use with WordPress */ require_once(dirname(__FILE__) . '/../Evolution.php'); -<%= wpConfigFile +<%- wpConfigFile // Already started PHP .replace(' /** Output rewriting for production.DOMAIN or *.staging.DOMAIN **/ Evolution::rewriteUrls(); diff --git a/package.json b/package.json index 3541eee8..f2e474ed 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,31 @@ { - "private": true, + "private": false, "name": "evolution-wordpress", - "version": "1.6.2", + "version": "2.0.0-alpha", "description": "Libraries for a multi-staged WordPress workflow with Vagrant", "main": "lib/yeoman/index.js", + "engines": { + "node": ">=4" + }, "dependencies": { - "chalk": "^0.5.1", - "fs-extra": "^0.20.1", - "request": "^2.42.0", - "ssh-keygen": "^0.2.1", - "in-words": "^0.1.0", - "yeoman-generator": "^0.16.0", - "deasync": "^0.1.0", - "inquirer": "^0.8.5", + "chalk": "^1.1.3", + "fs-extra": "^0.30.0", + "request": "^2.75.0", + "ssh-keygen": "^0.4.0", + "in-words": "^0.2.1", + "yeoman-generator": "^0.24.1", + "yeoman-test": "^1.0.0", + "deasync": "^0.1.8", + "inquirer": "^1.2.2", "replace": "^0.3.0", - "yauzl": "^2.5.0", - "glob": "^4.0.4" + "yauzl": "^2.6.0", + "semver": "^5.1.0", + "js-yaml":"^3.7.0", + "glob": "^7.1.1" }, "devDependencies": { - "mocha": "^1.20.1", - "zombie": "^3.1.1" + "mocha": "^3.1.2", + "nightmare": "^2.8.1" }, "scripts": { "test": "./bin/test" @@ -28,7 +34,10 @@ "type": "git", "url": "git://github.com/evolution/wordpress.git" }, - "author": "Eric Clemmons ", + "contributors": [ + "Evan Kaufman ", + "Eric Clemmons " + ], "license": "MIT", "bugs": { "url": "https://github.com/evolution/wordpress/issues" diff --git a/test/README.md b/test/README.md index 58ede28c..3b7750d0 100644 --- a/test/README.md +++ b/test/README.md @@ -3,15 +3,16 @@ ### Dependencies - Composer (`$ curl -sS https://getcomposer.org/installer | php`) -- Node + NPM +- Node + npm dev dependencies - Vagrant ### Setup -Install Composer dependencies: +Install Composer and dependencies: ```shell -$ php composer.phar install --dev +$ curl -sS https://getcomposer.org/installer | php +$ php composer.phar install ``` Install NPM dependencies: @@ -51,5 +52,7 @@ $ npm test ``` Tests will be ran against the new entries in `/etc/hosts`: - -> http://local.example.com and http://example.com/ +- local.example.com +- production.example.com +- www.example.com +- example.com diff --git a/test/functional/00-firewall.js b/test/functional/00-firewall.js index a57a38f5..2f029813 100644 --- a/test/functional/00-firewall.js +++ b/test/functional/00-firewall.js @@ -12,31 +12,28 @@ describe('firewall ports', function(done) { port_cmd = 'sudo iptables -L -n | grep :%d'; done(); } else { - exec('php -r "echo gethostbyname(\'local.example.com.\');"', { - cwd: process.cwd() + '/temp' - }, function(err, stdout, stderr) { - assert.ifError(err); - port_cmd = 'nc -z -w 1 ' + stdout.trim() + ' %d'; - done(); - }); + port_cmd = `nc -z -w 1 ${process.env.EXAMPLE_COM} %d`; + done(); } }); describe('should be closed', function(done) { it('mysql', function(done) { - exec(util.format(port_cmd, '3306'), { + var formatted_cmd = util.format(port_cmd, '3306'); + exec(formatted_cmd, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { - assert.ok(err); + assert.ok(err, formatted_cmd); done(); }); }); it('apache backend', function(done) { - exec(util.format(port_cmd, '8080'), { + var formatted_cmd = util.format(port_cmd, '8080'); + exec(formatted_cmd, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { - assert.ok(err); + assert.ok(err, formatted_cmd); done(); }); }); diff --git a/test/functional/01-install.js b/test/functional/01-install.js index 2391b352..8e4b79d9 100644 --- a/test/functional/01-install.js +++ b/test/functional/01-install.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../lib/node/nightmare'); var fs = require('fs'); var path = require('path'); @@ -10,34 +10,24 @@ describe('Mock site', function() { var browser = new Browser(); browser - .visit('http://local.example.com/wp/wp-admin/install.php') - .then(function() { - if (browser.button('Continue')) { - browser.select('language', 'English (United States)'); - - return browser.pressButton('Continue'); - } - }) - .then(function() { - if (browser.button('Hide')) { - return browser.pressButton('Hide'); - } - }) - .then(function() { - if (browser.button('Install WordPress')) { - browser - .fill('Site Title', 'Evolution WordPress Test') - .fill('Username', 'test') - .fill('admin_password', 'test') - .fill('admin_password2', 'test') - .fill('admin_email', 'test@example.com') - .uncheck('blog_public') - ; - - return browser.pressButton('Install WordPress'); - } - }) - .then(done, done) + .goto(`http://${process.env.EXAMPLE_COM}/wp/wp-admin/install.php`, {'Host':'local.example.com'}) + .select('select[name=language]', '') + .click('form[action$="?step=1"] input[type=submit]') + .wait('form[action$="?step=2"]') + .click('button.wp-hide-pw') + .type('input[name=weblog_title]', 'Evolution WordPress Test') + .type('input[name=user_name]', 'test') + .type('input[name=admin_password]', 'test') + .check('input[name=pw_weak]') + .type('input[name=admin_email]', 'test@example.com') + .uncheck('input[name=blog_public]') + .click('form[action$="?step=2"] input[type=submit]') + .wait('a[href$="/wp-login.php"]') + .end() + .then(done) + .catch(function(error) { + done(); + }); ; }); @@ -45,11 +35,15 @@ describe('Mock site', function() { var browser = new Browser(); browser - .visit('http://local.example.com/wp/wp-admin/install.php') - .then(function() { - assert.equal('Log In', browser.text('a.button')); + .goto(`http://${process.env.EXAMPLE_COM}/wp/wp-admin/install.php`, {'Host':'local.example.com'}) + .evaluate(function() { + return document.querySelector('a[href$="/wp-login.php"]').href + }) + .end() + .then(function(link) { + assert.equal(link, 'http://local.example.com/wp/wp-login.php'); + done(); }) - .then(done, done) ; }) }); diff --git a/test/functional/03-evolve.provision.js b/test/functional/03-evolve.provision.js index 32aeb28c..17aa97ae 100644 --- a/test/functional/03-evolve.provision.js +++ b/test/functional/03-evolve.provision.js @@ -1,15 +1,41 @@ 'use strict'; var assert = require('assert'); -var exec = require('child_process').exec; +var spawn = require('child_process').spawn; describe('cap production evolve:provision', function(done) { it('should not fail', function(done) { - exec('bundle exec cap production evolve:provision', { + var output = ''; + + var child = spawn('bundle', ['exec', 'cap', 'production', 'evolve:provision'], { cwd: process.cwd() + '/temp' - }, function(err, stdout, stderr) { - assert.ifError(err); - done(); + }); + + child.stdout.on('data', function (data) { + if (process.env.TRAVIS) { + console.log(data.toString()); + } else { + output += data.toString(); + } + }); + + child.stderr.on('data', function (data) { + if (process.env.TRAVIS) { + console.log(data.toString()); + } else { + output += data.toString(); + } + }); + + child.on('exit', function (code) { + if (code) { + if (!process.env.TRAVIS) { + console.error(output); + } + done(new Error('exited with ' + code)); + } else { + done(); + } }); }); }); diff --git a/test/functional/04-deploy.js b/test/functional/04-deploy.js index c6126296..ee81abea 100644 --- a/test/functional/04-deploy.js +++ b/test/functional/04-deploy.js @@ -1,15 +1,41 @@ 'use strict'; var assert = require('assert'); -var exec = require('child_process').exec; +var spawn = require('child_process').spawn; describe('cap production deploy', function(done) { it('should not fail', function(done) { - exec('bundle exec cap production deploy', { + var output = ''; + + var child = spawn('bundle', ['exec', 'cap', 'production', 'deploy'], { cwd: process.cwd() + '/temp' - }, function(err, stdout, stderr) { - assert.ifError(err); - done(); + }); + + child.stdout.on('data', function (data) { + if (process.env.TRAVIS) { + console.log(data.toString()); + } else { + output += data.toString(); + } + }); + + child.stderr.on('data', function (data) { + if (process.env.TRAVIS) { + console.log(data.toString()); + } else { + output += data.toString(); + } + }); + + child.on('exit', function (code) { + if (code) { + if (!process.env.TRAVIS) { + console.error(output); + } + done(new Error('exited with ' + code)); + } else { + done(); + } }); }); }); diff --git a/test/functional/05-evolve.db.up.js b/test/functional/05-evolve.db.up.js index 4cd443ee..53f24e36 100644 --- a/test/functional/05-evolve.db.up.js +++ b/test/functional/05-evolve.db.up.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../lib/node/nightmare'); var exec = require('child_process').exec; describe('cap production evolve:up:db', function(done) { @@ -18,11 +18,18 @@ describe('cap production evolve:up:db', function(done) { var browser = new Browser(); browser - .visit('http://example.com/') - .then(null, function() { - assert.equal('Evolution WordPress Test – Just another WordPress site', browser.text('title')); + .goto(`http://${process.env.EXAMPLE_COM}/`, {'Host':'example.com'}) + .evaluate(function() { + return document.title; + }) + .end() + .then(function(title) { + assert.equal(title, 'Evolution WordPress Test – Just another WordPress site'); + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); @@ -30,13 +37,14 @@ describe('cap production evolve:up:db', function(done) { var browser = new Browser(); browser - .visit('http://example.com/wp/wp-admin/install.php') - .then(function() { - assert(false, 'expected 403, got ' + browser.statusCode) + .goto(`http://${process.env.EXAMPLE_COM}/wp/wp-admin/install.php`, {'Host':'example.com'}) + .end() + .then(function(result) { + assert.equal(result.code, 403) + done() }) .catch(function(error) { - assert.equal(browser.statusCode, 403, "should be forbidden\n" + error); - done() + done(error) }) ; }); diff --git a/test/functional/06-evolve.files.up.js b/test/functional/06-evolve.files.up.js index 3d92648c..bef1df65 100644 --- a/test/functional/06-evolve.files.up.js +++ b/test/functional/06-evolve.files.up.js @@ -1,12 +1,12 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../lib/node/nightmare'); var fs = require('fs'); var exec = require('child_process').exec; var testFile = '/vagrant/web/wp-content/uploads/uploads_sync_test.jpg'; -var testUrl = 'http://production.example.com/wp-content/uploads/uploads_sync_test.jpg'; +var testUrl = `http://${process.env.EXAMPLE_COM}/wp-content/uploads/uploads_sync_test.jpg`; describe('cap production evolve:files:up', function(done) { it('may need to remove uploads', function(done) { @@ -31,12 +31,14 @@ describe('cap production evolve:files:up', function(done) { var browser = new Browser(); browser - .visit(testUrl) - .then(function() { - assert(false, "Url unexpectedly exists") + .goto(testUrl, {'Host':'production.example.com'}) + .end() + .then(function(result) { + assert.equal(404, result.code) + done() }) .catch(function(error) { - done(); + done(error); }) ; }); @@ -63,8 +65,10 @@ describe('cap production evolve:files:up', function(done) { var browser = new Browser(); browser - .visit(testUrl) - .then(function() { + .goto(testUrl, {'Host':'production.example.com'}) + .end() + .then(function(result) { + assert.notEqual(404, result.code) done(); }) .catch(function(error) { diff --git a/test/functional/07-sni.js b/test/functional/07-sni.js index 84c3a727..c19fe1c5 100644 --- a/test/functional/07-sni.js +++ b/test/functional/07-sni.js @@ -2,11 +2,10 @@ var assert = require('assert'); var exec = require('child_process').exec; -var Browser = require('zombie'); describe('ssl server name indication', function(done) { it('local host should serve local cert', function(done) { - exec('openssl s_client -connect local.example.com:443 -servername local.example.com', { + exec(`openssl s_client -connect ${process.env.EXAMPLE_COM}:443 -servername local.example.com`, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { assert.ifError(! stdout.match('CN=local.example.com')); @@ -15,7 +14,7 @@ describe('ssl server name indication', function(done) { }); it('production.domain (1/3) should serve production cert', function(done) { - exec('openssl s_client -connect production.example.com:443 -servername production.example.com', { + exec(`openssl s_client -connect ${process.env.EXAMPLE_COM}:443 -servername production.example.com`, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { assert.ifError(! stdout.match('CN=example.com')); @@ -24,7 +23,7 @@ describe('ssl server name indication', function(done) { }); it('www.domain (2/3) should serve production cert', function(done) { - exec('openssl s_client -connect www.example.com:443 -servername www.example.com', { + exec(`openssl s_client -connect ${process.env.EXAMPLE_COM}:443 -servername www.example.com`, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { assert.ifError(! stdout.match('CN=example.com')); @@ -33,7 +32,7 @@ describe('ssl server name indication', function(done) { }); it('bare domain (3/3) should serve production cert', function(done) { - exec('openssl s_client -connect production.example.com:443 -servername production.example.com', { + exec(`openssl s_client -connect ${process.env.EXAMPLE_COM}:443 -servername production.example.com`, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { assert.ifError(! stdout.match('CN=example.com')); @@ -42,7 +41,7 @@ describe('ssl server name indication', function(done) { }); it('not vulnerable to CVE-2014-3566 (SSLv3 POODLE)', function(done) { - exec('openssl s_client -connect local.example.com:443 -ssl3', { + exec(`openssl s_client -connect ${process.env.EXAMPLE_COM}:443 -ssl3`, { cwd: process.cwd() + '/temp' }, function(err, stdout, stderr) { assert.ifError(! stderr.match('routines:SSL3_READ_BYTES:sslv3 alert handshake failure')); diff --git a/test/functional/08-robots.js b/test/functional/08-robots.js index d1362ff8..b15b51ad 100644 --- a/test/functional/08-robots.js +++ b/test/functional/08-robots.js @@ -1,18 +1,25 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../lib/node/nightmare'); -describe('robots.txt', function(done) { +describe(`robots.txt`, function(done) { it('on local, should disallow all', function(done) { var browser = new Browser(); browser - .visit('http://local.example.com/robots.txt') - .then(function() { - assert(!!browser.text().match('# Block everything...')); + .goto(`http://${process.env.EXAMPLE_COM}/robots.txt`, {'Host':'local.example.com'}) + .evaluate(function() { + return document.body.innerText; + }) + .end() + .then(function(text) { + assert(!!text.match('# Block everything...'), text); + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); @@ -20,11 +27,19 @@ describe('robots.txt', function(done) { var browser = new Browser(); browser - .visit('http://example.com/robots.txt') - .then(function() { - assert(!!browser.text().match('# Sitemap: ')); + .goto(`http://${process.env.EXAMPLE_COM}/robots.txt`, {'Host':'example.com'}) + .evaluate(function() { + return document.body.innerText; + }) + .end() + .then(function(text, result) { + console.dir(result, {colors:true}); + assert(!!text.match('# Sitemap: '), text); + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); }); diff --git a/test/functional/varnish/cache.js b/test/functional/varnish/cache.js index 217c7686..c87ed93a 100644 --- a/test/functional/varnish/cache.js +++ b/test/functional/varnish/cache.js @@ -1,18 +1,25 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../../lib/node/nightmare'); describe('Varnish', function() { it('should access backend', function(done) { var browser = new Browser(); browser - .visit('http://example.com/') - .then(null, function() { - assert.equal('Evolution WordPress Test – Just another WordPress site', browser.text('title')); + .goto(`http://${process.env.EXAMPLE_COM}/`, {'Host':'example.com'}) + .evaluate(function() { + return document.title; + }) + .end() + .then(function(title) { + assert.equal(title, 'Evolution WordPress Test – Just another WordPress site'); + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); @@ -21,11 +28,15 @@ describe('Varnish', function() { var browser = new Browser(); browser - .visit('http://example.com/') - .then(null, function() { - assert.equal('cached', browser.resources[0].response.headers['x-cache']); + .goto(`http://${process.env.EXAMPLE_COM}/`, {'Host':'example.com'}) + .end() + .then(function(result) { + assert.equal(result.headers['x-cache'], 'cached') + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); }); @@ -35,19 +46,19 @@ describe('Varnish', function() { var browser = new Browser(); browser - .visit('http://example.com/wp-admin') - .then(null, function() { - assert(browser.resources.browser.getCookie('wordpress_test_cookie')); + .goto(`http://${process.env.EXAMPLE_COM}/`, { + 'Host':'example.com', + 'Cookie':'wordpress_test_cookie=WP+Cookie+check', }) - .then(null, function() { - return browser.visit('http://example.com/'); + .end() + .then(function(result) { + assert.equal(result.headers['age'], '0') + assert.equal(result.headers['x-cache'], 'uncached') + done() }) - .then(null, function() { - assert.equal('Evolution WordPress Test – Just another WordPress site', browser.text('title')); - assert.equal(0, browser.resources[0].response.headers.age); - assert.equal('uncached', browser.resources[0].response.headers['x-cache']); + .catch(function(error) { + done(error) }) - .then(done, done) ; }); }); @@ -56,62 +67,64 @@ describe('Varnish', function() { it('should ignore tracking cookies for cache', function(done) { var browser = new Browser(); - browser.setCookie({ - name: '_test', - value: +new Date(), - domain: 'example.com', - path: '/', - }); - browser - .visit('http://example.com/') - .then(null, function() { - assert(browser.getCookie('_test')); - assert(browser.resources[0].response.headers.age); - assert.equal('cached', browser.resources[0].response.headers['x-cache']); + .goto(`http://${process.env.EXAMPLE_COM}/`, { + 'Host':'example.com', + 'Cookie':`_test=${new Date()}`, + }) + .end() + .then(function(result) { + assert(result.headers['age']) + assert.equal(result.headers['x-cache'], 'cached') + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); }); describe('with an application cookies', function() { - var cookie = { - name: 'test', - value: +new Date(), - domain: 'example.com', - path: '/', - }; + var cookie = `test=${new Date()}`; it('should not be cached initially', function(done) { var browser = new Browser(); - browser.setCookie(cookie); - browser - .visit('http://example.com/') - .then(null, function() { - assert(browser.getCookie('test')); - assert.equal(0, browser.resources[0].response.headers.age); - assert.equal('uncached', browser.resources[0].response.headers['x-cache']); + .goto(`http://${process.env.EXAMPLE_COM}/`, { + 'Host':'example.com', + 'Cookie':cookie, + }) + .end() + .then(function(result) { + assert.equal(result.headers['age'], '0') + assert.equal(result.headers['x-cache'], 'uncached') + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); it('should be subsequently cached', function(done) { var browser = new Browser(); - browser.setCookie(cookie); - browser - .visit('http://example.com/') - .then(null, function() { - assert(browser.getCookie('test')); - assert(browser.resources[0].response.headers.age); - assert.equal('cached', browser.resources[0].response.headers['x-cache']); + .goto(`http://${process.env.EXAMPLE_COM}/`, { + 'Host':'example.com', + 'Cookie':cookie, + }) + .end() + .then(function(result) { + assert(result.headers['age']) + assert.equal(result.headers['x-cache'], 'cached') + done() + }) + .catch(function(error) { + done(error) }) - .then(done, done) ; }); }); diff --git a/test/functional/wordpress/redirect.js b/test/functional/wordpress/redirect.js index 288a8d1e..9155652b 100644 --- a/test/functional/wordpress/redirect.js +++ b/test/functional/wordpress/redirect.js @@ -1,22 +1,23 @@ 'use strict'; var assert = require('assert'); -var Browser = require('zombie'); +var Browser = require('../../../lib/node/nightmare'); describe('WordPress', function() { it('should redirect /wp-admin to /wp/wp-admin', function(done) { var browser = new Browser(); browser - .visit('http://example.com/wp-admin') - .then(function() { - var location = browser.location.toString(); - - assert(browser.redirected); - assert.equal(200, browser.statusCode); - assert.equal(0, location.indexOf('http://example.com/wp/wp-login')); + .goto(`http://${process.env.EXAMPLE_COM}/wp-admin`, {'Host':'example.com'}) + .end() + .then(function(result) { + assert.equal(200, result.code); + assert.equal(0, result.url.indexOf('http://example.com/wp/wp-login')); + done() + }) + .catch(function(error) { + done(err) }) - .then(done, done) ; }); });