Skip to content

Commit a8814b3

Browse files
humitosagjohnson
authored andcommitted
Notify users about the usage of deprecated webhooks (#4898)
* Notify users about the usage of deprecated webhooks Each time a deprecated webhook is hit, a notification is created (without duplicating it) to be sent. * Extend notification to support de-dup and delayed email sent * Improve decorator to support generic and specific VCS webhook views * Remove no necessary settings * DeprecatedWebhookEndpointNotification tests and improvements * Better docstring * Lint * Update copy on notifications for github services deprecation (#5067) * Updated copy on webhooks * Drop "deprecated webhook endpoint" copy, this is core team nomenclature, not user nomenclature. * Add small amount of docs to point to * Update docs and point to docs in notification message * Add year * Split up deprecated view notification to GitHub and other webhook endpoints (#5083) * Updated copy on webhooks * Drop "deprecated webhook endpoint" copy, this is core team nomenclature, not user nomenclature. * Add small amount of docs to point to * Update docs and point to docs in notification message * Split up deprecated view notification to GitHub and other webhook endpoints This sets a date for deprecated of these endpoints as Mar 1st 2019. Too soon? * Reduce complexity and drop decorator pattern for Notification classmethod pattern used in other notifications * Add notifications for non-GitHub incoming webhooks * Add docs as well * More renaming and slight refactor Found out 2x messages are being generated, so this stops the automated mechanism for triggering these messages. * Update dates * Also update docs * Typo on date * Back out some more of the changes to notifications to make them operable without automation * Add admin method for notification * Add admin filter for project features
1 parent 8ad259a commit a8814b3

11 files changed

+205
-10
lines changed

docs/webhooks.rst

+76
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ details and a list of HTTP exchanges that have taken place for the integration.
2020
You need this information for the URL, webhook, or Payload URL needed by the
2121
repository provider such as GitHub, GitLab, or Bitbucket.
2222

23+
.. _webhook-creation:
24+
2325
Webhook Creation
2426
----------------
2527

@@ -36,6 +38,8 @@ As an example, the URL pattern looks like this: *readthedocs.org/api/v2/webhook/
3638

3739
Use this URL when setting up a new webhook with your provider -- these steps vary depending on the provider:
3840

41+
.. _webhook-integration-github:
42+
3943
GitHub
4044
~~~~~~
4145

@@ -54,6 +58,8 @@ For a 403 error, it's likely that the Payload URL is incorrect.
5458

5559
.. note:: The webhook token, intended for the GitHub **Secret** field, is not yet implemented.
5660

61+
.. _webhook-integration-bitbucket:
62+
5763
Bitbucket
5864
~~~~~~~~~
5965

@@ -64,6 +70,8 @@ Bitbucket
6470
* Under **Triggers**, **Repository push** should be selected
6571
* Finish by clicking **Save**
6672

73+
.. _webhook-integration-gitlab:
74+
6775
GitLab
6876
~~~~~~
6977

@@ -74,6 +82,8 @@ GitLab
7482
* Leave the default **Push events** selected and mark **Tag push events** also
7583
* Finish by clicking **Add Webhook**
7684

85+
.. _webhook-integration-generic:
86+
7787
Using the generic API integration
7888
---------------------------------
7989

@@ -137,3 +147,69 @@ Resyncing webhooks
137147
It might be necessary to re-establish a webhook if you are noticing problems.
138148
To resync a webhook from Read the Docs, visit the integration detail page and
139149
follow the directions for re-syncing your repository webhook.
150+
151+
Troubleshooting
152+
---------------
153+
154+
My project isn't automatically building
155+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156+
157+
If your project isn't automatically building, you can check your integration on
158+
Read the Docs to see the payload sent to our servers. If there is no recent
159+
activity on your Read the Docs project webhook integration, then it's likely
160+
that your VCS provider is not configured correctly. If there is payload
161+
information on your Read the Docs project, you might need to verify that your
162+
versions are configured to build correctly.
163+
164+
Either way, it may help to either resync your webhook intergration (see
165+
`Resyncing webhooks`_ for information on this process), or set up an entirely
166+
new webhook intergration.
167+
168+
.. _webhook-github-services:
169+
170+
I was warned I shouldn't use GitHub Services
171+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172+
173+
Last year, GitHub announced that effective Jan 31st, 2019, GitHub Services will stop
174+
working [1]_. This means GitHub will stop sending notifications to Read the Docs
175+
for projects configured with the ``ReadTheDocs`` GitHub Service. If your project
176+
has been configured on Read the Docs for a long time, you are most likely still
177+
using this service to automatically build your project on Read the Docs.
178+
179+
In order for your project to continue automatically building, you will need to
180+
configure your GitHub repository with a new webhook. You can use either a
181+
connected GitHub account and a :ref:`GitHub webhook integration <webhook-integration-github>`
182+
on your Read the Docs project, or you can use a
183+
:ref:`generic webhook integraiton <webhook-integration-generic>` without a connected
184+
account.
185+
186+
.. [1] https://developer.github.com/changes/2018-04-25-github-services-deprecation/
187+
188+
.. _webhook-deprecated-endpoints:
189+
190+
I was warned that my project won't automatically build after April 1st
191+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
192+
193+
In addition to :ref:`no longer supporting GitHub Services <webhook-github-services>`,
194+
we have decided to no longer support several other legacy incoming webhook
195+
endpoints that were used before we introduced project webhook integrations. When
196+
we introduced our webhook integrations, we added several features and improved
197+
security for incoming webhooks and these features were not added to our leagcy
198+
incoming webhooks. New projects have not been able to use our legacy incoming
199+
webhooks since, however if you have a project that has been established for a
200+
while, you may still be using these endpoints.
201+
202+
After March 1st, 2019, we will stop accepting incoming webhook notifications for
203+
these legacy incoming webhooks. Your project will need to be reconfigured and
204+
have a webhook integration configured, pointing to a new webhook with your VCS
205+
provider.
206+
207+
In particular, the incoming webhook URLs that will be removed are:
208+
209+
* ``https://readthedocs.org/build``
210+
* ``https://readthedocs.org/bitbucket``
211+
* ``https://readthedocs.org/github`` (as noted :ref:`above <webhook-github-services>`)
212+
* ``https://readthedocs.org/gitlab``
213+
214+
In order to establish a new project webhook integration, :ref:`follow
215+
the directions for your VCS provider <webhook-creation>`

readthedocs/core/views/hooks.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,10 @@ def generic_build(request, project_id_or_slug=None):
370370
if request.method == 'POST':
371371
slug = request.POST.get('version_slug', project.default_version)
372372
log.info(
373-
"(Incoming Generic Build) %s [%s]", project.slug, slug)
373+
"(Incoming Generic Build) %s [%s]",
374+
project.slug,
375+
slug,
376+
)
374377
_build_version(project, slug)
375378
else:
376379
return HttpResponse("You must POST to this resource.")

readthedocs/notifications/backends.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ def send_notification(request, notification):
3232
backends = getattr(settings, 'NOTIFICATION_BACKENDS', [])
3333
for cls_name in backends:
3434
backend = import_string(cls_name)(request)
35-
# Do not send email notification if defined explicitly
36-
if backend.name == EmailBackend.name and not notification.send_email:
37-
pass
38-
else:
39-
backend.send(notification)
35+
backend.send(notification)
4036

4137

4238
class Backend(object):
@@ -55,11 +51,16 @@ class EmailBackend(Backend):
5551
5652
The content body is first rendered from an on-disk template, then passed
5753
into the standard email templates as a string.
54+
55+
If the notification is set to ``send_email=False``, this backend will exit
56+
early from :py:meth:`send`.
5857
"""
5958

6059
name = 'email'
6160

6261
def send(self, notification):
62+
if not notification.send_email:
63+
return
6364
# FIXME: if the level is an ERROR an email is received and sometimes
6465
# it's not necessary. This behavior should be clearly documented in the
6566
# code
@@ -114,6 +115,6 @@ def send(self, notification):
114115
backend_name=self.name,
115116
source_format=HTML,
116117
),
117-
extra_tags='',
118+
extra_tags=notification.extra_tags,
118119
user=notification.user,
119120
)

readthedocs/notifications/notification.py

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Notification(object):
3535
subject = None
3636
user = None
3737
send_email = True
38+
extra_tags = ''
3839

3940
def __init__(self, context_object, request, user=None):
4041
self.object = context_object

readthedocs/projects/admin.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,20 @@
3030
ProjectRelationship,
3131
WebHook,
3232
)
33-
from .notifications import ResourceUsageNotification
33+
from .notifications import (
34+
ResourceUsageNotification,
35+
DeprecatedBuildWebhookNotification,
36+
DeprecatedGitHubWebhookNotification,
37+
)
3438
from .tasks import remove_dir
3539

3640

3741
class ProjectSendNotificationView(SendNotificationView):
38-
notification_classes = [ResourceUsageNotification]
42+
notification_classes = [
43+
ResourceUsageNotification,
44+
DeprecatedBuildWebhookNotification,
45+
DeprecatedGitHubWebhookNotification,
46+
]
3947

4048
def get_object_recipients(self, obj):
4149
for owner in obj.users.all():
@@ -119,7 +127,7 @@ class ProjectAdmin(GuardedModelAdmin):
119127
list_display = ('name', 'slug', 'repo', 'repo_type', 'featured')
120128
list_filter = ('repo_type', 'featured', 'privacy_level',
121129
'documentation_type', 'programming_language',
122-
ProjectOwnerBannedFilter)
130+
'feature__feature_id', ProjectOwnerBannedFilter)
123131
list_editable = ('featured',)
124132
search_fields = ('slug', 'repo')
125133
inlines = [ProjectRelationshipInline, RedirectInline,

readthedocs/projects/notifications.py

+42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
# -*- coding: utf-8 -*-
12
"""Project notifications"""
23

34
from __future__ import absolute_import
5+
from datetime import timedelta
6+
from django.utils import timezone
7+
from django.http import HttpRequest
8+
from messages_extends.models import Message
49
from readthedocs.notifications import Notification
510
from readthedocs.notifications.constants import REQUIREMENT
611

@@ -11,3 +16,40 @@ class ResourceUsageNotification(Notification):
1116
context_object_name = 'project'
1217
subject = 'Builds for {{ project.name }} are using too many resources'
1318
level = REQUIREMENT
19+
20+
21+
class DeprecatedViewNotification(Notification):
22+
23+
"""Notification to alert user of a view that is going away."""
24+
25+
context_object_name = 'project'
26+
subject = '{{ project.name }} project webhook needs to be updated'
27+
level = REQUIREMENT
28+
29+
@classmethod
30+
def notify_project_users(cls, projects):
31+
"""
32+
Notify project users of deprecated view.
33+
34+
:param projects: List of project instances
35+
:type projects: [:py:class:`Project`]
36+
"""
37+
for project in projects:
38+
# Send one notification to each owner of the project
39+
for user in project.users.all():
40+
notification = cls(
41+
context_object=project,
42+
request=HttpRequest(),
43+
user=user,
44+
)
45+
notification.send()
46+
47+
48+
class DeprecatedGitHubWebhookNotification(DeprecatedViewNotification):
49+
50+
name = 'deprecated_github_webhook'
51+
52+
53+
class DeprecatedBuildWebhookNotification(DeprecatedViewNotification):
54+
55+
name = 'deprecated_build_webhook'

readthedocs/rtd_tests/tests/test_notifications.py

+48
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@
22
"""Notification tests"""
33

44
from __future__ import absolute_import
5+
from datetime import timedelta
56
import mock
67
import django_dynamic_fixture as fixture
8+
from django.http import HttpRequest
79
from django.test import TestCase
810
from django.test.utils import override_settings
911
from django.contrib.auth.models import User, AnonymousUser
12+
from django.utils import timezone
1013
from messages_extends.models import Message as PersistentMessage
1114

1215
from readthedocs.notifications import Notification, SiteNotification
1316
from readthedocs.notifications.backends import EmailBackend, SiteBackend
1417
from readthedocs.notifications.constants import ERROR, INFO_NON_PERSISTENT, WARNING_NON_PERSISTENT
18+
from readthedocs.projects.models import Project
19+
from readthedocs.projects.notifications import (
20+
DeprecatedGitHubWebhookNotification,
21+
DeprecatedBuildWebhookNotification,
22+
)
1523
from readthedocs.builds.models import Build
1624

1725

@@ -222,3 +230,43 @@ def test_message(self):
222230
with mock.patch('readthedocs.notifications.notification.log') as mock_log:
223231
self.assertEqual(self.n.get_message(False), '')
224232
mock_log.error.assert_called_once()
233+
234+
235+
class DeprecatedWebhookEndpointNotificationTests(TestCase):
236+
237+
def setUp(self):
238+
PersistentMessage.objects.all().delete()
239+
240+
self.user = fixture.get(User)
241+
self.project = fixture.get(Project, users=[self.user])
242+
self.request = HttpRequest()
243+
244+
self.notification = DeprecatedBuildWebhookNotification(
245+
self.project,
246+
self.request,
247+
self.user,
248+
)
249+
250+
@mock.patch('readthedocs.notifications.backends.send_email')
251+
def test_dedupliation(self, send_email):
252+
user = fixture.get(User)
253+
project = fixture.get(Project, main_language_project=None)
254+
project.users.add(user)
255+
project.refresh_from_db()
256+
self.assertEqual(project.users.count(), 1)
257+
258+
self.assertEqual(PersistentMessage.objects.filter(user=user).count(), 0)
259+
DeprecatedGitHubWebhookNotification.notify_project_users([project])
260+
261+
# Site and email notification will go out, site message doesn't have
262+
# any reason to deduplicate yet
263+
self.assertEqual(PersistentMessage.objects.filter(user=user).count(), 1)
264+
self.assertTrue(send_email.called)
265+
send_email.reset_mock()
266+
self.assertFalse(send_email.called)
267+
268+
# Expect the site message to deduplicate, the email won't
269+
DeprecatedGitHubWebhookNotification.notify_project_users([project])
270+
self.assertEqual(PersistentMessage.objects.filter(user=user).count(), 1)
271+
self.assertTrue(send_email.called)
272+
send_email.reset_mock()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<p>Your project, {{ project.name }}, is currently using a legacy incoming webhook to trigger builds on Read the Docs. Effective April 1st, 2019, Read the Docs will no longer accept incoming webhooks through these endpoints.</p>
2+
3+
<p>To continue building your Read the Docs project on changes to your repository, you will need to configure a new webhook with your VCS provider. You can find more information on how to configure a new webhook in our documentation, at:</p>
4+
5+
{% comment %}Plain text link because of text version of email{% endcomment %}
6+
<p><a href="https://docs.readthedocs.io/en/latest/webhooks.html#webhook-deprecated-endpoints">https://docs.readthedocs.io/en/latest/webhooks.html#webhook-deprecated-endpoints</a></p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Your project, {{ project.name }}, needs to be reconfigured in order to continue building automatically after April 1st, 2019. For more information, <a href="https://docs.readthedocs.io/en/latest/webhooks.html#webhook-deprecated-endpoints">see our documentation on webhook integrations</a>.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<p>Your project, {{ project.name }}, is currently using GitHub Services to trigger builds on Read the Docs. Effective January 31, 2019, GitHub will no longer process requests using the Services feature, and so Read the Docs will not receive notifications on updates to your repository.</p>
2+
3+
<p>To continue building your Read the Docs project on changes to your repository, you will need to add a new webhook on your GitHub repository. You can either connect your GitHub account and configure a GitHub webhook integration, or you can add a generic webhook integration.</p>
4+
5+
<p>You can find more information on our webhook intergrations in our documentation, at:</p>
6+
7+
{% comment %}Plain text link because of text version of email{% endcomment %}
8+
<p><a href="https://docs.readthedocs.io/en/latest/webhooks.html#webhook-github-services">https://docs.readthedocs.io/en/latest/webhooks.html#webhook-github-services</a></p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Your project, {{ project.name }}, needs to be reconfigured in order to continue building automatically after January 31st, 2019. For more information, <a href="https://docs.readthedocs.io/en/latest/webhooks.html#webhook-github-services">see our documentation on webhook integrations</a>.

0 commit comments

Comments
 (0)