diff --git a/static/css/quay.css b/static/css/quay.css
index 4653d5a42..fda081a88 100644
--- a/static/css/quay.css
+++ b/static/css/quay.css
@@ -524,7 +524,7 @@ i.toggle-icon:hover {
border-bottom: 1px solid #eee;
}
-.build-info:hover {
+.build-info.clickable:hover {
background: rgba(66, 139, 202, 0.2);
cursor: pointer;
border-radius: 4px;
@@ -535,33 +535,36 @@ i.toggle-icon:hover {
border-bottom: 0px;
}
-.build-status .phase-icon {
+.phase-icon {
border-radius: 50%;
display: inline-block;
width: 12px;
height: 12px;
margin-right: 6px;
+}
+
+.build-status .phase-icon {
margin-top: 4px;
float: left;
}
-.build-status .phase-icon.error {
+.phase-icon.error {
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;
}
-.build-status .phase-icon.building {
+.phase-icon.building {
background-color: #f0ad4e;
}
-.build-status .phase-icon.pushing {
+.phase-icon.pushing {
background-color: #5cb85c;
}
-.build-status .phase-icon.complete {
+.phase-icon.complete {
background-color: #428bca;
}
diff --git a/static/directives/build-message.html b/static/directives/build-message.html
new file mode 100644
index 000000000..af3edd285
--- /dev/null
+++ b/static/directives/build-message.html
@@ -0,0 +1 @@
+{{ getBuildMessage(build) }}
diff --git a/static/directives/build-progress.html b/static/directives/build-progress.html
new file mode 100644
index 000000000..bc3dd0575
--- /dev/null
+++ b/static/directives/build-progress.html
@@ -0,0 +1,6 @@
+
diff --git a/static/directives/build-status.html b/static/directives/build-status.html
index 8de830020..9b0226e80 100644
--- a/static/directives/build-status.html
+++ b/static/directives/build-status.html
@@ -1,14 +1,11 @@
- {{ getBuildMessage(build) }}
+
Started:
-
+
diff --git a/static/js/app.js b/static/js/app.js
index e83c938fa..b27997bc3 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -774,6 +774,7 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
fixFooter: 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/build', {templateUrl: '/static/partials/repo-build.html', controller:RepoBuildCtrl, reloadOnSearch: false}).
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
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',
@@ -2474,7 +2475,63 @@ quayApp.directive('buildStatus', function () {
'build': '=build'
},
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) {
case 'building':
return (buildInfo.status.current_command / buildInfo.status.total_commands) * 100;
@@ -2497,29 +2554,6 @@ quayApp.directive('buildStatus', function () {
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;
diff --git a/static/js/controllers.js b/static/js/controllers.js
index fc79963d1..7f04e0064 100644
--- a/static/js/controllers.js
+++ b/static/js/controllers.js
@@ -197,6 +197,11 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
+ $scope.showBuild = function(buildInfo) {
+ $location.path('/repository/' + namespace + '/' + name + '/build');
+ $location.search('current', buildInfo.id);
+ };
+
$scope.getTooltipCommand = function(image) {
var sanitized = ImageMetadataService.getEscapedFormattedCommand(image);
return '' + sanitized + '';
@@ -616,6 +621,69 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
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) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
diff --git a/static/partials/repo-build.html b/static/partials/repo-build.html
new file mode 100644
index 000000000..6d07af3c8
--- /dev/null
+++ b/static/partials/repo-build.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+ There are no active builds for this repository
+
+
+
+
+
diff --git a/static/partials/view-repo.html b/static/partials/view-repo.html
index b890d197b..3b888c141 100644
--- a/static/partials/view-repo.html
+++ b/static/partials/view-repo.html
@@ -42,14 +42,14 @@