How to deploy a Magento site using GIT




There are many ways to deploy a Magento site to your server. Many would argue that a simple FTP upload is fine, while others swear by version control.

The problem with the version control + Magento combo is that, necessarily, Magento stores a lot of information about its state in the filesystem – this can lead to a lot of trouble if care isn’t taken when creating the staging and deployment mechanisms.

I’ll walk you through with a tried and tested method I use for the deploying a Magento site using GIT.

Advantages

Before going any further, I’m going to pause to discuss the advantages of having your Magento installation under version control:

  • If you accidentally make breaking changes to code, it’s oh so easy to roll them back.
  • Testing changes in multiple development environments becomes a doddle.
  • Working out where bugs came from is a doddle.
  • Making sure all copies of the code contain the same version of patches, extensions and themes is a doddle.
  • Everything is a doddle, apparently.

Here’s a quick model of how everything will work:

DeployDiag

Pre-requisites

Assuming you already have GIT installed and Magento running locally, the first thing you’re going to want to do is set up version control in your application. To do this, simply navigate to your root directory using the shell/command prompt and perform a git init there.

Immediately after this, you should create a .gitignore file that ignores anything specific to your local installation or that you otherwise don’t want on the live server and/or other developers to have.

This includes:

  • the LICENSE.* files
  • log files
  • the /media folder
  • the /var folder
  • the /downloader folder contents
  • your /app/etc/local.xml

Each installation is different, so you may need to tailor this to your specific needs.

Interestingly, I am going to include all the core Magento files. While it is true that you should never edit core files yourself, you want all instances of this repo to be using the same codebase – so include those core files!

This will make for an enormous repo – 10,000+ files – but it will save a lot of ball ache later, trust me.

The next thing you need to do is add all your files…so let’s do a git add . and come back later…


So after a couple seconds, or minutes, you should have all of your files added. Go ahead and do a git commit -m "Initial Commit" and we now have our basic set up ready.

Branches

Before we go any further, we need to think about branches. You’ll want to think carefully now to avoid ballache in the future…

You’re probably going to at least want a master and develop branch.

I do, however, have one recommendation when it comes to branches:

git-flow

With git-flow you have a solid, tested method for keeping active development that works really well for Magento.

Picture from: http://nvie.com/posts/a-successful-git-branching-model/

Picture from: http://nvie.com/posts/a-successful-git-branching-model/

If you already use Atlassian SourceTree, then you’re in luck, because git flow is built in and works seamlessly.

Many people have sung the praises of git-flow already, so I’ll leave you and Google to research that one for yourself.

Dev Server Setup

The next step in our adventure is to set up the git repos on our server. I’m gonna go ahead and assume you have a dev/staging server set up (you do have a dev/staging server, right?), so lets try that one first.

Whether your dev server and live server are two physically separate servers, or just different accounts on the same physical server, these instructions should apply.

So, lets get going, first set up a bare repo in some folder on our server:

mkdir /home/username/GIT
mkdir /home/username/GIT/magento.git
cd /home/username/GIT/magento.git
git init --bare

Now, we’re going to make a post-receive hook for when we push to our dev server, so go ahead and do the following:

mkdir /home/username/GIT/checkout
cd /home/username/GIT/magento.git/hooks
touch post-receive
chmod +x post-receive
vim post-receive

You’ll see what the new directory is for in a moment…and now comes the fun part. What we want is the following:

  1. Checkout our development branch into the checkout folder
  2. Set up the correct permissions and ownership on those files
  3. Copy those files to our web directory
  4. Clean the caches

Achieving all of this is fairly simple from a shell script. To copy the files from the checkout directory to the web directory, we’re going to use a tool called rsync, which is capable of duplicating a directory in another directory with several options for customisation.

Here’s my final completed script:

/home/username/GIT/magento.git/post-receive

