Running JSLint as a Subversion Commit Hook with PHP and Rhino

By Gavin Davies

We recently blogged about using JSLint to automatically find trailing commas, which has helped us find IE7 bugs before they ever reach the browser. We thought we’d take this approach a step further and put a hook on our Subversion repository that not only checks for trailing commas, but reports any JSLint errors that have been committed. We’ll talk through the why and how so you can set this up in your own company.

Why run JSLint on a commit hook?

JavaScript is a sloppy language, but inside it there is an elegant, better language. JSLint helps you to program in that better language and to avoid most of the slop.
Douglas Crockford

JavaScript is a wonderful, powerful, expressive language, but it gives you plenty of scope to shoot yourself in the foot. JSLint is a static code analysis tool that scans the source for common coding errors. Although Box UK’s coding standards state that “all JavaScript files should pass JSLint”, we have not, up to now, done anything to enforce this. Following a spate of internal tickets related to bugs in IE7 that could have been avoided by following JSLint recommended best practises, we decided to implement a commit hook.

Post-Commit Hook results

JSLint errors are not necessarily showstoppers so it didn’t make sense to start failing commits, so we decided to use a temporary post-commit hook. Post commit hooks do not fail the commit – they happen after the commit has already taken place. We are planning to move to a pre-commit hook after a couple of months, once our developers have had chance to get used to making sure their code passes JSLint.

 

 

The How – Setting Up JSLint as a Commit Hook

We set this up on Debian Linux, but it should work equally on Windows, Mac or Unix. What you’ll need:

Step one: Make sure Rhino works and can run JSLint

On your SVN server, install Rhino. You should test Rhino on a JavaScript file. Here is a little script to check it:

# echo var foo = {}; > tmp.js
# rhino /usr/sbin/jslint.js tmp.js
jslint: No problems found in tmp.js

Step two: Create a jslint.php PHP file to check the repo

Create a file on your SVN server, jslint.php.

Our script, jslint.php, looks something like the following:

