Merge pull request #22 from coreos-inc/git

git's a pretty cool guy
This commit is contained in:
Jimmy Zelinskie 2015-04-23 17:33:36 -04:00
commit 2a13eade80
35 changed files with 797 additions and 136 deletions

View file

@ -29,7 +29,7 @@
}
.build-view .build-icon-message.internalerror {
color: #DFFF00;
color: rgb(151, 168, 0);
}
.build-view .build-icon-message.complete {

View file

@ -81,6 +81,7 @@
</span>
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
<div ng-switch-when="regex">
<input type="text" class="form-control" ng-model="currentConfig[field.name]"
ng-pattern="getPattern(field)"

View file

@ -0,0 +1,24 @@
<div ng-switch on="trigger.service">
<!-- Message -->
<div ng-switch-when="custom-git" class="alert alert-info">
<p>
In order to use this trigger, the following first requires action:
<ul>
<li>You must give the following public key read access to the git repository.</li>
<li>You must set your repository to POST to the following URL to trigger a build.</li>
</ul>
For more information, refer to the <a href="http://docs.quay.io/guides/custom-trigger.html" target="_blank">Custom Git Triggers documentation</a>.
</p>
</div>
<div ng-switch-when="github" class="alert alert-info">
<p>The following key has been automatically added to your GitHub repository.</p>
</div>
<!-- Credentials -->
<div ng-repeat="credential in trigger.config.credentials">
<p>
{{ credential.name }}:
<div class="copy-box" value="credential.value"></div>
</p>
</div>
</div>

View file

@ -18,11 +18,20 @@
<span ng-switch-when="option">
<span class="quay-spinner" ng-show="!fieldOptions[field.name]"></span>
<select ng-model="parameters[field.name]" ng-show="fieldOptions[field.name]"
ng-options="value for value in fieldOptions[field.name]"
ng-options="value for value in fieldOptions[field.name]"
required>
</select>
</span>
<input type="text" class="form-control" ng-model="parameters[field.name]" ng-switch-when="string" required>
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
<div ng-switch-when="regex">
<input type="text" class="form-control" ng-model="parameters[field.name]"
ng-pattern="getPattern(field)"
placeholder="{{ field.placeholder }}"
ng-name="field.name"
id="{{ field.name }}"
required>
</div>
</div>
</td>
</tr>

View file

@ -93,7 +93,7 @@
</button>
<ul class="dropdown-menu dropdown-menu-right pull-right">
<li ng-repeat="type in TriggerService.getTypes()">
<a href="{{ TriggerService.getRedirectUrl(type, repository.namespace, repository.name) }}">
<a href="{{ TriggerService.getRedirectUrl(type, repository.namespace, repository.name) }}" target="{{ TriggerService.getMetadata(type).is_external ? '' : '_self' }}">
<i class="fa fa-lg" ng-class="TriggerService.getMetadata(type).icon"></i>
{{ TriggerService.getTitle(type) }}
</a>
@ -135,13 +135,16 @@
<tr ng-repeat="trigger in triggers | filter:{'is_active':true}">
<td><div class="trigger-description" trigger="trigger" short="true"></div></td>
<td>{{ trigger.subdir || '(Root Directory)' }}</td>
<td>{{ trigger.config.branchtag_regex || '(All)' }}</td>
<td>{{ trigger.config.subdir || '/' }}</td>
<td>{{ trigger.config.branchtag_regex || 'All' }}</td>
<td>
<span class="entity-reference" entity="trigger.pull_robot" ng-if="trigger.pull_robot"></span>
</td>
<td>
<span class="cor-options-menu">
<span ng-if="trigger.config.credentials" class="cor-option" option-click="showTriggerCredentialsModal(trigger)">
<i class="fa fa-unlock-alt"></i> View Credentials
</span>
<span class="cor-option" option-click="askRunTrigger(trigger)"
ng-class="trigger.connected_user == user.username ? '' : 'disabled'">
<i class="fa fa-chevron-right"></i> Run Trigger Now
@ -159,6 +162,11 @@
</div>
</div> <!-- /Build Triggers -->
<!-- Dialogs -->
<!-- Trigger Credentials dialog -->
<div class="trigger-credentials-dialog" trigger="triggerCredentialsModalTrigger" counter="triggerCredentialsModalCounter"></div>
<!-- Delete Tag Confirm -->
<div class="cor-confirm-dialog"
dialog-context="deleteTriggerInfo"
@ -190,4 +198,6 @@
counter="showTriggerStartDialogCounter"
start-build="startTrigger(trigger, parameters)"></div>
</div>
<!-- /Dialogs -->
</div>

View file

@ -19,6 +19,11 @@
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
<div ng-switch-when="custom-git">
<div class="trigger-setup-custom" repository="repository" trigger="trigger"
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
analyze="checkAnalyze(isValid)"></div>
</div>
</div>
<!-- Loading pull information -->
@ -30,11 +35,13 @@
<div class="trigger-option-section" ng-show="currentView == 'analyzed'">
<!-- 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 ng-switch on="pullInfo.analysis.status">
<div ng-switch-when="error" class="alert alert-danger">{{ pullInfo.analysis.message }}</div>
<div ng-switch-when="warning" class="alert alert-warning">{{ pullInfo.analysis.message }}</div>
<div ng-switch-when="notimplemented" class="alert alert-warning">
<p>For {{ TriggerService.getTitle(trigger.service) }} triggers, we are unable to determine dependencies automatically.</p>
<p>If the git repository being built depends on a private base image, you must manually select a robot account with the proper permissions.</p>
</div>
</div>
<div class="dockerfile-found" ng-if="pullInfo.analysis.is_public === false">
<div class="dockerfile-found-content">
@ -54,7 +61,9 @@
</div>
</div>
<div style="margin-bottom: 12px">Please select the credentials to use when pulling the base image:</div>
<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>
@ -101,6 +110,13 @@
</div>
<div class="trigger-option-section" ng-show="currentView == 'postActivation'">
<div ng-if="trigger.config.credentials" class="credentials" trigger="trigger"></div>
<div ng-if="!trigger.config.credentials">
<div class="alert alert-success">The trigger has been successfully created.</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-disabled="!state.stepValid"
@ -112,7 +128,7 @@
ng-click="activate()"
ng-show="currentView == 'analyzed'">Create Trigger</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ currentView == 'postActivation' ? 'Done' : 'Cancel' }}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->

View file

@ -0,0 +1,19 @@
<!-- Modal message dialog -->
<div class="modal fade" id="triggercredentialsmodal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">
Trigger Credentials
</h4>
</div>
<div class="modal-body">
<div class="credentials" trigger="trigger"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Done</button>
</div>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->

View file

@ -1,4 +1,5 @@
<span class="trigger-description-element" ng-switch on="trigger.service">
<!-- GitHub -->
<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 <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> repository
@ -14,11 +15,24 @@
<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>
<span>{{ TriggerService.getDockerfileLocation(trigger) }}</span>
</div>
</div>
</span>
<!-- Git -->
<span ng-switch-when="custom-git">
<i class="fa fa-git fa-lg" style="margin-right: 6px;" data-title="git" bs-tooltip="tooltip.title"></i>
Push to {{ trigger.config.build_source }}
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">
<div>
<span class="trigger-description-subtitle">Dockerfile:</span>
<span>{{ TriggerService.getDockerfileLocation(trigger) }}</span<
</div>
</div>
</span>
<!-- Who knows? -->
<span ng-switch-default>
Unknown
</span>

View file

@ -0,0 +1,40 @@
<div class="trigger-setup-custom-element">
<div class="selected-info" ng-show="nextStepCounter > 0">
<table style="width: 100%;">
<tr ng-show="nextStepCounter > 0">
<td width="200px">Repository</td>
<td>{{ state.build_source }}</td>
</tr>
<tr ng-show="nextStepCounter > 1">
<td>Dockerfile Location:</td>
<td>
<div class="dockerfile-location">
<i class="fa fa-folder fa-lg"></i> {{ state.subdir || '/' }}
</div>
</td>
</table>
</div>
<!-- Step view -->
<div class="step-view" next-step-counter="nextStepCounter" current-step-valid="currentStepValid"
steps-completed="stepsCompleted()">
<!-- Git URL Input -->
<!-- TODO(jschorr): make nopLoad(callback) no longer required -->
<div class="step-view-step" complete-condition="trigger['config']['build_source']" load-callback="nopLoad(callback)"
load-message="Loading Git URL Input">
<div style="margin-bottom: 12px;">Please enter an HTTP or SSH style URL used to clone your git repository:</div>
<input class="form-control" type="text" placeholder="git@example.com:namespace/repository.git" style="width: 100%;"
ng-model="state.build_source" ng-pattern="/(((http|https):\/\/)(.+)|\w+@(.+):(.+))/">
</div>
<!-- Dockerfile folder select -->
<div class="step-view-step" complete-condition="trigger.$ready" load-callback="nopLoad(callback)"
load-message="Loading Folder Input">
<div style="margin-bottom: 12px">Dockerfile Location:</div>
<input class="form-control" type="text" placeholder="/" style="width: 100%;"
ng-model="state.subdir" ng-pattern="/^($|\/|\/.+)/">
</div>
</div>
</div>

View file

@ -33,7 +33,7 @@
</td>
<td>
<div class="dockerfile-location">
<i class="fa fa-folder fa-lg"></i> {{ state.currentLocation || '(Repository Root)' }}
<i class="fa fa-folder fa-lg"></i> {{ state.currentLocation || '(Repository Root)' }}
</div>
</td>
</tr>

View file

@ -1,18 +1,17 @@
<div class="triggered-build-description-element">
<span class="tbd-content" class="manual" ng-if="!build.trigger && !build.job_config.manual_user">
(Manually Triggered Build)
</span>
<span class="tbd-content" class="manual" ng-if="!build.trigger && !build.job_config.manual_user">
(Manually Triggered Build)
</span>
<span class="tbd-content" ng-if="!build.trigger && build.job_config.manual_user">
<i class="fa fa-user"></i> {{ build.job_config.manual_user }}
</span>
<span class="tbd-content" ng-if="!build.trigger && build.job_config.manual_user">
<i class="fa fa-user"></i> {{ build.job_config.manual_user }}
</span>
<span ng-switch on="build.trigger.service" ng-if="build.trigger">
<!-- GitHub -->
<span ng-switch-when="github">
<!-- Full Commit Information -->
<span class="tbd-content" ng-if="build.job_config.trigger_metadata.commit_info">
<!-- Full Commit Information -->
<span class="tbd-content" ng-if="build.job_config.trigger_metadata.commit_info" ng-switch on="build.trigger.service">
<!-- GitHub -->
<div ng-switch-when="github">
<div class="commit-message">
<a ng-href="{{ getGitHubRepoURL(build) }}/commit/{{ build.job_config.trigger_metadata.commit_sha }}"
target="_blank">
@ -39,27 +38,63 @@
branch-template="getGitHubRepoURL(build) + '/tree/{branch}'"
tag-template="getGitHubRepoURL(build) + '/releases/tag/{tag}'"></span>
</div>
</span>
</div>
<!-- Just commit SHA -->
<span class="tbd-content" ng-if="build.job_config.trigger_metadata && !build.job_config.trigger_metadata.commit_info">
Triggered by commit
<!-- Git -->
<div ng-switch-when="custom-git">
<div class="commit-message">
{{ build.job_config.trigger_metadata.commit_info.message }}
</div>
<div class="commit-information">
<span class="commit-who-when">
Authored
<span am-time-ago="build.job_config.trigger_metadata.commit_info.date"></span>
<span class="commit-who">
{{ build.job_config.trigger_metadata.commit_info.author.username }}
</span>
</span>
<span>
{{ build.job_config.trigger_metadata.commit_sha }}
</span>
<span>
{{ build.job_config.trigger_metadata.ref }}
</span>
</div>
</div>
</span>
<!-- Just commit SHA -->
<span class="tbd-content" ng-if="build.job_config.trigger_metadata && !build.job_config.trigger_metadata.commit_info" ng-switch on="build.trigger.service">
Triggered by commit
<!-- GitHub -->
<div ng-switch-when="github">
<span class="source-commit-link"
commit-sha="build.job_config.trigger_metadata.commit_sha"
url-template="getGitHubRepoURL(build) + '/commit/{sha}'"></span>
</span>
</div>
<!-- No information -->
<span class="tbd-content" ng-if="!build.job_config.trigger_metadata">
Triggered by commit to
<!-- Git -->
<div ng-switch-when="custom-git">
<span>{{ build.job_config.trigger_metadata.commit_sha }}</span>
</div>
</span>
<!-- No information -->
<span class="tbd-content" ng-if="!build.job_config.trigger_metadata" ng-switch on="build.trigger.service">
Triggered by commit to
<!-- GitHub -->
<div ng-switch-when="github">
<i class="fa fa-github fa-lg" data-title="GitHub" data-container="body" bs-tooltip></i>
<a ng-href="{{ getGitHubRepoURL(build) }}" target="_new">
{{ build.trigger.config.build_source }}
</a>
</span>
</div>
<!-- Git -->
<div ng-switch-when="custom-git">
<i class="fa fa-git fa-lg" data-title="git" data-container="body" bs-tooltip></i>
{{ build.trigger.config.build_source }}
</div>
</span>
<!-- Unknown -->
<span ng-switch-default></span>
</span>
</div>

View file

@ -34,6 +34,9 @@ angular.module('quay').directive('repoPanelBuilds', function () {
$scope.showTriggerStartDialogCounter = 0;
$scope.showTriggerSetupCounter = 0;
$scope.triggerCredentialsModalTrigger = null;
$scope.triggerCredentialsModalCounter = 0;
var updateBuilds = function() {
if (!$scope.allBuilds) { return; }
@ -164,6 +167,11 @@ angular.module('quay').directive('repoPanelBuilds', function () {
$scope.options.predicate = predicate;
};
$scope.showTriggerCredentialsModal = function(trigger) {
$scope.triggerCredentialsModalTrigger = trigger;
$scope.triggerCredentialsModalCounter++;
};
$scope.askDeleteTrigger = function(trigger) {
$scope.deleteTriggerInfo = {
'trigger': trigger

View file

@ -0,0 +1,16 @@
/**
* An element which displays a credentials for a build trigger.
*/
angular.module('quay').directive('credentials', function() {
var directiveDefinitionObject = {
templateUrl: '/static/directives/credentials.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'trigger': '=trigger'
},
controller: function($scope) {}
};
return directiveDefinitionObject;
});

View file

@ -25,6 +25,10 @@ angular.module('quay').directive('manualTriggerBuildDialog', function () {
});
};
$scope.getPattern = function(field) {
return new RegExp(field.regex);
};
$scope.show = function() {
$scope.parameters = {};
$scope.fieldOptions = {};
@ -58,4 +62,4 @@ angular.module('quay').directive('manualTriggerBuildDialog', function () {
}
};
return directiveDefinitionObject;
});
});

View file

@ -14,12 +14,13 @@ angular.module('quay').directive('setupTriggerDialog', function () {
'canceled': '&canceled',
'activated': '&activated'
},
controller: function($scope, $element, ApiService, UserService) {
controller: function($scope, $element, ApiService, UserService, TriggerService) {
var modalSetup = false;
$scope.state = {};
$scope.nextStepCounter = -1;
$scope.currentView = 'config';
$scope.TriggerService = TriggerService
$scope.show = function() {
if (!$scope.trigger || !$scope.repository) { return; }
@ -113,10 +114,11 @@ angular.module('quay').directive('setupTriggerDialog', function () {
});
ApiService.activateBuildTrigger(data, params).then(function(resp) {
$scope.hide();
$scope.trigger['is_active'] = true;
$scope.trigger['config'] = resp['config'];
$scope.trigger['pull_robot'] = resp['pull_robot'];
$scope.activated({'trigger': $scope.trigger});
$scope.currentView = 'postActivation';
}, errorHandler);
};

View file

@ -0,0 +1,29 @@
/**
* An element which displays a dialog with the necessary credentials for a build trigger.
*/
angular.module('quay').directive('triggerCredentialsDialog', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/trigger-credentials-dialog.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'trigger': '=trigger',
'counter': '=counter'
},
controller: function($scope, $element) {
var show = function() {
if (!$scope.trigger || !$scope.counter) {
$('#triggercredentialsmodal').modal('hide');
return;
}
$('#triggercredentialsmodal').modal({});
};
$scope.$watch('trigger', show);
$scope.$watch('counter', show);
}
};
return directiveDefinitionObject;
});

View file

@ -0,0 +1,49 @@
/**
* An element which displays custom git-specific setup information for its build triggers.
*/
angular.module('quay').directive('triggerSetupCustom', function() {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/trigger-setup-custom.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger',
'nextStepCounter': '=nextStepCounter',
'currentStepValid': '=currentStepValid',
'analyze': '&analyze'
},
controller: function($scope, $element, ApiService) {
$scope.analyzeCounter = 0;
$scope.setupReady = false;
$scope.state = {
'build_source': null,
'subdir': null
};
$scope.stepsCompleted = function() {
$scope.analyze({'isValid': $scope.state.build_source != null && $scope.state.subdir != null});
};
$scope.$watch('state.build_source', function(build_source) {
$scope.trigger['config']['build_source'] = build_source;
});
$scope.$watch('state.subdir', function(subdir) {
$scope.trigger['config']['subdir'] = subdir;
$scope.trigger.$ready = subdir != null;
});
$scope.nopLoad = function(callback) {
callback();
};
}
};
return directiveDefinitionObject;
});

View file

@ -69,7 +69,7 @@
$scope.isBuilding = function(build) {
if (!build) { return true; }
return build.phase != 'complete' && build.phase != 'error';
return build.phase != 'complete' && build.phase != 'error' && build.phase != 'internalerror';
};
}
})();
})();

View file

@ -64,4 +64,4 @@ angular.module('quay').factory('KeyService', ['$location', 'Config', function($l
};
return keyService;
}]);
}]);

View file

@ -2,8 +2,8 @@
* Helper service for defining the various kinds of build triggers and retrieving information
* about them.
*/
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService', 'Features', 'CookieService',
function(UtilService, $sanitize, KeyService, Features, CookieService) {
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService', 'Features', 'CookieService', 'Config',
function(UtilService, $sanitize, KeyService, Features, CookieService, Config) {
var triggerService = {};
var triggerTypes = {
@ -15,7 +15,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
desc += '<br>Dockerfile folder: //' + UtilService.textToSafeHtml(config['subdir']);
return desc;
},
'run_parameters': [
{
'title': 'Branch',
@ -23,7 +22,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
'name': 'branch_name'
}
],
'get_redirect_url': function(namespace, repository) {
var redirect_uri = KeyService['githubRedirectUri'] + '/trigger/' +
namespace + '/' + repository;
@ -39,13 +37,11 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
return authorize_url + 'client_id=' + client_id +
'&scope=repo,user:email&redirect_uri=' + redirect_uri;
},
'is_external': true,
'is_enabled': function() {
return Features.GITHUB_BUILD;
},
'icon': 'fa-github',
'title': function() {
var isEnterprise = KeyService.isEnterprise('github-trigger');
if (isEnterprise) {
@ -54,6 +50,31 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
return 'GitHub Repository Push';
}
},
'custom-git': {
'description': function(config) {
var source = UtilService.textToSafeHtml(config['build_source']);
var desc = '<i class"fa fa-git fa-lg" style="margin-left:2px; margin-right: 2px"></i> Push to Custom Git Repository ' + source;
desc += '<br>Dockerfile folder: //' + UtilService.textToSafeHtml(config['subdir']);
return desc;
},
'run_parameters': [
{
'title': 'Commit',
'type': 'regex',
'name': 'commit_sha',
'regex': '^([A-Fa-f0-9]{7})$',
'placeholder': '1c002dd'
}
],
'get_redirect_url': function(namespace, repository) {
return Config.getUrl('/customtrigger/setup/' + namespace + '/' + repository);
},
'is_external': false,
'is_enabled': function() { return true; },
'icon': 'fa-git',
'title': function() { return 'Custom Git Repository Push'; }
}
}
@ -76,6 +97,13 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
return type['get_redirect_url'](namespace, repository);
};
triggerService.getDockerfileLocation = function(trigger) {
if (!trigger.config.subdir) {
return '//Dockerfile';
}
return '//' + trigger.config.subdir.replace(new RegExp('(^\/+|\/+$)'), '') + '/Dockerfile';
};
triggerService.getTitle = function(name) {
var type = triggerTypes[name];
if (!type) {