UI and code improvements to make working with the multiple SCMs easier

This commit is contained in:
Joseph Schorr 2015-05-03 10:38:11 -07:00
parent f091aaa07e
commit d07f9f04e9
10 changed files with 70 additions and 114 deletions

View file

@ -48,14 +48,19 @@ def user_view(user):
def trigger_view(trigger, can_read=False, can_admin=False): def trigger_view(trigger, can_read=False, can_admin=False):
if trigger and trigger.uuid: if trigger and trigger.uuid:
build_trigger = BuildTriggerHandler.get_handler(trigger) build_trigger = BuildTriggerHandler.get_handler(trigger)
build_source = build_trigger.config.get('build_source')
repo_url = build_trigger.get_repository_url() if build_source else None
return { return {
'service': trigger.service.name, 'service': trigger.service.name,
'build_source': build_trigger.config.get('build_source') if can_read else None, 'build_source': build_source if can_read else None,
'config': build_trigger.config if can_admin else {}, 'config': build_trigger.config if can_admin else {},
'id': trigger.uuid, 'id': trigger.uuid,
'connected_user': trigger.connected_user.username, 'connected_user': trigger.connected_user.username,
'is_active': build_trigger.is_active(), 'is_active': build_trigger.is_active(),
'pull_robot': user_view(trigger.pull_robot) if trigger.pull_robot else None 'pull_robot': user_view(trigger.pull_robot) if trigger.pull_robot else None,
'repository_url': repo_url if can_read else None,
} }
return None return None

View file

@ -367,8 +367,7 @@ class BuildTriggerAnalyze(RepositoryParamResource):
'name': base_repository, 'name': base_repository,
'is_public': found_repository.visibility.name == 'public', 'is_public': found_repository.visibility.name == 'public',
'robots': read_robots, 'robots': read_robots,
'status': 'analyzed', 'status': 'analyzed'
'dockerfile_url': handler.dockerfile_url()
} }
except RepositoryReadException as rre: except RepositoryReadException as rre:

View file