#!/bin/sh
# Some handy variables
REPO_DIR="/home/username/GIT/magento.git/"
WC_DIR="/home/username/GIT/checkout/"
WEB_DIR="/home/username/public_html"
CACHE_DIR="/home/username/public_html/var/cache/*"
# Checkout our repo into the checkout folder
GIT_WORK_TREE=$WC_DIR git checkout -f develop
# Make some ownership and permissions changes
chmod -R 0775 $WC_DIR
chmod -Rf 0500 ${WC_DIR}app/etc/local.xml
chown -R nobody $WC_DIR
# rsync with web root
/usr/bin/rsync --archive \
--delete \
--exclude=/app/etc/local.xml \
--exclude=.svn \
--exclude=Thumbs.db \
--exclude=*.log \
--exclude=*.htaccess \
--exclude=/feeds/* \
--exclude=.gitignore \
--exclude=sitemap.xml \
--exclude=/cgi-bin/ \
--exclude=/sh/ \
--exclude=/tmp/ \
--exclude=/downloader/.cache/ \
--exclude=/includes/src/ \
--exclude=/media/ \
--exclude=/nbproject/ \
--exclude=/pkginfo/ \
--exclude=/tmp/ \
--exclude=/var/ \
$WC_DIR \
$WEB_DIR
# clear the caches
rm -rf $CACHE_DIR

Note that this checks out the develop branch.

One thing to note is you should have Magento set up on your dev server already – if you haven’t done so already, go ahead and upload a copy of your site however you wish and make the necessary set up changes (base_urls in core_config_data and the /app/etc/local.xml file)

Now, let’s test this deployment mechanism out. On your local machine, let’s add our server as a remote. I won’t cover here how to set up SSH keys and/or use usernames and passwords for SSH, but it should be fairly self-explanatory.

Using the method of your choice, you need to add your server as a remote. As an example, on the command line, you can do it like this:

git remote add origin ssh://root@yourserver.com:22/home/username/GIT/magento.git

Test

So now let’s test this out, make a small change anywhere in the code (add body { background:green;} to the css for example) and create a new test commit and push up to our server.

git add .
git commit -m "Made the background green"
git push origin develop

You might be asked to enter your password if you haven’t got an SSH key set up, but now with any luck, if you navigate to your dev site, you should see your changes!

greengento

shudder

Looks good!

Live server

The set up for the live server deployment will be much the same, but you have two options here – you can either:

  1. have the server detect what branch is being pushed and checkout/rsync to the corresponding place
  2. just checkout both the develop and master branches every time
  3. leave it as a manual process (just in case)

Which one you choose is up to you, I’ll go over all 3 here.

Option 1: Detecting which branch is pushed

When you push a branch, the post-receive hook gets this as arguments to stdin. Multiple branches can be pushed at the same time, so we’d need to loop over the arguments and perform the corresponding action:

/home/username/GIT/magento.git/post-receive

#!/bin/shell
# Loop through stdin arguments
while read oldrev newrev ref
do
# get the branch name
branch=${ref#refs/heads/}
# special case for master branch
if["$branch" = "master"]
then
# username for live installation
USER="live_username"
else
# username for dev installation
USER="dev_username"
fi
REPO_DIR="/home/$USER/GIT/magento.git/"
WC_DIR="/home/$USER/GIT/checkout/"
WEB_DIR="/home/$USER/public_html"
CACHE_DIR="/home/$USER/public_html/var/cache/*"
# Checkout our repo into the checkout folder
GIT_WORK_TREE=$WC_DIR git checkout -f $branch
# Make some ownership and permissions changes
chmod -R 0775 $WC_DIR
chmod -Rf 0500 ${WC_DIR}app/etc/local.xml
chown -R nobody $WC_DIR
# rsync with web root
/usr/bin/rsync --archive \
--delete \
--exclude=/app/etc/local.xml \
--exclude=.svn \
--exclude=Thumbs.db \
--exclude=*.log \
--exclude=*.htaccess \
--exclude=/feeds/* \
--exclude=.gitignore \
--exclude=sitemap.xml \
--exclude=/cgi-bin/ \
--exclude=/sh/ \
--exclude=/tmp/ \
--exclude=/downloader/.cache/ \
--exclude=/includes/src/ \
--exclude=/media/ \
--exclude=/nbproject/ \
--exclude=/pkginfo/ \
--exclude=/tmp/ \
--exclude=/var/ \
$WC_DIR \
$WEB_DIR
# clear the caches
rm -rf $CACHE_DIR
done

Option 2: Just checkout both branches each time

This can be advantageous – the dev server will always have the latest copy of the develop branch on it and the live server will always have the latest copy of the master branch on it.

This is as simple as doing the process twice in a loop:

/home/username/GIT/magento.git/post-receive

#!/bin/shell
# Loop through stdin arguments
BRANCHES=("develop" "master")
for branch in "${BRANCHES[@]}"
do
# special case for master branch
if["$branch" = "master"]
then
# username for live installation
USER="live_username"
else
# username for dev installation
USER="dev_username"
fi
REPO_DIR="/home/$USER/GIT/magento.git/"
WC_DIR="/home/$USER/GIT/checkout/"
WEB_DIR="/home/$USER/public_html"
CACHE_DIR="/home/$USER/public_html/var/cache/*"
# Checkout our repo into the checkout folder
GIT_WORK_TREE=$WC_DIR git checkout -f $branch
# Make some ownership and permissions changes
chmod -R 0775 $WC_DIR
chmod -Rf 0500 ${WC_DIR}app/etc/local.xml
chown -R nobody $WC_DIR
# rsync with web root
/usr/bin/rsync --archive \
--delete \
--exclude=/app/etc/local.xml \
--exclude=.svn \
--exclude=Thumbs.db \
--exclude=*.log \
--exclude=*.htaccess \
--exclude=/feeds/* \
--exclude=.gitignore \
--exclude=sitemap.xml \
--exclude=/cgi-bin/ \
--exclude=/sh/ \
--exclude=/tmp/ \
--exclude=/downloader/.cache/ \
--exclude=/includes/src/ \
--exclude=/media/ \
--exclude=/nbproject/ \
--exclude=/pkginfo/ \
--exclude=/tmp/ \
--exclude=/var/ \
$WC_DIR \
$WEB_DIR
# clear the caches
rm -rf $CACHE_DIR
done

Very similar to the other process!

Option 3: Leave it as a manual process, just in case

Leaving it as a manual process is very simple – create an executable file on the server somewhere:

cd /home/username/GIT/bin
touch to-live
chmod +x to-live
vim to-live

And then in this file put the following:

/home/username/GIT/bin/to-live

#!/bin/shell
REPO_DIR="/home/live_username/GIT/magento.git/"
WC_DIR="/home/live_username/GIT/checkout/"
WEB_DIR="/home/live_username/public_html"
CACHE_DIR="/home/live_username/public_html/var/cache/*"
# Checkout our repo into the checkout folder
cd $REPO_DIR
GIT_WORK_TREE=$WC_DIR git checkout -f master
# Make some ownership and permissions changes
chmod -R 0775 $WC_DIR
chmod -Rf 0500 ${WC_DIR}app/etc/local.xml
chown -R nobody $WC_DIR
# rsync with web root
/usr/bin/rsync --archive \
--delete \
--exclude=/app/etc/local.xml \
--exclude=.svn \
--exclude=Thumbs.db \
--exclude=*.log \
--exclude=*.htaccess \
--exclude=/feeds/* \
--exclude=.gitignore \
--exclude=sitemap.xml \
--exclude=/cgi-bin/ \
--exclude=/sh/ \
--exclude=/tmp/ \
--exclude=/downloader/.cache/ \
--exclude=/includes/src/ \
--exclude=/media/ \
--exclude=/nbproject/ \
--exclude=/pkginfo/ \
--exclude=/tmp/ \
--exclude=/var/ \
$WC_DIR \
$WEB_DIR
# clear the caches
rm -rf $CACHE_DIR

Then, when you want to push code to the live server:

bash /home/username/GIT/bin/to-live

simple!

Seperate Live and Dev Servers

You have several options when it comes to pushing code to the live server, two of which are:

  1. Use the method above and use rsync across the two servers using SSH
  2. Have 2 remotes

I’ll go through both:

Option 1:

By making a small change to the rsync command:

/home/username/GIT/bin/to-live

#!/bin/shell
# change number one
WEB_DIR="root@liveserver.com:/home/live_username/public_html/"
# ..same as usual up to this point...#
# rsync with web root
/usr/bin/rsync --archive \
--delete \
--exclude=/app/etc/local.xml \
--exclude=.svn \
--exclude=Thumbs.db \
--exclude=*.log \
--exclude=*.htaccess \
--exclude=/feeds/* \
--exclude=.gitignore \
--exclude=sitemap.xml \
--exclude=/cgi-bin/ \
--exclude=/sh/ \
--exclude=/tmp/ \
--exclude=/downloader/.cache/ \
--exclude=/includes/src/ \
--exclude=/media/ \
--exclude=/nbproject/ \
--exclude=/pkginfo/ \
--exclude=/tmp/ \
--exclude=/var/ \
-e "ssh -p 22" \   # if you need to specify the port number
$WC_DIR \
$WEB_DIR
# clear the caches
ssh -p 22 root@liveserver.com "rm -rf $CACHE_DIR"

Option 2: Two seperate remotes

This option is simple, simply have two remotes, duplicate the above steps for setting up the deployment process on the dev server and have two remotes, live and develop:

git remote add dev ssh://root@yourdevserver.com:22/home/dev_username/GIT/magento.git
git remote add live ssh://root@yourliveserver.com:22/home/live_username/GIT/magento.git

You can then replicate our setup on both servers.

Summary

You now have a robust and capable, GIT based deployment mechanism for your Magento site!

You may need to play around a bit with what files are excluded in the rsync and tinker around with things depending on your exact setup, but this guide should help you get through the main points of having a proper deployment mechanism.

4 Replies to “How to deploy a Magento site using GIT”

    1. Database is already set up on the server – we don’t want to interfere with the live site’s database!

      Any modules you install will automatically create the necessary tables when you sync their files over.

  1. Does this work with Magento 2? I’ve already used git to get the code from github and there is already a .gitignore file. Will I need to set up something a little different? (I’m new to git and version control)

Leave a Reply