endpoints.trigger: initial gitlab handler
This commit is contained in:
parent
de29a441c8
commit
ae83da75ce
1 changed files with 290 additions and 1 deletions
|
@ -6,6 +6,8 @@ import base64
|
|||
import re
|
||||
import json
|
||||
|
||||
import gitlab
|
||||
|
||||
from endpoints.building import PreparedBuild
|
||||
from github import Github, UnknownObjectException, GithubException
|
||||
from bitbucket import BitBucket
|
||||
|
@ -303,6 +305,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
raise TriggerActivationException(msg)
|
||||
|
||||
config['hook_id'] = data['id']
|
||||
self.config = config
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
def deactivate(self):
|
||||
|
@ -636,9 +639,14 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
|||
gh_client = self._get_client()
|
||||
usr = gh_client.get_user()
|
||||
|
||||
try:
|
||||
repos = usr.get_repos()
|
||||
except GithubException:
|
||||
raise RepositoryReadException('Unable to list user repositories')
|
||||
|
||||
personal = {
|
||||
'personal': True,
|
||||
'repos': [repo.full_name for repo in usr.get_repos()],
|
||||
'repos': [repo.full_name for repo in repos],
|
||||
'info': {
|
||||
'name': usr.login,
|
||||
'avatar_url': usr.avatar_url,
|
||||
|
@ -1075,3 +1083,284 @@ class CustomBuildTrigger(BuildTriggerHandler):
|
|||
config.pop('credentials', None)
|
||||
self.config = config
|
||||
return config
|
||||
|
||||
|
||||
class GitLabBuildTrigger(BuildTriggerHandler):
|
||||
"""
|
||||
BuildTrigger for GitLab.
|
||||
"""
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'gitlab'
|
||||
|
||||
def _get_authorized_client(self):
|
||||
host = app.config.get('GITLAB_TRIGGER_CONFIG', {}).get('HOSTNAME', '')
|
||||
auth_token = self.auth_token or 'invalid'
|
||||
return gitlab.Gitlab(host, oauth_token=auth_token)
|
||||
|
||||
def is_active(self):
|
||||
return 'hook_id' in self.config
|
||||
|
||||
def activate(self, standard_webhook_url):
|
||||
config = self.config
|
||||
new_build_source = config['build_source']
|
||||
gl_client = self._get_authorized_client()
|
||||
|
||||
# Find the GitLab repository.
|
||||
repository = gl_client.getproject(new_build_source)
|
||||
if repository is False:
|
||||
msg = 'Unable to find GitLab repository for source: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add a deploy key to the repository.
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
]
|
||||
success = gl_client.adddeploykey(repository['id'], '%s Builder' % app.config['REGISTRY_TITLE'],
|
||||
public_key)
|
||||
if success is False:
|
||||
msg = 'Unable to add deploy key to repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add the webhook to the GitLab repository.
|
||||
hook = gl_client.addprojecthook(repository['id'], standard_webhook_url, push=True)
|
||||
if hook is False:
|
||||
msg = 'Unable to create webhook on repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
config['hook_id'] = hook['id']
|
||||
self.config = config
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
def deactivate(self):
|
||||
config = self.config
|
||||
gl_client = self._get_authorized_client()
|
||||
|
||||
# Find the GitLab repository.
|
||||
repository = gl_client.getproject(config['build_source'])
|
||||
if repository is False:
|
||||
msg = 'Unable to find GitLab repository for source: %s' % config['build_source']
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# Remove the webhook.
|
||||
success = gl_client.deleteprojecthook(repository['id'], config['hook_id'])
|
||||
if success is False:
|
||||
msg = 'Unable to remove hook: %s' % config['hook_id']
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
config.pop('hook_id', None)
|
||||
self.config = config
|
||||
|
||||
return config
|
||||
|
||||
def list_build_sources(self):
|
||||
gl_client = self._get_authorized_client()
|
||||
current_user = gl_client.currentuser()
|
||||
|
||||
repositories = gl_client.getprojects()
|
||||
if repositories is False:
|
||||
raise RepositoryReadException('Unable to list user repositories')
|
||||
|
||||
namespaces = {}
|
||||
for repo in repositories:
|
||||
owner = repo['namespace']['name']
|
||||
if not owner in namespaces:
|
||||
namespaces[owner] = {
|
||||
'personal': owner == current_user.username,
|
||||
'repos': [],
|
||||
'info': {
|
||||
'name': owner,
|
||||
}
|
||||
}
|
||||
|
||||
namespaces[owner]['repos'].append(repo['path_with_namespace'])
|
||||
|
||||
return namespaces.values()
|
||||
|
||||
def list_build_subdirs(self):
|
||||
config = self.config
|
||||
gl_client = self._get_authorized_client()
|
||||
new_build_source = config['build_source']
|
||||
|
||||
repository = gl_client.getproject(new_build_source)
|
||||
if repository is False:
|
||||
msg = 'Unable to find GitLab repository for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
repo_branches = gl_client.getbranches(repository['id'])
|
||||
if repo_branches is False:
|
||||
msg = 'Unable to find GitLab branches for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
branches = [branch['name'] for branch in repo_branches]
|
||||
branches = find_matching_branches(config, branches)
|
||||
branches = branches or [repository['default_branch'] or 'master']
|
||||
|
||||
repo_tree = gl_client.getrepositorytree(repository['id'], ref_name=branches[0])
|
||||
if repo_tree is False:
|
||||
msg = 'Unable to find GitLab repository tree for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
for node in repo_tree:
|
||||
if node['name'] == 'Dockerfile':
|
||||
return ['/']
|
||||
|
||||
return []
|
||||
|
||||
def dockerfile_url(self):
|
||||
gl_client = self._get_authorized_client()
|
||||
subdir = self.config.get('subdir', '')
|
||||
path = subdir + '/Dockerfile' if subdir else 'Dockerfile'
|
||||
|
||||
repository = gl_client.getproject(self.config['build_source'])
|
||||
if repository is False:
|
||||
return None
|
||||
branch = repository['default_branch']
|
||||
|
||||
return '%s/%s/blob/%s/%s' % (gl_client.host,
|
||||
repository['path_with_namespace'],
|
||||
branch,
|
||||
path)
|
||||
|
||||
def load_dockerfile_contents(self):
|
||||
gl_client = self._get_authorized_client()
|
||||
subdir = self.config.get('subdir', '')
|
||||
path = subdir + '/Dockerfile' if subdir else 'Dockerfile'
|
||||
|
||||
repository = gl_client.getproject(self.config['build_source'])
|
||||
if repository is False:
|
||||
return None
|
||||
|
||||
contents = gl_client.getrawfile(repository['id'], repository['default_branch'], path)
|
||||
if contents is False:
|
||||
return None
|
||||
return contents
|
||||
|
||||
def list_field_values(self, field_name):
|
||||
if field_name == 'refs':
|
||||
branches = self.list_field_values('branch_name')
|
||||
tags = self.list_field_values('tag_name')
|
||||
|
||||
return ([{'kind': 'branch', 'name': b} for b in branches] +
|
||||
[{'kind': 'tag', 'name': t} for t in tags])
|
||||
|
||||
gl_client = self._get_authorized_client()
|
||||
repo = gl_client.getproject(self.config['build_source'])
|
||||
if repo is False:
|
||||
return []
|
||||
|
||||
if field_name == 'tag_name':
|
||||
tags = gl_client.getall(gl_client.getrepositorytags(repo['id']))
|
||||
if tags is False:
|
||||
return []
|
||||
return [tag.name for tag in tags]
|
||||
|
||||
if field_name == 'branch_name':
|
||||
branches = gl_client.getbranches(repo['id'])
|
||||
if branches is False:
|
||||
return []
|
||||
return [branch.name for branch in branches]
|
||||
|
||||
return None
|
||||
|
||||
def _prepare_build(self, commit, ref, is_manual):
|
||||
config = self.config
|
||||
gl_client = self._get_authorized_client()
|
||||
|
||||
repo = gl_client.getproject(self.config['build_source'])
|
||||
if repo is False:
|
||||
raise TriggerStartException('Could not find repository')
|
||||
|
||||
try:
|
||||
[committer] = gl_client.getusers(search=commit['committer_email'])
|
||||
except ValueError:
|
||||
committer = None
|
||||
|
||||
try:
|
||||
[author] = gl_client.getusers(search=commit['author_email'])
|
||||
except ValueError:
|
||||
author = None
|
||||
|
||||
metadata = {
|
||||
'commit_sha': commit['id'],
|
||||
'ref': ref,
|
||||
'default_branch': repo['default_branch'],
|
||||
'git_url': repo['ssh_url_to_repo'],
|
||||
'commit_info': {
|
||||
'url': '',
|
||||
'message': commit['message'],
|
||||
'date': commit['committed_date'],
|
||||
},
|
||||
}
|
||||
|
||||
if committer is not None:
|
||||
metadata['commit_info']['committer'] = {
|
||||
'username': committer['username'],
|
||||
'avatar_url': committer['avatar_url'],
|
||||
'url': client.host + '/' + committer['username'],
|
||||
}
|
||||
|
||||
if author is not None:
|
||||
metadata['commit_info']['author'] = {
|
||||
'username': author['username'],
|
||||
'avatar_url': author['avatar_url'],
|
||||
'url': client.host + '/' + author['username']
|
||||
}
|
||||
|
||||
prepared = PreparedBuild(self.trigger)
|
||||
prepared.tags_from_ref(ref, repo['default_branch'])
|
||||
prepared.name_from_sha(commit['id'])
|
||||
prepared.subdirectory = config['subdir']
|
||||
prepared.metadata = metadata
|
||||
prepared.is_manual = is_manual
|
||||
|
||||
return prepared
|
||||
|
||||
def handle_trigger_request(self, request):
|
||||
payload = request.get_json()
|
||||
if not payload:
|
||||
raise SkipRequestException()
|
||||
|
||||
logger.debug('GitLab trigger payload %s', payload)
|
||||
|
||||
if not payload.gets('commits'):
|
||||
raise SkipRequestException()
|
||||
|
||||
commit = payload['commits'][0]
|
||||
commit_message = commit['message']
|
||||
if should_skip_commit(commit_message):
|
||||
raise SkipRequestException()
|
||||
|
||||
ref = payload['ref']
|
||||
raise_if_skipped(self.config, ref)
|
||||
|
||||
return self._prepare_build(commit, ref, False)
|
||||
|
||||
def manual_start(self, run_parameters=None):
|
||||
run_parameters = run_parameters or {}
|
||||
gl_client = self._get_authorized_client()
|
||||
|
||||
repo = gl_client.getproject(self.config['build_source'])
|
||||
if repo is False:
|
||||
raise TriggerStartException('Could not find repository')
|
||||
|
||||
branch_name = run_parameters.get('branch_name') or repo['default_branch']
|
||||
|
||||
branches = gl_client.getbranches(repo['id'])
|
||||
if branches is False:
|
||||
raise TriggerStartException('Could not find branches')
|
||||
|
||||
commit = None
|
||||
for branch in branches:
|
||||
if branch['name'] is branch_name:
|
||||
commit = branch['commit']
|
||||
if commit is None:
|
||||
raise TriggerStartException('Could not find commit')
|
||||
|
||||
ref = 'refs/heads/%s' % branch_name
|
||||
|
||||
return self._prepare_build(commit, ref, True)
|
||||
|
|
Reference in a new issue