@ -120,12 +120,6 @@ class BuildTriggerHandler(object):
""" Returns the auth token for the trigger. """ """ Returns the auth token for the trigger. """
return self.trigger.auth_token return self.trigger.auth_token
def dockerfile_url(self):
"""
Returns the URL at which the Dockerfile for the trigger is found or None if none/not applicable.
"""
raise NotImplementedError
def load_dockerfile_contents(self): def load_dockerfile_contents(self):
""" """
Loads the Dockerfile found for the trigger's config and returns them or None if none could Loads the Dockerfile found for the trigger's config and returns them or None if none could
@ -188,6 +182,11 @@ class BuildTriggerHandler(object):
""" """
raise NotImplementedError 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 @classmethod
def service_name(cls): def service_name(cls):
""" """
@ -212,6 +211,18 @@ class BuildTriggerHandler(object):
""" Sets the auth token for the trigger, saving it to the DB. """ """ Sets the auth token for the trigger, saving it to the DB. """
model.update_build_trigger(self.trigger, self.config, auth_token=auth_token) model.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'
class BitbucketBuildTrigger(BuildTriggerHandler): class BitbucketBuildTrigger(BuildTriggerHandler):
""" """
@ -371,24 +382,9 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
return [] return []
def dockerfile_url(self):
repository = self._get_repository_client()
subdirectory = self.config.get('subdir', '')
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
master_branch = 'master'
(result, data, _) = repository.get_main_branch()
if result:
master_branch = data['name']
return 'https://bitbucket.org/%s/%s/src/%s/%s' % (repository.namespace,
repository.repository_name,
master_branch, path)
def load_dockerfile_contents(self): def load_dockerfile_contents(self):
repository = self._get_repository_client() repository = self._get_repository_client()
subdirectory = self.config.get('subdir', '/')[1:] path = self.get_dockerfile_path()
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
(result, data, err_msg) = repository.get_raw_path_contents(path, revision='master') (result, data, err_msg) = repository.get_raw_path_contents(path, revision='master')
if not result: if not result:
@ -539,6 +535,11 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
return self._prepare_build(commit_sha, ref, True) return self._prepare_build(commit_sha, ref, True)
def get_repository_url(self):
source = self.config['build_source']
(namespace, name) = source.split('/')
return 'https://bitbucket.org/%s/%s' % (namespace, name)
class GithubBuildTrigger(BuildTriggerHandler): class GithubBuildTrigger(BuildTriggerHandler):
""" """
@ -696,30 +697,13 @@ class GithubBuildTrigger(BuildTriggerHandler):
raise RepositoryReadException(message) raise RepositoryReadException(message)
def dockerfile_url(self):
config = self.config
source = config['build_source']
subdirectory = config.get('subdir', '')
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
gh_client = self._get_client()
try:
repo = gh_client.get_repo(source)
master_branch = repo.default_branch or 'master'
return 'https://github.com/%s/blob/%s/%s' % (source, master_branch, path)
except GithubException:
logger.exception('Could not load repository for Dockerfile.')
return None
def load_dockerfile_contents(self): def load_dockerfile_contents(self):
config = self.config config = self.config
gh_client = self._get_client() gh_client = self._get_client()
source = config['build_source'] source = config['build_source']
subdirectory = config.get('subdir', '') path = self.get_dockerfile_path()
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
try: try:
repo = gh_client.get_repo(source) repo = gh_client.get_repo(source)
file_info = repo.get_file_contents(path) file_info = repo.get_file_contents(path)
@ -922,6 +906,12 @@ class GithubBuildTrigger(BuildTriggerHandler):
return None return None
def get_repository_url(self):
from app import github_trigger
source = self.config['build_source']
return github_trigger.get_public_url(source)
class CustomBuildTrigger(BuildTriggerHandler): class CustomBuildTrigger(BuildTriggerHandler):
payload_schema = { payload_schema = {
'type': 'object', 'type': 'object',
@ -1084,6 +1074,9 @@ class CustomBuildTrigger(BuildTriggerHandler):
self.config = config self.config = config
return config return config
def get_repository_url(self):
return None
class GitLabBuildTrigger(BuildTriggerHandler): class GitLabBuildTrigger(BuildTriggerHandler):
""" """
@ -1221,35 +1214,9 @@ class GitLabBuildTrigger(BuildTriggerHandler):
return [] return []
def dockerfile_url(self):
gl_client = self._get_authorized_client()
subdir = self.config.get('subdir', '')
path = subdir + '/Dockerfile' if subdir else 'Dockerfile'
repository = gl_client.getproject(self.config['build_source'])
if repository is False:
return None
branches = self.list_field_values('branch_name')
branches = find_matching_branches(self.config, branches)
if branches == []:
return None
branch_name = branches[0]
if repository['default_branch'] in branches:
branch_name = repository['default_branch']
return '%s/%s/blob/%s/%s' % (gl_client.host,
repository['path_with_namespace'],
branch_name,
path)
def load_dockerfile_contents(self): def load_dockerfile_contents(self):
gl_client = self._get_authorized_client() gl_client = self._get_authorized_client()
subdir = self.config.get('subdir', '') path = self.get_dockerfile_path()
if subdir == '/':
subdir = ''
path = subdir + 'Dockerfile' if subdir else 'Dockerfile'
repository = gl_client.getproject(self.config['build_source']) repository = gl_client.getproject(self.config['build_source'])
if repository is False: if repository is False:
@ -1394,3 +1361,12 @@ class GitLabBuildTrigger(BuildTriggerHandler):
ref = 'refs/heads/%s' % branch_name ref = 'refs/heads/%s' % branch_name
return self._prepare_build(commit, ref, True) return self._prepare_build(commit, ref, True)
def get_repository_url(self):
gl_client = self._get_authorized_client()
repository = gl_client.getproject(self.config['build_source'])
if repository is False:
return None
return '%s/%s' % (gl_client.host, repository['path_with_namespace'])

View file

@ -13,29 +13,17 @@
<div class="modal-body" ng-show="currentView != 'activating'"> <div class="modal-body" ng-show="currentView != 'activating'">
<!-- Trigger-specific setup --> <!-- Trigger-specific setup -->
<div class="trigger-description-element trigger-option-section" ng-switch on="trigger.service"> <div class="trigger-description-element trigger-option-section" ng-switch on="trigger.service">
<div ng-switch-when="github">
<div class="trigger-setup-githost" repository="repository" trigger="trigger"
kind="github"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
<div ng-switch-when="bitbucket">
<div class="trigger-setup-githost" repository="repository" trigger="trigger"
kind="bitbucket"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
<div ng-switch-when="gitlab">
<div class="trigger-setup-githost" repository="repository" trigger="trigger"
kind="gitlab"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
<div ng-switch-when="custom-git"> <div ng-switch-when="custom-git">
<div class="trigger-setup-custom" repository="repository" trigger="trigger" <div class="trigger-setup-custom" repository="repository" trigger="trigger"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid" next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div> analyze="checkAnalyze(isValid)"></div>
</div> </div>
<div ng-switch-default>
<div class="trigger-setup-githost" repository="repository" trigger="trigger"
kind="{{ trigger.service }}"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
</div> </div>
<!-- Loading pull information --> <!-- Loading pull information -->
@ -57,14 +45,8 @@
</div> </div>
<div class="dockerfile-found" ng-if="pullInfo.analysis.is_public === false"> <div class="dockerfile-found" ng-if="pullInfo.analysis.is_public === false">
<div class="dockerfile-found-content"> <div class="dockerfile-found-content">
A robot account is <strong>required</strong> for this build trigger because A robot account is <strong>required</strong> for this build trigger because the
Dockerfile found
the
<a href="{{ pullInfo.analysis.dockerfile_url }}" ng-if="pullInfo.analysis.dockerfile_url" target="_blank">
Dockerfile found
</a>
<span ng-if="!pullInfo.analysis.dockerfile_url">Dockerfile found</span>
pulls from the private <span class="registry-name"></span> repository pulls from the private <span class="registry-name"></span> repository
<a href="/repository/{{ pullInfo.analysis.namespace }}/{{ pullInfo.analysis.name }}" target="_blank"> <a href="/repository/{{ pullInfo.analysis.namespace }}/{{ pullInfo.analysis.name }}" target="_blank">

View file

@ -1,7 +1,7 @@
<span> <span>
<i class="fa fa-bitbucket fa-lg" style="margin-right: 6px" data-title="BitBucket" bs-tooltip="tooltip.title"></i> <i class="fa fa-bitbucket fa-lg" style="margin-right: 6px" data-title="BitBucket" bs-tooltip="tooltip.title"></i>
Push to BitBucket repository Push to BitBucket repository
<a href="https://bitbucket.org/{{ trigger.config.build_source }}" target="_new"> <a href="{{ trigger.repository_url }}" target="_new">
{{ trigger.config.build_source }} {{ trigger.config.build_source }}
</a> </a>
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short"> <div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">

View file

@ -1,7 +1,7 @@
<span> <span>
<i class="fa fa-github fa-lg" style="margin-right: 6px" data-title="GitHub" bs-tooltip="tooltip.title"></i> <i class="fa fa-github fa-lg" style="margin-right: 6px" data-title="GitHub" bs-tooltip="tooltip.title"></i>
Push to GitHub <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> repository Push to GitHub <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> repository
<a href="{{ KeyService['githubTriggerEndpoint'] }}{{ trigger.config.build_source }}" target="_new"> <a href="{{ trigger.repository_url }}" target="_new">
{{ trigger.config.build_source }} {{ trigger.config.build_source }}
</a> </a>
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short"> <div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">

View file

@ -1,7 +1,7 @@
<span> <span>
<i class="fa fa-git-square fa-lg" style="margin-right: 6px" data-title="GitLab" bs-tooltip="tooltip.title"></i> <i class="fa fa-git-square fa-lg" style="margin-right: 6px" data-title="GitLab" bs-tooltip="tooltip.title"></i>
Push to GitLab repository Push to GitLab repository
<a href="https://gitlab.com/{{ trigger.config.build_source }}" target="_new"> <a ng-href="{{ trigger.repository_url }}" target="_new">
{{ trigger.config.build_source }} {{ trigger.config.build_source }}
</a> </a>
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short"> <div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">

View file

@ -55,9 +55,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
'credentials': '/static/directives/trigger/githost/credentials.html', 'credentials': '/static/directives/trigger/githost/credentials.html',
'trigger-description': '/static/directives/trigger/github/trigger-description.html' 'trigger-description': '/static/directives/trigger/github/trigger-description.html'
}, },
'repository_url': function(build) {
return KeyService['githubTriggerEndpoint'] + build.trigger.build_source;
},
'link_templates': { 'link_templates': {
'commit': '/commit/{sha}', 'commit': '/commit/{sha}',
'branch': '/tree/{branch}', 'branch': '/tree/{branch}',
@ -94,9 +91,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
'credentials': '/static/directives/trigger/githost/credentials.html', 'credentials': '/static/directives/trigger/githost/credentials.html',
'trigger-description': '/static/directives/trigger/bitbucket/trigger-description.html' 'trigger-description': '/static/directives/trigger/bitbucket/trigger-description.html'
}, },
'repository_url': function(build) {
return 'https://bitbucket.org/' + build.trigger.build_source;
},
'link_templates': { 'link_templates': {
'commit': '/commits/{sha}', 'commit': '/commits/{sha}',
'branch': '/branch/{branch}', 'branch': '/branch/{branch}',
@ -137,9 +131,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
'credentials': '/static/directives/trigger/githost/credentials.html', 'credentials': '/static/directives/trigger/githost/credentials.html',
'trigger-description': '/static/directives/trigger/gitlab/trigger-description.html' 'trigger-description': '/static/directives/trigger/gitlab/trigger-description.html'
}, },
'repository_url': function(build) {
return 'https://bitbucket.org/' + build.trigger.build_source;
},
'link_templates': { 'link_templates': {
'commit': '/commit/{sha}', 'commit': '/commit/{sha}',
'branch': '/tree/{branch}', 'branch': '/tree/{branch}',
@ -191,8 +182,8 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
return null; return null;
} }
var repositoryUrl = type.repository_url; var repositoryUrl = build.trigger.repository_url;
if (!repositoryUrl || !repositoryUrl(build)) { if (!repositoryUrl) {
return null; return null;
} }
@ -201,7 +192,7 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
return null; return null;
} }
return repositoryUrl(build) + linkTemplate[templateName]; return repositoryUrl + linkTemplate[templateName];
}; };
triggerService.supportsFullListing = function(name) { triggerService.supportsFullListing = function(name) {

View file

@ -2478,8 +2478,8 @@ class FakeBuildTrigger(BuildTriggerHandler):
prepared.is_manual = True prepared.is_manual = True
return prepared return prepared
def dockerfile_url(self): def get_repository_url(self):
return 'http://some/url' return 'http://foo/' + self.config['build_source']
def load_dockerfile_contents(self): def load_dockerfile_contents(self):
if not 'dockerfile' in self.config: if not 'dockerfile' in self.config:

View file

@ -78,6 +78,9 @@ class GithubOAuthConfig(OAuthConfig):
return [org.lower() for org in allowed] return [org.lower() for org in allowed]
def get_public_url(self, suffix):
return '%s%s' % (self._endpoint(), suffix)
def _endpoint(self): def _endpoint(self):
endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com') endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com')
if not endpoint.endswith('/'): if not endpoint.endswith('/'):