GitHub Webhooks with Flask
On my to-do list for a while I’ve had a goal of automating deployment of my blog. It’s powered by Jekyll, in case you didn’t know, so the source is all hosted on GitHub. Before today, this was my process for writing a new post:
- Make a new
.markdown
file in_drafts
- Push it to
master
because it’s in_drafts
, no biggie. - Write until it feels close to ready.
- Move it to
_posts
. - Run the website locally.
- Tweak it as I go.
- When it’s looking good,
ssh
onto my VPS to pull it down from GitHub - Manually deploy it to a staging site, send some friends and colleagues the staging link for critique and test that the deploy process hasn’t broken somehow.
- When that’s all over, manually deploy it to live.
A better solution would involve less work by me: why should my blog be any harder to publish to than a Wordpress-powered blog?
GitHub provides Webhooks for individual git repositories hosted on their service. The service can be configured so that when your repo is updated, GitHub will send a little HTTP request to the endpoint of your choice on the public Internet. The request contains information about what has changed including the branch, the latest commit, and some secret information to verify it came from GitHub.
So the solution has been clear in my mind for a while: get GitHub to tell my VPS when it needs to pull down the latest changes to my blog and run a deploy script.
How will I make this happen?
Inspiration
We’ve spent a lot of time in my current gig at Digi2al talking about and implementing CI systems in various places, so my mind is in that frame of mind anyway. It’s also been my first big bit of hands-on work with Python in quite a few years and I’ve been enjoying writing it. Simultaneously, Elli has been learning and the minimalistic Python web framework Flask with Code First Girls. So now I have three goals:
- Automate deployment of my blog
- Do a small but challenging Python project outside of work
- Use Flask for something so I can know what Elli is talking about
Nautrally, I decided to write a Flask app to implement my solution.
One evening last week, brain-tired after a long day at the office and
a little bit unwell, I typed mkdir
in Terminal I deliriously asked myself,
“what is this app supposed to do?” and my brain responded stupidly, “the thing.”
And that’s why I created dothething
.
It’s certainly not the most powerful webhooks server in the world, but it fits
my purposes nicely.
How does it work?
uWSGI, the application server, runs the Flask app and creates a unix socket through which to serve requests. All of this is configured in a simple but flexible configuration file. Nginx, which is the web server I already use on my Linode VPS, hooks up HTTP requests to that socket quite seamlessly, on a host that I have specified.
Whenever my GitHub repo is pushed to or updated on either the staging
or master
branches, GitHub’s webhooks send a request to the endpoint I’ve specified.
That endpoint is being served by the Flask app.
The app fetches the latest changes on all branches, does a checkout
on the latest
commit, deploys the blog to the appropriate folder (depending on whether it’s
staging
or master
) and does a checkout master
to wrap it all up. Job’s a goodun.
I learned a few things getting to this point:
- How to
uwsgi
. - How to
nginx
myuwsgi
. - How to add a special
ssh
key just for read-only GitHub access in the form of a Deploy Key - How to Flask.
- How to
Popen
with various different configurations of working directory, environment, path, etc. - How to debug while I
uwsgi
. - How to write and read simple but flexible configuration files for Python apps.
And that’s about it. Not bad for a three-or-four-nights project.
I realise also that I didn’t need to write a post about this tool which probably is only useful for me since it’s massively unflexible compared to some other likely alternatives, but it was a good excuse to test out my new workflow.
Incidentally…
This is my new workflow
- Make a new
.markdown
file in_posts
- Push it to
staging
so that it won’t deploy to danhough.com - Run the website locally.
- Tweak it as I go and keep committing.
- When it’s done, some friends and colleagues the staging link for critique.
- When that’s all over, make a PR for the
staging
branch onmaster
.
Much better, right? I think so. No more having to faff around on my server every time I want to do a new blog post.
Next level
I have yet to configure the sass
part of the build, and there’s no easy install
process for dothething
yet.
While I’m writing a new post or redesigning the site I might also want to create
non-master
and non-staging
branches, but I haven’t decided about that one
yet. They could even generate their own subdomains so that I don’t even need
to push to staging
. That’d be interesting, but maybe overkill.
Anyway, that’s all for now. If you wanna look at the simple little Flask app I made, go and check it out on GitHub. I’m open to suggestions and pull requests!
If it also happens to fit your requirements perfectly, then great: go ahead and
use it! It is at least flexible enough to be run in environments other than
uwsgi
-powered ones.