Skip to content

Commit a215295

Browse files
authored
Remove old/deprecated build endpoints (readthedocs#5479)
Remove old/deprecated build endpoints
2 parents e67937e + 3c1c5ba commit a215295

File tree

4 files changed

+2
-971
lines changed

4 files changed

+2
-971
lines changed

readthedocs/core/urls/__init__.py

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
# -*- coding: utf-8 -*-
2-
31
"""URL configuration for core app."""
42

53
from __future__ import absolute_import
64
from django.conf.urls import url
75

86
from readthedocs.constants import pattern_opts
97
from readthedocs.core import views
10-
from readthedocs.core.views import hooks, serve
8+
from readthedocs.core.views import serve
119
from readthedocs.projects.feeds import LatestProjectsFeed, NewProjectsFeed
1210

1311
docs_urls = [
@@ -43,18 +41,6 @@
4341
]
4442

4543
core_urls = [
46-
# Hooks
47-
url(r'^github', hooks.github_build, name='github_build'),
48-
url(r'^gitlab', hooks.gitlab_build, name='gitlab_build'),
49-
url(r'^bitbucket', hooks.bitbucket_build, name='bitbucket_build'),
50-
url(
51-
(
52-
r'^build/'
53-
r'(?P<project_id_or_slug>{project_slug})'.format(**pattern_opts)
54-
),
55-
hooks.generic_build,
56-
name='generic_build',
57-
),
5844
# Random other stuff
5945
url(
6046
r'^random/(?P<project_slug>{project_slug})'.format(**pattern_opts),

readthedocs/core/views/__init__.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# -*- coding: utf-8 -*-
2-
31
"""
42
Core views, including the main homepage,
53
@@ -87,7 +85,7 @@ def wipe_version(request, project_slug, version_slug):
8785
if request.method == 'POST':
8886
wipe_version_via_slugs(
8987
version_slug=version_slug,
90-
project_slug=project_slug
88+
project_slug=project_slug,
9189
)
9290
return redirect('project_version_list', project_slug)
9391
return render(

readthedocs/core/views/hooks.py

-298
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,14 @@
11
"""Views pertaining to builds."""
22

3-
import json
43
import logging
5-
import re
64

7-
from django.http import HttpResponse, HttpResponseNotFound
8-
from django.shortcuts import redirect
9-
from django.views.decorators.csrf import csrf_exempt
10-
11-
from readthedocs.builds.constants import LATEST
125
from readthedocs.core.utils import trigger_build
13-
from readthedocs.projects import constants
14-
from readthedocs.projects.models import Feature, Project
156
from readthedocs.projects.tasks import sync_repository_task
167

178

189
log = logging.getLogger(__name__)
1910

2011

21-
class NoProjectException(Exception):
22-
pass
23-
24-
25-
def _allow_deprecated_webhook(project):
26-
return project.has_feature(Feature.ALLOW_DEPRECATED_WEBHOOKS)
27-
28-
2912
def _build_version(project, slug, already_built=()):
3013
"""
3114
Where we actually trigger builds for a project and slug.
@@ -105,284 +88,3 @@ def sync_versions(project):
10588
except Exception:
10689
log.exception('Unknown sync versions exception')
10790
return None
108-
109-
110-
def get_project_from_url(url):
111-
if not url:
112-
return Project.objects.none()
113-
projects = (
114-
Project.objects.filter(repo__iendswith=url) |
115-
Project.objects.filter(repo__iendswith=url + '.git')
116-
)
117-
return projects
118-
119-
120-
def log_info(project, msg):
121-
log.info(
122-
constants.LOG_TEMPLATE,
123-
{
124-
'project': project,
125-
'version': '',
126-
'msg': msg,
127-
}
128-
)
129-
130-
131-
def _build_url(url, projects, branches):
132-
"""
133-
Map a URL onto specific projects to build that are linked to that URL.
134-
135-
Check each of the ``branches`` to see if they are active and should be
136-
built.
137-
"""
138-
ret = ''
139-
all_built = {}
140-
all_not_building = {}
141-
142-
# This endpoint doesn't require authorization, we shouldn't allow builds to
143-
# be triggered from this any longer. Deprecation plan is to selectively
144-
# allow access to this endpoint for now.
145-
if not any(_allow_deprecated_webhook(project) for project in projects):
146-
return HttpResponse('This API endpoint is deprecated', status=403)
147-
148-
for project in projects:
149-
(built, not_building) = build_branches(project, branches)
150-
if not built:
151-
# Call sync_repository_task to update tag/branch info
152-
version = project.versions.get(slug=LATEST)
153-
sync_repository_task.delay(version.pk)
154-
msg = '(URL Build) Syncing versions for %s' % project.slug
155-
log.info(msg)
156-
all_built[project.slug] = built
157-
all_not_building[project.slug] = not_building
158-
159-
for project_slug, built in list(all_built.items()):
160-
if built:
161-
msg = '(URL Build) Build Started: {} [{}]'.format(
162-
url,
163-
' '.join(built),
164-
)
165-
log_info(project_slug, msg=msg)
166-
ret += msg
167-
168-
for project_slug, not_building in list(all_not_building.items()):
169-
if not_building:
170-
msg = '(URL Build) Not Building: {} [{}]'.format(
171-
url,
172-
' '.join(not_building),
173-
)
174-
log_info(project_slug, msg=msg)
175-
ret += msg
176-
177-
if not ret:
178-
ret = '(URL Build) No known branches were pushed to.'
179-
180-
return HttpResponse(ret)
181-
182-
183-
@csrf_exempt
184-
def github_build(request): # noqa: D205
185-
"""
186-
GitHub webhook consumer.
187-
188-
.. warning:: **DEPRECATED**
189-
Use :py:class:`readthedocs.api.v2.views.integrations.GitHubWebhookView`
190-
instead of this view function
191-
192-
This will search for projects matching either a stripped down HTTP or SSH
193-
URL. The search is error prone, use the API v2 webhook for new webhooks.
194-
195-
Old webhooks may not have specified the content type to POST with, and
196-
therefore can use ``application/x-www-form-urlencoded`` to pass the JSON
197-
payload. More information on the API docs here:
198-
https://developer.github.com/webhooks/creating/#content-type
199-
"""
200-
if request.method == 'POST':
201-
try:
202-
if request.META['CONTENT_TYPE'] == 'application/x-www-form-urlencoded':
203-
data = json.loads(request.POST.get('payload'))
204-
else:
205-
data = json.loads(request.body)
206-
http_url = data['repository']['url']
207-
http_search_url = http_url.replace('http://', '').replace('https://', '')
208-
ssh_url = data['repository']['ssh_url']
209-
ssh_search_url = ssh_url.replace('git@', '').replace('.git', '')
210-
branches = [data['ref'].replace('refs/heads/', '')]
211-
except (ValueError, TypeError, KeyError):
212-
log.exception('Invalid GitHub webhook payload')
213-
return HttpResponse('Invalid request', status=400)
214-
try:
215-
repo_projects = get_project_from_url(http_search_url)
216-
if repo_projects:
217-
log.info(
218-
'GitHub webhook search: url=%s branches=%s',
219-
http_search_url,
220-
branches,
221-
)
222-
ssh_projects = get_project_from_url(ssh_search_url)
223-
if ssh_projects:
224-
log.info(
225-
'GitHub webhook search: url=%s branches=%s',
226-
ssh_search_url,
227-
branches,
228-
)
229-
projects = repo_projects | ssh_projects
230-
return _build_url(http_search_url, projects, branches)
231-
except NoProjectException:
232-
log.exception('Project match not found: url=%s', http_search_url)
233-
return HttpResponseNotFound('Project not found')
234-
else:
235-
return HttpResponse('Method not allowed, POST is required', status=405)
236-
237-
238-
@csrf_exempt
239-
def gitlab_build(request): # noqa: D205
240-
"""
241-
GitLab webhook consumer.
242-
243-
.. warning:: **DEPRECATED**
244-
Use :py:class:`readthedocs.api.v2.views.integrations.GitLabWebhookView`
245-
instead of this view function
246-
247-
Search project repository URLs using the site URL from GitLab webhook payload.
248-
This search is error-prone, use the API v2 webhook view for new webhooks.
249-
"""
250-
if request.method == 'POST':
251-
try:
252-
data = json.loads(request.body)
253-
url = data['project']['http_url']
254-
search_url = re.sub(r'^https?://(.*?)(?:\.git|)$', '\\1', url)
255-
branches = [data['ref'].replace('refs/heads/', '')]
256-
except (ValueError, TypeError, KeyError):
257-
log.exception('Invalid GitLab webhook payload')
258-
return HttpResponse('Invalid request', status=400)
259-
log.info(
260-
'GitLab webhook search: url=%s branches=%s',
261-
search_url,
262-
branches,
263-
)
264-
projects = get_project_from_url(search_url)
265-
if projects:
266-
return _build_url(search_url, projects, branches)
267-
268-
log.info('Project match not found: url=%s', search_url)
269-
return HttpResponseNotFound('Project match not found')
270-
return HttpResponse('Method not allowed, POST is required', status=405)
271-
272-
273-
@csrf_exempt
274-
def bitbucket_build(request):
275-
"""
276-
Consume webhooks from multiple versions of Bitbucket's API.
277-
278-
.. warning:: **DEPRECATED**
279-
Use :py:class:`readthedocs.api.v2.views.integrations.BitbucketWebhookView`
280-
instead of this view function
281-
282-
New webhooks are set up with v2, but v1 webhooks will still point to this
283-
endpoint. There are also "services" that point here and submit
284-
``application/x-www-form-urlencoded`` data.
285-
286-
API v1
287-
https://confluence.atlassian.com/bitbucket/events-resources-296095220.html
288-
289-
API v2
290-
https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html#EventPayloads-Push
291-
292-
Services
293-
https://confluence.atlassian.com/bitbucket/post-service-management-223216518.html
294-
"""
295-
if request.method == 'POST':
296-
try:
297-
if request.META['CONTENT_TYPE'] == 'application/x-www-form-urlencoded':
298-
data = json.loads(request.POST.get('payload'))
299-
else:
300-
data = json.loads(request.body)
301-
302-
version = 2 if request.META.get('HTTP_USER_AGENT') == 'Bitbucket-Webhooks/2.0' else 1 # yapf: disabled # noqa
303-
if version == 1:
304-
branches = [
305-
commit.get('branch', '') for commit in data['commits']
306-
]
307-
repository = data['repository']
308-
if not repository['absolute_url']:
309-
return HttpResponse('Invalid request', status=400)
310-
search_url = 'bitbucket.org{}'.format(
311-
repository['absolute_url'].rstrip('/'),
312-
)
313-
elif version == 2:
314-
changes = data['push']['changes']
315-
branches = [change['new']['name'] for change in changes]
316-
if not data['repository']['full_name']:
317-
return HttpResponse('Invalid request', status=400)
318-
search_url = 'bitbucket.org/{}'.format(
319-
data['repository']['full_name'],
320-
)
321-
except (TypeError, ValueError, KeyError):
322-
log.exception('Invalid Bitbucket webhook payload')
323-
return HttpResponse('Invalid request', status=400)
324-
325-
log.info(
326-
'Bitbucket webhook search: url=%s branches=%s',
327-
search_url,
328-
branches,
329-
)
330-
log.debug('Bitbucket webhook payload:\n\n%s\n\n', data)
331-
332-
projects = get_project_from_url(search_url)
333-
if projects and branches:
334-
return _build_url(search_url, projects, branches)
335-
336-
if not branches:
337-
log.info(
338-
'Commit/branch not found url=%s branches=%s',
339-
search_url,
340-
branches,
341-
)
342-
return HttpResponseNotFound('Commit/branch not found')
343-
344-
log.info('Project match not found: url=%s', search_url)
345-
return HttpResponseNotFound('Project match not found')
346-
return HttpResponse('Method not allowed, POST is required', status=405)
347-
348-
349-
@csrf_exempt
350-
def generic_build(request, project_id_or_slug=None):
351-
"""
352-
Generic webhook build endpoint.
353-
354-
.. warning:: **DEPRECATED**
355-
356-
Use :py:class:`readthedocs.api.v2.views.integrations.GenericWebhookView`
357-
instead of this view function
358-
"""
359-
try:
360-
project = Project.objects.get(pk=project_id_or_slug)
361-
# Allow slugs too
362-
except (Project.DoesNotExist, ValueError):
363-
try:
364-
project = Project.objects.get(slug=project_id_or_slug)
365-
except (Project.DoesNotExist, ValueError):
366-
log.exception(
367-
'(Incoming Generic Build) Repo not found: %s',
368-
project_id_or_slug,
369-
)
370-
return HttpResponseNotFound(
371-
'Repo not found: %s' % project_id_or_slug,
372-
)
373-
# This endpoint doesn't require authorization, we shouldn't allow builds to
374-
# be triggered from this any longer. Deprecation plan is to selectively
375-
# allow access to this endpoint for now.
376-
if not _allow_deprecated_webhook(project):
377-
return HttpResponse('This API endpoint is deprecated', status=403)
378-
if request.method == 'POST':
379-
slug = request.POST.get('version_slug', project.default_version)
380-
log.info(
381-
'(Incoming Generic Build) %s [%s]',
382-
project.slug,
383-
slug,
384-
)
385-
_build_version(project, slug)
386-
else:
387-
return HttpResponse('You must POST to this resource.')
388-
return redirect('builds_project_list', project.slug)

0 commit comments

Comments
 (0)