Deploy with git push

Introduction

Wouldn’t it be nice to publish a website with a simple git push?
Here I’ll explain you how I usually do this.

I assume that the environment of the staging/production servers are the following:

  • Operating system: Ubuntu 14.04 or Ubuntu 16.04

If your servers are not the above, the approach described in this document should work with minor changes given that they are *nix.

Configuration

Let’s assume that:

  • the remote server is available at the address
  • you want to create a new repository named
  • the web server impersonates the user in the group
  • you want to execute these commands after the push:
    • -

One-time server setup

Install required packages

First of all, you need to install git on the server.
You can do this by running the following command:

sudo apt-get update
sudo apt-get install -y git

If the above command fails with an error like Unable to locate package git, you can try this:

sudo apt-get install -y git-core

Create the git user

We need to create a user account on the server. This account will be the one used by the publish process.

With the following command we create that account:

sudo adduser --gecos Git --disabled-login --disabled-password --shell /usr/bin/git-shell --home /home/git --ingroup  git

Here’s the explanation of the above options:

  • --gecos Git: set the full name of the account to Git (this essentially in order to avoid asking useless data like the account room number and work/home phone)
  • --disabled-login: the user won’t be able to use the account until the password is set.
  • --disabled-password: disable the login using passwords (we’ll access the system with SSH RSA keys)
  • --shell /usr/bin/git-shell: when the user access the system, he will use the fake shell provided by git
  • --home /home/git: set the home directory for the user to /home/git
  • --ingroup : add the new user to the group instead of the default one
  • git: the username of the new account

Strengthen login security

We then need to configure the git shell.
In order to improve the security of unwanted logins and abort shell sessions, we create a file that is executed when the git user logs in the shell and that will abort the session.

sudo mkdir /home/git/git-shell-commands
sudo nano /home/git/git-shell-commands/no-interactive-login

In the editor type in these commands:

#!/bin/sh
printf '%s\n' "Hi $USER! You've successfully authenticated, but I do not"
printf '%s\n' "provide interactive shell access."
exit 128

To save the new file hit CTRL+o then ENTER.
To quit the editor, hit CTRL+x.

We finally have to make the file executable:

sudo chmod +x /home/git/git-shell-commands/no-interactive-login

Allow impersonation

The git user needs to be able to publish files acting like .
In order to allow this, run this command:

sudo visudo

Go to the end of the editor contents and add these lines:

Defaults!/usr/bin/git env_keep="GIT_DIR GIT_WORK_TREE"
git ALL=() NOPASSWD: /usr/bin/git

The first line tells the system that when the git command is executed with a sudo, we need to keep the two environment variables GIT_DIR and GIT_WORK_TREE.
The second line tells the system that the git user can execute the git command acting as without any further authentication.

Authorized developers

Every developer that should be able to publish needs an SSH-2 RSA key pair.
It’s possible (and recommended) to use a different key for every developer.

Create the keys under Windows

I order to create the key pair, you can use PuTTYgen.
If you already installed TortoiseGit you should already have this command, otherwise you can download it.

So, open PuTTYgen and:

  • in the Parameters page be sure to check SSH-2 RSA and 2048 for the Number of bits in a generated key
  • Hit the Generate button and move randomly your mouse over the PuTTYgen window
  • In the Key comment field enter ServerName - DeveloperName (where ServerName is the name of the server where the key will be used, and DeveloperName is the name of the developers owning the key)
  • In the Key passphrase and Confirm passphrase fields enter a password of your choice
  • Hit the Save private key button to save the private key to file
  • Copy the contents of the Public key for pasting into OpenSSH authorized_keys file and save it: this is the public key that we’ll need.

Create the keys under *nix

Simply run this command:

ssh-keygen -t rsa -b 2048 -f key-for-git -C 'ServerName - DeveloperName'

Where:

  • -t rsa: we want public/private key pair in the SSH-2 RSA format
  • -b 2048: we want 2048 bits in the key
  • -f key-for-git: this is the name of the file that will contain the private key
  • -C 'ServerName - DeveloperName': this is the comment to associate to the key (it’s a good practice to specify the name of the server and the one the developer owning the key)

Once you run the ssh-keygen command and specified a password of your choice, you’ll have these two files:

  • key-for-git: contains the private key
  • key-for-git.pub: contains the public key

Allow the developer to publish to the server

Login to the server and run this command:

sudo mkdir /home/git/.ssh
sudo nano /home/git/.ssh/authorized_keys

Go to the end of the editor contents and add a new line containing the previously generated public key.

The public key is a single line that starts with ssh-rsa , followed by a quite long list of characters and ending with the ServerName - DeveloperName comments specified during the creation of the key.

Create a new repository on the server

We’ll end up with:

  • Directory to be published: /var/www/
  • Repository directory: /var/git/

First of all, we create the directory that will contain web site (it will be owned by the user):

sudo mkdir -p /var/www/
sudo chown -R : /var/www/
sudo chmod u+rw -R /var/www/

Then we create the directory that will contain the bare repository data:

sudo mkdir -p /var/git/.git
cd /var/git/.git
sudo git init --bare
sudo git config core.sharedRepository group

The core.sharedRepository group option of the git repository is needed in order to grant write access to both the git and users (they both belong to the same user group - ).

And now the key concept of this whole approach: when someone pushes to this repository, we checkout the repository to the publish folder:

sudo nano /var/git/.git/hooks/post-receive

In the editor type these lines:

#!/bin/bash
currentUser=`whoami`
currentServer=`hostname`
repoDirectory=/var/git/.git
pubDirectory=/var/www/
echo "Hello! I'm $currentUser at $currentServer"
echo "I'm going to publish from"
echo "   $repoDirectory"
echo "to"
echo "   $pubDirectory"
rc=0
sudo -u  git --git-dir=$repoDirectory --work-tree=$pubDirectory checkout master -f
if [ "$?" -ne "0" ]; then
    echo "GOSH! GIT FAILED!!!!"
    rc=1
fi

if [ $rc -eq 0 ]; then
    echo "Don't worry, be happy: everything worked out like a charm ;)"
fi
exit $rc

We finally need to update the permissions of the newly created directory:

sudo chown -R git: /var/git/.git
sudo chmod -R ug+rwX /var/git/.git
sudo chmod -R ug+x /var/git/.git/hooks/post-receive

Push-to-publish

Everything is almost ready! The only step required to deploy with a simple git push is to add the remote to your repository.

For instance, here’s how to add a remote named deploy to your local repository:

git remote add deploy git@:/var/git/.git

When you push to the deploy remote, the published directory willbe updated automatically.

Nice, isn’t it?

Questions? Comments? Join the Gitter chat