#!/bin/bash # # git-deploy # # git post-receive hook to check out branches to a rsync destination # # Copyright 2012 K and H Research Company. # License: WTFPL, any version or GNU General Public License, version 2+ # ## ## Documentation ## ## Installation # # To install git-deploy, copy this file to the hooks/ directory of a repository # as "post-receive". Note that there is NO extension! # # You will need to set the git config variable deploy.$FOO.uri in order for this # script to do anything. See the 'Configuration' section for more information. # # In order to function properly you must have rsync and the git-core suite on # your system. If these are in non-standard locations or not within PATH you # should set the RSYNC and GIT vars below. Other common utilities such as mkdir, # cp, find, rm, umask, and tar are also required, but if you don't already have # these you should probably see a psychiatrist. # ## Configuration # # Several configuration options are supported by git-deploy, only one of which # is mandatory(deploy.$FOO.uri). These options are all set via git-config. # Several constants(see below) may be changed in the script itself, but you # should not need to do so on a sane system. In all of the following, $FOO is # the name of the branch which you wish to have automagically deployed. # # deploy.$FOO.opts # Set of options to pass to rsync. git-deploy defaults to "-rt --delete", # which will work (r)ecursively, attempt to maintain (t)imestamps, and # (delete) files which do not exist in the source. You will likely want to # add the --exclude=foo/ option to guard agaisnt deletion of ephermeral # data directories used by your application. Please note that no injection # checking is done against this option(patches welcome). # # deploy.$FOO.timestamps # Whether or not to attempt to maintain timestamps on the work-tree which # is checked-out. If true git-log is used to find the last commit which # affected each path in the worktre, and then 'touch -m' is used to set # the modification time to this date. # # deploy.$FOO.uri # rsync URI which should be deployed to for branch $FOO. This can be any # scheme which is known to 'rsync', including a local filesystem path, or # a remote host(via SSH) # ## Usage # # To use git-deploy simply push into your repo and git's hook system will take # care of the rest. Errors and information will be shown to you as the script # works its magic. If you wish to manually deploy you can do so by piping, on # stdin, the same data that is fed to any git pre-receive hook. # ## Todo # # 1) Split out the "meat" to a git-deploy script which can be invoked via the # 'git' binary in a non-bare repository # # 2) Improve documentation wording - find an English teacher to run it by or # something. # ## ## Constants ## # Path to the git binary GIT=$(which git) # Path to the rsync binary RSYNC=$(which rsync) # Temporary directory TMP="/tmp" # Repo directory export GIT_DIR=$(pwd) ## ## Variables ## ## ## Sanity checks ## ## Existence of git if [ ! -f "${GIT}" ] then # Error && exit echo "Error: git binary not found" exit 255 fi ## Existence of rsync if [ ! -f "${RSYNC}" ] then # Error && exit echo "Error: rsync binary not found" exit 255 fi ## Existence of tmpdir if [ ! -d "${TMP}" ] then # Error && exit echo "Error: tmp directory not found" exit 255 fi ## ## Runtime ## # Create scratch dir if mkdir "${TMP}/git-deploy.$$" then scratch="${TMP}/git-deploy.$$" else # Error && exit echo "Error: unable to create scratch dir or already exists." exit fi # Loop through stdin while read old new ref do # Find branch name branch=${ref#"refs/heads/"} # Check branch name if [ -z "${branch}" ] then echo "Refspec ${ref} is not a branch. Skipped!" fi # Don't attempt to handle deleted branches if [ "${new}" = "0000000000000000000000000000000000000000" ] then # Error && skip branch echo "Branch ${branch} deleted. Skipped!" continue fi ## Attempt to update echo "Branch ${branch} updated. Deploying..." # Deploy destination dest=$(git config --get "deploy.${branch}.uri") if [ -z "${dest}" ] then echo "Error: Destination not set! Deploy failed." continue fi echo "Destination: "${dest} # Rsync options opts=$(git config --get "deploy.${branch}.opts") if [ -z "${opts}" ] then opts="-rt --delete" fi echo "Options: "${opts} # Create directory to archive into mkdir "${scratch}/${branch}" # Drop into scratchdir cd "${scratch}/${branch}" # Set umask umask 007 # Get a copy of worktree $GIT archive --format=tar ${new} | tar xf - # Alter modification times? timestamps=$(git config --bool --get "deploy.${branch}.timestamps") if [ "${timestamps}" == "true" ] then # Set modification times to last-changed for file in $(find ./ -type f) do # Get the date of the last commit last=$(git log ${branch} --pretty=format:%ad --date=rfc -1 -- ${file}) # Set the modification time touch -t $(date -d "${last}" +%Y%m%d%H%M.%S) ${file} done fi # Copy worktree to destination $RSYNC $opts "${scratch}/${branch}/" "${dest}" status=$? if [ "${status}" -ne "0" ] then echo "Error: rsync exited with exit code ${status}. Deploy may not have been successful. Please review the error log above." else echo "Deploy successful!" fi echo "" done ## ## Cleanup ## # Remove scratch dir rm ${scratch} -rf # Unset environment variables unset GIT RSYNC TMP GIT_DIR scratch old new ref branch dest optstimestamps file unset last