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-folder
which is the folder that will contain the code and from where the production is runpost-receive
hook 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-folder
that 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 changeTARGET
in the next step
mkdir target-folder
- Create
post-receive
hook 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-receive
An 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/config
add 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.git
where 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 master
To 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: