Push-based Remote Deployment with Git (Revised)

Git is everyday. It has become an essential in the programmer’s toolbox and supports all the good buzzwords – maintainability, agility, predictability, you name it. The hooking functionality it provides has spawned an industry of its own, with companies like Codeship, Hakiri, Travis CI and such being fundamentally structured atop it.

These hooks are straight forward to work with, so just as the creators of those companies would’ve, we can jump in and have a go ourselves. I use it so I can easily push changes to web servers, etcetera, instead of having to manually (and error prone-ly) sync files, and that’s what I’ll be demonstrating here – one push to deploy.

It’ll be a simplified version of Digital Ocean’s great Git hooks post, and an improvement to a similar post I wrote almost a year ago (which has a critical security flaw (see my comment on that post)).

So lets get started…

 

… with some assumptions

This walkthrough assumes you have an existing project and Git repository, and that you want to set up easy and quick remote deployment – maybe to a web server.

This technique can be used for any project where you’re comfortable with simply pushing updates to a location and seeing them immediately active.

It works great for code bases like the one for this blog or any basic website you may be managing. However with larger projects that need automated testing and CI, etcetera, it may not be sufficient.

If this suits your needs, you’ll save a whole heap of time whenever you need to update a destination with your changes.

The final assumptions are that Git is installed and usable at the destination (if it’s not, you can hopefully easily install it following this guide), and that you won’t actually be coding or making any direct changes at the destination. Instead we’ll push in any changes and trust me, when you see how easy it is, you won’t need to make on-the-server/spot changes ever again – even for hotfixes and tiny bugs.

I’ll propose a hypothetical situation and walk you through the setup and usage for it. In reality you’ll have to adapt this guide to your own situation. You shouldn’t think of this as a bad thing though, as it will give you the opportunity to extend the solution however you feel and let it do more work for you.

Situation

I’ve got a website, www.bilalakil.me, that I update frequently. I code these updates from my home PC (hypothetical scenario >.>) or from Cloud9 when I’m on the go – so I never actually work on the webserver itself, I just upload my changes to it.

For my changes to become visible I’m used to (but not happy with) manually uploading them to my webserver under the ~/www/wp-content/themes/bilalakil/ folder, using my username: useruser and a password which I’ll never tell you.

I just wish I can easily and quickly get my webserver using my local or Cloud9 changes without having to deal with any tedious, annoying uploading or file syncing… sigh.

I want to push my changes to it.

Setup steps

Open your terminal and follow these instructions. You’ll have to appropriate them for your individual and unique situation though, so try to understand what’s going on.

  1. SSH into the destination server:
    ssh useruser@bilalakil.me

    Enter your password when prompted. We’re targeting the server that you want to deploy your pushes to – we’ll set up a Git repository here.

  2. Create a bare deployment repository and its post-receive hook by first setting some environment variables (based on your situation)…
    # The deployment repository should be in a private location -
    # people shouldn't be able to browse here insecurely.
    DEPLOYMENT_REPO=/home/useruser/deployment.git
    # Also note that we didn't use `~` here, but `/home/useruser` instead.
    
    DESTINATION=/home/useruser/www/wp-content/themes/bilalakil

    …and then simply copying and pasting the following:

    # Creating the bare repository
    mkdir $DEPLOYMENT_REPO
    git --git-dir=$DEPLOYMENT_REPO init --bare
    
    # Creating the `post-receive` hook
    cat > $DEPLOYMENT_REPO/hooks/post-receive << EOF
    #!/bin/bash
    DESTINATION=$DESTINATION
    
    git --work-tree=\$DESTINATION reset --hard HEAD
    git --work-tree=\$DESTINATION clean -xfd
    EOF
    chmod +x $DEPLOYMENT_REPO/hooks/post-receive
    
    # All done!

    The server should now be ready to receive pushes to the deployment repository and automagically have those changes mirrored to where they’re needed.

You should keep in mind one of the aforementioned assumptions: you shouldn’t be making edits from the destination. They’ll be lost whenever you push to the deployment repository. I think the cleaning step (which removes any files not part of the repository) is important to guarantee consistency and predictability, but is not required. If you’re against the cleaning, make sure you take the time to understand exactly how all of this is working and what’s appropriate for your situation – feel free to ask me for any clarification.

Usage

From each of your working copy Git repositories that you’ll push to the setup server from, you need to:

  1. Add the deployment repository as a remote:
    git remote add deployment useruser@bilalakil.me:/home/useruser/deployment.git/
  2. Push to it when you want your code to be immediately deployed:
    git push deployment <branch>

That’s everything!

Note that any branch that you push to the deployment repository will result in deployment. If this isn’t desirable, look into Digital Ocean’s great Git hooks guide for how they dealt with that under the “Set Up the Production Server Post-Receive Hook” title.

I skipped over a lot of the details and explanation here for simplicity and speed, but feel free to ask me anything you’re not sure about and would like clarified.

Author: Bilal Akil

Husband of Joy. Programming hobbyist and professional.

Leave a Reply

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