Skip to content

Commit 9ff43df

Browse files
authored
Merge pull request #5985 from saadmk11/build-status-bug-fix
Fix github build status reporting bug
2 parents f20e659 + 8c70f2a commit 9ff43df

File tree

12 files changed

+116
-51
lines changed

12 files changed

+116
-51
lines changed

readthedocs/api/v2/views/integrations.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,9 @@ def get_external_version_response(self, project):
219219
project, identifier, verbose_name
220220
)
221221
# returns external version verbose_name (pull/merge request number)
222-
to_build = build_external_version(project, external_version)
222+
to_build = build_external_version(
223+
project=project, version=external_version, commit=identifier
224+
)
223225

224226
return {
225227
'build_triggered': True,

readthedocs/core/utils/__init__.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def broadcast(type, task, args, kwargs=None, callback=None): # pylint: disable=
6060
def prepare_build(
6161
project,
6262
version=None,
63+
commit=None,
6364
record=True,
6465
force=False,
6566
immutable=True,
@@ -72,6 +73,7 @@ def prepare_build(
7273
7374
:param project: project's documentation to be built
7475
:param version: version of the project to be built. Default: ``project.get_default_version()``
76+
:param commit: commit sha of the version required for sending build status reports
7577
:param record: whether or not record the build in a new Build object
7678
:param force: build the HTML documentation even if the files haven't changed
7779
:param immutable: whether or not create an immutable Celery signature
@@ -102,6 +104,7 @@ def prepare_build(
102104
kwargs = {
103105
'record': record,
104106
'force': force,
107+
'commit': commit,
105108
}
106109

107110
if record:
@@ -111,6 +114,7 @@ def prepare_build(
111114
type='html',
112115
state=BUILD_STATE_TRIGGERED,
113116
success=True,
117+
commit=commit
114118
)
115119
kwargs['build_pk'] = build.pk
116120

@@ -131,9 +135,12 @@ def prepare_build(
131135
options['soft_time_limit'] = time_limit
132136
options['time_limit'] = int(time_limit * 1.2)
133137

134-
if build:
138+
if build and commit:
135139
# Send pending Build Status using Git Status API for External Builds.
136-
send_external_build_status(version=version, build_pk=build.id, status=BUILD_STATUS_PENDING)
140+
send_external_build_status(
141+
version_type=version.type, build_pk=build.id,
142+
commit=commit, status=BUILD_STATUS_PENDING
143+
)
137144

138145
return (
139146
update_docs_task.signature(
@@ -146,7 +153,7 @@ def prepare_build(
146153
)
147154

148155

149-
def trigger_build(project, version=None, record=True, force=False):
156+
def trigger_build(project, version=None, commit=None, record=True, force=False):
150157
"""
151158
Trigger a Build.
152159
@@ -155,6 +162,7 @@ def trigger_build(project, version=None, record=True, force=False):
155162
156163
:param project: project's documentation to be built
157164
:param version: version of the project to be built. Default: ``latest``
165+
:param commit: commit sha of the version required for sending build status reports
158166
:param record: whether or not record the build in a new Build object
159167
:param force: build the HTML documentation even if the files haven't changed
160168
:returns: Celery AsyncResult promise and Build instance
@@ -163,6 +171,7 @@ def trigger_build(project, version=None, record=True, force=False):
163171
update_docs_task, build = prepare_build(
164172
project,
165173
version,
174+
commit,
166175
record,
167176
force,
168177
immutable=True,

readthedocs/core/views/hooks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def delete_external_version(project, identifier, verbose_name):
157157
return None
158158

159159

160-
def build_external_version(project, version):
160+
def build_external_version(project, version, commit):
161161
"""
162162
Where we actually trigger builds for external versions.
163163
@@ -173,6 +173,6 @@ def build_external_version(project, version):
173173
project.slug,
174174
version.slug,
175175
)
176-
trigger_build(project=project, version=version, force=True)
176+
trigger_build(project=project, version=version, commit=commit, force=True)
177177

178178
return version.verbose_name

readthedocs/oauth/services/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,14 @@ def update_webhook(self, project, integration):
250250
"""
251251
raise NotImplementedError
252252

253-
def send_build_status(self, build, state):
253+
def send_build_status(self, build, commit, state):
254254
"""
255255
Create commit status for project.
256256
257257
:param build: Build to set up commit status for
258258
:type build: Build
259+
:param commit: commit sha of the pull/merge request
260+
:type commit: str
259261
:param state: build state failure, pending, or success.
260262
:type state: str
261263
:returns: boolean based on commit status creation was successful or not.

readthedocs/oauth/services/github.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,21 +315,22 @@ def update_webhook(self, project, integration):
315315
)
316316
return (False, resp)
317317

318-
def send_build_status(self, build, state):
318+
def send_build_status(self, build, commit, state):
319319
"""
320320
Create GitHub commit status for project.
321321
322322
:param build: Build to set up commit status for
323323
:type build: Build
324324
:param state: build state failure, pending, or success.
325325
:type state: str
326+
:param commit: commit sha of the pull request
327+
:type commit: str
326328
:returns: boolean based on commit status creation was successful or not.
327329
:rtype: Bool
328330
"""
329331
session = self.get_session()
330332
project = build.project
331333
owner, repo = build_utils.get_github_username_repo(url=project.repo)
332-
build_sha = build.version.identifier
333334

334335
# select the correct state and description.
335336
github_build_state = SELECT_BUILD_STATUS[state]['github']
@@ -346,7 +347,7 @@ def send_build_status(self, build, state):
346347

347348
try:
348349
resp = session.post(
349-
f'https://api.github.com/repos/{owner}/{repo}/statuses/{build_sha}',
350+
f'https://api.github.com/repos/{owner}/{repo}/statuses/{commit}',
350351
data=json.dumps(data),
351352
headers={'content-type': 'application/json'},
352353
)

readthedocs/projects/tasks.py

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def sync_repo(self):
145145
version_repo = self.get_vcs_repo()
146146
version_repo.update()
147147
self.sync_versions(version_repo)
148-
version_repo.checkout(self.version.identifier)
148+
identifier = self.commit or self.version.identifier
149+
version_repo.checkout(identifier)
149150

150151
def sync_versions(self, version_repo):
151152
"""
@@ -322,6 +323,7 @@ def __init__(
322323
build=None,
323324
project=None,
324325
version=None,
326+
commit=None,
325327
task=None,
326328
):
327329
self.build_env = build_env
@@ -333,6 +335,7 @@ def __init__(
333335
self.version = {}
334336
if version is not None:
335337
self.version = version
338+
self.commit = commit
336339
self.project = {}
337340
if project is not None:
338341
self.project = project
@@ -343,7 +346,7 @@ def __init__(
343346

344347
# pylint: disable=arguments-differ
345348
def run(
346-
self, version_pk, build_pk=None, record=True, docker=None,
349+
self, version_pk, build_pk=None, commit=None, record=True, docker=None,
347350
force=False, **__
348351
):
349352
"""
@@ -364,6 +367,7 @@ def run(
364367
365368
:param version_pk int: Project Version id
366369
:param build_pk int: Build id (if None, commands are not recorded)
370+
:param commit: commit sha of the version required for sending build status reports
367371
:param record bool: record a build object in the database
368372
:param docker bool: use docker to build the project (if ``None``,
369373
``settings.DOCKER_ENABLE`` is used)
@@ -380,6 +384,7 @@ def run(
380384
self.project = self.version.project
381385
self.build = self.get_build(build_pk)
382386
self.build_force = force
387+
self.commit = commit
383388
self.config = None
384389

385390
# Build process starts here
@@ -586,27 +591,42 @@ def run_build(self, docker, record):
586591
log.warning('No build ID, not syncing files')
587592

588593
if self.build_env.failed:
594+
# TODO: Send RTD Webhook notification for build failure.
589595
self.send_notifications(self.version.pk, self.build['id'])
590-
send_external_build_status(
591-
version=self.version, build_pk=self.build['id'], status=BUILD_STATUS_FAILURE
592-
)
596+
597+
if self.commit:
598+
send_external_build_status(
599+
version_type=self.version.type,
600+
build_pk=self.build['id'],
601+
commit=self.commit,
602+
status=BUILD_STATUS_FAILURE
603+
)
593604
elif self.build_env.successful:
594-
send_external_build_status(
595-
version=self.version, build_pk=self.build['id'], status=BUILD_STATUS_SUCCESS
596-
)
605+
# TODO: Send RTD Webhook notification for build success.
606+
if self.commit:
607+
send_external_build_status(
608+
version_type=self.version.type,
609+
build_pk=self.build['id'],
610+
commit=self.commit,
611+
status=BUILD_STATUS_SUCCESS
612+
)
597613
else:
598-
msg = 'Unhandled Build Status'
599-
send_external_build_status(
600-
version=self.version, build_pk=self.build['id'], status=BUILD_STATUS_FAILURE
601-
)
602-
log.warning(
603-
LOG_TEMPLATE,
604-
{
605-
'project': self.project.slug,
606-
'version': self.version.slug,
607-
'msg': msg,
608-
}
609-
)
614+
if self.commit:
615+
msg = 'Unhandled Build Status'
616+
send_external_build_status(
617+
version_type=self.version.type,
618+
build_pk=self.build['id'],
619+
commit=self.commit,
620+
status=BUILD_STATUS_FAILURE
621+
)
622+
log.warning(
623+
LOG_TEMPLATE,
624+
{
625+
'project': self.project.slug,
626+
'version': self.version.slug,
627+
'msg': msg,
628+
}
629+
)
610630

611631
build_complete.send(sender=Build, build=self.build_env.build)
612632

@@ -675,7 +695,7 @@ def setup_vcs(self):
675695
# Re raise the exception to stop the build at this point
676696
raise
677697

678-
commit = self.project.vcs_repo(self.version.slug).commit
698+
commit = self.commit or self.project.vcs_repo(self.version.slug).commit
679699
if commit:
680700
self.build['commit'] = commit
681701

@@ -1859,11 +1879,12 @@ def retry_domain_verification(domain_pk):
18591879

18601880

18611881
@app.task(queue='web')
1862-
def send_build_status(build_pk, status):
1882+
def send_build_status(build_pk, commit, status):
18631883
"""
18641884
Send Build Status to Git Status API for project external versions.
18651885
18661886
:param build_pk: Build primary key
1887+
:param commit: commit sha of the pull/merge request
18671888
:param status: build status failed, pending, or success to be sent.
18681889
"""
18691890
build = Build.objects.get(pk=build_pk)
@@ -1875,7 +1896,7 @@ def send_build_status(build_pk, status):
18751896
)
18761897

18771898
# send Status report using the API.
1878-
service.send_build_status(build, status)
1899+
service.send_build_status(build, commit, status)
18791900

18801901
except RemoteRepository.DoesNotExist:
18811902
log.info('Remote repository does not exist for %s', build.project)
@@ -1886,16 +1907,17 @@ def send_build_status(build_pk, status):
18861907
# TODO: Send build status for other providers.
18871908

18881909

1889-
def send_external_build_status(version, build_pk, status):
1910+
def send_external_build_status(version_type, build_pk, commit, status):
18901911
"""
18911912
Check if build is external and Send Build Status for project external versions.
18921913
1893-
:param version: Version instance
1914+
:param version_type: Version type e.g EXTERNAL, BRANCH, TAG
18941915
:param build_pk: Build pk
1916+
:param commit: commit sha of the pull/merge request
18951917
:param status: build status failed, pending, or success to be sent.
18961918
"""
18971919

18981920
# Send status reports for only External (pull/merge request) Versions.
1899-
if version.type == EXTERNAL:
1921+
if version_type == EXTERNAL:
19001922
# call the task that actually send the build status.
1901-
send_build_status.delay(build_pk, status)
1923+
send_build_status.delay(build_pk, commit, status)

readthedocs/rtd_tests/tests/test_api.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -784,12 +784,13 @@ def setUp(self):
784784
self.github_payload = {
785785
'ref': 'master',
786786
}
787+
self.commit = "ec26de721c3235aad62de7213c562f8c821"
787788
self.github_pull_request_payload = {
788789
"action": "opened",
789790
"number": 2,
790791
"pull_request": {
791792
"head": {
792-
"sha": "ec26de721c3235aad62de7213c562f8c821"
793+
"sha": self.commit
793794
}
794795
}
795796
}
@@ -932,7 +933,8 @@ def test_github_pull_request_opened_event(self, trigger_build, core_trigger_buil
932933
self.assertEqual(resp.data['project'], self.project.slug)
933934
self.assertEqual(resp.data['versions'], [external_version.verbose_name])
934935
core_trigger_build.assert_called_once_with(
935-
force=True, project=self.project, version=external_version
936+
force=True, project=self.project,
937+
version=external_version, commit=self.commit
936938
)
937939
self.assertTrue(external_version)
938940

@@ -963,7 +965,8 @@ def test_github_pull_request_reopened_event(self, trigger_build, core_trigger_bu
963965
self.assertEqual(resp.data['project'], self.project.slug)
964966
self.assertEqual(resp.data['versions'], [external_version.verbose_name])
965967
core_trigger_build.assert_called_once_with(
966-
force=True, project=self.project, version=external_version
968+
force=True, project=self.project,
969+
version=external_version, commit=self.commit
967970
)
968971
self.assertTrue(external_version)
969972

@@ -1007,7 +1010,8 @@ def test_github_pull_request_synchronize_event(self, trigger_build, core_trigger
10071010
self.assertEqual(resp.data['project'], self.project.slug)
10081011
self.assertEqual(resp.data['versions'], [external_version.verbose_name])
10091012
core_trigger_build.assert_called_once_with(
1010-
force=True, project=self.project, version=external_version
1013+
force=True, project=self.project,
1014+
version=external_version, commit=self.commit
10111015
)
10121016
# `synchronize` webhook event updated the identifier (commit hash)
10131017
self.assertNotEqual(prev_identifier, external_version.identifier)

readthedocs/rtd_tests/tests/test_celery.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,16 +343,22 @@ def test_send_build_status_task(self, send_build_status):
343343
external_build = get(
344344
Build, project=self.project, version=external_version
345345
)
346-
tasks.send_build_status(external_build.id, BUILD_STATUS_SUCCESS)
346+
tasks.send_build_status(
347+
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
348+
)
347349

348-
send_build_status.assert_called_once_with(external_build, BUILD_STATUS_SUCCESS)
350+
send_build_status.assert_called_once_with(
351+
external_build, external_build.commit, BUILD_STATUS_SUCCESS
352+
)
349353

350354
@patch('readthedocs.projects.tasks.GitHubService.send_build_status')
351355
def test_send_build_status_task_without_remote_repo(self, send_build_status):
352356
external_version = get(Version, project=self.project, type=EXTERNAL)
353357
external_build = get(
354358
Build, project=self.project, version=external_version
355359
)
356-
tasks.send_build_status(external_build.id, BUILD_STATUS_SUCCESS)
360+
tasks.send_build_status(
361+
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
362+
)
357363

358364
send_build_status.assert_not_called()

readthedocs/rtd_tests/tests/test_config_integration.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,10 @@ def get_update_docs_task(self):
368368
config=load_yaml_config(self.version),
369369
project=self.project,
370370
version=self.version,
371-
build={'id': 99, 'state': BUILD_STATE_TRIGGERED}
371+
build={
372+
'id': 99,
373+
'state': BUILD_STATE_TRIGGERED,
374+
},
372375
)
373376
return update_docs
374377

0 commit comments

Comments
 (0)