<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: travis</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/travis.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2018-06-01T17:21:50+00:00</updated><author><name>Simon Willison</name></author><entry><title>Continuous Integration with Travis CI - ZEIT Documentation</title><link href="https://simonwillison.net/2018/Jun/1/zeit-with-travis-ci/#atom-tag" rel="alternate"/><published>2018-06-01T17:21:50+00:00</published><updated>2018-06-01T17:21:50+00:00</updated><id>https://simonwillison.net/2018/Jun/1/zeit-with-travis-ci/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://zeit.co/docs/continuous-integration/travis?utm_source=twitter&amp;amp;utm_medium=social&amp;amp;utm_campaign=travis_ci_guide"&gt;Continuous Integration with Travis CI - ZEIT Documentation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
One of the neat things about Zeit Now is that since deployments are unlimited and are automatically assigned a unique URL you can set up a continuous integration system like Travis to deploy a brand new copy of every commit or every pull request. This documentation also shows how to have commits to master automatically aliased to a known URL. I have quite a few Datasette projects that are deployed automatically to Now by Travis and the pattern seems to be working great so far.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/continuous-deployment"&gt;continuous-deployment&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/continuous-integration"&gt;continuous-integration&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zeit-now"&gt;zeit-now&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/travis"&gt;travis&lt;/a&gt;&lt;/p&gt;



</summary><category term="continuous-deployment"/><category term="continuous-integration"/><category term="zeit-now"/><category term="travis"/></entry><entry><title>csvs-to-sqlite: Automatically deploy tags as PyPI releases</title><link href="https://simonwillison.net/2017/Nov/19/pypi/#atom-tag" rel="alternate"/><published>2017-11-19T06:35:45+00:00</published><updated>2017-11-19T06:35:45+00:00</updated><id>https://simonwillison.net/2017/Nov/19/pypi/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/csvs-to-sqlite/commit/9aa4ffa130197c6e12bec9373dac99c51bfefd2b"&gt;csvs-to-sqlite: Automatically deploy tags as PyPI releases&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I learned how to set up Travis CI to automatically deploy new package versions to PyPI when I push a version tag to GitHub (and the tests pass).


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/travis"&gt;travis&lt;/a&gt;&lt;/p&gt;



</summary><category term="pypi"/><category term="travis"/></entry><entry><title>Try hosting on PyPy by simonw</title><link href="https://simonwillison.net/2017/Nov/5/try-hosting-on-pypy/#atom-tag" rel="alternate"/><published>2017-11-05T19:17:29+00:00</published><updated>2017-11-05T19:17:29+00:00</updated><id>https://simonwillison.net/2017/Nov/5/try-hosting-on-pypy/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/simonwillisonblog/pull/11"&gt;Try hosting on PyPy by simonw&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I had a go at hosting my blog on PyPy. Thanks to the combination of Travis CI, Sentry and Heroku it was pretty easy to give it a go—I had to swap psycopg2  for psycopg2cffi and switch to the currently undocumented pypy3-5.8.0 Heroku runtime (pypy3-5.5.0 is only compatible with Python 3.3, which Django 2.0 does not support). I ran it in production for a few minutes and didn’t get any Sentry errors but did end up using more Heroku dyno memory than I’m comfortable with—see the graph I posted in a comment. I’m going to stick with CPython 3.6 for the moment. Amusingly I did almost all of the work on this on my phone! Travis CI means it’s easy to create and test a branch through GitHub’s web UI, and deploying a tested branch to Heroku is then just a button click.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/pypy"&gt;pypy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/heroku"&gt;heroku&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/travis"&gt;travis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sentry"&gt;sentry&lt;/a&gt;&lt;/p&gt;



</summary><category term="pypy"/><category term="python"/><category term="heroku"/><category term="travis"/><category term="sentry"/></entry><entry><title>Porting my blog to Python 3</title><link href="https://simonwillison.net/2017/Oct/21/python3/#atom-tag" rel="alternate"/><published>2017-10-21T22:22:40+00:00</published><updated>2017-10-21T22:22:40+00:00</updated><id>https://simonwillison.net/2017/Oct/21/python3/#atom-tag</id><summary type="html">
    &lt;p&gt;This blog is now running on Python 3! Admittedly this is nearly nine years after &lt;a href="https://www.python.org/download/releases/3.0/"&gt;the first release of Python 3.0&lt;/a&gt;, but it’s the first Python 3 project I’ve deployed myself so I’m pretty excited about it.&lt;/p&gt;
