Skip to content

Commit c79f9f1

Browse files
committed
Abstraction for UpdateDocsTask and SyncRepositoryTask
These are two separated tasks that share some code by inheriting from SyncRepositoryMixin. The final goal (as ``core.views.hooks._build_url`` is *DEPRECATED*) is to finally remove the SyncRepositoryTask and merge that code into UpdateDocsTask.
1 parent 7f6c098 commit c79f9f1

File tree

5 files changed

+142
-102
lines changed

5 files changed

+142
-102
lines changed

readthedocs/core/management/commands/pull.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ def handle(self, *args, **options):
2020
if args:
2121
for slug in args:
2222
version = utils.version_from_slug(slug, LATEST)
23-
tasks.UpdateDocsTask().run(
24-
version.project,
23+
tasks.SyncRepositoryTask().run(
2524
version.pk,
26-
sync_only=True,
2725
)

readthedocs/core/views/hooks.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from readthedocs.builds.constants import LATEST
1313
from readthedocs.projects import constants
1414
from readthedocs.projects.models import Project, Feature
15-
from readthedocs.projects.tasks import UpdateDocsTask
15+
from readthedocs.projects.tasks import SyncRepositoryTask
1616

1717
import logging
1818

@@ -123,15 +123,11 @@ def _build_url(url, projects, branches):
123123
for project in projects:
124124
(built, not_building) = build_branches(project, branches)
125125
if not built:
126-
# Call UpdateDocsTask with ``sync_only`` to update tag/branch info
126+
# Call SyncRepositoryTask to update tag/branch info
127127
version = project.versions.get(slug=LATEST)
128-
update_docs = UpdateDocsTask()
129-
update_docs.apply_async(
130-
args=(project.pk,),
131-
kwargs={
132-
'version_pk': version.pk,
133-
'sync_only': True,
134-
},
128+
sync_repository = SyncRepositoryTask()
129+
sync_repository.apply_async(
130+
args=(version.pk,),
135131
)
136132
msg = '(URL Build) Syncing versions for %s' % project.slug
137133
log.info(msg)

readthedocs/projects/apps.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ProjectsConfig(AppConfig):
77
name = 'readthedocs.projects'
88

99
def ready(self):
10-
from readthedocs.projects.tasks import UpdateDocsTask
10+
from readthedocs.projects import tasks
1111
from readthedocs.worker import app
12-
app.tasks.register(UpdateDocsTask)
12+
app.tasks.register(tasks.SyncRepositoryTask)
13+
app.tasks.register(tasks.UpdateDocsTask)

readthedocs/projects/tasks.py

Lines changed: 127 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,132 @@
6363
HTML_ONLY = getattr(settings, 'HTML_ONLY_PROJECTS', ())
6464

6565

66-
class UpdateDocsTask(Task):
66+
class SyncRepositoryMixin(object):
67+
68+
"""
69+
Mixin that handles the VCS sync/update.
70+
"""
71+
72+
@staticmethod
73+
def get_version(project=None, version_pk=None):
74+
"""
75+
Retrieve version data from the API.
76+
77+
:param project: project object to sync
78+
:type project: projects.models.Project
79+
:param version_pk: version pk to sync
80+
:type version_pk: int
81+
:returns: a data-complete version object
82+
:rtype: builds.models.APIVersion
83+
"""
84+
assert (project or version_pk), 'project or version_pk is needed'
85+
if version_pk:
86+
version_data = api_v2.version(version_pk).get()
87+
else:
88+
version_data = (api_v2
89+
.version(project.slug)
90+
.get(slug=LATEST)['objects'][0])
91+
return APIVersion(**version_data)
92+
93+
def sync_repo(self):
94+
"""
95+
Checkout/update the project's repository and hit ``sync_versions`` API.
96+
"""
97+
# Make Dirs
98+
if not os.path.exists(self.project.doc_path):
99+
os.makedirs(self.project.doc_path)
100+
101+
if not self.project.vcs_repo():
102+
raise RepositoryError(
103+
_('Repository type "{repo_type}" unknown').format(
104+
repo_type=self.project.repo_type,
105+
),
106+
)
107+
108+
with self.project.repo_nonblockinglock(
109+
version=self.version,
110+
max_lock_age=getattr(settings, 'REPO_LOCK_SECONDS', 30)):
111+
112+
# Get the actual code on disk
113+
try:
114+
before_vcs.send(sender=self.version)
115+
self._log(
116+
'Checking out version {slug}: {identifier}'.format(
117+
slug=self.version.slug,
118+
identifier=self.version.identifier,
119+
),
120+
)
121+
version_repo = self.project.vcs_repo(
122+
self.version.slug,
123+
# When called from ``SyncRepositoryTask.run`` we don't have
124+
# a ``setup_env`` so we use just ``None`` and commands won't
125+
# be recorded
126+
getattr(self, 'setup_env', None),
127+
)
128+
version_repo.checkout(self.version.identifier)
129+
finally:
130+
after_vcs.send(sender=self.version)
131+
132+
# Update tags/version
133+
version_post_data = {'repo': version_repo.repo_url}
134+
135+
if version_repo.supports_tags:
136+
version_post_data['tags'] = [
137+
{'identifier': v.identifier,
138+
'verbose_name': v.verbose_name,
139+
} for v in version_repo.tags
140+
]
141+
142+
if version_repo.supports_branches:
143+
version_post_data['branches'] = [
144+
{'identifier': v.identifier,
145+
'verbose_name': v.verbose_name,
146+
} for v in version_repo.branches
147+
]
148+
149+
try:
150+
# Hit the API ``sync_versions`` which may trigger a new build
151+
# for the stable version
152+
api_v2.project(self.project.pk).sync_versions.post(version_post_data)
153+
except HttpClientError:
154+
log.exception('Sync Versions Exception')
155+
except Exception:
156+
log.exception('Unknown Sync Versions Exception')
157+
158+
159+
class SyncRepositoryTask(SyncRepositoryMixin, Task):
160+
161+
"""
162+
Entry point to synchronize the VCS documentation.
163+
"""
164+
165+
max_retries = 5
166+
default_retry_delay = (7 * 60)
167+
name = __name__ + '.sync_repository'
168+
169+
def run(self, version_pk):
170+
"""
171+
Run the VCS synchronization.
172+
173+
:param version_pk: version pk to sync
174+
:type version_pk: int
175+
:returns: whether or not the task ended successfully
176+
:rtype: bool
177+
"""
178+
try:
179+
self.version = self.get_version(version_pk=version_pk)
180+
self.project = self.version.project
181+
self.sync_repo()
182+
return True
183+
# Catch unhandled errors when syncing
184+
except Exception:
185+
log.exception(
186+
'An unhandled exception was raised during VCS syncing',
187+
)
188+
return False
189+
190+
191+
class UpdateDocsTask(SyncRepositoryMixin, Task):
67192

68193
"""
69194
The main entry point for updating documentation.
@@ -106,8 +231,7 @@ def _log(self, msg):
106231

107232
# pylint: disable=arguments-differ
108233
def run(self, pk, version_pk=None, build_pk=None, record=True,
109-
docker=False, search=True, force=False, localmedia=True,
110-
sync_only=False, **__):
234+
docker=False, search=True, force=False, localmedia=True, **__):
111235
"""
112236
Run a documentation sync or sync n' build.
113237
@@ -145,10 +269,6 @@ def run(self, pk, version_pk=None, build_pk=None, record=True,
145269
self.build_force = force
146270
self.config = None
147271

148-
if sync_only:
149-
self.sync_repo()
150-
return True
151-
152272
setup_successful = self.run_setup(record=record)
153273
if not setup_successful:
154274
return False
@@ -301,17 +421,6 @@ def get_project(project_pk):
301421
project_data = api_v2.project(project_pk).get()
302422
return APIProject(**project_data)
303423

304-
@staticmethod
305-
def get_version(project, version_pk):
306-
"""Ensure we're using a sane version."""
307-
if version_pk:
308-
version_data = api_v2.version(version_pk).get()
309-
else:
310-
version_data = (api_v2
311-
.version(project.slug)
312-
.get(slug=LATEST)['objects'][0])
313-
return APIVersion(**version_data)
314-
315424
@staticmethod
316425
def get_build(build_pk):
317426
"""
@@ -342,70 +451,6 @@ def setup_vcs(self):
342451
if commit:
343452
self.build['commit'] = commit
344453

345-
def sync_repo(self):
346-
"""
347-
Checkout/update the project's repository and hit ``sync_versions`` API.
348-
"""
349-
# Make Dirs
350-
if not os.path.exists(self.project.doc_path):
351-
os.makedirs(self.project.doc_path)
352-
353-
if not self.project.vcs_repo():
354-
raise RepositoryError(
355-
_('Repository type "{repo_type}" unknown').format(
356-
repo_type=self.project.repo_type,
357-
),
358-
)
359-
360-
with self.project.repo_nonblockinglock(
361-
version=self.version,
362-
max_lock_age=getattr(settings, 'REPO_LOCK_SECONDS', 30)):
363-
364-
# Get the actual code on disk
365-
try:
366-
before_vcs.send(sender=self.version)
367-
self._log(
368-
'Checking out version {slug}: {identifier}'.format(
369-
slug=self.version.slug,
370-
identifier=self.version.identifier,
371-
),
372-
)
373-
version_repo = self.project.vcs_repo(
374-
self.version.slug,
375-
# When ``sync_only`` we don't a setup_env
376-
getattr(self, 'setup_env', None),
377-
)
378-
version_repo.checkout(self.version.identifier)
379-
finally:
380-
after_vcs.send(sender=self.version)
381-
382-
# Update tags/version
383-
version_post_data = {'repo': version_repo.repo_url}
384-
385-
if version_repo.supports_tags:
386-
version_post_data['tags'] = [
387-
{'identifier': v.identifier,
388-
'verbose_name': v.verbose_name,
389-
} for v in version_repo.tags
390-
]
391-
392-
if version_repo.supports_branches:
393-
version_post_data['branches'] = [
394-
{'identifier': v.identifier,
395-
'verbose_name': v.verbose_name,
396-
} for v in version_repo.branches
397-
]
398-
399-
try:
400-
# Hit the API ``sync_versions`` which may trigger a new build
401-
# for the stable version
402-
api_v2.project(self.project.pk).sync_versions.post(version_post_data)
403-
except HttpClientError:
404-
log.exception('Sync Versions Exception')
405-
except Exception:
406-
log.exception('Unknown Sync Versions Exception')
407-
408-
409454
def get_env_vars(self):
410455
"""Get bash environment variables used for all builder commands."""
411456
env = {

readthedocs/rtd_tests/tests/test_celery.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django_dynamic_fixture import get
99
from mock import patch, MagicMock
1010

11-
from readthedocs.builds.constants import BUILD_STATE_INSTALLING, BUILD_STATE_FINISHED
11+
from readthedocs.builds.constants import BUILD_STATE_INSTALLING, BUILD_STATE_FINISHED, LATEST
1212
from readthedocs.builds.models import Build
1313
from readthedocs.projects.models import Project
1414
from readthedocs.projects import tasks
@@ -115,11 +115,11 @@ def test_update_docs_unexpected_build_exception(self, mock_build_docs):
115115
intersphinx=False)
116116
self.assertTrue(result.successful())
117117

118-
def test_update_imported_doc(self):
118+
def test_sync_repository(self):
119+
version = self.project.versions.get(slug=LATEST)
119120
with mock_api(self.repo):
120-
update_docs = tasks.UpdateDocsTask()
121-
result = update_docs.apply_async(
122-
args=(self.project.pk,),
123-
kwargs={'sync_only': True},
121+
sync_repository = tasks.SyncRepositoryTask()
122+
result = sync_repository.apply_async(
123+
args=(version.pk,),
124124
)
125125
self.assertTrue(result.successful())

0 commit comments

Comments
 (0)