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 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)
|
||||||
|
|
Reference in a new issue