Push-button deployments with Arduino

TL;DR: Thanks to an Arduino, an Arduino ethernet shield, some Arduino code, buttons from eBay, LEDs from Fry’s, and SmugMug’s deployment web app, we’ve created a push-button deployment process that looks like this:

When I first started working at SmugMug, we deployed infrequently by manually merging branches, tagging, double-checking, then running a bunch of commands (some via sudo and some not). It was an eleven step process that not all developers had access to. Developers were usually uneasy about pushing due to the complexity involved.

Eventually the process was consolidated into a shell script, which still had to be run via sudo on a designated server. More recently, the shell script was wrapped in a web app that made things much easier.


While the web app is pretty awesome and easy to use, I thought using a real physical button to deploy code would be even better:

Introducing the SmugMug Deployinator 5000!


Deployinator 5000


Deployinator 5000 inside



Arduino and Ethernet Shield


The Deployinator 5000 consists of the following components:

The setup is relatively simple. The toggle switch, key lock, and two momentary switches are all wired up in sequence so the Arduino sees them as one button. When all four are pressed, the Arduino makes an HTTP POST request to our deployment server, which then pushes any pending code live. While the Arduino is waiting for the deployment to finish, it blinks the yellow LED. When the push is deployed, the green LED lights up. If something goes horribly wrong, the red LED strikes fear into the deployer’s heart.

It wasn’t too hard to wire up the Arduino, the buttons, and the LEDs, even for someone with no electronics experience (although I had a bit of help from other SmugMug employees with more experience). The fun and challenging part was finding an enclosure and mounting all the pieces inside it. Trips to Weird Stuff and Home Depot solved that problem easily!

After gutting the enclosure, I superglued the Arduino holder to the inside and drilled holes into the backing plate to mount the buttons with machine screws. I then reattached a few wires and circuitry for dramatic effect.

Long-term ideas for the Deployinator include adding larger lights, a disco ball, and playing music when a push occurs. PowerSwitch Tails would allow the Arduino to control anything that runs on 120V power.

Deploying code doesn’t have to be boring!

– Ryan Doherty, SmugMug DevOps

Using HTML5’s Fullscreen API for Fun and Profit

For the past few weeks I’ve been working on a new super magical awesome feature that involves using the new HTML5 Fullscreen API. As with most brand spankin’ new web APIs, its support and implementation varies per browser. I think it’s worth the effort considering how freaking awesome it is to do fullscreen web apps.

The Basics

OK, let’s get started with the basics of how this new API works. Via the JavaScript function requestFullscreen you tell the browser you want a specific HTML DOM element to fill the entire screen with no browser chrome displayed.

var myNode = document.querySelector("#myFullscreenNode");

This is not the normal fullscreen mode that many browsers have where the browser’s viewport is simply stretched to the edges of the edges of the screen and the browser chrome is hidden. As far as I know that type of fullscreen mode is not standardized and is not accessible via JavaScript.

Currently Firefox, Safari and Chrome support the fullscreen API. But of course each implements it slightly differently, which is exactly why I’m writing this article and you’re reading it.

Getting started

According to the W3C specification, the first thing you should do is determine if the browser supports the fullscreen API and is currently in a state where it’s safe to go fullscreen. This is achieved via the `fullscreenEnabled` property on the document object. If the property exists and is true this means you can request the browser’s fullscreen mode. (Note the terminology: request. There’s no guarantee it will always work so don’t expect it to.)

You want to use this flag (if available) because a browser may support the fullscreen API but be in a state where it can’t go fullscreen (still loading content, a browser preference pane may be focused, etc).

To determine if fullscreen mode is available, check the .fullscreenEnabled property on the document object like this:

if(document.fullscreenenabled) {
	var myNode = document.querySelector("#myFullscreenNode");
} else {

Currently only Firefox has this property on the document object as ‘mozFullScreenEnabled’ (note the capitalization), so it’s not worth relying on unless you really want to adhere to a draft spec.

The easier way to check if a browser supports the fullscreen API is to create a test HTML Node object and check if it has the requestFullscreen function on it:

var testNode = document.createElement('div');

if(testNode.requestFullscreen) {
	var myNode = document.querySelector("#myFullscreenNode");
} else {

The above snippet is the spec format, for use in Firefox/Chrome use .mozRequestFullScreen and .webkitRequestFullScreen (again note capitalization!).

Are we there yet?

Let’s assume we have a browser that supports fullscreen mode. We can just call requestFullscreen() on the DOM Node we specify and we’re golden, right? Wrong! Just because we call the function doesn’t mean we’re guaranteed to go fullscreen. The user could press the Escape key during the transition to fullscreen or something could occur in the browser itself where it needs to abort. This is where listening for the events ‘fullscreenchange’ and ‘fullscreenerror’ is helpful (both are available prefixed in Firefox and Chrome, fullscreenerror is not available in Safari).

These events are fired on the document object, not on the node that was requested to go fullscreen. Adding to our code snippet above we get this:

var testNode = document.createElement('div');

if(testNode.requestFullscreen) {
	document.onfullscreenchange = function(event) {
		//Fullscreen mode has changed

	document.onfullscreenerror = function(event) {

	var myNode = document.querySelector("#myFullscreenNode");

Again, the above code is per the spec, for Firefox and Chrome/Safari use ‘onmozfullscreenchange’ and ‘onwebkitfullscreenchange’, respectively.

Given there’s a fullscreen change event object you’d assume that it will tell you which mode the browser is currently in, right? Wrong! You can’t tell which mode the browser is in from the event fired. Luckily there is a document property to determine which mode the browser is in. (Are we having fun yet?!)

To determine which mode the browser is in check the ‘fullscreenElement’ property of the document object. If this property is not null the browser is in fullscreen mode (and the value is the DOM node that is fullscreen). Firefox, Chrome and Safari all support this property (namespaced).

if(document.fullscreenElement) {
    //Yay, we're fullscreen!

Checking for errors

Even if we’ve checked the ‘fullscreenEnabled’ and ‘fullscreenElement’ properties betore we request fullscreenmode, it’s still possible that the browser will deny our request. When this happens the browser will fire a ‘fullscreenerror’ event on the document object.

This can happen if there’s a user preference, security risk or platform limitation regarding fullscreen mode. Fullscreen mode is also only triggerable via user input (click, key press, etc), so if it is requested outside of those events the fullscreenerror event will be fired.

var testNode = document.createElement('div');
if(testNode.requestFullscreen) {
	document.onfullscreenerror = function(event) {

Firefox and Chrome support the onfullscreenerror events (prefixed), Safari does not.

All together now

Combining all our code examples above we get the following:

if(document.fullscreenEnabled) {
	document.onfullscreenchange = function(event) {
		if(document.fullscreenElement) {
			//We are fullscreen! Rejoice!
		} else {
			//We're not fullscreen 😦

	document.onfullscreenerror = function(event) {
		//Something went wrong...

	var fullscreenNode = document.querySelector("#myFullscreenNode");

Unfortunately none of this code will work in any current browsers. A lot of conditional logic is needed to determine which set of fullscreen APIs are available (Firefox, Chrome and Safari all differ).

Fortunately for you, I’ve wrapped it all up into a convenience function that will return the correct set of events and fullscreen function for the browser or false if the current browser does not have fullscreen support:

function FullScreenSupport() {
    var TEST_NODE = document.createElement('div');
            'requestFullscreen': {'change':'onfullscreenchange',
                                  'cancel': 'exitFullscreen',
                                    'cancel': 'mozCancelFullScreen',
            'webkitRequestFullScreen':{'change': 'onwebkitfullscreenchange',
                                       'request': 'webkitRequestFullScreen',
                                       'cancel': 'webkitCancelFullScreen',
                                       'error': 'onwebkitfullscreenerror',
                                       'fullScreenElement': 'webkitCurrentFullScreenElement'

        fullscreen = false;

        for(var prop in REQUEST_FULLSCREEN_FUNCS) {
            if(REQUEST_FULLSCREEN_FUNCS.hasOwnProperty(prop)) {
                if(prop in TEST_NODE) {
                    fullscreen = REQUEST_FULLSCREEN_FUNCS[prop];
                    //Still need to verify all properties are there as
                    //Chrome and Safari have different versions of Webkit
                    for(var item in fullscreen) {
                        if(!(fullscreen[item] in document) &&
                            !(fullscreen[item] in TEST_NODE)) {
                            delete fullscreen[item];

            if(fullscreen) {

        return fullscreen;

It ain’t pretty, but it does work. The function will return false if the browser doesn’t support fullscreen mode. Also note that just because a browser supports full screen mode doesn’t mean every function and property related to full screen mode is available, make sure it’s in the object FullScreenSupport returns.

Styling it all

Thought you were done? 🙂

Along with the long list of JavaScript functions for fullscreen mode, there’s a little bit of CSS styling that is applied to the element that is shown fullscreen. According to the spec, an element that is fullscreen gets this CSS:

  top:0; right:0; bottom:0; left:0;

Firefox applies this by default along with background-color: black, Safari and Chrome do not apply the width and height properties. To make Chrome and Safari match the spec and Firefox’s behavior, you can use the :fullscreen pseudo class to apply these styles:

#myFullscreenNode:-webkit-full-screen { //webkit prefix
  background-color: black;

Combine that with the FullScreenSupport function and you’ll have a relatively easy to use fullscreen API in three browsers! Also, if you happen to know anyone on the IE team please let them know they should implement it!


You shall not commit! (Without passing tests)

Welcome to the SmugMug engineering blog! Here at SmugMug we refer to our engineers as ‘Sorcerers’ and this blog will be all about the magic behind smugmug.com. Our engineering landscape is as vast and varied as Middle Earth so we’ll start with something everyone’s familiar with: committing code and testing.

At SmugMug our SCM tool is Bazaar (soon to be Git) and the unit test framework we fancy is PHPUnit since our website codebase is written in PHP. Running unit tests before committing is always a Good Thing for developers to do, but sometimes we forget and can commit code with broken tests. This is where a pre-commit hook comes in handy.

A pre-commit hook is a piece of code/script you tell your SCM tool to run after you type `bzr/git/svn commit` but before it actually commits your new code. A pre-commit hook can do just about anything, and with ours we run our unit tests and if they fail, we abort the commit.

The way we prevent ourselves from committing code with broken tests is by creating a short shell script that runs our unit tests via PHPUnit’s command line tool. Our script is named `precommit.sh’ and is pretty simple:

phpunit --bootstrap include/tests/bootstrap.php include/tests/

That script runs from the root of our Bazaar repo, telling PHPUnit where our bootstrap file and tests are. The script will return 0 if all tests passed (in Unix 0 means the command succeeded). If tests fail the script will return something other than 0. Now that we have that working (and our tests pass :)), we can get Bazaar to run it before committing our code.

The way you get Bazaar to run a pre-commit hook is by writing a plugin that adds the hook to Bazaar every time you run `bzr commit`. Plugins are simply Python files placed in ~/.bazaar/plugins/[plugin-name].py . Bazaar executes them and the plugins can then do just about anything they please. Here’s our pre-commit hook plugin, which I’ll walk us through:

from bzrlib import branch

def check_tests(local_branch, master_branch, old_revision_number, old_revision_id, future_revision_number, future_revision_id, tree_delta, future_tree):
    import os,subprocess
    from bzrlib import errors

    #Only execute our script if it exists in the current directory
    if not os.path.exists("precommit.sh"):

    #Run the pre-commit script
    tests_result = subprocess.call(os.path.abspath("precommit.sh"), shell=True)

    #If our script returns something other than 0, tests have failed
    if tests_result != 0:
        raise errors.BzrError("Tests failed, no soup for you!")

#Install the hook with Bazaar
branch.Branch.hooks.install_named_hook('pre_commit', check_tests, 'Run PHPUnit tests pre-commit hook')

Our plugin does the following:

  • Imports ‘branch’ from the Python module bzrlib.
  • Defines our function to run before committing new code. A pre-commit hook function accepts eight arguments, none of which we’ll need though. (For more info on hooks with Bazaar, read the hooks help page)
  • Checks for the existence of precommit.sh, which runs our tests. We do this because we could be working with multiple repos, some which may not have a pre-commit script to run.
  • If it does not exist we return and Bazaar will commit our code.
  • If it does exist we run precommit.sh via subprocess.call() and save the return code as ‘tests_result’.
  • If tests_result is is not 0, our tests have failed and we raise a Bazaar error, which will abort the commit.
  • Installs the pre-commit hook via branch.Branch.hooks.install_named_hook(). The first argument is where the hook should be run, the second is the function to run and the third is a name for the hook to be used in progress and error messages.

Here’s what it looks like for us if we commit code with broken tests:

With just a little bit of engineering we can have our own wizard preventing us from committing broken tests and code! Most other SCM tools have pre-commit hooks also (Git, SVN). Stay tuned for more posts about our architecture and engineering processes here at SmugMug.