&lt;p&gt;Library authors like to use &lt;a href="https://pypi.python.org/pypi/six"&gt;six&lt;/a&gt; to allow them to write code that supports both Python 2 and Python 3 at the same time… but my blog isn’t a library, so I used the &lt;a href="https://docs.python.org/3/library/2to3.html"&gt;2to3 conversion tool&lt;/a&gt; that ships with Python instead.&lt;/p&gt;
&lt;p&gt;And… it worked pretty well! I ran the following command from my project’s root directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2to3 -w -n blog/ config/ redirects/ feedstats/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-w&lt;/code&gt; option causes the files to be over-written in place. Since everything is already in git, there was no reason to have 2to3 show my a diff without applying it. Likewise, the &lt;code&gt;-n&lt;/code&gt; option tells 2to3 not to bother saving backups of the files it modifies.&lt;/p&gt;
&lt;p&gt;Here’s &lt;a href="https://github.com/simonw/simonwillisonblog/commit/615efeba55c0c32a8147bda49e207a7a52ddb674"&gt;the initial commit&lt;/a&gt; containing mostly the 2to3 changes.&lt;/p&gt;
&lt;p&gt;Next step: run the tests! My test suite may be very thin, but it does at least check that the app can run its migrations, start up and serve a few basic pages without errors. One of my migrations was failing due to rogue bytestrings but that was &lt;a href="https://github.com/simonw/simonwillisonblog/commit/f00224e7375098bb500b56b35c6f40dbc4955abc#diff-15a9a47c9ee1f93f3a07a4dbe0cf4214"&gt;an easy fix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this point I started to lean heavily on my &lt;a href="https://simonwillison.net/2017/Oct/17/free-continuous-deployment/"&gt;continuous integration setup built on Travis CI&lt;/a&gt;. All of my Python 3 work took place &lt;a href="https://github.com/simonw/simonwillisonblog/tree/python3"&gt;in a branch&lt;/a&gt;, and all it took was a &lt;a href="https://github.com/simonw/simonwillisonblog/commit/f00224e7375098bb500b56b35c6f40dbc4955abc#diff-354f30a63fb0907d4ad57269548329e3"&gt;one line change to my .travis.yml&lt;/a&gt; for Travis to start running the tests for that branch using Python 3.&lt;/p&gt;
&lt;p&gt;With the basic tests working, I made my first deploy to my Heroku staging instance - after first &lt;a href="https://github.com/simonw/simonwillisonblog/commit/54b31e98e35031afbbf8c18f3c2446a0af8b5c65"&gt;modifying my Heroku runtime.txt&lt;/a&gt; to tell it to use Python 3.6.2. My staging environment allowed me to sanity check that everything would work OK when deployed to Heroku.&lt;/p&gt;
&lt;p&gt;At this point I got a bit lazy. The responsible thing to do would have been extensive manual testing plus systematic unit test coverage of core functionality. My blog is hardly a critical piece of infrastructure though, so I went with the faster option: put it all live and &lt;a href="https://simonwillison.net/2017/Oct/17/free-continuous-deployment/#Step_4_Monitor_errors_with_Sentry_75"&gt;use Sentry to see if anything breaks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is where Heroku’s ability to deploy a specific branch came in handy: one click to deploy my python3 branch, keep an eye on Sentry (via push notifications from &lt;a href="https://simonwillison.net/2017/Oct/17/free-continuous-deployment/#Step_5_Hook_it_all_together_with_Slack_97"&gt;my private slack channel&lt;/a&gt;) and then one click to deploy my master branch again for an instant rollback in case of errors. Which I had to do instantly, because it turned out I had stored some data in Django’s cache using Python 2 pickle and was trying to read it back out again using Python 3.&lt;/p&gt;
&lt;p&gt;I fixed that by &lt;a href="https://github.com/simonw/simonwillisonblog/commit/41f7a112721ec5772ad189c4293da081291a604a"&gt;bumping my cache VERSION setting&lt;/a&gt; and deployed again. This deploy lasted a few minute longer before Sentry started to fill up with encoding errors and I rolled it back again.&lt;/p&gt;
&lt;p&gt;The single biggest difference between Python 2 and Python 3 is &lt;a href="https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit"&gt;how strings are handled&lt;/a&gt;. Python 3 strings are unicode sequences. Learning to live in a world where strings are all unicode and byte strings are the rare, deliberate exceptions takes some getting used to.&lt;/p&gt;
&lt;p&gt;The key challenge for my blog actually came from &lt;a href="https://github.com/simonw/simonwillisonblog/blob/45d7acd56af475119d2738e736d9b4cb19a9e8eb/blog/templatetags/entry_tags.py"&gt;my custom markup handling template tags&lt;/a&gt;. 15 years ago &lt;a href="https://simonwillison.net/2002/Jun/16/myFirstXhtmlMindBomb/"&gt;I made the decision&lt;/a&gt; to &lt;a href="https://simonwillison.net/2003/Jan/6/xhtmlIsJustFine/"&gt;store all of my blog entries&lt;/a&gt; as valid XHTML fragments. This meant I could use XML processors - back then in PHP, today &lt;a href="https://docs.python.org/3/library/xml.etree.elementtree.html"&gt;Python’s ElementTree&lt;/a&gt; - to perform various transformations on my content.&lt;/p&gt;
&lt;p&gt;ElementTree in Python 2 can only consume bytestrings. In Python 3 it expects unicode strings. Cleaning this up took a while, eventually inspiring me to &lt;a href="https://github.com/simonw/simonwillisonblog/commit/7295cddd1a6ab2c7bc6fcf3da410ab6ea0954791"&gt;refactor my custom template tags completely&lt;/a&gt;. In the process I realized that my blog templates were mostly written back before Django’s template language implemented autoescape (&lt;a href="https://simonwillison.net/2008/Jul/22/alpha/"&gt;in Django 1.0&lt;/a&gt;), so my code was littered with unnecessary &lt;code&gt;|escape&lt;/code&gt; and &lt;code&gt;|safe&lt;/code&gt; filters. Those are all gone now.&lt;/p&gt;
&lt;p&gt;Sentry lets you mark an exception as “resolved” when you think you’ve fixed it - if it occurs again after that it will be re-reported to your Slack channel and added back to the Sentry list of unresolved issues. Once Sentry was clear (especially given Googlebot had crawled my older pages) I could be pretty confident there were no more critical 500-causing errors.&lt;/p&gt;
&lt;p&gt;That left logic errors, of which only one has cropped up so far: the “zero years ago” bug. Entries on my homepage include a relative date representation, e.g. “three days ago”. Python 3 &lt;a href="https://www.python.org/dev/peps/pep-0238/"&gt;changed how the divison operator works on integers&lt;/a&gt; - &lt;code&gt;3 / 2 == 1.5&lt;/code&gt; where in Python 2 it gets truncated to &lt;code&gt;1&lt;/code&gt;. As a result, every entry on my homepage showed “zero years ago”. Thankfully this was &lt;a href="https://github.com/simonw/simonwillisonblog/commit/d6e6eeb93ac02aa33c59b151f2a20e26d41f18b1"&gt;a one-line fix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All in all this process was much less painful than I expected. It took me longer to write this blog entry than it did to actually make the conversion (thanks to 2to3 doing most of the tedious work), and the combination of Travis CI, Sentry and Heroku allowed me to ship aggressively with the knowledge that I could promptly identify and resolve any issues that came up.&lt;/p&gt;
&lt;p&gt;Next upgrade: &lt;a href="https://www.djangoproject.com/weblog/2017/oct/16/django-20-beta-1-released/"&gt;Django 2.0&lt;/a&gt;!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/continuous-integration"&gt;continuous-integration&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python3"&gt;python3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/heroku"&gt;heroku&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/travis"&gt;travis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sentry"&gt;sentry&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="continuous-integration"/><category term="python"/><category term="python3"/><category term="heroku"/><category term="travis"/><category term="sentry"/></entry><entry><title>How to set up world-class continuous deployment using free hosted tools</title><link href="https://simonwillison.net/2017/Oct/17/free-continuous-deployment/#atom-tag" rel="alternate"/><published>2017-10-17T13:32:49+00:00</published><updated>2017-10-17T13:32:49+00:00</updated><id>https://simonwillison.net/2017/Oct/17/free-continuous-deployment/#atom-tag</id><summary type="html">
    &lt;p&gt;I’m going to describe a way to put together a world-class continuous deployment infrastructure for your side-project without spending any money.&lt;/p&gt;
