initial import for Open Source 🎉
This commit is contained in:
parent
1898c361f3
commit
9c0dd3b722
2048 changed files with 218743 additions and 0 deletions
5
buildtrigger/__init__.py
Normal file
5
buildtrigger/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
import buildtrigger.bitbuckethandler
|
||||
import buildtrigger.customhandler
|
||||
import buildtrigger.githubhandler
|
||||
import buildtrigger.gitlabhandler
|
||||
|
367
buildtrigger/basehandler.py
Normal file
367
buildtrigger/basehandler.py
Normal file
|
@ -0,0 +1,367 @@
|
|||
import os
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from jsonschema import validate
|
||||
from six import add_metaclass
|
||||
|
||||
from active_migration import ActiveDataMigration, ERTMigrationFlags
|
||||
from endpoints.building import PreparedBuild
|
||||
from data import model
|
||||
from buildtrigger.triggerutil import get_trigger_config, InvalidServiceException
|
||||
|
||||
NAMESPACES_SCHEMA = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'personal': {
|
||||
'type': 'boolean',
|
||||
'description': 'True if the namespace is the user\'s personal namespace',
|
||||
},
|
||||
'score': {
|
||||
'type': 'number',
|
||||
'description': 'Score of the relevance of the namespace',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': ['string', 'null'],
|
||||
'description': 'URL of the avatar for this namespace',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL of the website to view the namespace',
|
||||
},
|
||||
'id': {
|
||||
'type': 'string',
|
||||
'description': 'Trigger-internal ID of the namespace',
|
||||
},
|
||||
'title': {
|
||||
'type': 'string',
|
||||
'description': 'Human-readable title of the namespace',
|
||||
},
|
||||
},
|
||||
'required': ['personal', 'score', 'avatar_url', 'id', 'title'],
|
||||
},
|
||||
}
|
||||
|
||||
BUILD_SOURCES_SCHEMA = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'description': 'The name of the repository, without its namespace',
|
||||
},
|
||||
'full_name': {
|
||||
'type': 'string',
|
||||
'description': 'The name of the repository, with its namespace',
|
||||
},
|
||||
'description': {
|
||||
'type': 'string',
|
||||
'description': 'The description of the repository. May be an empty string',
|
||||
},
|
||||
'last_updated': {
|
||||
'type': 'number',
|
||||
'description': 'The date/time when the repository was last updated, since epoch in UTC',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'The URL at which to view the repository in the browser',
|
||||
},
|
||||
'has_admin_permissions': {
|
||||
'type': 'boolean',
|
||||
'description': 'True if the current user has admin permissions on the repository',
|
||||
},
|
||||
'private': {
|
||||
'type': 'boolean',
|
||||
'description': 'True if the repository is private',
|
||||
},
|
||||
},
|
||||
'required': ['name', 'full_name', 'description', 'last_updated',
|
||||
'has_admin_permissions', 'private'],
|
||||
},
|
||||
}
|
||||
|
||||
METADATA_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'commit': {
|
||||
'type': 'string',
|
||||
'description': 'first 7 characters of the SHA-1 identifier for a git commit',
|
||||
'pattern': '^([A-Fa-f0-9]{7,})$',
|
||||
},
|
||||
'git_url': {
|
||||
'type': 'string',
|
||||
'description': 'The GIT url to use for the checkout',
|
||||
},
|
||||
'ref': {
|
||||
'type': 'string',
|
||||
'description': 'git reference for a git commit',
|
||||
'pattern': r'^refs\/(heads|tags|remotes)\/(.+)$',
|
||||
},
|
||||
'default_branch': {
|
||||
'type': 'string',
|
||||
'description': 'default branch of the git repository',
|
||||
},
|
||||
'commit_info': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about a git commit',
|
||||
'properties': {
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view a git commit',
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
'description': 'git commit message',
|
||||
},
|
||||
'date': {
|
||||
'type': 'string',
|
||||
'description': 'timestamp for a git commit'
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the author of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the author',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the author',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the author',
|
||||
},
|
||||
},
|
||||
'required': ['username'],
|
||||
},
|
||||
'committer': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the committer of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the committer',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the committer',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the committer',
|
||||
},
|
||||
},
|
||||
'required': ['username'],
|
||||
},
|
||||
},
|
||||
'required': ['message'],
|
||||
},
|
||||
},
|
||||
'required': ['commit', 'git_url'],
|
||||
}
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class BuildTriggerHandler(object):
|
||||
def __init__(self, trigger, override_config=None):
|
||||
self.trigger = trigger
|
||||
self.config = override_config or get_trigger_config(trigger)
|
||||
|
||||
@property
|
||||
def auth_token(self):
|
||||
""" Returns the auth token for the trigger. """
|
||||
# NOTE: This check is for testing.
|
||||
if isinstance(self.trigger.auth_token, str):
|
||||
return self.trigger.auth_token
|
||||
|
||||
# TODO(remove-unenc): Remove legacy field.
|
||||
if self.trigger.secure_auth_token is not None:
|
||||
return self.trigger.secure_auth_token.decrypt()
|
||||
|
||||
if ActiveDataMigration.has_flag(ERTMigrationFlags.READ_OLD_FIELDS):
|
||||
return self.trigger.auth_token
|
||||
|
||||
return None
|
||||
|
||||
@abstractmethod
|
||||
def load_dockerfile_contents(self):
|
||||
"""
|
||||
Loads the Dockerfile found for the trigger's config and returns them or None if none could
|
||||
be found/loaded.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_build_source_namespaces(self):
|
||||
"""
|
||||
Take the auth information for the specific trigger type and load the
|
||||
list of namespaces that can contain build sources.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_build_sources_for_namespace(self, namespace):
|
||||
"""
|
||||
Take the auth information for the specific trigger type and load the
|
||||
list of repositories under the given namespace.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_build_subdirs(self):
|
||||
"""
|
||||
Take the auth information and the specified config so far and list all of
|
||||
the possible subdirs containing dockerfiles.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def handle_trigger_request(self, request):
|
||||
"""
|
||||
Transform the incoming request data into a set of actions. Returns a PreparedBuild.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns True if the current build trigger is active. Inactive means further
|
||||
setup is needed.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def activate(self, standard_webhook_url):
|
||||
"""
|
||||
Activates the trigger for the service, with the given new configuration.
|
||||
Returns new public and private config that should be stored if successful.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def deactivate(self):
|
||||
"""
|
||||
Deactivates the trigger for the service, removing any hooks installed in
|
||||
the remote service. Returns the new config that should be stored if this
|
||||
trigger is going to be re-activated.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def manual_start(self, run_parameters=None):
|
||||
"""
|
||||
Manually creates a repository build for this trigger. Returns a PreparedBuild.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_field_values(self, field_name, limit=None):
|
||||
"""
|
||||
Lists all values for the given custom trigger field. For example, a trigger might have a
|
||||
field named "branches", and this method would return all branches.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_repository_url(self):
|
||||
""" Returns the URL of the current trigger's repository. Note that this operation
|
||||
can be called in a loop, so it should be as fast as possible. """
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def filename_is_dockerfile(cls, file_name):
|
||||
""" Returns whether the file is named Dockerfile or follows the convention <name>.Dockerfile"""
|
||||
return file_name.endswith(".Dockerfile") or u"Dockerfile" == file_name
|
||||
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
"""
|
||||
Particular service implemented by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def get_handler(cls, trigger, override_config=None):
|
||||
for subc in cls.__subclasses__():
|
||||
if subc.service_name() == trigger.service.name:
|
||||
return subc(trigger, override_config)
|
||||
|
||||
raise InvalidServiceException('Unable to find service: %s' % trigger.service.name)
|
||||
|
||||
def put_config_key(self, key, value):
|
||||
""" Updates a config key in the trigger, saving it to the DB. """
|
||||
self.config[key] = value
|
||||
model.build.update_build_trigger(self.trigger, self.config)
|
||||
|
||||
def set_auth_token(self, auth_token):
|
||||
""" Sets the auth token for the trigger, saving it to the DB. """
|
||||
model.build.update_build_trigger(self.trigger, self.config, auth_token=auth_token)
|
||||
|
||||
def get_dockerfile_path(self):
|
||||
""" Returns the normalized path to the Dockerfile found in the subdirectory
|
||||
in the config. """
|
||||
dockerfile_path = self.config.get('dockerfile_path') or 'Dockerfile'
|
||||
if dockerfile_path[0] == '/':
|
||||
dockerfile_path = dockerfile_path[1:]
|
||||
return dockerfile_path
|
||||
|
||||
def prepare_build(self, metadata, is_manual=False):
|
||||
# Ensure that the metadata meets the scheme.
|
||||
validate(metadata, METADATA_SCHEMA)
|
||||
|
||||
config = self.config
|
||||
ref = metadata.get('ref', None)
|
||||
commit_sha = metadata['commit']
|
||||
default_branch = metadata.get('default_branch', None)
|
||||
prepared = PreparedBuild(self.trigger)
|
||||
prepared.name_from_sha(commit_sha)
|
||||
prepared.subdirectory = config.get('dockerfile_path', None)
|
||||
prepared.context = config.get('context', None)
|
||||
prepared.is_manual = is_manual
|
||||
prepared.metadata = metadata
|
||||
|
||||
if ref is not None:
|
||||
prepared.tags_from_ref(ref, default_branch)
|
||||
else:
|
||||
prepared.tags = [commit_sha[:7]]
|
||||
|
||||
return prepared
|
||||
|
||||
@classmethod
|
||||
def build_sources_response(cls, sources):
|
||||
validate(sources, BUILD_SOURCES_SCHEMA)
|
||||
return sources
|
||||
|
||||
@classmethod
|
||||
def build_namespaces_response(cls, namespaces_dict):
|
||||
namespaces = list(namespaces_dict.values())
|
||||
validate(namespaces, NAMESPACES_SCHEMA)
|
||||
return namespaces
|
||||
|
||||
@classmethod
|
||||
def get_parent_directory_mappings(cls, dockerfile_path, current_paths=None):
|
||||
""" Returns a map of dockerfile_paths to it's possible contexts. """
|
||||
if dockerfile_path == "":
|
||||
return {}
|
||||
|
||||
if dockerfile_path[0] != os.path.sep:
|
||||
dockerfile_path = os.path.sep + dockerfile_path
|
||||
|
||||
dockerfile_path = os.path.normpath(dockerfile_path)
|
||||
all_paths = set()
|
||||
path, _ = os.path.split(dockerfile_path)
|
||||
if path == "":
|
||||
path = os.path.sep
|
||||
|
||||
all_paths.add(path)
|
||||
for i in range(1, len(path.split(os.path.sep))):
|
||||
path, _ = os.path.split(path)
|
||||
all_paths.add(path)
|
||||
|
||||
if current_paths:
|
||||
return dict({dockerfile_path: list(all_paths)}, **current_paths)
|
||||
|
||||
return {dockerfile_path: list(all_paths)}
|
545
buildtrigger/bitbuckethandler.py
Normal file
545
buildtrigger/bitbuckethandler.py
Normal file
|
@ -0,0 +1,545 @@
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
from calendar import timegm
|
||||
|
||||
import dateutil.parser
|
||||
from bitbucket import BitBucket
|
||||
from jsonschema import validate
|
||||
|
||||
from app import app, get_app_url
|
||||
from buildtrigger.basehandler import BuildTriggerHandler
|
||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||
TriggerDeactivationException, TriggerStartException,
|
||||
InvalidPayloadException, TriggerProviderException,
|
||||
SkipRequestException,
|
||||
determine_build_ref, raise_if_skipped_build,
|
||||
find_matching_branches)
|
||||
from util.dict_wrappers import JSONPathDict, SafeDictSetter
|
||||
from util.security.ssh import generate_ssh_keypair
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/commits/%s'
|
||||
_RAW_AUTHOR_REGEX = re.compile(r'.*<(.+)>')
|
||||
|
||||
BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'repository': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'full_name': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['full_name'],
|
||||
}, # /Repository
|
||||
'push': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'changes': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'new': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'target': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'hash': {
|
||||
'type': 'string'
|
||||
},
|
||||
'message': {
|
||||
'type': 'string'
|
||||
},
|
||||
'date': {
|
||||
'type': 'string'
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'user': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'display_name': {
|
||||
'type': 'string',
|
||||
},
|
||||
'account_id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'links': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'avatar': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'href': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['href'],
|
||||
},
|
||||
},
|
||||
'required': ['avatar'],
|
||||
}, # /User
|
||||
},
|
||||
}, # /Author
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': ['hash', 'message', 'date'],
|
||||
}, # /Target
|
||||
},
|
||||
'required': ['name', 'target'],
|
||||
}, # /New
|
||||
},
|
||||
}, # /Changes item
|
||||
}, # /Changes
|
||||
},
|
||||
'required': ['changes'],
|
||||
}, # / Push
|
||||
},
|
||||
'actor': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'account_id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'display_name': {
|
||||
'type': 'string',
|
||||
},
|
||||
'links': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'avatar': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'href': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['href'],
|
||||
},
|
||||
},
|
||||
'required': ['avatar'],
|
||||
},
|
||||
},
|
||||
}, # /Actor
|
||||
'required': ['push', 'repository'],
|
||||
} # /Root
|
||||
|
||||
BITBUCKET_COMMIT_INFO_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'node': {
|
||||
'type': 'string',
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
},
|
||||
'timestamp': {
|
||||
'type': 'string',
|
||||
},
|
||||
'raw_author': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['node', 'message', 'timestamp']
|
||||
}
|
||||
|
||||
def get_transformed_commit_info(bb_commit, ref, default_branch, repository_name, lookup_author):
|
||||
""" Returns the BitBucket commit information transformed into our own
|
||||
payload format.
|
||||
"""
|
||||
try:
|
||||
validate(bb_commit, BITBUCKET_COMMIT_INFO_SCHEMA)
|
||||
except Exception as exc:
|
||||
logger.exception('Exception when validating Bitbucket commit information: %s from %s', exc.message, bb_commit)
|
||||
raise InvalidPayloadException(exc.message)
|
||||
|
||||
commit = JSONPathDict(bb_commit)
|
||||
|
||||
config = SafeDictSetter()
|
||||
config['commit'] = commit['node']
|
||||
config['ref'] = ref
|
||||
config['default_branch'] = default_branch
|
||||
config['git_url'] = 'git@bitbucket.org:%s.git' % repository_name
|
||||
|
||||
config['commit_info.url'] = _BITBUCKET_COMMIT_URL % (repository_name, commit['node'])
|
||||
config['commit_info.message'] = commit['message']
|
||||
config['commit_info.date'] = commit['timestamp']
|
||||
|
||||
match = _RAW_AUTHOR_REGEX.match(commit['raw_author'])
|
||||
if match:
|
||||
author = lookup_author(match.group(1))
|
||||
author_info = JSONPathDict(author) if author is not None else None
|
||||
if author_info:
|
||||
config['commit_info.author.username'] = author_info['user.display_name']
|
||||
config['commit_info.author.avatar_url'] = author_info['user.avatar']
|
||||
|
||||
return config.dict_value()
|
||||
|
||||
|
||||
def get_transformed_webhook_payload(bb_payload, default_branch=None):
|
||||
""" Returns the BitBucket webhook JSON payload transformed into our own payload
|
||||
format. If the bb_payload is not valid, returns None.
|
||||
"""
|
||||
try:
|
||||
validate(bb_payload, BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA)
|
||||
except Exception as exc:
|
||||
logger.exception('Exception when validating Bitbucket webhook payload: %s from %s', exc.message,
|
||||
bb_payload)
|
||||
raise InvalidPayloadException(exc.message)
|
||||
|
||||
payload = JSONPathDict(bb_payload)
|
||||
change = payload['push.changes[-1].new']
|
||||
if not change:
|
||||
raise SkipRequestException
|
||||
|
||||
is_branch = change['type'] == 'branch'
|
||||
ref = 'refs/heads/' + change['name'] if is_branch else 'refs/tags/' + change['name']
|
||||
|
||||
repository_name = payload['repository.full_name']
|
||||
target = change['target']
|
||||
|
||||
config = SafeDictSetter()
|
||||
config['commit'] = target['hash']
|
||||
config['ref'] = ref
|
||||
config['default_branch'] = default_branch
|
||||
config['git_url'] = 'git@bitbucket.org:%s.git' % repository_name
|
||||
|
||||
config['commit_info.url'] = target['links.html.href'] or ''
|
||||
config['commit_info.message'] = target['message']
|
||||
config['commit_info.date'] = target['date']
|
||||
|
||||
config['commit_info.author.username'] = target['author.user.display_name']
|
||||
config['commit_info.author.avatar_url'] = target['author.user.links.avatar.href']
|
||||
|
||||
config['commit_info.committer.username'] = payload['actor.display_name']
|
||||
config['commit_info.committer.avatar_url'] = payload['actor.links.avatar.href']
|
||||
return config.dict_value()
|
||||
|
||||
|
||||
class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||
"""
|
||||
BuildTrigger for Bitbucket.
|
||||
"""
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'bitbucket'
|
||||
|
||||
def _get_client(self):
|
||||
""" Returns a BitBucket API client for this trigger's config. """
|
||||
key = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_KEY', '')
|
||||
secret = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_SECRET', '')
|
||||
|
||||
trigger_uuid = self.trigger.uuid
|
||||
callback_url = '%s/oauth1/bitbucket/callback/trigger/%s' % (get_app_url(), trigger_uuid)
|
||||
|
||||
return BitBucket(key, secret, callback_url, timeout=15)
|
||||
|
||||
def _get_authorized_client(self):
|
||||
""" Returns an authorized API client. """
|
||||
base_client = self._get_client()
|
||||
auth_token = self.auth_token or 'invalid:invalid'
|
||||
token_parts = auth_token.split(':')
|
||||
if len(token_parts) != 2:
|
||||
token_parts = ['invalid', 'invalid']
|
||||
|
||||
(access_token, access_token_secret) = token_parts
|
||||
return base_client.get_authorized_client(access_token, access_token_secret)
|
||||
|
||||
def _get_repository_client(self):
|
||||
""" Returns an API client for working with this config's BB repository. """
|
||||
source = self.config['build_source']
|
||||
(namespace, name) = source.split('/')
|
||||
bitbucket_client = self._get_authorized_client()
|
||||
return bitbucket_client.for_namespace(namespace).repositories().get(name)
|
||||
|
||||
def _get_default_branch(self, repository, default_value='master'):
|
||||
""" Returns the default branch for the repository or the value given. """
|
||||
(result, data, _) = repository.get_main_branch()
|
||||
if result:
|
||||
return data['name']
|
||||
|
||||
return default_value
|
||||
|
||||
def get_oauth_url(self):
|
||||
""" Returns the OAuth URL to authorize Bitbucket. """
|
||||
bitbucket_client = self._get_client()
|
||||
(result, data, err_msg) = bitbucket_client.get_authorization_url()
|
||||
if not result:
|
||||
raise TriggerProviderException(err_msg)
|
||||
|
||||
return data
|
||||
|
||||
def exchange_verifier(self, verifier):
|
||||
""" Exchanges the given verifier token to setup this trigger. """
|
||||
bitbucket_client = self._get_client()
|
||||
access_token = self.config.get('access_token', '')
|
||||
access_token_secret = self.auth_token
|
||||
|
||||
# Exchange the verifier for a new access token.
|
||||
(result, data, _) = bitbucket_client.verify_token(access_token, access_token_secret, verifier)
|
||||
if not result:
|
||||
return False
|
||||
|
||||
# Save the updated access token and secret.
|
||||
self.set_auth_token(data[0] + ':' + data[1])
|
||||
|
||||
# Retrieve the current authorized user's information and store the username in the config.
|
||||
authorized_client = self._get_authorized_client()
|
||||
(result, data, _) = authorized_client.get_current_user()
|
||||
if not result:
|
||||
return False
|
||||
|
||||
self.put_config_key('account_id', data['user']['account_id'])
|
||||
self.put_config_key('nickname', data['user']['nickname'])
|
||||
return True
|
||||
|
||||
def is_active(self):
|
||||
return 'webhook_id' in self.config
|
||||
|
||||
def activate(self, standard_webhook_url):
|
||||
config = self.config
|
||||
|
||||
# Add a deploy key to the repository.
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
]
|
||||
|
||||
repository = self._get_repository_client()
|
||||
(result, created_deploykey, err_msg) = repository.deploykeys().create(
|
||||
app.config['REGISTRY_TITLE'] + ' webhook key', public_key)
|
||||
|
||||
if not result:
|
||||
msg = 'Unable to add deploy key to repository: %s' % err_msg
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
config['deploy_key_id'] = created_deploykey['pk']
|
||||
|
||||
# Add a webhook callback.
|
||||
description = 'Webhook for invoking builds on %s' % app.config['REGISTRY_TITLE_SHORT']
|
||||
webhook_events = ['repo:push']
|
||||
(result, created_webhook, err_msg) = repository.webhooks().create(
|
||||
description, standard_webhook_url, webhook_events)
|
||||
|
||||
if not result:
|
||||
msg = 'Unable to add webhook to repository: %s' % err_msg
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
config['webhook_id'] = created_webhook['uuid']
|
||||
self.config = config
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
def deactivate(self):
|
||||
config = self.config
|
||||
|
||||
webhook_id = config.pop('webhook_id', None)
|
||||
deploy_key_id = config.pop('deploy_key_id', None)
|
||||
repository = self._get_repository_client()
|
||||
|
||||
# Remove the webhook.
|
||||
if webhook_id is not None:
|
||||
(result, _, err_msg) = repository.webhooks().delete(webhook_id)
|
||||
if not result:
|
||||
msg = 'Unable to remove webhook from repository: %s' % err_msg
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# Remove the public key.
|
||||
if deploy_key_id is not None:
|
||||
(result, _, err_msg) = repository.deploykeys().delete(deploy_key_id)
|
||||
if not result:
|
||||
msg = 'Unable to remove deploy key from repository: %s' % err_msg
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
return config
|
||||
|
||||
def list_build_source_namespaces(self):
|
||||
bitbucket_client = self._get_authorized_client()
|
||||
(result, data, err_msg) = bitbucket_client.get_visible_repositories()
|
||||
if not result:
|
||||
raise RepositoryReadException('Could not read repository list: ' + err_msg)
|
||||
|
||||
namespaces = {}
|
||||
for repo in data:
|
||||
owner = repo['owner']
|
||||
|
||||
if owner in namespaces:
|
||||
namespaces[owner]['score'] = namespaces[owner]['score'] + 1
|
||||
else:
|
||||
namespaces[owner] = {
|
||||
'personal': owner == self.config.get('nickname', self.config.get('username')),
|
||||
'id': owner,
|
||||
'title': owner,
|
||||
'avatar_url': repo['logo'],
|
||||
'url': 'https://bitbucket.org/%s' % (owner),
|
||||
'score': 1,
|
||||
}
|
||||
|
||||
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||
|
||||
def list_build_sources_for_namespace(self, namespace):
|
||||
def repo_view(repo):
|
||||
last_modified = dateutil.parser.parse(repo['utc_last_updated'])
|
||||
|
||||
return {
|
||||
'name': repo['slug'],
|
||||
'full_name': '%s/%s' % (repo['owner'], repo['slug']),
|
||||
'description': repo['description'] or '',
|
||||
'last_updated': timegm(last_modified.utctimetuple()),
|
||||
'url': 'https://bitbucket.org/%s/%s' % (repo['owner'], repo['slug']),
|
||||
'has_admin_permissions': repo['read_only'] is False,
|
||||
'private': repo['is_private'],
|
||||
}
|
||||
|
||||
bitbucket_client = self._get_authorized_client()
|
||||
(result, data, err_msg) = bitbucket_client.get_visible_repositories()
|
||||
if not result:
|
||||
raise RepositoryReadException('Could not read repository list: ' + err_msg)
|
||||
|
||||
repos = [repo_view(repo) for repo in data if repo['owner'] == namespace]
|
||||
return BuildTriggerHandler.build_sources_response(repos)
|
||||
|
||||
def list_build_subdirs(self):
|
||||
config = self.config
|
||||
repository = self._get_repository_client()
|
||||
|
||||
# Find the first matching branch.
|
||||
repo_branches = self.list_field_values('branch_name') or []
|
||||
branches = find_matching_branches(config, repo_branches)
|
||||
if not branches:
|
||||
branches = [self._get_default_branch(repository)]
|
||||
|
||||
(result, data, err_msg) = repository.get_path_contents('', revision=branches[0])
|
||||
if not result:
|
||||
raise RepositoryReadException(err_msg)
|
||||
|
||||
files = set([f['path'] for f in data['files']])
|
||||
return ["/" + file_path for file_path in files if self.filename_is_dockerfile(os.path.basename(file_path))]
|
||||
|
||||
def load_dockerfile_contents(self):
|
||||
repository = self._get_repository_client()
|
||||
path = self.get_dockerfile_path()
|
||||
|
||||
(result, data, err_msg) = repository.get_raw_path_contents(path, revision='master')
|
||||
if not result:
|
||||
return None
|
||||
|
||||
return data
|
||||
|
||||
def list_field_values(self, field_name, limit=None):
|
||||
if 'build_source' not in self.config:
|
||||
return None
|
||||
|
||||
source = self.config['build_source']
|
||||
(namespace, name) = source.split('/')
|
||||
|
||||
bitbucket_client = self._get_authorized_client()
|
||||
repository = bitbucket_client.for_namespace(namespace).repositories().get(name)
|
||||
|
||||
if field_name == 'refs':
|
||||
(result, data, _) = repository.get_branches_and_tags()
|
||||
if not result:
|
||||
return None
|
||||
|
||||
branches = [b['name'] for b in data['branches']]
|
||||
tags = [t['name'] for t in data['tags']]
|
||||
|
||||
return ([{'kind': 'branch', 'name': b} for b in branches] +
|
||||
[{'kind': 'tag', 'name': tag} for tag in tags])
|
||||
|
||||
if field_name == 'tag_name':
|
||||
(result, data, _) = repository.get_tags()
|
||||
if not result:
|
||||
return None
|
||||
|
||||
tags = list(data.keys())
|
||||
if limit:
|
||||
tags = tags[0:limit]
|
||||
|
||||
return tags
|
||||
|
||||
if field_name == 'branch_name':
|
||||
(result, data, _) = repository.get_branches()
|
||||
if not result:
|
||||
return None
|
||||
|
||||
branches = list(data.keys())
|
||||
if limit:
|
||||
branches = branches[0:limit]
|
||||
|
||||
return branches
|
||||
|
||||
return None
|
||||
|
||||
def get_repository_url(self):
|
||||
source = self.config['build_source']
|
||||
(namespace, name) = source.split('/')
|
||||
return 'https://bitbucket.org/%s/%s' % (namespace, name)
|
||||
|
||||
def handle_trigger_request(self, request):
|
||||
payload = request.get_json()
|
||||
if payload is None:
|
||||
raise InvalidPayloadException('Missing payload')
|
||||
|
||||
logger.debug('Got BitBucket request: %s', payload)
|
||||
|
||||
repository = self._get_repository_client()
|
||||
default_branch = self._get_default_branch(repository)
|
||||
|
||||
metadata = get_transformed_webhook_payload(payload, default_branch=default_branch)
|
||||
prepared = self.prepare_build(metadata)
|
||||
|
||||
# Check if we should skip this build.
|
||||
raise_if_skipped_build(prepared, self.config)
|
||||
return prepared
|
||||
|
||||
def manual_start(self, run_parameters=None):
|
||||
run_parameters = run_parameters or {}
|
||||
repository = self._get_repository_client()
|
||||
bitbucket_client = self._get_authorized_client()
|
||||
|
||||
def get_branch_sha(branch_name):
|
||||
# Lookup the commit SHA for the branch.
|
||||
(result, data, _) = repository.get_branch(branch_name)
|
||||
if not result:
|
||||
raise TriggerStartException('Could not find branch in repository')
|
||||
|
||||
return data['target']['hash']
|
||||
|
||||
def get_tag_sha(tag_name):
|
||||
# Lookup the commit SHA for the tag.
|
||||
(result, data, _) = repository.get_tag(tag_name)
|
||||
if not result:
|
||||
raise TriggerStartException('Could not find tag in repository')
|
||||
|
||||
return data['target']['hash']
|
||||
|
||||
def lookup_author(email_address):
|
||||
(result, data, _) = bitbucket_client.accounts().get_profile(email_address)
|
||||
return data if result else None
|
||||
|
||||
# Find the branch or tag to build.
|
||||
default_branch = self._get_default_branch(repository)
|
||||
(commit_sha, ref) = determine_build_ref(run_parameters, get_branch_sha, get_tag_sha,
|
||||
default_branch)
|
||||
|
||||
# Lookup the commit SHA in BitBucket.
|
||||
(result, commit_info, _) = repository.changesets().get(commit_sha)
|
||||
if not result:
|
||||
raise TriggerStartException('Could not lookup commit SHA')
|
||||
|
||||
# Return a prepared build for the commit.
|
||||
repository_name = '%s/%s' % (repository.namespace, repository.repository_name)
|
||||
metadata = get_transformed_commit_info(commit_info, ref, default_branch,
|
||||
repository_name, lookup_author)
|
||||
|
||||
return self.prepare_build(metadata, is_manual=True)
|
229
buildtrigger/customhandler.py
Normal file
229
buildtrigger/customhandler.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
import logging
|
||||
import json
|
||||
|
||||
from jsonschema import validate, ValidationError
|
||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||
TriggerStartException, ValidationRequestException,
|
||||
InvalidPayloadException,
|
||||
SkipRequestException, raise_if_skipped_build,
|
||||
find_matching_branches)
|
||||
|
||||
from buildtrigger.basehandler import BuildTriggerHandler
|
||||
|
||||
from buildtrigger.bitbuckethandler import (BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as bb_schema,
|
||||
get_transformed_webhook_payload as bb_payload)
|
||||
|
||||
from buildtrigger.githubhandler import (GITHUB_WEBHOOK_PAYLOAD_SCHEMA as gh_schema,
|
||||
get_transformed_webhook_payload as gh_payload)
|
||||
|
||||
from buildtrigger.gitlabhandler import (GITLAB_WEBHOOK_PAYLOAD_SCHEMA as gl_schema,
|
||||
get_transformed_webhook_payload as gl_payload)
|
||||
|
||||
from util.security.ssh import generate_ssh_keypair
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Defines an ordered set of tuples of the schemas and associated transformation functions
|
||||
# for incoming webhook payloads.
|
||||
SCHEMA_AND_HANDLERS = [
|
||||
(gh_schema, gh_payload),
|
||||
(bb_schema, bb_payload),
|
||||
(gl_schema, gl_payload),
|
||||
]
|
||||
|
||||
|
||||
def custom_trigger_payload(metadata, git_url):
|
||||
# First try the customhandler schema. If it matches, nothing more to do.
|
||||
custom_handler_validation_error = None
|
||||
try:
|
||||
validate(metadata, CustomBuildTrigger.payload_schema)
|
||||
except ValidationError as vex:
|
||||
custom_handler_validation_error = vex
|
||||
|
||||
# Otherwise, try the defined schemas, in order, until we find a match.
|
||||
for schema, handler in SCHEMA_AND_HANDLERS:
|
||||
try:
|
||||
validate(metadata, schema)
|
||||
except ValidationError:
|
||||
continue
|
||||
|
||||
result = handler(metadata)
|
||||
result['git_url'] = git_url
|
||||
return result
|
||||
|
||||
# If we have reached this point and no other schemas validated, then raise the error for the
|
||||
# custom schema.
|
||||
if custom_handler_validation_error is not None:
|
||||
raise InvalidPayloadException(custom_handler_validation_error.message)
|
||||
|
||||
metadata['git_url'] = git_url
|
||||
return metadata
|
||||
|
||||
|
||||
class CustomBuildTrigger(BuildTriggerHandler):
|
||||
payload_schema = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'commit': {
|
||||
'type': 'string',
|
||||
'description': 'first 7 characters of the SHA-1 identifier for a git commit',
|
||||
'pattern': '^([A-Fa-f0-9]{7,})$',
|
||||
},
|
||||
'ref': {
|
||||
'type': 'string',
|
||||
'description': 'git reference for a git commit',
|
||||
'pattern': '^refs\/(heads|tags|remotes)\/(.+)$',
|
||||
},
|
||||
'default_branch': {
|
||||
'type': 'string',
|
||||
'description': 'default branch of the git repository',
|
||||
},
|
||||
'commit_info': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about a git commit',
|
||||
'properties': {
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view a git commit',
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
'description': 'git commit message',
|
||||
},
|
||||
'date': {
|
||||
'type': 'string',
|
||||
'description': 'timestamp for a git commit'
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the author of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the author',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the author',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the author',
|
||||
},
|
||||
},
|
||||
'required': ['username', 'url', 'avatar_url'],
|
||||
},
|
||||
'committer': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the committer of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the committer',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the committer',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the committer',
|
||||
},
|
||||
},
|
||||
'required': ['username', 'url', 'avatar_url'],
|
||||
},
|
||||
},
|
||||
'required': ['url', 'message', 'date'],
|
||||
},
|
||||
},
|
||||
'required': ['commit', 'ref', 'default_branch'],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'custom-git'
|
||||
|
||||
def is_active(self):
|
||||
return self.config.has_key('credentials')
|
||||
|
||||
def _metadata_from_payload(self, payload, git_url):
|
||||
# Parse the JSON payload.
|
||||
try:
|
||||
metadata = json.loads(payload)
|
||||
except ValueError as vex:
|
||||
raise InvalidPayloadException(vex.message)
|
||||
|
||||
return custom_trigger_payload(metadata, git_url)
|
||||
|
||||
def handle_trigger_request(self, request):
|
||||
payload = request.data
|
||||
if not payload:
|
||||
raise InvalidPayloadException('Missing expected payload')
|
||||
|
||||
logger.debug('Payload %s', payload)
|
||||
|
||||
metadata = self._metadata_from_payload(payload, self.config['build_source'])
|
||||
prepared = self.prepare_build(metadata)
|
||||
|
||||
# Check if we should skip this build.
|
||||
raise_if_skipped_build(prepared, self.config)
|
||||
|
||||
return prepared
|
||||
|
||||
def manual_start(self, run_parameters=None):
|
||||
# commit_sha is the only required parameter
|
||||
commit_sha = run_parameters.get('commit_sha')
|
||||
if commit_sha is None:
|
||||
raise TriggerStartException('missing required parameter')
|
||||
|
||||
config = self.config
|
||||
metadata = {
|
||||
'commit': commit_sha,
|
||||
'git_url': config['build_source'],
|
||||
}
|
||||
|
||||
try:
|
||||
return self.prepare_build(metadata, is_manual=True)
|
||||
except ValidationError as ve:
|
||||
raise TriggerStartException(ve.message)
|
||||
|
||||
def activate(self, standard_webhook_url):
|
||||
config = self.config
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
{
|
||||
'name': 'Webhook Endpoint URL',
|
||||
'value': standard_webhook_url,
|
||||
},
|
||||
]
|
||||
self.config = config
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
def deactivate(self):
|
||||
config = self.config
|
||||
config.pop('credentials', None)
|
||||
self.config = config
|
||||
return config
|
||||
|
||||
def get_repository_url(self):
|
||||
return None
|
||||
|
||||
def list_build_source_namespaces(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def list_build_sources_for_namespace(self, namespace):
|
||||
raise NotImplementedError
|
||||
|
||||
def list_build_subdirs(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def list_field_values(self, field_name, limit=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def load_dockerfile_contents(self):
|
||||
raise NotImplementedError
|
587
buildtrigger/githubhandler.py
Normal file
587
buildtrigger/githubhandler.py
Normal file
|
@ -0,0 +1,587 @@
|
|||
import logging
|
||||
import os.path
|
||||
import base64
|
||||
import re
|
||||
|
||||
from calendar import timegm
|
||||
from functools import wraps
|
||||
from ssl import SSLError
|
||||
|
||||
from github import (Github, UnknownObjectException, GithubException,
|
||||
BadCredentialsException as GitHubBadCredentialsException)
|
||||
|
||||
from jsonschema import validate
|
||||
|
||||
from app import app, github_trigger
|
||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||
TriggerDeactivationException, TriggerStartException,
|
||||
EmptyRepositoryException, ValidationRequestException,
|
||||
SkipRequestException, InvalidPayloadException,
|
||||
determine_build_ref, raise_if_skipped_build,
|
||||
find_matching_branches)
|
||||
from buildtrigger.basehandler import BuildTriggerHandler
|
||||
from endpoints.exception import ExternalServiceError
|
||||
from util.security.ssh import generate_ssh_keypair
|
||||
from util.dict_wrappers import JSONPathDict, SafeDictSetter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GITHUB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'ref': {
|
||||
'type': 'string',
|
||||
},
|
||||
'head_commit': {
|
||||
'type': ['object', 'null'],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
},
|
||||
'timestamp': {
|
||||
'type': 'string',
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string'
|
||||
},
|
||||
'html_url': {
|
||||
'type': 'string'
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string'
|
||||
},
|
||||
},
|
||||
},
|
||||
'committer': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string'
|
||||
},
|
||||
'html_url': {
|
||||
'type': 'string'
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string'
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': ['id', 'url', 'message', 'timestamp'],
|
||||
},
|
||||
'repository': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'ssh_url': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['ssh_url'],
|
||||
},
|
||||
},
|
||||
'required': ['ref', 'head_commit', 'repository'],
|
||||
}
|
||||
|
||||
def get_transformed_webhook_payload(gh_payload, default_branch=None, lookup_user=None):
|
||||
""" Returns the GitHub webhook JSON payload transformed into our own payload
|
||||
format. If the gh_payload is not valid, returns None.
|
||||
"""
|
||||
try:
|
||||
validate(gh_payload, GITHUB_WEBHOOK_PAYLOAD_SCHEMA)
|
||||
except Exception as exc:
|
||||
raise InvalidPayloadException(exc.message)
|
||||
|
||||
payload = JSONPathDict(gh_payload)
|
||||
|
||||
if payload['head_commit'] is None:
|
||||
raise SkipRequestException
|
||||
|
||||
config = SafeDictSetter()
|
||||
config['commit'] = payload['head_commit.id']
|
||||
config['ref'] = payload['ref']
|
||||
config['default_branch'] = payload['repository.default_branch'] or default_branch
|
||||
config['git_url'] = payload['repository.ssh_url']
|
||||
|
||||
config['commit_info.url'] = payload['head_commit.url']
|
||||
config['commit_info.message'] = payload['head_commit.message']
|
||||
config['commit_info.date'] = payload['head_commit.timestamp']
|
||||
|
||||
config['commit_info.author.username'] = payload['head_commit.author.username']
|
||||
config['commit_info.author.url'] = payload.get('head_commit.author.html_url')
|
||||
config['commit_info.author.avatar_url'] = payload.get('head_commit.author.avatar_url')
|
||||
|
||||
config['commit_info.committer.username'] = payload.get('head_commit.committer.username')
|
||||
config['commit_info.committer.url'] = payload.get('head_commit.committer.html_url')
|
||||
config['commit_info.committer.avatar_url'] = payload.get('head_commit.committer.avatar_url')
|
||||
|
||||
# Note: GitHub doesn't always return the extra information for users, so we do the lookup
|
||||
# manually if possible.
|
||||
if (lookup_user and not payload.get('head_commit.author.html_url') and
|
||||
payload.get('head_commit.author.username')):
|
||||
author_info = lookup_user(payload['head_commit.author.username'])
|
||||
if author_info:
|
||||
config['commit_info.author.url'] = author_info['html_url']
|
||||
config['commit_info.author.avatar_url'] = author_info['avatar_url']
|
||||
|
||||
if (lookup_user and
|
||||
payload.get('head_commit.committer.username') and
|
||||
not payload.get('head_commit.committer.html_url')):
|
||||
committer_info = lookup_user(payload['head_commit.committer.username'])
|
||||
if committer_info:
|
||||
config['commit_info.committer.url'] = committer_info['html_url']
|
||||
config['commit_info.committer.avatar_url'] = committer_info['avatar_url']
|
||||
|
||||
return config.dict_value()
|
||||
|
||||
|
||||
def _catch_ssl_errors(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except SSLError as se:
|
||||
msg = 'Request to the GitHub API failed: %s' % se.message
|
||||
logger.exception(msg)
|
||||
raise ExternalServiceError(msg)
|
||||
return wrapper
|
||||
|
||||
|
||||
class GithubBuildTrigger(BuildTriggerHandler):
|
||||
"""
|
||||
BuildTrigger for GitHub that uses the archive API and buildpacks.
|
||||
"""
|
||||
def _get_client(self):
|
||||
""" Returns an authenticated client for talking to the GitHub API. """
|
||||
return Github(self.auth_token,
|
||||
base_url=github_trigger.api_endpoint(),
|
||||
client_id=github_trigger.client_id(),
|
||||
client_secret=github_trigger.client_secret(),
|
||||
timeout=5)
|
||||
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'github'
|
||||
|
||||
def is_active(self):
|
||||
return 'hook_id' in self.config
|
||||
|
||||
def get_repository_url(self):
|
||||
source = self.config['build_source']
|
||||
return github_trigger.get_public_url(source)
|
||||
|
||||
@staticmethod
|
||||
def _get_error_message(ghe, default_msg):
|
||||
if ghe.data.get('errors') and ghe.data['errors'][0].get('message'):
|
||||
return ghe.data['errors'][0]['message']
|
||||
|
||||
return default_msg
|
||||
|
||||
@_catch_ssl_errors
|
||||
def activate(self, standard_webhook_url):
|
||||
config = self.config
|
||||
new_build_source = config['build_source']
|
||||
gh_client = self._get_client()
|
||||
|
||||
# Find the GitHub repository.
|
||||
try:
|
||||
gh_repo = gh_client.get_repo(new_build_source)
|
||||
except UnknownObjectException:
|
||||
msg = 'Unable to find GitHub repository for source: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add a deploy key to the GitHub repository.
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
]
|
||||
|
||||
try:
|
||||
deploy_key = gh_repo.create_key('%s Builder' % app.config['REGISTRY_TITLE'],
|
||||
public_key)
|
||||
config['deploy_key_id'] = deploy_key.id
|
||||
except GithubException as ghe:
|
||||
default_msg = 'Unable to add deploy key to repository: %s' % new_build_source
|
||||
msg = GithubBuildTrigger._get_error_message(ghe, default_msg)
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add the webhook to the GitHub repository.
|
||||
webhook_config = {
|
||||
'url': standard_webhook_url,
|
||||
'content_type': 'json',
|
||||
}
|
||||
|
||||
try:
|
||||
hook = gh_repo.create_hook('web', webhook_config)
|
||||
config['hook_id'] = hook.id
|
||||
config['master_branch'] = gh_repo.default_branch
|
||||
except GithubException as ghe:
|
||||
default_msg = 'Unable to create webhook on repository: %s' % new_build_source
|
||||
msg = GithubBuildTrigger._get_error_message(ghe, default_msg)
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
@_catch_ssl_errors
|
||||
def deactivate(self):
|
||||
config = self.config
|
||||
gh_client = self._get_client()
|
||||
|
||||
# Find the GitHub repository.
|
||||
try:
|
||||
repo = gh_client.get_repo(config['build_source'])
|
||||
except UnknownObjectException:
|
||||
msg = 'Unable to find GitHub repository for source: %s' % config['build_source']
|
||||
raise TriggerDeactivationException(msg)
|
||||
except GitHubBadCredentialsException:
|
||||
msg = 'Unable to access repository to disable trigger'
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# If the trigger uses a deploy key, remove it.
|
||||
try:
|
||||
if config['deploy_key_id']:
|
||||
deploy_key = repo.get_key(config['deploy_key_id'])
|
||||
deploy_key.delete()
|
||||
except KeyError:
|
||||
# There was no config['deploy_key_id'], thus this is an old trigger without a deploy key.
|
||||
pass
|
||||
except GithubException as ghe:
|
||||
default_msg = 'Unable to remove deploy key: %s' % config['deploy_key_id']
|
||||
msg = GithubBuildTrigger._get_error_message(ghe, default_msg)
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# Remove the webhook.
|
||||
if 'hook_id' in config:
|
||||
try:
|
||||
hook = repo.get_hook(config['hook_id'])
|
||||
hook.delete()
|
||||
except GithubException as ghe:
|
||||
default_msg = 'Unable to remove hook: %s' % config['hook_id']
|
||||
msg = GithubBuildTrigger._get_error_message(ghe, default_msg)
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
config.pop('hook_id', None)
|
||||
self.config = config
|
||||
return config
|
||||
|
||||
@_catch_ssl_errors
|
||||
def list_build_source_namespaces(self):
|
||||
gh_client = self._get_client()
|
||||
usr = gh_client.get_user()
|
||||
|
||||
# Build the full set of namespaces for the user, starting with their own.
|
||||
namespaces = {}
|
||||
namespaces[usr.login] = {
|
||||
'personal': True,
|
||||
'id': usr.login,
|
||||
'title': usr.name or usr.login,
|
||||
'avatar_url': usr.avatar_url,
|
||||
'url': usr.html_url,
|
||||
'score': usr.plan.private_repos if usr.plan else 0,
|
||||
}
|
||||
|
||||
for org in usr.get_orgs():
|
||||
organization = org.login if org.login else org.name
|
||||
|
||||
# NOTE: We don't load the organization's html_url nor its plan, because doing
|
||||
# so requires loading *each organization* via its own API call in this tight
|
||||
# loop, which was massively slowing down the load time for users when setting
|
||||
# up triggers.
|
||||
namespaces[organization] = {
|
||||
'personal': False,
|
||||
'id': organization,
|
||||
'title': organization,
|
||||
'avatar_url': org.avatar_url,
|
||||
'url': '',
|
||||
'score': 0,
|
||||
}
|
||||
|
||||
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||
|
||||
@_catch_ssl_errors
|
||||
def list_build_sources_for_namespace(self, namespace):
|
||||
def repo_view(repo):
|
||||
return {
|
||||
'name': repo.name,
|
||||
'full_name': repo.full_name,
|
||||
'description': repo.description or '',
|
||||
'last_updated': timegm(repo.pushed_at.utctimetuple()) if repo.pushed_at else 0,
|
||||
'url': repo.html_url,
|
||||
'has_admin_permissions': repo.permissions.admin,
|
||||
'private': repo.private,
|
||||
}
|
||||
|
||||
gh_client = self._get_client()
|
||||
usr = gh_client.get_user()
|
||||
if namespace == usr.login:
|
||||
repos = [repo_view(repo) for repo in usr.get_repos(type='owner', sort='updated')]
|
||||
return BuildTriggerHandler.build_sources_response(repos)
|
||||
|
||||
try:
|
||||
org = gh_client.get_organization(namespace)
|
||||
if org is None:
|
||||
return []
|
||||
except GithubException:
|
||||
return []
|
||||
|
||||
repos = [repo_view(repo) for repo in org.get_repos(type='member')]
|
||||
return BuildTriggerHandler.build_sources_response(repos)
|
||||
|
||||
|
||||
@_catch_ssl_errors
|
||||
def list_build_subdirs(self):
|
||||
config = self.config
|
||||
gh_client = self._get_client()
|
||||
source = config['build_source']
|
||||
|
||||
try:
|
||||
repo = gh_client.get_repo(source)
|
||||
|
||||
# Find the first matching branch.
|
||||
repo_branches = self.list_field_values('branch_name') or []
|
||||
branches = find_matching_branches(config, repo_branches)
|
||||
branches = branches or [repo.default_branch or 'master']
|
||||
default_commit = repo.get_branch(branches[0]).commit
|
||||
commit_tree = repo.get_git_tree(default_commit.sha, recursive=True)
|
||||
|
||||
return [elem.path for elem in commit_tree.tree
|
||||
if (elem.type == u'blob' and self.filename_is_dockerfile(os.path.basename(elem.path)))]
|
||||
except GithubException as ghe:
|
||||
message = ghe.data.get('message', 'Unable to list contents of repository: %s' % source)
|
||||
if message == 'Branch not found':
|
||||
raise EmptyRepositoryException()
|
||||
|
||||
raise RepositoryReadException(message)
|
||||
|
||||
@_catch_ssl_errors
|
||||
def load_dockerfile_contents(self):
|
||||
config = self.config
|
||||
gh_client = self._get_client()
|
||||
source = config['build_source']
|
||||
|
||||
try:
|
||||
repo = gh_client.get_repo(source)
|
||||
except GithubException as ghe:
|
||||
message = ghe.data.get('message', 'Unable to list contents of repository: %s' % source)
|
||||
raise RepositoryReadException(message)
|
||||
|
||||
path = self.get_dockerfile_path()
|
||||
if not path:
|
||||
return None
|
||||
|
||||
try:
|
||||
file_info = repo.get_contents(path)
|
||||
# TypeError is needed because directory inputs cause a TypeError
|
||||
except (GithubException, TypeError) as ghe:
|
||||
logger.error("got error from trying to find github file %s" % ghe)
|
||||
return None
|
||||
|
||||
if file_info is None:
|
||||
return None
|
||||
|
||||
if isinstance(file_info, list):
|
||||
return None
|
||||
|
||||
content = file_info.content
|
||||
if file_info.encoding == 'base64':
|
||||
content = base64.b64decode(content)
|
||||
return content
|
||||
|
||||
@_catch_ssl_errors
|
||||
def list_field_values(self, field_name, limit=None):
|
||||
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': tag} for tag in tags])
|
||||
|
||||
config = self.config
|
||||
source = config.get('build_source')
|
||||
if source is None:
|
||||
return []
|
||||
|
||||
if field_name == 'tag_name':
|
||||
try:
|
||||
gh_client = self._get_client()
|
||||
repo = gh_client.get_repo(source)
|
||||
gh_tags = repo.get_tags()
|
||||
if limit:
|
||||
gh_tags = repo.get_tags()[0:limit]
|
||||
|
||||
return [tag.name for tag in gh_tags]
|
||||
except GitHubBadCredentialsException:
|
||||
return []
|
||||
except GithubException:
|
||||
logger.exception("Got GitHub Exception when trying to list tags for trigger %s",
|
||||
self.trigger.id)
|
||||
return []
|
||||
|
||||
if field_name == 'branch_name':
|
||||
try:
|
||||
gh_client = self._get_client()
|
||||
repo = gh_client.get_repo(source)
|
||||
gh_branches = repo.get_branches()
|
||||
if limit:
|
||||
gh_branches = repo.get_branches()[0:limit]
|
||||
|
||||
branches = [branch.name for branch in gh_branches]
|
||||
|
||||
if not repo.default_branch in branches:
|
||||
branches.insert(0, repo.default_branch)
|
||||
|
||||
if branches[0] != repo.default_branch:
|
||||
branches.remove(repo.default_branch)
|
||||
branches.insert(0, repo.default_branch)
|
||||
|
||||
return branches
|
||||
except GitHubBadCredentialsException:
|
||||
return ['master']
|
||||
except GithubException:
|
||||
logger.exception("Got GitHub Exception when trying to list branches for trigger %s",
|
||||
self.trigger.id)
|
||||
return ['master']
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _build_metadata_for_commit(cls, commit_sha, ref, repo):
|
||||
try:
|
||||
commit = repo.get_commit(commit_sha)
|
||||
except GithubException:
|
||||
logger.exception('Could not load commit information from GitHub')
|
||||
return None
|
||||
|
||||
commit_info = {
|
||||
'url': commit.html_url,
|
||||
'message': commit.commit.message,
|
||||
'date': commit.last_modified
|
||||
}
|
||||
|
||||
if commit.author:
|
||||
commit_info['author'] = {
|
||||
'username': commit.author.login,
|
||||
'avatar_url': commit.author.avatar_url,
|
||||
'url': commit.author.html_url
|
||||
}
|
||||
|
||||
if commit.committer:
|
||||
commit_info['committer'] = {
|
||||
'username': commit.committer.login,
|
||||
'avatar_url': commit.committer.avatar_url,
|
||||
'url': commit.committer.html_url
|
||||
}
|
||||
|
||||
return {
|
||||
'commit': commit_sha,
|
||||
'ref': ref,
|
||||
'default_branch': repo.default_branch,
|
||||
'git_url': repo.ssh_url,
|
||||
'commit_info': commit_info
|
||||
}
|
||||
|
||||
@_catch_ssl_errors
|
||||
def manual_start(self, run_parameters=None):
|
||||
config = self.config
|
||||
source = config['build_source']
|
||||
|
||||
try:
|
||||
gh_client = self._get_client()
|
||||
repo = gh_client.get_repo(source)
|
||||
default_branch = repo.default_branch
|
||||
except GithubException as ghe:
|
||||
msg = GithubBuildTrigger._get_error_message(ghe, 'Unable to start build trigger')
|
||||
raise TriggerStartException(msg)
|
||||
|
||||
def get_branch_sha(branch_name):
|
||||
try:
|
||||
branch = repo.get_branch(branch_name)
|
||||
return branch.commit.sha
|
||||
except GithubException:
|
||||
raise TriggerStartException('Could not find branch in repository')
|
||||
|
||||
def get_tag_sha(tag_name):
|
||||
tags = {tag.name: tag for tag in repo.get_tags()}
|
||||
if not tag_name in tags:
|
||||
raise TriggerStartException('Could not find tag in repository')
|
||||
|
||||
return tags[tag_name].commit.sha
|
||||
|
||||
# Find the branch or tag to build.
|
||||
(commit_sha, ref) = determine_build_ref(run_parameters, get_branch_sha, get_tag_sha,
|
||||
default_branch)
|
||||
|
||||
metadata = GithubBuildTrigger._build_metadata_for_commit(commit_sha, ref, repo)
|
||||
return self.prepare_build(metadata, is_manual=True)
|
||||
|
||||
@_catch_ssl_errors
|
||||
def lookup_user(self, username):
|
||||
try:
|
||||
gh_client = self._get_client()
|
||||
user = gh_client.get_user(username)
|
||||
return {
|
||||
'html_url': user.html_url,
|
||||
'avatar_url': user.avatar_url
|
||||
}
|
||||
except GithubException:
|
||||
return None
|
||||
|
||||
@_catch_ssl_errors
|
||||
def handle_trigger_request(self, request):
|
||||
# Check the payload to see if we should skip it based on the lack of a head_commit.
|
||||
payload = request.get_json()
|
||||
if payload is None:
|
||||
raise InvalidPayloadException('Missing payload')
|
||||
|
||||
# This is for GitHub's probing/testing.
|
||||
if 'zen' in payload:
|
||||
raise SkipRequestException()
|
||||
|
||||
# Lookup the default branch for the repository.
|
||||
if 'repository' not in payload:
|
||||
raise InvalidPayloadException("Missing 'repository' on request")
|
||||
|
||||
if 'owner' not in payload['repository']:
|
||||
raise InvalidPayloadException("Missing 'owner' on repository")
|
||||
|
||||
if 'name' not in payload['repository']['owner']:
|
||||
raise InvalidPayloadException("Missing owner 'name' on repository")
|
||||
|
||||
if 'name' not in payload['repository']:
|
||||
raise InvalidPayloadException("Missing 'name' on repository")
|
||||
|
||||
default_branch = None
|
||||
lookup_user = None
|
||||
try:
|
||||
repo_full_name = '%s/%s' % (payload['repository']['owner']['name'],
|
||||
payload['repository']['name'])
|
||||
|
||||
gh_client = self._get_client()
|
||||
repo = gh_client.get_repo(repo_full_name)
|
||||
default_branch = repo.default_branch
|
||||
lookup_user = self.lookup_user
|
||||
except GitHubBadCredentialsException:
|
||||
logger.exception('Got GitHub Credentials Exception; Cannot lookup default branch')
|
||||
except GithubException:
|
||||
logger.exception("Got GitHub Exception when trying to start trigger %s", self.trigger.id)
|
||||
raise SkipRequestException()
|
||||
|
||||
logger.debug('GitHub trigger payload %s', payload)
|
||||
metadata = get_transformed_webhook_payload(payload, default_branch=default_branch,
|
||||
lookup_user=lookup_user)
|
||||
prepared = self.prepare_build(metadata)
|
||||
|
||||
# Check if we should skip this build.
|
||||
raise_if_skipped_build(prepared, self.config)
|
||||
return prepared
|
621
buildtrigger/gitlabhandler.py
Normal file
621
buildtrigger/gitlabhandler.py
Normal file
|
@ -0,0 +1,621 @@
|
|||
import os.path
|
||||
import logging
|
||||
|
||||
from calendar import timegm
|
||||
from functools import wraps
|
||||
|
||||
import dateutil.parser
|
||||
import gitlab
|
||||
import requests
|
||||
|
||||
from jsonschema import validate
|
||||
|
||||
from app import app, gitlab_trigger
|
||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||
TriggerDeactivationException, TriggerStartException,
|
||||
SkipRequestException, InvalidPayloadException,
|
||||
TriggerAuthException,
|
||||
determine_build_ref, raise_if_skipped_build,
|
||||
find_matching_branches)
|
||||
from buildtrigger.basehandler import BuildTriggerHandler
|
||||
from endpoints.exception import ExternalServiceError
|
||||
from util.security.ssh import generate_ssh_keypair
|
||||
from util.dict_wrappers import JSONPathDict, SafeDictSetter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GITLAB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'ref': {
|
||||
'type': 'string',
|
||||
},
|
||||
'checkout_sha': {
|
||||
'type': ['string', 'null'],
|
||||
},
|
||||
'repository': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'git_ssh_url': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['git_ssh_url'],
|
||||
},
|
||||
'commits': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'url': {
|
||||
'type': ['string', 'null'],
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
},
|
||||
'timestamp': {
|
||||
'type': 'string',
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'email': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['email'],
|
||||
},
|
||||
},
|
||||
'required': ['id', 'message', 'timestamp'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': ['ref', 'checkout_sha', 'repository'],
|
||||
}
|
||||
|
||||
_ACCESS_LEVEL_MAP = {
|
||||
50: ("owner", True),
|
||||
40: ("master", True),
|
||||
30: ("developer", False),
|
||||
20: ("reporter", False),
|
||||
10: ("guest", False),
|
||||
}
|
||||
|
||||
_PER_PAGE_COUNT = 20
|
||||
|
||||
|
||||
def _catch_timeouts_and_errors(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except requests.exceptions.Timeout:
|
||||
msg = 'Request to the GitLab API timed out'
|
||||
logger.exception(msg)
|
||||
raise ExternalServiceError(msg)
|
||||
except gitlab.GitlabError:
|
||||
msg = 'GitLab API error. Please contact support.'
|
||||
logger.exception(msg)
|
||||
raise ExternalServiceError(msg)
|
||||
return wrapper
|
||||
|
||||
|
||||
def _paginated_iterator(func, exc, **kwargs):
|
||||
""" Returns an iterator over invocations of the given function, automatically handling
|
||||
pagination.
|
||||
"""
|
||||
page = 1
|
||||
while True:
|
||||
result = func(page=page, per_page=_PER_PAGE_COUNT, **kwargs)
|
||||
if result is None or result is False:
|
||||
raise exc
|
||||
|
||||
counter = 0
|
||||
for item in result:
|
||||
yield item
|
||||
counter = counter + 1
|
||||
|
||||
if counter < _PER_PAGE_COUNT:
|
||||
break
|
||||
|
||||
page = page + 1
|
||||
|
||||
|
||||
def get_transformed_webhook_payload(gl_payload, default_branch=None, lookup_user=None,
|
||||
lookup_commit=None):
|
||||
""" Returns the Gitlab webhook JSON payload transformed into our own payload
|
||||
format. If the gl_payload is not valid, returns None.
|
||||
"""
|
||||
try:
|
||||
validate(gl_payload, GITLAB_WEBHOOK_PAYLOAD_SCHEMA)
|
||||
except Exception as exc:
|
||||
raise InvalidPayloadException(exc.message)
|
||||
|
||||
payload = JSONPathDict(gl_payload)
|
||||
|
||||
if payload['object_kind'] != 'push' and payload['object_kind'] != 'tag_push':
|
||||
# Unknown kind of webhook.
|
||||
raise SkipRequestException
|
||||
|
||||
# Check for empty commits. The commits list will be empty if the branch is deleted.
|
||||
commits = payload['commits']
|
||||
if payload['object_kind'] == 'push' and not commits:
|
||||
raise SkipRequestException
|
||||
|
||||
# Check for missing commit information.
|
||||
commit_sha = payload['checkout_sha'] or payload['after']
|
||||
if commit_sha is None or commit_sha == '0000000000000000000000000000000000000000':
|
||||
raise SkipRequestException
|
||||
|
||||
config = SafeDictSetter()
|
||||
config['commit'] = commit_sha
|
||||
config['ref'] = payload['ref']
|
||||
config['default_branch'] = default_branch
|
||||
config['git_url'] = payload['repository.git_ssh_url']
|
||||
|
||||
found_commit = JSONPathDict({})
|
||||
if payload['object_kind'] == 'push' or payload['object_kind'] == 'tag_push':
|
||||
# Find the commit associated with the checkout_sha. Gitlab doesn't (necessary) send this in
|
||||
# any order, so we cannot simply index into the commits list.
|
||||
found_commit = None
|
||||
if commits is not None:
|
||||
for commit in commits:
|
||||
if commit['id'] == payload['checkout_sha']:
|
||||
found_commit = JSONPathDict(commit)
|
||||
break
|
||||
|
||||
if found_commit is None and lookup_commit:
|
||||
checkout_sha = payload['checkout_sha'] or payload['after']
|
||||
found_commit_info = lookup_commit(payload['project_id'], checkout_sha)
|
||||
found_commit = JSONPathDict(dict(found_commit_info) if found_commit_info else {})
|
||||
|
||||
if found_commit is None:
|
||||
raise SkipRequestException
|
||||
|
||||
config['commit_info.url'] = found_commit['url']
|
||||
config['commit_info.message'] = found_commit['message']
|
||||
config['commit_info.date'] = found_commit['timestamp']
|
||||
|
||||
# Note: Gitlab does not send full user information with the payload, so we have to
|
||||
# (optionally) look it up.
|
||||
author_email = found_commit['author.email'] or found_commit['author_email']
|
||||
if lookup_user and author_email:
|
||||
author_info = lookup_user(author_email)
|
||||
if author_info:
|
||||
config['commit_info.author.username'] = author_info['username']
|
||||
config['commit_info.author.url'] = author_info['html_url']
|
||||
config['commit_info.author.avatar_url'] = author_info['avatar_url']
|
||||
|
||||
return config.dict_value()
|
||||
|
||||
|
||||
class GitLabBuildTrigger(BuildTriggerHandler):
|
||||
"""
|
||||
BuildTrigger for GitLab.
|
||||
"""
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'gitlab'
|
||||
|
||||
def _get_authorized_client(self):
|
||||
auth_token = self.auth_token or 'invalid'
|
||||
api_version = self.config.get('API_VERSION', '4')
|
||||
client = gitlab.Gitlab(gitlab_trigger.api_endpoint(), oauth_token=auth_token, timeout=20,
|
||||
api_version=api_version)
|
||||
try:
|
||||
client.auth()
|
||||
except gitlab.GitlabGetError as ex:
|
||||
raise TriggerAuthException(ex.message)
|
||||
|
||||
return client
|
||||
|
||||
def is_active(self):
|
||||
return 'hook_id' in self.config
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
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.
|
||||
gl_project = gl_client.projects.get(new_build_source)
|
||||
if not gl_project:
|
||||
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,
|
||||
},
|
||||
]
|
||||
|
||||
key = gl_project.keys.create({
|
||||
'title': '%s Builder' % app.config['REGISTRY_TITLE'],
|
||||
'key': public_key,
|
||||
})
|
||||
|
||||
if not key:
|
||||
msg = 'Unable to add deploy key to repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
config['key_id'] = key.get_id()
|
||||
|
||||
# Add the webhook to the GitLab repository.
|
||||
hook = gl_project.hooks.create({
|
||||
'url': standard_webhook_url,
|
||||
'push': True,
|
||||
'tag_push': True,
|
||||
'push_events': True,
|
||||
'tag_push_events': True,
|
||||
})
|
||||
if not hook:
|
||||
msg = 'Unable to create webhook on repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
config['hook_id'] = hook.get_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.
|
||||
try:
|
||||
gl_project = gl_client.projects.get(config['build_source'])
|
||||
if not gl_project:
|
||||
config.pop('key_id', None)
|
||||
config.pop('hook_id', None)
|
||||
self.config = config
|
||||
return config
|
||||
except gitlab.GitlabGetError as ex:
|
||||
if ex.response_code != 404:
|
||||
raise
|
||||
|
||||
# Remove the webhook.
|
||||
try:
|
||||
gl_project.hooks.delete(config['hook_id'])
|
||||
except gitlab.GitlabDeleteError as ex:
|
||||
if ex.response_code != 404:
|
||||
raise
|
||||
|
||||
config.pop('hook_id', None)
|
||||
|
||||
# Remove the key
|
||||
try:
|
||||
gl_project.keys.delete(config['key_id'])
|
||||
except gitlab.GitlabDeleteError as ex:
|
||||
if ex.response_code != 404:
|
||||
raise
|
||||
|
||||
config.pop('key_id', None)
|
||||
|
||||
self.config = config
|
||||
return config
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def list_build_source_namespaces(self):
|
||||
gl_client = self._get_authorized_client()
|
||||
current_user = gl_client.user
|
||||
if not current_user:
|
||||
raise RepositoryReadException('Unable to get current user')
|
||||
|
||||
namespaces = {}
|
||||
for namespace in _paginated_iterator(gl_client.namespaces.list, RepositoryReadException):
|
||||
namespace_id = namespace.get_id()
|
||||
if namespace_id in namespaces:
|
||||
namespaces[namespace_id]['score'] = namespaces[namespace_id]['score'] + 1
|
||||
else:
|
||||
owner = namespace.attributes['name']
|
||||
namespaces[namespace_id] = {
|
||||
'personal': namespace.attributes['kind'] == 'user',
|
||||
'id': str(namespace_id),
|
||||
'title': namespace.attributes['name'],
|
||||
'avatar_url': namespace.attributes.get('avatar_url'),
|
||||
'score': 1,
|
||||
'url': namespace.attributes.get('web_url') or '',
|
||||
}
|
||||
|
||||
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||
|
||||
def _get_namespace(self, gl_client, gl_namespace, lazy=False):
|
||||
try:
|
||||
if gl_namespace.attributes['kind'] == 'group':
|
||||
return gl_client.groups.get(gl_namespace.attributes['id'], lazy=lazy)
|
||||
|
||||
if gl_namespace.attributes['kind'] == 'user':
|
||||
return gl_client.users.get(gl_client.user.attributes['id'], lazy=lazy)
|
||||
|
||||
# Note: This doesn't seem to work for IDs retrieved via the namespaces API; the IDs are
|
||||
# different.
|
||||
return gl_client.users.get(gl_namespace.attributes['id'], lazy=lazy)
|
||||
except gitlab.GitlabGetError:
|
||||
return None
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def list_build_sources_for_namespace(self, namespace_id):
|
||||
if not namespace_id:
|
||||
return []
|
||||
|
||||
def repo_view(repo):
|
||||
# Because *anything* can be None in GitLab API!
|
||||
permissions = repo.attributes.get('permissions') or {}
|
||||
group_access = permissions.get('group_access') or {}
|
||||
project_access = permissions.get('project_access') or {}
|
||||
|
||||
missing_group_access = permissions.get('group_access') is None
|
||||
missing_project_access = permissions.get('project_access') is None
|
||||
|
||||
access_level = max(group_access.get('access_level') or 0,
|
||||
project_access.get('access_level') or 0)
|
||||
|
||||
has_admin_permission = _ACCESS_LEVEL_MAP.get(access_level, ("", False))[1]
|
||||
if missing_group_access or missing_project_access:
|
||||
# Default to has permission if we cannot check the permissions. This will allow our users
|
||||
# to select the repository and then GitLab's own checks will ensure that the webhook is
|
||||
# added only if allowed.
|
||||
# TODO: Do we want to display this differently in the UI?
|
||||
has_admin_permission = True
|
||||
|
||||
view = {
|
||||
'name': repo.attributes['path'],
|
||||
'full_name': repo.attributes['path_with_namespace'],
|
||||
'description': repo.attributes.get('description') or '',
|
||||
'url': repo.attributes.get('web_url'),
|
||||
'has_admin_permissions': has_admin_permission,
|
||||
'private': repo.attributes.get('visibility') == 'private',
|
||||
}
|
||||
|
||||
if repo.attributes.get('last_activity_at'):
|
||||
try:
|
||||
last_modified = dateutil.parser.parse(repo.attributes['last_activity_at'])
|
||||
view['last_updated'] = timegm(last_modified.utctimetuple())
|
||||
except ValueError:
|
||||
logger.exception('Gitlab gave us an invalid last_activity_at: %s', last_modified)
|
||||
|
||||
return view
|
||||
|
||||
gl_client = self._get_authorized_client()
|
||||
|
||||
try:
|
||||
gl_namespace = gl_client.namespaces.get(namespace_id)
|
||||
except gitlab.GitlabGetError:
|
||||
return []
|
||||
|
||||
namespace_obj = self._get_namespace(gl_client, gl_namespace, lazy=True)
|
||||
repositories = _paginated_iterator(namespace_obj.projects.list, RepositoryReadException)
|
||||
|
||||
try:
|
||||
return BuildTriggerHandler.build_sources_response([repo_view(repo) for repo in repositories])
|
||||
except gitlab.GitlabGetError:
|
||||
return []
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def list_build_subdirs(self):
|
||||
config = self.config
|
||||
gl_client = self._get_authorized_client()
|
||||
new_build_source = config['build_source']
|
||||
|
||||
gl_project = gl_client.projects.get(new_build_source)
|
||||
if not gl_project:
|
||||
msg = 'Unable to find GitLab repository for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
repo_branches = gl_project.branches.list()
|
||||
if not repo_branches:
|
||||
msg = 'Unable to find GitLab branches for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
branches = [branch.attributes['name'] for branch in repo_branches]
|
||||
branches = find_matching_branches(config, branches)
|
||||
branches = branches or [gl_project.attributes['default_branch'] or 'master']
|
||||
|
||||
repo_tree = gl_project.repository_tree(ref=branches[0])
|
||||
if not repo_tree:
|
||||
msg = 'Unable to find GitLab repository tree for source: %s' % new_build_source
|
||||
raise RepositoryReadException(msg)
|
||||
|
||||
return [node['name'] for node in repo_tree if self.filename_is_dockerfile(node['name'])]
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def load_dockerfile_contents(self):
|
||||
gl_client = self._get_authorized_client()
|
||||
path = self.get_dockerfile_path()
|
||||
|
||||
gl_project = gl_client.projects.get(self.config['build_source'])
|
||||
if not gl_project:
|
||||
return None
|
||||
|
||||
branches = self.list_field_values('branch_name')
|
||||
branches = find_matching_branches(self.config, branches)
|
||||
if branches == []:
|
||||
return None
|
||||
|
||||
branch_name = branches[0]
|
||||
if gl_project.attributes['default_branch'] in branches:
|
||||
branch_name = gl_project.attributes['default_branch']
|
||||
|
||||
try:
|
||||
return gl_project.files.get(path, branch_name).decode()
|
||||
except gitlab.GitlabGetError:
|
||||
return None
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def list_field_values(self, field_name, limit=None):
|
||||
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()
|
||||
gl_project = gl_client.projects.get(self.config['build_source'])
|
||||
if not gl_project:
|
||||
return []
|
||||
|
||||
if field_name == 'tag_name':
|
||||
tags = gl_project.tags.list()
|
||||
if not tags:
|
||||
return []
|
||||
|
||||
if limit:
|
||||
tags = tags[0:limit]
|
||||
|
||||
return [tag.attributes['name'] for tag in tags]
|
||||
|
||||
if field_name == 'branch_name':
|
||||
branches = gl_project.branches.list()
|
||||
if not branches:
|
||||
return []
|
||||
|
||||
if limit:
|
||||
branches = branches[0:limit]
|
||||
|
||||
return [branch.attributes['name'] for branch in branches]
|
||||
|
||||
return None
|
||||
|
||||
def get_repository_url(self):
|
||||
return gitlab_trigger.get_public_url(self.config['build_source'])
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def lookup_commit(self, repo_id, commit_sha):
|
||||
if repo_id is None:
|
||||
return None
|
||||
|
||||
gl_client = self._get_authorized_client()
|
||||
gl_project = gl_client.projects.get(self.config['build_source'], lazy=True)
|
||||
commit = gl_project.commits.get(commit_sha)
|
||||
if not commit:
|
||||
return None
|
||||
|
||||
return commit
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def lookup_user(self, email):
|
||||
gl_client = self._get_authorized_client()
|
||||
try:
|
||||
result = gl_client.users.list(search=email)
|
||||
if not result:
|
||||
return None
|
||||
|
||||
[user] = result
|
||||
return {
|
||||
'username': user.attributes['username'],
|
||||
'html_url': user.attributes['web_url'],
|
||||
'avatar_url': user.attributes['avatar_url']
|
||||
}
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def get_metadata_for_commit(self, commit_sha, ref, repo):
|
||||
commit = self.lookup_commit(repo.get_id(), commit_sha)
|
||||
if commit is None:
|
||||
return None
|
||||
|
||||
metadata = {
|
||||
'commit': commit.attributes['id'],
|
||||
'ref': ref,
|
||||
'default_branch': repo.attributes['default_branch'],
|
||||
'git_url': repo.attributes['ssh_url_to_repo'],
|
||||
'commit_info': {
|
||||
'url': os.path.join(repo.attributes['web_url'], 'commit', commit.attributes['id']),
|
||||
'message': commit.attributes['message'],
|
||||
'date': commit.attributes['committed_date'],
|
||||
},
|
||||
}
|
||||
|
||||
committer = None
|
||||
if 'committer_email' in commit.attributes:
|
||||
committer = self.lookup_user(commit.attributes['committer_email'])
|
||||
|
||||
author = None
|
||||
if 'author_email' in commit.attributes:
|
||||
author = self.lookup_user(commit.attributes['author_email'])
|
||||
|
||||
if committer is not None:
|
||||
metadata['commit_info']['committer'] = {
|
||||
'username': committer['username'],
|
||||
'avatar_url': committer['avatar_url'],
|
||||
'url': committer.get('http_url', ''),
|
||||
}
|
||||
|
||||
if author is not None:
|
||||
metadata['commit_info']['author'] = {
|
||||
'username': author['username'],
|
||||
'avatar_url': author['avatar_url'],
|
||||
'url': author.get('http_url', ''),
|
||||
}
|
||||
|
||||
return metadata
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def manual_start(self, run_parameters=None):
|
||||
gl_client = self._get_authorized_client()
|
||||
gl_project = gl_client.projects.get(self.config['build_source'])
|
||||
if not gl_project:
|
||||
raise TriggerStartException('Could not find repository')
|
||||
|
||||
def get_tag_sha(tag_name):
|
||||
try:
|
||||
tag = gl_project.tags.get(tag_name)
|
||||
except gitlab.GitlabGetError:
|
||||
raise TriggerStartException('Could not find tag in repository')
|
||||
|
||||
return tag.attributes['commit']['id']
|
||||
|
||||
def get_branch_sha(branch_name):
|
||||
try:
|
||||
branch = gl_project.branches.get(branch_name)
|
||||
except gitlab.GitlabGetError:
|
||||
raise TriggerStartException('Could not find branch in repository')
|
||||
|
||||
return branch.attributes['commit']['id']
|
||||
|
||||
# Find the branch or tag to build.
|
||||
(commit_sha, ref) = determine_build_ref(run_parameters, get_branch_sha, get_tag_sha,
|
||||
gl_project.attributes['default_branch'])
|
||||
|
||||
metadata = self.get_metadata_for_commit(commit_sha, ref, gl_project)
|
||||
return self.prepare_build(metadata, is_manual=True)
|
||||
|
||||
@_catch_timeouts_and_errors
|
||||
def handle_trigger_request(self, request):
|
||||
payload = request.get_json()
|
||||
if not payload:
|
||||
raise InvalidPayloadException()
|
||||
|
||||
logger.debug('GitLab trigger payload %s', payload)
|
||||
|
||||
# Lookup the default branch.
|
||||
gl_client = self._get_authorized_client()
|
||||
gl_project = gl_client.projects.get(self.config['build_source'])
|
||||
if not gl_project:
|
||||
logger.debug('Skipping GitLab build; project %s not found', self.config['build_source'])
|
||||
raise InvalidPayloadException()
|
||||
|
||||
def lookup_commit(repo_id, commit_sha):
|
||||
commit = self.lookup_commit(repo_id, commit_sha)
|
||||
if commit is None:
|
||||
return None
|
||||
|
||||
return dict(commit.attributes)
|
||||
|
||||
default_branch = gl_project.attributes['default_branch']
|
||||
metadata = get_transformed_webhook_payload(payload, default_branch=default_branch,
|
||||
lookup_user=self.lookup_user,
|
||||
lookup_commit=lookup_commit)
|
||||
prepared = self.prepare_build(metadata)
|
||||
|
||||
# Check if we should skip this build.
|
||||
raise_if_skipped_build(prepared, self.config)
|
||||
return prepared
|
0
buildtrigger/test/__init__.py
Normal file
0
buildtrigger/test/__init__.py
Normal file
159
buildtrigger/test/bitbucketmock.py
Normal file
159
buildtrigger/test/bitbucketmock.py
Normal file
|
@ -0,0 +1,159 @@
|
|||
from datetime import datetime
|
||||
from mock import Mock
|
||||
|
||||
from buildtrigger.bitbuckethandler import BitbucketBuildTrigger
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
def get_bitbucket_trigger(dockerfile_path=''):
|
||||
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||
trigger = BitbucketBuildTrigger(trigger_obj, {
|
||||
'build_source': 'foo/bar',
|
||||
'dockerfile_path': dockerfile_path,
|
||||
'nickname': 'knownuser',
|
||||
'account_id': 'foo',
|
||||
})
|
||||
|
||||
trigger._get_client = get_mock_bitbucket
|
||||
return trigger
|
||||
|
||||
def get_repo_path_contents(path, revision):
|
||||
data = {
|
||||
'files': [{'path': 'Dockerfile'}],
|
||||
}
|
||||
|
||||
return (True, data, None)
|
||||
|
||||
def get_raw_path_contents(path, revision):
|
||||
if path == 'Dockerfile':
|
||||
return (True, 'hello world', None)
|
||||
|
||||
if path == 'somesubdir/Dockerfile':
|
||||
return (True, 'hi universe', None)
|
||||
|
||||
return (False, None, None)
|
||||
|
||||
def get_branches_and_tags():
|
||||
data = {
|
||||
'branches': [{'name': 'master'}, {'name': 'otherbranch'}],
|
||||
'tags': [{'name': 'sometag'}, {'name': 'someothertag'}],
|
||||
}
|
||||
return (True, data, None)
|
||||
|
||||
def get_branches():
|
||||
return (True, {'master': {}, 'otherbranch': {}}, None)
|
||||
|
||||
def get_tags():
|
||||
return (True, {'sometag': {}, 'someothertag': {}}, None)
|
||||
|
||||
def get_branch(branch_name):
|
||||
if branch_name != 'master':
|
||||
return (False, None, None)
|
||||
|
||||
data = {
|
||||
'target': {
|
||||
'hash': 'aaaaaaa',
|
||||
},
|
||||
}
|
||||
|
||||
return (True, data, None)
|
||||
|
||||
def get_tag(tag_name):
|
||||
if tag_name != 'sometag':
|
||||
return (False, None, None)
|
||||
|
||||
data = {
|
||||
'target': {
|
||||
'hash': 'aaaaaaa',
|
||||
},
|
||||
}
|
||||
|
||||
return (True, data, None)
|
||||
|
||||
def get_changeset_mock(commit_sha):
|
||||
if commit_sha != 'aaaaaaa':
|
||||
return (False, None, 'Not found')
|
||||
|
||||
data = {
|
||||
'node': 'aaaaaaa',
|
||||
'message': 'some message',
|
||||
'timestamp': 'now',
|
||||
'raw_author': 'foo@bar.com',
|
||||
}
|
||||
|
||||
return (True, data, None)
|
||||
|
||||
def get_changesets():
|
||||
changesets_mock = Mock()
|
||||
changesets_mock.get = Mock(side_effect=get_changeset_mock)
|
||||
return changesets_mock
|
||||
|
||||
def get_deploykeys():
|
||||
deploykeys_mock = Mock()
|
||||
deploykeys_mock.create = Mock(return_value=(True, {'pk': 'someprivatekey'}, None))
|
||||
deploykeys_mock.delete = Mock(return_value=(True, {}, None))
|
||||
return deploykeys_mock
|
||||
|
||||
def get_webhooks():
|
||||
webhooks_mock = Mock()
|
||||
webhooks_mock.create = Mock(return_value=(True, {'uuid': 'someuuid'}, None))
|
||||
webhooks_mock.delete = Mock(return_value=(True, {}, None))
|
||||
return webhooks_mock
|
||||
|
||||
def get_repo_mock(name):
|
||||
if name != 'bar':
|
||||
return None
|
||||
|
||||
repo_mock = Mock()
|
||||
repo_mock.get_main_branch = Mock(return_value=(True, {'name': 'master'}, None))
|
||||
repo_mock.get_path_contents = Mock(side_effect=get_repo_path_contents)
|
||||
repo_mock.get_raw_path_contents = Mock(side_effect=get_raw_path_contents)
|
||||
repo_mock.get_branches_and_tags = Mock(side_effect=get_branches_and_tags)
|
||||
repo_mock.get_branches = Mock(side_effect=get_branches)
|
||||
repo_mock.get_tags = Mock(side_effect=get_tags)
|
||||
repo_mock.get_branch = Mock(side_effect=get_branch)
|
||||
repo_mock.get_tag = Mock(side_effect=get_tag)
|
||||
|
||||
repo_mock.changesets = Mock(side_effect=get_changesets)
|
||||
repo_mock.deploykeys = Mock(side_effect=get_deploykeys)
|
||||
repo_mock.webhooks = Mock(side_effect=get_webhooks)
|
||||
return repo_mock
|
||||
|
||||
def get_repositories_mock():
|
||||
repos_mock = Mock()
|
||||
repos_mock.get = Mock(side_effect=get_repo_mock)
|
||||
return repos_mock
|
||||
|
||||
def get_namespace_mock(namespace):
|
||||
namespace_mock = Mock()
|
||||
namespace_mock.repositories = Mock(side_effect=get_repositories_mock)
|
||||
return namespace_mock
|
||||
|
||||
def get_repo(namespace, name):
|
||||
return {
|
||||
'owner': namespace,
|
||||
'logo': 'avatarurl',
|
||||
'slug': name,
|
||||
'description': 'some %s repo' % (name),
|
||||
'utc_last_updated': str(datetime.utcfromtimestamp(0)),
|
||||
'read_only': namespace != 'knownuser',
|
||||
'is_private': name == 'somerepo',
|
||||
}
|
||||
|
||||
def get_visible_repos():
|
||||
repos = [
|
||||
get_repo('knownuser', 'somerepo'),
|
||||
get_repo('someorg', 'somerepo'),
|
||||
get_repo('someorg', 'anotherrepo'),
|
||||
]
|
||||
return (True, repos, None)
|
||||
|
||||
def get_authed_mock(token, secret):
|
||||
authed_mock = Mock()
|
||||
authed_mock.for_namespace = Mock(side_effect=get_namespace_mock)
|
||||
authed_mock.get_visible_repositories = Mock(side_effect=get_visible_repos)
|
||||
return authed_mock
|
||||
|
||||
def get_mock_bitbucket():
|
||||
bitbucket_mock = Mock()
|
||||
bitbucket_mock.get_authorized_client = Mock(side_effect=get_authed_mock)
|
||||
return bitbucket_mock
|
178
buildtrigger/test/githubmock.py
Normal file
178
buildtrigger/test/githubmock.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
from datetime import datetime
|
||||
from mock import Mock
|
||||
|
||||
from github import GithubException
|
||||
|
||||
from buildtrigger.githubhandler import GithubBuildTrigger
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
def get_github_trigger(dockerfile_path=''):
|
||||
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||
trigger = GithubBuildTrigger(trigger_obj, {'build_source': 'foo', 'dockerfile_path': dockerfile_path})
|
||||
trigger._get_client = get_mock_github
|
||||
return trigger
|
||||
|
||||
def get_mock_github():
|
||||
def get_commit_mock(commit_sha):
|
||||
if commit_sha == 'aaaaaaa':
|
||||
commit_mock = Mock()
|
||||
commit_mock.sha = commit_sha
|
||||
commit_mock.html_url = 'http://url/to/commit'
|
||||
commit_mock.last_modified = 'now'
|
||||
|
||||
commit_mock.commit = Mock()
|
||||
commit_mock.commit.message = 'some cool message'
|
||||
|
||||
commit_mock.committer = Mock()
|
||||
commit_mock.committer.login = 'someuser'
|
||||
commit_mock.committer.avatar_url = 'avatarurl'
|
||||
commit_mock.committer.html_url = 'htmlurl'
|
||||
|
||||
commit_mock.author = Mock()
|
||||
commit_mock.author.login = 'someuser'
|
||||
commit_mock.author.avatar_url = 'avatarurl'
|
||||
commit_mock.author.html_url = 'htmlurl'
|
||||
return commit_mock
|
||||
|
||||
raise GithubException(None, None)
|
||||
|
||||
def get_branch_mock(branch_name):
|
||||
if branch_name == 'master':
|
||||
branch_mock = Mock()
|
||||
branch_mock.commit = Mock()
|
||||
branch_mock.commit.sha = 'aaaaaaa'
|
||||
return branch_mock
|
||||
|
||||
raise GithubException(None, None)
|
||||
|
||||
def get_repo_mock(namespace, name):
|
||||
repo_mock = Mock()
|
||||
repo_mock.owner = Mock()
|
||||
repo_mock.owner.login = namespace
|
||||
|
||||
repo_mock.full_name = '%s/%s' % (namespace, name)
|
||||
repo_mock.name = name
|
||||
repo_mock.description = 'some %s repo' % (name)
|
||||
|
||||
if name != 'anotherrepo':
|
||||
repo_mock.pushed_at = datetime.utcfromtimestamp(0)
|
||||
else:
|
||||
repo_mock.pushed_at = None
|
||||
|
||||
repo_mock.html_url = 'https://bitbucket.org/%s/%s' % (namespace, name)
|
||||
repo_mock.private = name == 'somerepo'
|
||||
repo_mock.permissions = Mock()
|
||||
repo_mock.permissions.admin = namespace == 'knownuser'
|
||||
return repo_mock
|
||||
|
||||
def get_user_repos_mock(type='all', sort='created'):
|
||||
return [get_repo_mock('knownuser', 'somerepo')]
|
||||
|
||||
def get_org_repos_mock(type='all'):
|
||||
return [get_repo_mock('someorg', 'somerepo'), get_repo_mock('someorg', 'anotherrepo')]
|
||||
|
||||
def get_orgs_mock():
|
||||
return [get_org_mock('someorg')]
|
||||
|
||||
def get_user_mock(username='knownuser'):
|
||||
if username == 'knownuser':
|
||||
user_mock = Mock()
|
||||
user_mock.name = username
|
||||
user_mock.plan = Mock()
|
||||
user_mock.plan.private_repos = 1
|
||||
user_mock.login = username
|
||||
user_mock.html_url = 'https://bitbucket.org/%s' % (username)
|
||||
user_mock.avatar_url = 'avatarurl'
|
||||
user_mock.get_repos = Mock(side_effect=get_user_repos_mock)
|
||||
user_mock.get_orgs = Mock(side_effect=get_orgs_mock)
|
||||
return user_mock
|
||||
|
||||
raise GithubException(None, None)
|
||||
|
||||
def get_org_mock(namespace):
|
||||
if namespace == 'someorg':
|
||||
org_mock = Mock()
|
||||
org_mock.get_repos = Mock(side_effect=get_org_repos_mock)
|
||||
org_mock.login = namespace
|
||||
org_mock.html_url = 'https://bitbucket.org/%s' % (namespace)
|
||||
org_mock.avatar_url = 'avatarurl'
|
||||
org_mock.name = namespace
|
||||
org_mock.plan = Mock()
|
||||
org_mock.plan.private_repos = 2
|
||||
return org_mock
|
||||
|
||||
raise GithubException(None, None)
|
||||
|
||||
def get_tags_mock():
|
||||
sometag = Mock()
|
||||
sometag.name = 'sometag'
|
||||
sometag.commit = get_commit_mock('aaaaaaa')
|
||||
|
||||
someothertag = Mock()
|
||||
someothertag.name = 'someothertag'
|
||||
someothertag.commit = get_commit_mock('aaaaaaa')
|
||||
return [sometag, someothertag]
|
||||
|
||||
def get_branches_mock():
|
||||
master = Mock()
|
||||
master.name = 'master'
|
||||
master.commit = get_commit_mock('aaaaaaa')
|
||||
|
||||
otherbranch = Mock()
|
||||
otherbranch.name = 'otherbranch'
|
||||
otherbranch.commit = get_commit_mock('aaaaaaa')
|
||||
return [master, otherbranch]
|
||||
|
||||
def get_contents_mock(filepath):
|
||||
if filepath == 'Dockerfile':
|
||||
m = Mock()
|
||||
m.content = 'hello world'
|
||||
return m
|
||||
|
||||
if filepath == 'somesubdir/Dockerfile':
|
||||
m = Mock()
|
||||
m.content = 'hi universe'
|
||||
return m
|
||||
|
||||
raise GithubException(None, None)
|
||||
|
||||
def get_git_tree_mock(commit_sha, recursive=False):
|
||||
first_file = Mock()
|
||||
first_file.type = 'blob'
|
||||
first_file.path = 'Dockerfile'
|
||||
|
||||
second_file = Mock()
|
||||
second_file.type = 'other'
|
||||
second_file.path = '/some/Dockerfile'
|
||||
|
||||
third_file = Mock()
|
||||
third_file.type = 'blob'
|
||||
third_file.path = 'somesubdir/Dockerfile'
|
||||
|
||||
t = Mock()
|
||||
|
||||
if commit_sha == 'aaaaaaa':
|
||||
t.tree = [
|
||||
first_file, second_file, third_file,
|
||||
]
|
||||
else:
|
||||
t.tree = []
|
||||
|
||||
return t
|
||||
|
||||
repo_mock = Mock()
|
||||
repo_mock.default_branch = 'master'
|
||||
repo_mock.ssh_url = 'ssh_url'
|
||||
|
||||
repo_mock.get_branch = Mock(side_effect=get_branch_mock)
|
||||
repo_mock.get_tags = Mock(side_effect=get_tags_mock)
|
||||
repo_mock.get_branches = Mock(side_effect=get_branches_mock)
|
||||
repo_mock.get_commit = Mock(side_effect=get_commit_mock)
|
||||
repo_mock.get_contents = Mock(side_effect=get_contents_mock)
|
||||
repo_mock.get_git_tree = Mock(side_effect=get_git_tree_mock)
|
||||
|
||||
gh_mock = Mock()
|
||||
gh_mock.get_repo = Mock(return_value=repo_mock)
|
||||
gh_mock.get_user = Mock(side_effect=get_user_mock)
|
||||
gh_mock.get_organization = Mock(side_effect=get_org_mock)
|
||||
return gh_mock
|
598
buildtrigger/test/gitlabmock.py
Normal file
598
buildtrigger/test/gitlabmock.py
Normal file
|
@ -0,0 +1,598 @@
|
|||
import base64
|
||||
import json
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
import gitlab
|
||||
|
||||
from httmock import urlmatch, HTTMock
|
||||
|
||||
from buildtrigger.gitlabhandler import GitLabBuildTrigger
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab')
|
||||
def catchall_handler(url, request):
|
||||
return {'status_code': 404}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/users$')
|
||||
def users_handler(url, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
if url.query.find('knownuser') < 0:
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([]),
|
||||
}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([
|
||||
{
|
||||
"id": 1,
|
||||
"username": "knownuser",
|
||||
"name": "Known User",
|
||||
"state": "active",
|
||||
"avatar_url": "avatarurl",
|
||||
"web_url": "https://bitbucket.org/knownuser",
|
||||
},
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/user$')
|
||||
def user_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 1,
|
||||
"username": "john_smith",
|
||||
"email": "john@example.com",
|
||||
"name": "John Smith",
|
||||
"state": "active",
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/foo%2Fbar$')
|
||||
def project_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 4,
|
||||
"description": None,
|
||||
"default_branch": "master",
|
||||
"visibility": "private",
|
||||
"path_with_namespace": "someorg/somerepo",
|
||||
"ssh_url_to_repo": "git@example.com:someorg/somerepo.git",
|
||||
"web_url": "http://example.com/someorg/somerepo",
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/tree$')
|
||||
def project_tree_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([
|
||||
{
|
||||
"id": "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba",
|
||||
"name": "Dockerfile",
|
||||
"type": "tree",
|
||||
"path": "files/Dockerfile",
|
||||
"mode": "040000",
|
||||
},
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/tags$')
|
||||
def project_tags_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([
|
||||
{
|
||||
'name': 'sometag',
|
||||
'commit': {
|
||||
'id': '60a8ff033665e1207714d6670fcd7b65304ec02f',
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'someothertag',
|
||||
'commit': {
|
||||
'id': '60a8ff033665e1207714d6670fcd7b65304ec02f',
|
||||
},
|
||||
},
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/branches$')
|
||||
def project_branches_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([
|
||||
{
|
||||
'name': 'master',
|
||||
'commit': {
|
||||
'id': '60a8ff033665e1207714d6670fcd7b65304ec02f',
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'otherbranch',
|
||||
'commit': {
|
||||
'id': '60a8ff033665e1207714d6670fcd7b65304ec02f',
|
||||
},
|
||||
},
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/branches/master$')
|
||||
def project_branch_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"name": "master",
|
||||
"merged": True,
|
||||
"protected": True,
|
||||
"developers_can_push": False,
|
||||
"developers_can_merge": False,
|
||||
"commit": {
|
||||
"author_email": "john@example.com",
|
||||
"author_name": "John Smith",
|
||||
"authored_date": "2012-06-27T05:51:39-07:00",
|
||||
"committed_date": "2012-06-28T03:44:20-07:00",
|
||||
"committer_email": "john@example.com",
|
||||
"committer_name": "John Smith",
|
||||
"id": "60a8ff033665e1207714d6670fcd7b65304ec02f",
|
||||
"short_id": "7b5c3cc",
|
||||
"title": "add projects API",
|
||||
"message": "add projects API",
|
||||
"parent_ids": [
|
||||
"4ad91d3c1144c406e50c7b33bae684bd6837faf8",
|
||||
],
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/namespaces/someorg$')
|
||||
def namespace_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 2,
|
||||
"name": "someorg",
|
||||
"path": "someorg",
|
||||
"kind": "group",
|
||||
"full_path": "someorg",
|
||||
"parent_id": None,
|
||||
"members_count_with_descendants": 2
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/namespaces/knownuser$')
|
||||
def user_namespace_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 1,
|
||||
"name": "knownuser",
|
||||
"path": "knownuser",
|
||||
"kind": "user",
|
||||
"full_path": "knownuser",
|
||||
"parent_id": None,
|
||||
"members_count_with_descendants": 2
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/namespaces(/)?$')
|
||||
def namespaces_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([{
|
||||
"id": 2,
|
||||
"name": "someorg",
|
||||
"path": "someorg",
|
||||
"kind": "group",
|
||||
"full_path": "someorg",
|
||||
"parent_id": None,
|
||||
"web_url": "http://gitlab.com/groups/someorg",
|
||||
"members_count_with_descendants": 2
|
||||
}]),
|
||||
}
|
||||
|
||||
|
||||
def get_projects_handler(add_permissions_block):
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/groups/2/projects$')
|
||||
def projects_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
permissions_block = {
|
||||
"project_access": {
|
||||
"access_level": 10,
|
||||
"notification_level": 3
|
||||
},
|
||||
"group_access": {
|
||||
"access_level": 20,
|
||||
"notification_level": 3
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([{
|
||||
"id": 4,
|
||||
"name": "Some project",
|
||||
"description": None,
|
||||
"default_branch": "master",
|
||||
"visibility": "private",
|
||||
"path": "someproject",
|
||||
"path_with_namespace": "someorg/someproject",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"web_url": "http://example.com/someorg/someproject",
|
||||
"permissions": permissions_block if add_permissions_block else None,
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Another project",
|
||||
"description": None,
|
||||
"default_branch": "master",
|
||||
"visibility": "public",
|
||||
"path": "anotherproject",
|
||||
"path_with_namespace": "someorg/anotherproject",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"web_url": "http://example.com/someorg/anotherproject",
|
||||
}]),
|
||||
}
|
||||
return projects_handler
|
||||
|
||||
|
||||
def get_group_handler(null_avatar):
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/groups/2$')
|
||||
def group_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 1,
|
||||
"name": "SomeOrg Group",
|
||||
"path": "someorg",
|
||||
"description": "An interesting group",
|
||||
"visibility": "public",
|
||||
"lfs_enabled": True,
|
||||
"avatar_url": 'avatar_url' if not null_avatar else None,
|
||||
"web_url": "http://gitlab.com/groups/someorg",
|
||||
"request_access_enabled": False,
|
||||
"full_name": "SomeOrg Group",
|
||||
"full_path": "someorg",
|
||||
"parent_id": None,
|
||||
}),
|
||||
}
|
||||
return group_handler
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/files/Dockerfile$')
|
||||
def dockerfile_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"file_name": "Dockerfile",
|
||||
"file_path": "Dockerfile",
|
||||
"size": 10,
|
||||
"encoding": "base64",
|
||||
"content": base64.b64encode('hello world'),
|
||||
"ref": "master",
|
||||
"blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
|
||||
"commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
|
||||
"last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d"
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/files/somesubdir%2FDockerfile$')
|
||||
def sub_dockerfile_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"file_name": "Dockerfile",
|
||||
"file_path": "somesubdir/Dockerfile",
|
||||
"size": 10,
|
||||
"encoding": "base64",
|
||||
"content": base64.b64encode('hi universe'),
|
||||
"ref": "master",
|
||||
"blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
|
||||
"commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
|
||||
"last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d"
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/repository/tags/sometag$')
|
||||
def tag_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"name": "sometag",
|
||||
"message": "some cool message",
|
||||
"target": "60a8ff033665e1207714d6670fcd7b65304ec02f",
|
||||
"commit": {
|
||||
"id": "60a8ff033665e1207714d6670fcd7b65304ec02f",
|
||||
"short_id": "60a8ff03",
|
||||
"title": "Initial commit",
|
||||
"created_at": "2017-07-26T11:08:53.000+02:00",
|
||||
"parent_ids": [
|
||||
"f61c062ff8bcbdb00e0a1b3317a91aed6ceee06b"
|
||||
],
|
||||
"message": "v5.0.0\n",
|
||||
"author_name": "Arthur Verschaeve",
|
||||
"author_email": "contact@arthurverschaeve.be",
|
||||
"authored_date": "2015-02-01T21:56:31.000+01:00",
|
||||
"committer_name": "Arthur Verschaeve",
|
||||
"committer_email": "contact@arthurverschaeve.be",
|
||||
"committed_date": "2015-02-01T21:56:31.000+01:00"
|
||||
},
|
||||
"release": None,
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/foo%2Fbar/repository/commits/60a8ff033665e1207714d6670fcd7b65304ec02f$')
|
||||
def commit_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": "60a8ff033665e1207714d6670fcd7b65304ec02f",
|
||||
"short_id": "60a8ff03366",
|
||||
"title": "Sanitize for network graph",
|
||||
"author_name": "someguy",
|
||||
"author_email": "some.guy@gmail.com",
|
||||
"committer_name": "Some Guy",
|
||||
"committer_email": "some.guy@gmail.com",
|
||||
"created_at": "2012-09-20T09:06:12+03:00",
|
||||
"message": "Sanitize for network graph",
|
||||
"committed_date": "2012-09-20T09:06:12+03:00",
|
||||
"authored_date": "2012-09-20T09:06:12+03:00",
|
||||
"parent_ids": [
|
||||
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
|
||||
],
|
||||
"last_pipeline" : {
|
||||
"id": 8,
|
||||
"ref": "master",
|
||||
"sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
|
||||
"status": "created",
|
||||
},
|
||||
"stats": {
|
||||
"additions": 15,
|
||||
"deletions": 10,
|
||||
"total": 25
|
||||
},
|
||||
"status": "running"
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/deploy_keys$', method='POST')
|
||||
def create_deploykey_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 1,
|
||||
"title": "Public key",
|
||||
"key": "ssh-rsa some stuff",
|
||||
"created_at": "2013-10-02T10:12:29Z",
|
||||
"can_push": False,
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/hooks$', method='POST')
|
||||
def create_hook_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({
|
||||
"id": 1,
|
||||
"url": "http://example.com/hook",
|
||||
"project_id": 4,
|
||||
"push_events": True,
|
||||
"issues_events": True,
|
||||
"confidential_issues_events": True,
|
||||
"merge_requests_events": True,
|
||||
"tag_push_events": True,
|
||||
"note_events": True,
|
||||
"job_events": True,
|
||||
"pipeline_events": True,
|
||||
"wiki_page_events": True,
|
||||
"enable_ssl_verification": True,
|
||||
"created_at": "2012-10-12T17:04:47Z",
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/hooks/1$', method='DELETE')
|
||||
def delete_hook_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/projects/4/deploy_keys/1$', method='DELETE')
|
||||
def delete_deploykey_handker(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps({}),
|
||||
}
|
||||
|
||||
|
||||
@urlmatch(netloc=r'fakegitlab', path=r'/api/v4/users/1/projects$')
|
||||
def user_projects_list_handler(_, request):
|
||||
if not request.headers.get('Authorization') == 'Bearer foobar':
|
||||
return {'status_code': 401}
|
||||
|
||||
return {
|
||||
'status_code': 200,
|
||||
'headers': {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
'content': json.dumps([
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Another project",
|
||||
"description": None,
|
||||
"default_branch": "master",
|
||||
"visibility": "public",
|
||||
"path": "anotherproject",
|
||||
"path_with_namespace": "knownuser/anotherproject",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"web_url": "http://example.com/knownuser/anotherproject",
|
||||
}
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_gitlab_trigger(dockerfile_path='', add_permissions=True, missing_avatar_url=False):
|
||||
handlers = [user_handler, users_handler, project_branches_handler, project_tree_handler,
|
||||
project_handler, get_projects_handler(add_permissions), tag_handler,
|
||||
project_branch_handler, get_group_handler(missing_avatar_url), dockerfile_handler,
|
||||
sub_dockerfile_handler, namespace_handler, user_namespace_handler, namespaces_handler,
|
||||
commit_handler, create_deploykey_handler, delete_deploykey_handker,
|
||||
create_hook_handler, delete_hook_handler, project_tags_handler,
|
||||
user_projects_list_handler, catchall_handler]
|
||||
|
||||
with HTTMock(*handlers):
|
||||
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||
trigger = GitLabBuildTrigger(trigger_obj, {
|
||||
'build_source': 'foo/bar',
|
||||
'dockerfile_path': dockerfile_path,
|
||||
'username': 'knownuser'
|
||||
})
|
||||
|
||||
client = gitlab.Gitlab('http://fakegitlab', oauth_token='foobar', timeout=20, api_version=4)
|
||||
client.auth()
|
||||
|
||||
trigger._get_authorized_client = lambda: client
|
||||
yield trigger
|
55
buildtrigger/test/test_basehandler.py
Normal file
55
buildtrigger/test/test_basehandler.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
import pytest
|
||||
|
||||
from buildtrigger.basehandler import BuildTriggerHandler
|
||||
|
||||
|
||||
@pytest.mark.parametrize('input,output', [
|
||||
("Dockerfile", True),
|
||||
("server.Dockerfile", True),
|
||||
(u"Dockerfile", True),
|
||||
(u"server.Dockerfile", True),
|
||||
("bad file name", False),
|
||||
(u"bad file name", False),
|
||||
])
|
||||
def test_path_is_dockerfile(input, output):
|
||||
assert BuildTriggerHandler.filename_is_dockerfile(input) == output
|
||||
|
||||
|
||||
@pytest.mark.parametrize('input,output', [
|
||||
("", {}),
|
||||
("/a", {"/a": ["/"]}),
|
||||
("a", {"/a": ["/"]}),
|
||||
("/b/a", {"/b/a": ["/b", "/"]}),
|
||||
("b/a", {"/b/a": ["/b", "/"]}),
|
||||
("/c/b/a", {"/c/b/a": ["/c/b", "/c", "/"]}),
|
||||
("/a//b//c", {"/a/b/c": ["/", "/a", "/a/b"]}),
|
||||
("/a", {"/a": ["/"]}),
|
||||
])
|
||||
def test_subdir_path_map_no_previous(input, output):
|
||||
actual_mapping = BuildTriggerHandler.get_parent_directory_mappings(input)
|
||||
for key in actual_mapping:
|
||||
value = actual_mapping[key]
|
||||
actual_mapping[key] = value.sort()
|
||||
for key in output:
|
||||
value = output[key]
|
||||
output[key] = value.sort()
|
||||
|
||||
assert actual_mapping == output
|
||||
|
||||
|
||||
@pytest.mark.parametrize('new_path,original_dictionary,output', [
|
||||
("/a", {}, {"/a": ["/"]}),
|
||||
("b", {"/a": ["some_path", "another_path"]}, {"/a": ["some_path", "another_path"], "/b": ["/"]}),
|
||||
("/a/b/c/d", {"/e": ["some_path", "another_path"]},
|
||||
{"/e": ["some_path", "another_path"], "/a/b/c/d": ["/", "/a", "/a/b", "/a/b/c"]}),
|
||||
])
|
||||
def test_subdir_path_map(new_path, original_dictionary, output):
|
||||
actual_mapping = BuildTriggerHandler.get_parent_directory_mappings(new_path, original_dictionary)
|
||||
for key in actual_mapping:
|
||||
value = actual_mapping[key]
|
||||
actual_mapping[key] = value.sort()
|
||||
for key in output:
|
||||
value = output[key]
|
||||
output[key] = value.sort()
|
||||
|
||||
assert actual_mapping == output
|
91
buildtrigger/test/test_bitbuckethandler.py
Normal file
91
buildtrigger/test/test_bitbuckethandler.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
import json
|
||||
import pytest
|
||||
|
||||
from buildtrigger.test.bitbucketmock import get_bitbucket_trigger
|
||||
from buildtrigger.triggerutil import (SkipRequestException, ValidationRequestException,
|
||||
InvalidPayloadException)
|
||||
from endpoints.building import PreparedBuild
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
@pytest.fixture
|
||||
def bitbucket_trigger():
|
||||
return get_bitbucket_trigger()
|
||||
|
||||
|
||||
def test_list_build_subdirs(bitbucket_trigger):
|
||||
assert bitbucket_trigger.list_build_subdirs() == ["/Dockerfile"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dockerfile_path, contents', [
|
||||
('/Dockerfile', 'hello world'),
|
||||
('somesubdir/Dockerfile', 'hi universe'),
|
||||
('unknownpath', None),
|
||||
])
|
||||
def test_load_dockerfile_contents(dockerfile_path, contents):
|
||||
trigger = get_bitbucket_trigger(dockerfile_path)
|
||||
assert trigger.load_dockerfile_contents() == contents
|
||||
|
||||
|
||||
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||
('{}', InvalidPayloadException, "'push' is a required property"),
|
||||
|
||||
# Valid payload:
|
||||
('''{
|
||||
"push": {
|
||||
"changes": [{
|
||||
"new": {
|
||||
"name": "somechange",
|
||||
"target": {
|
||||
"hash": "aaaaaaa",
|
||||
"message": "foo",
|
||||
"date": "now",
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "somelink"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
"repository": {
|
||||
"full_name": "foo/bar"
|
||||
}
|
||||
}''', None, None),
|
||||
|
||||
# Skip message:
|
||||
('''{
|
||||
"push": {
|
||||
"changes": [{
|
||||
"new": {
|
||||
"name": "somechange",
|
||||
"target": {
|
||||
"hash": "aaaaaaa",
|
||||
"message": "[skip build] foo",
|
||||
"date": "now",
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "somelink"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
"repository": {
|
||||
"full_name": "foo/bar"
|
||||
}
|
||||
}''', SkipRequestException, ''),
|
||||
])
|
||||
def test_handle_trigger_request(bitbucket_trigger, payload, expected_error, expected_message):
|
||||
def get_payload():
|
||||
return json.loads(payload)
|
||||
|
||||
request = AttrDict(dict(get_json=get_payload))
|
||||
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
bitbucket_trigger.handle_trigger_request(request)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(bitbucket_trigger.handle_trigger_request(request), PreparedBuild)
|
51
buildtrigger/test/test_customhandler.py
Normal file
51
buildtrigger/test/test_customhandler.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
import pytest
|
||||
|
||||
from buildtrigger.customhandler import CustomBuildTrigger
|
||||
from buildtrigger.triggerutil import (InvalidPayloadException, SkipRequestException,
|
||||
TriggerStartException)
|
||||
from endpoints.building import PreparedBuild
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||
('', InvalidPayloadException, 'Missing expected payload'),
|
||||
('{}', InvalidPayloadException, "'commit' is a required property"),
|
||||
|
||||
('{"commit": "foo", "ref": "refs/heads/something", "default_branch": "baz"}',
|
||||
InvalidPayloadException, "u'foo' does not match '^([A-Fa-f0-9]{7,})$'"),
|
||||
|
||||
('{"commit": "11d6fbc", "ref": "refs/heads/something", "default_branch": "baz"}', None, None),
|
||||
('''{
|
||||
"commit": "11d6fbc",
|
||||
"ref": "refs/heads/something",
|
||||
"default_branch": "baz",
|
||||
"commit_info": {
|
||||
"message": "[skip build]",
|
||||
"url": "http://foo.bar",
|
||||
"date": "NOW"
|
||||
}
|
||||
}''', SkipRequestException, ''),
|
||||
])
|
||||
def test_handle_trigger_request(payload, expected_error, expected_message):
|
||||
trigger = CustomBuildTrigger(None, {'build_source': 'foo'})
|
||||
request = AttrDict(dict(data=payload))
|
||||
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
trigger.handle_trigger_request(request)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(trigger.handle_trigger_request(request), PreparedBuild)
|
||||
|
||||
@pytest.mark.parametrize('run_parameters, expected_error, expected_message', [
|
||||
({}, TriggerStartException, 'missing required parameter'),
|
||||
({'commit_sha': 'foo'}, TriggerStartException, "'foo' does not match '^([A-Fa-f0-9]{7,})$'"),
|
||||
({'commit_sha': '11d6fbc'}, None, None),
|
||||
])
|
||||
def test_manual_start(run_parameters, expected_error, expected_message):
|
||||
trigger = CustomBuildTrigger(None, {'build_source': 'foo'})
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
trigger.manual_start(run_parameters)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(trigger.manual_start(run_parameters), PreparedBuild)
|
121
buildtrigger/test/test_githosthandler.py
Normal file
121
buildtrigger/test/test_githosthandler.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
import pytest
|
||||
|
||||
from buildtrigger.triggerutil import TriggerStartException
|
||||
from buildtrigger.test.bitbucketmock import get_bitbucket_trigger
|
||||
from buildtrigger.test.githubmock import get_github_trigger
|
||||
from endpoints.building import PreparedBuild
|
||||
|
||||
# Note: This test suite executes a common set of tests against all the trigger types specified
|
||||
# in this fixture. Each trigger's mock is expected to return the same data for all of these calls.
|
||||
@pytest.fixture(params=[get_github_trigger(), get_bitbucket_trigger()])
|
||||
def githost_trigger(request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('run_parameters, expected_error, expected_message', [
|
||||
# No branch or tag specified: use the commit of the default branch.
|
||||
({}, None, None),
|
||||
|
||||
# Invalid branch.
|
||||
({'refs': {'kind': 'branch', 'name': 'invalid'}}, TriggerStartException,
|
||||
'Could not find branch in repository'),
|
||||
|
||||
# Invalid tag.
|
||||
({'refs': {'kind': 'tag', 'name': 'invalid'}}, TriggerStartException,
|
||||
'Could not find tag in repository'),
|
||||
|
||||
# Valid branch.
|
||||
({'refs': {'kind': 'branch', 'name': 'master'}}, None, None),
|
||||
|
||||
# Valid tag.
|
||||
({'refs': {'kind': 'tag', 'name': 'sometag'}}, None, None),
|
||||
])
|
||||
def test_manual_start(run_parameters, expected_error, expected_message, githost_trigger):
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
githost_trigger.manual_start(run_parameters)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(githost_trigger.manual_start(run_parameters), PreparedBuild)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, expected', [
|
||||
('refs', [
|
||||
{'kind': 'branch', 'name': 'master'},
|
||||
{'kind': 'branch', 'name': 'otherbranch'},
|
||||
{'kind': 'tag', 'name': 'sometag'},
|
||||
{'kind': 'tag', 'name': 'someothertag'},
|
||||
]),
|
||||
('tag_name', set(['sometag', 'someothertag'])),
|
||||
('branch_name', set(['master', 'otherbranch'])),
|
||||
('invalid', None)
|
||||
])
|
||||
def test_list_field_values(name, expected, githost_trigger):
|
||||
if expected is None:
|
||||
assert githost_trigger.list_field_values(name) is None
|
||||
elif isinstance(expected, set):
|
||||
assert set(githost_trigger.list_field_values(name)) == set(expected)
|
||||
else:
|
||||
assert githost_trigger.list_field_values(name) == expected
|
||||
|
||||
|
||||
def test_list_build_source_namespaces():
|
||||
namespaces_expected = [
|
||||
{
|
||||
'personal': True,
|
||||
'score': 1,
|
||||
'avatar_url': 'avatarurl',
|
||||
'id': 'knownuser',
|
||||
'title': 'knownuser',
|
||||
'url': 'https://bitbucket.org/knownuser',
|
||||
},
|
||||
{
|
||||
'score': 2,
|
||||
'title': 'someorg',
|
||||
'personal': False,
|
||||
'url': 'https://bitbucket.org/someorg',
|
||||
'avatar_url': 'avatarurl',
|
||||
'id': 'someorg'
|
||||
}
|
||||
]
|
||||
|
||||
found = get_bitbucket_trigger().list_build_source_namespaces()
|
||||
found.sort()
|
||||
|
||||
namespaces_expected.sort()
|
||||
assert found == namespaces_expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('namespace, expected', [
|
||||
('', []),
|
||||
('unknown', []),
|
||||
|
||||
('knownuser', [
|
||||
{
|
||||
'last_updated': 0, 'name': 'somerepo',
|
||||
'url': 'https://bitbucket.org/knownuser/somerepo', 'private': True,
|
||||
'full_name': 'knownuser/somerepo', 'has_admin_permissions': True,
|
||||
'description': 'some somerepo repo'
|
||||
}]),
|
||||
|
||||
('someorg', [
|
||||
{
|
||||
'last_updated': 0, 'name': 'somerepo',
|
||||
'url': 'https://bitbucket.org/someorg/somerepo', 'private': True,
|
||||
'full_name': 'someorg/somerepo', 'has_admin_permissions': False,
|
||||
'description': 'some somerepo repo'
|
||||
},
|
||||
{
|
||||
'last_updated': 0, 'name': 'anotherrepo',
|
||||
'url': 'https://bitbucket.org/someorg/anotherrepo', 'private': False,
|
||||
'full_name': 'someorg/anotherrepo', 'has_admin_permissions': False,
|
||||
'description': 'some anotherrepo repo'
|
||||
}]),
|
||||
])
|
||||
def test_list_build_sources_for_namespace(namespace, expected, githost_trigger):
|
||||
assert githost_trigger.list_build_sources_for_namespace(namespace) == expected
|
||||
|
||||
|
||||
def test_activate_and_deactivate(githost_trigger):
|
||||
_, private_key = githost_trigger.activate('http://some/url')
|
||||
assert 'private_key' in private_key
|
||||
githost_trigger.deactivate()
|
117
buildtrigger/test/test_githubhandler.py
Normal file
117
buildtrigger/test/test_githubhandler.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
import json
|
||||
import pytest
|
||||
|
||||
from buildtrigger.test.githubmock import get_github_trigger
|
||||
from buildtrigger.triggerutil import (SkipRequestException, ValidationRequestException,
|
||||
InvalidPayloadException)
|
||||
from endpoints.building import PreparedBuild
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
@pytest.fixture
|
||||
def github_trigger():
|
||||
return get_github_trigger()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||
('{"zen": true}', SkipRequestException, ""),
|
||||
|
||||
('{}', InvalidPayloadException, "Missing 'repository' on request"),
|
||||
('{"repository": "foo"}', InvalidPayloadException, "Missing 'owner' on repository"),
|
||||
|
||||
# Valid payload:
|
||||
('''{
|
||||
"repository": {
|
||||
"owner": {
|
||||
"name": "someguy"
|
||||
},
|
||||
"name": "somerepo",
|
||||
"ssh_url": "someurl"
|
||||
},
|
||||
"ref": "refs/tags/foo",
|
||||
"head_commit": {
|
||||
"id": "11d6fbc",
|
||||
"url": "http://some/url",
|
||||
"message": "some message",
|
||||
"timestamp": "NOW"
|
||||
}
|
||||
}''', None, None),
|
||||
|
||||
# Skip message:
|
||||
('''{
|
||||
"repository": {
|
||||
"owner": {
|
||||
"name": "someguy"
|
||||
},
|
||||
"name": "somerepo",
|
||||
"ssh_url": "someurl"
|
||||
},
|
||||
"ref": "refs/tags/foo",
|
||||
"head_commit": {
|
||||
"id": "11d6fbc",
|
||||
"url": "http://some/url",
|
||||
"message": "[skip build]",
|
||||
"timestamp": "NOW"
|
||||
}
|
||||
}''', SkipRequestException, ''),
|
||||
])
|
||||
def test_handle_trigger_request(github_trigger, payload, expected_error, expected_message):
|
||||
def get_payload():
|
||||
return json.loads(payload)
|
||||
|
||||
request = AttrDict(dict(get_json=get_payload))
|
||||
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
github_trigger.handle_trigger_request(request)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(github_trigger.handle_trigger_request(request), PreparedBuild)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dockerfile_path, contents', [
|
||||
('/Dockerfile', 'hello world'),
|
||||
('somesubdir/Dockerfile', 'hi universe'),
|
||||
('unknownpath', None),
|
||||
])
|
||||
def test_load_dockerfile_contents(dockerfile_path, contents):
|
||||
trigger = get_github_trigger(dockerfile_path)
|
||||
assert trigger.load_dockerfile_contents() == contents
|
||||
|
||||
|
||||
@pytest.mark.parametrize('username, expected_response', [
|
||||
('unknownuser', None),
|
||||
('knownuser', {'html_url': 'https://bitbucket.org/knownuser', 'avatar_url': 'avatarurl'}),
|
||||
])
|
||||
def test_lookup_user(username, expected_response, github_trigger):
|
||||
assert github_trigger.lookup_user(username) == expected_response
|
||||
|
||||
|
||||
def test_list_build_subdirs(github_trigger):
|
||||
assert github_trigger.list_build_subdirs() == ['Dockerfile', 'somesubdir/Dockerfile']
|
||||
|
||||
|
||||
def test_list_build_source_namespaces(github_trigger):
|
||||
namespaces_expected = [
|
||||
{
|
||||
'personal': True,
|
||||
'score': 1,
|
||||
'avatar_url': 'avatarurl',
|
||||
'id': 'knownuser',
|
||||
'title': 'knownuser',
|
||||
'url': 'https://bitbucket.org/knownuser',
|
||||
},
|
||||
{
|
||||
'score': 0,
|
||||
'title': 'someorg',
|
||||
'personal': False,
|
||||
'url': '',
|
||||
'avatar_url': 'avatarurl',
|
||||
'id': 'someorg'
|
||||
}
|
||||
]
|
||||
|
||||
found = github_trigger.list_build_source_namespaces()
|
||||
found.sort()
|
||||
|
||||
namespaces_expected.sort()
|
||||
assert found == namespaces_expected
|
231
buildtrigger/test/test_gitlabhandler.py
Normal file
231
buildtrigger/test/test_gitlabhandler.py
Normal file
|
@ -0,0 +1,231 @@
|
|||
import json
|
||||
import pytest
|
||||
|
||||
from mock import Mock
|
||||
|
||||
from buildtrigger.test.gitlabmock import get_gitlab_trigger
|
||||
from buildtrigger.triggerutil import (SkipRequestException, ValidationRequestException,
|
||||
InvalidPayloadException, TriggerStartException)
|
||||
from endpoints.building import PreparedBuild
|
||||
from util.morecollections import AttrDict
|
||||
|
||||
@pytest.fixture()
|
||||
def gitlab_trigger():
|
||||
with get_gitlab_trigger() as t:
|
||||
yield t
|
||||
|
||||
|
||||
def test_list_build_subdirs(gitlab_trigger):
|
||||
assert gitlab_trigger.list_build_subdirs() == ['Dockerfile']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dockerfile_path, contents', [
|
||||
('/Dockerfile', 'hello world'),
|
||||
('somesubdir/Dockerfile', 'hi universe'),
|
||||
('unknownpath', None),
|
||||
])
|
||||
def test_load_dockerfile_contents(dockerfile_path, contents):
|
||||
with get_gitlab_trigger(dockerfile_path=dockerfile_path) as trigger:
|
||||
assert trigger.load_dockerfile_contents() == contents
|
||||
|
||||
|
||||
@pytest.mark.parametrize('email, expected_response', [
|
||||
('unknown@email.com', None),
|
||||
('knownuser', {'username': 'knownuser', 'html_url': 'https://bitbucket.org/knownuser',
|
||||
'avatar_url': 'avatarurl'}),
|
||||
])
|
||||
def test_lookup_user(email, expected_response, gitlab_trigger):
|
||||
assert gitlab_trigger.lookup_user(email) == expected_response
|
||||
|
||||
|
||||
def test_null_permissions():
|
||||
with get_gitlab_trigger(add_permissions=False) as trigger:
|
||||
sources = trigger.list_build_sources_for_namespace('someorg')
|
||||
source = sources[0]
|
||||
assert source['has_admin_permissions']
|
||||
|
||||
|
||||
def test_list_build_sources():
|
||||
with get_gitlab_trigger() as trigger:
|
||||
sources = trigger.list_build_sources_for_namespace('someorg')
|
||||
assert sources == [
|
||||
{
|
||||
'last_updated': 1380548762,
|
||||
'name': u'someproject',
|
||||
'url': u'http://example.com/someorg/someproject',
|
||||
'private': True,
|
||||
'full_name': u'someorg/someproject',
|
||||
'has_admin_permissions': False,
|
||||
'description': ''
|
||||
},
|
||||
{
|
||||
'last_updated': 1380548762,
|
||||
'name': u'anotherproject',
|
||||
'url': u'http://example.com/someorg/anotherproject',
|
||||
'private': False,
|
||||
'full_name': u'someorg/anotherproject',
|
||||
'has_admin_permissions': True,
|
||||
'description': '',
|
||||
}]
|
||||
|
||||
|
||||
def test_null_avatar():
|
||||
with get_gitlab_trigger(missing_avatar_url=True) as trigger:
|
||||
namespace_data = trigger.list_build_source_namespaces()
|
||||
expected = {
|
||||
'avatar_url': None,
|
||||
'personal': False,
|
||||
'title': u'someorg',
|
||||
'url': u'http://gitlab.com/groups/someorg',
|
||||
'score': 1,
|
||||
'id': '2',
|
||||
}
|
||||
|
||||
assert namespace_data == [expected]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||
('{}', InvalidPayloadException, ''),
|
||||
|
||||
# Valid payload:
|
||||
('''{
|
||||
"object_kind": "push",
|
||||
"ref": "refs/heads/master",
|
||||
"checkout_sha": "aaaaaaa",
|
||||
"repository": {
|
||||
"git_ssh_url": "foobar"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "aaaaaaa",
|
||||
"url": "someurl",
|
||||
"message": "hello there!",
|
||||
"timestamp": "now"
|
||||
}
|
||||
]
|
||||
}''', None, None),
|
||||
|
||||
# Skip message:
|
||||
('''{
|
||||
"object_kind": "push",
|
||||
"ref": "refs/heads/master",
|
||||
"checkout_sha": "aaaaaaa",
|
||||
"repository": {
|
||||
"git_ssh_url": "foobar"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "aaaaaaa",
|
||||
"url": "someurl",
|
||||
"message": "[skip build] hello there!",
|
||||
"timestamp": "now"
|
||||
}
|
||||
]
|
||||
}''', SkipRequestException, ''),
|
||||
])
|
||||
def test_handle_trigger_request(gitlab_trigger, payload, expected_error, expected_message):
|
||||
def get_payload():
|
||||
return json.loads(payload)
|
||||
|
||||
request = AttrDict(dict(get_json=get_payload))
|
||||
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
gitlab_trigger.handle_trigger_request(request)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(gitlab_trigger.handle_trigger_request(request), PreparedBuild)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('run_parameters, expected_error, expected_message', [
|
||||
# No branch or tag specified: use the commit of the default branch.
|
||||
({}, None, None),
|
||||
|
||||
# Invalid branch.
|
||||
({'refs': {'kind': 'branch', 'name': 'invalid'}}, TriggerStartException,
|
||||
'Could not find branch in repository'),
|
||||
|
||||
# Invalid tag.
|
||||
({'refs': {'kind': 'tag', 'name': 'invalid'}}, TriggerStartException,
|
||||
'Could not find tag in repository'),
|
||||
|
||||
# Valid branch.
|
||||
({'refs': {'kind': 'branch', 'name': 'master'}}, None, None),
|
||||
|
||||
# Valid tag.
|
||||
({'refs': {'kind': 'tag', 'name': 'sometag'}}, None, None),
|
||||
])
|
||||
def test_manual_start(run_parameters, expected_error, expected_message, gitlab_trigger):
|
||||
if expected_error is not None:
|
||||
with pytest.raises(expected_error) as ipe:
|
||||
gitlab_trigger.manual_start(run_parameters)
|
||||
assert str(ipe.value) == expected_message
|
||||
else:
|
||||
assert isinstance(gitlab_trigger.manual_start(run_parameters), PreparedBuild)
|
||||
|
||||
|
||||
def test_activate_and_deactivate(gitlab_trigger):
|
||||
_, private_key = gitlab_trigger.activate('http://some/url')
|
||||
assert 'private_key' in private_key
|
||||
|
||||
gitlab_trigger.deactivate()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, expected', [
|
||||
('refs', [
|
||||
{'kind': 'branch', 'name': 'master'},
|
||||
{'kind': 'branch', 'name': 'otherbranch'},
|
||||
{'kind': 'tag', 'name': 'sometag'},
|
||||
{'kind': 'tag', 'name': 'someothertag'},
|
||||
]),
|
||||
('tag_name', set(['sometag', 'someothertag'])),
|
||||
('branch_name', set(['master', 'otherbranch'])),
|
||||
('invalid', None)
|
||||
])
|
||||
def test_list_field_values(name, expected, gitlab_trigger):
|
||||
if expected is None:
|
||||
assert gitlab_trigger.list_field_values(name) is None
|
||||
elif isinstance(expected, set):
|
||||
assert set(gitlab_trigger.list_field_values(name)) == set(expected)
|
||||
else:
|
||||
assert gitlab_trigger.list_field_values(name) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('namespace, expected', [
|
||||
('', []),
|
||||
('unknown', []),
|
||||
|
||||
('knownuser', [
|
||||
{
|
||||
'last_updated': 1380548762,
|
||||
'name': u'anotherproject',
|
||||
'url': u'http://example.com/knownuser/anotherproject',
|
||||
'private': False,
|
||||
'full_name': u'knownuser/anotherproject',
|
||||
'has_admin_permissions': True,
|
||||
'description': ''
|
||||
},
|
||||
]),
|
||||
|
||||
('someorg', [
|
||||
{
|
||||
'last_updated': 1380548762,
|
||||
'name': u'someproject',
|
||||
'url': u'http://example.com/someorg/someproject',
|
||||
'private': True,
|
||||
'full_name': u'someorg/someproject',
|
||||
'has_admin_permissions': False,
|
||||
'description': ''
|
||||
},
|
||||
{
|
||||
'last_updated': 1380548762,
|
||||
'name': u'anotherproject',
|
||||
'url': u'http://example.com/someorg/anotherproject',
|
||||
'private': False,
|
||||
'full_name': u'someorg/anotherproject',
|
||||
'has_admin_permissions': True,
|
||||
'description': '',
|
||||
}]),
|
||||
])
|
||||
def test_list_build_sources_for_namespace(namespace, expected, gitlab_trigger):
|
||||
assert gitlab_trigger.list_build_sources_for_namespace(namespace) == expected
|
572
buildtrigger/test/test_prepare_trigger.py
Normal file
572
buildtrigger/test/test_prepare_trigger.py
Normal file
|
@ -0,0 +1,572 @@
|
|||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from jsonschema import validate
|
||||
|
||||
from buildtrigger.customhandler import custom_trigger_payload
|
||||
from buildtrigger.basehandler import METADATA_SCHEMA
|
||||
from buildtrigger.bitbuckethandler import get_transformed_webhook_payload as bb_webhook
|
||||
from buildtrigger.bitbuckethandler import get_transformed_commit_info as bb_commit
|
||||
from buildtrigger.githubhandler import get_transformed_webhook_payload as gh_webhook
|
||||
from buildtrigger.gitlabhandler import get_transformed_webhook_payload as gl_webhook
|
||||
from buildtrigger.triggerutil import SkipRequestException
|
||||
|
||||
def assertSkipped(filename, processor, *args, **kwargs):
|
||||
with open('buildtrigger/test/triggerjson/%s.json' % filename) as f:
|
||||
payload = json.loads(f.read())
|
||||
|
||||
nargs = [payload]
|
||||
nargs.extend(args)
|
||||
|
||||
with pytest.raises(SkipRequestException):
|
||||
processor(*nargs, **kwargs)
|
||||
|
||||
|
||||
def assertSchema(filename, expected, processor, *args, **kwargs):
|
||||
with open('buildtrigger/test/triggerjson/%s.json' % filename) as f:
|
||||
payload = json.loads(f.read())
|
||||
|
||||
nargs = [payload]
|
||||
nargs.extend(args)
|
||||
|
||||
created = processor(*nargs, **kwargs)
|
||||
assert created == expected
|
||||
validate(created, METADATA_SCHEMA)
|
||||
|
||||
|
||||
def test_custom_custom():
|
||||
expected = {
|
||||
u'commit':u'1c002dd',
|
||||
u'commit_info': {
|
||||
u'url': u'gitsoftware.com/repository/commits/1234567',
|
||||
u'date': u'timestamp',
|
||||
u'message': u'initial commit',
|
||||
u'committer': {
|
||||
u'username': u'user',
|
||||
u'url': u'gitsoftware.com/users/user',
|
||||
u'avatar_url': u'gravatar.com/user.png'
|
||||
},
|
||||
u'author': {
|
||||
u'username': u'user',
|
||||
u'url': u'gitsoftware.com/users/user',
|
||||
u'avatar_url': u'gravatar.com/user.png'
|
||||
}
|
||||
},
|
||||
u'ref': u'refs/heads/master',
|
||||
u'default_branch': u'master',
|
||||
u'git_url': u'foobar',
|
||||
}
|
||||
|
||||
assertSchema('custom_webhook', expected, custom_trigger_payload, git_url='foobar')
|
||||
|
||||
|
||||
def test_custom_gitlab():
|
||||
expected = {
|
||||
'commit': u'fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'ref': u'refs/heads/master',
|
||||
'git_url': u'git@gitlab.com:jsmith/somerepo.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/jsmith/somerepo/commit/fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'date': u'2015-08-13T19:33:18+00:00',
|
||||
'message': u'Fix link\n',
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('gitlab_webhook', expected, custom_trigger_payload, git_url='git@gitlab.com:jsmith/somerepo.git')
|
||||
|
||||
|
||||
def test_custom_github():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile',
|
||||
'committer': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
'author': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('github_webhook', expected, custom_trigger_payload,
|
||||
git_url='git@github.com:jsmith/anothertest.git')
|
||||
|
||||
|
||||
def test_custom_bitbucket():
|
||||
expected = {
|
||||
"commit": u"af64ae7188685f8424040b4735ad12941b980d75",
|
||||
"ref": u"refs/heads/master",
|
||||
"git_url": u"git@bitbucket.org:jsmith/another-repo.git",
|
||||
"commit_info": {
|
||||
"url": u"https://bitbucket.org/jsmith/another-repo/commits/af64ae7188685f8424040b4735ad12941b980d75",
|
||||
"date": u"2015-09-10T20:40:54+00:00",
|
||||
"message": u"Dockerfile edited online with Bitbucket",
|
||||
"author": {
|
||||
"username": u"John Smith",
|
||||
"avatar_url": u"https://bitbucket.org/account/jsmith/avatar/32/",
|
||||
},
|
||||
"committer": {
|
||||
"username": u"John Smith",
|
||||
"avatar_url": u"https://bitbucket.org/account/jsmith/avatar/32/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('bitbucket_webhook', expected, custom_trigger_payload, git_url='git@bitbucket.org:jsmith/another-repo.git')
|
||||
|
||||
|
||||
def test_bitbucket_customer_payload_noauthor():
|
||||
expected = {
|
||||
"commit": "a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"ref": "refs/heads/master",
|
||||
"git_url": "git@bitbucket.org:somecoollabs/svc-identity.git",
|
||||
"commit_info": {
|
||||
"url": "https://bitbucket.org/somecoollabs/svc-identity/commits/a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"date": "2015-09-25T00:55:08+00:00",
|
||||
"message": "Update version.py to 0.1.2 [skip ci]\n\n(by utilitybelt/scripts/autotag_version.py)\n",
|
||||
"committer": {
|
||||
"username": "CodeShip Tagging",
|
||||
"avatar_url": "https://bitbucket.org/account/SomeCoolLabs_CodeShip/avatar/32/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('bitbucket_customer_example_noauthor', expected, bb_webhook)
|
||||
|
||||
|
||||
def test_bitbucket_customer_payload_tag():
|
||||
expected = {
|
||||
"commit": "a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"ref": "refs/tags/0.1.2",
|
||||
"git_url": "git@bitbucket.org:somecoollabs/svc-identity.git",
|
||||
"commit_info": {
|
||||
"url": "https://bitbucket.org/somecoollabs/svc-identity/commits/a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"date": "2015-09-25T00:55:08+00:00",
|
||||
"message": "Update version.py to 0.1.2 [skip ci]\n\n(by utilitybelt/scripts/autotag_version.py)\n",
|
||||
"committer": {
|
||||
"username": "CodeShip Tagging",
|
||||
"avatar_url": "https://bitbucket.org/account/SomeCoolLabs_CodeShip/avatar/32/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('bitbucket_customer_example_tag', expected, bb_webhook)
|
||||
|
||||
|
||||
def test_bitbucket_commit():
|
||||
ref = 'refs/heads/somebranch'
|
||||
default_branch = 'somebranch'
|
||||
repository_name = 'foo/bar'
|
||||
|
||||
def lookup_author(_):
|
||||
return {
|
||||
'user': {
|
||||
'display_name': 'cooluser',
|
||||
'avatar': 'http://some/avatar/url'
|
||||
}
|
||||
}
|
||||
|
||||
expected = {
|
||||
"commit": u"abdeaf1b2b4a6b9ddf742c1e1754236380435a62",
|
||||
"ref": u"refs/heads/somebranch",
|
||||
"git_url": u"git@bitbucket.org:foo/bar.git",
|
||||
"default_branch": u"somebranch",
|
||||
"commit_info": {
|
||||
"url": u"https://bitbucket.org/foo/bar/commits/abdeaf1b2b4a6b9ddf742c1e1754236380435a62",
|
||||
"date": u"2012-07-24 00:26:36",
|
||||
"message": u"making some changes\n",
|
||||
"author": {
|
||||
"avatar_url": u"http://some/avatar/url",
|
||||
"username": u"cooluser",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertSchema('bitbucket_commit', expected, bb_commit, ref, default_branch,
|
||||
repository_name, lookup_author)
|
||||
|
||||
def test_bitbucket_webhook_payload():
|
||||
expected = {
|
||||
"commit": u"af64ae7188685f8424040b4735ad12941b980d75",
|
||||
"ref": u"refs/heads/master",
|
||||
"git_url": u"git@bitbucket.org:jsmith/another-repo.git",
|
||||
"commit_info": {
|
||||
"url": u"https://bitbucket.org/jsmith/another-repo/commits/af64ae7188685f8424040b4735ad12941b980d75",
|
||||
"date": u"2015-09-10T20:40:54+00:00",
|
||||
"message": u"Dockerfile edited online with Bitbucket",
|
||||
"author": {
|
||||
"username": u"John Smith",
|
||||
"avatar_url": u"https://bitbucket.org/account/jsmith/avatar/32/",
|
||||
},
|
||||
"committer": {
|
||||
"username": u"John Smith",
|
||||
"avatar_url": u"https://bitbucket.org/account/jsmith/avatar/32/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('bitbucket_webhook', expected, bb_webhook)
|
||||
|
||||
|
||||
def test_github_webhook_payload_slash_branch():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/slash/branch',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile',
|
||||
'committer': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
'author': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('github_webhook_slash_branch', expected, gh_webhook)
|
||||
|
||||
|
||||
def test_github_webhook_payload():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile',
|
||||
'committer': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
'author': {
|
||||
'username': u'jsmith',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('github_webhook', expected, gh_webhook)
|
||||
|
||||
|
||||
def test_github_webhook_payload_with_lookup():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile',
|
||||
'committer': {
|
||||
'username': u'jsmith',
|
||||
'url': u'http://github.com/jsmith',
|
||||
'avatar_url': u'http://some/avatar/url',
|
||||
},
|
||||
'author': {
|
||||
'username': u'jsmith',
|
||||
'url': u'http://github.com/jsmith',
|
||||
'avatar_url': u'http://some/avatar/url',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return {
|
||||
'html_url': 'http://github.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url'
|
||||
}
|
||||
|
||||
assertSchema('github_webhook', expected, gh_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_github_webhook_payload_missing_fields_with_lookup():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile'
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_user(username):
|
||||
if not username:
|
||||
raise Exception('Fail!')
|
||||
|
||||
return {
|
||||
'html_url': 'http://github.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url'
|
||||
}
|
||||
|
||||
assertSchema('github_webhook_missing', expected, gh_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_gitlab_webhook_payload():
|
||||
expected = {
|
||||
'commit': u'fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'ref': u'refs/heads/master',
|
||||
'git_url': u'git@gitlab.com:jsmith/somerepo.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/jsmith/somerepo/commit/fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'date': u'2015-08-13T19:33:18+00:00',
|
||||
'message': u'Fix link\n',
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('gitlab_webhook', expected, gl_webhook)
|
||||
|
||||
|
||||
def test_github_webhook_payload_known_issue():
|
||||
expected = {
|
||||
"commit": "118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"ref": "refs/heads/master",
|
||||
"default_branch": "master",
|
||||
"git_url": "git@github.com:jsmith/docker-test.git",
|
||||
"commit_info": {
|
||||
"url": "https://github.com/jsmith/docker-test/commit/118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"date": "2015-09-25T14:55:11-04:00",
|
||||
"message": "Fail",
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('github_webhook_noname', expected, gh_webhook)
|
||||
|
||||
|
||||
def test_github_webhook_payload_missing_fields():
|
||||
expected = {
|
||||
'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
'git_url': u'git@github.com:jsmith/anothertest.git',
|
||||
'commit_info': {
|
||||
'url': u'https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c',
|
||||
'date': u'2015-09-11T14:26:16-04:00',
|
||||
'message': u'Update Dockerfile'
|
||||
},
|
||||
}
|
||||
|
||||
assertSchema('github_webhook_missing', expected, gh_webhook)
|
||||
|
||||
|
||||
def test_gitlab_webhook_nocommit_payload():
|
||||
assertSkipped('gitlab_webhook_nocommit', gl_webhook)
|
||||
|
||||
|
||||
def test_gitlab_webhook_multiple_commits():
|
||||
expected = {
|
||||
'commit': u'9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53',
|
||||
'ref': u'refs/heads/master',
|
||||
'git_url': u'git@gitlab.com:jsmith/some-test-project.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/jsmith/some-test-project/commit/9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53',
|
||||
'date': u'2016-09-29T15:02:41+00:00',
|
||||
'message': u"Merge branch 'foobar' into 'master'\r\n\r\nAdd changelog\r\n\r\nSome merge thing\r\n\r\nSee merge request !1",
|
||||
'author': {
|
||||
'username': 'jsmith',
|
||||
'url': 'http://gitlab.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url'
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return {
|
||||
'username': 'jsmith',
|
||||
'html_url': 'http://gitlab.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
}
|
||||
|
||||
assertSchema('gitlab_webhook_multicommit', expected, gl_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_gitlab_webhook_for_tag():
|
||||
expected = {
|
||||
'commit': u'82b3d5ae55f7080f1e6022629cdb57bfae7cccc7',
|
||||
'commit_info': {
|
||||
'author': {
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
'url': 'http://gitlab.com/jsmith',
|
||||
'username': 'jsmith'
|
||||
},
|
||||
'date': '2015-08-13T19:33:18+00:00',
|
||||
'message': 'Fix link\n',
|
||||
'url': 'https://some/url',
|
||||
},
|
||||
'git_url': u'git@example.com:jsmith/example.git',
|
||||
'ref': u'refs/tags/v1.0.0',
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return {
|
||||
'username': 'jsmith',
|
||||
'html_url': 'http://gitlab.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
}
|
||||
|
||||
def lookup_commit(repo_id, commit_sha):
|
||||
if commit_sha == '82b3d5ae55f7080f1e6022629cdb57bfae7cccc7':
|
||||
return {
|
||||
"id": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"message": "Fix link\n",
|
||||
"timestamp": "2015-08-13T19:33:18+00:00",
|
||||
"url": "https://some/url",
|
||||
"author_name": "Foo Guy",
|
||||
"author_email": "foo@bar.com",
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
assertSchema('gitlab_webhook_tag', expected, gl_webhook, lookup_user=lookup_user,
|
||||
lookup_commit=lookup_commit)
|
||||
|
||||
|
||||
def test_gitlab_webhook_for_tag_nocommit():
|
||||
assertSkipped('gitlab_webhook_tag', gl_webhook)
|
||||
|
||||
|
||||
def test_gitlab_webhook_for_tag_commit_sha_null():
|
||||
assertSkipped('gitlab_webhook_tag_commit_sha_null', gl_webhook)
|
||||
|
||||
|
||||
def test_gitlab_webhook_for_tag_known_issue():
|
||||
expected = {
|
||||
'commit': u'770830e7ca132856991e6db4f7fc0f4dbe20bd5f',
|
||||
'ref': u'refs/tags/thirdtag',
|
||||
'git_url': u'git@gitlab.com:someuser/some-test-project.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/someuser/some-test-project/commit/770830e7ca132856991e6db4f7fc0f4dbe20bd5f',
|
||||
'date': u'2019-10-17T18:07:48Z',
|
||||
'message': u'Update Dockerfile',
|
||||
'author': {
|
||||
'username': 'someuser',
|
||||
'url': 'http://gitlab.com/someuser',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return {
|
||||
'username': 'someuser',
|
||||
'html_url': 'http://gitlab.com/someuser',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
}
|
||||
|
||||
assertSchema('gitlab_webhook_tag_commit_issue', expected, gl_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_gitlab_webhook_payload_known_issue():
|
||||
expected = {
|
||||
'commit': u'770830e7ca132856991e6db4f7fc0f4dbe20bd5f',
|
||||
'ref': u'refs/tags/fourthtag',
|
||||
'git_url': u'git@gitlab.com:someuser/some-test-project.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/someuser/some-test-project/commit/770830e7ca132856991e6db4f7fc0f4dbe20bd5f',
|
||||
'date': u'2019-10-17T18:07:48Z',
|
||||
'message': u'Update Dockerfile',
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_commit(repo_id, commit_sha):
|
||||
if commit_sha == '770830e7ca132856991e6db4f7fc0f4dbe20bd5f':
|
||||
return {
|
||||
"added": [],
|
||||
"author": {
|
||||
"name": "Some User",
|
||||
"email": "someuser@somedomain.com"
|
||||
},
|
||||
"url": "https://gitlab.com/someuser/some-test-project/commit/770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"message": "Update Dockerfile",
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
],
|
||||
"id": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f"
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
assertSchema('gitlab_webhook_known_issue', expected, gl_webhook, lookup_commit=lookup_commit)
|
||||
|
||||
|
||||
def test_gitlab_webhook_for_other():
|
||||
assertSkipped('gitlab_webhook_other', gl_webhook)
|
||||
|
||||
|
||||
def test_gitlab_webhook_payload_with_lookup():
|
||||
expected = {
|
||||
'commit': u'fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'ref': u'refs/heads/master',
|
||||
'git_url': u'git@gitlab.com:jsmith/somerepo.git',
|
||||
'commit_info': {
|
||||
'url': u'https://gitlab.com/jsmith/somerepo/commit/fb88379ee45de28a0a4590fddcbd8eff8b36026e',
|
||||
'date': u'2015-08-13T19:33:18+00:00',
|
||||
'message': u'Fix link\n',
|
||||
'author': {
|
||||
'username': 'jsmith',
|
||||
'url': 'http://gitlab.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return {
|
||||
'username': 'jsmith',
|
||||
'html_url': 'http://gitlab.com/jsmith',
|
||||
'avatar_url': 'http://some/avatar/url',
|
||||
}
|
||||
|
||||
assertSchema('gitlab_webhook', expected, gl_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_github_webhook_payload_deleted_commit():
|
||||
expected = {
|
||||
'commit': u'456806b662cb903a0febbaed8344f3ed42f27bab',
|
||||
'commit_info': {
|
||||
'author': {
|
||||
'username': u'jsmith'
|
||||
},
|
||||
'committer': {
|
||||
'username': u'jsmith'
|
||||
},
|
||||
'date': u'2015-12-08T18:07:03-05:00',
|
||||
'message': (u'Merge pull request #1044 from jsmith/errerror\n\n' +
|
||||
'Assign the exception to a variable to log it'),
|
||||
'url': u'https://github.com/jsmith/somerepo/commit/456806b662cb903a0febbaed8344f3ed42f27bab'
|
||||
},
|
||||
'git_url': u'git@github.com:jsmith/somerepo.git',
|
||||
'ref': u'refs/heads/master',
|
||||
'default_branch': u'master',
|
||||
}
|
||||
|
||||
def lookup_user(_):
|
||||
return None
|
||||
|
||||
assertSchema('github_webhook_deletedcommit', expected, gh_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_github_webhook_known_issue():
|
||||
def lookup_user(_):
|
||||
return None
|
||||
|
||||
assertSkipped('github_webhook_knownissue', gh_webhook, lookup_user=lookup_user)
|
||||
|
||||
|
||||
def test_bitbucket_webhook_known_issue():
|
||||
assertSkipped('bitbucket_knownissue', bb_webhook)
|
25
buildtrigger/test/test_triggerutil.py
Normal file
25
buildtrigger/test/test_triggerutil.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from buildtrigger.triggerutil import matches_ref
|
||||
|
||||
@pytest.mark.parametrize('ref, filt, matches', [
|
||||
('ref/heads/master', '.+', True),
|
||||
('ref/heads/master', 'heads/.+', True),
|
||||
('ref/heads/master', 'heads/master', True),
|
||||
('ref/heads/slash/branch', 'heads/slash/branch', True),
|
||||
('ref/heads/slash/branch', 'heads/.+', True),
|
||||
|
||||
('ref/heads/foobar', 'heads/master', False),
|
||||
('ref/heads/master', 'tags/master', False),
|
||||
|
||||
('ref/heads/master', '(((heads/alpha)|(heads/beta))|(heads/gamma))|(heads/master)', True),
|
||||
('ref/heads/alpha', '(((heads/alpha)|(heads/beta))|(heads/gamma))|(heads/master)', True),
|
||||
('ref/heads/beta', '(((heads/alpha)|(heads/beta))|(heads/gamma))|(heads/master)', True),
|
||||
('ref/heads/gamma', '(((heads/alpha)|(heads/beta))|(heads/gamma))|(heads/master)', True),
|
||||
|
||||
('ref/heads/delta', '(((heads/alpha)|(heads/beta))|(heads/gamma))|(heads/master)', False),
|
||||
])
|
||||
def test_matches_ref(ref, filt, matches):
|
||||
assert matches_ref(ref, re.compile(filt)) == matches
|
24
buildtrigger/test/triggerjson/bitbucket_commit.json
Normal file
24
buildtrigger/test/triggerjson/bitbucket_commit.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"files": [
|
||||
{
|
||||
"type": "added",
|
||||
"file": "AnotherFile.txt"
|
||||
},
|
||||
{
|
||||
"type": "modified",
|
||||
"file": "Readme"
|
||||
}
|
||||
],
|
||||
"raw_author": "Mark Anthony <manthony@example.com>",
|
||||
"utctimestamp": "2012-07-23 22:26:36+00:00",
|
||||
"author": "Mark Anthony",
|
||||
"timestamp": "2012-07-24 00:26:36",
|
||||
"node": "abdeaf1b2b4a6b9ddf742c1e1754236380435a62",
|
||||
"parents": [
|
||||
"86432202a2d5"
|
||||
],
|
||||
"branch": "master",
|
||||
"message": "making some changes\n",
|
||||
"revision": null,
|
||||
"size": -1
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
{
|
||||
"actor": {
|
||||
"account_id": "SomeCoolLabs_CodeShip",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/SomeCoolLabs_CodeShip"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/SomeCoolLabs_CodeShip/avatar/32/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "CodeShip Tagging"
|
||||
},
|
||||
"repository": {
|
||||
"full_name": "somecoollabs/svc-identity",
|
||||
"name": "svc-identity",
|
||||
"scm": "git",
|
||||
"type": "repository",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/avatar/16/"
|
||||
}
|
||||
},
|
||||
"is_private": true,
|
||||
"owner": {
|
||||
"account_id": "somecoollabs",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/teams/somecoollabs"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/somecoollabs/avatar/32/"
|
||||
}
|
||||
},
|
||||
"type": "team",
|
||||
"display_name": "Some Cool Labs"
|
||||
}
|
||||
},
|
||||
"push": {
|
||||
"changes": [
|
||||
{
|
||||
"commits": [
|
||||
{
|
||||
"hash": "a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"raw": "scripts/autotag_version.py <utilitybelt@somecoollabs.com>"
|
||||
},
|
||||
"type": "commit",
|
||||
"message": "Update version.py to 0.1.2 [skip ci]\n\n(by utilitybelt/scripts/autotag_version.py)\n"
|
||||
}
|
||||
],
|
||||
"created": false,
|
||||
"forced": false,
|
||||
"old": {
|
||||
"target": {
|
||||
"parents": [
|
||||
{
|
||||
"hash": "bd749165b0c50c65c15fc4df526b8e9df26eff10",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/bd749165b0c50c65c15fc4df526b8e9df26eff10"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/bd749165b0c50c65c15fc4df526b8e9df26eff10"
|
||||
}
|
||||
},
|
||||
"type": "commit"
|
||||
},
|
||||
{
|
||||
"hash": "910b5624b74190dfaa51938d851563a4c5254926",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/910b5624b74190dfaa51938d851563a4c5254926"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/910b5624b74190dfaa51938d851563a4c5254926"
|
||||
}
|
||||
},
|
||||
"type": "commit"
|
||||
}
|
||||
],
|
||||
"date": "2015-09-25T00:54:41+00:00",
|
||||
"type": "commit",
|
||||
"message": "Merged in create-update-user (pull request #3)\n\nCreate + update identity\n",
|
||||
"hash": "263736ecc250113fad56a93f83b712093554ad42",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/263736ecc250113fad56a93f83b712093554ad42"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/263736ecc250113fad56a93f83b712093554ad42"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"raw": "John Smith <j@smith.com>",
|
||||
"user": {
|
||||
"account_id": "jsmith",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith"
|
||||
}
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/refs/branches/master"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commits/master"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/branch/master"
|
||||
}
|
||||
},
|
||||
"name": "master",
|
||||
"type": "branch"
|
||||
},
|
||||
"links": {
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/diff/a0ec139843b2bb281ab21a433266ddc498e605dc..263736ecc250113fad56a93f83b712093554ad42"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commits?include=a0ec139843b2bb281ab21a433266ddc498e605dc&exclude=263736ecc250113fad56a93f83b712093554ad42"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/branches/compare/a0ec139843b2bb281ab21a433266ddc498e605dc..263736ecc250113fad56a93f83b712093554ad42"
|
||||
}
|
||||
},
|
||||
"new": {
|
||||
"target": {
|
||||
"parents": [
|
||||
{
|
||||
"hash": "263736ecc250113fad56a93f83b712093554ad42",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/263736ecc250113fad56a93f83b712093554ad42"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/263736ecc250113fad56a93f83b712093554ad42"
|
||||
}
|
||||
},
|
||||
"type": "commit"
|
||||
}
|
||||
],
|
||||
"date": "2015-09-25T00:55:08+00:00",
|
||||
"type": "commit",
|
||||
"message": "Update version.py to 0.1.2 [skip ci]\n\n(by utilitybelt/scripts/autotag_version.py)\n",
|
||||
"hash": "a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"raw": "scripts/autotag_version.py <utilitybelt@somecoollabs.com>"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/refs/branches/master"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commits/master"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/branch/master"
|
||||
}
|
||||
},
|
||||
"name": "master",
|
||||
"type": "branch"
|
||||
},
|
||||
"closed": false,
|
||||
"truncated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"push": {
|
||||
"changes": [
|
||||
{
|
||||
"links": {
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commits?include=a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
}
|
||||
},
|
||||
"closed": false,
|
||||
"new": {
|
||||
"target": {
|
||||
"date": "2015-09-25T00:55:08+00:00",
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/a0ec139843b2bb281ab21a433266ddc498e605dc"
|
||||
}
|
||||
},
|
||||
"message": "Update version.py to 0.1.2 [skip ci]\n\n(by utilitybelt/scripts/autotag_version.py)\n",
|
||||
"type": "commit",
|
||||
"parents": [
|
||||
{
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/263736ecc250113fad56a93f83b712093554ad42"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commit/263736ecc250113fad56a93f83b712093554ad42"
|
||||
}
|
||||
},
|
||||
"hash": "263736ecc250113fad56a93f83b712093554ad42",
|
||||
"type": "commit"
|
||||
}
|
||||
],
|
||||
"hash": "a0ec139843b2bb281ab21a433266ddc498e605dc",
|
||||
"author": {
|
||||
"raw": "scripts/autotag_version.py <utilitybelt@somecoollabs.com>"
|
||||
}
|
||||
},
|
||||
"name": "0.1.2",
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/commits/tag/0.1.2"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/refs/tags/0.1.2"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity/commits/0.1.2"
|
||||
}
|
||||
},
|
||||
"type": "tag"
|
||||
},
|
||||
"truncated": false,
|
||||
"created": true,
|
||||
"old": null,
|
||||
"forced": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"name": "svc-identity",
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/somecoollabs/svc-identity"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/somecoollabs/svc-identity/avatar/16/"
|
||||
}
|
||||
},
|
||||
"is_private": true,
|
||||
"type": "repository",
|
||||
"scm": "git",
|
||||
"owner": {
|
||||
"account_id": "somecoollabs",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/teams/somecoollabs"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/somecoollabs/avatar/32/"
|
||||
}
|
||||
},
|
||||
"display_name": "Some Cool Labs",
|
||||
"type": "team"
|
||||
},
|
||||
"full_name": "somecoollabs/svc-identity"
|
||||
},
|
||||
"actor": {
|
||||
"account_id": "SomeCoolLabs_CodeShip",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/SomeCoolLabs_CodeShip"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/SomeCoolLabs_CodeShip/avatar/32/"
|
||||
}
|
||||
},
|
||||
"display_name": "CodeShip Tagging",
|
||||
"type": "user"
|
||||
}
|
||||
}
|
68
buildtrigger/test/triggerjson/bitbucket_knownissue.json
Normal file
68
buildtrigger/test/triggerjson/bitbucket_knownissue.json
Normal file
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"push": {
|
||||
"changes": [
|
||||
|
||||
]
|
||||
},
|
||||
"actor": {
|
||||
"account_id": "jsmith",
|
||||
"display_name": "John Smith",
|
||||
"type": "user",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https:\/\/api.bitbucket.org\/2.0\/users\/jsmith"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https:\/\/bitbucket.org\/account\/jsmith\/avatar\/32\/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"website": "",
|
||||
"scm": "git",
|
||||
"name": "slip-api",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/goldcuff\/slip-api"
|
||||
},
|
||||
"html": {
|
||||
"href": "https:\/\/bitbucket.org\/goldcuff\/slip-api"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https:\/\/bitbucket.org\/goldcuff\/slip-api\/avatar\/32\/"
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https:\/\/api.bitbucket.org\/2.0\/teams\/goldcuff\/projects\/SLIP"
|
||||
},
|
||||
"html": {
|
||||
"href": "https:\/\/bitbucket.org\/account\/user\/goldcuff\/projects\/SLIP"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https:\/\/bitbucket.org\/account\/user\/goldcuff\/projects\/SLIP\/avatar\/32"
|
||||
}
|
||||
},
|
||||
"type": "project",
|
||||
"name": "SLIP",
|
||||
"key": "SLIP"
|
||||
},
|
||||
"full_name": "goldcuff\/slip-api",
|
||||
"owner": {
|
||||
"account_id": "goldcuff",
|
||||
"display_name": "Goldcuff",
|
||||
"type": "team",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https:\/\/api.bitbucket.org\/2.0\/teams\/goldcuff"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https:\/\/bitbucket.org\/account\/goldcuff\/avatar\/32\/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"is_private": true
|
||||
}
|
||||
}
|
219
buildtrigger/test/triggerjson/bitbucket_webhook.json
Normal file
219
buildtrigger/test/triggerjson/bitbucket_webhook.json
Normal file
|
@ -0,0 +1,219 @@
|
|||
{
|
||||
"push": {
|
||||
"changes": [
|
||||
{
|
||||
"links": {
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commits?include=af64ae7188685f8424040b4735ad12941b980d75&exclude=1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
},
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/diff/af64ae7188685f8424040b4735ad12941b980d75..1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/branches/compare/af64ae7188685f8424040b4735ad12941b980d75..1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
}
|
||||
},
|
||||
"old": {
|
||||
"name": "master",
|
||||
"links": {
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commits/master"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/branch/master"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/refs/branches/master"
|
||||
}
|
||||
},
|
||||
"type": "branch",
|
||||
"target": {
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/commits/1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commit/1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith",
|
||||
"account_id": "jsmith"
|
||||
},
|
||||
"raw": "John Smith <j@smith.com>"
|
||||
},
|
||||
"date": "2015-09-10T20:37:54+00:00",
|
||||
"parents": [
|
||||
{
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/commits/5329daa0961ec968de9ef36f30024bfa0da73103"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commit/5329daa0961ec968de9ef36f30024bfa0da73103"
|
||||
}
|
||||
},
|
||||
"type": "commit",
|
||||
"hash": "5329daa0961ec968de9ef36f30024bfa0da73103"
|
||||
}
|
||||
],
|
||||
"type": "commit",
|
||||
"message": "Dockerfile edited online with Bitbucket",
|
||||
"hash": "1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
}
|
||||
},
|
||||
"forced": false,
|
||||
"truncated": false,
|
||||
"commits": [
|
||||
{
|
||||
"author": {
|
||||
"user": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith",
|
||||
"account_id": "jsmith"
|
||||
},
|
||||
"raw": "John Smith <j@smith.com>"
|
||||
},
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/commits/af64ae7188685f8424040b4735ad12941b980d75"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commit/af64ae7188685f8424040b4735ad12941b980d75"
|
||||
}
|
||||
},
|
||||
"message": "Dockerfile edited online with Bitbucket",
|
||||
"type": "commit",
|
||||
"hash": "af64ae7188685f8424040b4735ad12941b980d75"
|
||||
}
|
||||
],
|
||||
"new": {
|
||||
"name": "master",
|
||||
"links": {
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commits/master"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/branch/master"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/refs/branches/master"
|
||||
}
|
||||
},
|
||||
"type": "branch",
|
||||
"target": {
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/commits/af64ae7188685f8424040b4735ad12941b980d75"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commit/af64ae7188685f8424040b4735ad12941b980d75"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith",
|
||||
"account_id": "jsmith"
|
||||
},
|
||||
"raw": "John Smith <j@smith.com>"
|
||||
},
|
||||
"date": "2015-09-10T20:40:54+00:00",
|
||||
"parents": [
|
||||
{
|
||||
"links": {
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/commits/1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo/commit/1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
}
|
||||
},
|
||||
"type": "commit",
|
||||
"hash": "1784139225279a587e0afb151bed1f9ba3dd509e"
|
||||
}
|
||||
],
|
||||
"type": "commit",
|
||||
"message": "Dockerfile edited online with Bitbucket",
|
||||
"hash": "af64ae7188685f8424040b4735ad12941b980d75"
|
||||
}
|
||||
},
|
||||
"closed": false,
|
||||
"created": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo/avatar/16/"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/jsmith/another-repo"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/jsmith/another-repo"
|
||||
}
|
||||
},
|
||||
"full_name": "jsmith/another-repo",
|
||||
"type": "repository",
|
||||
"is_private": true,
|
||||
"name": "Another Repo",
|
||||
"owner": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith",
|
||||
"account_id": "jsmith"
|
||||
},
|
||||
"scm": "git"
|
||||
},
|
||||
"actor": {
|
||||
"links": {
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/jsmith/avatar/32/"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/jsmith"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"display_name": "John Smith",
|
||||
"account_id": "jsmith"
|
||||
}
|
||||
}
|
20
buildtrigger/test/triggerjson/custom_webhook.json
Normal file
20
buildtrigger/test/triggerjson/custom_webhook.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"commit": "1c002dd",
|
||||
"ref": "refs/heads/master",
|
||||
"default_branch": "master",
|
||||
"commit_info": {
|
||||
"url": "gitsoftware.com/repository/commits/1234567",
|
||||
"message": "initial commit",
|
||||
"date": "timestamp",
|
||||
"author": {
|
||||
"username": "user",
|
||||
"avatar_url": "gravatar.com/user.png",
|
||||
"url": "gitsoftware.com/users/user"
|
||||
},
|
||||
"committer": {
|
||||
"username": "user",
|
||||
"avatar_url": "gravatar.com/user.png",
|
||||
"url": "gitsoftware.com/users/user"
|
||||
}
|
||||
}
|
||||
}
|
153
buildtrigger/test/triggerjson/github_webhook.json
Normal file
153
buildtrigger/test/triggerjson/github_webhook.json
Normal file
|
@ -0,0 +1,153 @@
|
|||
{
|
||||
"ref": "refs/heads/master",
|
||||
"before": "9ea43cab474709d4a61afb7e3340de1ffc405b41",
|
||||
"after": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/anothertest/compare/9ea43cab4747...410f4cdf8ff0",
|
||||
"commits": [
|
||||
{
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"author": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"author": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"id": 1234567,
|
||||
"name": "anothertest",
|
||||
"full_name": "jsmith/anothertest",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/jsmith/anothertest",
|
||||
"description": "",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/anothertest",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/anothertest/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/anothertest/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/anothertest/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/anothertest/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/anothertest/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/anothertest/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/anothertest/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/anothertest/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/anothertest/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/anothertest/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/anothertest/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/anothertest/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/anothertest/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/anothertest/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/anothertest/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/anothertest/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/anothertest/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/anothertest/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/anothertest/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/anothertest/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/anothertest/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/anothertest/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/anothertest/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/anothertest/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/anothertest/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/anothertest/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/anothertest/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/anothertest/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/anothertest/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/anothertest/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/anothertest/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/anothertest/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/anothertest/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/anothertest/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/anothertest/releases{/id}",
|
||||
"created_at": 1430426945,
|
||||
"updated_at": "2015-04-30T20:49:05Z",
|
||||
"pushed_at": 1441995976,
|
||||
"git_url": "git://github.com/jsmith/anothertest.git",
|
||||
"ssh_url": "git@github.com:jsmith/anothertest.git",
|
||||
"clone_url": "https://github.com/jsmith/anothertest.git",
|
||||
"svn_url": "https://github.com/jsmith/anothertest",
|
||||
"homepage": null,
|
||||
"size": 144,
|
||||
"stargazers_count": 0,
|
||||
"watchers_count": 0,
|
||||
"language": null,
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 0,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 0,
|
||||
"forks": 0,
|
||||
"open_issues": 0,
|
||||
"watchers": 0,
|
||||
"default_branch": "master",
|
||||
"stargazers": 0,
|
||||
"master_branch": "master"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 1234567,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1234567?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
199
buildtrigger/test/triggerjson/github_webhook_deletedcommit.json
Normal file
199
buildtrigger/test/triggerjson/github_webhook_deletedcommit.json
Normal file
|
@ -0,0 +1,199 @@
|
|||
{
|
||||
"ref": "refs/heads/master",
|
||||
"before": "c7fa613b99d509c0d4fcbf946f0415b5f024150b",
|
||||
"after": "456806b662cb903a0febbaed8344f3ed42f27bab",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/somerepo/compare/c7fa613b99d5...456806b662cb",
|
||||
"commits": [
|
||||
{
|
||||
"id": "e00365b225ad7f454982e9198756cc1ab5dc4428",
|
||||
"distinct": true,
|
||||
"message": "Assign the exception to a variable to log it",
|
||||
"timestamp": "2015-12-08T18:03:48-05:00",
|
||||
"url": "https://github.com/jsmith/somerepo/commit/e00365b225ad7f454982e9198756cc1ab5dc4428",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"storage/basestorage.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "456806b662cb903a0febbaed8344f3ed42f27bab",
|
||||
"distinct": true,
|
||||
"message": "Merge pull request #1044 from jsmith/errerror\n\nAssign the exception to a variable to log it",
|
||||
"timestamp": "2015-12-08T18:07:03-05:00",
|
||||
"url": "https://github.com/jsmith/somerepo/commit/456806b662cb903a0febbaed8344f3ed42f27bab",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"storage/basestorage.py"
|
||||
]
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "456806b662cb903a0febbaed8344f3ed42f27bab",
|
||||
"distinct": true,
|
||||
"message": "Merge pull request #1044 from jsmith/errerror\n\nAssign the exception to a variable to log it",
|
||||
"timestamp": "2015-12-08T18:07:03-05:00",
|
||||
"url": "https://github.com/jsmith/somerepo/commit/456806b662cb903a0febbaed8344f3ed42f27bab",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"storage/basestorage.py"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"id": 12345678,
|
||||
"name": "somerepo",
|
||||
"full_name": "jsmith/somerepo",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": null
|
||||
},
|
||||
"private": true,
|
||||
"html_url": "https://github.com/jsmith/somerepo",
|
||||
"description": "Some Cool Repo",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/somerepo",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/somerepo/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/somerepo/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/somerepo/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/somerepo/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/somerepo/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/somerepo/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/somerepo/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/somerepo/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/somerepo/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/somerepo/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/somerepo/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/somerepo/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/somerepo/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/somerepo/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/somerepo/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/somerepo/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/somerepo/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/somerepo/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/somerepo/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/somerepo/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/somerepo/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/somerepo/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/somerepo/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/somerepo/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/somerepo/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/somerepo/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/somerepo/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/somerepo/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/somerepo/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/somerepo/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/somerepo/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/somerepo/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/somerepo/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/somerepo/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/somerepo/releases{/id}",
|
||||
"created_at": 1415056063,
|
||||
"updated_at": "2015-11-12T05:16:51Z",
|
||||
"pushed_at": 1449616023,
|
||||
"git_url": "git://github.com/jsmith/somerepo.git",
|
||||
"ssh_url": "git@github.com:jsmith/somerepo.git",
|
||||
"clone_url": "https://github.com/jsmith/somerepo.git",
|
||||
"svn_url": "https://github.com/jsmith/somerepo",
|
||||
"homepage": "",
|
||||
"size": 183677,
|
||||
"stargazers_count": 3,
|
||||
"watchers_count": 3,
|
||||
"language": "Python",
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": false,
|
||||
"has_pages": false,
|
||||
"forks_count": 8,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 188,
|
||||
"forks": 8,
|
||||
"open_issues": 188,
|
||||
"watchers": 3,
|
||||
"default_branch": "master",
|
||||
"stargazers": 3,
|
||||
"master_branch": "master",
|
||||
"organization": "jsmith"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"organization": {
|
||||
"login": "jsmith",
|
||||
"id": 9876543,
|
||||
"url": "https://api.github.com/orgs/jsmith",
|
||||
"repos_url": "https://api.github.com/orgs/jsmith/repos",
|
||||
"events_url": "https://api.github.com/orgs/jsmith/events",
|
||||
"members_url": "https://api.github.com/orgs/jsmith/members{/member}",
|
||||
"public_members_url": "https://api.github.com/orgs/jsmith/public_members{/member}",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5504624?v=3",
|
||||
"description": null
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 1234567,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/000000?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
126
buildtrigger/test/triggerjson/github_webhook_knownissue.json
Normal file
126
buildtrigger/test/triggerjson/github_webhook_knownissue.json
Normal file
|
@ -0,0 +1,126 @@
|
|||
{
|
||||
"ref": "refs/heads/1.2.6",
|
||||
"before": "76a309ed96c72986eddffc02d2f4dda3fe689f10",
|
||||
"after": "0000000000000000000000000000000000000000",
|
||||
"created": false,
|
||||
"deleted": true,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/somerepo/compare/76a309ed96c7...000000000000",
|
||||
"commits": [
|
||||
|
||||
],
|
||||
"head_commit": null,
|
||||
"repository": {
|
||||
"id": 12345678,
|
||||
"name": "somerepo",
|
||||
"full_name": "jsmith/somerepo",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"private": true,
|
||||
"html_url": "https://github.com/jsmith/somerepo",
|
||||
"description": "Dockerfile for some repo",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/somerepo",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/somerepo/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/somerepo/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/somerepo/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/somerepo/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/somerepo/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/somerepo/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/somerepo/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/somerepo/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/somerepo/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/somerepo/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/somerepo/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/somerepo/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/somerepo/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/somerepo/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/somerepo/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/somerepo/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/somerepo/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/somerepo/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/somerepo/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/somerepo/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/somerepo/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/somerepo/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/somerepo/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/somerepo/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/somerepo/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/somerepo/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/somerepo/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/somerepo/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/somerepo/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/somerepo/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/somerepo/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/somerepo/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/somerepo/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/somerepo/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/somerepo/releases{/id}",
|
||||
"deployments_url": "https://api.github.com/repos/jsmith/somerepo/deployments",
|
||||
"created_at": 1461165926,
|
||||
"updated_at": "2016-11-03T18:20:01Z",
|
||||
"pushed_at": 1479313569,
|
||||
"git_url": "git://github.com/jsmith/somerepo.git",
|
||||
"ssh_url": "git@github.com:jsmith/somerepo.git",
|
||||
"clone_url": "https://github.com/jsmith/somerepo.git",
|
||||
"svn_url": "https://github.com/jsmith/somerepo",
|
||||
"homepage": "",
|
||||
"size": 3114,
|
||||
"stargazers_count": 0,
|
||||
"watchers_count": 0,
|
||||
"language": "Shell",
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 0,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 0,
|
||||
"forks": 0,
|
||||
"open_issues": 0,
|
||||
"watchers": 0,
|
||||
"default_branch": "master",
|
||||
"stargazers": 0,
|
||||
"master_branch": "master",
|
||||
"organization": "jsmith"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"organization": {
|
||||
"login": "jsmith",
|
||||
"id": 9876543,
|
||||
"url": "https://api.github.com/orgs/jsmith",
|
||||
"repos_url": "https://api.github.com/orgs/jsmith/repos",
|
||||
"events_url": "https://api.github.com/orgs/jsmith/events",
|
||||
"hooks_url": "https://api.github.com/orgs/jsmith/hooks",
|
||||
"issues_url": "https://api.github.com/orgs/jsmith/issues",
|
||||
"members_url": "https://api.github.com/orgs/jsmith/members{/member}",
|
||||
"public_members_url": "https://api.github.com/orgs/jsmith/public_members{/member}",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1234567?v=3",
|
||||
"description": "Open Source Projects for Linux Containers"
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 12345678,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1234567?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
133
buildtrigger/test/triggerjson/github_webhook_missing.json
Normal file
133
buildtrigger/test/triggerjson/github_webhook_missing.json
Normal file
|
@ -0,0 +1,133 @@
|
|||
{
|
||||
"ref": "refs/heads/master",
|
||||
"before": "9ea43cab474709d4a61afb7e3340de1ffc405b41",
|
||||
"after": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/anothertest/compare/9ea43cab4747...410f4cdf8ff0",
|
||||
"commits": [
|
||||
{
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"id": 12345678,
|
||||
"name": "anothertest",
|
||||
"full_name": "jsmith/anothertest",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/jsmith/anothertest",
|
||||
"description": "",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/anothertest",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/anothertest/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/anothertest/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/anothertest/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/anothertest/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/anothertest/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/anothertest/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/anothertest/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/anothertest/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/anothertest/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/anothertest/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/anothertest/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/anothertest/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/anothertest/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/anothertest/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/anothertest/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/anothertest/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/anothertest/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/anothertest/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/anothertest/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/anothertest/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/anothertest/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/anothertest/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/anothertest/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/anothertest/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/anothertest/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/anothertest/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/anothertest/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/anothertest/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/anothertest/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/anothertest/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/anothertest/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/anothertest/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/anothertest/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/anothertest/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/anothertest/releases{/id}",
|
||||
"created_at": 1430426945,
|
||||
"updated_at": "2015-04-30T20:49:05Z",
|
||||
"pushed_at": 1441995976,
|
||||
"git_url": "git://github.com/jsmith/anothertest.git",
|
||||
"ssh_url": "git@github.com:jsmith/anothertest.git",
|
||||
"clone_url": "https://github.com/jsmith/anothertest.git",
|
||||
"svn_url": "https://github.com/jsmith/anothertest",
|
||||
"homepage": null,
|
||||
"size": 144,
|
||||
"stargazers_count": 0,
|
||||
"watchers_count": 0,
|
||||
"language": null,
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 0,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 0,
|
||||
"forks": 0,
|
||||
"open_issues": 0,
|
||||
"watchers": 0,
|
||||
"default_branch": "master",
|
||||
"stargazers": 0,
|
||||
"master_branch": "master"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 1234567,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4073002?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
149
buildtrigger/test/triggerjson/github_webhook_noname.json
Normal file
149
buildtrigger/test/triggerjson/github_webhook_noname.json
Normal file
|
@ -0,0 +1,149 @@
|
|||
{
|
||||
"ref": "refs/heads/master",
|
||||
"before": "9716b516939221dc754a056e0f9ddf599e71d4b8",
|
||||
"after": "118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/docker-test/compare/9716b5169392...118b07121695",
|
||||
"commits": [
|
||||
{
|
||||
"id": "118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"distinct": true,
|
||||
"message": "Fail",
|
||||
"timestamp": "2015-09-25T14:55:11-04:00",
|
||||
"url": "https://github.com/jsmith/docker-test/commit/118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"committer": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"distinct": true,
|
||||
"message": "Fail",
|
||||
"timestamp": "2015-09-25T14:55:11-04:00",
|
||||
"url": "https://github.com/jsmith/docker-test/commit/118b07121695d9f2e40a5ff264fdcc2917680870",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"committer": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"README.md"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"id": 1234567,
|
||||
"name": "docker-test",
|
||||
"full_name": "jsmith/docker-test",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/jsmith/docker-test",
|
||||
"description": "",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/docker-test",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/docker-test/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/docker-test/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/docker-test/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/docker-test/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/docker-test/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/docker-test/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/docker-test/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/docker-test/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/docker-test/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/docker-test/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/docker-test/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/docker-test/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/docker-test/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/docker-test/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/docker-test/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/docker-test/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/docker-test/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/docker-test/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/docker-test/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/docker-test/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/docker-test/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/docker-test/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/docker-test/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/docker-test/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/docker-test/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/docker-test/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/docker-test/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/docker-test/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/docker-test/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/docker-test/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/docker-test/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/docker-test/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/docker-test/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/docker-test/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/docker-test/releases{/id}",
|
||||
"created_at": 1442254053,
|
||||
"updated_at": "2015-09-14T18:07:33Z",
|
||||
"pushed_at": 1443207315,
|
||||
"git_url": "git://github.com/jsmith/docker-test.git",
|
||||
"ssh_url": "git@github.com:jsmith/docker-test.git",
|
||||
"clone_url": "https://github.com/jsmith/docker-test.git",
|
||||
"svn_url": "https://github.com/jsmith/docker-test",
|
||||
"homepage": null,
|
||||
"size": 108,
|
||||
"stargazers_count": 0,
|
||||
"watchers_count": 0,
|
||||
"language": null,
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 0,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 0,
|
||||
"forks": 0,
|
||||
"open_issues": 0,
|
||||
"watchers": 0,
|
||||
"default_branch": "master",
|
||||
"stargazers": 0,
|
||||
"master_branch": "master"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 1234567,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1234567?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
153
buildtrigger/test/triggerjson/github_webhook_slash_branch.json
Normal file
153
buildtrigger/test/triggerjson/github_webhook_slash_branch.json
Normal file
|
@ -0,0 +1,153 @@
|
|||
{
|
||||
"ref": "refs/heads/slash/branch",
|
||||
"before": "9ea43cab474709d4a61afb7e3340de1ffc405b41",
|
||||
"after": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/jsmith/anothertest/compare/9ea43cab4747...410f4cdf8ff0",
|
||||
"commits": [
|
||||
{
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"author": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"distinct": true,
|
||||
"message": "Update Dockerfile",
|
||||
"timestamp": "2015-09-11T14:26:16-04:00",
|
||||
"url": "https://github.com/jsmith/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c",
|
||||
"author": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"committer": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com",
|
||||
"username": "jsmith"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"id": 1234567,
|
||||
"name": "anothertest",
|
||||
"full_name": "jsmith/anothertest",
|
||||
"owner": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"private": false,
|
||||
"html_url": "https://github.com/jsmith/anothertest",
|
||||
"description": "",
|
||||
"fork": false,
|
||||
"url": "https://github.com/jsmith/anothertest",
|
||||
"forks_url": "https://api.github.com/repos/jsmith/anothertest/forks",
|
||||
"keys_url": "https://api.github.com/repos/jsmith/anothertest/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/jsmith/anothertest/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/jsmith/anothertest/teams",
|
||||
"hooks_url": "https://api.github.com/repos/jsmith/anothertest/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/jsmith/anothertest/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/jsmith/anothertest/events",
|
||||
"assignees_url": "https://api.github.com/repos/jsmith/anothertest/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/jsmith/anothertest/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/jsmith/anothertest/tags",
|
||||
"blobs_url": "https://api.github.com/repos/jsmith/anothertest/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/jsmith/anothertest/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/jsmith/anothertest/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/jsmith/anothertest/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/jsmith/anothertest/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/jsmith/anothertest/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/jsmith/anothertest/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/jsmith/anothertest/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/jsmith/anothertest/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/jsmith/anothertest/subscription",
|
||||
"commits_url": "https://api.github.com/repos/jsmith/anothertest/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/jsmith/anothertest/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/jsmith/anothertest/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/jsmith/anothertest/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/jsmith/anothertest/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/jsmith/anothertest/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/jsmith/anothertest/merges",
|
||||
"archive_url": "https://api.github.com/repos/jsmith/anothertest/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/jsmith/anothertest/downloads",
|
||||
"issues_url": "https://api.github.com/repos/jsmith/anothertest/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/jsmith/anothertest/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/jsmith/anothertest/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/jsmith/anothertest/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/jsmith/anothertest/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/jsmith/anothertest/releases{/id}",
|
||||
"created_at": 1430426945,
|
||||
"updated_at": "2015-04-30T20:49:05Z",
|
||||
"pushed_at": 1441995976,
|
||||
"git_url": "git://github.com/jsmith/anothertest.git",
|
||||
"ssh_url": "git@github.com:jsmith/anothertest.git",
|
||||
"clone_url": "https://github.com/jsmith/anothertest.git",
|
||||
"svn_url": "https://github.com/jsmith/anothertest",
|
||||
"homepage": null,
|
||||
"size": 144,
|
||||
"stargazers_count": 0,
|
||||
"watchers_count": 0,
|
||||
"language": null,
|
||||
"has_issues": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"forks_count": 0,
|
||||
"mirror_url": null,
|
||||
"open_issues_count": 0,
|
||||
"forks": 0,
|
||||
"open_issues": 0,
|
||||
"watchers": 0,
|
||||
"default_branch": "master",
|
||||
"stargazers": 0,
|
||||
"master_branch": "master"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "jsmith",
|
||||
"email": "jsmith@users.noreply.github.com"
|
||||
},
|
||||
"sender": {
|
||||
"login": "jsmith",
|
||||
"id": 1234567,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1234567?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/jsmith",
|
||||
"html_url": "https://github.com/jsmith",
|
||||
"followers_url": "https://api.github.com/users/jsmith/followers",
|
||||
"following_url": "https://api.github.com/users/jsmith/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/jsmith/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/jsmith/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/jsmith/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/jsmith/orgs",
|
||||
"repos_url": "https://api.github.com/users/jsmith/repos",
|
||||
"events_url": "https://api.github.com/users/jsmith/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/jsmith/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
54
buildtrigger/test/triggerjson/gitlab_webhook.json
Normal file
54
buildtrigger/test/triggerjson/gitlab_webhook.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"object_kind": "push",
|
||||
"before": "11fcaca195e8b17ca7e3dc47d9608d5b6b892f45",
|
||||
"after": "fb88379ee45de28a0a4590fddcbd8eff8b36026e",
|
||||
"ref": "refs/heads/master",
|
||||
"checkout_sha": "fb88379ee45de28a0a4590fddcbd8eff8b36026e",
|
||||
"message": null,
|
||||
"user_id": 98765,
|
||||
"user_name": "John Smith",
|
||||
"user_email": "j@smith.com",
|
||||
"project_id": 12344567,
|
||||
"repository": {
|
||||
"name": "somerepo",
|
||||
"url": "git@gitlab.com:jsmith/somerepo.git",
|
||||
"description": "",
|
||||
"homepage": "https://gitlab.com/jsmith/somerepo",
|
||||
"git_http_url": "https://gitlab.com/jsmith/somerepo.git",
|
||||
"git_ssh_url": "git@gitlab.com:jsmith/somerepo.git",
|
||||
"visibility_level": 20
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "fb88379ee45de28a0a4590fddcbd8eff8b36026e",
|
||||
"message": "Fix link\n",
|
||||
"timestamp": "2015-08-13T19:33:18+00:00",
|
||||
"url": "https://gitlab.com/jsmith/somerepo/commit/fb88379ee45de28a0a4590fddcbd8eff8b36026e",
|
||||
"author": {
|
||||
"name": "Jane Smith",
|
||||
"email": "jane@smith.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "4ca166bc0b511f21fa331873f260f1a7cb38d723",
|
||||
"message": "Do Some Cool Thing",
|
||||
"timestamp": "2015-08-13T15:52:15+00:00",
|
||||
"url": "https://gitlab.com/jsmith/somerepo/commit/4ca166bc0b511f21fa331873f260f1a7cb38d723",
|
||||
"author": {
|
||||
"name": "Jane Smith",
|
||||
"email": "jane@smith.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "11fcaca195e8b17ca7e3dc47d9608d5b6b892f45",
|
||||
"message": "Merge another cool thing",
|
||||
"timestamp": "2015-08-13T09:31:47+00:00",
|
||||
"url": "https://gitlab.com/jsmith/somerepo/commit/11fcaca195e8b17ca7e3dc47d9608d5b6b892f45",
|
||||
"author": {
|
||||
"name": "Kate Smith",
|
||||
"email": "kate@smith.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_commits_count": 3
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"ref": "refs/tags/fourthtag",
|
||||
"user_id": 4797254,
|
||||
"object_kind": "tag_push",
|
||||
"repository": {
|
||||
"git_ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"name": "Some test project",
|
||||
"url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"git_http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"visibility_level": 0,
|
||||
"homepage": "https://gitlab.com/someuser/some-test-project",
|
||||
"description": "Some test project"
|
||||
},
|
||||
"event_name": "tag_push",
|
||||
"commits": [
|
||||
{
|
||||
"added": [],
|
||||
"author": {
|
||||
"name": "Some User",
|
||||
"email": "someuser@somedomain.com"
|
||||
},
|
||||
"url": "https://gitlab.com/someuser/some-test-project/commit/770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"timestamp": "2019-10-17T18:07:48Z",
|
||||
"message": "Update Dockerfile",
|
||||
"removed": [],
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
],
|
||||
"id": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f"
|
||||
}
|
||||
],
|
||||
"after": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"project": {
|
||||
"git_ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"ci_config_path": null,
|
||||
"web_url": "https://gitlab.com/someuser/some-test-project",
|
||||
"description": "Some test project",
|
||||
"url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"namespace": "Some User",
|
||||
"default_branch": "master",
|
||||
"homepage": "https://gitlab.com/someuser/some-test-project",
|
||||
"git_http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"avatar_url": null,
|
||||
"ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"path_with_namespace": "someuser/some-test-project",
|
||||
"visibility_level": 0,
|
||||
"id": 14838571,
|
||||
"name": "Some test project"
|
||||
},
|
||||
"user_username": "someuser",
|
||||
"checkout_sha": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"total_commits_count": 1,
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"user_avatar": "https://secure.gravatar.com/avatar/0ea05bdf5c3f2cb8aac782a4a2ac3177?s=80&d=identicon",
|
||||
"message": "",
|
||||
"project_id": 14838571,
|
||||
"user_name": "Some User",
|
||||
"user_email": "",
|
||||
"push_options": {}
|
||||
}
|
100
buildtrigger/test/triggerjson/gitlab_webhook_multicommit.json
Normal file
100
buildtrigger/test/triggerjson/gitlab_webhook_multicommit.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"object_kind": "push",
|
||||
"event_name": "push",
|
||||
"before": "0da5b5ebb397f0a8569c97f28e266c718607e8da",
|
||||
"after": "9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53",
|
||||
"ref": "refs\/heads\/master",
|
||||
"checkout_sha": "9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53",
|
||||
"message": null,
|
||||
"user_id": 750047,
|
||||
"user_name": "John Smith",
|
||||
"user_email": "j@smith.com",
|
||||
"user_avatar": "https:\/\/secure.gravatar.com\/avatar\/32784623495678234678234?s=80&d=identicon",
|
||||
"project_id": 1756744,
|
||||
"project": {
|
||||
"name": "some-test-project",
|
||||
"description": "",
|
||||
"web_url": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"avatar_url": null,
|
||||
"git_ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"git_http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git",
|
||||
"namespace": "jsmith",
|
||||
"visibility_level": 0,
|
||||
"path_with_namespace": "jsmith\/some-test-project",
|
||||
"default_branch": "master",
|
||||
"homepage": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "f00a0a6a71118721ac1f586bf79650170042609f",
|
||||
"message": "Add changelog",
|
||||
"timestamp": "2016-09-29T14:59:23+00:00",
|
||||
"url": "https:\/\/gitlab.com\/jsmith\/some-test-project\/commit\/f00a0a6a71118721ac1f586bf79650170042609f",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"added": [
|
||||
"CHANGELOG"
|
||||
],
|
||||
"modified": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cc66287314cb154c986665a6c29377ef42edee60",
|
||||
"message": "Add new file",
|
||||
"timestamp": "2016-09-29T15:02:01+00:00",
|
||||
"url": "https:\/\/gitlab.com\/jsmith\/some-test-project\/commit\/cc66287314cb154c986665a6c29377ef42edee60",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"added": [
|
||||
"YetAnotherFIle"
|
||||
],
|
||||
"modified": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53",
|
||||
"message": "Merge branch 'foobar' into 'master'\r\n\r\nAdd changelog\r\n\r\nSome merge thing\r\n\r\nSee merge request !1",
|
||||
"timestamp": "2016-09-29T15:02:41+00:00",
|
||||
"url": "https:\/\/gitlab.com\/jsmith\/some-test-project\/commit\/9a052a0b2fbe01d4a1a88638dd9fe31c1c56ef53",
|
||||
"author": {
|
||||
"name": "John Smith",
|
||||
"email": "j@smith.com"
|
||||
},
|
||||
"added": [
|
||||
"CHANGELOG",
|
||||
"YetAnotherFIle"
|
||||
],
|
||||
"modified": [
|
||||
|
||||
],
|
||||
"removed": [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_commits_count": 3,
|
||||
"repository": {
|
||||
"name": "some-test-project",
|
||||
"url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"description": "",
|
||||
"homepage": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"git_http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git",
|
||||
"git_ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"visibility_level": 0
|
||||
}
|
||||
}
|
44
buildtrigger/test/triggerjson/gitlab_webhook_nocommit.json
Normal file
44
buildtrigger/test/triggerjson/gitlab_webhook_nocommit.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"object_kind": "push",
|
||||
"event_name": "push",
|
||||
"before": "cc66287314cb154c986665a6c29377ef42edee60",
|
||||
"after": "0000000000000000000000000000000000000000",
|
||||
"ref": "refs\/heads\/foobar",
|
||||
"checkout_sha": null,
|
||||
"message": null,
|
||||
"user_id": 750047,
|
||||
"user_name": "John Smith",
|
||||
"user_email": "j@smith.com",
|
||||
"user_avatar": "https:\/\/secure.gravatar.com\/avatar\/2348972348972348973?s=80&d=identicon",
|
||||
"project_id": 1756744,
|
||||
"project": {
|
||||
"name": "some-test-project",
|
||||
"description": "",
|
||||
"web_url": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"avatar_url": null,
|
||||
"git_ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"git_http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git",
|
||||
"namespace": "jsmith",
|
||||
"visibility_level": 0,
|
||||
"path_with_namespace": "jsmith\/some-test-project",
|
||||
"default_branch": "master",
|
||||
"homepage": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git"
|
||||
},
|
||||
"commits": [
|
||||
|
||||
],
|
||||
"total_commits_count": 0,
|
||||
"repository": {
|
||||
"name": "some-test-project",
|
||||
"url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"description": "",
|
||||
"homepage": "https:\/\/gitlab.com\/jsmith\/some-test-project",
|
||||
"git_http_url": "https:\/\/gitlab.com\/jsmith\/some-test-project.git",
|
||||
"git_ssh_url": "git@gitlab.com:jsmith\/some-test-project.git",
|
||||
"visibility_level": 0
|
||||
}
|
||||
}
|
||||
|
14
buildtrigger/test/triggerjson/gitlab_webhook_other.json
Normal file
14
buildtrigger/test/triggerjson/gitlab_webhook_other.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"object_kind": "someother",
|
||||
"ref": "refs/tags/v1.0.0",
|
||||
"checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"repository":{
|
||||
"name": "Example",
|
||||
"url": "ssh://git@example.com/jsmith/example.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/jsmith/example",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"visibility_level":0
|
||||
}
|
||||
}
|
38
buildtrigger/test/triggerjson/gitlab_webhook_tag.json
Normal file
38
buildtrigger/test/triggerjson/gitlab_webhook_tag.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"object_kind": "tag_push",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"ref": "refs/tags/v1.0.0",
|
||||
"checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"user_id": 1,
|
||||
"user_name": "John Smith",
|
||||
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
|
||||
"project_id": 1,
|
||||
"project":{
|
||||
"name":"Example",
|
||||
"description":"",
|
||||
"web_url":"http://example.com/jsmith/example",
|
||||
"avatar_url":null,
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"namespace":"Jsmith",
|
||||
"visibility_level":0,
|
||||
"path_with_namespace":"jsmith/example",
|
||||
"default_branch":"master",
|
||||
"homepage":"http://example.com/jsmith/example",
|
||||
"url":"git@example.com:jsmith/example.git",
|
||||
"ssh_url":"git@example.com:jsmith/example.git",
|
||||
"http_url":"http://example.com/jsmith/example.git"
|
||||
},
|
||||
"repository":{
|
||||
"name": "Example",
|
||||
"url": "ssh://git@example.com/jsmith/example.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/jsmith/example",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"visibility_level":0
|
||||
},
|
||||
"commits": [],
|
||||
"total_commits_count": 0
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"after": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"checkout_sha": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"commits": [
|
||||
{
|
||||
"added": [],
|
||||
"author": {
|
||||
"name": "Some User",
|
||||
"email": "some.user@someplace.com"
|
||||
},
|
||||
"id": "770830e7ca132856991e6db4f7fc0f4dbe20bd5f",
|
||||
"message": "Update Dockerfile",
|
||||
"modified": [
|
||||
"Dockerfile"
|
||||
],
|
||||
"removed": [],
|
||||
"timestamp": "2019-10-17T18:07:48Z",
|
||||
"url": "https://gitlab.com/someuser/some-test-project/commit/770830e7ca132856991e6db4f7fc0f4dbe20bd5f"
|
||||
}
|
||||
],
|
||||
"event_name": "tag_push",
|
||||
"message": "",
|
||||
"object_kind": "tag_push",
|
||||
"project": {
|
||||
"avatar_url": null,
|
||||
"ci_config_path": null,
|
||||
"default_branch": "master",
|
||||
"description": "Some test project",
|
||||
"git_http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"git_ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"homepage": "https://gitlab.com/someuser/some-test-project",
|
||||
"http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"id": 14838571,
|
||||
"name": "Some test project",
|
||||
"namespace": "Joey Schorr",
|
||||
"path_with_namespace": "someuser/some-test-project",
|
||||
"ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"visibility_level": 0,
|
||||
"web_url": "https://gitlab.com/someuser/some-test-project"
|
||||
},
|
||||
"project_id": 14838571,
|
||||
"push_options": {},
|
||||
"ref": "refs/tags/thirdtag",
|
||||
"repository": {
|
||||
"description": "Some test project",
|
||||
"git_http_url": "https://gitlab.com/someuser/some-test-project.git",
|
||||
"git_ssh_url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"homepage": "https://gitlab.com/someuser/some-test-project",
|
||||
"name": "Some test project",
|
||||
"url": "git@gitlab.com:someuser/some-test-project.git",
|
||||
"visibility_level": 0
|
||||
},
|
||||
"total_commits_count": 1,
|
||||
"user_avatar": "https://secure.gravatar.com/avatar/someavatar?s=80&d=identicon",
|
||||
"user_email": "",
|
||||
"user_id": 4797254,
|
||||
"user_name": "Some User",
|
||||
"user_username": "someuser"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"object_kind": "tag_push",
|
||||
"before": "0000000000000000000000000000000000000000",
|
||||
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
|
||||
"ref": "refs/tags/v1.0.0",
|
||||
"checkout_sha": null,
|
||||
"user_id": 1,
|
||||
"user_name": "John Smith",
|
||||
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
|
||||
"project_id": 1,
|
||||
"project":{
|
||||
"name":"Example",
|
||||
"description":"",
|
||||
"web_url":"http://example.com/jsmith/example",
|
||||
"avatar_url":null,
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"namespace":"Jsmith",
|
||||
"visibility_level":0,
|
||||
"path_with_namespace":"jsmith/example",
|
||||
"default_branch":"master",
|
||||
"homepage":"http://example.com/jsmith/example",
|
||||
"url":"git@example.com:jsmith/example.git",
|
||||
"ssh_url":"git@example.com:jsmith/example.git",
|
||||
"http_url":"http://example.com/jsmith/example.git"
|
||||
},
|
||||
"repository":{
|
||||
"name": "Example",
|
||||
"url": "ssh://git@example.com/jsmith/example.git",
|
||||
"description": "",
|
||||
"homepage": "http://example.com/jsmith/example",
|
||||
"git_http_url":"http://example.com/jsmith/example.git",
|
||||
"git_ssh_url":"git@example.com:jsmith/example.git",
|
||||
"visibility_level":0
|
||||
},
|
||||
"commits": [],
|
||||
"total_commits_count": 0
|
||||
}
|
130
buildtrigger/triggerutil.py
Normal file
130
buildtrigger/triggerutil.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
import json
|
||||
import io
|
||||
import logging
|
||||
import re
|
||||
|
||||
class TriggerException(Exception):
|
||||
pass
|
||||
|
||||
class TriggerAuthException(TriggerException):
|
||||
pass
|
||||
|
||||
class InvalidPayloadException(TriggerException):
|
||||
pass
|
||||
|
||||
class BuildArchiveException(TriggerException):
|
||||
pass
|
||||
|
||||
class InvalidServiceException(TriggerException):
|
||||
pass
|
||||
|
||||
class TriggerActivationException(TriggerException):
|
||||
pass
|
||||
|
||||
class TriggerDeactivationException(TriggerException):
|
||||
pass
|
||||
|
||||
class TriggerStartException(TriggerException):
|
||||
pass
|
||||
|
||||
class ValidationRequestException(TriggerException):
|
||||
pass
|
||||
|
||||
class SkipRequestException(TriggerException):
|
||||
pass
|
||||
|
||||
class EmptyRepositoryException(TriggerException):
|
||||
pass
|
||||
|
||||
class RepositoryReadException(TriggerException):
|
||||
pass
|
||||
|
||||
class TriggerProviderException(TriggerException):
|
||||
pass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def determine_build_ref(run_parameters, get_branch_sha, get_tag_sha, default_branch):
|
||||
run_parameters = run_parameters or {}
|
||||
|
||||
kind = ''
|
||||
value = ''
|
||||
|
||||
if 'refs' in run_parameters and run_parameters['refs']:
|
||||
kind = run_parameters['refs']['kind']
|
||||
value = run_parameters['refs']['name']
|
||||
elif 'branch_name' in run_parameters:
|
||||
kind = 'branch'
|
||||
value = run_parameters['branch_name']
|
||||
|
||||
kind = kind or 'branch'
|
||||
value = value or default_branch or 'master'
|
||||
|
||||
ref = 'refs/tags/' + value if kind == 'tag' else 'refs/heads/' + value
|
||||
commit_sha = get_tag_sha(value) if kind == 'tag' else get_branch_sha(value)
|
||||
return (commit_sha, ref)
|
||||
|
||||
|
||||
def find_matching_branches(config, branches):
|
||||
if 'branchtag_regex' in config:
|
||||
try:
|
||||
regex = re.compile(config['branchtag_regex'])
|
||||
return [branch for branch in branches
|
||||
if matches_ref('refs/heads/' + branch, regex)]
|
||||
except:
|
||||
pass
|
||||
|
||||
return branches
|
||||
|
||||
|
||||
def should_skip_commit(metadata):
|
||||
if 'commit_info' in metadata:
|
||||
message = metadata['commit_info']['message']
|
||||
return '[skip build]' in message or '[build skip]' in message
|
||||
return False
|
||||
|
||||
|
||||
def raise_if_skipped_build(prepared_build, config):
|
||||
""" Raises a SkipRequestException if the given build should be skipped. """
|
||||
# Check to ensure we have metadata.
|
||||
if not prepared_build.metadata:
|
||||
logger.debug('Skipping request due to missing metadata for prepared build')
|
||||
raise SkipRequestException()
|
||||
|
||||
# Check the branchtag regex.
|
||||
if 'branchtag_regex' in config:
|
||||
try:
|
||||
regex = re.compile(config['branchtag_regex'])
|
||||
except:
|
||||
regex = re.compile('.*')
|
||||
|
||||
if not matches_ref(prepared_build.metadata.get('ref'), regex):
|
||||
raise SkipRequestException()
|
||||
|
||||
# Check the commit message.
|
||||
if should_skip_commit(prepared_build.metadata):
|
||||
logger.debug('Skipping request due to commit message request')
|
||||
raise SkipRequestException()
|
||||
|
||||
|
||||
def matches_ref(ref, regex):
|
||||
match_string = ref.split('/', 1)[1]
|
||||
if not regex:
|
||||
return False
|
||||
|
||||
m = regex.match(match_string)
|
||||
if not m:
|
||||
return False
|
||||
|
||||
return len(m.group(0)) == len(match_string)
|
||||
|
||||
|
||||
def raise_unsupported():
|
||||
raise io.UnsupportedOperation
|
||||
|
||||
|
||||
def get_trigger_config(trigger):
|
||||
try:
|
||||
return json.loads(trigger.config)
|
||||
except ValueError:
|
||||
return {}
|
Reference in a new issue