- 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:
Joseph Schorr 2014-02-14 22:59:44 -05:00
parent 0ceeb6f8e7
commit 7bf6936154
8 changed files with 439 additions and 204 deletions

View file

@ -846,9 +846,19 @@ i.toggle-icon:hover {
display: none;
}
.visible-md-inline {
display: none;
}
.hidden-sm-inline {
display: inline;
}
}
@media (min-width: 991px) {
.visible-md-inline {
display: inline;
}
}
@media (max-width: 991px) and (min-width: 768px) {
.visible-sm-inline {
@ -1449,7 +1459,10 @@ p.editable:hover i {
}
.repo .header {
margin-bottom: 10px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #eee;
position: relative;
}
@ -1499,36 +1512,12 @@ p.editable:hover i {
display: inline-block;
}
.repo .status-boxes {
float: right;
margin-bottom: 20px;
}
.repo .status-boxes .status-box {
cursor: pointer;
.repo .repo-controls .count {
display: inline-block;
border: 1px solid #eee;
border-radius: 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;
padding-left: 4px;
padding-right: 4px;
font-weight: bold;
color: #428bca;
transform: scaleX(0);
-webkit-transform: scaleX(0);
@ -1539,13 +1528,13 @@ p.editable:hover i {
-moz-transition: -moz-transform 500ms ease-in-out;
}
.repo .status-boxes .status-box .count.visible {
.repo .repo-controls .count.visible {
transform: scaleX(1);
-webkit-transform: scaleX(1);
-moz-transform: scaleX(1);
}
.repo .pull-command {
.repo .repo-controls {
float: right;
display: inline-block;
font-size: 0.8em;
@ -3197,4 +3186,9 @@ pre.command:before {
font-size: 14px;
font-weight: bold;
padding: 4px 8px;
}
.file-drop {
padding: 10px;
margin: 10px;
}

View 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">&times;</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>

View 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>

View file

@ -2700,6 +2700,239 @@ quayApp.directive('buildProgress', function () {
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.
quayApp.directive('ngBlur', function() {
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 () {
return function (scope, element, attr) {
scope.$watch(attr.ngVisible, function (visible) {

View file

@ -341,8 +341,17 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
// Start scope methods //////////////////////////////////////////
$scope.buildDialogShowCounter = 0;
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
$scope.showNewBuildDialog = function() {
$scope.buildDialogShowCounter++;
};
$scope.handleBuildStarted = function(build) {
startBuildInfoTimer($scope.repo);
};
$scope.showBuild = function(buildInfo) {
$location.path('/repository/' + namespace + '/' + name + '/build');
$location.search('current', buildInfo.id);
@ -784,6 +793,17 @@ function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope
$scope.builds = [];
$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() {
$('.build-logs').height($(window).height() - 385);
};
@ -1548,12 +1568,6 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
'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
// accordingly.
$scope.isUserNamespace = true;
@ -1600,15 +1614,36 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
$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() {
$('#repoName').popover('hide');
var uploader = $('#file-drop')[0];
if ($scope.repo.initialize && uploader.files.length < 1) {
$('#missingfileModal').modal();
return;
}
$scope.creating = true;
var repo = $scope.repo;
var data = {
@ -1624,7 +1659,7 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
// Repository created. Start the upload process if applicable.
if ($scope.repo.initialize) {
startFileUpload(created);
$scope.createdForBuild = created;
return;
}
@ -1653,74 +1688,6 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService
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) {
$scope.planChanging = false;

View file

@ -12,16 +12,7 @@
<div class="quay-spinner"></div>
</div>
<div class="container" ng-show="!user.anonymous && 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 new-repo" ng-show="!user.anonymous && !creating && !uploading && !building">
<div class="container new-repo" ng-show="!user.anonymous && !creating && !building">
<form method="post" name="newRepoForm" id="newRepoForm" ng-submit="createNewRepo()">
<!-- Header -->
@ -109,11 +100,9 @@
</div>
<div class="initialize-repo" ng-show="repo.initialize">
<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">
<div class="dockerfile-build-form" repository="createdForBuild" upload-failed="handleBuildFailed(message)"
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
has-dockerfile="hasDockerfile" uploading="uploading" building="building"></div>
</div>
</div>
</div>
@ -123,7 +112,7 @@
<div class="col-md-1"></div>
<div class="col-md-8">
<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
</button>
</div>
@ -157,26 +146,6 @@
</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">&times;</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 -->
<div class="modal fade" id="cannotcreateModal">
<div class="modal-dialog">
@ -195,26 +164,6 @@
</div><!-- /.modal-dialog -->
</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">&times;</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 -->
<div class="modal fade" id="couldnotsubscribeModal">
<div class="modal-dialog">

View file

@ -9,6 +9,13 @@
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="'Build History'"></span>
</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 class="row" ng-show="!builds.length">
@ -74,7 +81,8 @@
</div>
<div style="margin-top: 10px">
<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)">
<i class="fa fa-refresh"></i>
Run Build Again
@ -86,6 +94,10 @@
</div>
</div>
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
build-started="handleBuildStarted(build)">
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="confirmRestartBuildModal">
<div class="modal-dialog">

View file

@ -5,51 +5,65 @@
</div>
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
<div class="container repo">
<div class="container repo repo-view">
<!-- Repo Header -->
<div class="header">
<h3>
<span class="repo-circle" 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>
<span class="repo-breadcrumb" repo="repo"></span>
</h3>
<!-- Pull command -->
<div class="pull-command visible-md visible-lg" style="display: none;">
<span class="pull-command-title">Pull repository:</span>
<div class="pull-container">
<div class="input-group">
<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">
<i class="fa fa-copy"></i>
</span>
</div>
</div>
<div id="clipboardCopied" class="hovering" style="display: none">
Copied to clipboard
</div>
</div>
</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">
<div class="repo-controls">
<!-- Builds -->
<div class="dropdown" data-placement="top" style="display: inline-block"
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="showBuild(buildInfo)">
<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">
<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">
<i class="fa fa-copy"></i>
</span>
</div>
</div>
<div id="clipboardCopied" class="hovering" style="display: none">
Copied to clipboard
</div>
</span>
</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="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>
@ -235,6 +249,10 @@ sudo docker push quay.io/{{repo.namespace}}/{{repo.name}}</pre>
</div>
</div>
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
build-started="handleBuildStarted(build)">
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="confirmdeleteTagModal">
<div class="modal-dialog">