Skip to content

Commit 5fb2e4e

Browse files
committed
Only build external version from webhook payload
1 parent c74d294 commit 5fb2e4e

File tree

10 files changed

+77
-98
lines changed

10 files changed

+77
-98
lines changed

readthedocs/api/v2/utils.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
STABLE,
1414
STABLE_VERBOSE_NAME,
1515
TAG,
16-
EXTERNAL,
1716
)
18-
from readthedocs.core.utils import trigger_build
1917
from readthedocs.builds.models import Version
2018

2119

@@ -79,15 +77,6 @@ def sync_versions(project, versions, type): # pylint: disable=redefined-builtin
7977
version_name,
8078
version_id,
8179
)
82-
elif type == EXTERNAL:
83-
created_version = Version.objects.create(
84-
project=project,
85-
type=type,
86-
identifier=version_id,
87-
verbose_name=version_name,
88-
)
89-
added.add(created_version.slug)
90-
9180
else:
9281
# New Version
9382
created_version = Version.objects.create(
@@ -146,9 +135,6 @@ def delete_versions(project, version_data):
146135
versions_tags = [
147136
version['verbose_name'] for version in version_data.get('tags', [])
148137
]
149-
external_versions = [
150-
version['verbose_name'] for version in version_data.get('external_branches', [])
151-
]
152138
versions_branches = [
153139
version['identifier'] for version in version_data.get('branches', [])
154140
]
@@ -161,10 +147,6 @@ def delete_versions(project, version_data):
161147
type=BRANCH,
162148
identifier__in=versions_branches,
163149
)
164-
to_delete_qs = to_delete_qs.exclude(
165-
type=EXTERNAL,
166-
verbose_name__in=external_versions,
167-
)
168150
to_delete_qs = to_delete_qs.exclude(uploaded=True)
169151
to_delete_qs = to_delete_qs.exclude(active=True)
170152
to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)
@@ -194,21 +176,6 @@ def run_automation_rules(project, versions_slug):
194176
rule.run(version)
195177

196178

197-
def trigger_external_build(project, version_list):
198-
"""
199-
Trigger Builds for all external versions provided.
200-
201-
The rules are sorted by priority.
202-
203-
"""
204-
for version_slug in version_list:
205-
version = project.versions(manager=EXTERNAL).get(slug=version_slug)
206-
version.active = True
207-
version.save()
208-
209-
trigger_build(project=project, version=version)
210-
211-
212179
class RemoteOrganizationPagination(PageNumberPagination):
213180
page_size = 25
214181

readthedocs/api/v2/views/integrations.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
webhook_github,
2020
webhook_gitlab,
2121
)
22-
from readthedocs.core.views.hooks import build_branches, sync_versions
22+
from readthedocs.core.views.hooks import (
23+
build_branches,
24+
sync_versions,
25+
get_or_create_external_version,
26+
)
2327
from readthedocs.integrations.models import HttpExchange, Integration
2428
from readthedocs.projects.models import Project
2529

@@ -30,7 +34,7 @@
3034
GITHUB_SIGNATURE_HEADER = 'HTTP_X_HUB_SIGNATURE'
3135
GITHUB_PUSH = 'push'
3236
GITHUB_PULL_REQUEST = 'pull_request'
33-
GITHUB_PULL_REQUEST_OPEN = 'open'
37+
GITHUB_PULL_REQUEST_OPEN = 'opened'
3438
GITHUB_PULL_REQUEST_SYNC = 'synchronize'
3539
GITHUB_CREATE = 'create'
3640
GITHUB_DELETE = 'delete'
@@ -113,6 +117,10 @@ def handle_webhook(self):
113117
"""Handle webhook payload."""
114118
raise NotImplementedError
115119

120+
def get_external_version_data(self):
121+
"""Get External Version data from payload."""
122+
raise NotImplementedError
123+
116124
def is_payload_valid(self):
117125
"""Validates the webhook's payload using the integration's secret."""
118126
return False
@@ -221,6 +229,13 @@ def get_data(self):
221229
pass
222230
return super().get_data()
223231