&lt;p&gt;With &lt;a href="https://puppet.com/blog/continuous-delivery-vs-continuous-deployment-what-s-diff"&gt;continuous deployment&lt;/a&gt; every code commit is tested against an automated test suite. If the tests pass it gets deployed directly to the production environment! How’s that for an incentive to write comprehensive tests?&lt;/p&gt;
&lt;p&gt;Each of the tools I’m using offers a free tier which is easily enough to handle most side-projects. And once you outgrow those free plans, you can solve those limitations in exchange for money!&lt;/p&gt;
&lt;p&gt;Here’s the magic combination:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://travis-ci.org/"&gt;Travis CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://heroku.com/"&gt;Heroku&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sentry.io/"&gt;Sentry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a id="Step_one_Publish_some_code_to_GitHub_with_some_tests_16"&gt;&lt;/a&gt;Step one: Publish some code to GitHub with some tests&lt;/h2&gt;
&lt;p&gt;I’ll be using the &lt;a href="https://github.com/simonw/simonwillisonblog"&gt;code for my blog&lt;/a&gt; as an example. It’s a classic Django application, with a small (OK, tiny) suite of unit tests. The tests are run using the standard Django &lt;code&gt;./manage.py test&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Writing a Django application with tests is outside the scope of this article. Thankfully the official Django tutorial &lt;a href="https://docs.djangoproject.com/en/1.11/intro/tutorial05/"&gt;covers testing in some detail&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;a id="Step_two_Hook_up_Travis_CI_22"&gt;&lt;/a&gt;Step two: Hook up Travis CI&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://travis-ci.org/"&gt;Travis CI&lt;/a&gt; is an outstanding hosted platform for continuous integration. Given a small configuration file it can check out code from GitHub, set up an isolated test environment (including hefty dependencies like a PostgreSQL database server, Elasticsearch, Redis etc), run your test suite and report the resulting pass/fail grade back to GitHub.&lt;/p&gt;
&lt;p&gt;It’s free for publicly hosted GitHub projects. If you want to test code in a private repository you’ll have to pay them some money.&lt;/p&gt;
&lt;p&gt;Here’s &lt;a href="https://github.com/simonw/simonwillisonblog/blob/a5c2d2549f26dd2d75cbf863c8b36d617092c2a1/.travis.yml"&gt;my .travis.yml configuration file&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;language: python

