A TCP Packet Walks into a Bar...


Using Git to Remotely Deploy to McGill's Ubuntu Server (or any Cloud-Based Linux Instance)

Written by Clark Gredoña on 2 October 2013.

I recently had to create an account with McGill's CS department in order to submit an Operating Systems assignment, and I was pleasantly surprised to discover that the CS department will host a personal site for CS (or, as in my case, ECSE) students on its Linux Ubuntu server for "free".1

The Linux workstations on the 3rd floor of Trottier aren't exactly as far as Mordor, but obviously it's more convenient to work from anywhere. And it's pretty trivial to SSH into the Linux server, but then working remotely still ends up being a little cumbersome, whether it be directly in the prompt using nano or vi, using remote desktop, or graphics over SSH.

A photograph of the 3rd floor of the Trottier building

I'd much rather work locally on my Windows machine as much as possible. This is partially because it's more convenient, partially because I'm not a fan of Ubuntu GUI, and partially because I've already configured my Windows work environment extensively. More than anything though, network latency really makes working remotely a drag, and of course it's nice to be able to work even without Internet access. We could work locally and then use SCP to push changes to the remote, but there's a better solution: Git, which fortunately is pre-installed on McGill's Linux server.

After setting Git up correctly, deploying a change straight from my local machine takes three short commands:

	git add [file]
	git commit -m "Message"
	git push [remoteRepositoryName] [branchName]

Here's a screenshot:

Of course, using Git comes with the added huge benefit of version control, whereas relying on just SCP to push changes into production without any sort of backup or history is extremely hazardous. In this scenario, there's probably only going to be two versions of a file at once: one on our local machine and one the remote server. But if we wanted to deploy a change to multiple servers or instances (a breeze with Git), copying files proves both especially tedious and dangerous. Obviously Git, as with any VCS, also allows team members to collaborate and push changes to a to central repo without stepping on each other's toes.

I learned how do this from from Jeff Hoefs' blog, and I'll reproduce here the exact steps I took to adapt this for McGill's Ubuntu server. But just as Jeff shows, it's easy this to adapt for any cloud-based Linux instance like Amazon's EC2, as long as Git is installed.

1. SSH in to the remote server the normal way.

	ssh -R 2222:localhost:22 user@linux.cs.mcgill.ca

2. mkdir a new .git directory and init a new bare Git repository in it. It's important create a bare repository (ie one without a working tree) because this repository needs to accept pushes. Normally, trying to push to a checked out branch in a non-bare repository will result in an error, and with good reason. Imagine if you were working in a Git repository and someone somewhere else pushed and forced changes they made on your work. Bare repositories (a good example is any Github repository) are meant for sharing on a centralized server, not working in them. We would never push from Github, but we certainly would push to it or pull from it.

	mdkir [repositoryName] && cd [repositoryName]
	git init --bare

3. Create the post-receive hook (hooks/post-receive), modify it, and modify its permissions to be executable. This is what will be executed after the git repository receives a push. In this example, we'll deploy changes to a basic personal website to the public_html directory. Read this for instructions on how to set up that directory.

	cat > hooks/post-receive
	#!/bin/sh
	echo STARTING POST-RECEIVE
	GIT_WORK_TREE=~/public_html #This is the directory where files and changes will be deployed
	export GIT_WORK_TREE
	git checkout -f #This throws away local changes (ie changes in the repository we are pushing to)
	chmod 644 ~/public_html/index.html #Fix permissions which may revert when the file is pushed from elsewhere
	chmod +x hooks/post-receive

4. On the local machine, init a git repository and add the remote repository. This repository initialized here is the one we'll make work in. We'll call the remote "linux" here for obvious reasons.

	mdkir local-homepage-repository & cd local-homepage-repository
	git init
	git remote add linux cgredo@linux.cs.mcgill.ca:~/homepage.git

5. Make some initial change, and commit it. Otherwise you'll get an error when you push: "src refspec branch does not match any".

	git add .
	git commit -m "Initial commit"

6. Push to the remote. The "+master:refs/heads/master" is only necessary in this first push.

	git push linux +master:refs/heads/master

From now, on pushing changes only requires

	git push linux master

Try adding a index.html to local-homepage-repository, committing the change, and pushing it. Then refresh or navigate to your McGill CS homepage - you should see your changes!

If other people want to contribute, they merely need to clone the central Linux repo.

	git clone cgredo@linux.cs.mcgill.ca:~/homepage.git

This seems like a long post for something that ends up being fairly simple, but the curriculum at McGill (and I imagine most universities) doesn't really delve into purely practical aspects like this, so I figured I'd elaborate. Hopefully somebody finds this as useful as I did.

1 - Of course there's no such thing as a free lunch, especially when international students (like myself) at McGill pay 8x as much as their Quebecois or French counterparts.