-
Notifications
You must be signed in to change notification settings - Fork 671
Deployment Techniques
r.js is an optimization tool that builds javascript and css before deployment. As such, it is deployment-agnostic. There are several tools and techniques that can help make deployment easier once optimization and packaging are complete. Some of these techniques are illustrated here to provide some useful suggestions.
Firstly, there are a couple problems that need to be solved.
- Built assets should not be committed to git
There are several reasons for this, the most significant being _maintenance_. Remembering to build before committing to git can be a pain. For instance, you might change some css that looks right in development. It's been tested and it's ready for testing on a server running in production mode. But then, after pushing to your server, you realize the built assets weren't updated. You end up with superfluous commits that contain messages like "Update built files". A git repo that ignores built files forces servers to build the files themselves, which ensures your static assets are always up-to-date in production. It also makes your repo easier to navigate, as you won't ever need to see diffs of minified javascript.
- Race conditions
This is probably the most important problem to solve. Deploying static assets with __r.js__ on a live web server is not the best solution. __r.js__ keeps optimization as simple as possible, which means it just removes the build folder completely before rebuilding. The consequence is that for the time it takes __r.js__ to build your javascript and css files, those files will not be available on your server. It may only be a few seconds, but depending on your server load, it will cause multiple 404s for any user requesting static assets at that time. This can be avoided.
grunt.js, the javascript task runner, and the unix-native copying tool rsync
can be very useful when deploying static assets to a live server. The arguments for using grunt can be found on the grunt website. The argument for using rsync
is simple. The nice thing about rsync is it only updates files that changed. Here is an example deployment task that assumes you need to build to a static folder within a dynamic web server:
/**
* Example app.build.js
*/
{
// Important: a temporary build directory location outside the static folder
"dir": "./build",
"appDir": "./website/public/",
"baseUrl": "js",
"optimizeCss": "standard",
"paths": {
"jquery": "empty:"
},
"modules": [{ "name": "main" }]
}
/**
* Gruntfile.js
*/
var requirejs = require("requirejs"),
exec = require("child_process").exec,
fatal = grunt.fail.fatal,
log = grunt.log,
verbose = grunt.verbose,
// Your r.js build configuration
buildConfigMain = grunt.file.readJSON("app.build.js");
// Transfer the build folder to the right location on the server
grunt.registerTask(
"transfer",
"Transfer the build folder to ./website/build and remove it",
function() {
var done = this.async();
// Delete the build folder locally after transferring
exec("rsync -rlv --delete --delete-after ./build ./website && rm -rf ./build",
function( err, stdout, stderr ) {
if ( err ) {
fatal("Problem with rsync: " + err + " " + stderr );
}
verbose.writeln( stdout );
log.ok("Rsync complete.");
done();
});
}
);
// Build static assets using r.js
grunt.registerTask(
"build",
"Run the r.js build script",
function() {
var done = this.async();
log.writeln("Running build...");
requirejs.optimize(buildConfigMain, function( output ) {
log.writeln( output );
log.ok("Main build complete.");
done();
}, function( err ) {
fatal( "Main build failure: " + err );
});
// This is run after the build completes
grunt.task.run([ "transfer" ]);
}
);