Continuous delivery at YunoJuno
BitBucket to Codeship to Heroku
Disclaimer: I'm a Continuous Deployment sceptic. I've always been suspicious of people who automatically deploy code to their live environment, and still am. Seems risky to me. See this article for more details on the difference between continuous delivery and deployment.
I've blogged before about our deployment tools, which enable us to deploy to our remote Heroku environments with a single command, but this has always been a manual process. Someone (generally me) is responsible for working out when a deployment is due, and kicking it off. However, we've recently 'upgraded' our process, so that branch pushes are deployed automatically. Final deployments from staging to live are still manual, but since we use Heroku's Pipelines feature to promote code from staging to live, this is a very quick process. This blog post is about the tools we use to make it easy for ourselves.
1. Gitflow
(There seems to have been a bit of a backlash against Gitflow recently, with a number of aticles on HN complaining that it's too complex, and that have two permanent branches is overkill. Well, we use it, and it works for us.)
We use Gitflow (loosely), which means that we have two 'permanent' branches - master and dev. Master is considered the only stable branch, and it is never committed to directly - only via merging in dev (for a feature release), or merging in a hotfix branch. Master is the branch that represents the live site. dev is our development / integration branch - it's where everyone's features get merged into. This isn't stable, because of the heavy merge volume, where there's always the chance of regression or oversight, but it is the branch that represents what the next full deployment will look like (once the merge bugs are fixed). This is where we get to see what the site will look like.
In addition to master and dev, everyone works on feature branches for features (which branch off of dev, and back into dev), and hotfix branches for hotfixes (they branch off of master, and are merged back into both master and dev at the same time). As a rough guide, we have 11 feature and 2 hotfix branches open right now.
If a feature branch lasts for more than a day, then the dev branch is merged into it on a daily basis, to ensure that the merge back into dev is as painless as possible.
This gives us a strict branch strategy:
- master -> the site as-is
- dev -> the site as it will be at next full deployment
- feature -> the current state site of a given developer's branch
2. Heroku
We have three shared environments, and an environment per developer:
- LIVE - live data, master branch
- UAT - recent (anonymised) data, master branch, pre-live testing
- DEV - test dataset, dev branch, used for testing
- dev-1/2/3/... feature branches, used to share current state of work
The individual developer environments are there because we used to allow everyone to push to DEV, but then when the team got to a certain size everyone started tripping over each other. By having a personal Heroku environment, everyone can always share their current branch.These individual apps cost us the equivalent of 1/2 hour of a developer's time to host on Heroku for a month - well worth the money.
3. BitBucket
We use BitBucket to host our private repos.
4. Codeship (& tox)
We use Codeship to run our tests, and to manage our deployments. We use tox to run our test suite on Codeship - we have just over 2,100 tests, and they take about 20 minutes to run, so having devs run them locally each time they commit wouldn't work in practice. By offloading this onto Codeship we get a much more consistent view of our tests (and they run whether a developer wants them to or not). Codeship is integrated into BitBucket, and runs on every branch commit.
On completion of a successful test run, the code is deployed:
Deployments
Because of our fixed branch and environment configuration, we can set up fixed deployment routes:
- master -> UAT
- dev -> DEV
- feature/* -> developer-specific environment
(That last deployment is possible through Codeship's custom deployment scripts.)
5. Opbeat
We use Opbeat to monitor our deployments, as it gives us deployment stats and integration with BitBucket to show which commits have been deployed:
Opbeat also manages our error reporting (much like Sentry), and has some useful stats for Django apps (e.g. long running view functions).
6. Codecov
I honestly never thought we'd get this going, but we now have coverage running on every test run, thanks to Codecov:
(As an aside, Codecov has some very nice touches - including 'coverage suggestions' for where to write tests to cover up the barest patches. Definitely worth checking out.)
7. HipChat
Tying all of this together is HipChat. We now have the following services participating in our dev room:
- Trello (card movements, comments, checklist changes)
- BitBucket (commits, pull request updates, comments)
- Codeship (test results)
- Codecov (coverage updates)
- Manual deployments (using heroku-tools)
And Finally
The one piece of this that isn't automated, as referred to right at the beginning of this article, is the final deployment from UAT to LIVE. The UAT environment will always have the latest version of master deployed to it by Codeship, but that doesn't mean it gets deployed immediately to LIVE. We give the site a quick smoke test on UAT to check that nothing has regressed, check with the sales team that they're not mid-demo, the support team that they're not in the middle of serving someone, and then we press the button. This final step uses Heroku's Pipelines feature, and is very fast:
heroku pipeline:promote
will copy your app’s build artifact (i.e. slug) to the downstream app as a new release.
Posted in: heroku