Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion readthedocs/builds/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
)
from readthedocs.builds.models import Build, Version
from readthedocs.builds.utils import memcache_lock
from readthedocs.projects.tasks import send_build_status
from readthedocs.oauth.models import RemoteRepository
from readthedocs.oauth.notifications import GitBuildStatusFailureNotification
from readthedocs.projects.constants import GITHUB_BRAND, GITLAB_BRAND
from readthedocs.worker import app

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -206,3 +208,100 @@ def delete_inactive_external_versions(limit=200, days=30 * 3):
version.project.slug, version.slug,
)
version.delete()


@app.task(queue='web')
def send_build_status(build_pk, commit, status, link_to_build=False):
"""
Send Build Status to Git Status API for project external versions.

It tries using these services' account in order:

1. user's account that imported the project
2. each user's account from the project's maintainers

:param build_pk: Build primary key
:param commit: commit sha of the pull/merge request
:param status: build status failed, pending, or success to be sent.
"""
# TODO: Send build status for BitBucket.
service = None
success = None
build = Build.objects.get(pk=build_pk)
provider_name = build.project.git_provider_name

log.info('Sending build status. build=%s, project=%s', build.pk, build.project.slug)

if provider_name in [GITHUB_BRAND, GITLAB_BRAND]:
# get the service class for the project e.g: GitHubService.
service_class = build.project.git_service_class()

# First, try using user who imported the project's account
try:
service = service_class(
build.project.remote_repository.users.first(),
build.project.remote_repository.account
)

except RemoteRepository.DoesNotExist:
log.warning(
'Project does not have a RemoteRepository. project=%s',
build.project.slug,
)

if service is not None:
# Send status report using the API.
success = service.send_build_status(
build=build,
commit=commit,
state=status,
link_to_build=link_to_build,
)

if success:
log.info(
'Build status report sent correctly. project=%s build=%s status=%s commit=%s',
build.project.slug,
build.pk,
status,
commit,
)
return True

# Try using any of the users' maintainer accounts
# Try to loop through all project users to get their social accounts
users = build.project.users.all()
for user in users:
user_accounts = service_class.for_user(user)
# Try to loop through users all social accounts to send a successful request
for account in user_accounts:
if account.provider_name == provider_name:
success = account.send_build_status(build, commit, status)
if success:
log.info(
'Build status report sent correctly using an user account. '
'project=%s build=%s status=%s commit=%s user=%s',
build.project.slug,
build.pk,
status,
commit,
user.username,
)
return True

for user in users:
# Send Site notification about Build status reporting failure
# to all the users of the project.
notification = GitBuildStatusFailureNotification(
context_object=build.project,
extra_context={'provider_name': provider_name},
user=user,
success=False,
)
notification.send()

log.info(
'No social account or repository permission available for %s',
build.project.slug
)
return False
101 changes: 1 addition & 100 deletions readthedocs/projects/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
)
from readthedocs.builds.models import APIVersion, Build, Version
from readthedocs.builds.signals import build_complete
from readthedocs.builds.tasks import send_build_status
from readthedocs.config import ConfigError
from readthedocs.core.resolver import resolve_path
from readthedocs.core.utils import send_email
Expand All @@ -67,9 +68,6 @@
)
from readthedocs.doc_builder.loader import get_builder_class
from readthedocs.doc_builder.python_environments import Conda, Virtualenv
from readthedocs.oauth.models import RemoteRepository
from readthedocs.oauth.notifications import GitBuildStatusFailureNotification
from readthedocs.projects.constants import GITHUB_BRAND, GITLAB_BRAND
from readthedocs.projects.models import APIProject, Feature
from readthedocs.search.utils import index_new_files, remove_indexed_files
from readthedocs.sphinx_domains.models import SphinxDomain
Expand Down Expand Up @@ -1891,103 +1889,6 @@ def retry_domain_verification(domain_pk):
)


@app.task(queue='web')
def send_build_status(build_pk, commit, status, link_to_build=False):
"""
Send Build Status to Git Status API for project external versions.

It tries using these services' account in order:

1. user's account that imported the project
2. each user's account from the project's maintainers

:param build_pk: Build primary key
:param commit: commit sha of the pull/merge request
:param status: build status failed, pending, or success to be sent.
"""
# TODO: Send build status for BitBucket.
service = None
success = None
build = Build.objects.get(pk=build_pk)
provider_name = build.project.git_provider_name

log.info('Sending build status. build=%s, project=%s', build.pk, build.project.slug)

if provider_name in [GITHUB_BRAND, GITLAB_BRAND]:
# get the service class for the project e.g: GitHubService.
service_class = build.project.git_service_class()

# First, try using user who imported the project's account
try:
service = service_class(
build.project.remote_repository.users.first(),
build.project.remote_repository.account
)

except RemoteRepository.DoesNotExist:
log.warning(
'Project does not have a RemoteRepository. project=%s',
build.project.slug,
)

if service is not None:
# Send status report using the API.
success = service.send_build_status(
build=build,
commit=commit,
state=status,
link_to_build=link_to_build,
)

if success:
log.info(
'Build status report sent correctly. project=%s build=%s status=%s commit=%s',
build.project.slug,
build.pk,
status,
commit,
)
return True

# Try using any of the users' maintainer accounts
# Try to loop through all project users to get their social accounts
users = build.project.users.all()
for user in users:
user_accounts = service_class.for_user(user)
# Try to loop through users all social accounts to send a successful request
for account in user_accounts:
if account.provider_name == provider_name:
success = account.send_build_status(build, commit, status)
if success:
log.info(
'Build status report sent correctly using an user account. '
'project=%s build=%s status=%s commit=%s user=%s',
build.project.slug,
build.pk,
status,
commit,
user.username,
)
return True

for user in users:
# Send Site notification about Build status reporting failure
# to all the users of the project.
notification = GitBuildStatusFailureNotification(
context_object=build.project,
extra_context={'provider_name': provider_name},
user=user,
success=False,
)
notification.send()

log.info(
'No social account or repository permission available for %s',
build.project.slug
)
return False


def send_external_build_status(version_type, build_pk, commit, status):
"""
Check if build is external and Send Build Status for project external versions.
Expand Down
13 changes: 7 additions & 6 deletions readthedocs/rtd_tests/tests/test_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from readthedocs.doc_builder.exceptions import VersionLockedError
from readthedocs.oauth.models import RemoteRepository
from readthedocs.projects import tasks
from readthedocs.builds import tasks as build_tasks
from readthedocs.projects.exceptions import RepositoryError
from readthedocs.projects.models import Project
from readthedocs.rtd_tests.mocks.mock_api import mock_api
Expand Down Expand Up @@ -352,7 +353,7 @@ def test_send_build_status_with_remote_repo_github(self, send_build_status):
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand All @@ -375,7 +376,7 @@ def test_send_build_status_with_social_account_github(self, send_build_status):
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand All @@ -392,7 +393,7 @@ def test_send_build_status_no_remote_repo_or_social_account_github(self, send_bu
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand All @@ -412,7 +413,7 @@ def test_send_build_status_with_remote_repo_gitlab(self, send_build_status):
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand All @@ -435,7 +436,7 @@ def test_send_build_status_with_social_account_gitlab(self, send_build_status):
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand All @@ -452,7 +453,7 @@ def test_send_build_status_no_remote_repo_or_social_account_gitlab(self, send_bu
external_build = get(
Build, project=self.project, version=external_version
)
tasks.send_build_status(
build_tasks.send_build_status(
external_build.id, external_build.commit, BUILD_STATUS_SUCCESS
)

Expand Down