endpoints.trigger: initial gitlab handler

This commit is contained in:
Jimmy Zelinskie 2015-04-30 17:12:41 -04:00
parent de29a441c8
commit ae83da75ce

View file

@ -6,6 +6,8 @@ import base64
import re import re
import json import json
import gitlab
from endpoints.building import PreparedBuild from endpoints.building import PreparedBuild
from github import Github, UnknownObjectException, GithubException from github import Github, UnknownObjectException, GithubException
from bitbucket import BitBucket from bitbucket import BitBucket
@ -303,6 +305,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
raise TriggerActivationException(msg) raise TriggerActivationException(msg)
config['hook_id'] = data['id'] config['hook_id'] = data['id']
self.config = config
return config, {'private_key': private_key} return config, {'private_key': private_key}
def deactivate(self): def deactivate(self):
@ -636,9 +639,14 @@ class GithubBuildTrigger(BuildTriggerHandler):
gh_client = self._get_client() gh_client = self._get_client()
usr = gh_client.get_user() usr = gh_client.get_user()
try:
repos = usr.get_repos()
except GithubException:
raise RepositoryReadException('Unable to list user repositories')
personal = { personal = {
'personal': True, 'personal': True,
'repos': [repo.full_name for repo in usr.get_repos()], 'repos': [repo.full_name for repo in repos],
'info': { 'info': {
'name': usr.login, 'name': usr.login,
'avatar_url': usr.avatar_url, 'avatar_url': usr.avatar_url,
@ -1075,3 +1083,284 @@ class CustomBuildTrigger(BuildTriggerHandler):
config.pop('credentials', None) config.pop('credentials', None)
self.config = config self.config = config
return 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)