Deploying from GitHub to a Server

Chris Ward
Chris Ward
Share
Chinchilla and Octocat

GitHub, and the Git version control system it’s based on, are fantastic tools for managing and collaborating on projects – code-based or otherwise.

In this article, we’ll look at options for making Git and GitHub projects fit better into developer workflows, allowing for a smooth and hands-off deployment process.

I’ll break these options into the different types of toolsets available – which allow for options from automatically running tests and code checks to deploying your code to a server.

Why Do This?

With these automated processes up and running, you and your team can focus purely on coding, approving and merging code, rather than spending hours on deployments and repetitive tasks every time a new build or change is ready.

Disadvantages of Automation

The main problem with automatically deploying changes is that changes are automatically deployed. You have to trust your team and the code they write. This is why automatic deployment is typically paired with automated testing, and the tools presented below reflect this.

Of course, it also means that any small issues are equally as quick to fix. Automation should be paired with communication. If pushing to a repository’s master branch can trigger live builds, it needs to be clear when this happens and who can make it happen.

The initial setup of an automation process can take some time to get right. It’s important to weigh up whether or not your team or workflow really needs it. Add up the amount of time you spend on testing and deploying new builds – and if it’s more than a few minutes each time, then it’s worth it.

Git Hooks

Git has a suite of in-built hooks that can be used for automation, and these are often our first port of call for processing tasks after particular Git actions. These are divided into server- and client-side hooks.

Server-side hooks are for events such as listening to network operations – for example, when a repository receives a push. Client-side hooks are triggered on actions that occur on a developer’s machine, such as commits and merges.

There’s a full list of hooks in Git’s documentation. I’ll look at a couple here to get you started. Hopefully you’ll start to see how they may be useful in your own projects and current (manual) workflows. The hooks are files that can contain commands in any language the host system can run, allowing for a lot of power and flexibility.

pre-commit

This client-side hook runs before any other hook, and before any changes are committed. It’s a perfect place to run tests or other checks on your code.

Let’s add some basic JavaScript to our small project (and yes, there is an intentional mistake here):

document.onload = function() {
    alert("Hello World")
};

We’ll use JSHint to check the JavaScript for errors. (You can find installation instructions here.)

Rename hooks/pre-commit.sample to hooks/pre-commit, and change the contents of the file to this:

#!/bin/sh
jshint index.js

Try to commit the changes:

git commit -m "adding Javascript file"

You’ll see the following error message:

index.js: line 5, col 25, Missing semicolon.

1 error

Add the missing semicolon and try again. The commit will now progress without any problems.

post-receive

This server-side hook triggers when a push to a remote Git repository completes. In this example, we checkout the latest version of a simple website into our webserver directory, effectively a (basic) deployment.

I have an existing website that consists of an index.html page – along with some other pages that we’ll use in later examples. You can create your own or use the repository set up here.

Clone the repository, specifying the --bare flag to create a repository that only consists of version control information and not our code base:

git clone --bare https://github.com/sitepoint-editors/GitHub-Auto-Deploy.git GitHub-Auto-Deploy.git

Now we’ll create our hook:

cd GitHub-Auto-Deploy.git/hooks
vi post-receive

Add these lines to the file:

#!/bin/sh
git --work-tree=/var/www/html --git-dir=/var/repo/GitHub-Auto-Deploy.git checkout -f

Note: these locations are relevant to an Ubuntu installation, so remember to change paths to suit your setup.

This command will checkout the current repository into the defined working directory, but without any version control data.

We need to make the hook executable:

chmod +x post-receive

On your local machine, clone the repository as normal, using your tool of choice, and add a new remote for the live server (remember to change the server details to your webserver and user details):

git remote add prod ssh://user@domain.com/var/repo/GitHub-Auto-Deploy.git

To deploy to our production server instead of the repository, enter:

git push prod master

If you look inside the var/www/html folder, you’ll find the index.html file automatically copied into your web folder.

If you’re using your own Git repository, you can have it located on the same server as your application, and deployments are now automated. If you’re using GitHub or another external Git service, then this hook has not completely automated your workflow, but rather has reduced it to one step. This can then be simplified further.

One option is using rsync or scp commands in the post-receive hook on GitHub. Another option – especially if your application needs a build process before going live (GitHub limits possible commands) – is to use the post-receive hook to trigger scripts on your application server that checks-out your code base from GitHub (with the -f option) and runs any other necessary commands. This is starting to get complicated, which leads nicely to our next set of tools.

Autodeployment Directly from GitHub

GitHub has its own documentation for automating deployments to integration platforms, some of which are hosting providers.

To be honest, most of the documentation I checked out was incorrect, inaccurate or unhelpful, so I did some searching to link to official documentation on some popular hosting providers, and for any others I suggest you use the post-receive or continuous integration methods:

Continuous Integration (CI) Services

There are a myriad services available that can watch your GitHub repos for changes and not only then deploy them for you, but also perform other functions such as running tests and build processes for you.

Moving to a new and more complex example, we could use CI to automate the build process of a project. Firstly, pulling the Master branch of a repository, triggering a bash script to run the build and deploy process, and then tweeting about the updates. The CI and web services could be on the same server or on different servers depending on your preference.

Let’s take a quick look at some of the most popular.