232+
def get_external_version_data(self):
233+
"""Get Commit Sha and pull request number from payload"""
234+
identifier = self.data['pull_request']['head']['sha']
235+
verbose_name = str(self.data['number'])
236+
237+
return identifier, verbose_name
238+
224239
def is_payload_valid(self):
225240
"""
226241
GitHub use a HMAC hexdigest hash to sign the payload.
@@ -275,8 +290,19 @@ def handle_webhook(self):
275290
if event in (GITHUB_CREATE, GITHUB_DELETE):
276291
return self.sync_versions(self.project)
277292

278-
if event == GITHUB_PULL_REQUEST:
279-
return self.sync_versions(self.project)
293+
if (
294+
event == GITHUB_PULL_REQUEST and
295+
self.data['action'] in [GITHUB_PULL_REQUEST_OPEN, GITHUB_PULL_REQUEST_SYNC]
296+
):
297+
try:
298+
identifier, verbose_name = self.get_external_version_data()
299+
external_version = get_or_create_external_version(
300+
self.project, identifier, verbose_name
301+
)
302+
return self.get_response_push(self.project, external_version.verbose_name)
303+
304+
except KeyError:
305+
raise ParseError('Parameters "sha" and "number" are required')
280306

281307
return None
282308

readthedocs/api/v2/views/model_views.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from rest_framework.renderers import BaseRenderer, JSONRenderer
1111
from rest_framework.response import Response
1212

13-
from readthedocs.builds.constants import BRANCH, TAG, INTERNAL, EXTERNAL
13+
from readthedocs.builds.constants import BRANCH, TAG
1414
from readthedocs.builds.models import Build, BuildCommandResult, Version
1515
from readthedocs.core.utils import trigger_build
1616
from readthedocs.core.utils.extend import SettingsOverrideObject
@@ -130,7 +130,7 @@ def active_versions(self, request, **kwargs):
130130
Project.objects.api(request.user),
131131
pk=kwargs['pk'],
132132
)
133-
versions = project.versions(manager=INTERNAL).filter(active=True)
133+
versions = project.versions.filter(active=True)
134134
return Response({
135135
'versions': VersionSerializer(versions, many=True).data,
136136
})
@@ -189,7 +189,6 @@ def sync_versions(self, request, **kwargs): # noqa: D205
189189
# Update All Versions
190190
data = request.data
191191
added_versions = set()
192-
added_external_versions = set()
193192
if 'tags' in data:
194193
ret_set = api_utils.sync_versions(
195194
project=project,
@@ -204,15 +203,6 @@ def sync_versions(self, request, **kwargs): # noqa: D205
204203
type=BRANCH,
205204
)
206205
added_versions.update(ret_set)
207-
208-
if 'external_branches' in data:
209-
ret_set = api_utils.sync_versions(
210-
project=project,
211-
versions=data['external_branches'],
212-
type=EXTERNAL,
213-
)
214-
added_external_versions.update(ret_set)
215-
216206
deleted_versions = api_utils.delete_versions(project, data)
217207
except Exception as e:
218208
log.exception('Sync Versions Error')
@@ -223,13 +213,11 @@ def sync_versions(self, request, **kwargs): # noqa: D205
223213
status=status.HTTP_400_BAD_REQUEST,
224214
)
225215

226-
all_added_versions = added_versions | added_external_versions
227-
228216
try:
229217
# The order of added_versions isn't deterministic.
230218
# We don't track the commit time or any other metadata.
231219
# We usually have one version added per webhook.
232-
api_utils.run_automation_rules(project, all_added_versions)
220+
api_utils.run_automation_rules(project, added_versions)
233221
except Exception:
234222
# Don't interrupt the request if something goes wrong
235223
# in the automation rules.
@@ -238,12 +226,6 @@ def sync_versions(self, request, **kwargs): # noqa: D205
238226
project.slug, added_versions
239227
)
240228

241-
if added_external_versions:
242-
api_utils.trigger_external_build(
243-
project=project,
244-
version_list=added_external_versions
245-
)
246-
247229
# TODO: move this to an automation rule
248230
promoted_version = project.update_stable_version()
249231
new_stable = project.get_stable_version()
@@ -268,7 +250,7 @@ def sync_versions(self, request, **kwargs): # noqa: D205
268250
trigger_build(project=project, version=promoted_version)
269251

270252
return Response({
271-
'added_versions': all_added_versions,
253+
'added_versions': added_versions,
272254
'deleted_versions': deleted_versions,
273255
})
274256

readthedocs/core/views/hooks.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import logging
44

5+
from readthedocs.builds.constants import EXTERNAL
6+
from readthedocs.builds.models import Version
57
from readthedocs.core.utils import trigger_build
68
from readthedocs.projects.tasks import sync_repository_task
79

@@ -88,3 +90,21 @@ def sync_versions(project):
8890
except Exception:
8991
log.exception('Unknown sync versions exception')
9092
return None
93+
94+
95+
def get_or_create_external_version(project, identifier, verbose_name):
96+
external_version = project.versions(manager=EXTERNAL).filter(verbose_name=verbose_name).first()
97+
if external_version:
98+
if external_version.identifier != identifier:
99+
external_version.identifier = identifier
100+
external_version.save()
101+
else:
102+
created_external_version = Version.objects.create(
103+
project=project,
104+
type=EXTERNAL,
105+
identifier=identifier,
106+
verbose_name=verbose_name,
107+
active=True
108+
)
109+
return created_external_version
110+
return external_version

readthedocs/projects/constants.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,4 @@
334334
'{action}/{version}{docroot}{path}{source_suffix}'
335335
)
336336

337-
DEFAULT_GIT_PATTERN = 'refs/heads/*:refs/remotes/origin/*'
338-
# https://help.github.com/en/articles/checking-out-pull-requests-locally#modifying-an-inactive-pull-request-locally
339-
GITHUB_GIT_PATTERN = 'refs/pull/*/head:refs/remotes/origin/external/*'
337+
GITHUB_GIT_PATTERN = 'pull/{id}/head:external-{id}'

readthedocs/projects/tasks.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def sync_repo(self):
138138
}
139139
)
140140
version_repo = self.get_vcs_repo()
141-
version_repo.update()
141+
version_repo.update(version=self.version)
142142
self.sync_versions(version_repo)
143143
version_repo.checkout(self.version.identifier)
144144
finally:
@@ -165,12 +165,6 @@ def sync_versions(self, version_repo):
165165
'verbose_name': v.verbose_name,
166166
} for v in version_repo.branches]
167167

168-
if version_repo.supports_external_branches:
169-
version_post_data['external_branches'] = [{
170-
'identifier': v.identifier,
171-
'verbose_name': v.verbose_name,
172-
} for v in version_repo.external_branches]
173-
174168
self.validate_duplicate_reserved_versions(version_post_data)
175169

176170
try:

readthedocs/vcs_support/backends/bzr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Backend(BaseVCS):
1717
supports_tags = True
1818
fallback_branch = ''
1919

20-
def update(self):
20+
def update(self, version=None): # pylint: disable=arguments-differ
2121
super().update()
2222
retcode = self.run('bzr', 'status', record=False)[0]
2323
if retcode == 0:

readthedocs/vcs_support/backends/git.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
from django.core.exceptions import ValidationError
1111
from git.exc import BadName, InvalidGitRepositoryError
1212

13+
from readthedocs.builds.constants import EXTERNAL
1314
from readthedocs.config import ALL
14-
from readthedocs.projects.constants import GITHUB_GIT_PATTERN, DEFAULT_GIT_PATTERN
15+
from readthedocs.projects.constants import GITHUB_GIT_PATTERN
1516
from readthedocs.projects.exceptions import RepositoryError
1617
from readthedocs.projects.validators import validate_submodule_url
1718
from readthedocs.vcs_support.base import BaseVCS, VCSVersion
@@ -52,16 +53,21 @@ def _get_clone_url(self):
5253
def set_remote_url(self, url):
5354
return self.run('git', 'remote', 'set-url', 'origin', url)
5455

55-
def update(self):
56+
def update(self, version=None): # pylint: disable=arguments-differ
5657
"""Clone or update the repository."""
5758
super().update()
5859
if self.repo_exists():
5960
self.set_remote_url(self.repo_url)
60-
else:
61-
self.make_clean_working_dir()
62-
self.clone()
61+
# A fetch is always required to get external versions properly
62+
if version and version.type == EXTERNAL:
63+
return self.fetch(version.verbose_name)
64+
return self.fetch()
65+
self.make_clean_working_dir()
6366
# A fetch is always required to get external versions properly
64-
self.fetch()
67+
if version and version.type == EXTERNAL:
68+
self.clone()
69+
return self.fetch(version.verbose_name)
70+
return self.clone()
6571

6672
def repo_exists(self):
6773
try:
@@ -147,11 +153,15 @@ def use_shallow_clone(self):
147153
from readthedocs.projects.models import Feature
148154
return not self.project.has_feature(Feature.DONT_SHALLOW_CLONE)
149155

150-
def fetch(self):
156+
def fetch(self, verbose_name=None):
151157
cmd = ['git', 'fetch', 'origin',
152-
DEFAULT_GIT_PATTERN, GITHUB_GIT_PATTERN,
153158
'--tags', '--prune', '--prune-tags']
154159

160+
if verbose_name and 'github.com' in self.repo_url:
161+
cmd.append(
162+
GITHUB_GIT_PATTERN.format(id=verbose_name)
163+
)
164+
155165
if self.use_shallow_clone():
156166
cmd.extend(['--depth', str(self.repo_depth)])
157167

@@ -222,24 +232,6 @@ def branches(self):
222232
versions.append(VCSVersion(self, str(branch), verbose_name))
223233
return versions
224234

225-
@property
226-
def external_branches(self):
227-
repo = git.Repo(self.working_dir)
228-
versions = []
229-
branches = []
230-
231-
# ``repo.remotes.origin.refs`` returns remote branches
232-
if repo.remotes:
233-
branches += repo.remotes.origin.refs
234-
235-
for branch in branches:
236-
verbose_name = branch.name
237-
if verbose_name.startswith('origin/external/'):
238-
verbose_name = verbose_name.replace('origin/', '')
239-
versions.append(VCSVersion(self, str(branch.commit), verbose_name))
240-
continue
241-
return versions
242-
243235
@property
244236
def commit(self):
245237
if self.repo_exists():

readthedocs/vcs_support/backends/hg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Backend(BaseVCS):
1313
supports_branches = True
1414
fallback_branch = 'default'
1515

16-
def update(self):
16+
def update(self, version=None): # pylint: disable=arguments-differ
1717
super().update()
1818
retcode = self.run('hg', 'status', record=False)[0]
1919
if retcode == 0:

readthedocs/vcs_support/backends/svn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, *args, **kwargs):
2727
else:
2828
self.base_url = self.repo_url
2929

30-
def update(self):
30+
def update(self, version=None): # pylint: disable=arguments-differ
3131
super().update()
3232
# For some reason `svn status` gives me retcode 0 in non-svn
3333
# directories that's why I use `svn info` here.

0 commit comments

Comments
 (0)