The following script outlines how we can use git-hooks for continuous deployment (when you push your git repository, it will automatically be deployed to the server).
Note, this script is heavily based on but slightly extends this gist, see also this explanation.
Basic idea: using git-hooks to copy the files on a target machine from a remote git repository to a specific target location.
For this, we need multiple components:
- On the target machine (the production server)
- bare-repository that servers as a remote for our local repository
target-folderwhich is the folder that will contain the code and from where the production is runpost-receivehook that copies the files from the bare-repository to the target-folder
- On the development machine (our laptop)
- ssh-config that specifies the connection to our server (name, ip, key-file, etc)
- add the target machine as a remote to our local git repo, allowing us to push to it
Here is how to achieve that:
- Initiate bare repository
~/git-repo.git, this repo will contain all the git stuff and most importantly the hook that copies the master branch to the target-folder
git init --bare ~/git-repo.git- Create the
target-folderthat will contain the code later on. Skip this step if you already have a folder for the app/whatever-your-server-does, but make sure to changeTARGETin the next step
mkdir target-folder- Create
post-receivehook in the git repository
The basic idea is that this bash-script is run after a new push is received on the server, it checks if a certain branch is updated and copies the contents to the target folder
vim git-repo.git/hooks/post-receive
chmod +x git-repo.git/hooks/post-receiveAn example post-receive hook might look like this
#!/bin/bash
TARGET="/home/ubuntu/target-folder" # the path to the target folder where the code will be copied to
GIT_DIR="/home/ubuntu/git-repo.git" # the path to the git repository
BRANCH="master" # which branch should we consider here, i.e., only pushes to master will be copied to TARGET
LOGFILE="/home/ubuntu/deployment-history.log"
# datetime for logging
DATETIME=$(date +'%Y-%m-%d %H:%M:%S')
while read oldrev newrev ref
do
# only checking out the master (or whatever branch you would like to deploy)
if [ "$ref" = "refs/heads/$BRANCH" ];
then
COMMIT=$(echo $newrev | cut -c 1-7)
TXT="[${DATETIME}] ref $ref | commit ${COMMIT} | Copying branch ${BRANCH} to ${TARGET}"
echo $TXT
echo $TXT >> $LOGFILE
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
else
TXT="[${DATETIME}] ref $ref | Only branch ${BRANCH} accepted, nothing done"
echo $TXT
echo $TXT >> $LOGFILE
fi
done- Configure ssh-config to allow git to connect to the server
vim ~/.ssh/configadd to ~/.ssh/config (or create if necessary) (see this answer for an explanation)
host name-of-the-server
HostName 123.456.789.012
IdentityFile ~/path/to/the/key.key
User git
where name-of-the-server is an arbitrary name that we use later to address the server, 123.456.789.012 is the IP address of the server, and ~path/to/the/key.key is the same key you would use in ssh (i.e., in ssh -i ~/path/to/the/key.key [email protected].
- Add the target machine as a remote
Go to your repository and add the target as a remote
git remote add production ubuntu@name-of-the-server:git-repo.gitwhere production is the name of the remote, ubuntu the user on the target machine, name-of-the-server the name of the server as specified in the previous step, and git-repo.git the bare git repository as setup in the first step.
If you mess up this step (or want to change it), git remote rm production removes the production repository.
And you are all setup.
Work as usual, once you have committed your changes, push the master branch to production with
git push production masterTo make sure that the new version is in production, you can look at the logs (on the target machine) tail ~/git-repo/deployment-hook.log (or whatever you named your logfile).
As always: Make sure that you only commit production-level code to master (and subsequently push only working code to the target server/production). Alternatively, you can can change the master branch to another production branch or similar.
This heavily encourages a good git branching model such as this one:
