Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR Builder GitLab Integration #6066

Merged
merged 10 commits into from
Aug 19, 2019
55 changes: 54 additions & 1 deletion readthedocs/api/v2/views/integrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
GITHUB_PULL_REQUEST_SYNC = 'synchronize'
GITHUB_CREATE = 'create'
GITHUB_DELETE = 'delete'
GITLAB_MERGE_REQUEST = 'merge_request'
GITLAB_MERGE_REQUEST_CLOSE = 'close'
GITLAB_MERGE_REQUEST_MERGE = 'merge'
GITLAB_MERGE_REQUEST_OPEN = 'open'
GITLAB_MERGE_REQUEST_REOPEN = 'reopen'
GITLAB_MERGE_REQUEST_UPDATE = 'update'
GITLAB_TOKEN_HEADER = 'HTTP_X_GITLAB_TOKEN'
GITLAB_PUSH = 'push'
GITLAB_NULL_HASH = '0' * 40
Expand Down Expand Up @@ -401,7 +407,7 @@ class GitLabWebhookView(WebhookMixin, APIView):
"""
Webhook consumer for GitLab.

Accepts webhook events from GitLab, 'push' events trigger builds.
Accepts webhook events from GitLab, 'push' and 'merge_request' events trigger builds.

Expects the following JSON::

Expand All @@ -413,10 +419,26 @@ class GitLabWebhookView(WebhookMixin, APIView):
...
}

For merge_request events:

{
"object_kind": "merge_request",
"object_attributes": {
"iid": 2,
"last_commit": {
"id": "717abb9a6a0f3111dbd601ef6f58c70bdd165aef",
},
"action": "open"
...
},
...
}

See full payload here:

- https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#push-events
- https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#tag-events
- https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#merge-request-events
"""

integration_type = Integration.GITLAB_WEBHOOK
Expand All @@ -441,6 +463,17 @@ def is_payload_valid(self):
return False
return token == secret

def get_external_version_data(self):
"""Get Commit Sha and merge request number from payload."""
saadmk11 marked this conversation as resolved.
Show resolved Hide resolved
try:
identifier = self.data['object_attributes']['last_commit']['id']
verbose_name = str(self.data['object_attributes']['iid'])

return identifier, verbose_name

except KeyError:
raise ParseError('Parameters "id" and "iid" are required')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this ParseError handled?

Copy link
Member Author

@saadmk11 saadmk11 Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there is a ParseError we return 400 to the API. We do the same thing for the push events.

try:
branches = [self._normalize_ref(data['ref'])]
return self.get_response_push(self.project, branches)
except KeyError:
raise ParseError('Parameter "ref" is required')


def handle_webhook(self):
"""
Handle GitLab events for push and tag_push.
Expand All @@ -450,6 +483,7 @@ def handle_webhook(self):
0000000000000000000000000000000000000000 ('0' * 40)
"""
event = self.request.data.get('object_kind', GITLAB_PUSH)
action = self.data.get('object_attributes', {}).get('action', None)
webhook_gitlab.send(
Project,
project=self.project,
Expand All @@ -470,6 +504,25 @@ def handle_webhook(self):
return self.get_response_push(self.project, branches)
except KeyError:
raise ParseError('Parameter "ref" is required')

if (
self.project.has_feature(Feature.EXTERNAL_VERSION_BUILD) and
event == GITLAB_MERGE_REQUEST and action
):
if (
action in
[
GITLAB_MERGE_REQUEST_OPEN,
GITLAB_MERGE_REQUEST_REOPEN,
GITLAB_MERGE_REQUEST_UPDATE
]
):
# Handle open, update, reopen merge_request event.
return self.get_external_version_response(self.project)

if action in [GITLAB_MERGE_REQUEST_CLOSE, GITLAB_MERGE_REQUEST_MERGE]:
# Handle merge and close merge_request event.
return self.get_delete_external_version_response(self.project)
return None

def _normalize_ref(self, ref):
Expand Down
1 change: 1 addition & 0 deletions readthedocs/builds/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@
RTD_BUILD_STATUS_API_NAME = 'continuous-documentation/read-the-docs'

GITHUB_EXTERNAL_VERSION_NAME = 'Pull Request'
GITLAB_EXTERNAL_VERSION_NAME = 'Merge Request'
GENERIC_EXTERNAL_VERSION_NAME = 'External Version'
32 changes: 29 additions & 3 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
GITHUB_URL,
GITHUB_PULL_REQUEST_URL,
GITHUB_PULL_REQUEST_COMMIT_URL,
GITLAB_BRAND,
GITLAB_COMMIT_URL,
GITLAB_MERGE_REQUEST_URL,
GITLAB_MERGE_REQUEST_COMMIT_URL,
GITLAB_URL,
PRIVACY_CHOICES,
PRIVATE,
Expand All @@ -44,6 +47,7 @@
BUILD_TYPES,
GENERIC_EXTERNAL_VERSION_NAME,
GITHUB_EXTERNAL_VERSION_NAME,
GITLAB_EXTERNAL_VERSION_NAME,
INTERNAL,
LATEST,
NON_REPOSITORY_VERSIONS,
Expand Down Expand Up @@ -178,7 +182,14 @@ def vcs_url(self):
repo=repo,
number=self.verbose_name,
)
# TODO: Add VCS URL for other Git Providers
if 'gitlab' in self.project.repo:
user, repo = get_gitlab_username_repo(self.project.repo)
return GITLAB_MERGE_REQUEST_URL.format(
user=user,
repo=repo,
number=self.verbose_name,
)
# TODO: Add VCS URL for BitBucket.
return ''

url = ''
Expand Down Expand Up @@ -787,7 +798,19 @@ def get_commit_url(self):
number=self.version.verbose_name,
commit=self.commit
)
# TODO: Add External Version Commit URL for other Git Providers
if 'gitlab' in repo_url:
user, repo = get_gitlab_username_repo(repo_url)
if not user and not repo:
return ''

repo = repo.rstrip('/')
return GITLAB_MERGE_REQUEST_COMMIT_URL.format(
user=user,
repo=repo,
number=self.version.verbose_name,
commit=self.commit
)
# TODO: Add External Version Commit URL for BitBucket.
else:
if 'github' in repo_url:
user, repo = get_github_username_repo(repo_url)
Expand Down Expand Up @@ -845,8 +868,11 @@ def external_version_name(self):
if self.is_external:
if self.project.git_provider_name == GITHUB_BRAND:
return GITHUB_EXTERNAL_VERSION_NAME
# TODO: Add External Version Name for other Git Providers

if self.project.git_provider_name == GITLAB_BRAND:
return GITLAB_EXTERNAL_VERSION_NAME

# TODO: Add External Version Name for BitBucket.
return GENERIC_EXTERNAL_VERSION_NAME
return None

Expand Down
11 changes: 11 additions & 0 deletions readthedocs/projects/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,19 @@
'https://gitlab.com/{user}/{repo}/'
'commit/{commit}'
)
GITLAB_MERGE_REQUEST_COMMIT_URL = (
'https://gitlab.com/{user}/{repo}/'
'commit/{commit}?merge_request_iid={number}'
)
GITLAB_MERGE_REQUEST_URL = (
'https://gitlab.com/{user}/{repo}/'
'merge_requests/{number}'
)

# Patterns to pull merge/pull request from providers
GITHUB_PR_PULL_PATTERN = 'pull/{id}/head:external-{id}'
GITLAB_MR_PULL_PATTERN = 'merge-requests/{id}/head:external-{id}'

# Git provider names
GITHUB_BRAND = 'GitHub'
GITLAB_BRAND = 'GitLab'
Loading