python:
  - 2.7

services: postgresql

addons:
  postgresql: &amp;quot;9.6&amp;quot;

install:
  - pip install -r requirements.txt

before_script:
  - psql -c &amp;quot;CREATE DATABASE travisci;&amp;quot; -U postgres
  - python manage.py migrate --noinput
  - python manage.py collectstatic

script:
  - python manage.py test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s the resulting &lt;a href="https://travis-ci.org/simonw/simonwillisonblog"&gt;Travis CI dashboard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The integration of Travis with GitHub runs &lt;em&gt;deep&lt;/em&gt;. Once you’ve set up Travis, it will automatically test every push to every branch - driven by GitHub webhooks, so test runs are set off almost instantly. Travis will then report the test results back to GitHub, where they’ll show up in a bunch of different places -  including these pleasing green ticks on &lt;a href="https://github.com/simonw/simonwillisonblog/branches"&gt;the branches page&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 100%" src="https://static.simonwillison.net/static/2017/github-branches-with-ci-small.png" alt="GitHub branches page showing CI results" /&gt;&lt;/p&gt;
&lt;p&gt;Travis will also run tests against any &lt;a href="https://github.com/simonw/simonwillisonblog/pull/3"&gt;open pull requests&lt;/a&gt;. This is a great incentive to build new features in a pull request even if you aren’t using them for code review:&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 100%" src="https://static.simonwillison.net/static/2017/github-pull-request-with-ci-small.png" alt="GitHub pull request showing CI results" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://circleci.com/"&gt;Circle CI&lt;/a&gt; deserves a mention as an alternative to Travis. The two are close competitors and offer very similar feature sets, and Circle CI's free plan allows up to 1,500 build minutes of private repositories per month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 25th July 2020&lt;/strong&gt;: I've started using GitHub Actions for most of my projects now - see my &lt;a href="https://simonwillison.net/tags/githubactions/"&gt;githubactions&lt;/a&gt; tag.&lt;/p&gt;

