Automatically version your Javascript files with Subversion

17 Feb

We’ve already talked about serving static files in a good way. This post can be read as a follow-up to the Particletree article “Automatically Version Your CSS and JavaScript Files“.

A little excerpt of the article (read it!): When serving static files like .js or .css, the idea is having them cached by the client (a.k.a. browser) for a very long time (let’s say 10 years). But if we modify one of this static files, we need a way for the client to get the new version downloaded again. With that in mind we introduce the “version” of the file in the name, either with mod_rewrite (or similar), either in the querystring as a parameter. Changing the version number will get the browser think is a new file, and get it downloaded again.

But as said, read the Particletree article, is a lot better explained there.

Version

Now the thing is how to determine that version number for each static file. Obviously the idea is not having it done by hand, and that’s why in the Particletree article they use the filemtime() function, to get the last modification timestamp for the file.

Although it’s a first option, personally I don’t like it so much. That would imply a disk access each time an uncached static file is requested, and even we can avoid that disk access saving the timestamps in a include file… Why not use the repository version itself?

The staticVersions.py script

And here’s my humbly contribution. Is a Python script that scans the specified folders for static files (js and css), and returns a PHP array with the name of the files and its version in the Subversion repository, that is, when the last change happened.

Download the script: staticVersions.py.

Example:
./staticVersions.py -d ./js -d ./css

<?php 

//$productionSite - if set and true, append versions to static files in template 'top.tpl'
$productionSite = true;

//array with filenames (as used in template 'top.tpl') and file SVN last revision
$staticFilesVersions = array(
    '/js/register.js' => '1' ,
    '/js/post.js' => '15' ,
    '/js/interface.js' => '25' ,
    '/js/visualizeus.js' => '73' ,
    '/css/general.css' => '1' ,
    '/css/ie.css' => '38' ,
    '/css/user.css' => '1' ,
    '/css/visualizeus.css' => '1' ,
    '/css/default.css' => '52' ,
    '/css/post.css' => '15' 
);
?>

Each time we run a svn update in production, we run the staticVersions.py script and get the include updated with the new file versions. I’m doing it with a simple shell script but probably you could use some of the subversion’s hooks for this.

It’s worth mention that the array keys that I define, fits with my current file structure in the same way I use it in the templates, and in the same way as the webserver serves them. If you’re going to use the script, maybe you have to adapt this to your own structure.

How all this fits in your templates

As I’ve already told, I use PHP native templates, no Smarty or similars, so: how all this fits in the templates?

As you’ve already noticed in the previous output, I define a global variable that tells if the site is in production or not, and depending on that flag either I rewrite the files with its repository version (production), or with the last modification timestamp (development). That’s this function work:

function autoVersion($url) {

    if (isset($GLOBALS['productionSite']) && $GLOBALS['productionSite']) {
        if (array_key_exists($url, $GLOBALS['staticFilesVersions']))
            $version = $GLOBALS['staticFilesVersions'][$url];
        else $version = '1';
    } else {
        $stat = stat($_SERVER['DOCUMENT_ROOT'] . $url);
        $version = $stat['mtime'];
    }

    return preg_replace('!\.([a-z]+?)$!', ".v$version.\$1", $url);
}

And little more to say. In your header template or bottom or wherever you load the static files, just make sure you’re using the autoVersion function, and also make sure you set up the appropiate mod_rewrite rules in Apache so the webserver doesn’t get crazy looking for inexistant files (see Particletree article) and you’re ready to go!

Comments are closed.