Jenkins

You’ll need to set up your own Jenkins server, which means you get complete control, but it requires maintenance. Fortunately, it supports many platforms, including Docker if you just want to experiment first.

Jenkins achieves most of its functionality with plugins, and thanks to its age, open-source nature and popularity, it has a lot of plugins. For example, there are plugins for Git, GitHub and Twitter.

Jenkins requires a lot of configuration, and sometimes piecing together the instructions you need to construct your desired workflow can require a lot of research.

Travis

Again, the instructions for integrating Travis with GitHub are out of date in GitHub’s documentation. It’s even more simple now: read the Travis docs to find out more.

Travis doesn’t require any hosting or server setup, so if you’re keen to try CI without investing too much setup time, it’s a good starting point. However, extending it beyond its (comprehensive) default integrations will involve some extra config work. For example, Tweeting requires access to webhooks.

Travis has a habit of being slow to notice updates in your repos – especially in its own configuration file. These issues can then be hard to solve, as you have no access to the Travis server itself.

Other Commercial Services

Continuous integration is increasingly popular, so there’s been a plethora of new services and applications – many released by the creators of tools you may already be using, and which will fit snugly into existing toolchains and workflows. Here are some examples:

Wrap Up

Hopefully this brief introduction has clarified a few things for you regarding how this kind of deployment works. We’ve certainly come a long way from the days of FTPing your files to your server!

If you have any questions about the processes described above, please let me know in the comments.

Frequently Asked Questions (FAQs) on Deploying from GitHub to a Server

What are the prerequisites for deploying from GitHub to a server?

Before you can deploy from GitHub to a server, you need to have a few things in place. First, you need to have a GitHub account and a repository where your code is stored. Second, you need to have access to a server where you want to deploy your code. This could be a local server or a remote server hosted by a service provider. You also need to have SSH access to the server and the necessary permissions to read, write, and execute files on the server. Lastly, you need to have Git installed on the server.

How do I set up a deployment script on GitHub?

Setting up a deployment script on GitHub involves creating a file in your repository that contains the commands to deploy your code to the server. This file is typically named deploy.sh and is placed in the root directory of your repository. The script should include commands to clone your repository, checkout the correct branch, and copy the files to the appropriate location on the server. You can use the git clone command to clone your repository, the git checkout command to switch to the correct branch, and the cp command to copy files.

How do I automate deployments from GitHub to a server?

Automating deployments from GitHub to a server can be achieved using GitHub Actions. GitHub Actions is a CI/CD tool that allows you to automate workflows, including deployments. You can create a workflow file in your repository that defines the steps to deploy your code whenever a certain event occurs, such as when code is pushed to a specific branch. The workflow file is written in YAML and is placed in the .github/workflows directory of your repository.

How do I troubleshoot deployment issues?

Troubleshooting deployment issues can involve several steps. First, check the output of your deployment script or workflow for any error messages. These messages can often provide clues about what went wrong. If the error is related to a specific command in your script, try running that command manually on the server to see if you get the same error. If the error is related to permissions, make sure you have the necessary permissions to read, write, and execute files on the server. If the error is related to a missing file or directory, make sure the file or directory exists and is in the correct location.

How do I roll back a deployment?

Rolling back a deployment involves reverting the changes made during the deployment. This can be done by checking out a previous version of your code using Git. You can use the git log command to view the commit history and find the commit hash of the previous version. Then, you can use the git checkout command followed by the commit hash to switch to that version. After switching to the previous version, you can run your deployment script again to deploy the old version of your code.

How do I secure my deployments?

Securing your deployments involves several best practices. First, always use SSH to connect to your server, as it encrypts the data transferred between your computer and the server. Second, restrict access to your server to only those who need it. This can be done by setting up a firewall and allowing only certain IP addresses to connect. Third, regularly update your server and the software on it to patch any security vulnerabilities. Lastly, consider using a deployment tool that supports secret management, such as GitHub Actions, to securely store and use sensitive information like passwords and API keys.

How do I deploy to multiple servers?

Deploying to multiple servers involves running your deployment script on each server. This can be done manually by logging into each server and running the script, or it can be automated using a tool like GitHub Actions. With GitHub Actions, you can define a workflow that deploys your code to multiple servers in parallel. This involves setting up SSH access to each server and adding a step in your workflow for each server.

How do I deploy different branches to different servers?

Deploying different branches to different servers involves creating a separate deployment script or workflow for each branch and server. In your script or workflow, you can specify the branch to deploy using the git checkout command followed by the branch name. Then, you can specify the server to deploy to by setting the SSH connection details for that server. You can create multiple scripts or workflows and run them separately to deploy different branches to different servers.

How do I deploy only changed files?

Deploying only changed files involves using Git to determine which files have changed and then copying only those files to the server. You can use the git diff command followed by the --name-only option to get a list of changed files. Then, you can use the cp command to copy each changed file to the server. This can be done in a loop in your deployment script.

How do I test my deployments?

Testing your deployments involves running tests on your code after it has been deployed to the server. This can be done using a testing framework that supports automated testing, such as Jest for JavaScript or pytest for Python. You can add a step in your deployment script or workflow to run your tests after the code has been deployed. If the tests fail, you can stop the deployment and investigate the issue.