&lt;h2&gt;&lt;a id="Step_3_Deploy_to_Heroku_and_turn_on_continuous_deployment_61"&gt;&lt;/a&gt;Step 3: Deploy to Heroku and turn on continuous deployment&lt;/h2&gt;
&lt;p&gt;I’m a big fan of &lt;a href="https://heroku.com/"&gt;Heroku&lt;/a&gt; for side projects, because it means not having to worry about ongoing server-maintenance. I’ve lost several side-projects to &lt;a href="https://blog.heroku.com/archives/2011/6/28/the_new_heroku_4_erosion_resistance_explicit_contracts/"&gt;entropy and software erosion&lt;/a&gt; - getting an initial VPS set up may be pretty simple, but a year later security patches need applying and the OS needs upgrading and the log files have filled up the disk and you’ve forgotten how you set everything up in the first place…&lt;/p&gt;
&lt;p&gt;It turns out Heroku has basic support for continuous deployment baked in, and it’s trivially easy to set up. You can tell Heroku to deploy on every commit to GitHub, and then if you’ve attached a CI service like Travis that reports build health back you can check the box for “Wait for CI to pass before deploy”:&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 100%" src="https://static.simonwillison.net/static/2017/heroku-deploy-settings-small.png" alt="Heroku deployment settings for continuous deployment" /&gt;&lt;/p&gt;
&lt;p&gt;Since small dynos on Heroku are free, you can even set up a separate Heroku app as a staging environment. I started my continuous integration adventure just deploying automatically to my staging instance, then switched over to deploying to production once I gained some confidence in how it all fitted together.&lt;/p&gt;
&lt;p&gt;If you’re using continuous deployment with Heroku and Django, it’s a good idea to set up Heroku to automatically run your migrations for every deploy - otherwise you might merge a pull request with a model change and forget to run the migrations before the deploy goes out. You can do that using Heroku’s &lt;a href="https://devcenter.heroku.com/articles/release-phase"&gt;release phase&lt;/a&gt; feature, by adding the line &lt;code&gt;release: python manage.py migrate --noinput&lt;/code&gt; to your Heroku &lt;code&gt;Procfile&lt;/code&gt; (&lt;a href="https://github.com/simonw/simonwillisonblog/blob/81f7e2ba19b84f572e8a546bcc28bbfb1e211eb6/Procfile"&gt;here’s mine&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Once you go beyond Heroku’s free tier things get much more powerful: &lt;a href="https://www.heroku.com/flow"&gt;Heroku Flow&lt;/a&gt; combines pipelines, review apps and their own CI solution to provide a comprehensive solution for much larger teams.&lt;/p&gt;
&lt;h2&gt;&lt;a id="Step_4_Monitor_errors_with_Sentry_75"&gt;&lt;/a&gt;Step 4: Monitor errors with Sentry&lt;/h2&gt;
&lt;p&gt;If you’re going to move fast and break things, you need to know when things have broken. &lt;a href="https://sentry.io/"&gt;Sentry&lt;/a&gt; is a fantastic tool for collecting exceptions, aggregating them and spotting when something new crops up. It’s open source so you can host it yourself, but they also offer a robust hosted version with a free plan that can track up to 10,000 errors a month.&lt;/p&gt;
&lt;p&gt;My favourite feature of Sentry is that it gives each exception it sees a “signature” based on a MD5 hash of its traceback. This means it can tell if errors are the same underlying issue or something different, and can hence de-dupe them and only alert you the first time it spots an error it has not seen before.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 100%" src="https://static.simonwillison.net/static/2017/sentry-small.png" alt="Notifications from Travis CI and GitHub in Slack" /&gt;&lt;/p&gt;
&lt;p&gt;Sentry has integrations for most modern languages, but it’s particularly easy to use with Django. Just install &lt;a href="https://pypi.python.org/pypi/raven"&gt;raven&lt;/a&gt; and add few extra lines to your &lt;a href="http://settings.py"&gt;settings.py&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SENTRY_DSN = os.environ.get('SENTRY_DSN')
if SENTRY_DSN:
    INSTALLED_APPS += (
        'raven.contrib.django.raven_compat',
    )
    RAVEN_CONFIG = {
        'dsn': SENTRY_DSN,
        'release': os.environ.get('HEROKU_SLUG_COMMIT', ''),
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I’m using the Heroku pattern of &lt;a href="https://devcenter.heroku.com/articles/config-vars"&gt;keeping configuration in environment variables&lt;/a&gt;. &lt;code&gt;SENTRY_DSN&lt;/code&gt; is provided by Sentry when you create your project there - you just have to add it as a Heroku config variable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;HEROKU_SLUG_COMMIT&lt;/code&gt; line causes the currently deployed git commit hash to be fed to Sentry so that it knows what version of your code was running when it reports an error. To enable that variable, you’ll need to &lt;a href="https://devcenter.heroku.com/articles/dyno-metadata"&gt;enable Dyno Metadata&lt;/a&gt; by running &lt;code&gt;heroku labs:enable runtime-dyno-metadata&lt;/code&gt; against your application.&lt;/p&gt;
&lt;h2&gt;&lt;a id="Step_5_Hook_it_all_together_with_Slack_97"&gt;&lt;/a&gt;Step 5: Hook it all together with Slack&lt;/h2&gt;
&lt;p&gt;Would you like a push notification to your phone every time your site gets code committed / the tests pass or fail / a deploy goes out / a new error is detected? All of the above tools can report such things to &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt;, and Slack’s free plan is easily enough to collect all of these notifications and push them to your phone via the free Slack &lt;a href="https://slack.com/downloads/ios"&gt;iOS&lt;/a&gt; or &lt;a href="https://slack.com/downloads/android"&gt;Android&lt;/a&gt; apps.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 100%" src="https://static.simonwillison.net/static/2017/slack-github-ci-small.png" alt="Notifications from Travis CI and GitHub in Slack" /&gt;&lt;/p&gt;
&lt;p&gt;Here are instructions for setting up Slack with &lt;a href="https://get.slack.help/hc/en-us/articles/232289568-Use-GitHub-with-Slack"&gt;GitHub&lt;/a&gt;, &lt;a href="https://docs.travis-ci.com/user/notifications/#Configuring-slack-notifications"&gt;Travis CI&lt;/a&gt;, &lt;a href="https://slack.com/apps/A0F7VRF7E-heroku"&gt;Heroku&lt;/a&gt; and &lt;a href="https://slack.com/apps/A0F814BEV-sentry"&gt;Sentry&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;a id="Need_more_Pay_for_it_105"&gt;&lt;/a&gt;Need more? Pay for it!&lt;/h2&gt;
&lt;p&gt;Having run much of this kind of infrastructure myself in the past I for one am delighted by the idea of outsourcing it, especially when the hosted options are of such high quality.&lt;/p&gt;
&lt;p&gt;Each of these tools offers a free tier which is generous enough to work great for small side projects. As you start scaling up, you can start paying for them - that’s why they gave you a free tier in the first place.&lt;/p&gt;

&lt;p&gt;Comments or suggestions? Join &lt;a href="https://news.ycombinator.com/item?id=15490935"&gt;this thread on Hacker News&lt;/a&gt;.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/continuous-deployment"&gt;continuous-deployment&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/continuous-integration"&gt;continuous-integration&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/testing"&gt;testing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/heroku"&gt;heroku&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slack"&gt;slack&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/travis"&gt;travis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sentry"&gt;sentry&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="continuous-deployment"/><category term="continuous-integration"/><category term="django"/><category term="github"/><category term="postgresql"/><category term="testing"/><category term="heroku"/><category term="slack"/><category term="travis"/><category term="sentry"/></entry></feed>