Blog : Suit My Mind - Software that suits you

Deploying multiple environments on Heroku (while still hosting code on Github)

As mentioned in an earlier post, Cashflow App is now hosted on Heroku.

The main repository for my code is still hosted on Github. This is where I manage/version it.

When you create an application on Heroku, it is automatically associated with a git repository hosted on Heroku. Deploying changes to your application is as simple as pushing your code to the associated git repository.

Here is a schema of what I want to achieve :

In this post, I will explain how I use Github to version my code and Heroku to deploy it, with 2 different environments. You may also read this article that explains how to deploy your application on Heroku and this one that also explain how to deal with multiple environments.

Setup different environments on Github

On Github, I want to work with 2 branches : a master branch for production and a staging branch for testing. The master branch is what you get when you first clone your repository :

git clone git@github.com:account_name/repository_name.git
cd repository_name
[master]$

To create a staging branch, just do :

[master]$ git checkout -b staging
Switched to a new branch "staging"
[staging]$

Then push your local staging branch to Github :

[staging]$ git push origin staging
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:account_name/repository_name.git
* [new branch]      staging -> staging
[staging]$

You should now have 2 branches on Github

Create the Heroku Staging App

Now, I will create a Staging app on Heroku. This will create a git repository. I will only push code to this repository when I want to deploy modifications to the Staging app.

[staging]$ heroku create myapp-staging --remote heroku-staging
Created http://myapp-staging.heroku.com/ | git@heroku.com:myapp-staging.git
Git remote heroku-staging added
[staging]$

You'll notice that I specified a custom remote name : heroku-staging. It will allow you to specifically deploy your code to the Staging app.

Your .git/config file should now look like this :

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
[remote "origin"]
url = git@github.com:account_name/repository_name.com.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[remote "heroku-staging"]
url = git@heroku.com:myapp-staging.git
fetch = +refs/heads/*:refs/remotes/heroku-staging/*

Create the Heroku Production App

Go back to the master branch :

[staging]$ git checkout master
Switched to branch "master"
[master]$

Create the heroku prod app :

[master]$ heroku create myapp-prod --remote heroku-prod
Created http://myapp-prod.heroku.com/ | git@heroku.com:myapp-prod.git
Git remote heroku-prod added
[master]$

Your .git/config file should now look like this :

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
[remote "origin"]
url = git@github.com:account_name/repository_name.com.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[remote "heroku-staging"]
url = git@heroku.com:myapp-staging.git
fetch = +refs/heads/*:refs/remotes/heroku-staging/*
[remote "heroku-prod"]
url = git@heroku.com:myapp-prod.git
fetch = +refs/heads/*:refs/remotes/heroku-prod/*

Deploy the Staging app

The Staging app on Heroku is linked to the heroku-staging remote. You'll want to push the code from your staging branch to the heroku-staging/master branch, as show in the first schema :

[master]$ git push heroku-staging staging:master
...
To git@heroku.com:myapp-staging.git
* [new branch]      staging -> master
[master]$

staging:master means that you push your staging local branch to the master remote branch. Here, the remote branch is heroku-staging, which is linked to the staging app.

Migrate the staging db :

[master]$ git checkout staging
Switched to branch "staging"
[staging]$ heroku rake db:migrate --app myapp-staging

Deploy prod app

[staging]$ git checkout master
Switched to branch "master"
[master]$ git push heroku-prod master
...
To git@heroku.com:myapp-prod.git
* [new branch]      master -> master
[master]$

Migrate the prod db :

[master]$ heroku rake db:migrate --app myapp-prod

Make some changes in staging and deploy them

First, switch to the staging branch, and make your changes.

[master]$ git checkout staging
Switched to branch "staging"
[staging]$ make your change
[staging*]$ git commit -a -m "test staging modification"

Then push your modifications to the staging branch on Github :

[staging]$ git push origin staging

Finaly, deploy your modifications to the heroku staging app

[staging]$ git push heroku-staging staging:master

Make some changes in production and deploy them

Switch to the master branch, and make your changes.

[staging]$ git checkout master
Switched to branch "master"
[master]$ make your change
[master*]$ git commit -a -m "test production modification"

Then push your modifications to the master branch on Github :

[master]$ git push origin master

Finaly, deploy your modifications to the heroku prod app

[master]$ git push heroku-prod master

Feel free to comment this solution and to share your tips here.