<?php
 /**
  * This script will reject any files that fail our JSLint test
  */
 // variables
 $lintCmd = '/usr/bin/rhino /opt/BoxUK/commitHookScripts/jslint.js';
 
 // collect arguments passed in
 list( $ALL, $svnlook, $reposPath, $revId ) = $argv;
 exec( "$svnlook changed "$reposPath" -r "$revId" ", $lines );
 
 // array collection used to determine pass/fail
 $jsLintErrors = array();
 
 foreach ( $lines as $line ) {
 
   // Make sure we only check javascript files
     $ext = substr($line, -3);
     if ($ext !== '.js') {
         continue;
     }
 
     // collection of relevant files
     $aMatches = array();
     if (!preg_match('/^[AU]s+(.+.)(.+)$/i',$line,$aMatches) ) {
         continue;
     }
 
     // extract the actual filename
     list( $ALL, $fileName, $extension ) = $aMatches;
     $filePath = $fileName . $extension;
 
     // see what has been committed
     $data = '';
     $cat =  "svnlook cat "$reposPath/" "$filePath" 2>&1 ";
     exec( $cat, $data );
 
     // write javascript to temporary file for ease of linting
     $tmpFile = '/tmp/js' . time() . '.js';
     $fp = fopen( $tmpFile, 'w' );
     fwrite( $fp, join("n",$data) );
     fclose( $fp );
 
     // jslint the file
     $returnValue = null;
     $errorCount = 0;
     $aErrors = array();
     exec( "$lintCmd "$tmpFile"", $aErrors, $errorCount );
 
     // remove temporary file
     unlink( $tmpFile );
 
     // if there were any problems, then report them
     if($errorCount > 0) { // error count
         $jsLintErrors[] = array(
             'path' => $filePath,
             'errors' => $aErrors
         );
     }
 }
 
 function gecho($s) {
     static $cnt = 0;
     if (++$cnt < 50) {
         echo $s;
     } else if ($cnt === 50) {
         echo "n ** Too many errors to display in SVN response! **";
     }
 }
 
 // If we have errors, then pass them back and throw a fail code 1
 if (count($jsLintErrors) > 0) {
     gecho ("** JAVASCRIPT JSLINT WARNINGS ** ");
     gecho ("   Your files are committed and your work is safe, but you should 
                do an SVN UP on the files you just committed, fix these problems, 
                and commit again.");
     foreach ($jsLintErrors as $err ) {
       gecho ("n  WARNING: Bad JavaScript detected in {$err['path']}:n");
       foreach($err['errors'] as $error) {
         gecho ("n    $error");
       }
     }
     exit(42);
 }

Notice that we use the function “gecho”, which keeps a count of the number of errors it has output. This is because if we had too many errors, SVN would return MERGE 200 ERROR (see Troubleshooting section below).

To test it, run:

php jslint.php {pathToSvnLook} {pathToRepo} {revisionNumber}

e.g.

php jslint.php /bin/svnlook /svn/amaxus4 8130

This should give you a summary of JavaScript error messages in that commit.

Step 3: Install commit hook

Find your SVN repository’s hooks directory. There will be a file in there called  post-commit.tmpl – that’s the template for a post commit hook. Create a file called post-commit. Make sure whatever use SVN is has execute permissions for that file.

#!/bin/sh
# POST-COMMIT HOOK
# Make sure that the file passes JSLint if it is a JS file
PHP=/usr/bin/php
SCRIPTS=/opt/BoxUK/commitHookScripts
SVNLOOK=/bin/svnlook
REPOS="$1"
REV="$2"
 
$PHP $SCRIPTS/jslint.php "$SVNLOOK" "$REPOS" "$REV" 1>&2 || exit 15

All this does is call the PHP file we made in step 2, passing in the parameters the script needs. Make sure you alter the PHP, SCRIPTS, and SVNLOOK variables to match your machine’s settings.

For Windows, this will be a batch file, so it will look a bit different. If you research commit hooks on Windows, there are plenty of resources out there for getting this working.

Step 4: Test the hook

We recommend the following tests:

  • Commit some good JavaScript to your repo. Observe that all is well and no errors are shown.
  • Commit non-JavaScript files to your repo. Observe that all is well and no errors are shown.
  • Commit some bad JavaScript to your repo. Observe that it is committed, but errors show up.

Hopefully all is well, but if not, here are some troubleshooting tips:

Troubleshooting

It doesn’t work!

Check your permissions. The repo hook should be executable, and the system should be able to run Rhino and Java without assuming any environment variables.

Rhino very slow?

We found that with the GJJ implementation of the JVM, Rhino was slow to the point of being unusable. We switched to Sun Java 1.6 and it was 20 times quicker.

200 MERGE OK error

One teething issue we had was we got a “200 MERGE OK” error when there was a lot of output from the commit hook. It turned out that we could only send back 60 lines or so; any more, and we got the 200 MERGE OK error and no more information back from Subversion.

Alternatives and further materials

All we’ve done here is integrate JSLint with an SVN post-commit hook. Greg Sherwood shows how you can integrate jslint with PHPCodeSniffer for a more complete approach; you could add this to a commit hook if you wished.

JavaScript Lint (jsl) is similar to jslint, and can also be integrated with PHPCodeSniffer.

Wrap up

Static validation cannot catch all problems, but it is another line of defence in the battle against bugs. Have a read of Douglas Crockford’s book or his website. For the Amaxus team, this is another step towards having the highest quality Web CMS on the market.

CommentPosted on 23rd March 2010

3 Comments

  1. matt marksbury said... 27th April 2010

    I'm using windows vista and I get get the darn thing to work, keeps locking up my screen.
    Ohio Life Insurance
  2. matt marksbury said... 27th April 2010

    I'm using windows vista and I get get the darn thing to work, keeps locking up my screen.
    Ohio Life Insurance
  3. tim said... 27th June 2010

    very helpful post.

    fyi for anyone copy-and-pasting: the html rendering of jslint.php has a few errors:
    lines 10, 34, and 47 have encapsulated double quotation marks.

Post Comment

Amaxus: The Most Efficient Web CMS Ever Made
The Amaxus Web CMS Widget Manager

The Amaxus Content Management System offers a sophisticated feature set, across hundreds of highly-configurable modules; all within the most usable Web CMS interface available.

Latest News

RSS
The new website and member network of BCS, The Chartered Institute for IT, both powered by Amaxus, has won the Best use of Web/Digital Award in the Transform 2010: The UK’s Rebranding Awards.

Behind the Scenes

RSS
Posted on 19th August 2010Code quality is a slippery beast, difficult to define – as much art as science. Good code should be unsurprising, clear, and well tested. So how can we help to achieve this? And how can we measure whether what we’re producing is high quality? In this article we look at some of the approaches used developing Amaxus.

Get In Touch

Whether you’re an existing client, are investigating Content Management Systems for an upcoming project, or are interested in our Partner Programme, get in touch today.