Skip to content

Instantly share code, notes, and snippets.

@lunix33
Last active September 20, 2019 23:17
Show Gist options
  • Save lunix33/2826e93894c65fceb24596de85d38928 to your computer and use it in GitHub Desktop.
Save lunix33/2826e93894c65fceb24596de85d38928 to your computer and use it in GitHub Desktop.
Git bare automated deployment

Prelude

This guide will help you setup a bare git repository (essentially a repository which serves as a "server") to be able to automatically deploy its content when a push is received.

This can be useful if you have by example a web server (like bluehost or simple vps) where you which to deploy your code without setting up a full code deployment Pipeline through utilities like Jenkins.

The strategy used in this guide to automate the deployment is through the post-receive git hook.

The hook configuration is flexible and modular so you can split your deployment procedure through multiple files and it doesn't enforce a language as long as you can run the script with the first line as a shebang like this on the first line: #!/bin/bash (replace /bin/bash with the actual executable which should be used to run your script)

Note: This guide doesn't focus on setting up transfert solution like ssh or http nor how to install the git client, you'll need to figure it out on your own.

Requirement

  • Basic git and bash understanding
  • A bash terminal (kinda hard to miss on linux, on windows you can use msys2 (git bash) or cygwin)
  • A server an client computer with the git client installed
  • A server setup with a supported mean to clone and push a git repository (ssh/git/http(s)) Be certain the client is able to login (ssh) or have access to the content (http(s))

Setting things up

  1. On your server
    1. On your server locate where you want your git repository to be and create a {repo name}.git folder
    2. In that folder initialize your repository with the command: git init --bare
    3. Go into the hooks folder and copy the post-receive script which is available a bit lower.
    4. Make the hook script executable with: chmod +x post-receive
    5. Make sure the appropriate user can read/write the repository.
  2. On your client
    1. Create a folder and initialize an empty repository with git init
    2. Add your server's remote repository with: git remote add origin {remote address}
    3. Add your code
    4. Now you can run the regular: git add .; git commit -m '{message}'; git push -u origin master.

At this point your code should by synced to the remote repository and be automatically deployed.

But how do we automatically deploy ?!

You have two solution to be able to deploy your code. You can make a deploy script which will be executed once the code is received by the server. The other way involve making a collection of smaller script which will handle all the operations needed to deploy the code.

Single script

At the root of your repository working tree add a script named: .gitdeploy. The language your script is using doesn't matter as long as you can run your script with a shebang at the top of the file. The post-receive hook uses the shebang information to find which runtime needs to be called to execute the script.

Multiple files

At the root of your repository working tree add a folder named: .gitdeploy. In that folder you are free to create as many scripts as you wish to actually deploy your application. The scripts are executed in alphabetical order (numbers). Like for the single script solution, you aren't restricted when it comes to the scripting language.

When it comes to the multiple file solution, some sanity checking is done. If your script doesn't end with an error code 0, then the deployment is canceled.

General information

It's important each script has its own shebang (#!/path/to/bin) on the very first line of the script. The shebang is used for runtime detection therefore it is mandatory.

Your script will be called by the runtime with the following positional arguments:

  1. Git work tree root path
  2. Branch name
  3. Git ref

Feedback

Don't hesitate to provide feedback and suggest changes to the hook.

#!/bin/bash
# Global variables
rc=0 # DO NOT EDIT
gitdir=$(cd $(dirname $0)/..; pwd) # DO NOT EDIT
checkoutdir="${gitdir}/work-tree"
gitdeploy="${checkoutdir}/.gitdeploy" # DO NOT EDIT
##
# Get the runtime of the gitdeploy with the shebang.
# $1: File from which we need to find the runtime.
# Returns the file runtime.
##
function runtime {
shebang=$(head -n 1 $1)
bang=${shebang:0:2}
if [ "${bang}" == "#!" ]; then
echo ${shebang:2}
else
echo ""
fi
}
##
# Execute a file. This function tries to auto-detect the runtime.
# $1: The file to run
# $2: The git branch
# $3: The git ref
# Returns the exit code of the command.
##
function runfile {
command=$(runtime $1)
if [ "${command}" != "" ]; then
echo "Running: ${command} $1 ${checkoutdir} $2 $3"
${command} $1 ${checkoutdir} $2 $3
return $?
else
echo "Unable to detect the runtime. Make sure your `.gitdeploy` has a shebang"
exit 1
fi
}
##
# Main
##
mkdir -p ${checkoutdir}
while read oldref newref ref; do
# Find branch name
branch=$(echo ${ref} | cut -d '/' -f 3)
echo "Received data for ${branch} (${ref})"
# Checkout code in work tree
git --work-tree=${checkoutdir} --git-dir=${gitdir} checkout -f ${branch}
# Action taken if .gitdeploy is a folder
# Run each of the files within .gitdeploy, cancel if one of the files exit with non-zero code.
if [ -d "${gitdeploy}" ]; then
for f in ${gitdeploy}/*; do
if [ -f "$f" ]; then
runfile ${f} ${branch} ${ref}; rc=$?
echo "$(basename $f) finished with code: ${rc}"
if [ "${rc}" != "0" ]; then
break
fi
fi
done
# Action taken if the .gitdeploy is a file
elif [ -f "${gitdeploy}" ]; then
runfile ${gitdeploy} ${branch} ${ref}; rc=$?
echo "Git deploy finished with code: ${rc}"
fi
done
exit ${rc}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment