-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
hooks.py
221 lines (186 loc) · 6.74 KB
/
hooks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
"""Views pertaining to builds."""
import structlog
from readthedocs.api.v2.models import BuildAPIKey
from readthedocs.builds.constants import (
EXTERNAL,
EXTERNAL_VERSION_STATE_CLOSED,
EXTERNAL_VERSION_STATE_OPEN,
)
from readthedocs.core.utils import trigger_build
from readthedocs.projects.models import Feature, Project
from readthedocs.projects.tasks.builds import sync_repository_task
log = structlog.get_logger(__name__)
def _build_version(project, slug, already_built=()):
"""
Where we actually trigger builds for a project and slug.
All webhook logic should route here to call ``trigger_build``.
"""
if not project.has_valid_webhook:
project.has_valid_webhook = True
project.save()
# Previously we were building the latest version (inactive or active)
# when building the default version,
# some users may have relied on this to update the version list #4450
version = project.versions.filter(active=True, slug=slug).first()
if version and slug not in already_built:
log.info(
"Building.",
project_slug=project.slug,
version_slug=version.slug,
)
trigger_build(project=project, version=version)
return slug
log.info("Not building.", version_slug=slug)
return None
def build_branches(project, branch_list):
"""
Build the branches for a specific project.
Returns:
to_build - a list of branches that were built
not_building - a list of branches that we won't build
"""
to_build = set()
not_building = set()
for branch in branch_list:
versions = project.versions_from_branch_name(branch)
for version in versions:
log.debug(
"Processing.",
project_slug=project.slug,
version_slug=version.slug,
)
ret = _build_version(project, version.slug, already_built=to_build)
if ret:
to_build.add(ret)
else:
not_building.add(version.slug)
return (to_build, not_building)
def trigger_sync_versions(project):
"""
Sync the versions of a repo using its latest version.
This doesn't register a new build,
but clones the repo and syncs the versions.
Due that `sync_repository_task` is bound to a version,
we always pass the default version.
:returns: The version slug that was used to trigger the clone.
:rtype: str or ``None`` if failed
"""
if not Project.objects.is_active(project):
log.warning(
"Sync not triggered because project is not active.",
project_slug=project.slug,
)
return None
try:
version_identifier = project.get_default_branch()
version = project.versions.filter(
identifier=version_identifier,
).first()
if not version:
log.info(
"Unable to sync from version.", version_identifier=version_identifier
)
return None
if project.has_feature(Feature.SKIP_SYNC_VERSIONS):
log.info("Skipping sync versions for project.", project_slug=project.slug)
return None
options = {}
if project.build_queue:
# respect the queue for this project
options["queue"] = project.build_queue
_, build_api_key = BuildAPIKey.objects.create_key(project=project)
log.debug(
"Triggering sync repository.",
project_slug=version.project.slug,
version_slug=version.slug,
)
sync_repository_task.apply_async(
args=[version.pk],
kwargs={"build_api_key": build_api_key},
**options,
)
return version.slug
except Exception:
log.exception("Unknown sync versions exception")
return None
def get_or_create_external_version(project, version_data):
"""
Get or create version using the ``commit`` as identifier, and PR id as ``verbose_name``.
if external version does not exist create an external version
:param project: Project instance
:param version_data: A :py:class:`readthedocs.api.v2.views.integrations.ExternalVersionData`
instance.
:returns: External version.
:rtype: Version
"""
external_version, created = project.versions.get_or_create(
verbose_name=version_data.id,
type=EXTERNAL,
defaults={
"identifier": version_data.commit,
"active": True,
"state": EXTERNAL_VERSION_STATE_OPEN,
},
)
if created:
log.info(
"External version created.",
project_slug=project.slug,
version_slug=external_version.slug,
)
else:
# Identifier will change if there is a new commit to the Pull/Merge Request.
external_version.identifier = version_data.commit
# If the PR was previously closed it was marked as closed
external_version.state = EXTERNAL_VERSION_STATE_OPEN
external_version.save()
log.info(
"External version updated.",
project_slug=project.slug,
version_slug=external_version.slug,
)
return external_version
def close_external_version(project, version_data):
"""
Close external versions using `identifier` and `verbose_name`.
We mark the version's state as `closed` so another celery task will remove
it after some days. If external version does not exist then returns `None`.
:param project: Project instance
:param version_data: A :py:class:`readthedocs.api.v2.views.integrations.ExternalVersionData`
instance.
:rtype: str
"""
external_version = (
project.versions(manager=EXTERNAL)
.filter(
verbose_name=version_data.id,
identifier=version_data.commit,
)
.first()
)
if external_version:
external_version.state = EXTERNAL_VERSION_STATE_CLOSED
external_version.save()
log.info(
"External version marked as closed.",
project_slug=project.slug,
version_slug=external_version.slug,
)
return external_version.verbose_name
return None
def build_external_version(project, version):
"""
Where we actually trigger builds for external versions.
All pull/merge request webhook logic should route here to call ``trigger_build``.
"""
if not project.has_valid_webhook:
project.has_valid_webhook = True
project.save()
# Build External version
log.info(
"Building external version",
project_slug=project.slug,
version_slug=version.slug,
)
trigger_build(project=project, version=version, commit=version.identifier)
return version.verbose_name