Redo the UI for the trigger setup dialog and add the ability for github triggers to be filtered using a regex on their branch name.
This commit is contained in:
parent
37aa70c28e
commit
c3171a2690
10 changed files with 597 additions and 226 deletions
|
@ -317,7 +317,7 @@ class BuildTriggerAnalyze(RepositoryParamResource):
|
|||
if not found_repository:
|
||||
return {
|
||||
'status': 'error',
|
||||
'message': 'Repository "%s" was not found' % (base_image)
|
||||
'message': 'Repository "%s" referenced by the Dockerfile was not found' % (base_image)
|
||||
}
|
||||
|
||||
# If the repository is private and the user cannot see that repo, then
|
||||
|
@ -326,7 +326,7 @@ class BuildTriggerAnalyze(RepositoryParamResource):
|
|||
if found_repository.visibility.name != 'public' and not can_read:
|
||||
return {
|
||||
'status': 'error',
|
||||
'message': 'Repository "%s" was not found' % (base_image)
|
||||
'message': 'Repository "%s" referenced by the Dockerfile was not found' % (base_image)
|
||||
}
|
||||
|
||||
# Check to see if the repository is public. If not, we suggest the
|
||||
|
@ -450,18 +450,18 @@ class BuildTriggerFieldValues(RepositoryParamResource):
|
|||
""" Custom verb to fetch a values list for a particular field name. """
|
||||
@require_repo_admin
|
||||
@nickname('listTriggerFieldValues')
|
||||
def get(self, namespace, repository, trigger_uuid, field_name):
|
||||
def post(self, namespace, repository, trigger_uuid, field_name):
|
||||
""" List the field values for a custom run field. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
raise NotFound()
|
||||
|
||||
config = request.get_json() or json.loads(trigger.config)
|
||||
user_permission = UserAdminPermission(trigger.connected_user.username)
|
||||
if user_permission.can():
|
||||
trigger_handler = BuildTriggerBase.get_trigger_for_service(trigger.service.name)
|
||||
values = trigger_handler.list_field_values(trigger.auth_token, json.loads(trigger.config),
|
||||
field_name)
|
||||
values = trigger_handler.list_field_values(trigger.auth_token, config, field_name)
|
||||
|
||||
if values is None:
|
||||
raise NotFound()
|
||||
|
|
|
@ -70,7 +70,7 @@ def create_user():
|
|||
abort(400, 'User creation is disabled. Please speak to your administrator.')
|
||||
|
||||
user_data = request.get_json()
|
||||
if not 'username' in user_data:
|
||||
if not user_data or not 'username' in user_data:
|
||||
abort(400, 'Missing username')
|
||||
|
||||
username = user_data['username']
|
||||
|
|
|
@ -3,6 +3,7 @@ import io
|
|||
import os.path
|
||||
import tarfile
|
||||
import base64
|
||||
import re
|
||||
|
||||
from github import Github, UnknownObjectException, GithubException
|
||||
from tempfile import SpooledTemporaryFile
|
||||
|
@ -229,13 +230,35 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
return repos_by_org
|
||||
|
||||
def matches_branch(self, branch_name, regex):
|
||||
if not regex:
|
||||
return False
|
||||
|
||||
m = regex.match(branch_name)
|
||||
if not m:
|
||||
return False
|
||||
|
||||
return len(m.group(0)) == len(branch_name)
|
||||
|
||||
def list_build_subdirs(self, auth_token, config):
|
||||
gh_client = self._get_client(auth_token)
|
||||
source = config['build_source']
|
||||
|
||||
try:
|
||||
try:
|
||||
repo = gh_client.get_repo(source)
|
||||
default_commit = repo.get_branch(repo.default_branch or 'master').commit
|
||||
|
||||
# Find the first matching branch.
|
||||
branches = None
|
||||
if 'branch_regex' in config:
|
||||
try:
|
||||
regex = re.compile(config['branch_regex'])
|
||||
branches = [branch.name for branch in repo.get_branches()
|
||||
if self.matches_branch(branch.name, regex)]
|
||||
except:
|
||||
pass
|
||||
|
||||
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 [os.path.dirname(elem.path) for elem in commit_tree.tree
|
||||
|
@ -330,7 +353,7 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
payload = request.get_json()
|
||||
if not payload or payload.get('head_commit') is None:
|
||||
raise SkipRequestException()
|
||||
|
||||
|
||||
if 'zen' in payload:
|
||||
raise ValidationRequestException()
|
||||
|
||||
|
@ -339,6 +362,16 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
commit_sha = payload['head_commit']['id']
|
||||
commit_message = payload['head_commit'].get('message', '')
|
||||
|
||||
if 'branch_regex' in config:
|
||||
try:
|
||||
regex = re.compile(config['branch_regex'])
|
||||
except:
|
||||
regex = re.compile('.*')
|
||||
|
||||
branch = ref.split('/')[-1]
|
||||
if not self.matches_branch(branch, regex):
|
||||
raise SkipRequestException()
|
||||
|
||||
if should_skip_commit(commit_message):
|
||||
raise SkipRequestException()
|
||||
|
||||
|
|
|
@ -4105,6 +4105,27 @@ pre.command:before {
|
|||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .branch-reference.not-match {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .branch-reference.not-match a {
|
||||
color: #ccc !important;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .branch-filter {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .branch-filter span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .selected-info {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .github-org-icon {
|
||||
width: 20px;
|
||||
margin-right: 8px;
|
||||
|
@ -4120,6 +4141,45 @@ pre.command:before {
|
|||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .matching-branches {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .matching-branches li:before {
|
||||
content: "\f126";
|
||||
font-family: FontAwesome;
|
||||
}
|
||||
|
||||
.trigger-setup-github-element .matching-branches li {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.setup-trigger-directive-element .dockerfile-found-content {
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.setup-trigger-directive-element .dockerfile-found-content:before {
|
||||
content: "\f071";
|
||||
font-family: FontAwesome;
|
||||
color: rgb(255, 194, 0);
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.setup-trigger-directive-element .dockerfile-found {
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.slideinout {
|
||||
-webkit-transition:0.5s all;
|
||||
transition:0.5s linear all;
|
||||
|
@ -4127,7 +4187,7 @@ pre.command:before {
|
|||
|
||||
position: relative;
|
||||
|
||||
height: 75px;
|
||||
height: 32px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,102 +8,110 @@
|
|||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Setup new build trigger</h4>
|
||||
</div>
|
||||
<div class="modal-body" ng-show="activating">
|
||||
<div class="modal-body" ng-show="currentView == 'activating'">
|
||||
<span class="quay-spinner"></span> Setting up trigger...
|
||||
</div>
|
||||
<div class="modal-body" ng-show="!activating">
|
||||
<div class="modal-body" ng-show="currentView != 'activating'">
|
||||
<!-- Trigger-specific setup -->
|
||||
<div class="trigger-description-element trigger-option-section" ng-switch on="trigger.service">
|
||||
<div ng-switch-when="github">
|
||||
<div class="trigger-setup-github" repository="repository" trigger="trigger"
|
||||
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
|
||||
analyze="checkAnalyze(isValid)"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading pull information -->
|
||||
<div ng-show="currentView == 'analyzing'">
|
||||
<span class="quay-spinner"></span> Checking pull credential requirements...
|
||||
</div>
|
||||
|
||||
<!-- Pull information -->
|
||||
<div class="trigger-option-section" ng-show="showPullRequirements">
|
||||
<div ng-show="!pullRequirements">
|
||||
<span class="quay-spinner"></span> Checking pull credential requirements...
|
||||
</div>
|
||||
<div class="trigger-option-section" ng-show="currentView == 'analyzed'">
|
||||
|
||||
<div ng-show="pullRequirements">
|
||||
<div class="alert alert-danger" ng-if="pullRequirements.status == 'error'">
|
||||
{{ pullRequirements.message }}
|
||||
</div>
|
||||
<div class="alert alert-warning" ng-if="pullRequirements.status == 'warning'">
|
||||
{{ pullRequirements.message }}
|
||||
</div>
|
||||
<div class="alert alert-success" ng-if="pullRequirements.status == 'analyzed' && pullRequirements.is_public === false">
|
||||
The
|
||||
<a href="{{ pullRequirements.dockerfile_url }}" ng-if="pullRequirements.dockerfile_url" target="_blank">Dockerfile found</a>
|
||||
<span ng-if="!pullRequirements.dockerfile_url">Dockerfile found</span>
|
||||
depends on the private <span class="registry-name"></span> repository
|
||||
<a href="/repository/{{ pullRequirements.namespace }}/{{ pullRequirements.name }}" target="_blank">
|
||||
{{ pullRequirements.namespace }}/{{ pullRequirements.name }}
|
||||
</a> which requires
|
||||
a robot account for pull access, because it is marked <strong>private</strong>.
|
||||
<!-- Messaging -->
|
||||
<div class="alert alert-danger" ng-if="pullInfo.analysis.status == 'error'">
|
||||
{{ pullInfo.analysis.message }}
|
||||
</div>
|
||||
<div class="alert alert-warning" ng-if="pullInfo.analysis.status == 'warning'">
|
||||
{{ pullRequirements.message }}
|
||||
</div>
|
||||
<div class="dockerfile-found" ng-if="pullInfo.analysis.is_public === false">
|
||||
<div class="dockerfile-found-content">
|
||||
A robot account is <strong>required</strong> for this build trigger because
|
||||
|
||||
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
|
||||
|
||||
<a href="/repository/{{ pullInfo.analysis.namespace }}/{{ pullInfo.analysis.name }}" target="_blank">
|
||||
{{ pullInfo.analysis.namespace }}/{{ pullInfo.analysis.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="pullRequirements">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="width: 162px">
|
||||
<span class="context-tooltip" data-title="The credentials given to 'docker pull' in the builder for pulling images"
|
||||
style="margin-bottom: 10px" bs-tooltip>
|
||||
docker pull Credentials:
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-if="!isNamespaceAdmin(repository.namespace)" style="color: #aaa;">
|
||||
In order to set pull credentials for a build trigger, you must be an Administrator of the namespace <strong>{{ repository.namespace }}</strong>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm" ng-if="isNamespaceAdmin(repository.namespace)">
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="publicPull ? 'active btn-info' : ''" ng-click="setPublicPull(true)">None</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="publicPull ? '' : 'active btn-info'" ng-click="setPublicPull(false)">
|
||||
<i class="fa fa-wrench"></i>
|
||||
Robot account
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="margin-bottom: 12px">Please select the credentials to use when pulling the base image:</div>
|
||||
<div ng-if="!isNamespaceAdmin(repository.namespace)" style="color: #aaa;">
|
||||
<strong>Note:</strong> In order to set pull credentials for a build trigger, you must be an
|
||||
Administrator of the namespace <strong>{{ repository.namespace }}</strong>
|
||||
</div>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr ng-show="!publicPull">
|
||||
<td>
|
||||
<div class="entity-search" namespace="repository.namespace"
|
||||
placeholder="'Select robot account for pulling...'"
|
||||
current-entity="pullEntity"
|
||||
allowed-entities="['robot']"></div>
|
||||
<!-- Namespace admin -->
|
||||
<div ng-show="isNamespaceAdmin(repository.namespace)">
|
||||
<!-- Select credentials -->
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="pullInfo.is_public ? 'active btn-info' : ''"
|
||||
ng-click="pullInfo.is_public = true">
|
||||
None
|
||||
</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="pullInfo.is_public ? '' : 'active btn-info'"
|
||||
ng-click="pullInfo.is_public = false">
|
||||
<i class="fa fa-wrench"></i>
|
||||
Robot account
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" ng-if="pullRequirements.status == 'analyzed' && pullRequirements.robots.length"
|
||||
style="margin-top: 20px; margin-bottom: 0px;">
|
||||
Note: We've automatically selected robot account
|
||||
<span class="entity-reference" entity="pullRequirements.robots[0]"></span>, since it has access to the private
|
||||
repository.
|
||||
</div>
|
||||
<div class="alert alert-warning"
|
||||
ng-if="pullRequirements.status == 'analyzed' && !pullRequirements.robots.length && pullRequirements.name"
|
||||
style="margin-top: 20px; margin-bottom: 0px;">
|
||||
Note: No robot account currently has access to the private repository. Please create one and/or assign access in the
|
||||
<a href="/repository/{{ pullRequirements.namespace }}/{{ pullRequirements.name }}/admin" target="_blank">repository's
|
||||
admin panel</a>.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- Robot Select -->
|
||||
<div ng-show="!pullInfo.is_public" style="margin-top: 10px">
|
||||
<div class="entity-search" namespace="repository.namespace"
|
||||
placeholder="'Select robot account for pulling...'"
|
||||
current-entity="pullInfo.pull_entity"
|
||||
allowed-entities="['robot']"></div>
|
||||
|
||||
<div ng-if="pullInfo.analysis.robots.length" style="margin-top: 20px; margin-bottom: 0px;">
|
||||
<strong>Note</strong>: We've automatically selected robot account
|
||||
<span class="entity-reference" entity="pullInfo.analysis.robots[0]"></span>,
|
||||
since it has access to the private repository.
|
||||
</div>
|
||||
<div ng-if="!pullInfo.analysis.robots.length && pullInfo.analysis.name"
|
||||
style="margin-top: 20px; margin-bottom: 0px;">
|
||||
<strong>Note</strong>: No robot account currently has access to the private repository. Please create one and/or assign access in the
|
||||
<a href="/repository/{{ pullInfo.analysis.namespace }}/{{ pullInfo.analysis.name }}/admin" target="_blank">
|
||||
repository's admin panel.
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-disabled="!state.stepValid"
|
||||
ng-click="nextStepCounter = nextStepCounter + 1"
|
||||
ng-show="currentView == 'config'">Next</button>
|
||||
|
||||
<button type="button" class="btn btn-primary"
|
||||
ng-disabled="!trigger.$ready || (!publicPull && !pullEntity) || checkingPullRequirements || activating"
|
||||
ng-click="activate()">Finished</button>
|
||||
ng-disabled="!trigger.$ready || (!pullInfo['is_public'] && !pullInfo['pull_entity'])"
|
||||
ng-click="activate()"
|
||||
ng-show="currentView == 'analyzed'">Create Trigger</button>
|
||||
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
|
|
9
static/directives/step-view-step.html
Normal file
9
static/directives/step-view-step.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<span class="step-view-step-content">
|
||||
<span ng-show="!loading">
|
||||
<span ng-transclude></span>
|
||||
</span>
|
||||
<span ng-show="loading">
|
||||
<span class="quay-spinner"></span>
|
||||
{{ loadMessage }}
|
||||
</span>
|
||||
</span>
|
3
static/directives/step-view.html
Normal file
3
static/directives/step-view.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="step-view-element">
|
||||
<div class="transcluded" ng-transclude>
|
||||
</div>
|
|
@ -2,19 +2,18 @@
|
|||
<span ng-switch-when="github">
|
||||
<i class="fa fa-github fa-lg" style="margin-right: 6px" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
||||
Push to GitHub repository <a href="https://github.com/{{ trigger.config.build_source }}" target="_new">{{ trigger.config.build_source }}</a>
|
||||
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="trigger.config.subdir">
|
||||
<span>Dockerfile:
|
||||
<a href="https://github.com/{{ trigger.config.build_source }}/tree/{{ trigger.config.master_branch || 'master' }}/{{ trigger.config.subdir }}/Dockerfile" target="_blank">
|
||||
//{{ trigger.config.subdir }}/Dockerfile
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!trigger.config.subdir && !short">
|
||||
<span><span class="trigger-description-subtitle">Dockerfile:</span>
|
||||
<a href="https://github.com/{{ trigger.config.build_source }}/tree/{{ trigger.config.master_branch || 'master' }}/Dockerfile" target="_blank">
|
||||
//Dockerfile
|
||||
</a>
|
||||
</span>
|
||||
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">
|
||||
<div>
|
||||
<span class="trigger-description-subtitle">Branches:</span>
|
||||
<span ng-if="trigger.config.branch_regex">Matching Regular Expression {{ trigger.config.branch_regex }}</span>
|
||||
<span ng-if="!trigger.config.branch_regex">(All Branches)</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="trigger-description-subtitle">Dockerfile:</span>
|
||||
<span ng-if="trigger.config.subdir">//{{ trigger.config.subdir}}/Dockerfile</span>
|
||||
<span ng-if="!trigger.config.subdir">//Dockerfile</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span ng-switch-default>
|
||||
|
|
|
@ -1,48 +1,147 @@
|
|||
<div class="trigger-setup-github-element">
|
||||
<div ng-show="loading">
|
||||
<span class="quay-spinner" style="vertical-align: middle; margin-right: 10px"></span>
|
||||
Loading Repository List
|
||||
<!-- Current selected info -->
|
||||
<div class="selected-info" ng-show="nextStepCounter > 0">
|
||||
<table style="width: 100%;">
|
||||
<tr ng-show="currentRepo && nextStepCounter > 0">
|
||||
<td width="200px">
|
||||
Repository:
|
||||
</td>
|
||||
<td>
|
||||
<div class="current-repo">
|
||||
<img class="dropdown-select-icon github-org-icon"
|
||||
ng-src="{{ currentRepo.avatar_url ? currentRepo.avatar_url : '//www.gravatar.com/avatar/' }}">
|
||||
{{ currentRepo.repo }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-show="nextStepCounter > 1">
|
||||
<td>
|
||||
Branches:
|
||||
</td>
|
||||
<td>
|
||||
<div class="branch-filter">
|
||||
<span ng-if="!state.hasBranchFilter">(All Branches)</span>
|
||||
<span ng-if="state.hasBranchFilter">Regular Expression: <code>{{ state.branchFilter }}</code></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-show="nextStepCounter > 2">
|
||||
<td>
|
||||
Dockerfile Location:
|
||||
</td>
|
||||
<td>
|
||||
<div class="dockerfile-location">
|
||||
<i class="fa fa-folder fa-lg"></i> {{ state.currentLocation || '(Repository Root)' }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div ng-show="!loading">
|
||||
<div style="margin-bottom: 18px">Please choose the GitHub repository that will trigger the build:</div>
|
||||
|
||||
|
||||
<!-- Step view -->
|
||||
<div class="step-view" next-step-counter="nextStepCounter" current-step-valid="currentStepValid"
|
||||
steps-completed="stepsCompleted()">
|
||||
<!-- Repository select -->
|
||||
<div class="dropdown-select" placeholder="'Select a repository'" selected-item="currentRepo"
|
||||
lookahead-items="repoLookahead">
|
||||
<!-- Icons -->
|
||||
<i class="dropdown-select-icon none-icon fa fa-github fa-lg"></i>
|
||||
<img class="dropdown-select-icon github-org-icon" ng-src="{{ currentRepo.avatar_url ? currentRepo.avatar_url : '//www.gravatar.com/avatar/' }}">
|
||||
<div class="step-view-step" complete-condition="currentRepo" load-callback="loadRepositories(callback)"
|
||||
load-message="Loading Repositories">
|
||||
<div style="margin-bottom: 12px">Please choose the GitHub repository that will trigger the build:</div>
|
||||
<div class="dropdown-select" placeholder="'Select a repository'" selected-item="currentRepo"
|
||||
lookahead-items="repoLookahead">
|
||||
<!-- Icons -->
|
||||
<i class="dropdown-select-icon none-icon fa fa-github fa-lg"></i>
|
||||
<img class="dropdown-select-icon github-org-icon"
|
||||
ng-src="{{ currentRepo.avatar_url ? currentRepo.avatar_url : '//www.gravatar.com/avatar/' }}">
|
||||
|
||||
<!-- Dropdown menu -->
|
||||
<ul class="dropdown-select-menu" role="menu">
|
||||
<li ng-repeat-start="org in orgs" role="presentation" class="dropdown-header github-org-header">
|
||||
<img ng-src="{{ org.info.avatar_url }}" class="github-org-icon">{{ org.info.name }}
|
||||
</li>
|
||||
<li ng-repeat="repo in org.repos" class="github-repo-listing">
|
||||
<a href="javascript:void(0)" ng-click="selectRepo(repo, org)"><i class="fa fa-github fa-lg"></i> {{ repo }}</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-repeat-end ng-show="$index < orgs.length - 1"></li>
|
||||
</ul>
|
||||
<!-- Dropdown menu -->
|
||||
<ul class="dropdown-select-menu" role="menu">
|
||||
<li ng-repeat-start="org in orgs" role="presentation" class="dropdown-header github-org-header">
|
||||
<img ng-src="{{ org.info.avatar_url }}" class="github-org-icon">{{ org.info.name }}
|
||||
</li>
|
||||
<li ng-repeat="repo in org.repos" class="github-repo-listing">
|
||||
<a href="javascript:void(0)" ng-click="selectRepo(repo, org)"><i class="fa fa-github fa-lg"></i> {{ repo }}</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-repeat-end ng-show="$index < orgs.length - 1"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Branch filter/select -->
|
||||
<div class="step-view-step" complete-condition="!state.hasBranchFilter || state.branchFilter"
|
||||
load-callback="loadBranches(callback)"
|
||||
load-message="Loading Branches">
|
||||
|
||||
<div style="margin-bottom: 12px">Please choose the branches to which this trigger will apply:</div>
|
||||
<div style="margin-left: 20px;">
|
||||
<div class="btn-group btn-group-sm" style="margin-bottom: 12px">
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="state.hasBranchFilter ? '' : 'active btn-info'" ng-click="state.hasBranchFilter = false">
|
||||
All Branches
|
||||
</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
ng-class="state.hasBranchFilter ? 'active btn-info' : ''" ng-click="state.hasBranchFilter = true">
|
||||
Matching Regular Expression
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div ng-show="state.hasBranchFilter" style="margin-top: 10px;">
|
||||
<form>
|
||||
<input class="form-control" type="text" ng-model="state.branchFilter"
|
||||
placeholder="(Regular expression)" required>
|
||||
</form>
|
||||
|
||||
<div style="margin-top: 10px">
|
||||
<div ng-if="branchNames.length">
|
||||
Branches:
|
||||
<ul class="matching-branches">
|
||||
<li ng-repeat="branchName in branchNames | limitTo:20"
|
||||
class="branch-reference"
|
||||
ng-class="isMatchingBranch(branchName, state.branchFilter) ? 'match' : 'not-match'">
|
||||
<a href="https://github.com/{{ currentRepo.repo }}/tree/{{ branchName }}" target="_blank">
|
||||
{{ branchName }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span ng-if="branchNames.length > 20">...</span>
|
||||
</div>
|
||||
<div ng-if="state.branchFilter && !branchNames.length"
|
||||
style="margin-top: 10px">
|
||||
<strong>Warning:</strong> No branches found
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dockerfile folder select -->
|
||||
<div class="slideinout" ng-show="currentRepo">
|
||||
<div style="margin-top: 10px">Dockerfile Location:</div>
|
||||
<div class="dropdown-select" placeholder="'(Repository Root)'" selected-item="currentLocation"
|
||||
lookahead-items="locations" handle-input="handleLocationInput(input)" handle-item-selected="handleLocationSelected(datum)"
|
||||
<div class="step-view-step" complete-condition="trigger.$ready" load-callback="loadLocations(callback)"
|
||||
load-message="Loading Folders">
|
||||
|
||||
<div style="margin-bottom: 12px">Dockerfile Location:</div>
|
||||
<div class="dropdown-select" placeholder="'(Repository Root)'" selected-item="state.currentLocation"
|
||||
lookahead-items="locations" handle-input="handleLocationInput(input)"
|
||||
handle-item-selected="handleLocationSelected(datum)"
|
||||
allow-custom-input="true">
|
||||
<!-- Icons -->
|
||||
<i class="dropdown-select-icon none-icon fa fa-folder-o fa-lg" ng-show="isInvalidLocation"></i>
|
||||
<i class="dropdown-select-icon none-icon fa fa-folder fa-lg" style="color: black;" ng-show="!isInvalidLocation"></i>
|
||||
<i class="dropdown-select-icon none-icon fa fa-folder-o fa-lg" ng-show="state.isInvalidLocation"></i>
|
||||
<i class="dropdown-select-icon none-icon fa fa-folder fa-lg" style="color: black;" ng-show="!state.isInvalidLocation"></i>
|
||||
<i class="dropdown-select-icon fa fa-folder fa-lg"></i>
|
||||
|
||||
<!-- Dropdown menu -->
|
||||
<ul class="dropdown-select-menu" role="menu">
|
||||
<li ng-repeat="location in locations">
|
||||
<a href="javascript:void(0)" ng-click="setLocation(location)" ng-if="!location"><i class="fa fa-github fa-lg"></i> Repository Root</a>
|
||||
<a href="javascript:void(0)" ng-click="setLocation(location)" ng-if="location"><i class="fa fa-folder fa-lg"></i> {{ location }}</a>
|
||||
<a href="javascript:void(0)" ng-click="setLocation(location)" ng-if="!location">
|
||||
<i class="fa fa-github fa-lg"></i> Repository Root
|
||||
</a>
|
||||
<a href="javascript:void(0)" ng-click="setLocation(location)" ng-if="location">
|
||||
<i class="fa fa-folder fa-lg"></i> {{ location }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-header" role="presentation" ng-show="!locations.length">No Dockerfiles found in repository</li>
|
||||
<li class="dropdown-header" role="presentation" ng-show="!locations.length">
|
||||
No Dockerfiles found in repository
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@ -53,10 +152,10 @@
|
|||
<div class="alert alert-warning" ng-show="locationError">
|
||||
{{ locationError }}
|
||||
</div>
|
||||
<div class="alert alert-info" ng-show="locations.length && isInvalidLocation">
|
||||
<div class="alert alert-info" ng-show="locations.length && state.isInvalidLocation">
|
||||
Note: The folder does not currently exist or contain a Dockerfile
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- /step-view -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
360
static/js/app.js
360
static/js/app.js
|
@ -2988,6 +2988,28 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
|||
});
|
||||
|
||||
|
||||
quayApp.filter('regex', function() {
|
||||
return function(input, regex) {
|
||||
if (!regex) { return []; }
|
||||
|
||||
try {
|
||||
var patt = new RegExp(regex);
|
||||
} catch (ex) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var out = [];
|
||||
for (var i = 0; i < input.length; ++i){
|
||||
var m = input[i].match(patt);
|
||||
if (m && m[0].length == input[i].length) {
|
||||
out.push(input[i]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
quayApp.filter('reverse', function() {
|
||||
return function(items) {
|
||||
return items.slice().reverse();
|
||||
|
@ -4744,6 +4766,118 @@ quayApp.directive('triggerDescription', function () {
|
|||
});
|
||||
|
||||
|
||||
quayApp.directive('stepView', function ($compile) {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/step-view.html',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'nextStepCounter': '=nextStepCounter',
|
||||
'currentStepValid': '=currentStepValid',
|
||||
|
||||
'stepsCompleted': '&stepsCompleted'
|
||||
},
|
||||
controller: function($scope, $element, $rootScope) {
|
||||
this.currentStepIndex = -1;
|
||||
this.steps = [];
|
||||
this.watcher = null;
|
||||
|
||||
this.getCurrentStep = function() {
|
||||
return this.steps[this.currentStepIndex];
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
this.currentStepIndex = -1;
|
||||
for (var i = 0; i < this.steps.length; ++i) {
|
||||
this.steps[i].element.hide();
|
||||
}
|
||||
|
||||
$scope.currentStepValid = false;
|
||||
};
|
||||
|
||||
this.next = function() {
|
||||
if (this.currentStepIndex >= 0) {
|
||||
if (!this.getCurrentStep().scope.completeCondition) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getCurrentStep().element.hide();
|
||||
|
||||
if (this.unwatch) {
|
||||
this.unwatch();
|
||||
this.unwatch = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.currentStepIndex++;
|
||||
|
||||
if (this.currentStepIndex < this.steps.length) {
|
||||
var currentStep = this.getCurrentStep();
|
||||
currentStep.element.show();
|
||||
currentStep.scope.load()
|
||||
|
||||
this.unwatch = currentStep.scope.$watch('completeCondition', function(cc) {
|
||||
$scope.currentStepValid = !!cc;
|
||||
});
|
||||
} else {
|
||||
$scope.stepsCompleted();
|
||||
}
|
||||
};
|
||||
|
||||
this.register = function(scope, element) {
|
||||
element.hide();
|
||||
|
||||
this.steps.push({
|
||||
'scope': scope,
|
||||
'element': element
|
||||
});
|
||||
};
|
||||
|
||||
var that = this;
|
||||
$scope.$watch('nextStepCounter', function(nsc) {
|
||||
if (nsc >= 0) {
|
||||
that.next();
|
||||
} else {
|
||||
that.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('stepViewStep', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 1,
|
||||
require: '^stepView',
|
||||
templateUrl: '/static/directives/step-view-step.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'completeCondition': '=completeCondition',
|
||||
'loadCallback': '&loadCallback',
|
||||
'loadMessage': '@loadMessage'
|
||||
},
|
||||
link: function(scope, element, attrs, controller) {
|
||||
controller.register(scope, element);
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
$scope.load = function() {
|
||||
$scope.loading = true;
|
||||
$scope.loadCallback({'callback': function() {
|
||||
$scope.loading = false;
|
||||
}});
|
||||
};
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('dropdownSelect', function ($compile) {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
@ -4986,25 +5120,28 @@ quayApp.directive('setupTriggerDialog', function () {
|
|||
controller: function($scope, $element, ApiService, UserService) {
|
||||
var modalSetup = false;
|
||||
|
||||
$scope.state = {};
|
||||
$scope.nextStepCounter = -1;
|
||||
$scope.currentView = 'config';
|
||||
|
||||
$scope.show = function() {
|
||||
if (!$scope.trigger || !$scope.repository) { return; }
|
||||
|
||||
$scope.activating = false;
|
||||
$scope.pullEntity = null;
|
||||
$scope.publicPull = true;
|
||||
$scope.showPullRequirements = false;
|
||||
|
||||
$scope.currentView = 'config';
|
||||
$('#setupTriggerModal').modal({});
|
||||
|
||||
if (!modalSetup) {
|
||||
$('#setupTriggerModal').on('hidden.bs.modal', function () {
|
||||
if (!$scope.trigger || $scope.trigger['is_active']) { return; }
|
||||
|
||||
$scope.nextStepCounter = -1;
|
||||
$scope.$apply(function() {
|
||||
$scope.cancelSetupTrigger();
|
||||
});
|
||||
});
|
||||
|
||||
modalSetup = true;
|
||||
$scope.nextStepCounter = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5017,27 +5154,20 @@ quayApp.directive('setupTriggerDialog', function () {
|
|||
};
|
||||
|
||||
$scope.hide = function() {
|
||||
$scope.activating = false;
|
||||
$('#setupTriggerModal').modal('hide');
|
||||
};
|
||||
|
||||
$scope.setPublicPull = function(value) {
|
||||
$scope.publicPull = value;
|
||||
};
|
||||
|
||||
$scope.checkAnalyze = function(isValid) {
|
||||
$scope.currentView = 'analyzing';
|
||||
$scope.pullInfo = {
|
||||
'is_public': true
|
||||
};
|
||||
|
||||
if (!isValid) {
|
||||
$scope.publicPull = true;
|
||||
$scope.pullEntity = null;
|
||||
$scope.showPullRequirements = false;
|
||||
$scope.checkingPullRequirements = false;
|
||||
$scope.currentView = 'analyzed';
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.checkingPullRequirements = true;
|
||||
$scope.showPullRequirements = true;
|
||||
$scope.pullRequirements = null;
|
||||
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'trigger_uuid': $scope.trigger.id
|
||||
|
@ -5048,26 +5178,20 @@ quayApp.directive('setupTriggerDialog', function () {
|
|||
};
|
||||
|
||||
ApiService.analyzeBuildTrigger(data, params).then(function(resp) {
|
||||
$scope.pullRequirements = resp;
|
||||
|
||||
if (resp['status'] == 'publicbase') {
|
||||
$scope.publicPull = true;
|
||||
$scope.pullEntity = null;
|
||||
} else if (resp['namespace']) {
|
||||
$scope.publicPull = false;
|
||||
$scope.currentView = 'analyzed';
|
||||
|
||||
if (resp['status'] == 'analyzed') {
|
||||
if (resp['robots'] && resp['robots'].length > 0) {
|
||||
$scope.pullEntity = resp['robots'][0];
|
||||
} else {
|
||||
$scope.pullEntity = null;
|
||||
$scope.pullInfo['pull_entity'] = resp['robots'][0];
|
||||
} else {
|
||||
$scope.pullInfo['pull_entity'] = null;
|
||||
}
|
||||
|
||||
$scope.pullInfo['is_public'] = false;
|
||||
}
|
||||
|
||||
$scope.checkingPullRequirements = false;
|
||||
}, function(resp) {
|
||||
$scope.pullRequirements = resp;
|
||||
$scope.checkingPullRequirements = false;
|
||||
});
|
||||
$scope.pullInfo['analysis'] = resp;
|
||||
}, ApiService.errorDisplay('Cannot load Dockerfile information'));
|
||||
};
|
||||
|
||||
$scope.activate = function() {
|
||||
|
@ -5084,7 +5208,7 @@ quayApp.directive('setupTriggerDialog', function () {
|
|||
data['pull_robot'] = $scope.pullEntity['name'];
|
||||
}
|
||||
|
||||
$scope.activating = true;
|
||||
$scope.currentView = 'activating';
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Cannot activate build trigger', function(resp) {
|
||||
$scope.hide();
|
||||
|
@ -5125,17 +5249,99 @@ quayApp.directive('triggerSetupGithub', function () {
|
|||
scope: {
|
||||
'repository': '=repository',
|
||||
'trigger': '=trigger',
|
||||
|
||||
'nextStepCounter': '=nextStepCounter',
|
||||
'currentStepValid': '=currentStepValid',
|
||||
|
||||
'analyze': '&analyze'
|
||||
},
|
||||
controller: function($scope, $element, ApiService) {
|
||||
$scope.analyzeCounter = 0;
|
||||
$scope.setupReady = false;
|
||||
$scope.loading = true;
|
||||
|
||||
$scope.branchNames = null;
|
||||
|
||||
$scope.state = {
|
||||
'branchFilter': '',
|
||||
'hasBranchFilter': false,
|
||||
'isInvalidLocation': true,
|
||||
'currentLocation': null
|
||||
};
|
||||
|
||||
$scope.isMatchingBranch = function(branchName, filter) {
|
||||
try {
|
||||
var patt = new RegExp(filter);
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var m = branchName.match(patt);
|
||||
return m && m[0].length == branchName.length;
|
||||
}
|
||||
|
||||
$scope.stepsCompleted = function() {
|
||||
$scope.analyze({'isValid': !$scope.state.isInvalidLocation});
|
||||
};
|
||||
|
||||
$scope.loadRepositories = function(callback) {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'trigger_uuid': $scope.trigger.id
|
||||
};
|
||||
|
||||
ApiService.listTriggerBuildSources(null, params).then(function(resp) {
|
||||
$scope.orgs = resp['sources'];
|
||||
setupTypeahead();
|
||||
callback();
|
||||
}, ApiService.errorDisplay('Cannot load repositories'));
|
||||
};
|
||||
|
||||
$scope.loadBranches = function(callback) {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'trigger_uuid': $scope.trigger['id'],
|
||||
'field_name': 'branch_name'
|
||||
};
|
||||
|
||||
ApiService.listTriggerFieldValues($scope.trigger['config'], params).then(function(resp) {
|
||||
$scope.branchNames = resp['values'];
|
||||
callback();
|
||||
}, ApiService.errorDisplay('Cannot load branch names'));
|
||||
};
|
||||
|
||||
$scope.loadLocations = function(callback) {
|
||||
$scope.locations = null;
|
||||
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'trigger_uuid': $scope.trigger.id
|
||||
};
|
||||
|
||||
ApiService.listBuildTriggerSubdirs($scope.trigger['config'], params).then(function(resp) {
|
||||
if (resp['status'] == 'error') {
|
||||
callback(resp['message'] || 'Could not load Dockerfile locations');
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.locations = resp['subdir'] || [];
|
||||
|
||||
// Select a default location (if any).
|
||||
if ($scope.locations.length > 0) {
|
||||
$scope.setLocation($scope.locations[0]);
|
||||
} else {
|
||||
$scope.state.currentLocation = null;
|
||||
$scope.state.isInvalidLocation = resp['subdir'].indexOf('') < 0;
|
||||
$scope.trigger.$ready = true;
|
||||
}
|
||||
|
||||
callback();
|
||||
}, ApiService.errorDisplay('Cannot load locations'));
|
||||
}
|
||||
|
||||
$scope.handleLocationInput = function(location) {
|
||||
$scope.state.isInvalidLocation = $scope.locations.indexOf(location) < 0;
|
||||
$scope.trigger['config']['subdir'] = location || '';
|
||||
$scope.isInvalidLocation = $scope.locations.indexOf(location) < 0;
|
||||
$scope.analyze({'isValid': !$scope.isInvalidLocation});
|
||||
$scope.trigger.$ready = true;
|
||||
};
|
||||
|
||||
$scope.handleLocationSelected = function(datum) {
|
||||
|
@ -5143,10 +5349,10 @@ quayApp.directive('triggerSetupGithub', function () {
|
|||
};
|
||||
|
||||
$scope.setLocation = function(location) {
|
||||
$scope.currentLocation = location;
|
||||
$scope.state.currentLocation = location;
|
||||
$scope.state.isInvalidLocation = false;
|
||||
$scope.trigger['config']['subdir'] = location || '';
|
||||
$scope.isInvalidLocation = false;
|
||||
$scope.analyze({'isValid': true});
|
||||
$scope.trigger.$ready = true;
|
||||
};
|
||||
|
||||
$scope.selectRepo = function(repo, org) {
|
||||
|
@ -5160,10 +5366,7 @@ quayApp.directive('triggerSetupGithub', function () {
|
|||
};
|
||||
|
||||
$scope.selectRepoInternal = function(currentRepo) {
|
||||
if (!currentRepo) {
|
||||
$scope.trigger.$ready = false;
|
||||
return;
|
||||
}
|
||||
$scope.trigger.$ready = false;
|
||||
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
|
@ -5175,39 +5378,6 @@ quayApp.directive('triggerSetupGithub', function () {
|
|||
'build_source': repo,
|
||||
'subdir': ''
|
||||
};
|
||||
|
||||
// Lookup the possible Dockerfile locations.
|
||||
$scope.locations = null;
|
||||
if (repo) {
|
||||
ApiService.listBuildTriggerSubdirs($scope.trigger['config'], params).then(function(resp) {
|
||||
if (resp['status'] == 'error') {
|
||||
$scope.locationError = resp['message'] || 'Could not load Dockerfile locations';
|
||||
$scope.locations = null;
|
||||
$scope.trigger.$ready = false;
|
||||
$scope.isInvalidLocation = false;
|
||||
$scope.analyze({'isValid': false});
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.locationError = null;
|
||||
$scope.locations = resp['subdir'] || [];
|
||||
$scope.trigger.$ready = true;
|
||||
|
||||
if ($scope.locations.length > 0) {
|
||||
$scope.setLocation($scope.locations[0]);
|
||||
} else {
|
||||
$scope.currentLocation = null;
|
||||
$scope.isInvalidLocation = resp['subdir'].indexOf('') < 0;
|
||||
$scope.analyze({'isValid': !$scope.isInvalidLocation});
|
||||
}
|
||||
}, function(resp) {
|
||||
$scope.locationError = resp['message'] || 'Could not load Dockerfile locations';
|
||||
$scope.locations = null;
|
||||
$scope.trigger.$ready = false;
|
||||
$scope.isInvalidLocation = false;
|
||||
$scope.analyze({'isValid': false});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var setupTypeahead = function() {
|
||||
|
@ -5237,30 +5407,20 @@ quayApp.directive('triggerSetupGithub', function () {
|
|||
$scope.repoLookahead = repos;
|
||||
};
|
||||
|
||||
var loadSources = function() {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
'trigger_uuid': $scope.trigger.id
|
||||
};
|
||||
|
||||
ApiService.listTriggerBuildSources(null, params).then(function(resp) {
|
||||
$scope.orgs = resp['sources'];
|
||||
setupTypeahead();
|
||||
$scope.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
var check = function() {
|
||||
if ($scope.repository && $scope.trigger) {
|
||||
loadSources();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watch('repository', check);
|
||||
$scope.$watch('trigger', check);
|
||||
|
||||
$scope.$watch('currentRepo', function(repo) {
|
||||
$scope.selectRepoInternal(repo);
|
||||
if (repo) {
|
||||
$scope.selectRepoInternal(repo);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('state.branchFilter', function(bf) {
|
||||
if (!$scope.trigger) { return; }
|
||||
|
||||
if ($scope.state.hasBranchFilter) {
|
||||
$scope.trigger['config']['branch_regex'] = bf;
|
||||
} else {
|
||||
delete $scope.trigger['config']['branch_regex'];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
Reference in a new issue