from endpoints.building import PreparedBuild from data import model from buildtrigger.triggerutil import get_trigger_config, InvalidServiceException from jsonschema import validate 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': '^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': ['url', 'message', 'date'], }, }, 'required': ['commit', 'git_url'], } 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. """ return self.trigger.auth_token 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. """ raise NotImplementedError def list_build_sources(self): """ Take the auth information for the specific trigger type and load the list of build sources(repositories). """ raise NotImplementedError def list_build_subdirs(self): """ Take the auth information and the specified config so far and list all of the possible subdirs containing dockerfiles. """ raise NotImplementedError def handle_trigger_request(self): """ Transform the incoming request data into a set of actions. Returns a PreparedBuild. """ raise NotImplementedError def is_active(self): """ Returns True if the current build trigger is active. Inactive means further setup is needed. """ raise NotImplementedError 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. """ raise NotImplementedError 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. """ raise NotImplementedError def manual_start(self, run_parameters=None): """ Manually creates a repository build for this trigger. Returns a PreparedBuild. """ raise NotImplementedError 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. """ raise NotImplementedError 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. """ raise NotImplementedError @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. """ subdirectory = self.config.get('subdir', '') if subdirectory == '/': subdirectory = '' else: if not subdirectory.endswith('/'): subdirectory = subdirectory + '/' return subdirectory + 'Dockerfile' 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('subdir', 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