Webhooks for the GitHub Repository


Simple Server-to-Server communication, part 2 Webhooks for the GitHub Repository

For quite a few developers, GitHub is the center of their outstaffing work – and if something happens there in the repo, you also want to be informed about it. With webhooks and a Sinatra server, every repo activity can be evaluated in real time.

Companies on the topic

A GitHub webhook is easy to implement with ngrok and Sinatra.A GitHub webhook is easy to implement with ngrok and Sinatra.

(Picture: Long)

Today, there are plenty of ways to connect tools with each other or to be notified about events on any platform in almost any manner – all advantages and disadvantages to me. If you work with GitHub and are completely limited to git and text editors for local development, webhooks are a good option.

The nice thing about it: you can also set it up wonderfully easy at home or simply ad-hoc, if you want to play with webhooks and automatic notifications at short notice. A new pull request in repo? If you simply want to receive a mail or Telegram message, you could-as shown in the first part of the webhooks – simply use the service If – This-Then-That: IF a pull request appears in the repo, THEN send a message to ABC.

But maybe you just want to be notified locally in the terminal. Or otherwise process the resulting data. Then you have to offer yourself a webhook through which GitHub can report new events – which fortunately dominates the platform. In the following, we show you how to configure webhooks on GitHub and how to receive and evaluate requests locally.

If you have a GitHub account, you can replay it directly, because locally you only need a Linux and the two tools ngrok and Sinatra, both of which you can easily install via package manager. You also do not have to adjust port forwarding or firewall rules separately, ngrok provides the connection to the outside world.

Setting up GitHub notifications

The work, or rather dataflow, runs as follows later: something happens in your GitHub repository-for example, a simple commit. GitHub then throws in the stored webhook endpoint URL and sends the repo data with it. The webhook endpoint URL is provided by the ngrok tunneling web service, which is responsible for exposing the server.

Sinatra then waits for HTTP POST queries behind the exposed port. In the simplest case, the Sinatra app can consist of just six lines of Ruby to output a message and the incoming data. Even if GitHub likes to explain it the other way around, it’s best to start with Sinatra and ngrok.

The reason for this is that in the GitHub webhook configuration, you need to specify a valid endpoint URL, which is just provided by ngrok. First, install Sinatra [http://sinatrarb.com/], under Ubuntu this works without further preparation via the Ruby package manager, for this a simple “gem install sinatra”is sufficient.

You can download Ngrok from the website and then simply unpack the binary “ngrok”. You can also start ngrok directly by specifying a protocol and port: “ngrok http 4567”. You will then see how ngrok works-based on the message:

Forwarding         http://141e2e1c7849.ngrok.io -> http://localhost:4567

The webhook configuration in GitHub including ngrok URL.The webhook configuration in GitHub including ngrok URL.

(Image: Lang / GitHub)

ngrok provides a personal ngrok.io address that forwards to your local Server and the Port specified, a Tunnel. The free ngrok service is sufficient for tests and ad hoc installations, there are paid plans for permanent use. Caution: When you restart ngrok, a new URL is generated – and since it serves as a webhook endpoint, the configured GitHub webhook would go blank.

Now that you have the Endpoint URL, you can log into your GitHub repo and access its settings there. You will find here in the navigation the point Webhooks. Enter the following data :

  • Payload URL: the ngrok URL plus / payload
  • Content type: Application / json
  • Trigger: Send me everything

In this configuration, GitHub will trigger the Webhook for each activity in the Repo. Alternatively, you could of course only have certain events reported. For example, changes in the wiki, new forks, new deploy keys or, of course, new pull requests are also available as triggers.

All these webhook events are addressed differently and deliver equally different payloads – an overview can also be found at GitHub. Now the server is accessible thanks to ngrok and GitHub fires webhooks – still missing the interpreter, so a Sinatra app.

How simple web applications can be created with Sinatra is probably best shown by an example. Create a script, such as “webhooktest.rb”, and insert the following content:

require 'sinatra'
require 'json'
post '/payload' do
push = JSON.parse(request.body.read)
puts "Hier kommen die Daten: #{push.inspect}"

ngrok, the Sinatra code and the running Sinatra server-actually simple.ngrok, the Sinatra code and the running Sinatra server-actually simple.

(Picture: Long)

The structure of the Ruby script is very simple: under” post “the local webhook endpoint URL is specified-so if something about “http://141e2e1c7849.ngrok.io/payload”, the following ” do ” statement is executed. In this case, this means that after “Here comes the data:” the repo data follows, for example the name and address of a committer. The “push” function only stores the JSON data in an array.

GitHub logs all webhook actions.GitHub logs all webhook actions.

(Image: Lang / GitHub)

For testing, you can now simply modify a readme file in the repo, add a file or perform any other action – you should in any case have a corresponding message in the terminal of the running Sinatra server “webhooktest.rb“ see. You can also see the webhook actions in GitHub itself: Under “Settings/Webhooks” all log entries appear at the end of the page, including status and payload.


In practice, you should now use an authentication – theoretically quite simple, practically it will come down to a little development with Ruby, the JSON data and tools such as Base64 and OpenSSL. GitHub itself provides a simple variant in the official documentation, which however does not work as well as other sections in it. Even various Sinatra apps have not been able to solve this problem during the test.

As of now, the comparison of the passed key on our test system works only partially – somewhere there are problems with reading and processing the key. However, since the verification depends on your system, this is no longer wild – you won’t be able to avoid your own adjustments anyway.

Step 1 for protection is simple: In the Webhook configuration on GitHub, simply place any key in the “Secret” field.

Step 2 then takes place in the Sinatra app. GitHub uses the stored key to create a signature and passes it along with payload over the webhook endpoint URL. Your Sinatra app simply needs to get the key, process it in the same way as GitHub, and compare your own with the result obtained via webhook. The (minimally customized) original GitHub code provides this as follows:

post '/payload' do
payload_body = request.body.read
push = JSON.parse(params[:payload])
"Schlüssel akzeptiert, es kommt was an!"
def verify_signature(payload_body)
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'])

Important: You have previously saved the key in an environment variable: “export SECRET_TOKEN=mykey” is sufficient for this. For testing, you could also hard code the key into the Ruby script.

The real magic is in the function “verify_signatures”, which reproduces the signature here (→ signature) and with the method ” Rack::Utils.secure_compare ” with the value supplied by GitHub (→ request.env[‘HTTP_X_HUB_SIGNATURE’]) compare.

The error message from the sample code remains on the track in the local test scenario, but at least: if the key matches, the script says “Key accepted, something arrives!”off-otherwise it simply remains silent.

Ultimately, however, Sinatra with its mini-servers/apps is the real highlight: you can easily receive webhooks, whether from GitHub or any other source, and process the results completely freely via Ruby. And since authentication ultimately consists only of a simple comparison of two cryptographically created strings, this is not a hurdle either.

At the end of the day, one thing above all should get stuck: webhooks are extremely flexible, fairly simple and they enable whole chains of actions – after all, webhooks can also trigger webhooks that trigger webhooks that …


Ready to see us in action:

More To Explore

Enable registration in settings - general
Have any project in mind?

Contact us: