Casting Off With ShipIt

Share on FacebookShare on Google+Tweet about this on TwitterShare on Reddit

Deploying a web application is always kind of a scary process.  I feel like even when it works well, there’s a second where I wonder if it’s going to break.  Deploying a web application manually is probably where this irrational fear comes from.  Now that automatic deployments are the norm, there’s no reason to be scared anymore.

Capistrano is a standard for automatic deployments.  It’s been around for a while now, it’s a mature product, and it works really well.  There’s good documentation available, and honestly, it’s never really given me any problems.  Capistrano is also written in Ruby.  Unfortunately, I don’t program in Ruby (nor do I want to), so I’ve never really felt comfortable setting up and configuring Capistrano.  Not to mention that I feel a little dirty having Ruby code in my beautiful node codebase.

When I heard about ShipIt though, I got really excited.  I could have automatic deployments with a tool written in Javascript?!  Count me in!  I installed it, started configuring it into my Koa Starter Kit, and wrote this blog to document my progress.

Now that you’re here, let me be real with you.  ShipIt is a pain.  Mainly because it’s very modular, but no one who maintains the project seems to like to write documentation.  When I first started using it, I wished for more thorough examples, so that’s why I’m writing this blog post.

So how do you get started? First things are first, we need to install ShipIt.  I’d highly recommend installing the command line tool:

npm install -g shipit-cli

After the CLI is installed, we can install ShipIt-Deploy

npm install --save-dev shipit-deploy

Everything everything is installed, you can start creating your deployment file.  There’s a couple of ways to do this, but for the way I chose, you need to create a  shipitfile.js in the root of your project.  The syntax of this is fairly similar to Grunt (which I’ve talked about before), so it shouldn’t be too tough to understand.  This is how my file starts off:

A few of things I’d like to point out about this:

  • The “workspace” property is a temporary folder directly inside your project root that ShipIt will use to prepare to deploy.  It will also wipe this folder, so be careful!
  • I’m using a lot of config values in here, and that’s because this is for an open source project, and I don’t want to expose my data to people if I can avoid it.  I keep a config.json  file outside of version control to store all private data for my app.
  • The “keepReleases” property controls how many old releases we store on the server before deleting them.  I kept mine at 2 because rolling back more than 2 isn’t something I usually do.
  • I’m only including a “production” environment.  You can include more, but I simply didn’t need them.

ShipIt along with ShipIt-Deploy will take your entire repository and move it to a remote location of your choosing.  That’s pretty cool, but now what?  At least with a node project, it’s not enough to just move it, we need to do an npm install  and start the server up.  This is where ShipIt tasks come into play.  There’s two main types of tasks that I found myself using, regular tasks and blocking tasks.  Multiple regular tasks can run at the same time, but a blocking task waits until it’s complete (exits with a code) to continue.  For this specific ShipIt deployment file, I used all blocking tasks because I wanted every task to complete before the next one started.

Before I get into what tasks I used and why, I’d like to briefly say that the way I’m describing isn’t the “right” way, and I don’t even know that it’s a good way, but it’s a way that works for me.  Feel free to alter what you see to make ShipIt work for you.  Also feel free to send me a pull request. 😉

Back on the subject of tasks, I decided I had 4 distinct steps to my deployment.  They were (in order):

  • Kill the screen running the web application (if it’s running)
  • Run an npm install  in the project folder
  • Copy over my unversioned config file
  • Start the screen running the web application

Screen could be a whole series of articles on it’s own (and maybe it will be…), but if you don’t have experience with it, you can find a manual here.  I chose screen because it’s standard on all macs, it’s easy to use, and very powerful.  You could obviously use something else.  When screen was giving me trouble, I debated running the process in the background, and saving a PID file for reference later.  Ultimately, I got screen to work and went with that.  Here’s what this task looks like:

Let me explain what’s going on in this step and why:

  • I’m essentially checking to see if there’s already a screen running with whatever name we have stored in config.deploy.screen
  • If there is, attach the screen, and send a control + c to it.
  • We wrap the second part in an if statement because if we try to attach a screen that doesn’t exist, screen will throw and error and our deployment will stop.

After the screen is killed, we need to run an npm install .  Here’s that step:

This one is pretty self explanatory, but basically we set our PATH value (this would normally be set for us by our bash profile), cd into the current release project directory, run npm install , and output everything to /dev/null .  We do this to prevent our deployment log from being flooded with package installation messages.

After the installation is done, we need to move our unversioned config.json  file.  ShipIt has a built in method called “remoteCopy” that will handle this for me:

After we’ve copied our config.json , all that’s left is to start back up the server:

Again, pretty self explanatory (especially if you’re knowledgable with screen), but if you’re not, let me explain.

  • We’re setting our PATH (like we did with the npm install ) and then cd-ing into the current release project directory.
  • Once in there, we’re starting a new screen, and giving it the name set in the config (with the -S parameter).
  • We’re also starting it detached (-d) and telling it to ignore what type of terminal we’re in (-m).
  • Then we start the server with npm start .

Having those tasks in your file isn’t enough on its own though.  You have to call them and tell ShipIt what order to run them.  I had my tasks run after deployment was finished (using the “deployed event):

That’s pretty much it!  It deploys like a charm, and since I used screen, when I connect to the server, I can watch the logs and interact with the app in real time with:  screen -r SCREEN-NAME (where “SCREEN-NAME” is the name set in your config).

My goal with this deployment script was to have it require as little installation/configuration on the server as possible.  You might need to install screen, and you’ll need to make sure your SSH key is in authorized_hosts , but other than that it should be plug-and-play.

Be sure to check out the finished product in my Koa Starter repository.  Thanks for reading, and remember that comments and pull requests are always welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *