Skip to content

Support for generic webhooks #8522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
12cd38b
Support for generic webhooks
stsewd Sep 22, 2021
ed703fd
Rename unsub_url -> unsubscribe_url
stsewd Sep 29, 2021
2584afd
Merge branch 'master' into webhooks
stsewd Sep 29, 2021
224622e
Update migrations
stsewd Sep 29, 2021
c531286
Fix merge conflict
stsewd Sep 29, 2021
1a5c5b7
Add more susbtitutions
stsewd Oct 4, 2021
2e03439
Fix
stsewd Oct 4, 2021
9a961e7
Zero downtime
stsewd Oct 4, 2021
297d1a6
Test views
stsewd Oct 4, 2021
73e5efb
fix tests
stsewd Oct 4, 2021
c0689f1
Document generic webhooks
astrojuanlu Oct 21, 2021
c99df5d
Merge branch 'master' into webhooks
stsewd Oct 21, 2021
c6c499b
Whitespace
astrojuanlu Oct 25, 2021
0a031b3
Describe legacy webhooks and migration path
astrojuanlu Oct 25, 2021
9405d4b
Add examples
astrojuanlu Oct 25, 2021
2dd6b39
Fix external version disclaimer
astrojuanlu Oct 25, 2021
3e988a5
Add example to validate webhook signature
astrojuanlu Oct 25, 2021
80446dd
Merge branch 'webhooks' into webhooks-documentation
astrojuanlu Oct 25, 2021
ce31189
Fix markup for Discord webhook example
astrojuanlu Oct 25, 2021
43f9dd4
Clarify that validation is optional
astrojuanlu Oct 25, 2021
e1b426e
Upgrade to use sha256 instead of sha1
stsewd Oct 25, 2021
b6fd66e
Style improvements
astrojuanlu Oct 25, 2021
b443b4f
Fix digest comparison
astrojuanlu Oct 25, 2021
8f55e27
Webhook example improvements
astrojuanlu Oct 25, 2021
39c8ab2
Update external documentation links
astrojuanlu Oct 25, 2021
37484f1
Link to docs
stsewd Oct 25, 2021
fe45d31
Fix screenshot zoom
astrojuanlu Oct 25, 2021
5ff125c
Update test
stsewd Oct 25, 2021
342eea7
Support build.start_date
stsewd Oct 25, 2021
509b09b
Don't show timezone and microseconds
stsewd Oct 25, 2021
e4ea22e
Merge branch 'master' into webhooks
stsewd Nov 2, 2021
2b16a86
Change substitutions format again
stsewd Nov 2, 2021
33f9557
Move payload validation down
astrojuanlu Nov 8, 2021
1077d61
Update template syntax
astrojuanlu Nov 8, 2021
481380d
Merge branch 'webhooks' into webhooks-documentation
astrojuanlu Nov 8, 2021
19eec46
Complete webhook example payload
astrojuanlu Nov 8, 2021
088e629
Add screenshot for custom payload
astrojuanlu Nov 8, 2021
2a0a031
Style improvements
astrojuanlu Nov 8, 2021
a26bd54
Comment
stsewd Nov 8, 2021
a2b6a2a
Merge branch 'master' into webhooks
stsewd Nov 8, 2021
ec9b4fc
Switch to one space for template substitutions
astrojuanlu Nov 8, 2021
b049047
Add examples for URLs
astrojuanlu Nov 8, 2021
94f9fc1
Fix parameter name
astrojuanlu Nov 8, 2021
a928097
Complete remaining variables
astrojuanlu Nov 8, 2021
8f20472
Fix title length
astrojuanlu Nov 8, 2021
04e2953
Fix JSON
astrojuanlu Nov 8, 2021
d1a21f0
Merge pull request #8609 from readthedocs/webhooks-documentation
astrojuanlu Nov 8, 2021
b6dd115
Small updates to docs
stsewd Nov 8, 2021
732ccdd
Merge branch 'master' into webhooks
stsewd Nov 9, 2021
d2611b4
Explicitly test with/out organizations
stsewd Nov 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/_static/images/webhooks-activity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/images/webhooks-events.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/images/webhooks-payload.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/images/webhooks-secret.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
272 changes: 250 additions & 22 deletions docs/build-notifications.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
Enabling Build Notifications
============================
Build Notifications and Webhooks
================================

.. note::

Currently we don't send notifications when
a :doc:`build from a pull request fails </pull-requests>`.
Currently we don't send notifications or trigger webhooks
on :doc:`builds from pull requests </pull-requests>`.

Using email
-----------
Email notifications
-------------------

Read the Docs allows you to configure emails that can be sent on failing builds.
This makes sure you know when your builds have failed.
Expand All @@ -20,31 +20,259 @@ Take these steps to enable build notifications using email:

You should now get notified by email when your builds fail!

Using webhook
-------------
Build Status Webhooks
---------------------

Read the Docs can also send webhooks when builds are triggered, successful or failed.

Take these steps to enable build notifications using a webhook:

* Go to :guilabel:`Admin` > :guilabel:`Notifications` in your project.
* Fill in the **URL** field under the **New Webhook Notifications** heading
* Submit the form
* Go to :guilabel:`Admin` > :guilabel:`Webhooks` in your project.
* Fill in the **URL** field and select what events will trigger the webhook
* Modify the payload or leave the default (see below)
* Click on :guilabel:`Save`

.. figure:: /_static/images/webhooks-events.png
:align: center
:alt: URL and events for a webhook

URL and events for a webhook

Every time one of the checked events triggers,
Read the Docs will send a POST request to your webhook URL.
The default payload will look like this:

