Deployment Techniques

jrburke edited this page Apr 21, 2013 · 3 revisions
Clone this wiki locally

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.

Live web server deployments

This scenario is when r.js is used with a live server where the following constraints are in place:

  1. Built assets not 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.

  2. 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 + rsync (unix/mac only)

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" ]);
    }
);