Merge pull request #255 from coreos-inc/betterbb
Change to use the new BitBucket webhooks
This commit is contained in:
commit
fee8bf8607
1 changed files with 120 additions and 36 deletions
|
@ -318,7 +318,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
return True
|
||||
|
||||
def is_active(self):
|
||||
return 'hook_id' in self.config
|
||||
return 'webhook_id' in self.config
|
||||
|
||||
def activate(self, standard_webhook_url):
|
||||
config = self.config
|
||||
|
@ -333,51 +333,58 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
]
|
||||
|
||||
repository = self._get_repository_client()
|
||||
(result, data, err_msg) = repository.deploykeys().create(
|
||||
(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'] = data['pk']
|
||||
config['deploy_key_id'] = created_deploykey['pk']
|
||||
|
||||
# Add a webhook callback.
|
||||
(result, data, err_msg) = repository.services().create('POST', URL=standard_webhook_url)
|
||||
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['hook_id'] = data['id']
|
||||
config['webhook_id'] = created_webhook['uuid']
|
||||
self.config = config
|
||||
return config, {'private_key': private_key}
|
||||
|
||||
def deactivate(self):
|
||||
config = self.config
|
||||
|
||||
hook_id = config.pop('hook_id', None)
|
||||
# TODO(jschorr): Remove the old hook removal code once everyone is migrated.
|
||||
old_hook_id = config.pop('hook_id', None)
|
||||
webhook_id = config.pop('webhook_id', None)
|
||||
deploy_key_id = config.pop('deploy_key_id', None)
|
||||
repository = self._get_repository_client()
|
||||
|
||||
try:
|
||||
repository = self._get_repository_client()
|
||||
# Remove the old service hook.
|
||||
if old_hook_id is not None:
|
||||
(result, _, err_msg) = repository.services().delete(old_hook_id)
|
||||
if not result:
|
||||
msg = 'Unable to remove service hook from repository: %s' % err_msg
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# Remove the webhook link.
|
||||
if hook_id is not None:
|
||||
(result, _, err_msg) = repository.services().delete(hook_id)
|
||||
if not result:
|
||||
msg = 'Unable to remove webhook from repository: %s' % err_msg
|
||||
raise TriggerDeactivationException(msg)
|
||||
# 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)
|
||||
except GitHubBadCredentialsException:
|
||||
msg = ('Unable to remove trigger as credentials are invalid. ' +
|
||||
'Please manually remove the webhook and deploy key.')
|
||||
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
|
||||
|
||||
|
@ -470,7 +477,17 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
|
||||
return None
|
||||
|
||||
def _prepare_build(self, commit_sha, ref, is_manual):
|
||||
_BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/%s/commits/%s'
|
||||
|
||||
def _prepare_build(self, commit_sha, ref, is_manual, target=None, actor=None):
|
||||
def _build_user_block(info):
|
||||
return {
|
||||
'username': info['username'],
|
||||
'url': info['links']['html']['href'],
|
||||
'avatar_url': info['links']['avatar']['href'],
|
||||
'display_name': info['display_name']
|
||||
}
|
||||
|
||||
config = self.config
|
||||
repository = self._get_repository_client()
|
||||
|
||||
|
@ -478,23 +495,33 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
# the tags.
|
||||
default_branch = self._get_default_branch(repository)
|
||||
|
||||
# Lookup the commit sha.
|
||||
(result, data, _) = repository.changesets().get(commit_sha)
|
||||
if not result:
|
||||
raise TriggerStartException('Could not lookup commit SHA')
|
||||
# Lookup the commit sha (if necessary)
|
||||
data = {}
|
||||
if target is None:
|
||||
(result, data, _) = repository.changesets().get(commit_sha)
|
||||
if not result:
|
||||
raise TriggerStartException('Could not lookup commit SHA')
|
||||
|
||||
namespace = repository.namespace
|
||||
name = repository.repository_name
|
||||
|
||||
# Build the commit information.
|
||||
commit_url = self._BITBUCKET_COMMIT_URL % (namespace, name, commit_sha)
|
||||
if target is not None and 'links' in target:
|
||||
commit_url = target['links']['html']['href']
|
||||
|
||||
commit_info = {
|
||||
'url': 'https://bitbucket.org/%s/%s/commits/%s' % (namespace, name, commit_sha),
|
||||
'message': data['message'],
|
||||
'date': data['timestamp']
|
||||
'url': commit_url,
|
||||
'message': target['message'] if target else data['message'],
|
||||
'date': target['date'] if target else data['timestamp']
|
||||
}
|
||||
|
||||
# Try to lookup the author by email address. The raw_author field (if it exists) is returned
|
||||
# in the form: "Joseph Schorr <joseph.schorr@coreos.com>"
|
||||
if data.get('raw_author'):
|
||||
# Add the commit's author.
|
||||
if target.get('author') and 'user' in target['author']:
|
||||
commit_info['author'] = _build_user_block(target['author']['user'])
|
||||
elif data.get('raw_author'):
|
||||
# Try to lookup the author by email address. The raw_author field (if it exists) is returned
|
||||
# in the form: "Joseph Schorr <joseph.schorr@coreos.com>"
|
||||
match = re.compile(r'.*<(.+)>').match(data['raw_author'])
|
||||
if match:
|
||||
email_address = match.group(1)
|
||||
|
@ -507,6 +534,10 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
'avatar_url': data['user']['avatar']
|
||||
}
|
||||
|
||||
# Add the commit's actor (committer).
|
||||
if actor is not None:
|
||||
commit_info['committer'] = _build_user_block(actor)
|
||||
|
||||
metadata = {
|
||||
'commit': commit_sha,
|
||||
'ref': ref,
|
||||
|
@ -524,8 +555,61 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
|||
|
||||
return prepared
|
||||
|
||||
|
||||
def handle_trigger_request(self, request):
|
||||
# Check for a payload field. If found, this is a V1 service request.
|
||||
payload_json = request.form.get('payload')
|
||||
if payload_json:
|
||||
return self.handle_V1_trigger_request(request)
|
||||
|
||||
# Otherwise, this is a V2 webhook request.
|
||||
return self.handle_V2_trigger_request(request)
|
||||
|
||||
def handle_V2_trigger_request(self, request):
|
||||
payload = request.get_json()
|
||||
if not 'push' in payload:
|
||||
logger.debug('Skipping BitBucket request due to missing push data in payload')
|
||||
raise SkipRequestException()
|
||||
|
||||
push_payload = payload['push']
|
||||
if not 'changes' in push_payload or not push_payload['changes']:
|
||||
logger.debug('Skipping BitBucket request due to empty changes list')
|
||||
raise SkipRequestException()
|
||||
|
||||
# Make sure we have a new change.
|
||||
changes = push_payload['changes']
|
||||
last_change = changes[-1]
|
||||
if not last_change.get('new'):
|
||||
logger.debug('Skipping BitBucket request due to change being a deletion')
|
||||
raise SkipRequestException()
|
||||
|
||||
change_info = last_change['new']
|
||||
change_target = change_info.get('target')
|
||||
if not change_target:
|
||||
logger.debug('Skipping BitBucket request due to missing change target')
|
||||
raise SkipRequestException()
|
||||
|
||||
# Check if this build should be skipped by commit message.
|
||||
commit_message = change_target.get('message', '')
|
||||
if should_skip_commit(commit_message):
|
||||
logger.debug('Skipping BitBucket request due to commit message request')
|
||||
raise SkipRequestException()
|
||||
|
||||
# Check to see if this build should be skipped by ref.
|
||||
ref = ('refs/heads/' + change_info['name'] if change_info['type'] == 'branch'
|
||||
else 'refs/tags/' + change_info['name'])
|
||||
|
||||
logger.debug('Checking BitBucket request: %s', ref)
|
||||
raise_if_skipped(self.config, ref)
|
||||
|
||||
# Prepare the build.
|
||||
commit_sha = change_target['hash']
|
||||
return self._prepare_build(commit_sha, ref, False, target=change_target,
|
||||
actor=payload.get('actor'))
|
||||
|
||||
|
||||
def handle_V1_trigger_request(self, request):
|
||||
# TODO(jschorr): Delete this code after the migration has successfully completed.
|
||||
|
||||
# Parse the JSON payload.
|
||||
payload_json = request.form.get('payload')
|
||||
if not payload_json:
|
||||
|
|
Reference in a new issue