.. code-block:: json

{
"event": "build:triggered",
"name": "docs",
"slug": "docs",
"version": "latest",
"commit": "2552bb609ca46865dc36401dee0b1865a0aee52d",
"build": "15173336",
"start_date": "2021-11-03T16:23:14",
"build_url": "https://readthedocs.org/projects/docs/builds/15173336/",
"docs_url": "https://docs.readthedocs.io/en/latest/"
}

When a webhook is sent, a new entry will be added to the
"Recent Activity" table. By clicking on each individual entry,
you will see the server response, the webhook request, and the payload.

.. figure:: /_static/images/webhooks-activity.png
:align: center
:alt: Activity of a webhook

Activity of a webhook

Custom payload examples
~~~~~~~~~~~~~~~~~~~~~~~

You can customize the payload of the webhook to suit your needs,
as long as it is valid JSON. Below you have a couple of examples,
and in the following section you will find all the available variables.

.. figure:: /_static/images/webhooks-payload.png
:width: 80%
:align: center
:alt: Custom payload

Custom payload

The project name, slug and its details for the build will be sent as POST request to your webhook URL:
Slack
+++++

.. code-block:: json

{
"attachments": [
{
"name": "Read the Docs",
"slug": "rtd",
"build": {
"id": 6321373,
"commit": "e8dd17a3f1627dd206d721e4be08ae6766fda40",
"state": "finished",
"success": false,
"date": "2017-02-15 20:35:54"
}
"color": "#db3238",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Read the Docs build failed*"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Project*: <{{ project.url }}|{{ project.name }}>"
},
{
"type": "mrkdwn",
"text": "*Version*: {{ version.name }} ({{ build.commit }})"
},
{
"type": "mrkdwn",
"text": "*Build*: <{{ build.url }}|{{ build.id }}>"
}
]
}
]
}
]
}

More information on `the Slack Incoming Webhooks documentation <https://api.slack.com/messaging/webhooks>`_.

Discord
+++++++

.. code-block:: json

{
"username": "Read the Docs",
"content": "Read the Docs build failed",
"embeds": [
{
"title": "Build logs",
"url": "{{ build.url }}",
"color": 15258703,
"fields": [
{
"name": "*Project*",
"value": "{{ project.url }}",
"inline": true
},
{
"name": "*Version*",
"value": "{{ version.name }} ({{ build.commit }})",
"inline": true
},
{
"name": "*Build*",
"value": "{{ build.url }}"
}
]
}
]
}

More information on `the Discord webhooks documentation <https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks>`_.

Variable substitutions reference
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``{{ event }}``
Event that triggered the webhook, one of ``build:triggered``, ``build:failed``, or ``build:passed``.

``{{ build.id }}``
Build ID.

``{{ build.commit }}``
Commit corresponding to the build, if present (otherwise ``""``).

``{{ build.url }}``
URL of the build, for example ``https://readthedocs.org/projects/docs/builds/15173336/``.

``{{ build.docs_url }}``
URL of the documentation corresponding to the build,
for example ``https://docs.readthedocs.io/en/latest/``.

You should now get notified on your webhook when your builds start and finish (failure/success)!
``{{ build.start_date }}``
Start date of the build (UTC, ISO format), for example ``2021-11-03T16:23:14``.

``{{ organization.name }}``
Organization name (Commercial only).

``{{ organization.slug }}``
Organization slug (Commercial only).

``{{ project.slug }}``
Project slug.

``{{ project.name }}``
Project name.

``{{ project.url }}``
URL of the project :term:`dashboard`.

``{{ version.slug }}``
Version slug.

``{{ version.name }}``
Version name.

Validating the payload
~~~~~~~~~~~~~~~~~~~~~~

After you add a new webhook, Read the Docs will generate a secret key for it
and uses it to generate a hash signature (HMAC-SHA256) for each payload
that is included in the ``X-Hub-Signature`` header of the request.

.. figure:: /_static/images/webhooks-secret.png
:width: 80%
:align: center
:alt: Webhook secret

Webhook secret

We highly recommend using this signature
to verify that the webhook is coming from Read the Docs.
To do so, you can add some custom code on your server,
like this:

.. code-block:: python

import hashlib
import hmac
import os


def verify_signature(payload, request_headers):
"""
Verify that the signature of payload is the same as the one coming from request_headers.
"""
digest = hmac.new(
key=os.environ["WEBHOOK_SECRET"].encode(),
msg=payload.encode(),
digestmod=hashlib.sha256,
)
expected_signature = digest.hexdigest()

return hmac.compare_digest(
request_headers["X-Hub-Signature"].encode(),
expected_signature.encode(),
)

Legacy webhooks
~~~~~~~~~~~~~~~

Webhooks created before the custom payloads functionality was added to Read the Docs
send a payload with the following structure:

.. code-block:: json

{
"name": "Read the Docs",
"slug": "rtd",
"build": {
"id": 6321373,
"commit": "e8dd17a3f1627dd206d721e4be08ae6766fda40",
"state": "finished",
"success": false,
"date": "2017-02-15 20:35:54"
}
}

To migrate to the new webhooks and keep a similar structure,
you can use this payload:

.. code-block:: json

{
"name": "{{ project.name }}",
"slug": "{{ project.slug }}",
"build": {
"id": "{{ build.id }}",
"commit": "{{ build.commit }}",
"state": "{{ event }}",
"date": "{{ build.start_date }}"
}
}
2 changes: 0 additions & 2 deletions readthedocs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

"""Read the Docs."""

import os.path
Expand Down
Loading