GitHub and Notion Integration in Python
Teams often use Notion for issue tracking as the tool is very intuitive to use and requires zero setup. Combined with issues or PRs on GitHub, this workflow sometimes leads to duplicated information, such as the status of an issue that has to be updated on both places.
In this article we will build a simple automation that will change the status of an issue on Notion to
Done as soon as the corresponding PR on GitHub is closed.
Exploring the (unofficial)
Until the Notion API is publicly available, there are a number of unofficial libraries that achieve a really good job in accessing a Notion workspace. We will use the
notion-py library available at github.com/jamalex/notion-py.
Connecting the Notion integration
When using Deepnote, create a new
Environment variables integration in the left sidebar and specify the following two environment variables:
NOTION_TOKEN_V2– set this to the value of the
token_v2cookie in a logged-in Notion session in your browser
NOTION_USER_EMAIL– set this to the email associated to your Notion account (this is useful if you have multiple logged in Notion users but onyl can can access the target Notion page)
The following code creates a Notion client object that will be able to access (and modify) Notion pages.
Changing a Notion ticket status
A ticket (or issue) in our Notion database may look as follows:
Let's define a function that will close all issues whose
PR link points to the given issue.
Yes, it's as easy as this!
The next goal is to run the function as soon as a PR is closed or merged.
Accepting a GitHub webhooks in Python
We will use
Flask, a micro web framework written in Python, together with the
python-github-webhook library (available at github.com/bloomberg/python-github-webhook) to write a python program that will receive and parse webhooks from GitHub. Specifically, we are interested in Pull request webhooks that will tell us when a PR has been merged or closed.
GitHub provides a detailed explanation of possible webhook events and their properties here. From all actions under the
pull_request event, we will filter only the
requirements.txt so that it will be installed automatically every time a machine has started.
Verifying that payloads actually come from GitHub
To verify that a request actually comes from GitHub (and not from an attacker), we can let GitHub send us a hash-based message authentication code (HMAC) in each request, based on a fixed mutually agreed secret. All webhook requests will then be signed, and the signature can be verified when we receive the webhook.
All the verification is handled by the
python-github-webhook library, we only need to make sure that the secret in the code above is identical to the secret set in GitHub. In the code above, replace
secret="U7L56ddCXr" with arbitrary string.
Accepting webhooks behind a firewall
To allow our app to receive webhooks even behind firewalls (so that we can run this project anywhere), we will use the
pysmee Python library - it's a client library for smee.io, a free webhook payload delivery service. (I've added the
pysmee library to
requirements.txt so that it's always installed automatically).
Smee.io will provide you with a URL such as
https://smee.io/AZ982kOIyr. Payloads sent to the given public URL will be forwarded to a locally running application using the smee.io client registered under the given URL.
To make this work using our Python Flask app, let's open a terminal and run the following command (substituting the smee.io url with your registered URL):
pysmee forward https://smee.io/AZ982kOIyr http://localhost:8080/postreceive
This will forward all requests sent to the public smee.io URL to our locally running Flask server.
Setting up webhooks on GitHub
The final step now is to make GitHub send a request to our bot. Within your target repository, navigate to
Add webhook and do the following:
- Change Content type to
application/json(this is how the
bloomberg/python-github-webhooklibrary expects to get the payload)
- Set Secret to the same string as you used in the Flask app
- Select Let me select individual events and uncheck everything except Pull requests
Launching the rocket 🚀
Finally, run our webhook receiver server:
And, that's it!
Run this notebook (making sure that the
pysmee command is running in the terminal window), and when you close a PR in your targer repository, the status of any issues in the Notion dabasase that reference the PR in the PR column will change to
Done. Try it yourself by duplicating this notebook!