- New UI for the repo view, which shows the build status and admin button on the top bar, and allows for creation of new builds as well as linking to build history
- Add a new build button to the build history page -
This commit is contained in:
parent
0ceeb6f8e7
commit
7bf6936154
8 changed files with 439 additions and 204 deletions
|
@ -846,10 +846,20 @@ i.toggle-icon:hover {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visible-md-inline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden-sm-inline {
|
.hidden-sm-inline {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 991px) {
|
||||||
|
.visible-md-inline {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 991px) and (min-width: 768px) {
|
@media (max-width: 991px) and (min-width: 768px) {
|
||||||
.visible-sm-inline {
|
.visible-sm-inline {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
@ -1449,7 +1459,10 @@ p.editable:hover i {
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo .header {
|
.repo .header {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1499,36 +1512,12 @@ p.editable:hover i {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo .status-boxes {
|
.repo .repo-controls .count {
|
||||||
float: right;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo .status-boxes .status-box {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid #eee;
|
padding-left: 4px;
|
||||||
border-radius: 4px;
|
padding-right: 4px;
|
||||||
}
|
|
||||||
|
|
||||||
.repo .status-boxes .status-box .title {
|
|
||||||
padding: 4px;
|
|
||||||
display: inline-block;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo .status-boxes .status-box .title i {
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo .status-boxes .status-box .count {
|
|
||||||
display: inline-block;
|
|
||||||
background-image: linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);
|
|
||||||
padding: 4px;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
color: #428bca;
|
||||||
|
|
||||||
transform: scaleX(0);
|
transform: scaleX(0);
|
||||||
-webkit-transform: scaleX(0);
|
-webkit-transform: scaleX(0);
|
||||||
|
@ -1539,13 +1528,13 @@ p.editable:hover i {
|
||||||
-moz-transition: -moz-transform 500ms ease-in-out;
|
-moz-transition: -moz-transform 500ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo .status-boxes .status-box .count.visible {
|
.repo .repo-controls .count.visible {
|
||||||
transform: scaleX(1);
|
transform: scaleX(1);
|
||||||
-webkit-transform: scaleX(1);
|
-webkit-transform: scaleX(1);
|
||||||
-moz-transform: scaleX(1);
|
-moz-transform: scaleX(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo .pull-command {
|
.repo .repo-controls {
|
||||||
float: right;
|
float: right;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
@ -3198,3 +3187,8 @@ pre.command:before {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-drop {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
27
static/directives/dockerfile-build-dialog.html
Normal file
27
static/directives/dockerfile-build-dialog.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<div class="dockerfile-build-dialog-element">
|
||||||
|
<!-- Modal message dialog -->
|
||||||
|
<div class="modal fade" id="dockerfilebuildModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">
|
||||||
|
Start new Dockerfile build
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body token-dialog-body">
|
||||||
|
<div class="alert alert-danger" ng-show="errorMessage">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
<div class="dockerfile-build-form" repository="repository" upload-failed="handleBuildFailed(message)"
|
||||||
|
build-started="handleBuildStarted(build)" build-failed="handleBuildFailed(message)" start-now="startCounter"
|
||||||
|
has-dockerfile="hasDockerfile" uploading="uploading" building="building"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="startBuild()" ng-disabled="building || uploading || !hasDockerfile">Start Build</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
</div>
|
19
static/directives/dockerfile-build-form.html
Normal file
19
static/directives/dockerfile-build-form.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="dockerfile-build-form-element">
|
||||||
|
<div class="container" ng-show="building">
|
||||||
|
<div class="quay-spinner"></div>
|
||||||
|
</div>
|
||||||
|
<div class="container" ng-show="uploading">
|
||||||
|
<span class="message">Uploading file {{ upload_file }}</span>
|
||||||
|
<div class="progress progress-striped active">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="{{ upload_progress }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + upload_progress + '%' }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container" ng-show="!uploading && !building">
|
||||||
|
<div class="init-description">
|
||||||
|
Upload a Dockerfile or a zip file containing a Dockerfile <b>in the root directory</b>
|
||||||
|
</div>
|
||||||
|
<input id="file-drop" class="file-drop" type="file" file-present="internal.hasDockerfile">
|
||||||
|
</div>
|
||||||
|
</div>
|
249
static/js/app.js
249
static/js/app.js
|
@ -2700,6 +2700,239 @@ quayApp.directive('buildProgress', function () {
|
||||||
return directiveDefinitionObject;
|
return directiveDefinitionObject;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('dockerfileBuildDialog', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/dockerfile-build-dialog.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'showNow': '=showNow',
|
||||||
|
'buildStarted': '&buildStarted'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.building = false;
|
||||||
|
$scope.uploading = false;
|
||||||
|
$scope.startCounter = 0;
|
||||||
|
|
||||||
|
$scope.handleBuildStarted = function(build) {
|
||||||
|
$('#dockerfilebuildModal').modal('hide');
|
||||||
|
if ($scope.buildStarted) {
|
||||||
|
$scope.buildStarted({'build': build});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildFailed = function(message) {
|
||||||
|
$scope.errorMessage = message;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.startBuild = function() {
|
||||||
|
$scope.errorMessage = null;
|
||||||
|
$scope.startCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('showNow', function(sn) {
|
||||||
|
if (sn && $scope.repository) {
|
||||||
|
$('#dockerfilebuildModal').modal({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('dockerfileBuildForm', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/dockerfile-build-form.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'startNow': '=startNow',
|
||||||
|
'hasDockerfile': '=hasDockerfile',
|
||||||
|
'uploadFailed': '&uploadFailed',
|
||||||
|
'uploadStarted': '&uploadStarted',
|
||||||
|
'buildStarted': '&buildStarted',
|
||||||
|
'buildFailed': '&buildFailed',
|
||||||
|
'missingFile': '&missingFile',
|
||||||
|
'uploading': '=uploading',
|
||||||
|
'building': '=building'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService) {
|
||||||
|
$scope.internal = {'hasDockerfile': false};
|
||||||
|
|
||||||
|
var handleBuildFailed = function(message) {
|
||||||
|
message = message || 'Dockerfile build failed to start';
|
||||||
|
|
||||||
|
var result = false;
|
||||||
|
if ($scope.buildFailed) {
|
||||||
|
result = $scope.buildFailed({'message': message});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
bootbox.dialog({
|
||||||
|
"message": message,
|
||||||
|
"title": "Cannot start Dockerfile build",
|
||||||
|
"buttons": {
|
||||||
|
"close": {
|
||||||
|
"label": "Close",
|
||||||
|
"className": "btn-primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleUploadFailed = function(message) {
|
||||||
|
message = message || 'Error with file upload';
|
||||||
|
|
||||||
|
var result = false;
|
||||||
|
if ($scope.uploadFailed) {
|
||||||
|
result = $scope.uploadFailed({'message': message});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
bootbox.dialog({
|
||||||
|
"message": message,
|
||||||
|
"title": "Cannot upload file for Dockerfile build",
|
||||||
|
"buttons": {
|
||||||
|
"close": {
|
||||||
|
"label": "Close",
|
||||||
|
"className": "btn-primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleMissingFile = function() {
|
||||||
|
var result = false;
|
||||||
|
if ($scope.missingFile) {
|
||||||
|
result = $scope.missingFile({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
bootbox.dialog({
|
||||||
|
"message": 'A Dockerfile or an archive containing a Dockerfile is required',
|
||||||
|
"title": "Missing Dockerfile",
|
||||||
|
"buttons": {
|
||||||
|
"close": {
|
||||||
|
"label": "Close",
|
||||||
|
"className": "btn-primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var startBuild = function(fileId) {
|
||||||
|
$scope.building = true;
|
||||||
|
|
||||||
|
var repo = $scope.repository;
|
||||||
|
var data = {
|
||||||
|
'file_id': fileId
|
||||||
|
};
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': repo.namespace + '/' + repo.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.requestRepoBuild(data, params).then(function(resp) {
|
||||||
|
$scope.building = false;
|
||||||
|
$scope.uploading = false;
|
||||||
|
|
||||||
|
if ($scope.buildStarted) {
|
||||||
|
$scope.buildStarted({'build': resp});
|
||||||
|
}
|
||||||
|
}, function(resp) {
|
||||||
|
$scope.building = false;
|
||||||
|
$scope.uploading = false;
|
||||||
|
|
||||||
|
handleBuildFailed(resp.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var conductUpload = function(file, url, fileId, mimeType) {
|
||||||
|
if ($scope.uploadStarted) {
|
||||||
|
$scope.uploadStarted({});
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open('PUT', url, true);
|
||||||
|
request.setRequestHeader('Content-Type', mimeType);
|
||||||
|
request.onprogress = function(e) {
|
||||||
|
$scope.$apply(function() {
|
||||||
|
var percentLoaded;
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
$scope.upload_progress = (e.loaded / e.total) * 100;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
request.onerror = function() {
|
||||||
|
$scope.$apply(function() {
|
||||||
|
handleUploadFailed();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
var state = request.readyState;
|
||||||
|
if (state == 4) {
|
||||||
|
$scope.$apply(function() {
|
||||||
|
startBuild(fileId);
|
||||||
|
$scope.uploading = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.send(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
var startFileUpload = function(repo) {
|
||||||
|
$scope.uploading = true;
|
||||||
|
$scope.uploading_progress = 0;
|
||||||
|
|
||||||
|
var uploader = $('#file-drop')[0];
|
||||||
|
if (uploader.files.length == 0) {
|
||||||
|
handleMissingFile();
|
||||||
|
$scope.uploading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = uploader.files[0];
|
||||||
|
$scope.upload_file = file.name;
|
||||||
|
|
||||||
|
var mimeType = file.type || 'application/octet-stream';
|
||||||
|
var data = {
|
||||||
|
'mimeType': mimeType
|
||||||
|
};
|
||||||
|
|
||||||
|
var getUploadUrl = ApiService.getFiledropUrl(data).then(function(resp) {
|
||||||
|
conductUpload(file, resp.url, resp.file_id, mimeType);
|
||||||
|
}, function() {
|
||||||
|
handleUploadFailed('Could not retrieve upload URL');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('internal.hasDockerfile', function(d) {
|
||||||
|
$scope.hasDockerfile = d;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$watch('startNow', function() {
|
||||||
|
if ($scope.startNow && $scope.repository && !$scope.uploading && !$scope.building) {
|
||||||
|
startFileUpload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Note: ngBlur is not yet in Angular stable, so we add it manaully here.
|
// Note: ngBlur is not yet in Angular stable, so we add it manaully here.
|
||||||
quayApp.directive('ngBlur', function() {
|
quayApp.directive('ngBlur', function() {
|
||||||
return function( scope, elem, attrs ) {
|
return function( scope, elem, attrs ) {
|
||||||
|
@ -2709,6 +2942,22 @@ quayApp.directive('ngBlur', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
quayApp.directive("filePresent", [function () {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
scope: {
|
||||||
|
'filePresent': "="
|
||||||
|
},
|
||||||
|
link: function (scope, element, attributes) {
|
||||||
|
element.bind("change", function (changeEvent) {
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.filePresent = changeEvent.target.files.length > 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
quayApp.directive('ngVisible', function () {
|
quayApp.directive('ngVisible', function () {
|
||||||
return function (scope, element, attr) {
|
return function (scope, element, attr) {
|
||||||
scope.$watch(attr.ngVisible, function (visible) {
|
scope.$watch(attr.ngVisible, function (visible) {
|
||||||
|
|
|
@ -341,8 +341,17 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
||||||
|
|
||||||
// Start scope methods //////////////////////////////////////////
|
// Start scope methods //////////////////////////////////////////
|
||||||
|
|
||||||
|
$scope.buildDialogShowCounter = 0;
|
||||||
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
|
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
|
||||||
|
|
||||||
|
$scope.showNewBuildDialog = function() {
|
||||||
|
$scope.buildDialogShowCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildStarted = function(build) {
|
||||||
|
startBuildInfoTimer($scope.repo);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.showBuild = function(buildInfo) {
|
$scope.showBuild = function(buildInfo) {
|
||||||
$location.path('/repository/' + namespace + '/' + name + '/build');
|
$location.path('/repository/' + namespace + '/' + name + '/build');
|
||||||
$location.search('current', buildInfo.id);
|
$location.search('current', buildInfo.id);
|
||||||
|
@ -784,6 +793,17 @@ function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope
|
||||||
$scope.builds = [];
|
$scope.builds = [];
|
||||||
$scope.polling = false;
|
$scope.polling = false;
|
||||||
|
|
||||||
|
$scope.buildDialogShowCounter = 0;
|
||||||
|
|
||||||
|
$scope.showNewBuildDialog = function() {
|
||||||
|
$scope.buildDialogShowCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildStarted = function(newBuild) {
|
||||||
|
$scope.builds.push(newBuild);
|
||||||
|
$scope.setCurrentBuild(newBuild['id'], true);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.adjustLogHeight = function() {
|
$scope.adjustLogHeight = function() {
|
||||||
$('.build-logs').height($(window).height() - 385);
|
$('.build-logs').height($(window).height() - 385);
|
||||||
};
|
};
|
||||||
|
@ -1548,12 +1568,6 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
|
||||||
'initialize': false
|
'initialize': false
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#couldnotbuildModal').on('hidden.bs.modal', function() {
|
|
||||||
$scope.$apply(function() {
|
|
||||||
$location.path('/repository/' + $scope.created.namespace + '/' + $scope.created.name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch the namespace on the repo. If it changes, we update the plan and the public/private
|
// Watch the namespace on the repo. If it changes, we update the plan and the public/private
|
||||||
// accordingly.
|
// accordingly.
|
||||||
$scope.isUserNamespace = true;
|
$scope.isUserNamespace = true;
|
||||||
|
@ -1600,15 +1614,36 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
|
||||||
$scope.repo.namespace = namespace;
|
$scope.repo.namespace = namespace;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildStarted = function() {
|
||||||
|
var repo = $scope.repo;
|
||||||
|
$location.path('/repository/' + repo.namespace + '/' + repo.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildFailed = function(message) {
|
||||||
|
var repo = $scope.repo;
|
||||||
|
|
||||||
|
bootbox.dialog({
|
||||||
|
"message": message,
|
||||||
|
"title": "Could not start Dockerfile build",
|
||||||
|
"buttons": {
|
||||||
|
"close": {
|
||||||
|
"label": "Close",
|
||||||
|
"className": "btn-primary",
|
||||||
|
"callback": function() {
|
||||||
|
$scope.$apply(function() {
|
||||||
|
$location.path('/repository/' + repo.namespace + '/' + repo.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.createNewRepo = function() {
|
$scope.createNewRepo = function() {
|
||||||
$('#repoName').popover('hide');
|
$('#repoName').popover('hide');
|
||||||
|
|
||||||
var uploader = $('#file-drop')[0];
|
|
||||||
if ($scope.repo.initialize && uploader.files.length < 1) {
|
|
||||||
$('#missingfileModal').modal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.creating = true;
|
$scope.creating = true;
|
||||||
var repo = $scope.repo;
|
var repo = $scope.repo;
|
||||||
var data = {
|
var data = {
|
||||||
|
@ -1624,7 +1659,7 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
|
||||||
|
|
||||||
// Repository created. Start the upload process if applicable.
|
// Repository created. Start the upload process if applicable.
|
||||||
if ($scope.repo.initialize) {
|
if ($scope.repo.initialize) {
|
||||||
startFileUpload(created);
|
$scope.createdForBuild = created;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1654,74 +1689,6 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
|
||||||
PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks);
|
PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks);
|
||||||
};
|
};
|
||||||
|
|
||||||
var startBuild = function(repo, fileId) {
|
|
||||||
$scope.building = true;
|
|
||||||
|
|
||||||
var data = {
|
|
||||||
'file_id': fileId
|
|
||||||
};
|
|
||||||
|
|
||||||
var params = {
|
|
||||||
'repository': repo.namespace + '/' + repo.name
|
|
||||||
};
|
|
||||||
|
|
||||||
ApiService.requestRepoBuild(data, params).then(function(resp) {
|
|
||||||
$location.path('/repository/' + params.repository);
|
|
||||||
}, function() {
|
|
||||||
$('#couldnotbuildModal').modal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var conductUpload = function(repo, file, url, fileId, mimeType) {
|
|
||||||
var request = new XMLHttpRequest();
|
|
||||||
request.open('PUT', url, true);
|
|
||||||
request.setRequestHeader('Content-Type', mimeType);
|
|
||||||
request.onprogress = function(e) {
|
|
||||||
$scope.$apply(function() {
|
|
||||||
var percentLoaded;
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
$scope.upload_progress = (e.loaded / e.total) * 100;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
request.onerror = function() {
|
|
||||||
$scope.$apply(function() {
|
|
||||||
$('#couldnotbuildModal').modal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
request.onreadystatechange = function() {
|
|
||||||
var state = request.readyState;
|
|
||||||
if (state == 4) {
|
|
||||||
$scope.$apply(function() {
|
|
||||||
$scope.uploading = false;
|
|
||||||
startBuild(repo, fileId);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
request.send(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
var startFileUpload = function(repo) {
|
|
||||||
$scope.uploading = true;
|
|
||||||
$scope.uploading_progress = 0;
|
|
||||||
|
|
||||||
var uploader = $('#file-drop')[0];
|
|
||||||
var file = uploader.files[0];
|
|
||||||
$scope.upload_file = file.name;
|
|
||||||
|
|
||||||
var mimeType = file.type || 'application/octet-stream';
|
|
||||||
var data = {
|
|
||||||
'mimeType': mimeType
|
|
||||||
};
|
|
||||||
|
|
||||||
var getUploadUrl = ApiService.getFiledropUrl(data).then(function(resp) {
|
|
||||||
conductUpload(repo, file, resp.url, resp.file_id, mimeType);
|
|
||||||
}, function() {
|
|
||||||
$('#couldnotbuildModal').modal();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var subscribedToPlan = function(sub) {
|
var subscribedToPlan = function(sub) {
|
||||||
$scope.planChanging = false;
|
$scope.planChanging = false;
|
||||||
$scope.subscription = sub;
|
$scope.subscription = sub;
|
||||||
|
|
|
@ -12,16 +12,7 @@
|
||||||
<div class="quay-spinner"></div>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!user.anonymous && uploading">
|
<div class="container new-repo" ng-show="!user.anonymous && !creating && !building">
|
||||||
<span class="message">Uploading file {{ upload_file }}</span>
|
|
||||||
<div class="progress progress-striped active">
|
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{{ upload_progress }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + upload_progress + '%' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container new-repo" ng-show="!user.anonymous && !creating && !uploading && !building">
|
|
||||||
<form method="post" name="newRepoForm" id="newRepoForm" ng-submit="createNewRepo()">
|
<form method="post" name="newRepoForm" id="newRepoForm" ng-submit="createNewRepo()">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
|
@ -109,11 +100,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="initialize-repo" ng-show="repo.initialize">
|
<div class="initialize-repo" ng-show="repo.initialize">
|
||||||
<div class="init-description">
|
<div class="dockerfile-build-form" repository="createdForBuild" upload-failed="handleBuildFailed(message)"
|
||||||
Upload a Dockerfile or a zip file containing a Dockerfile <b>in the root directory</b>
|
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
|
||||||
</div>
|
has-dockerfile="hasDockerfile" uploading="uploading" building="building"></div>
|
||||||
|
|
||||||
<input id="file-drop" class="file-drop" type="file">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -123,7 +112,7 @@
|
||||||
<div class="col-md-1"></div>
|
<div class="col-md-1"></div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<button class="btn btn-large btn-success" type="submit"
|
<button class="btn btn-large btn-success" type="submit"
|
||||||
ng-disabled="newRepoForm.$invalid || (repo.is_public == '0' && (planRequired || checkingPlan))">
|
ng-disabled="uploading || building || newRepoForm.$invalid || (repo.is_public == '0' && (planRequired || checkingPlan)) || (repo.initialize && !hasDockerfile)">
|
||||||
Create Repository
|
Create Repository
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,26 +146,6 @@
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
|
||||||
<div class="modal fade" id="missingfileModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h4 class="modal-title">File required</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
A file is required in order to initialize a repository.
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="cannotcreateModal">
|
<div class="modal fade" id="cannotcreateModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
@ -195,26 +164,6 @@
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
|
||||||
<div class="modal fade" id="couldnotbuildModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h4 class="modal-title">Cannot initialize repository</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
The repository could not be initialized with the selected Dockerfile. Please try again later.
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="couldnotsubscribeModal">
|
<div class="modal fade" id="couldnotsubscribeModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|
|
@ -9,6 +9,13 @@
|
||||||
<span class="repo-circle no-background" repo="repo"></span>
|
<span class="repo-circle no-background" repo="repo"></span>
|
||||||
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="'Build History'"></span>
|
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="'Build History'"></span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<div class="repo-controls">
|
||||||
|
<button class="btn btn-success" ng-click="showNewBuildDialog()">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
New Dockerfile Build
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-show="!builds.length">
|
<div class="row" ng-show="!builds.length">
|
||||||
|
@ -74,7 +81,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<span class="quay-spinner" ng-show="polling"></span>
|
<span class="quay-spinner" ng-show="polling"></span>
|
||||||
<button class="btn btn-default" ng-show="(build.phase == 'error' || build.phase == 'complete') && build.resource_key"
|
<button class="btn" ng-show="(build.phase == 'error' || build.phase == 'complete') && build.resource_key"
|
||||||
|
ng-class="build.phase == 'error' ? 'btn-success' : 'btn-default'"
|
||||||
ng-click="askRestartBuild(build)">
|
ng-click="askRestartBuild(build)">
|
||||||
<i class="fa fa-refresh"></i>
|
<i class="fa fa-refresh"></i>
|
||||||
Run Build Again
|
Run Build Again
|
||||||
|
@ -86,6 +94,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
|
||||||
|
build-started="handleBuildStarted(build)">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="confirmRestartBuildModal">
|
<div class="modal fade" id="confirmRestartBuildModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|
|
@ -5,23 +5,52 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
|
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
|
||||||
<div class="container repo">
|
<div class="container repo repo-view">
|
||||||
<!-- Repo Header -->
|
<!-- Repo Header -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="repo-circle" repo="repo"></span>
|
<span class="repo-circle" repo="repo"></span>
|
||||||
<span class="repo-breadcrumb" repo="repo"></span>
|
<span class="repo-breadcrumb" repo="repo"></span>
|
||||||
<span class="settings-cog" ng-show="repo.can_admin" title="Repository Settings" bs-tooltip="tooltip.title" data-placement="bottom">
|
|
||||||
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}">
|
|
||||||
<i class="fa fa-cog fa-lg"></i>
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<!-- Pull command -->
|
<div class="repo-controls">
|
||||||
<div class="pull-command visible-md visible-lg" style="display: none;">
|
<!-- Builds -->
|
||||||
<span class="pull-command-title">Pull repository:</span>
|
<div class="dropdown" data-placement="top" style="display: inline-block"
|
||||||
<div class="pull-container">
|
bs-tooltip="buildsInfo ? 'Dockerfile Builds Running: ' + (buildsInfo.length) : 'Dockerfile Build'"
|
||||||
|
ng-show="repo.can_write || buildsInfo.length">
|
||||||
|
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<i class="fa fa-tasks fa-lg"></i>
|
||||||
|
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '' }}</span></span>
|
||||||
|
<b class="caret"></b>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-show="repo.can_write"><a href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/build' }}">
|
||||||
|
<i class="fa fa-tasks"></i>Dockerfile Build History</a>
|
||||||
|
</li>
|
||||||
|
<li ng-show="repo.can_write">
|
||||||
|
<a href="javascript:void(0)" ng-click="showNewBuildDialog()">
|
||||||
|
<i class="fa fa-plus" style="margin-left: 1px; margin-right: 8px;"></i>New Dockerfile Build
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation" class="divider" ng-show="buildsInfo && repo.can_write"></li>
|
||||||
|
<li role="presentation" class="dropdown-header" ng-show="buildsInfo">Current Builds</li>
|
||||||
|
<li ng-repeat="buildInfo in buildsInfo">
|
||||||
|
<div class="build-info" ng-class="repo.can_write ? 'clickable' : ''" ng-click="repo.can_write && showBuild(buildInfo)">
|
||||||
|
<span class="build-status" build="buildInfo"></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Admin -->
|
||||||
|
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}"
|
||||||
|
ng-show="repo.can_admin">
|
||||||
|
<button class="btn btn-default" title="Repository Settings" bs-tooltip="tooltip.title" data-placement="top">
|
||||||
|
<i class="fa fa-cog fa-lg"></i></button></a>
|
||||||
|
|
||||||
|
<!-- Pull Command -->
|
||||||
|
<span class="pull-command visible-md-inline">
|
||||||
|
<div class="pull-container" title="Pull repository" bs-tooltip="tooltip.title">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="pull-text" type="text" class="form-control" value="{{ 'docker pull quay.io/' + repo.namespace + '/' + repo.name }}" readonly>
|
<input id="pull-text" type="text" class="form-control" value="{{ 'docker pull quay.io/' + repo.namespace + '/' + repo.name }}" readonly>
|
||||||
<span id="copyClipboard" class="input-group-addon" title="Copy to Clipboard" data-clipboard-target="pull-text">
|
<span id="copyClipboard" class="input-group-addon" title="Copy to Clipboard" data-clipboard-target="pull-text">
|
||||||
|
@ -33,23 +62,8 @@
|
||||||
<div id="clipboardCopied" class="hovering" style="display: none">
|
<div id="clipboardCopied" class="hovering" style="display: none">
|
||||||
Copied to clipboard
|
Copied to clipboard
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Status boxes -->
|
|
||||||
<div class="status-boxes">
|
|
||||||
<div id="buildInfoBox" class="status-box" ng-show="repo.is_building">
|
|
||||||
<div class="dropdown" data-placement="top">
|
|
||||||
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '-' }}</span></span>
|
|
||||||
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">Building Dockerfile<span ng-show="buildsInfo.length > 1">s</span> <b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu pull-right">
|
|
||||||
<li ng-repeat="buildInfo in buildsInfo">
|
|
||||||
<div class="build-info" ng-class="repo.can_write ? 'clickable' : ''" ng-click="showBuild(buildInfo)">
|
|
||||||
<span class="build-status" build="buildInfo"></span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -72,7 +86,7 @@ sudo docker push quay.io/{{repo.namespace}}/{{repo.name}}</pre>
|
||||||
|
|
||||||
<div class="repo-content" ng-show="!currentTag.image && repo.is_building">
|
<div class="repo-content" ng-show="!currentTag.image && repo.is_building">
|
||||||
<div class="empty-message">
|
<div class="empty-message">
|
||||||
Your build is currently processing, if this takes longer than an hour, please contact <a href="mailto:support@quay.io">Quay.io support</a>
|
A build is currently processing. If this takes longer than an hour, please <a href="/contact">contact us</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -235,6 +249,10 @@ sudo docker push quay.io/{{repo.namespace}}/{{repo.name}}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
|
||||||
|
build-started="handleBuildStarted(build)">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="confirmdeleteTagModal">
|
<div class="modal fade" id="confirmdeleteTagModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|
Reference in a new issue