From 3bf8f4e2f092264360d4933626dee3ce7e334bad Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Tue, 4 Oct 2016 13:20:01 +0000 Subject: [PATCH 1/8] Add options to package task to exclude paths such as node_modules and bower_components --- tasks/package.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tasks/package.js b/tasks/package.js index d56ce90c..6e3437cd 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -66,6 +66,20 @@ module.exports = function(grunt) { var srcFiles = ['**', '!**/.gitkeep'].concat((config && config.srcFiles && config.srcFiles.length) ? config.srcFiles : '**'); var projFiles = (config && config.projFiles && config.projFiles.length) ? config.projFiles : []; + var exclude = grunt.config('config.packages.exclude'); + if (exclude !== false) { + var excludePaths = grunt.config('config.packages.excludePaths'); + if (!excludePaths || excludePaths.length == 0) { + excludePaths = ['bower_components', 'node_modules']; + } + + for (var key in excludePaths) { + excludePaths[key] = '!**/' + excludePaths[key] + '/**'; + } + srcFiles = srcFiles.concat(excludePaths); + projFiles = projFiles.concat(excludePaths); + } + // Look for a package target spec, build destination path. var packageName = grunt.option('name') || config.name || 'package'; var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; From ecdde95f80023dee1368e3058c53c63793bb4770 Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Tue, 4 Oct 2016 14:11:15 +0000 Subject: [PATCH 2/8] Fix eslint issues --- tasks/package.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/package.js b/tasks/package.js index 6e3437cd..62f5faa5 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -69,12 +69,12 @@ module.exports = function(grunt) { var exclude = grunt.config('config.packages.exclude'); if (exclude !== false) { var excludePaths = grunt.config('config.packages.excludePaths'); - if (!excludePaths || excludePaths.length == 0) { + if (!excludePaths || !excludePaths) { excludePaths = ['bower_components', 'node_modules']; } - for (var key in excludePaths) { - excludePaths[key] = '!**/' + excludePaths[key] + '/**'; + for (var i = 0; i < excludePaths.length; i++) { + excludePaths[i] = '!**/' + excludePaths[i] + '/**'; } srcFiles = srcFiles.concat(excludePaths); projFiles = projFiles.concat(excludePaths); From 2b386227719d3afb5fc67ce5d926aac459bd29de Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Tue, 4 Oct 2016 18:48:34 +0000 Subject: [PATCH 3/8] Testing rsync --- tasks/package.js | 73 ++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/tasks/package.js b/tasks/package.js index 62f5faa5..03eec615 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -61,11 +61,18 @@ module.exports = function(grunt) { grunt.registerTask('package', 'Package the operational codebase for deployment. Use package:compress to create an archive.', function() { grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-shell'); var config = grunt.config.get('config.packages'); var srcFiles = ['**', '!**/.gitkeep'].concat((config && config.srcFiles && config.srcFiles.length) ? config.srcFiles : '**'); var projFiles = (config && config.projFiles && config.projFiles.length) ? config.projFiles : []; + // Look for a package target spec, build destination path. + var packageName = grunt.option('name') || config.name || 'package'; + var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; + var tasks = []; + grunt.option('package-dest', destPath); + var exclude = grunt.config('config.packages.exclude'); if (exclude !== false) { var excludePaths = grunt.config('config.packages.excludePaths'); @@ -76,44 +83,50 @@ module.exports = function(grunt) { for (var i = 0; i < excludePaths.length; i++) { excludePaths[i] = '!**/' + excludePaths[i] + '/**'; } - srcFiles = srcFiles.concat(excludePaths); - projFiles = projFiles.concat(excludePaths); + //srcFiles = srcFiles.concat(excludePaths); + //projFiles = projFiles.concat(excludePaths); } - // Look for a package target spec, build destination path. - var packageName = grunt.option('name') || config.name || 'package'; - var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; - var tasks = []; - grunt.option('package-dest', destPath); + var rsync = 'rsync -a ' + + '--exclude node_modules ' + + '--exclude bower_components ' + + '--exclude .git ' + + '--exclude sites/*/files ' + + '--exclude xmlrpc.php '; + + var srcPath = grunt.config('config.buildPaths.html'); + var destSrc = path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''); + var command = rsync + srcPath + '/ ' + destSrc + '/'; + console.log(command); + console.log(destSrc) + grunt.config('shell.mkSrc', { + command: 'mkdir -p ' + destSrc + }); + grunt.config('shell.srcFiles', { + command: command + }); - grunt.config('copy.package', { - files: [ - { - expand: true, - cwd: '<%= config.buildPaths.html %>', - src: srcFiles, - dest: path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''), - dot: true, - follow: true - }, - { - expand: true, - src: projFiles, - dest: path.resolve(destPath, grunt.config.get('config.packages.dest.devResources') || ''), - dot: true, - follow: true - } - ], - options: { - gruntLogHeader: false, - mode: true - } + var destProj = path.resolve(destPath, grunt.config.get('config.packages.dest.devResources') || ''); + grunt.config('shell.mkProj', { + command: 'mkdir -p ' + destProj }); + for (var i = 0; i < projFiles.length; i++) { + var command = rsync + srcPath + '/' + projFiles[i] + ' ' + destProj; + console.log(command); + grunt.config('shell.projFiles' + i, { + command: command + }); + } grunt.config.set('clean.packages', [destPath]); tasks.push('clean:packages'); - tasks.push('copy:package'); + tasks.push('shell:mkSrc'); + tasks.push('shell:srcFiles'); + tasks.push('shell:mkProj'); + for (var i = 0; i < projFiles.length; i++) { + tasks.push('shell:projFiles' + i); + } // If the `composer.json` file is being packaged, rebuild composer dependencies without dev. if (projFiles.find(function(pattern) { From 9115c50815c3438ccbd0b71e542c81baf4357666 Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Tue, 4 Oct 2016 19:15:57 +0000 Subject: [PATCH 4/8] Revert "Testing rsync" This reverts commit 2b386227719d3afb5fc67ce5d926aac459bd29de. --- tasks/package.js | 73 ++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/tasks/package.js b/tasks/package.js index 03eec615..62f5faa5 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -61,18 +61,11 @@ module.exports = function(grunt) { grunt.registerTask('package', 'Package the operational codebase for deployment. Use package:compress to create an archive.', function() { grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-shell'); var config = grunt.config.get('config.packages'); var srcFiles = ['**', '!**/.gitkeep'].concat((config && config.srcFiles && config.srcFiles.length) ? config.srcFiles : '**'); var projFiles = (config && config.projFiles && config.projFiles.length) ? config.projFiles : []; - // Look for a package target spec, build destination path. - var packageName = grunt.option('name') || config.name || 'package'; - var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; - var tasks = []; - grunt.option('package-dest', destPath); - var exclude = grunt.config('config.packages.exclude'); if (exclude !== false) { var excludePaths = grunt.config('config.packages.excludePaths'); @@ -83,50 +76,44 @@ module.exports = function(grunt) { for (var i = 0; i < excludePaths.length; i++) { excludePaths[i] = '!**/' + excludePaths[i] + '/**'; } - //srcFiles = srcFiles.concat(excludePaths); - //projFiles = projFiles.concat(excludePaths); + srcFiles = srcFiles.concat(excludePaths); + projFiles = projFiles.concat(excludePaths); } - var rsync = 'rsync -a ' - + '--exclude node_modules ' - + '--exclude bower_components ' - + '--exclude .git ' - + '--exclude sites/*/files ' - + '--exclude xmlrpc.php '; - - var srcPath = grunt.config('config.buildPaths.html'); - var destSrc = path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''); - var command = rsync + srcPath + '/ ' + destSrc + '/'; - console.log(command); - console.log(destSrc) - grunt.config('shell.mkSrc', { - command: 'mkdir -p ' + destSrc - }); - grunt.config('shell.srcFiles', { - command: command - }); + // Look for a package target spec, build destination path. + var packageName = grunt.option('name') || config.name || 'package'; + var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; + var tasks = []; + grunt.option('package-dest', destPath); - var destProj = path.resolve(destPath, grunt.config.get('config.packages.dest.devResources') || ''); - grunt.config('shell.mkProj', { - command: 'mkdir -p ' + destProj + grunt.config('copy.package', { + files: [ + { + expand: true, + cwd: '<%= config.buildPaths.html %>', + src: srcFiles, + dest: path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''), + dot: true, + follow: true + }, + { + expand: true, + src: projFiles, + dest: path.resolve(destPath, grunt.config.get('config.packages.dest.devResources') || ''), + dot: true, + follow: true + } + ], + options: { + gruntLogHeader: false, + mode: true + } }); - for (var i = 0; i < projFiles.length; i++) { - var command = rsync + srcPath + '/' + projFiles[i] + ' ' + destProj; - console.log(command); - grunt.config('shell.projFiles' + i, { - command: command - }); - } grunt.config.set('clean.packages', [destPath]); tasks.push('clean:packages'); - tasks.push('shell:mkSrc'); - tasks.push('shell:srcFiles'); - tasks.push('shell:mkProj'); - for (var i = 0; i < projFiles.length; i++) { - tasks.push('shell:projFiles' + i); - } + tasks.push('copy:package'); // If the `composer.json` file is being packaged, rebuild composer dependencies without dev. if (projFiles.find(function(pattern) { From b975570f7f21974c0d5ef6c1bcf69572f3b9652d Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Tue, 4 Oct 2016 21:03:35 +0000 Subject: [PATCH 5/8] Add rsync support. Experimental, testing performance --- tasks/package.js | 111 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/tasks/package.js b/tasks/package.js index 62f5faa5..2bf63f12 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -61,41 +61,86 @@ module.exports = function(grunt) { grunt.registerTask('package', 'Package the operational codebase for deployment. Use package:compress to create an archive.', function() { grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-shell'); var config = grunt.config.get('config.packages'); - var srcFiles = ['**', '!**/.gitkeep'].concat((config && config.srcFiles && config.srcFiles.length) ? config.srcFiles : '**'); + var srcFiles = (config && config.srcFiles && config.srcFiles.length) ? config.srcFiles : []; var projFiles = (config && config.projFiles && config.projFiles.length) ? config.projFiles : []; - var exclude = grunt.config('config.packages.exclude'); - if (exclude !== false) { - var excludePaths = grunt.config('config.packages.excludePaths'); - if (!excludePaths || !excludePaths) { - excludePaths = ['bower_components', 'node_modules']; + // Look for a package target spec, build destination path. + var packageName = grunt.option('name') || config.name || 'package'; + + // Determine if we are using rsync and vmPath and tarball for performance. + var useRsync = grunt.config('config.packages.rsync'); + useRsync = (typeof(useRsync) !== 'undefined') ? useRsync : true; + + // When using rsync, generate to a path that can be mounted in the VM. + var vmPath = grunt.config.get('config.buildPaths.packages.vmpath') || 'vm_package'; + var destPath = vmPath + '/' + packageName; + var finalPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; + if (!useRsync) { + // If not using rsync, then generate to final path. + destPath = finalPath; + } + var tasks = []; + grunt.option('package-dest', destPath); + + var excludePaths = ['bower_components', 'node_modules', '.gitkeep']; + + if (useRsync) { + + for (var i=0; i < srcFiles.length; i++) { + var item = srcFiles[i]; + if (item.substr(0,1) === '!') { + item = item.slice(1); + item = item.replace('**', '*'); + excludePaths.push(item); + } + } + + var rsync = 'rsync -ahWL --no-l --stats --chmod=Du+rwx '; + for (var i=0; i < excludePaths.length; i++) { + rsync = rsync + "--exclude " + excludePaths[i] + ' '; } + var srcPath = grunt.config('config.buildPaths.html'); + var destSrc = path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''); + var rsyncCommand = rsync + srcPath + '/ ' + destSrc + '/'; + grunt.config('shell.mkSrc', { + command: 'mkdir -p ' + destSrc + }); + grunt.config('shell.srcFiles', { + command: rsyncCommand + }); + + } + else { + // Use slower copy if rsync is disabled in options. + srcFiles.unshift('**'); for (var i = 0; i < excludePaths.length; i++) { - excludePaths[i] = '!**/' + excludePaths[i] + '/**'; + excludePaths[i] = '!**/' + excludePaths[i]; } srcFiles = srcFiles.concat(excludePaths); - projFiles = projFiles.concat(excludePaths); + grunt.config('copy.source', { + files: [ + { + expand: true, + cwd: '<%= config.buildPaths.html %>', + src: srcFiles, + dest: path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''), + dot: true, + follow: true + } + ], + options: { + gruntLogHeader: false, + mode: true + } + }); } - // Look for a package target spec, build destination path. - var packageName = grunt.option('name') || config.name || 'package'; - var destPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; - var tasks = []; - grunt.option('package-dest', destPath); - grunt.config('copy.package', { files: [ - { - expand: true, - cwd: '<%= config.buildPaths.html %>', - src: srcFiles, - dest: path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''), - dot: true, - follow: true - }, { expand: true, src: projFiles, @@ -113,6 +158,14 @@ module.exports = function(grunt) { grunt.config.set('clean.packages', [destPath]); tasks.push('clean:packages'); + + if (useRsync) { + tasks.push('shell:mkSrc'); + tasks.push('shell:srcFiles'); + } + else { + tasks.push('copy:source'); + } tasks.push('copy:package'); // If the `composer.json` file is being packaged, rebuild composer dependencies without dev. @@ -152,6 +205,20 @@ module.exports = function(grunt) { tasks.push('compress:package'); } + if (useRsync) { + // Tar the generated package and move it out of the VM. + grunt.config('shell.tar', { + command: 'tar czf ' + vmPath + '/' + packageName + '.tar.gz ' + destPath + }); + + grunt.config('shell.mvtar', { + command: 'mv ' + vmPath + '/' + packageName + '.tar.gz ' + finalPath + }); + + tasks.push('shell:tar'); + tasks.push('shell:mvtar'); + } + grunt.task.run(tasks); }); From 30d59971838d98a595c4ee6c2e20f6e65bd5b534 Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Wed, 5 Oct 2016 14:26:22 +0000 Subject: [PATCH 6/8] Use existing compress task and grunt-mkdir. Fix eslint stuff --- tasks/package.js | 86 ++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/tasks/package.js b/tasks/package.js index 2bf63f12..b105cefa 100644 --- a/tasks/package.js +++ b/tasks/package.js @@ -71,56 +71,68 @@ module.exports = function(grunt) { var packageName = grunt.option('name') || config.name || 'package'; // Determine if we are using rsync and vmPath and tarball for performance. - var useRsync = grunt.config('config.packages.rsync'); - useRsync = (typeof(useRsync) !== 'undefined') ? useRsync : true; + var useRsync = grunt.config('config.packages.rsync') || false; + // Determine if we are creating a tarball archive. + var archive = grunt.config('config.packages.archive') || useRsync; // When using rsync, generate to a path that can be mounted in the VM. - var vmPath = grunt.config.get('config.buildPaths.packages.vmpath') || 'vm_package'; + var vmPath = grunt.config.get('config.packages.vmData') || 'build/vm_data'; var destPath = vmPath + '/' + packageName; var finalPath = grunt.config.get('config.buildPaths.packages') + '/' + packageName; if (!useRsync) { - // If not using rsync, then generate to final path. + // If not using rsync, then directly generate to final path. destPath = finalPath; } + var tasks = []; + var cleanPaths = [destPath]; + if (destPath !== finalPath) { + cleanPaths.push(finalPath); + } grunt.option('package-dest', destPath); + grunt.config.set('clean.packages', cleanPaths); + tasks.push('clean:packages'); var excludePaths = ['bower_components', 'node_modules', '.gitkeep']; if (useRsync) { - - for (var i=0; i < srcFiles.length; i++) { - var item = srcFiles[i]; - if (item.substr(0,1) === '!') { + // Pull negate conditions from srcFiles into excludePaths. + for (var srcIndex = 0; srcIndex < srcFiles.length; srcIndex++) { + var item = srcFiles[srcIndex]; + if (item.substr(0, 1) === '!') { item = item.slice(1); item = item.replace('**', '*'); excludePaths.push(item); } } + // Setup default rsync command and options. var rsync = 'rsync -ahWL --no-l --stats --chmod=Du+rwx '; - for (var i=0; i < excludePaths.length; i++) { - rsync = rsync + "--exclude " + excludePaths[i] + ' '; + for (var pathIndex = 0; pathIndex < excludePaths.length; pathIndex++) { + rsync = rsync + "--exclude " + excludePaths[pathIndex] + ' '; } + // Ensure destination exists and sent the rsync. var srcPath = grunt.config('config.buildPaths.html'); var destSrc = path.resolve(destPath, grunt.config.get('config.packages.dest.docroot') || ''); var rsyncCommand = rsync + srcPath + '/ ' + destSrc + '/'; - grunt.config('shell.mkSrc', { - command: 'mkdir -p ' + destSrc + + grunt.config.set('mkdir.package', { + options: { + create: [destSrc] + } }); - grunt.config('shell.srcFiles', { + + grunt.config('shell.rsync', { command: rsyncCommand }); - - } - else { + } else { // Use slower copy if rsync is disabled in options. srcFiles.unshift('**'); + // Add any additional excludePaths to srcFiles. for (var i = 0; i < excludePaths.length; i++) { - excludePaths[i] = '!**/' + excludePaths[i]; + srcFiles.push('!**/' + excludePaths[i]); } - srcFiles = srcFiles.concat(excludePaths); grunt.config('copy.source', { files: [ { @@ -139,6 +151,7 @@ module.exports = function(grunt) { }); } + // Always copy any files specified in projFiles. grunt.config('copy.package', { files: [ { @@ -155,15 +168,10 @@ module.exports = function(grunt) { } }); - grunt.config.set('clean.packages', [destPath]); - - tasks.push('clean:packages'); - if (useRsync) { - tasks.push('shell:mkSrc'); - tasks.push('shell:srcFiles'); - } - else { + tasks.push('mkdir:package'); + tasks.push('shell:rsync'); + } else { tasks.push('copy:source'); } tasks.push('copy:package'); @@ -184,39 +192,31 @@ module.exports = function(grunt) { tasks.push('composer:drupal-scaffold'); } - if (this.args[0] && this.args[0] === 'compress') { + if (archive || (this.args[0] && this.args[0] === 'compress')) { grunt.loadNpmTasks('grunt-contrib-compress'); grunt.config('compress.package', { options: { archive: destPath + '.tgz', - mode: 'tgz', - gruntLogHeader: false + mode: 'tgz' }, files: [ { expand: true, dot: true, - cwd: grunt.config.get('config.buildPaths.packages') + '/' + packageName, + cwd: destPath, src: ['**'] } ] }); tasks.push('compress:package'); - } - if (useRsync) { - // Tar the generated package and move it out of the VM. - grunt.config('shell.tar', { - command: 'tar czf ' + vmPath + '/' + packageName + '.tar.gz ' + destPath - }); - - grunt.config('shell.mvtar', { - command: 'mv ' + vmPath + '/' + packageName + '.tar.gz ' + finalPath - }); - - tasks.push('shell:tar'); - tasks.push('shell:mvtar'); + if (destPath !== finalPath) { + grunt.config('shell.mvArchive', { + command: 'mv ' + destPath + '.tgz ' + finalPath + '.tgz' + }); + tasks.push('shell:mvArchive'); + } } grunt.task.run(tasks); From 1139d5cb522794c7c99a11feb962706f0029fd52 Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Wed, 5 Oct 2016 14:56:41 +0000 Subject: [PATCH 7/8] Added updated docs --- docs/60_PACKAGE.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/60_PACKAGE.md b/docs/60_PACKAGE.md index f2ed4443..73322116 100644 --- a/docs/60_PACKAGE.md +++ b/docs/60_PACKAGE.md @@ -51,6 +51,35 @@ directory. Defaults to the docroot. packages directory. This can be overridden by calling grunt package with the `--name` parameter. +**archive**: Used to enable the `compress` option via the configuration instead +of the command line. Defaults to false. + +**rsync**: If set to true, rsync will be used instead of file copying for +improved performance. This automatically enables the `compress` option and will +write the archive to the package destination. + +**vmData**: This path is used as the intermediate generation directory for the +package when using the `rsync` option. By selecting a volume mounted in your +VM `/data` area, this can significantly improve performance. The archive is +still moved to the normal package destination when complete. Defaults to +`build/vm_data`. + +## Performance + +When running `grunt package` within a docker container, the default file +operations will be using NFS to update the files on your local disk, which will +be very slow. Using the `rsync` option along with creating the `build/vm_data` +intermediate mount point in your docker container can improve performance by +a factor of 20 or more. However, the downside is only the compressed archive of +your package will be written to your local disk. The full package folder will +only be accessible within your docker container. + +A sample docker-composer.yml entry to mount the vm_data looks like this: +``` + volumes: + - /data/PROJECTNAME/package:/var/www/build/vm_data +``` + ## Packaging for Acquia The `package` command has the flexibility to support many different use cases, @@ -65,6 +94,7 @@ Acquia repository with support for custom hooks and scripts. "packages": { "srcFiles": ["!sites/*/files/**", "!xmlrpc.php", "!modules/php/*"], "projFiles": ["README*", "bin/**", "hooks/**"], + "rsync": true, "dest": { "docroot": "docroot" } From 33519bb906de8028d65d8d6cc4221f4cb64ed7e4 Mon Sep 17 00:00:00 2001 From: Mike Potter Date: Wed, 5 Oct 2016 18:18:05 +0000 Subject: [PATCH 8/8] Remove --quiet from tests --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3450a18b..c585c2a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,15 +44,15 @@ script: - echo "Working copy disk usage"; du -chs . - grunt --version - grunt help - - grunt --quiet --timer + - grunt --timer - echo "Fully built disk usage"; du -chs . - - grunt install --db-url=sqlite:/`pwd`/.ht.sqlite --quiet + - grunt install --db-url=sqlite:/`pwd`/.ht.sqlite - grunt serve:test >/dev/null & - until curl -I -XGET -s $GDT_TEST_URL 2>/dev/null | egrep -q '^HTTP.*200'; do sleep 0.5; done - vendor/bin/drush --root=build/html en -y simpletest - grunt test - sleep 1; while (ps aux | grep '[b]ehat' > /dev/null); do sleep 1; done - for pid in `ps aux | grep drush | grep runserver | awk '{print $2}'`; do echo "Stopping drush pid $pid"; kill -SIGINT $pid; done; - - grunt package --quiet + - grunt package - mocha --timeout 30000 node_modules/grunt-drupal-tasks/test/build.js - mocha node_modules/grunt-drupal-tasks/test/library.js