Start on builds page for repos
This commit is contained in:
parent
9e8f765040
commit
35cfdcaa8c
8 changed files with 192 additions and 39 deletions
|
@ -524,7 +524,7 @@ i.toggle-icon:hover {
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-info:hover {
|
.build-info.clickable:hover {
|
||||||
background: rgba(66, 139, 202, 0.2);
|
background: rgba(66, 139, 202, 0.2);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -535,33 +535,36 @@ i.toggle-icon:hover {
|
||||||
border-bottom: 0px;
|
border-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon {
|
.phase-icon {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-status .phase-icon {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon.error {
|
.phase-icon.error {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon.waiting, .build-status .phase-icon.starting, .build-status .phase-icon.initializing {
|
.phase-icon.waiting, .phase-icon.starting, .phase-icon.initializing {
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon.building {
|
.phase-icon.building {
|
||||||
background-color: #f0ad4e;
|
background-color: #f0ad4e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon.pushing {
|
.phase-icon.pushing {
|
||||||
background-color: #5cb85c;
|
background-color: #5cb85c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-status .phase-icon.complete {
|
.phase-icon.complete {
|
||||||
background-color: #428bca;
|
background-color: #428bca;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
static/directives/build-message.html
Normal file
1
static/directives/build-message.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<span class="build-message-element">{{ getBuildMessage(build) }}</span>
|
6
static/directives/build-progress.html
Normal file
6
static/directives/build-progress.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="build-progress-element">
|
||||||
|
<div class="progress" ng-class="getPercentage(build) < 100 ? 'active progress-striped' : ''" ng-show="getPercentage(build) > 0 && getPercentage(build) < 100">
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuenow="{{ getPercentage(build) }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + getPercentage(build) + '%' }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,14 +1,11 @@
|
||||||
<div id="build-status-container" class="build-status-container">
|
<div id="build-status-container" class="build-status-container">
|
||||||
<div>
|
<div>
|
||||||
<span class="phase-icon" ng-class="build.phase"></span>
|
<span class="phase-icon" ng-class="build.phase"></span>
|
||||||
<span class="build-message">{{ getBuildMessage(build) }}</span>
|
<span class="build-message" build="build"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="timing">
|
<div class="timing">
|
||||||
<i class="fa fa-clock-o"></i>
|
<i class="fa fa-clock-o"></i>
|
||||||
Started: <span am-time-ago="build.started || 0"></span>
|
Started: <span am-time-ago="build.started || 0"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" ng-class="getBuildProgress(build) < 100 ? 'active progress-striped' : ''" ng-show="getBuildProgress(build) > 0 && getBuildProgress(build) < 100">
|
<div class="build-progress" build="build"></div>
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{{ getBuildProgress(build) }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + getBuildProgress(build) + '%' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -774,6 +774,7 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
|
||||||
fixFooter: false}).
|
fixFooter: false}).
|
||||||
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}).
|
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}).
|
||||||
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}).
|
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}).
|
||||||
|
when('/repository/:namespace/:name/build', {templateUrl: '/static/partials/repo-build.html', controller:RepoBuildCtrl, reloadOnSearch: false}).
|
||||||
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
|
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
|
||||||
templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}).
|
templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}).
|
||||||
when('/user/', {title: 'Account Settings', description:'Account settings for Quay.io', templateUrl: '/static/partials/user-admin.html',
|
when('/user/', {title: 'Account Settings', description:'Account settings for Quay.io', templateUrl: '/static/partials/user-admin.html',
|
||||||
|
@ -2474,7 +2475,63 @@ quayApp.directive('buildStatus', function () {
|
||||||
'build': '=build'
|
'build': '=build'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element) {
|
controller: function($scope, $element) {
|
||||||
$scope.getBuildProgress = function(buildInfo) {
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('buildMessage', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/build-message.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'build': '=build'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.getBuildMessage = function (buildInfo) {
|
||||||
|
switch (buildInfo.phase) {
|
||||||
|
case 'starting':
|
||||||
|
case 'initializing':
|
||||||
|
return 'Starting Dockerfile build';
|
||||||
|
|
||||||
|
case 'waiting':
|
||||||
|
return 'Waiting for available build worker.';
|
||||||
|
|
||||||
|
case 'building':
|
||||||
|
return 'Building image from Dockerfile';
|
||||||
|
|
||||||
|
case 'pushing':
|
||||||
|
return 'Pushing image built from Dockerfile';
|
||||||
|
|
||||||
|
case 'complete':
|
||||||
|
return 'Dockerfile build completed and pushed';
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
return 'Dockerfile build failed.';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('buildProgress', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/build-progress.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'build': '=build'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.getPercentage = function(buildInfo) {
|
||||||
switch (buildInfo.phase) {
|
switch (buildInfo.phase) {
|
||||||
case 'building':
|
case 'building':
|
||||||
return (buildInfo.status.current_command / buildInfo.status.total_commands) * 100;
|
return (buildInfo.status.current_command / buildInfo.status.total_commands) * 100;
|
||||||
|
@ -2497,29 +2554,6 @@ quayApp.directive('buildStatus', function () {
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getBuildMessage = function(buildInfo) {
|
|
||||||
switch (buildInfo.phase) {
|
|
||||||
case 'starting':
|
|
||||||
case 'initializing':
|
|
||||||
return 'Starting Dockerfile build';
|
|
||||||
|
|
||||||
case 'waiting':
|
|
||||||
return 'Waiting for available build worker.';
|
|
||||||
|
|
||||||
case 'building':
|
|
||||||
return 'Building image from Dockerfile';
|
|
||||||
|
|
||||||
case 'pushing':
|
|
||||||
return 'Pushing image built from Dockerfile';
|
|
||||||
|
|
||||||
case 'complete':
|
|
||||||
return 'Dockerfile build completed and pushed';
|
|
||||||
|
|
||||||
case 'error':
|
|
||||||
return 'Dockerfile build failed.';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return directiveDefinitionObject;
|
return directiveDefinitionObject;
|
||||||
|
|
|
@ -197,6 +197,11 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
||||||
|
|
||||||
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
|
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
|
||||||
|
|
||||||
|
$scope.showBuild = function(buildInfo) {
|
||||||
|
$location.path('/repository/' + namespace + '/' + name + '/build');
|
||||||
|
$location.search('current', buildInfo.id);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.getTooltipCommand = function(image) {
|
$scope.getTooltipCommand = function(image) {
|
||||||
var sanitized = ImageMetadataService.getEscapedFormattedCommand(image);
|
var sanitized = ImageMetadataService.getEscapedFormattedCommand(image);
|
||||||
return '<span class=\'codetooltip\'>' + sanitized + '</span>';
|
return '<span class=\'codetooltip\'>' + sanitized + '</span>';
|
||||||
|
@ -616,6 +621,69 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
|
||||||
loadViewInfo();
|
loadViewInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope, $location) {
|
||||||
|
var namespace = $routeParams.namespace;
|
||||||
|
var name = $routeParams.name;
|
||||||
|
|
||||||
|
// Watch for changes to the current parameter.
|
||||||
|
$scope.$on('$routeUpdate', function(){
|
||||||
|
if ($location.search().current) {
|
||||||
|
$scope.setCurrentBuild($location.search().current, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.builds = [];
|
||||||
|
|
||||||
|
$scope.getShortId = function(id) {
|
||||||
|
var lastIndex = id.lastIndexOf('-');
|
||||||
|
return id.substr(lastIndex + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setCurrentBuild = function(buildId, opt_updateURL) {
|
||||||
|
// Find the build.
|
||||||
|
for (var i = 0; i < $scope.builds.length; ++i) {
|
||||||
|
if ($scope.builds[i].id == buildId) {
|
||||||
|
$scope.setCurrentBuildInternal($scope.builds[i], opt_updateURL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setCurrentBuildInternal = function(build, opt_updateURL) {
|
||||||
|
$scope.currentBuild = build;
|
||||||
|
if (opt_updateURL) {
|
||||||
|
$location.search('current', build.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchRepository = function() {
|
||||||
|
var params = {'repository': namespace + '/' + name};
|
||||||
|
$rootScope.title = 'Loading Repository...';
|
||||||
|
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
|
||||||
|
$scope.repo = repo;
|
||||||
|
getBuildInfo();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getBuildInfo = function(repo) {
|
||||||
|
// Note: We use restangular manually here because we need to turn off the loading bar.
|
||||||
|
var buildInfo = Restangular.one('repository/' + namespace + '/' + name + '/build/');
|
||||||
|
buildInfo.withHttpConfig({
|
||||||
|
'ignoreLoadingBar': true
|
||||||
|
});
|
||||||
|
|
||||||
|
buildInfo.get().then(function(resp) {
|
||||||
|
$scope.builds = resp.builds;
|
||||||
|
|
||||||
|
if ($location.search().current) {
|
||||||
|
$scope.setCurrentBuild($location.search().current, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchRepository();
|
||||||
|
}
|
||||||
|
|
||||||
function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope) {
|
function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope) {
|
||||||
var namespace = $routeParams.namespace;
|
var namespace = $routeParams.namespace;
|
||||||
var name = $routeParams.name;
|
var name = $routeParams.name;
|
||||||
|
|
44
static/partials/repo-build.html
Normal file
44
static/partials/repo-build.html
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<div class="resource-view" resource="repository" error-message="'No repository found'"></div>
|
||||||
|
<div class="container repo repo-build" ng-show="repo && !deleting">
|
||||||
|
<div class="header row">
|
||||||
|
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||||
|
<h3>
|
||||||
|
<span class="repo-circle no-background" repo="repo"></span>
|
||||||
|
<span class="repo-breadcrumb" repo="repo"></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" ng-show="!repo.is_building">
|
||||||
|
There are no active builds for this repository
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row" ng-show="repo.is_building">
|
||||||
|
<!-- Side tabs -->
|
||||||
|
<div class="col-md-2">
|
||||||
|
<ul class="nav nav-pills nav-stacked">
|
||||||
|
<li ng-class="currentBuild == build ? 'active' : ''" ng-repeat="build in builds">
|
||||||
|
<a href="javascript:void(0)" ng-click="setCurrentBuild(build.id, true)">
|
||||||
|
<span class="phase-icon" ng-class="build.phase"></span>
|
||||||
|
<span>{{ getShortId(build.id) }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="col-md-10">
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div ng-repeat="build in builds" class="tab-pane" ng-class="currentBuild == build ? 'active' : ''">
|
||||||
|
<div>{{ build.id }}</div>
|
||||||
|
<div>
|
||||||
|
<span class="phase-icon" ng-class="build.phase"></span>
|
||||||
|
<span class="build-message" build="build"></span>
|
||||||
|
<div class="build-progress" build="build"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -42,14 +42,14 @@
|
||||||
<div class="dropdown" data-placement="top">
|
<div class="dropdown" data-placement="top">
|
||||||
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '-' }}</span></span>
|
<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>
|
<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">
|
<ul class="dropdown-menu pull-right">
|
||||||
<li ng-repeat="buildInfo in buildsInfo">
|
<li ng-repeat="buildInfo in buildsInfo">
|
||||||
<div class="build-info">
|
<div class="build-info" ng-class="repo.can_write ? 'clickable' : ''" ng-click="showBuild(buildInfo)">
|
||||||
<span class="build-status" build="buildInfo"></span>
|
<span class="build-status" build="buildInfo"></span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Reference in a new issue