Function(al) Webhooks with Fn
This post demonstrates how to develop an Fn function that is triggered by a GitHub webhook and posts new release details to Twitter. It covers the general concept, code as well as configuration and deployment of your function.
Once configured and deployed, the serverless function will be invoked in response to a new release in a GitHub repository (thanks to the webhook) and post its details (name, version and URL) to Twitter.
If everything works out well, you should have a tweet similar to this one
Overview
“… user-defined HTTP callbacks… usually triggered by some event…the source site makes an HTTP request to the URL configured for the webhook … ”
As mentioned above, the goal is to send out a tweet in response to a new release in a GitHub repository. GitHub provides webhooks for such use cases using which you can tap into it’s events in (near) real-time — these events include actions like pull requests submitted or edited, a new release published to a repository etc.
In the context of GitHub,
“Webhooks allow you to build or set up GitHub Apps which subscribe to certain events on GitHub.com. When one of those events is triggered, we’ll send a HTTP POST payload to the webhook’s configured URL…”
Pay attention to “we’ll send a HTTP POST payload to the webhook’s configured URL…” — this is where our serverless Fn function kicks in! In this case, it acts as the glue between GitHub and Twitter. It implements the piece of logic which enables the flow where,
- a developer pushes a new release to GitHub
- GitHub triggers the Webhook which points to the HTTP endpoint of a Fn function…
- … which in turn takes care of validating the event before posting the release details to Twitter.
Here is a simple diagram to depict the end-to-end integration.
It’s time to dive into the nitty gritty !
Show me the codez…
The source code for the function along other instructions are available on GitHub
Let’s walk through the code bit by bit — it’s a simple Go function.
ThewebhookHandler
function is the entrypoint for our function and it starts by checking the GitHub event type
- get the FDK context and
- read the X-GitHub-Event header to extract the event type — we’re only interested in the release event in this case.
Then comes the part where it’s ensured that the event was indeed sent (i.e. our function was invoked) by GitHub — we make use of the HMAC hex digest sent via the X-Hub-Signature header. A common (user defined) webhook Secret
(in GitHub) is used as the HMAC
key — this is provided as a part of the function configuration (github_webhook_secret
) and extracted at runtime using fnCtx.Config()[“github_webhook_secret”]
(it’s a map).
ThematchSignature
function
- computes the expected
HMAC
from the payload - and compares it with the HMAC (from GitHub) using
hmac.Equal
.
The function returns with an error if the signatures don’t match.
Finally,
- the GitHub event payload is converted into a simple
newReleaseNotification
Go struct - and the
tweet
function posts the details to Twitter using the anaconda client library.
Function func.yaml
The code section cannot be wrapped up without mentioning func.yaml
which contains the function metadata.
schema_version: 20180708
name: github-release-handler
version: 0.0.1
runtime: go
entrypoint: ./func
format: http-stream
triggers:
- name: handle-release
type: http
source: /handle-release
Notice the triggers
section — it is an array of trigger entities that specific triggers for the function. In this case, we have a single HTTP trigger which provides an endpoint to access the function.
Configuration and deployment
The Deploy the function section in the GitHub repo has the complete details, so here is the TL;DR of what needs to be done to get this up and running.
In terms of pre-requisites, you need to setup the following :
- Twitter account
- Twitter app
- GitHub account
Deploying the function is as simple as executing a few Fn CLI commands :
fn start
— to start your Fn server
Just make sure you have the Fn server installed on a machine which can be accessed from public Internet, or use ngrok on your local machine to set up a secure tunnel to your localhost.
fn create app
— to create the app along with the Twitter credentials and GitHub webhook secret passed in as configuration and- and
fn deploy
.
Once the function is deployed, just setup a GitHub webhook to point to it — it’s just a REST call away. The config.url
(highlighted below) attribute should point to the HTTP endpoint of the function e.g. http://localhost:8080/t/fn-webhook-app/handle-release
.
curl -X POST -u <github_user_id>:<your_github_token_or_password> \
https://api.github.com/repos/<github_user>/<github_repo>/hooks \
-H 'content-type: application/json' \
-d '{
"name": "web",
"active": true,
"events": [
"release"
],
"config": {
"url": "<fn_function_endpoint>",
"secret": "<your_github_webhook_secret>",
"content_type": "json",
"insecure_ssl": 1
}
}'
Please refer to Configure GitHub webhook section in the GitHub repo for details.
Test drive
Again, the Test section in the GitHub repo has details, so I am going to keep this concise.
- Start by creating a release in your GitHub repository — you can use the GitHub UI or simple REST call.
curl -X POST -u <github_user_id>:<your_github_token_or_password> \
https://api.github.com/repos/<github_user>/<github_repo>/releases \
-H 'content-type: application/json' \
-d '{
"tag_name": "v1.0.0",
"target_commitish": "master",
"name": "v1.0.0",
"body": "Description of the release",
"draft": false,
"prerelease": false
}'
- if all is well, your function will be triggered and the release details should be posted to Twitter — so please keep an eye out for that!
- … and you can also check the Recent Deliveries in GitHub Webhooks (UI) for further details (it is helpful even in case you run into issues).
The journey from GitHub to Twitter is simple — thanks to a nice and simple Fn function! Although this example is configured to respond to GitHub releases, the same can be extended/applied to other events such as pull requests, commits etc.
That’s it for now!
Don’t forget to …
- … check out fnproject on Github
- and join us on Slack !
Cheers!