Work In Progress!
Get the full activation and deactivation cycle working for bitbucket.
This commit is contained in:
parent
5cc91ed202
commit
6479f8ddc9
8 changed files with 204 additions and 65 deletions
|
@ -7,7 +7,7 @@ import re
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from github import Github, UnknownObjectException, GithubException
|
from github import Github, UnknownObjectException, GithubException
|
||||||
from bitbucket.bitbucket import Bitbucket
|
from bitbucket import BitBucket
|
||||||
from tempfile import SpooledTemporaryFile
|
from tempfile import SpooledTemporaryFile
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
from data import model
|
from data import model
|
||||||
|
@ -186,75 +186,123 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
def service_name(cls):
|
def service_name(cls):
|
||||||
return 'bitbucket'
|
return 'bitbucket'
|
||||||
|
|
||||||
def _get_authorized_client(self, namespace=None):
|
def _get_client(self):
|
||||||
key = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_KEY', '')
|
key = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_KEY', '')
|
||||||
secret = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_SECRET', '')
|
secret = app.config.get('BITBUCKET_TRIGGER_CONFIG', {}).get('CONSUMER_SECRET', '')
|
||||||
|
|
||||||
trigger_uuid = self.trigger.uuid
|
trigger_uuid = self.trigger.uuid
|
||||||
callback_url = '%s/oauth1/bitbucket/callback/trigger/%s' % (get_app_url(), trigger_uuid)
|
callback_url = '%s/oauth1/bitbucket/callback/trigger/%s' % (get_app_url(), trigger_uuid)
|
||||||
|
|
||||||
bitbucket_client = Bitbucket(username=namespace or self.config.get('username', ''))
|
return BitBucket(key, secret, callback_url)
|
||||||
|
|
||||||
(result, err_message) = bitbucket_client.authorize(key, secret, callback_url,
|
def _get_authorized_client(self):
|
||||||
access_token=self.config.get('access_token'),
|
base_client = self._get_client()
|
||||||
access_token_secret=self.auth_token)
|
auth_token = self.auth_token or 'invalid:invalid'
|
||||||
if not result:
|
(access_token, access_token_secret) = auth_token.split(':')
|
||||||
raise TriggerProviderException(err_message)
|
return base_client.get_authorized_client(access_token, access_token_secret)
|
||||||
|
|
||||||
return bitbucket_client
|
def _get_repository_client(self):
|
||||||
|
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_oauth_url(self):
|
def get_oauth_url(self):
|
||||||
bitbucket_client = self._get_authorized_client()
|
bitbucket_client = self._get_client()
|
||||||
url = bitbucket_client.url('AUTHENTICATE', token=bitbucket_client.access_token)
|
(result, data, err_msg) = bitbucket_client.get_authorization_url()
|
||||||
return {
|
if not result:
|
||||||
'access_token': bitbucket_client.access_token,
|
raise RepositoryReadException(err_msg)
|
||||||
'access_token_secret': bitbucket_client.access_token_secret,
|
|
||||||
'url': url
|
return data
|
||||||
}
|
|
||||||
|
|
||||||
def exchange_verifier(self, verifier):
|
def exchange_verifier(self, verifier):
|
||||||
bitbucket_client = self._get_authorized_client()
|
bitbucket_client = self._get_client()
|
||||||
(result, data) = bitbucket_client.verify(verifier,
|
access_token = self.config.get('access_token', '')
|
||||||
access_token=self.config.get('access_token', ''),
|
access_token_secret = self.auth_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:
|
if not result:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Request the user's information and save it and the access token to the config.
|
# Save the updated access token and secret.
|
||||||
user_url = bitbucket_client.URLS['BASE'] % 'user'
|
self.set_auth_token(data[0] + ':' + data[1])
|
||||||
(result, data) = bitbucket_client.dispatch('GET', user_url, auth=bitbucket_client.auth)
|
|
||||||
|
# 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:
|
if not result:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
username = data['user']['username']
|
username = data['user']['username']
|
||||||
new_access_token = bitbucket_client.access_token
|
|
||||||
|
|
||||||
self.put_config_key('username', username)
|
self.put_config_key('username', username)
|
||||||
self.put_config_key('access_token', new_access_token)
|
|
||||||
self.set_auth_token(bitbucket_client.access_token_secret)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return False
|
return 'hook_id' in self.config
|
||||||
|
|
||||||
def activate(self, standard_webhook_url):
|
def activate(self, standard_webhook_url):
|
||||||
return {}
|
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, data, 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']
|
||||||
|
|
||||||
|
# Add a webhook callback.
|
||||||
|
(result, data, err_msg) = repository.services().create('POST', URL=standard_webhook_url)
|
||||||
|
if not result:
|
||||||
|
msg = 'Unable to add webhook to repository: %s' % err_msg
|
||||||
|
raise TriggerActivationException(msg)
|
||||||
|
|
||||||
|
config['hook_id'] = data['id']
|
||||||
|
return config, {'private_key': private_key}
|
||||||
|
|
||||||
|
|
||||||
def deactivate(self):
|
def deactivate(self):
|
||||||
return self.config
|
config = self.config
|
||||||
|
repository = self._get_repository_client()
|
||||||
|
|
||||||
|
# Remove the webhook link.
|
||||||
|
(result, _, err_msg) = repository.services().delete(config['hook_id'])
|
||||||
|
if not result:
|
||||||
|
msg = 'Unable to remove webhook from repository: %s' % err_msg
|
||||||
|
raise TriggerDeactivationException(msg)
|
||||||
|
|
||||||
|
# Remove the public key.
|
||||||
|
(result, _, err_msg) = repository.deploykeys().delete(config['deploy_key_id'])
|
||||||
|
if not result:
|
||||||
|
msg = 'Unable to remove deploy key from repository: %s' % err_msg
|
||||||
|
raise TriggerDeactivationException(msg)
|
||||||
|
|
||||||
|
config.pop('hook_id', None)
|
||||||
|
config.pop('deploy_key_id', None)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
def list_build_sources(self):
|
def list_build_sources(self):
|
||||||
bitbucket_client = self._get_authorized_client()
|
bitbucket_client = self._get_authorized_client()
|
||||||
success, repositories = bitbucket_client.repository.all()
|
(result, data, err_msg) = bitbucket_client.get_visible_repositories()
|
||||||
if not success:
|
if not result:
|
||||||
raise RepositoryReadException('Could not read repository list')
|
raise RepositoryReadException('Could not read repository list: ' + err_msg)
|
||||||
|
|
||||||
namespaces = {}
|
namespaces = {}
|
||||||
|
for repo in data:
|
||||||
for repo in repositories:
|
|
||||||
if not repo['scm'] == 'git':
|
if not repo['scm'] == 'git':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -272,36 +320,101 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
||||||
return namespaces.values()
|
return namespaces.values()
|
||||||
|
|
||||||
|
|
||||||
def list_build_subdirs(self):
|
def list_build_subdirs(self):
|
||||||
source = self.config['build_source']
|
repository = self._get_repository_client()
|
||||||
(namespace, name) = source.split('/')
|
(result, data, err_msg) = repository.get_path_contents('', revision='master')
|
||||||
(result, data) = self._get_authorized_client(namespace=namespace).repository.get(name)
|
if not result:
|
||||||
|
raise RepositoryReadException(err_msg)
|
||||||
|
|
||||||
|
|
||||||
|
files = set([f['path'] for f in data['files']])
|
||||||
|
if 'Dockerfile' in files:
|
||||||
|
return ['/']
|
||||||
|
|
||||||
print result
|
|
||||||
print data
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def dockerfile_url(self):
|
def dockerfile_url(self):
|
||||||
return None
|
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):
|
||||||
raise RepositoryReadException('Not supported')
|
repository = self._get_repository_client()
|
||||||
|
subdirectory = self.config.get('subdir', '/')[1:]
|
||||||
|
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
|
||||||
|
|
||||||
def handle_trigger_request(self, request):
|
(result, data, err_msg) = repository.get_raw_path_contents(path, revision='master')
|
||||||
return
|
if not result:
|
||||||
|
raise RepositoryReadException(err_msg)
|
||||||
|
|
||||||
def manual_start(self, run_parameters=None):
|
return data
|
||||||
return None
|
|
||||||
|
|
||||||
def list_field_values(self, field_name):
|
def list_field_values(self, field_name):
|
||||||
source = self.config['build_source']
|
source = self.config['build_source']
|
||||||
(namespace, name) = source.split('/')
|
(namespace, name) = source.split('/')
|
||||||
(result, data) = self._get_authorized_client(namespace=namespace).repository.get(name)
|
|
||||||
|
|
||||||
print result
|
bitbucket_client = self._get_authorized_client()
|
||||||
print data
|
repository = bitbucket_client.for_namespace(namespace).repositories().get(name)
|
||||||
return []
|
|
||||||
|
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
|
||||||
|
|
||||||
|
return data.keys()
|
||||||
|
|
||||||
|
if field_name == 'branch_name':
|
||||||
|
(result, data, _) = repository.get_branches()
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return data.keys()
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def handle_trigger_request(self, request):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def manual_start(self, run_parameters=None):
|
||||||
|
config = self.config
|
||||||
|
repository = self._get_repository_client()
|
||||||
|
|
||||||
|
source = config['build_source']
|
||||||
|
run_parameters = run_parameters or {}
|
||||||
|
|
||||||
|
# Lookup the branch to build.
|
||||||
|
master_branch = 'master'
|
||||||
|
(result, data, _) = repository.get_main_branch()
|
||||||
|
if result:
|
||||||
|
master_branch = data['name']
|
||||||
|
|
||||||
|
branch_name = run_parameters.get('branch_name') or master_branch
|
||||||
|
|
||||||
|
# Find the SHA for the branch.
|
||||||
|
# TODO
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class GithubBuildTrigger(BuildTriggerHandler):
|
class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
|
@ -42,6 +42,7 @@ git+https://github.com/DevTable/avatar-generator.git
|
||||||
git+https://github.com/DevTable/pygithub.git
|
git+https://github.com/DevTable/pygithub.git
|
||||||
git+https://github.com/DevTable/container-cloud-config.git
|
git+https://github.com/DevTable/container-cloud-config.git
|
||||||
git+https://github.com/DevTable/python-etcd.git
|
git+https://github.com/DevTable/python-etcd.git
|
||||||
|
git+https://github.com/coreos/py-bitbucket.git
|
||||||
gipc
|
gipc
|
||||||
pyOpenSSL
|
pyOpenSSL
|
||||||
pygpgme
|
pygpgme
|
||||||
|
|
|
@ -69,3 +69,4 @@ git+https://github.com/DevTable/pygithub.git
|
||||||
git+https://github.com/DevTable/container-cloud-config.git
|
git+https://github.com/DevTable/container-cloud-config.git
|
||||||
git+https://github.com/DevTable/python-etcd.git
|
git+https://github.com/DevTable/python-etcd.git
|
||||||
git+https://github.com/NateFerrero/oauth2lib.git
|
git+https://github.com/NateFerrero/oauth2lib.git
|
||||||
|
git+https://github.com/coreos/py-bitbucket.git
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<input type="text" class="lookahead-input form-control" placeholder="{{ placeholder }}"
|
<input type="text" class="lookahead-input form-control" placeholder="{{ placeholder }}"
|
||||||
ng-readonly="!allowCustomInput"></input>
|
ng-readonly="!allowCustomInput"></input>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown">
|
<div class="dropdown" ng-show="!hideDropdown">
|
||||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
|
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -164,7 +164,8 @@
|
||||||
<div class="dropdown-select" placeholder="'(Repository Root)'" selected-item="state.currentLocation"
|
<div class="dropdown-select" placeholder="'(Repository Root)'" selected-item="state.currentLocation"
|
||||||
lookahead-items="locations" handle-input="handleLocationInput(input)"
|
lookahead-items="locations" handle-input="handleLocationInput(input)"
|
||||||
handle-item-selected="handleLocationSelected(datum)"
|
handle-item-selected="handleLocationSelected(datum)"
|
||||||
allow-custom-input="true">
|
allow-custom-input="true"
|
||||||
|
hide-dropdown="!supportsFullListing">
|
||||||
<!-- Icons -->
|
<!-- Icons -->
|
||||||
<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-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 none-icon fa fa-folder fa-lg" style="color: black;" ng-show="!state.isInvalidLocation"></i>
|
||||||
|
@ -187,13 +188,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="quay-spinner" ng-show="!locations && !locationError"></div>
|
<div class="quay-spinner" ng-show="!locations && !locationError"></div>
|
||||||
<div class="alert alert-warning" ng-show="locations && !locations.length">
|
|
||||||
Warning: No Dockerfiles were found in {{ state.currentRepo.repo }}
|
|
||||||
</div>
|
|
||||||
<div class="alert alert-warning" ng-show="locationError">
|
<div class="alert alert-warning" ng-show="locationError">
|
||||||
{{ locationError }}
|
{{ locationError }}
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info" ng-show="locations.length && state.isInvalidLocation">
|
<div class="alert alert-warning" ng-show="locations && !locations.length && supportsFullListing">
|
||||||
|
Warning: No Dockerfiles were found in {{ state.currentRepo.repo }}
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-info" ng-show="locations.length && state.isInvalidLocation && supportsFullListing">
|
||||||
Note: The folder does not currently exist or contain a Dockerfile
|
Note: The folder does not currently exist or contain a Dockerfile
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
|
||||||
'selectedItem': '=selectedItem',
|
'selectedItem': '=selectedItem',
|
||||||
'placeholder': '=placeholder',
|
'placeholder': '=placeholder',
|
||||||
'lookaheadItems': '=lookaheadItems',
|
'lookaheadItems': '=lookaheadItems',
|
||||||
|
'hideDropdown': '=hideDropdown',
|
||||||
|
|
||||||
'allowCustomInput': '@allowCustomInput',
|
'allowCustomInput': '@allowCustomInput',
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
|
|
||||||
'analyze': '&analyze'
|
'analyze': '&analyze'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, ApiService) {
|
controller: function($scope, $element, ApiService, TriggerService) {
|
||||||
$scope.analyzeCounter = 0;
|
$scope.analyzeCounter = 0;
|
||||||
$scope.setupReady = false;
|
$scope.setupReady = false;
|
||||||
$scope.refs = null;
|
$scope.refs = null;
|
||||||
|
@ -33,6 +33,12 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
'currentLocation': null
|
'currentLocation': null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var checkLocation = function() {
|
||||||
|
var location = $scope.state.currentLocation || '';
|
||||||
|
$scope.state.isInvalidLocation = $scope.supportsFullListing &&
|
||||||
|
$scope.locations.indexOf(location) < 0;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.isMatching = function(kind, name, filter) {
|
$scope.isMatching = function(kind, name, filter) {
|
||||||
try {
|
try {
|
||||||
var patt = new RegExp(filter);
|
var patt = new RegExp(filter);
|
||||||
|
@ -122,8 +128,8 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
$scope.setLocation($scope.locations[0]);
|
$scope.setLocation($scope.locations[0]);
|
||||||
} else {
|
} else {
|
||||||
$scope.state.currentLocation = null;
|
$scope.state.currentLocation = null;
|
||||||
$scope.state.isInvalidLocation = resp['subdir'].indexOf('') < 0;
|
|
||||||
$scope.trigger.$ready = true;
|
$scope.trigger.$ready = true;
|
||||||
|
checkLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
@ -131,9 +137,9 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.handleLocationInput = function(location) {
|
$scope.handleLocationInput = function(location) {
|
||||||
$scope.state.isInvalidLocation = $scope.locations.indexOf(location) < 0;
|
|
||||||
$scope.trigger['config']['subdir'] = location || '';
|
$scope.trigger['config']['subdir'] = location || '';
|
||||||
$scope.trigger.$ready = true;
|
$scope.trigger.$ready = true;
|
||||||
|
checkLocation();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.handleLocationSelected = function(datum) {
|
$scope.handleLocationSelected = function(datum) {
|
||||||
|
@ -142,9 +148,9 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
|
|
||||||
$scope.setLocation = function(location) {
|
$scope.setLocation = function(location) {
|
||||||
$scope.state.currentLocation = location;
|
$scope.state.currentLocation = location;
|
||||||
$scope.state.isInvalidLocation = false;
|
|
||||||
$scope.trigger['config']['subdir'] = location || '';
|
$scope.trigger['config']['subdir'] = location || '';
|
||||||
$scope.trigger.$ready = true;
|
$scope.trigger.$ready = true;
|
||||||
|
checkLocation();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.selectRepo = function(repo, org) {
|
$scope.selectRepo = function(repo, org) {
|
||||||
|
@ -199,6 +205,11 @@ angular.module('quay').directive('triggerSetupGithost', function () {
|
||||||
$scope.repoLookahead = repos;
|
$scope.repoLookahead = repos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.$watch('trigger', function(trigger) {
|
||||||
|
if (!trigger) { return; }
|
||||||
|
$scope.supportsFullListing = TriggerService.supportsFullListing(trigger.service)
|
||||||
|
});
|
||||||
|
|
||||||
$scope.$watch('state.currentRepo', function(repo) {
|
$scope.$watch('state.currentRepo', function(repo) {
|
||||||
if (repo) {
|
if (repo) {
|
||||||
$scope.selectRepoInternal(repo);
|
$scope.selectRepoInternal(repo);
|
||||||
|
|
|
@ -49,7 +49,8 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'GitHub Repository Push';
|
return 'GitHub Repository Push';
|
||||||
}
|
},
|
||||||
|
'supports_full_directory_listing': true
|
||||||
},
|
},
|
||||||
|
|
||||||
'bitbucket': {
|
'bitbucket': {
|
||||||
|
@ -75,7 +76,8 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
return Features.BITBUCKET_BUILD;
|
return Features.BITBUCKET_BUILD;
|
||||||
},
|
},
|
||||||
'icon': 'fa-bitbucket',
|
'icon': 'fa-bitbucket',
|
||||||
'title': function() { return 'Bitbucket Repository Push'; }
|
'title': function() { return 'Bitbucket Repository Push'; },
|
||||||
|
'supports_full_directory_listing': false
|
||||||
},
|
},
|
||||||
|
|
||||||
'custom-git': {
|
'custom-git': {
|
||||||
|
@ -104,6 +106,15 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggerService.supportsFullListing = function(name) {
|
||||||
|
var type = triggerTypes[name];
|
||||||
|
if (!type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!type['supports_full_directory_listing'];
|
||||||
|
};
|
||||||
|
|
||||||
triggerService.getTypes = function() {
|
triggerService.getTypes = function() {
|
||||||
var types = [];
|
var types = [];
|
||||||
for (var key in triggerTypes) {
|
for (var key in triggerTypes) {
|
||||||
|
|
Reference in a new issue