MichD

 

Deploying to your server with git push

in Blog

If you’re using git for your project’s version control, it might interest you that you can deploy your latest changes to the server where you’re hosting your application by using just git on your local computer. I set this up for the site you’re reading, so I figured I’d put up a write-up of how I did it.

The heart of the matter are git hooks. Hooks are optional scripts that git can run for you in response of certain events happening. The main event we’re after, on the server end, is called post-receive. It is triggered after a git push.

Assumptions

For this guide, I assume the following:

  • You have git installed on your local computer, and the server you deploy to
  • Your server is running a Linux distribution, or at least has a bash shell.
  • You have some experience using a (bash) terminal
  • You are able to connect to your server via SSH, from the machine you’re working on

Setting up the repository on the server

SSH into your server. I recommend doing so with a normal user, rather than root.

$ ssh youruser@yourserver

Navigate to where you want to store your git repository, and create a directory. For convention’s sake, I like adding .git as part of the folder name, as this will be more familiar later on.

$ mkdir myproject.git
$ cd myproject.git

Next, set up a “bare” git repository in this folder. It needs to be bare to be able to push to it normally. A bare repository does not include the working directory; instead, it contains essentially all you’d find in the .git folder of your local version.

Please note that this step is still valid if you already have an existing repository you’d like to deploy with.

$ git init --bare

We do want to get a working directory containing all the repository’s files as well, so the next step is cloning the repository. We’re still on the server, and have not changed directories.

$ git clone . files

You likely recognise the git clone command. Here we’re using . to refer to the location of the repository we’re cloning. We’re in the very folder we are cloning from, so . refers to it. files is the name of the folder we’re cloning into. Git will now warn you that you are cloning an empty repository; this is expected, as we’ve just initialized a bare one without adding anything to it.

Setting up hooks

$ cd hooks
$ vim post-receive

Replace vim with the editor of your choice. In any case, ensure the file post-receive contains the following content:

#!/bin/bash

GIT_WORK_TREE=/home/youruser/myproject.git/files GIT_DIR=/home/youruser/myproject.git/files/.git git pull origin master
exit

Replace the paths with the full path to the directory you’re in. These set git’s environment variables to the exact things they need to be, instead of relying on it being right in the context of the script being run. (Note: this may possibly be improved upon, but I’ve yet to experiment with doing this more simply. At present though, this setup works.)

After setting the environment variables, git pull origin master is run. In this case, origin is the bare repository we’ve set up, myproject.git.

Save the file. If you ended up using vi or vim and need some quick relevant help:

Upon opening vi or vim, it’s in “normal mode” and won’t let you type. Press i to enter insert mode, then type the contents. Once you’re done typing up the file, press the escape key to leave insert mode. Now press : to open up a little command prompt, followed by w and q, and lastly the return/enter key. These are two commands, “write” and “quit”, so it’ll save the file and exit the editor.

Since this file will be executed by git, we need to ensure it’s executable.

$ chmod +x post-receive

Now we need to set up a second hook, this time in the working copy (files). This one is run whenever a post-merge event happens, a result of git pull.

$ cd files
$ vim .git/hooks/post-merge
$ chmod +x .git/hooks/post-merge

The contents of post-merge can be anything you like. I like having it to check for a deploy.sh file that’s part of my repository, so I can commit a deploy script along with the repository itself.

Here’s my post-merge:

#!/bin/bash

if [ -x "deploy.sh" ]; then
  echo "Running deploy script."
  ./deploy.sh
else
  echo "deploy.sh was not found or is not executable, not doing anything."
fi

This checks for the existence of a deploy.sh file in the working directory (here files/), while checking if it’s executable as well. For more info on available tests you can do in bash, check the manual on test in bash.

If the file exists and can be executed, it executes it; otherwise, it prints a message indicating the issues.

Save and quit, and don’t forget the chmod command.

You’re now mostly done on the server side.

Setting up a new remote on your local machine

Navigate to your local git repository and add a new remote with git:

$ cd path/to/my/repository
$ git remote add production youruser@yourserver:/home/youruser/myproject.git

Here production is the name for the new remote git repository. Normally, when you’ve cloned a repository, it only has the automatically added origin remote. production is the new one being added. youruser is the user you log in as when you SSH to your server, yourserver is the host name or IP address of your server. After the colon you specify the full absolute path to the myproject.git directory you set up on the server.

After running this, your .git/config file should contain the following, among other things:

[remote "production"]
	url = youruser@yourserver:/home/youruser/myproject.git
	fetch = +refs/heads/*:refs/remotes/production/*

From this point, you’re essentially ready to go. You can now push to your server as follows:

$ git push production master

You will then be prompted for the same login details like you would when SSH-ing into your server. This could be a password, or a password to access your private/public key pair.

On the first such push after initializing the empty bare repository on the server, the post-merge script will not be run, because nothing actually gets merged yet. If you need to run the deploy script this time, you can either run it manually on the server, or create a new commit which you then push.

Include a deploy script in your repository

If you’re using my version of the post-merge script, you’ll want to add a file called deploy.sh in your git repository’s main directory.

This is another bash script in which you can write anything you like. For example, this website uses Hugo as a static site generator. So, upon pushing the markdown files, I want to run the hugo command to generate the HTML forming the webpage you’re reading right now. My deploy.sh file looks as follows:

#!/bin/bash
# This script is run by a git hook on my server when pushed to.
echo "Wiping public/ folder..."
rm -r public
echo "Updating static site with hugo..."
hugo
echo "Done."

Don’t forget to make this file executable:

$ chmod +x deploy.sh

Don’t forget to point your web server to the appropriate new directory after setting this up.

Anyway, that’s it it. Please let me know on Mastodon or in the comments if you’ve got any questions or improvements.