Codular

HomeWriters RSS

Branch Subdomains with Git & Nginx

Introduction

When developing applications using source control it's always advisable to use different branches for features, or bug fixes. Especially if you follow the GitFlow paradigm, which stipulates that you have a develop, and master branch at the very least, with master being what is in production currently.

One of the problems with distributed development among teams is how to actually stage these development branches, which might have super cool features that you want to demo to people, without destroying your main staging environment. Instead, it'd be pretty swell to have them on a subdomain of your staging environment, that people can expect to be there and ready to view/test.

Here is an example of how to automate that, this is written with the proviso that you are using nginx (if you want to write the same into Apache, feel free to contribute and we'll add it in), with a Git repository.

How it Works

What we're going to do is have a script that sits in the master branch of the repository, which we can then call using a Cronjob to run every 5 minutes or whatever.

The concept of the bash script is this:

The script will: - Check out branches to /git/branches/... - Ignore HEAD branch - Not run any compile/build tasks if required

The Bash Script

# Fetch the latest branches
git fetch

# Get the repository path
repoPath=`git config --get remote.origin.url`

# Loop through each branch
for branches in $(git for-each-ref --format='%(refname)' refs/remotes/)
do
        # Remove redundant string from branch name
        myBranch=${branches/refs\/remotes\/origin\//}

        # Check if we are using 'HEAD' branch
        if [ "$myBranch" == "HEAD" ]
        then
                continue
        fi

        # Check if the branch has already been checked out
        if [ -e /git/branches/"$myBranch" ]
        then
                # Switch to the branch directory
                cd /git/branches/"$myBranch"

                # Pull latest code
                status=`git pull`

                # Check if there are any changes
                if [ "$status" == "Already up-to-date." ]
                then
                    continue
                else
                    continue
                fi
        else
            # Switch to branches directory
            cd /git/branches/

            # Clone repository to branch named directory
            git clone $repoPath "$myBranch"

            # Switch to specific branch directory
            cd /git/branches/"$myBranch"

            # Switch to the branch
            git checkout $myBranch

        fi
done

Bash Explanation

$(git for-each-ref --format='%(refname)' refs/remotes/)

This chunk of code is very handy, and uses a built in Git function. What it does is outputs all of the references (or branches) that are in your repository in a format that you specify. We can then loop through all of these using the loop as per the code above, read more.

myBranch=${branches/refs\/remotes\/origin\//}

Here we're string replacing the unnecessary string of text that's returned with the branches that we have. This will replace the string refs/remotes/origin/ with nothing, so that we are just left with the actual name of the branch.

if [ "$status" == "Already up-to-date." ]
then
    continue
else
    continue
fi

This is mostly there for use if you so need it, what this will do is allow you to either skip over if the branch doesn't already exist, or to run some code if it does. Very handy if you have some build steps to run, as there's no point running them if nothing in the actual repository has changed.

NGinx Code

This is the basic server code that we need to create a subdirectory for each branch:

server {
    server_name ~^(?<branch>.*)\.staging\.mydomain\.com$;
    access_log /var/log/nginx/branch-access.log;
    error_log /var/log/nginx/branch-error.log;
    index index.php index.html;
    root /git/branches/$branch;
}

Obviously, if you're using PHP you'd include the specific location chunk within this, changing the paths as relevant to have the correct root if needed etc.

The key thing here is in the server_name line, where by we actually specify a regular expression capture - which will look for any subdomain of staging.mydomain.com and assign it to the variable branch that we can then use later to look for the branch.

We then use that to specify that the web root will be /git/branches/$branch which we've magically set up automatically through the bash script further up above.

Conclusion

Very intense code breakdown of how to simply set up a subdomain per Git branch using a very simple bash script, and some Nginx wizardry. There are other things you can do to push this further, such as adding gulp or grunt tasks to run after getting the latest code changes.

One minor thing to note is that the script is non-destructive. Ie, if you delete a branch in the repository, it will not be removed from the server. You could delete all branch directories regularly and then re-run the pull script if you are worried about this, but it should be okay.