It’s pretty simple to get information from your Github PRs using webhooks. Let’s say you want a form of changelog and the titles of your PR are good enough. Github will send you information about the PR which you can then pick apart to do what you need with it.
Imagine a basic rails app that stores changelog entries. When a PR is made, we want Github to send the PR info to the app. We’ll then store it as an entry.
When an event occurs, POST some relevant data to a specified URL. You can set one up for a repo under settings. ‘Webhooks & services’ is on the left. We’re interested in the pull request event. But first…
We need a URL for the webhook to send data to. Ngrok is a neat little tool that can allow something you’re running on local to be a public URL.
This way we can avoid having to deploy anything while testing out the webhook. Running
/.ngrok http 3000 should give something like so
Another advantage is the ability to replay requests by the interface at
That way, we don’t need to keep creating/changing PRs to trigger a request.
Plugging it in
Now we can plug that into the webhooks setup with a minor alteration.
We want the rails app to create an entry, the webhook is a POST, so we’ll send it to the CREATE path
It still won’t quite work. Rails
protect_from_forgery is checking for csrf tokens for the create action - the webhook POST request didn’t come from a page in our rails app so it won’t have the token.
Easiest for now is to skip the before action only for
Making it useful
Everything is set up now. Creating a PR or adding a label to one, sends a bunch of PR info to the route we specified. At the moment we’re creating an entry on every change. If we only wanted PRs merged to master, we can pull out the information like so:
# Merged && closed == merged PR params[:pull_request][:merged] && params[:pull_request][:state] == 'closed' params[:pull_request][:base][:ref] == branch
Keeping it secure
We don’t want to create data from any old request that has the right format. We should check that it comes from Github. We need to:
- Add a secret token in the webhook settings e.g.
- Add an environment variable with the same value
ENV['SECRET_TOKEN']. For development, I like the approach outlined here.
- Add a similar like the securing doc suggests:
def verify_signature payload_body = request.body.read signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body) return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE']) end
The final file would look something like this.