diff --git a/CHANGELOG.md b/CHANGELOG.md index 2025378..d00c165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ### 1.0.0 (2015-10-04) +- Improved image cleanup tactics. Images should be slightly smaller. +- Improved the way tupperbuild executes commands. If an error occurs, it will report the `stderr` and `stdout`. +- Improved order of onbuild triggers to utilise intermediary layer caching (thanks @ijpiantanida). +- Added ability to specify pre and post `meteor build` commands to be run. See README. + Bundles: - Node.js 0.10.40 diff --git a/Dockerfile b/Dockerfile index 00b9b46..c4bcab8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ EXPOSE 80 ENTRYPOINT sh /tupperware/scripts/start_app.sh ONBUILD COPY ./.meteor/release /app/.meteor/release + ONBUILD COPY ./*.json /app/ ONBUILD RUN sh /tupperware/scripts/on_build.sh install diff --git a/README.md b/README.md index 6aa53d7..a0018f7 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Default configuration options: ```javascript /* tupperware.json */ { + "preBuildCommands": [], "dependencies": { "phantomJs": false, "imageMagick": false @@ -86,21 +87,17 @@ Default configuration options: - dependencies - phantomJs: `true` or `false` (for installing PhantomJS) - imageMagick: `true` or `false` (for installing ImageMagick) +- preBuildCommands `[string]` (an array of commands to run before `meteor build`. Paths are relative to your app directory) +- postBuildCommands `[string]` (an array of commands to run after `meteor build`. Paths are relative to your app directory) - buildOptions - mobileServerUrl: `false` or type `string` (for specifying a server URL if you have mobile clients via Cordova) - additionalFlags: `false` or type `string` (for passing additional command line flags to `meteor build`) -## Comparison with meteord +### Pre and post build commands -1. meteor-tupperware produces smaller images than meteord. +As above, you can use the `preBuildCommands` option in `tupperware.json` to specify a list of commands that should be run prior to `meteor build`. You can use `postBuildCommands` to specify a list of commands that should be run after. The commands are executed within your app directory (`/app` with the container image). -1. meteor-tupperware supports configuring additional build flags (like --mobile-settings or --server). meteord is not this flexible. - -1. meteor-tupperware doesn't include PhantomJS by default, but you can include it with one setting. meteord bundles it in irrespective of necessity (producing larger images). - -1. meteor-tupperware allows you to easily include ImageMagick if it is a dependency of your app. - -1. meteord allows you to run an already-built app bundle inside a container from: a) a local mount or; b) a downloadable bundle archive. meteor-tupperware doesn't support this. +This can be useful for installing private smart packages for your Meteor app prior to building, or performing post build transformations/cleanup. ## Contributions diff --git a/includes/scripts/on_build.sh b/includes/scripts/on_build.sh index 54e2123..3a0aa34 100644 --- a/includes/scripts/on_build.sh +++ b/includes/scripts/on_build.sh @@ -4,7 +4,4 @@ BASEDIR=`dirname $0` . $BASEDIR/_common.sh -node $TUPPERBUILD_DIR/main.js $1 -check_code $? - -. $BASEDIR/_cleanup_deps.sh +exec node $TUPPERBUILD_DIR/main.js $1 diff --git a/includes/tupperbuild/main.js b/includes/tupperbuild/main.js index 776b9a1..f032995 100644 --- a/includes/tupperbuild/main.js +++ b/includes/tupperbuild/main.js @@ -11,13 +11,16 @@ var tupperwareJsonDefaults = { "phantomJs": false, "imageMagick": false }, + "preBuildCommands": [], + "postBuildCommands": [], "buildOptions": { "mobileServerUrl": false, "additionalFlags": false } }; -var copyPath = '/app', +var runMode = '', + copyPath = '/app', meteorReleaseString, meteorVersion, tupperwareJson = {}; @@ -76,7 +79,7 @@ function printBanner (done) { ].join("\n") ); log.info("github.com/chriswessels/meteor-tupperware (tupperbuild v" + pkgjson.version + ")\n"); - + log.info("Running in " + runMode + " mode."); done(); } @@ -85,7 +88,8 @@ function checkCopyPath (done) { try { meteorReleaseString = fs.readFileSync(copyPath + '/.meteor/release'); } catch (e) { - suicide("This doesn't look like a Meteor project.", e.toString()); + log.error("This doesn't look like a Meteor project."); + suicide(); } done(); @@ -252,9 +256,55 @@ function downloadMeteorInstaller (done) { } function installMeteor (done) { + // Have to use child_process.spawn instead of child_process.exec here + // Otherwise Meteor installer dies randomly... + log.info('Installing Meteor ' + meteorVersion + '...'); - var cmd = 'sh /tmp/install_meteor.sh'; - child_process.exec(cmd, _.partial(handleExecError, done, cmd, 'install Meteor.js')); + + var cmd = 'sh', + args = ['/tmp/install_meteor.sh']; + + var stdOut = '', stdErr = ''; + + var child = child_process.spawn(cmd, args); + child.stdout.on('data', function (data) { + stdOut += data; + }); + child.stderr.on('data', function (data) { + stdErr += data; + }); + child.on('close', function (code) { + var error; + if (code !== 0) { + error = { code: code }; + } + handleExecError(done, cmd + ' ' + args.join(' '), 'install Meteor.js', error, stdOut, stdErr); + }); +} + +function runPreBuildCommands (done) { + if (tupperwareJson.preBuildCommands.length > 0) { + log.info('Running pre-build commands...'); + + var tasks = []; + + _.each(tupperwareJson.preBuildCommands, function (cmd, index) { + tasks.push(function (done) { + child_process.exec(cmd, { + cwd: copyPath + }, _.partial(handleExecError, done, cmd, 'run pre-build command')); + }); + }); + + tasks.push(function () { + done(); + }); + + async.series(tasks); + + } else { + done(); + } } function buildApp (done) { @@ -279,6 +329,31 @@ function npmInstall (done) { }, _.partial(handleExecError, done, cmd, 'install your application\'s npm dependencies')); } +function runPostBuildCommands (done) { + if (tupperwareJson.postBuildCommands.length > 0) { + log.info('Running post-build commands...'); + + var tasks = []; + + _.each(tupperwareJson.postBuildCommands, function (cmd, index) { + tasks.push(function (done) { + child_process.exec(cmd, { + cwd: copyPath + }, _.partial(handleExecError, done, cmd, 'run post-build command')); + }); + }); + + tasks.push(function () { + done(); + }); + + async.series(tasks); + + } else { + done(); + } +} + function runCleanup (done) { log.info("Performing final image cleanup..."); @@ -302,7 +377,11 @@ function printDone (done) { done(); } -if (process.argv[2] === "install") { +// Kick things off + +runMode = process.argv[2]; + +if (runMode === "install") { async.series([ printBanner, checkCopyPath, @@ -311,12 +390,14 @@ if (process.argv[2] === "install") { downloadMeteorInstaller, installMeteor ]); -} else if (process.argv[2] === "build") { +} else if (runMode === "build") { async.series([ printBanner, extractTupperwareJson, + runPreBuildCommands, buildApp, npmInstall, + runPostBuildCommands, runCleanup, printDone ]);