import logging import io from github import Github from tempfile import SpooledTemporaryFile from app import app user_files = app.config['USERFILES'] client = app.config['HTTPCLIENT'] logger = logging.getLogger(__name__) ZIPBALL = 'application/zip' CHUNK_SIZE = 512 * 1024 class BuildArchiveException(Exception): pass class InvalidServiceException(Exception): pass class TriggerActivationException(Exception): pass class BuildTrigger(object): def __init__(self): pass def list_build_sources(self, auth_token): """ Take the auth information for the specific trigger type and load the list of build sources(repositories). """ raise NotImplementedError def handle_trigger_request(self, request, auth_token, config): """ Transform the incoming request data into a set of actions. """ raise NotImplementedError def is_active(self, config): """ Returns True if the current build trigger is active. Inactive means further setup is needed. """ raise NotImplementedError def activate(self, auth_token, config): """ Activates the trigger for the service, with the given new configuration. """ raise NotImplementedError @classmethod def service_name(cls): """ Particular service implemented by subclasses. """ raise NotImplementedError @classmethod def get_trigger_for_service(cls, service): for subc in cls.__subclasses__(): if subc.service_name() == service: return subc() raise InvalidServiceException('Unable to find service: %s' % service) def raise_unsupported(): raise io.UnsupportedOperation class GithubBuildTrigger(BuildTrigger): @staticmethod def _get_client(auth_token): return Github(auth_token, client_id=app.config['GITHUB_CLIENT_ID'], client_secret=app.config['GITHUB_CLIENT_SECRET']) @classmethod def service_name(cls): return 'github' def is_active(self, config): return 'build_source' in config and len(config['build_source']) > 0 def activate(self, auth_token, config): # TODO: Add the callback web hook to the github repository. pass def list_build_sources(self, auth_token): gh_client = self._get_client(auth_token) usr = gh_client.get_user() personal = { 'personal': True, 'repos': [repo.full_name for repo in usr.get_repos()], 'info': { 'name': usr.login, 'avatar_url': usr.avatar_url, } } repos_by_org = [personal] for org in usr.get_orgs(): repo_list = [] for repo in org.get_repos(): repo_list.append(repo.full_name) repos_by_org.append({ 'personal': False, 'repos': repo_list, 'info': { 'name': org.name, 'avatar_url': org.avatar_url } }) return repos_by_org def handle_trigger_request(self, request, auth_token, config): payload = request.get_json() logger.debug('Payload %s', payload) ref = payload['ref'] commit_id = payload['head_commit']['id'][0:7] gh_client = self._get_client(auth_token) repo_full_name = '%s/%s' % (payload['repository']['owner']['name'], payload['repository']['name']) repo = gh_client.get_repo(repo_full_name) logger.debug('Github repo: %s', repo) # Prepare the download and upload URLs branch_name = ref.split('/')[-1] archive_link = repo.get_archive_link('zipball', branch_name) download_archive = client.get(archive_link, stream=True) with SpooledTemporaryFile(CHUNK_SIZE) as zipball: for chunk in download_archive.iter_content(CHUNK_SIZE): zipball.write(chunk) dockerfile_id = user_files.store_file(zipball, ZIPBALL) logger.debug('Successfully prepared job') return dockerfile_id, branch_name, commit_id