Deploying to live/dev
Deployment to the live server consists of creating a bare repo in a location on the server accessible via SSH, then pushing to that. A hook script is setup inside this repo to pull any changes out into the working directory for that particular site.
You need to be able to push changes over SSH to the live server. This means your local machine’s SSH setup must contain the proper keys and an appropriate configuration.
In the case of projects that use private submodules, the server and GitHub repo will need to be set-up with the appropriate deployment keys.
Deployment keys are SSH keys that are not tied to a particular user account. They are used to authenticate a server as being able to download code from GitHub for deployment.
For example, if code is pushed to the server that contains links to private submodules on GitHub, then the post-receive hook will try to download and update that code. It’s not appropriate to put a particular user’s keys on the server, so a separate deployment key is created instead.
Deployment keys can be created in the usual way, with the “ssh-keygen” command. The key should be password-less so that updates on the server do not have to be performed manually. The idea is that code in the repos and any associated code to be pulled from submodules happens as quickly as possible to reduce downtime.
The private key should be added to the SSH configuration on the server and associated with the GitHub host. The public key should be placed there for safe-keeping but will also need to be added to any GitHub repos that deploy in this fashion. Under the “Admin” page for a particular repo, there is a tab called “Deploy Keys”.
Deployment keys are transient. If there is an issue with the server, the old keys can be removed from the server and the GitHub repos and new ones generated.
Creating the Remote Repo
SSH into the live server, and create a directory (outside of www is a good place) called “Repos”. Inside here, create a folder with the name of the repo and a “.git” extension. For consistency, match the name used for the repo inside GitHub.
Change into the newly created directory and initialise a “bare” git repository.
git init --bare my-git-repo.git
A bare repo only has information about the various branches and commits and has no working directory. This means that there are no local changes in this repo, so changes from elsewhere can be pushed in without any conflicts (if it was not bare, a pull action would be necessary, which involves logging in to the server each time).
This repo can then be set-up as a remote to any local repo. To push changes over SSH, you just require the location of the repo on the server and the server address:
git remote add live ssh://my-live-server.com/home/ubuntu/Repos/my-git-repo.git
The current state can then be pushed to this repo’s master branch:
git push live master
this assumes you have already setup a ‘live’ ssh shortcut in the .ssh/config file. Without that it would look something like:
git push ubuntu@ssh:my-live-server master
The Working Directory
We can now set-up the main location for the site (i.e. where you would normally upload files to over FTP) as a working directory and pull changes from this bare repo. How this is done depends on whether it’s a new site or an existing one.
For a new site, the process is very simple. It involves changing into the directory where the new site is going to be based and cloning from our bare repo.
git clone /home/ubuntu/Repos/my-git-repo.git .
For a site that already contains files, it’s a little more complicated. We need to create a git repo out of the current directory, add a remote to it under the name “origin”, fetch the branches from the origin and then checkout a branch into master.
git remote add origin /home/ubuntu/Repos/my-git-repo.git
git checkout -f -b master origin/master
The “-f” flag will overwrite all files in the directory that are currently under version control. Any files in the destination directory that are not under version control will stay as they are. Running a “git status” will no show all the remaining untracked files. These should either be removed if they’re not necessary or added to the “.gitignore” file.
After pulling from the bare repo the first time, we want to automate the process of merging in new changes. The act of pushing changes to the bare repo can trigger a script called a post-receive hook. This is simply a bash or other script within the bare repo.
Here’s an example script to take changes pushed to the bare repo and merge them into the working tree where the site sits:
git merge origin/master
git submodule update --init --recursive
After setting up some environment variables and changing into the working directory for the site, the script fetches all the current refs from all the remotes. It then merges the master branch from the origin remote (which should point to the bare repo). Finally, it will update all submodules within the repo. Here you can also run any cache clear commands or other housekeeping.
This script would sit inside the bare repo’s “hooks” directory, named “post-receive”. Make sure that execute permissions are set properly on this file otherwise it will not be able to run:
chmod a+x post-receive
The output of this script will be echoed back to you when you push changes to the bare repo so that any problems can be addressed.