Merge branch 'tagview'
This commit is contained in:
commit
3c305beeea
160 changed files with 151133 additions and 497 deletions
|
@ -401,6 +401,7 @@ class ImageStorage(BaseModel):
|
||||||
command = TextField(null=True)
|
command = TextField(null=True)
|
||||||
image_size = BigIntegerField(null=True)
|
image_size = BigIntegerField(null=True)
|
||||||
uncompressed_size = BigIntegerField(null=True)
|
uncompressed_size = BigIntegerField(null=True)
|
||||||
|
aggregate_size = BigIntegerField(null=True)
|
||||||
uploading = BooleanField(default=True, null=True)
|
uploading = BooleanField(default=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1450,6 +1450,18 @@ def set_image_size(docker_image_id, namespace_name, repository_name, image_size,
|
||||||
|
|
||||||
image.storage.image_size = image_size
|
image.storage.image_size = image_size
|
||||||
image.storage.uncompressed_size = uncompressed_size
|
image.storage.uncompressed_size = uncompressed_size
|
||||||
|
|
||||||
|
ancestors = image.ancestors.split('/')[1:-1]
|
||||||
|
if ancestors:
|
||||||
|
try:
|
||||||
|
parent_image = Image.get(Image.id == ancestors[-1])
|
||||||
|
total_size = image_size + parent_image.storage.aggregate_size
|
||||||
|
image.storage.aggregate_size = total_size
|
||||||
|
except Image.DoesNotExist:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
image.storage.aggregate_size = image_size
|
||||||
|
|
||||||
image.storage.save()
|
image.storage.save()
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
@ -2072,11 +2084,14 @@ def get_repository_build(build_uuid):
|
||||||
|
|
||||||
|
|
||||||
def list_repository_builds(namespace_name, repository_name, limit,
|
def list_repository_builds(namespace_name, repository_name, limit,
|
||||||
include_inactive=True):
|
include_inactive=True, since=None):
|
||||||
query = (_get_build_base_query()
|
query = (_get_build_base_query()
|
||||||
.where(Repository.name == repository_name, Namespace.username == namespace_name)
|
.where(Repository.name == repository_name, Namespace.username == namespace_name)
|
||||||
.limit(limit))
|
.limit(limit))
|
||||||
|
|
||||||
|
if since is not None:
|
||||||
|
query = query.where(RepositoryBuild.started >= since)
|
||||||
|
|
||||||
if not include_inactive:
|
if not include_inactive:
|
||||||
query = query.where(RepositoryBuild.phase != 'error',
|
query = query.where(RepositoryBuild.phase != 'error',
|
||||||
RepositoryBuild.phase != 'complete')
|
RepositoryBuild.phase != 'complete')
|
||||||
|
@ -2209,8 +2224,8 @@ def log_action(kind_name, user_or_organization_name, performer=None,
|
||||||
kind = LogEntryKind.get(LogEntryKind.name == kind_name)
|
kind = LogEntryKind.get(LogEntryKind.name == kind_name)
|
||||||
account = User.get(User.username == user_or_organization_name)
|
account = User.get(User.username == user_or_organization_name)
|
||||||
LogEntry.create(kind=kind, account=account, performer=performer,
|
LogEntry.create(kind=kind, account=account, performer=performer,
|
||||||
repository=repository, ip=ip,
|
repository=repository, ip=ip, metadata_json=json.dumps(metadata),
|
||||||
metadata_json=json.dumps(metadata), datetime=timestamp)
|
datetime=timestamp)
|
||||||
|
|
||||||
|
|
||||||
def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None):
|
def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None):
|
||||||
|
@ -2525,6 +2540,26 @@ def cancel_repository_build(build, work_queue):
|
||||||
build.delete_instance()
|
build.delete_instance()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_repository_pushes(repository, time_delta):
|
||||||
|
since = date.today() - time_delta
|
||||||
|
push_repo = LogEntryKind.get(name = 'push_repo')
|
||||||
|
return (LogEntry.select()
|
||||||
|
.where(LogEntry.repository == repository)
|
||||||
|
.where(LogEntry.kind == push_repo)
|
||||||
|
.where(LogEntry.datetime >= since)
|
||||||
|
.count())
|
||||||
|
|
||||||
|
def get_repository_pulls(repository, time_delta):
|
||||||
|
since = date.today() - time_delta
|
||||||
|
repo_pull = LogEntryKind.get(name = 'pull_repo')
|
||||||
|
repo_verb = LogEntryKind.get(name = 'repo_verb')
|
||||||
|
return (LogEntry.select()
|
||||||
|
.where(LogEntry.repository == repository)
|
||||||
|
.where((LogEntry.kind == repo_pull) | (LogEntry.kind == repo_verb))
|
||||||
|
.where(LogEntry.datetime >= since)
|
||||||
|
.count())
|
||||||
|
|
||||||
|
|
||||||
def get_repository_usage():
|
def get_repository_usage():
|
||||||
one_month_ago = date.today() - timedelta(weeks=4)
|
one_month_ago = date.today() - timedelta(weeks=4)
|
||||||
repo_pull = LogEntryKind.get(name = 'pull_repo')
|
repo_pull = LogEntryKind.get(name = 'pull_repo')
|
||||||
|
|
|
@ -148,12 +148,17 @@ class RepositoryBuildList(RepositoryParamResource):
|
||||||
@require_repo_read
|
@require_repo_read
|
||||||
@parse_args
|
@parse_args
|
||||||
@query_param('limit', 'The maximum number of builds to return', type=int, default=5)
|
@query_param('limit', 'The maximum number of builds to return', type=int, default=5)
|
||||||
|
@query_param('since', 'Returns all builds since the given unix timecode', type=int, default=None)
|
||||||
@nickname('getRepoBuilds')
|
@nickname('getRepoBuilds')
|
||||||
def get(self, args, namespace, repository):
|
def get(self, args, namespace, repository):
|
||||||
""" Get the list of repository builds. """
|
""" Get the list of repository builds. """
|
||||||
limit = args['limit']
|
limit = args.get('limit', 5)
|
||||||
builds = list(model.list_repository_builds(namespace, repository, limit))
|
since = args.get('since', None)
|
||||||
|
|
||||||
|
if since is not None:
|
||||||
|
since = datetime.datetime.utcfromtimestamp(since)
|
||||||
|
|
||||||
|
builds = model.list_repository_builds(namespace, repository, limit, since=since)
|
||||||
can_write = ModifyRepositoryPermission(namespace, repository).can()
|
can_write = ModifyRepositoryPermission(namespace, repository).can()
|
||||||
return {
|
return {
|
||||||
'builds': [build_status_view(build, can_write) for build in builds]
|
'builds': [build_status_view(build, can_write) for build in builds]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import datetime
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
|
@ -177,7 +179,9 @@ class Repository(RepositoryParamResource):
|
||||||
def tag_view(tag):
|
def tag_view(tag):
|
||||||
return {
|
return {
|
||||||
'name': tag.name,
|
'name': tag.name,
|
||||||
'image_id': tag.image.docker_image_id
|
'image_id': tag.image.docker_image_id,
|
||||||
|
'last_modified': format_date(datetime.datetime.fromtimestamp(tag.lifetime_start_ts)),
|
||||||
|
'size': tag.image.storage.aggregate_size
|
||||||
}
|
}
|
||||||
|
|
||||||
organization = None
|
organization = None
|
||||||
|
@ -196,6 +200,9 @@ class Repository(RepositoryParamResource):
|
||||||
active_builds = model.list_repository_builds(namespace, repository, 1,
|
active_builds = model.list_repository_builds(namespace, repository, 1,
|
||||||
include_inactive=False)
|
include_inactive=False)
|
||||||
|
|
||||||
|
is_starred = (model.repository_is_starred(get_authenticated_user(), repo)
|
||||||
|
if get_authenticated_user() else False)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'namespace': namespace,
|
'namespace': namespace,
|
||||||
'name': repository,
|
'name': repository,
|
||||||
|
@ -206,7 +213,18 @@ class Repository(RepositoryParamResource):
|
||||||
'is_public': is_public,
|
'is_public': is_public,
|
||||||
'is_building': len(list(active_builds)) > 0,
|
'is_building': len(list(active_builds)) > 0,
|
||||||
'is_organization': bool(organization),
|
'is_organization': bool(organization),
|
||||||
'status_token': repo.badge_token if not is_public else ''
|
'is_starred': is_starred,
|
||||||
|
'status_token': repo.badge_token if not is_public else '',
|
||||||
|
'stats': {
|
||||||
|
'pulls': {
|
||||||
|
'today': model.get_repository_pulls(repo, timedelta(days=1)),
|
||||||
|
'thirty_day': model.get_repository_pulls(repo, timedelta(days=30))
|
||||||
|
},
|
||||||
|
'pushes': {
|
||||||
|
'today': model.get_repository_pushes(repo, timedelta(days=1)),
|
||||||
|
'thirty_day': model.get_repository_pushes(repo, timedelta(days=30))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
|
|
|
@ -247,6 +247,7 @@ def github_oauth_attach():
|
||||||
|
|
||||||
|
|
||||||
@callback.route('/github/callback/trigger/<path:repository>', methods=['GET'])
|
@callback.route('/github/callback/trigger/<path:repository>', methods=['GET'])
|
||||||
|
@callback.route('/github/callback/trigger/<path:repository>/__new', methods=['GET'])
|
||||||
@route_show_if(features.GITHUB_BUILD)
|
@route_show_if(features.GITHUB_BUILD)
|
||||||
@require_session_login
|
@require_session_login
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -260,9 +261,18 @@ def attach_github_build_trigger(namespace, repository):
|
||||||
abort(404, message=msg)
|
abort(404, message=msg)
|
||||||
|
|
||||||
trigger = model.create_build_trigger(repo, 'github', token, current_user.db_user())
|
trigger = model.create_build_trigger(repo, 'github', token, current_user.db_user())
|
||||||
|
|
||||||
|
# TODO(jschorr): Remove once the new layout is in place.
|
||||||
admin_path = '%s/%s/%s' % (namespace, repository, 'admin')
|
admin_path = '%s/%s/%s' % (namespace, repository, 'admin')
|
||||||
full_url = '%s%s%s' % (url_for('web.repository', path=admin_path), '?tab=trigger&new_trigger=',
|
full_url = '%s%s%s' % (url_for('web.repository', path=admin_path), '?tab=trigger&new_trigger=',
|
||||||
trigger.uuid)
|
trigger.uuid)
|
||||||
|
|
||||||
|
if '__new' in request.url:
|
||||||
|
repo_path = '%s/%s' % (namespace, repository)
|
||||||
|
full_url = '%s%s%s' % (url_for('web.repository', path=repo_path), '?tab=builds&newtrigger=',
|
||||||
|
trigger.uuid)
|
||||||
|
|
||||||
|
|
||||||
logger.debug('Redirecting to full url: %s' % full_url)
|
logger.debug('Redirecting to full url: %s' % full_url)
|
||||||
return redirect(full_url)
|
return redirect(full_url)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ from app import app, oauth_apps, dockerfile_build_queue, LoginWrappedDBUser
|
||||||
|
|
||||||
from auth.permissions import QuayDeferredPermissionUser
|
from auth.permissions import QuayDeferredPermissionUser
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
|
from auth.auth_context import get_authenticated_user
|
||||||
from endpoints.api.discovery import swagger_route_data
|
from endpoints.api.discovery import swagger_route_data
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -225,7 +226,8 @@ def start_build(repository, dockerfile_id, tags, build_name, subdir, manual,
|
||||||
'registry': host,
|
'registry': host,
|
||||||
'build_subdir': subdir,
|
'build_subdir': subdir,
|
||||||
'trigger_metadata': trigger_metadata or {},
|
'trigger_metadata': trigger_metadata or {},
|
||||||
'is_manual': manual
|
'is_manual': manual,
|
||||||
|
'manual_user': get_authenticated_user().username if get_authenticated_user() else None
|
||||||
}
|
}
|
||||||
|
|
||||||
with app.config['DB_TRANSACTION_FACTORY'](db):
|
with app.config['DB_TRANSACTION_FACTORY'](db):
|
||||||
|
@ -250,7 +252,8 @@ def start_build(repository, dockerfile_id, tags, build_name, subdir, manual,
|
||||||
'repo': repository.name,
|
'repo': repository.name,
|
||||||
'namespace': repository.namespace_user.username,
|
'namespace': repository.namespace_user.username,
|
||||||
'fileid': dockerfile_id,
|
'fileid': dockerfile_id,
|
||||||
'manual': manual,
|
'is_manual': manual,
|
||||||
|
'manual_user': get_authenticated_user().username if get_authenticated_user() else None
|
||||||
}
|
}
|
||||||
|
|
||||||
if trigger:
|
if trigger:
|
||||||
|
@ -267,7 +270,8 @@ def start_build(repository, dockerfile_id, tags, build_name, subdir, manual,
|
||||||
'build_id': build_request.uuid,
|
'build_id': build_request.uuid,
|
||||||
'build_name': build_name,
|
'build_name': build_name,
|
||||||
'docker_tags': tags,
|
'docker_tags': tags,
|
||||||
'is_manual': manual
|
'is_manual': manual,
|
||||||
|
'manual_user': get_authenticated_user().username if get_authenticated_user() else None
|
||||||
}
|
}
|
||||||
|
|
||||||
if trigger:
|
if trigger:
|
||||||
|
|
10
initdb.py
10
initdb.py
|
@ -75,7 +75,7 @@ def __create_subtree(repo, structure, creator_username, parent):
|
||||||
new_image.storage.checksum = checksum
|
new_image.storage.checksum = checksum
|
||||||
new_image.storage.save()
|
new_image.storage.save()
|
||||||
|
|
||||||
creation_time = REFERENCE_DATE + timedelta(days=image_num)
|
creation_time = REFERENCE_DATE + timedelta(weeks=image_num) + timedelta(days=i)
|
||||||
command_list = SAMPLE_CMDS[image_num % len(SAMPLE_CMDS)]
|
command_list = SAMPLE_CMDS[image_num % len(SAMPLE_CMDS)]
|
||||||
command = json.dumps(command_list) if command_list else None
|
command = json.dumps(command_list) if command_list else None
|
||||||
new_image = model.set_image_metadata(docker_image_id, repo.namespace_user.username, repo.name,
|
new_image = model.set_image_metadata(docker_image_id, repo.namespace_user.username, repo.name,
|
||||||
|
@ -364,6 +364,14 @@ def populate_database():
|
||||||
__generate_repository(new_user_1, 'simple', 'Simple repository.', False,
|
__generate_repository(new_user_1, 'simple', 'Simple repository.', False,
|
||||||
[], (4, [], ['latest', 'prod']))
|
[], (4, [], ['latest', 'prod']))
|
||||||
|
|
||||||
|
__generate_repository(new_user_1, 'sharedtags',
|
||||||
|
'Shared tags repository',
|
||||||
|
False, [(new_user_2, 'read'), (dtrobot[0], 'read')],
|
||||||
|
(2, [(3, [], ['v2.0', 'v2.1', 'v2.2']),
|
||||||
|
(1, [(1, [(1, [], ['prod', '581a284'])],
|
||||||
|
['staging', '8423b58']),
|
||||||
|
(1, [], None)], None)], None))
|
||||||
|
|
||||||
__generate_repository(new_user_1, 'history', 'Historical repository.', False,
|
__generate_repository(new_user_1, 'history', 'Historical repository.', False,
|
||||||
[], (4, [(2, [], 'latest'), (3, [], '#latest')], None))
|
[], (4, [(2, [], 'latest'), (3, [], '#latest')], None))
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
float: none;
|
float: none;
|
||||||
padding: 20px;
|
padding: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.co-tabs li {
|
.co-tabs li {
|
||||||
|
@ -190,7 +190,7 @@
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.co-panel .co-panel-heading i.fa {
|
.co-panel .co-panel-heading > i.fa {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -754,3 +754,159 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-family: FontAwesome;
|
font-family: FontAwesome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.co-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table td {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td a {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td:after {
|
||||||
|
content: "\f175";
|
||||||
|
font-family: FontAwesome;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 10px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td.current:after {
|
||||||
|
content: "\f175";
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td.current.reversed:after {
|
||||||
|
content: "\f176";
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table thead td.current a {
|
||||||
|
color: #337ab7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table .checkbox-col {
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table td.options-col {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table .add-row td {
|
||||||
|
border-top: 2px solid #eee;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cor-checkable-menu {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-menu .co-checkable-menu-state {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -1px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-menu .dropdown {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-item, .co-checkable-menu-state {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
position: relative
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-item:after, .co-checkable-menu-state:after {
|
||||||
|
content: "\f00c";
|
||||||
|
font-family: FontAwesome;
|
||||||
|
color: #ccc;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: 1px;
|
||||||
|
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-menu-state.some:after {
|
||||||
|
content: "-";
|
||||||
|
font-size: 35px;
|
||||||
|
top: -19px;
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-item:hover:after {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-checkable-item.checked:after, .co-checkable-menu-state.all:after, .co-checkable-menu-state.some:after {
|
||||||
|
visibility: visible;
|
||||||
|
color: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-table .co-checkable-row.checked {
|
||||||
|
background: #F6FCFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar .co-checked-actions {
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
margin-left: 10px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar .co-checked-actions .btn {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar .co-checked-actions .btn .fa {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar .co-filter-box {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.co-check-bar .co-filter-box input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-primary-msg {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-secondary-msg {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
12
static/css/directives/repo-view/repo-panel-builds.css
Normal file
12
static/css/directives/repo-view/repo-panel-builds.css
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.repo-panel-builds .status-col {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-builds .building-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-builds .building-tag .fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
28
static/css/directives/repo-view/repo-panel-changes.css
Normal file
28
static/css/directives/repo-view/repo-panel-changes.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.repo-panel-changes .tab-header {
|
||||||
|
margin-bottom: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-changes .side-panel-title {
|
||||||
|
font-size: 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-changes .side-panel-title i.fa {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-changes .side-panel {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-changes .visualized-tag {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-changes .visualized-tag .fa {
|
||||||
|
margin-right: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
63
static/css/directives/repo-view/repo-panel-info.css
Normal file
63
static/css/directives/repo-view/repo-panel-info.css
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
.repo-panel-info-element .stat-col {
|
||||||
|
border-right: 2px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .stat-title {
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .stat {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .stat .stat-value {
|
||||||
|
font-size: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .stat .stat-subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ccc;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .description-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
border-top: 2px solid #eee;
|
||||||
|
padding-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .description {
|
||||||
|
padding-bottom: 0px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .description p {
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .description-container h4:before {
|
||||||
|
content: "\f02d";
|
||||||
|
color: #black;
|
||||||
|
font-size: 20px;
|
||||||
|
font-family: FontAwesome;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .description .fa-edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .build-mini-status {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-info-element .view-all {
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
25
static/css/directives/repo-view/repo-panel-settings.css
Normal file
25
static/css/directives/repo-view/repo-panel-settings.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.repo-panel-settings-element .panel-section {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-settings-element .lock-section {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-settings-element .lock-section .lock-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 22px;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-settings-element .panel-section .btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-settings-element .panel-section .btn .fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
65
static/css/directives/repo-view/repo-panel-tags.css
Normal file
65
static/css/directives/repo-view/repo-panel-tags.css
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
.repo-panel-tags-element .fa-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .empty {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track {
|
||||||
|
width: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track-dot {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: 2px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
z-index: 300;
|
||||||
|
|
||||||
|
border: 2px solid black;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track-line {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
bottom: -1px;
|
||||||
|
left: 7px;
|
||||||
|
width: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
border-left: 2px solid black;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track-line.start {
|
||||||
|
top: 18px;
|
||||||
|
height: 24px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track-line.middle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-track-line.end {
|
||||||
|
top: -1px;
|
||||||
|
height: 16px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .image-id-col {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .options-col {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
|
@ -32,3 +32,7 @@
|
||||||
.build-info-bar-element .build-side-info .timing {
|
.build-info-bar-element .build-side-info .timing {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.build-info-bar-element .source-commit-link {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
33
static/css/directives/ui/build-mini-status.css
Normal file
33
static/css/directives/ui/build-mini-status.css
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
.build-mini-status {
|
||||||
|
display: block;
|
||||||
|
padding: 4px;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-mini-status-element {
|
||||||
|
color: black;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-mini-status .timing {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-mini-status .timing .fa {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-mini-status .build-description {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
left: 250px;
|
||||||
|
bottom: 4px;
|
||||||
|
line-height: 33px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
19
static/css/directives/ui/build-state-icon.css
Normal file
19
static/css/directives/ui/build-state-icon.css
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.build-state-icon {
|
||||||
|
width: 42px;
|
||||||
|
padding: 4px;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-state-icon .error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-state-icon .internalerror {
|
||||||
|
color: #DFFF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-state-icon .complete {
|
||||||
|
color: #2fcc66;
|
||||||
|
}
|
15
static/css/directives/ui/filter-control.css
Normal file
15
static/css/directives/ui/filter-control.css
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.filter-control {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control .not-selected a {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control .selected {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
47
static/css/directives/ui/image-changes-view.css
Normal file
47
static/css/directives/ui/image-changes-view.css
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
.image-changes-view .change-count {
|
||||||
|
font-size: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .change-count b {
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .changes-container .well {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .changes-container i.fa-plus-square {
|
||||||
|
color: rgb(73, 209, 73);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .changes-container i.fa-minus-square {
|
||||||
|
color: rgb(209, 73, 73);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .changes-container i.fa-pencil-square {
|
||||||
|
color: rgb(73, 100, 209);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .change-count:last-child {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .change-count i {
|
||||||
|
font-size: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .change i {
|
||||||
|
float: right;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-changes-view .more-changes {
|
||||||
|
padding: 6px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
68
static/css/directives/ui/image-info-sidebar.css
Normal file
68
static/css/directives/ui/image-info-sidebar.css
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
.image-info-sidebar .image-comment {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .tag {
|
||||||
|
margin: 2px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .section-icon {
|
||||||
|
float: left;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-right: 14px;
|
||||||
|
color: #bbb;
|
||||||
|
width: 18px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .section-info {
|
||||||
|
padding: 4px;
|
||||||
|
padding-left: 6px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.05);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0,0,0,0.05);
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
|
vertical-align: middle;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .section-info-with-dropdown {
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .dropdown {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
bottom: 2px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info-sidebar .image-section .dropdown-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
padding: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
|
@ -30,19 +30,6 @@
|
||||||
right: 2px;
|
right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-primary-msg {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-secondary-msg {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #999;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-list-title {
|
.repo-list-title {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
@ -86,7 +73,7 @@
|
||||||
background: linear-gradient(to bottom, rgba(255,240,188,1) 0%,rgba(255,255,255,0.5) 5%,rgba(255,255,255,0.49) 51%,rgba(255,255,255,0) 100%); /* W3C */
|
background: linear-gradient(to bottom, rgba(255,240,188,1) 0%,rgba(255,255,255,0.5) 5%,rgba(255,255,255,0.49) 51%,rgba(255,255,255,0) 100%); /* W3C */
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-icon {
|
.repo-list-grid .star-icon {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
@ -94,15 +81,6 @@
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-icon:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
cursor: hand;
|
|
||||||
}
|
|
||||||
|
|
||||||
.star-icon.starred {
|
|
||||||
color: #ffba6d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-repo-listing {
|
.new-repo-listing {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
8
static/css/directives/ui/repo-star.css
Normal file
8
static/css/directives/ui/repo-star.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.repo-star .star-icon:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-star .star-icon.starred {
|
||||||
|
color: #ffba6d;
|
||||||
|
}
|
3
static/css/directives/ui/repository-events-table.css
Normal file
3
static/css/directives/ui/repository-events-table.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.repository-events-table-element .notification-row i.fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
5
static/css/directives/ui/tag-info-sidebar.css
Normal file
5
static/css/directives/ui/tag-info-sidebar.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.tag-info-sidebar .control-bar {
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
|
@ -42,3 +42,11 @@
|
||||||
.triggered-build-description-element .commit-who:before {
|
.triggered-build-description-element .commit-who:before {
|
||||||
content: "by ";
|
content: "by ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.triggered-build-description-element .manual {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triggered-build-description-element .fa-user {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
41
static/css/pages/repo-view.css
Normal file
41
static/css/pages/repo-view.css
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
.repository-view .repo-circle {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .cor-title-content .repo-star {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .repo-star {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .tab-header {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .tab-header-controls {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .tab-header-controls .btn .fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .heading-title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .heading-controls {
|
||||||
|
font-size: 14px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .heading-controls .btn {
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-view .heading-controls .btn .fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
|
@ -324,6 +324,10 @@ nav.navbar-default .navbar-nav>li>a.active {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resource-view.element .resource-content.hidden .tab-content > .active {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.resource-view-element .resource-content.visible {
|
.resource-view-element .resource-content.visible {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<!-- Side information -->
|
<!-- Side information -->
|
||||||
<div class="build-side-info">
|
<div class="build-side-info">
|
||||||
<!-- Build ID -->
|
<!-- Build ID -->
|
||||||
<div class="build-side-id">{{ build.id }}</div>
|
<div class="build-side-id" ng-if="!hideId">{{ build.id }}</div>
|
||||||
|
|
||||||
<!-- Timing -->
|
<!-- Timing -->
|
||||||
<div class="timing" ng-if="showTime">
|
<div class="timing" ng-if="showTime">
|
||||||
|
|
10
static/directives/build-mini-status.html
Normal file
10
static/directives/build-mini-status.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<a class="build-mini-status-element" href="/repository/{{ build.repository.namespace }}/{{ build.repository.name }}/build/{{ build.id }}">
|
||||||
|
<div>
|
||||||
|
<span class="build-state-icon" build="build"></span>
|
||||||
|
<span class="timing">
|
||||||
|
<i class="fa fa-clock-o"></i><span am-time-ago="build.started || 0"></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="build-description triggered-build-description" build="build"></div>
|
||||||
|
</div>
|
||||||
|
</a>
|
8
static/directives/build-state-icon.html
Normal file
8
static/directives/build-state-icon.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<span class="build-state-icon-element" ng-class="build.phase">
|
||||||
|
<span class="cor-loader-inline" ng-if="isBuilding(build)"></span>
|
||||||
|
<span ng-if="!isBuilding(build)">
|
||||||
|
<i class="fa fa-check-circle" ng-if="build.phase == 'complete'"></i>
|
||||||
|
<i class="fa fa-times-circle" ng-if="build.phase == 'error'"></i>
|
||||||
|
<i class="fa fa-exclamation-circle" ng-if="build.phase == 'internalerror'"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!-- NOTE: DEPRECATED -->
|
||||||
<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>
|
||||||
|
|
3
static/directives/cor-checkable-item.html
Normal file
3
static/directives/cor-checkable-item.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<span class="co-checkable-item" ng-click="toggleItem()"
|
||||||
|
ng-class="controller.isChecked(item, controller.checked) ? 'checked': 'not-checked'">
|
||||||
|
</span>
|
1
static/directives/cor-checkable-menu-item.html
Normal file
1
static/directives/cor-checkable-menu-item.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<li><a href="javascript:void(0)" ng-click="selected()"><span ng-transclude/></a></li>
|
12
static/directives/cor-checkable-menu.html
Normal file
12
static/directives/cor-checkable-menu.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<span class="co-checkable-menu">
|
||||||
|
<span class="dropdown" style="text-align: left;">
|
||||||
|
<span class="btn btn-default" data-toggle="dropdown">
|
||||||
|
<span class="co-checkable-menu-state"
|
||||||
|
ng-class="getClass(controller.items, controller.checked)"
|
||||||
|
ng-click="toggleItems($event)">
|
||||||
|
</span>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu" ng-transclude></ul>
|
||||||
|
</span>
|
||||||
|
</span>
|
25
static/directives/cor-confirm-dialog.html
Normal file
25
static/directives/cor-confirm-dialog.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="cor-confirm-dialog-element">
|
||||||
|
<div class="modal fade co-dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" ng-show="!working"
|
||||||
|
data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">{{ dialogTitle }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" ng-show="working">
|
||||||
|
<div class="cor-loader"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" ng-show="!working">
|
||||||
|
<span ng-transclude/>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" ng-show="!working">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="performAction()">
|
||||||
|
{{ dialogActionTitle }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
</div>
|
|
@ -1,3 +1,3 @@
|
||||||
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-6">
|
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-1">
|
||||||
<span class="co-nav-title-action co-fx-text-shadow" ng-transclude></span>
|
<span class="co-nav-title-action co-fx-text-shadow" ng-transclude></span>
|
||||||
</div>
|
</div>
|
|
@ -1 +1 @@
|
||||||
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-6" ng-transclude></div>
|
<div class="col-lg-3 col-md-3 col-sm-3 hidden-xs" ng-transclude></div>
|
3
static/directives/filter-control.html
Normal file
3
static/directives/filter-control.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<span class="filter-control-element" ng-class="filter == value ? 'selected': 'not-selected'">
|
||||||
|
<a href="javascript:void(0)" ng-click="setFilter()"><span ng-transclude/></a>
|
||||||
|
</span>
|
33
static/directives/image-changes-view.html
Normal file
33
static/directives/image-changes-view.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<div class="image-changes-view-element">
|
||||||
|
<div class="resource-view" resource="imageChangesResource">
|
||||||
|
<div class="changes-container" ng-show="hasChanges">
|
||||||
|
<span class="change-count added"
|
||||||
|
ng-show="changeData.added.length > 0"
|
||||||
|
data-title="Files Added"
|
||||||
|
data-placement="top"
|
||||||
|
data-container="body"
|
||||||
|
bs-tooltip>
|
||||||
|
<i class="fa fa-plus-square"></i>
|
||||||
|
<b>{{ changeData.added.length }}</b>
|
||||||
|
</span>
|
||||||
|
<span class="change-count removed"
|
||||||
|
ng-show="changeData.removed.length > 0"
|
||||||
|
data-title="Files Removed"
|
||||||
|
data-placement="top"
|
||||||
|
data-container="body"
|
||||||
|
bs-tooltip>
|
||||||
|
<i class="fa fa-minus-square"></i>
|
||||||
|
<b>{{ changeData.removed.length }}</b>
|
||||||
|
</span>
|
||||||
|
<span class="change-count changed"
|
||||||
|
ng-show="changeData.changed.length > 0"
|
||||||
|
data-title="Files Changes"
|
||||||
|
data-placement="top"
|
||||||
|
data-container="body"
|
||||||
|
bs-tooltip>
|
||||||
|
<i class="fa fa-pencil-square"></i>
|
||||||
|
<b>{{ changeData.changed.length }}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
105
static/directives/image-info-sidebar.html
Normal file
105
static/directives/image-info-sidebar.html
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<div class="image-info-sidebar-element">
|
||||||
|
<!-- Comment -->
|
||||||
|
<div class="image-comment" ng-if="imageData.comment">
|
||||||
|
<blockquote style="margin-top: 10px;">
|
||||||
|
<span class="markdown-view" content="imageData.comment"></span>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image ID -->
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-code section-icon" bs-tooltip="tooltip.title" data-title="Full Image ID"></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<a class="image-link" ng-href="{{ tracker.imageLink(image) }}">
|
||||||
|
{{ imageData.id }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tags -->
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-tag section-icon" data-title="Current Tags" bs-tooltip></i>
|
||||||
|
<span class="section-info section-info-with-dropdown">
|
||||||
|
<a class="label tag label-default" ng-repeat="tag in imageData.tags"
|
||||||
|
href="javascript:void(0)" ng-click="tagSelected({'tag': tag})">
|
||||||
|
{{ tag }}
|
||||||
|
</a>
|
||||||
|
<span style="color: #ccc;" ng-if="!imageData.tags.length">(No Tags)</span>
|
||||||
|
|
||||||
|
<div class="dropdown" data-placement="top"
|
||||||
|
ng-if="tracker.repository.can_write || imageData.tags">
|
||||||
|
<a href="javascript:void(0)" class="dropdown-button" data-toggle="dropdown"
|
||||||
|
bs-tooltip="tooltip.title" data-title="Manage Tags"
|
||||||
|
data-container="body">
|
||||||
|
<b class="caret"></b>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu pull-right">
|
||||||
|
<li ng-repeat="tag in imageData.tags">
|
||||||
|
<a href="javascript:void(0)" ng-click="tagSelected({'tag': tag})">
|
||||||
|
<i class="fa fa-tag"></i>{{ tag }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider" role="presentation"
|
||||||
|
ng-if="tracker.repository.can_write && imageData.tags"></li>
|
||||||
|
<li>
|
||||||
|
<a href="javascript:void(0)"
|
||||||
|
ng-click="addTagRequested({'image': image})"
|
||||||
|
ng-if="tracker.repository.can_write">
|
||||||
|
<i class="fa fa-plus"></i>Add New Tag
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Command -->
|
||||||
|
<div class="image-section" ng-if="imageData.command && imageData.command.length">
|
||||||
|
<i class="fa fa-terminal section-icon" data-title="Image Command" bs-tooltip></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<span class="formatted-command trimmed"
|
||||||
|
data-html="true"
|
||||||
|
data-title="{{ getTooltipCommand(imageData) }}"
|
||||||
|
data-placement="top" bs-tooltip>{{ getFormattedCommand(imageData) }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Created -->
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-calendar section-icon" data-title="Created" bs-tooltip></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<dd am-time-ago="parseDate(imageData.created)"></dd>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Size -->
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-cloud-upload section-icon"
|
||||||
|
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
||||||
|
bs-tooltip></i>
|
||||||
|
<span class="section-info">{{ imageData.size | bytes }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Locations -->
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-map-marker section-icon"
|
||||||
|
data-title="The geographic region(s) in which this image data is located"
|
||||||
|
bs-tooltip></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<span class="location-view" location="location"
|
||||||
|
ng-repeat="location in imageData.locations"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Changes -->
|
||||||
|
<div class="image-section" ng-show="hasImageChanges">
|
||||||
|
<i class="fa fa-code-fork section-icon"
|
||||||
|
data-title="File Changes"
|
||||||
|
bs-tooltip></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<div class="image-changes-view" repository="tracker.repository" image="image"
|
||||||
|
has-changes="hasImageChanges"></div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="markdown-input-container">
|
<div class="markdown-input-container">
|
||||||
<p ng-class="'lead ' + (canWrite ? 'editable' : 'noteditable')" ng-click="editContent()">
|
<p ng-class="'lead ' + (canWrite ? 'editable' : 'noteditable')" ng-click="editContent()">
|
||||||
<span class="markdown-view" content="content"></span>
|
<span class="markdown-view" content="content"></span>
|
||||||
<span class="empty" ng-show="!content && canWrite">(Click to set {{ fieldTitle }})</span>
|
<span class="empty" ng-show="!content && canWrite">Click to set {{ fieldTitle }}</span>
|
||||||
<i class="fa fa-edit"></i>
|
<i class="fa fa-edit"></i>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">
|
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">
|
||||||
<i ng-class="repository.is_starred ? 'starred fa fa-star' : 'fa fa-star-o'" class="star-icon" ng-click="toggleStar({repository: repository})"></i>
|
<span class="repo-star" repository="repository"
|
||||||
|
star-toggled="starToggled({'repository': repository})"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="description markdown-view" content="repository.description" first-line-only="true" placeholder-needed="true"></div>
|
<div class="description markdown-view" content="repository.description" first-line-only="true" placeholder-needed="true"></div>
|
||||||
|
|
5
static/directives/repo-star.html
Normal file
5
static/directives/repo-star.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<span class="repo-star-element">
|
||||||
|
<i ng-class="repository.is_starred ? 'starred fa fa-star' : 'fa fa-star-o'"
|
||||||
|
class="star-icon" ng-click="toggleStar()">
|
||||||
|
</i>
|
||||||
|
</span>
|
187
static/directives/repo-view/repo-panel-builds.html
Normal file
187
static/directives/repo-view/repo-panel-builds.html
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<div class="repo-panel-builds-element">
|
||||||
|
<div class="tab-header-controls">
|
||||||
|
<button class="btn btn-primary" ng-click="showNewBuildDialog()">
|
||||||
|
<i class="fa fa-plus"></i>Start Build
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h3 class="tab-header">Repository Builds</h3>
|
||||||
|
|
||||||
|
<!-- Builds -->
|
||||||
|
<div class="co-panel">
|
||||||
|
<!-- Builds header controls -->
|
||||||
|
<div class="co-panel-heading">
|
||||||
|
<div class="heading-controls hidden-sm hidden-xs">
|
||||||
|
<span class="filter-control" filter="options.filter" value="recent">Recent Builds</span>
|
||||||
|
<span class="filter-control" filter="options.filter" value="48hour">Last 48 Hours</span>
|
||||||
|
<span class="filter-control" filter="options.filter" value="30day">Last 30 days</span>
|
||||||
|
</div>
|
||||||
|
<div class="heading-title">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
|
Build History
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Builds list content -->
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="resource-view" resource="buildsResource" error-message="'Could not load build information'">
|
||||||
|
|
||||||
|
<!-- No builds found -->
|
||||||
|
<div class="empty" ng-if="!fullBuilds.length">
|
||||||
|
<div class="empty-primary-msg">No matching builds found</div>
|
||||||
|
<div class="empty-secondary-msg">
|
||||||
|
Please change the filter above to search for more builds.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Builds list table -->
|
||||||
|
<table class="co-table" ng-if="fullBuilds.length">
|
||||||
|
<thead>
|
||||||
|
<td class="status-col"></td>
|
||||||
|
<td ng-class="tablePredicateClass('id', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('id')">Build ID</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('tags', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('tags')">Tags</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('commit_sha', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('commit_sha')">Triggered By</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('started_datetime', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('started_datetime')">Date Started</a>
|
||||||
|
</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tr ng-repeat="build in fullBuilds">
|
||||||
|
<td><span class="build-state-icon" build="build"></span></td>
|
||||||
|
<td>
|
||||||
|
<a href="/repository/{{ repository.namespace }}/{{ repository.name }}/build/{{ build.id }}">{{ build.id.substr(0, 8) }}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="building-tag" ng-repeat="tag in build.building_tags">
|
||||||
|
<i class="fa fa-tag"></i>{{ tag }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="triggered-build-description" build="build"></div>
|
||||||
|
</td>
|
||||||
|
<td>{{ build.started | amCalendar }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /Builds -->
|
||||||
|
|
||||||
|
<!-- Build Triggers -->
|
||||||
|
<div class="co-panel" ng-if="repository.can_admin && TriggerService.getTypes().length">
|
||||||
|
<!-- Builds header controls -->
|
||||||
|
<div class="co-panel-heading">
|
||||||
|
<i class="fa fa-flash"></i>
|
||||||
|
Build Triggers
|
||||||
|
|
||||||
|
<div class="heading-controls hidden-sm hidden-xs">
|
||||||
|
<!-- Add Build Trigger -->
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||||
|
Create Build Trigger
|
||||||
|
<b class="caret"></b>
|
||||||
|
</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) }}">
|
||||||
|
<i class="fa fa-lg" ng-class="TriggerService.getMetadata(type).icon"></i>
|
||||||
|
{{ TriggerService.getTitle(type) }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Builds list content -->
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="resource-view" resource="triggersResource" error-message="'Could not load build triggers'">
|
||||||
|
<!-- No Triggers defined -->
|
||||||
|
<div class="empty" ng-if="!triggers.length">
|
||||||
|
<div class="empty-primary-msg">No build triggers defined</div>
|
||||||
|
<div class="empty-secondary-msg">
|
||||||
|
Build triggers invoke builds whenever the triggered condition is met (source control push, webhook, etc)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Triggers list -->
|
||||||
|
<table class="co-table" ng-if="triggers.length">
|
||||||
|
<thead>
|
||||||
|
<td>Trigger Name</td>
|
||||||
|
<td>Dockerfile Location</td>
|
||||||
|
<td>Branches/Tags</td>
|
||||||
|
<td>Pull Robot</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tr ng-repeat="trigger in triggers | filter:{'is_active':false}">
|
||||||
|
<td colspan="5" style="text-align: center">
|
||||||
|
<span class="cor-loader-inline"></span>
|
||||||
|
Trigger Setup in progress:
|
||||||
|
<a href="javascript:void(0)" ng-click="setupTrigger(trigger)">Resume</a> |
|
||||||
|
<a href="javascript:void(0)" ng-click="deleteTrigger(trigger)">Cancel</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<span class="entity-reference" entity="trigger.pull_robot" ng-if="trigger.pull_robot"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="askRunTrigger(trigger)">
|
||||||
|
<i class="fa fa-chevron-right"></i> Run Trigger Now
|
||||||
|
</span>
|
||||||
|
<span class="cor-option" option-click="askDeleteTrigger(trigger)">
|
||||||
|
<i class="fa fa-times"></i> Delete Trigger
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /Build Triggers -->
|
||||||
|
|
||||||
|
<!-- Delete Tag Confirm -->
|
||||||
|
<div class="cor-confirm-dialog"
|
||||||
|
dialog-context="deleteTriggerInfo"
|
||||||
|
dialog-action="deleteTrigger(info.trigger, callback)"
|
||||||
|
dialog-title="Delete Build Trigger"
|
||||||
|
dialog-action-title="Delete Trigger">
|
||||||
|
Are you sure you want to delete this build trigger? No further builds will be automatically
|
||||||
|
started.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dockerfile build dialog -->
|
||||||
|
<div class="dockerfile-build-dialog"
|
||||||
|
show-now="showBuildDialogCounter"
|
||||||
|
repository="repository"
|
||||||
|
build-started="handleBuildStarted(build)">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Setup trigger dialog-->
|
||||||
|
<div class="setup-trigger-dialog"
|
||||||
|
repository="repository"
|
||||||
|
trigger="currentSetupTrigger"
|
||||||
|
canceled="cancelSetupTrigger(trigger)"
|
||||||
|
counter="showTriggerSetupCounter"></div>
|
||||||
|
|
||||||
|
<!-- Manual trigger dialog -->
|
||||||
|
<div class="manual-trigger-build-dialog"
|
||||||
|
repository="repository"
|
||||||
|
trigger="currentStartTrigger"
|
||||||
|
counter="showTriggerStartDialogCounter"
|
||||||
|
start-build="startTrigger(trigger, parameters)"></div>
|
||||||
|
|
||||||
|
</div>
|
63
static/directives/repo-view/repo-panel-changes.html
Normal file
63
static/directives/repo-view/repo-panel-changes.html
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<div class="repo-panel-changes-element">
|
||||||
|
<!-- No Tags Selected -->
|
||||||
|
<div class="empty" ng-if="!selectedTags.length">
|
||||||
|
<div class="empty-primary-msg">No tags selected to view</div>
|
||||||
|
<div class="empty-secondary-msg">
|
||||||
|
Please select one or more tags in the <i class="fa fa-tags" style="margin-left: 4px; margin-right: 4px;"></i> Tags tab to visualize.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tags Selected -->
|
||||||
|
<div ng-if="selectedTags.length > 0">
|
||||||
|
<h3 class="tab-header">
|
||||||
|
Visualize Tags:
|
||||||
|
<span class="visualized-tag" ng-repeat="tag in selectedTags">
|
||||||
|
<i class="fa fa-tag"></i>{{ tag }}
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div id="image-history row" class="resource-view" resource="imagesResource"
|
||||||
|
error-message="'Cannot load repository images'">
|
||||||
|
|
||||||
|
<!-- Tree View container -->
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<!-- Image history tree -->
|
||||||
|
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Side Panel -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="side-panel-title" ng-if="currentTag">
|
||||||
|
<i class="fa fa-tag"></i>{{ currentTag }}
|
||||||
|
</div>
|
||||||
|
<div class="side-panel-title" ng-if="currentImage">
|
||||||
|
<i class="fa fa-archive"></i>{{ currentImage.substr(0, 12) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="side-panel">
|
||||||
|
<!-- Tag Info -->
|
||||||
|
<div class="tag-info-sidebar"
|
||||||
|
tracker="tracker"
|
||||||
|
tag="currentTag"
|
||||||
|
image-selected="setImage(image)"
|
||||||
|
delete-tag-requested="tagActionHandler.askDeleteTag(tag)"
|
||||||
|
ng-if="currentTag">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image Info -->
|
||||||
|
<div class="image-info-sidebar"
|
||||||
|
tracker="tracker"
|
||||||
|
image="currentImage"
|
||||||
|
tag-selected="setTag(tag)"
|
||||||
|
add-tag-requested="tagActionHandler.askAddTag(image)"
|
||||||
|
ng-if="currentImage">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tag-operations-dialog" repository="repository" images="images"
|
||||||
|
action-handler="tagActionHandler" tag-changed="handleTagChanged(data)"></div>
|
72
static/directives/repo-view/repo-panel-info.html
Normal file
72
static/directives/repo-view/repo-panel-info.html
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<div class="repo-panel-info-element">
|
||||||
|
<!-- Repository stats and builds summary -->
|
||||||
|
<div class="repository-stats row">
|
||||||
|
<!-- Pull Stats -->
|
||||||
|
<div class="col-sm-3 stat-col">
|
||||||
|
<div class="stat-title">Repo Pulls</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">{{ repository.stats.pulls.today }}</div>
|
||||||
|
<div class="stat-subtitle">Last 24 hours</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">{{ repository.stats.pulls.thirty_day }}</div>
|
||||||
|
<div class="stat-subtitle">Last 30 days</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Push Stats -->
|
||||||
|
<div class="col-sm-3 stat-col">
|
||||||
|
<div class="stat-title">Repo Pushes</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">{{ repository.stats.pushes.today }}</div>
|
||||||
|
<div class="stat-subtitle">Last 24 hours</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">{{ repository.stats.pushes.thirty_day }}</div>
|
||||||
|
<div class="stat-subtitle">Last 30 days</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Builds -->
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="stat-title">Recent Repo Builds</div>
|
||||||
|
|
||||||
|
<!-- Loading -->
|
||||||
|
<div class="cor-loader" ng-if="!builds"></div>
|
||||||
|
|
||||||
|
<!-- No Builds -->
|
||||||
|
<div class="empty" ng-if="builds && !builds.length">
|
||||||
|
<div class="empty-primary-msg">No builds have been run for this repository.</div>
|
||||||
|
<div class="empty-secondary-msg" ng-if="repository.can_write">
|
||||||
|
Click on the <i class="fa fa-tasks" style="margin-left: 6px"></i> Builds tab to start a new build.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Builds -->
|
||||||
|
<div ng-if="builds && builds.length">
|
||||||
|
<div class="build-mini-status" ng-repeat="build in builds" build="build"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- View All -->
|
||||||
|
<a href="/repository/{{ repository.namespace }}/{{ repository.name }}?tab=builds"
|
||||||
|
class="view-all" ng-if="repository.can_admin && builds.length">
|
||||||
|
View Build History
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Repository Description -->
|
||||||
|
<div class="description-container">
|
||||||
|
<h4 style="font-size:20px;">Description</h4>
|
||||||
|
<div class="description markdown-input"
|
||||||
|
content="repository.description"
|
||||||
|
can-write="repository.can_write"
|
||||||
|
content-changed="updateDescription"
|
||||||
|
field-title="'repository description'">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
95
static/directives/repo-view/repo-panel-settings.html
Normal file
95
static/directives/repo-view/repo-panel-settings.html
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<div class="repo-panel-settings-element">
|
||||||
|
<h3 class="tab-header">Repository Settings</h3>
|
||||||
|
|
||||||
|
<!-- User/Team Permissions -->
|
||||||
|
<div class="co-panel">
|
||||||
|
<div class="co-panel-heading"><i class="fa fa-key"></i> User and Robot Permissions</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="repository-permissions-table" repository="repository"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Access Tokens (DEPRECATED) -->
|
||||||
|
<div class="co-panel" ng-show="hasTokens">
|
||||||
|
<div class="co-panel-heading"><i class="fa fa-key"></i> Access Token Permissions</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="repository-tokens-table" repository="repository" has-tokens="hasTokens"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Events and Notifications -->
|
||||||
|
<div class="repository-events-table" repository="repository"></div>
|
||||||
|
|
||||||
|
<!-- Other settings -->
|
||||||
|
<div class="co-panel">
|
||||||
|
<div class="co-panel-heading"><i class="fa fa-gears"></i> Repository Settings</div>
|
||||||
|
|
||||||
|
<div class="cor-loader" ng-show="!repository"></div>
|
||||||
|
|
||||||
|
<div ng-show="repository">
|
||||||
|
<!-- Public/Private -->
|
||||||
|
<div class="panel-body panel-section lock-section" ng-if="!repository.is_public">
|
||||||
|
<i class="fa fa-lock lock-icon"></i>
|
||||||
|
<div>This repository is currently <b>private</b>. Only users on the permissions list may view and interact with it.</div>
|
||||||
|
|
||||||
|
<button class="btn btn-default" ng-click="askChangeAccess('public')">
|
||||||
|
<i class="fa fa-unlock"></i>Make Public
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-body panel-section lock-section" ng-if="repository.is_public">
|
||||||
|
<i class="fa fa-unlock lock-icon"></i>
|
||||||
|
|
||||||
|
<div>This repository is currently <b>public</b> and is visible to all users, and may be pulled by all users.</div>
|
||||||
|
|
||||||
|
<button class="btn btn-default" ng-click="askChangeAccess('private')">
|
||||||
|
<i class="fa fa-lock"></i>Make Private
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Repository -->
|
||||||
|
<div class="panel-body panel-section">
|
||||||
|
<div>Deleting a repository <b>cannot be undone</b>. Here be dragons!</div>
|
||||||
|
<button class="btn btn-danger" ng-click="askDelete()">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
Delete Repository
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Build Status Badge -->
|
||||||
|
<div class="panel-body panel-section">
|
||||||
|
<!-- Status Image -->
|
||||||
|
<a ng-href="/repository/{{ repository.namespace }}/{{ repository.name }}">
|
||||||
|
<img ng-src="/repository/{{ repository.namespace }}/{{ repository.name }}/status?token={{ repository.status_token }}"
|
||||||
|
data-title="Docker Repository on Quay.io">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Embed formats -->
|
||||||
|
<table style="margin-top: 20px; width: 600px;">
|
||||||
|
<thead>
|
||||||
|
<th style="width: 150px"></th>
|
||||||
|
<th></th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>Image (SVG):</td>
|
||||||
|
<td>
|
||||||
|
<div class="copy-box" hovering-message="true" value="getBadgeFormat('svg', repository)"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Markdown:</td>
|
||||||
|
<td>
|
||||||
|
<div class="copy-box" hovering-message="true" value="getBadgeFormat('md', repository)"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>AsciiDoc:</td>
|
||||||
|
<td>
|
||||||
|
<div class="copy-box" hovering-message="true" value="getBadgeFormat('asciidoc', repository)"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
99
static/directives/repo-view/repo-panel-tags.html
Normal file
99
static/directives/repo-view/repo-panel-tags.html
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<div class="repo-panel-tags-element">
|
||||||
|
<h3 class="tab-header">Repository Tags</h3>
|
||||||
|
<div class="resource-view" resource="imagesResource" error-message="'Could not load images'">
|
||||||
|
|
||||||
|
<div class="co-check-bar">
|
||||||
|
<span class="cor-checkable-menu" controller="checkedTags">
|
||||||
|
<div class="cor-checkable-menu-item" item-filter="allTagFilter">
|
||||||
|
<i class="fa fa-check-square-o"></i>All Tags
|
||||||
|
</div>
|
||||||
|
<div class="cor-checkable-menu-item" item-filter="noTagFilter(tag)">
|
||||||
|
<i class="fa fa-square-o"></i>No Tags
|
||||||
|
</div>
|
||||||
|
<div class="cor-checkable-menu-item" item-filter="commitTagFilter(tag)">
|
||||||
|
<i class="fa fa-git"></i>Commit SHAs
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cor-checkable-menu-item" item-filter="imageIDFilter(it.image_id, tag)"
|
||||||
|
ng-repeat="it in imageTracks">
|
||||||
|
<i class="fa fa-circle-o" ng-style="{'color': it.color}"></i> {{ it.image_id.substr(0, 12) }}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="co-checked-actions" ng-if="checkedTags.checked.length">
|
||||||
|
<a href="javascript:void(0)" class="btn btn-default" ng-click="setTab('changes')">
|
||||||
|
<i class="fa fa-code-fork"></i> Visualize
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-default"
|
||||||
|
ng-click="askDeleteMultipleTags(checkedTags.checked)"
|
||||||
|
ng-if="repository.can_write">
|
||||||
|
<i class="fa fa-times"></i> Delete
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="co-filter-box">
|
||||||
|
<input class="form-control" type="text" ng-model="options.tagFilter" placeholder="Filter Tags...">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="co-table">
|
||||||
|
<thead>
|
||||||
|
<td class="checkbox-col"></td>
|
||||||
|
<td ng-class="tablePredicateClass('name', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('name')">Tag</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('last_modified_datetime', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('last_modified_datetime')">Last Modified</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('size', options.predicate, options.reverse)">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('size')">Size</a>
|
||||||
|
</td>
|
||||||
|
<td ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
|
||||||
|
colspan="{{ imageTracks.length + 1 }}">
|
||||||
|
<a href="javascript:void(0)" ng-click="orderBy('image_id')">Image</a>
|
||||||
|
</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tr class="co-checkable-row"
|
||||||
|
ng-repeat="tag in tags"
|
||||||
|
ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''">
|
||||||
|
<td><span class="cor-checkable-item" controller="checkedTags" item="tag"></span></td>
|
||||||
|
<td><i class="fa fa-tag"></i> {{ tag.name }}</td>
|
||||||
|
<td><span am-time-ago="tag.last_modified"></span></td>
|
||||||
|
<td>{{ tag.size | bytes }}</td>
|
||||||
|
<td class="image-id-col">
|
||||||
|
<a ng-href="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}">
|
||||||
|
{{ tag.image_id.substr(0, 12) }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="image-track" ng-repeat="it in imageTracks">
|
||||||
|
<span class="image-track-dot" ng-if="it.image_id == tag.image_id"
|
||||||
|
ng-style="{'borderColor': it.color}"></span>
|
||||||
|
<span class="image-track-line" ng-class="trackLineClass($parent.$index, it)"
|
||||||
|
ng-style="{'borderColor': it.color}"></span>
|
||||||
|
</td>
|
||||||
|
<td class="options-col">
|
||||||
|
<span class="cor-options-menu" ng-if="repository.can_write">
|
||||||
|
<span class="cor-option" option-click="askDeleteTag(tag.name)">
|
||||||
|
<i class="fa fa-times"></i> Delete Tag
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="empty" ng-if="allTags.length && !tags.length">
|
||||||
|
<div class="empty-primary-msg">No matching tags found.</div>
|
||||||
|
<div class="empty-secondary-msg">Try expanding your filtering terms.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty" ng-if="!allTags.length">
|
||||||
|
<div class="empty-primary-msg">This repository is empty.</div>
|
||||||
|
<div class="empty-secondary-msg">Push a tag or initiate a build to populate this repository.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tag-operations-dialog" repository="repository" images="images"
|
||||||
|
action-handler="tagActionHandler"></div>
|
75
static/directives/repository-events-table.html
Normal file
75
static/directives/repository-events-table.html
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<div class="repository-events-table-element">
|
||||||
|
<div class="co-panel">
|
||||||
|
<div class="co-panel-heading">
|
||||||
|
<i class="fa fa-bell"></i> Events and Notifications
|
||||||
|
|
||||||
|
<div class="heading-controls hidden-sm hidden-xs">
|
||||||
|
<button class="btn btn-primary" ng-click="askCreateNotification()">
|
||||||
|
<i class="fa fa-plus"></i> Create Notification
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="resource-view" resource="notificationsResource"
|
||||||
|
error-message="'Could not load repository events'">
|
||||||
|
|
||||||
|
<div class="empty" ng-if="!notifications.length">
|
||||||
|
<div class="empty-primary-msg">No notification have been setup for this repository.</div>
|
||||||
|
<div class="empty-secondary-msg" ng-if="repository.can_write">
|
||||||
|
Click the "Create Notification" button above to add a new notification for a repository event.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="co-table permissions" ng-if="notifications.length">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Event</td>
|
||||||
|
<td>Notification</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr class="notification-row" ng-repeat="notification in notifications">
|
||||||
|
<td>
|
||||||
|
<span class="notification-event">
|
||||||
|
<i class="fa fa-lg" ng-class="getEventInfo(notification).icon"></i>
|
||||||
|
{{ getEventInfo(notification).title }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="notification-method">
|
||||||
|
<i class="fa fa-lg" ng-class="getMethodInfo(notification).icon"></i>
|
||||||
|
{{ getMethodInfo(notification).title }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="testNotification(notification)">
|
||||||
|
<i class="fa fa-send"></i> Test Notification
|
||||||
|
</span>
|
||||||
|
<span class="cor-option" option-click="showWebhookInfo(notification)"
|
||||||
|
ng-if="getMethodInfo(notification).id == 'webhook'">
|
||||||
|
<i class="fa fa-book"></i>
|
||||||
|
Webhook Documentation
|
||||||
|
</span>
|
||||||
|
<span class="cor-option" option-click="deleteNotification(notification)">
|
||||||
|
<i class="fa fa-times"></i> Delete Notification
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- New notification dialog-->
|
||||||
|
<div class="create-external-notification-dialog"
|
||||||
|
repository="repository"
|
||||||
|
counter="showNewNotificationCounter"
|
||||||
|
notification-created="handleNotificationCreated(notification)"></div>
|
||||||
|
</div>
|
82
static/directives/repository-permissions-table.html
Normal file
82
static/directives/repository-permissions-table.html
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<div class="repository-permissions-table-element">
|
||||||
|
<div class="resource-view"
|
||||||
|
resources="[permissionResources.team, permissionResources.user]"
|
||||||
|
error-message="'Could not load repository permissions'">
|
||||||
|
<table class="co-table permissions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Account Name</td>
|
||||||
|
<td style="width: 300px">Permissions</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<!-- Team Permissions -->
|
||||||
|
<tr ng-repeat="(name, permission) in permissionResources.team.value">
|
||||||
|
<td class="team entity">
|
||||||
|
<span class="entity-reference" namespace="repository.namespace"
|
||||||
|
entity="buildEntityForPermission(name, permission, 'team')">
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="user-permissions">
|
||||||
|
<span class="role-group" current-role="permission.role" role-changed="setRole(role, name, 'team')" roles="roles"></span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="options-col">
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="deleteRole(name, 'team')">
|
||||||
|
<i class="fa fa-times"></i> Delete Permission
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- User Permissions -->
|
||||||
|
<tr ng-repeat="(name, permission) in permissionResources.user.value">
|
||||||
|
<td class="{{ 'user entity ' + (permission.is_org_member ? '' : 'outside') }}">
|
||||||
|
<span class="entity-reference" namespace="repository.namespace"
|
||||||
|
entity="buildEntityForPermission(name, permission, 'user')">
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="user-permissions">
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<span class="role-group" current-role="permission.role" role-changed="setRole(role, name, 'user')" roles="roles"></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="options-col">
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="deleteRole(name, 'user')">
|
||||||
|
<i class="fa fa-times"></i> Delete Permission
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="add-row">
|
||||||
|
<td id="add-entity-permission" class="admin-search">
|
||||||
|
<span class="entity-search" namespace="repository.namespace"
|
||||||
|
placeholder="'Select a ' + (repository.is_organization ? 'team or ' : '') + 'user...'"
|
||||||
|
current-entity="addPermissionInfo.entity"></span>
|
||||||
|
</td>
|
||||||
|
<td colspan="2">
|
||||||
|
<span class="role-group" current-role="addPermissionInfo.role" roles="roles"
|
||||||
|
role-changed="addPermissionInfo.role = role"></span>
|
||||||
|
<button class="btn btn-success" style="margin-left: 10px"
|
||||||
|
ng-disabled="!addPermissionInfo.role || !addPermissionInfo.entity"
|
||||||
|
ng-click="addPermission()">
|
||||||
|
Add Permission
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Grant Permission Confirm -->
|
||||||
|
<div class="cor-confirm-dialog"
|
||||||
|
dialog-context="grantPermissionInfo"
|
||||||
|
dialog-action="grantPermission(info.entity, callback)"
|
||||||
|
dialog-title="Grant Permission"
|
||||||
|
dialog-action-title="Grant Permission">
|
||||||
|
The selected user is outside of your organization. Are you sure you want to grant the user access to this repository?
|
||||||
|
</div>
|
||||||
|
</div>
|
35
static/directives/repository-tokens-table.html
Normal file
35
static/directives/repository-tokens-table.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<div class="repository-tokens-table-element">
|
||||||
|
<div class="resource-view" resource="tokensResource"
|
||||||
|
error-message="'Could not load repository tokens'">
|
||||||
|
<div class="alert alert-warning">Note: Access tokens are <strong>deprecated</strong> and will be removed in the near future. <a href="http://docs.quay.io/glossary/robot-accounts.html">Robot accounts</a> are the recommended replacement.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="co-table permissions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td style="min-width: 400px;">Token Name</td>
|
||||||
|
<td>Permissions</td>
|
||||||
|
<td style="width: 95px;"></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="(code, token) in tokens">
|
||||||
|
<td class="user token">
|
||||||
|
<i class="fa fa-key"></i>
|
||||||
|
<a ng-click="showToken(token.code)">{{ token.friendlyName }}</a>
|
||||||
|
</td>
|
||||||
|
<td class="user-permissions">
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'read')" ng-class="{read: 'active', write: ''}[token.role]">Read only</button>
|
||||||
|
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'write')" ng-class="{read: '', write: 'active'}[token.role]">Write</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="delete-ui" delete-title="'Delete Token'" perform-delete="deleteToken(token.code)"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="resource-error alert alert-info" ng-show="getState(resources) == 'error'">
|
<div class="resource-error alert alert-info" ng-show="getState(resources) == 'error'">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</div>
|
||||||
<div class="resource-content" ng-class="getState(resources) == 'ready' ? 'visible' : ''">
|
<div class="resource-content" ng-class="getState(resources) == 'ready' ? 'visible' : 'hidden'">
|
||||||
<span ng-transclude></span>
|
<span ng-transclude></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
42
static/directives/tag-info-sidebar.html
Normal file
42
static/directives/tag-info-sidebar.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<div class="tag-info-sidebar-element">
|
||||||
|
<dl class="dl-normal">
|
||||||
|
<dt>Last Modified</dt>
|
||||||
|
<dd am-time-ago="parseDate(tagImage.created)"></dd>
|
||||||
|
|
||||||
|
<dt>Total Compressed Size</dt>
|
||||||
|
<dd>
|
||||||
|
<span class="context-tooltip"
|
||||||
|
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
||||||
|
data-container="body"
|
||||||
|
bs-tooltip>
|
||||||
|
{{ tracker.getTotalSize(tag) | bytes }}
|
||||||
|
</span>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<div class="tag-image-sizes">
|
||||||
|
<div class="tag-image-size" ng-repeat="image in tracker.getImagesForTagBySize(tag) | limitTo: 10">
|
||||||
|
<span class="size-limiter">
|
||||||
|
<span class="size-bar"
|
||||||
|
ng-style="{'width': (image.size / tracker.getTotalSize(tag)) * 100 + '%'}"
|
||||||
|
data-title="{{ image.size | bytes }}"
|
||||||
|
bs-tooltip>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="size-title">
|
||||||
|
<a class="image-size-link"
|
||||||
|
href="javascript:void(0)"
|
||||||
|
ng-click="imageSelected({'image': image.id})"
|
||||||
|
data-image="{{ image.id.substr(0, 12) }}">
|
||||||
|
{{ image.id.substr(0, 12) }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-bar" ng-show="tracker.repository.can_admin">
|
||||||
|
<button class="btn btn-default" ng-click="deleteTagRequested({'tag': tag})">
|
||||||
|
<i class="fa fa-times" style="margin-right: 6px;"></i>Delete Tag
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
87
static/directives/tag-operations-dialog.html
Normal file
87
static/directives/tag-operations-dialog.html
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<div class="tag-operations-dialog-element">
|
||||||
|
<!-- Add Tag Dialog -->
|
||||||
|
<div class="modal fade" id="createOrMoveTagModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"
|
||||||
|
aria-hidden="true" ng-show="!addingTag">×</button>
|
||||||
|
<h4 class="modal-title">{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move' : 'Add' }} Tag to Image {{ toTagImage.substr(0, 12) }}</h4>
|
||||||
|
</div>
|
||||||
|
<form name="addTagForm" ng-submit="createOrMoveTag(toTagImage, tagToCreate);">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="cor-loader" ng-show="addingTag"></div>
|
||||||
|
<div ng-show="!addingTag">
|
||||||
|
<input type="text" class="form-control" id="tagName"
|
||||||
|
placeholder="Enter tag name"
|
||||||
|
ng-model="tagToCreate" ng-pattern="/^([a-z0-9_\.-]){3,30}$/"
|
||||||
|
ng-disabled="creatingTag" autofocus required>
|
||||||
|
|
||||||
|
<div style="margin: 10px; margin-top: 20px;"
|
||||||
|
ng-show="isOwnedTag(toTagImage, tagToCreate)">
|
||||||
|
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to this image.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin: 10px; margin-top: 20px;"
|
||||||
|
ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||||
|
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to another image. This will <b>move</b> the tag.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tag-specific-images-view"
|
||||||
|
tag="tagToCreate"
|
||||||
|
repository="repo"
|
||||||
|
images="images"
|
||||||
|
image-cutoff="toTagImage"
|
||||||
|
style="margin: 10px; margin-top: 20px; margin-bottom: -10px;"
|
||||||
|
ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||||
|
This will also delete any unattach images and delete the following images:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" ng-show="!addingTag">
|
||||||
|
<button type="submit" class="btn btn-primary"
|
||||||
|
ng-disabled="addTagForm.$invalid || isOwnedTag(toTagImage, tagToCreate)"
|
||||||
|
ng-class="isAnotherImageTag(toTagImage, tagToCreate) ? 'btn-warning' : 'btn-primary'"
|
||||||
|
ng-show="!creatingTag">
|
||||||
|
{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move Tag' : 'Create Tag' }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-default" data-dismiss="modal" ng-show="!addingTag">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
<!-- Delete Tags Confirm -->
|
||||||
|
<div class="cor-confirm-dialog"
|
||||||
|
dialog-context="deleteMultipleTagsInfo"
|
||||||
|
dialog-action="deleteMultipleTags(info.tags, callback)"
|
||||||
|
dialog-title="Delete Tags"
|
||||||
|
dialog-action-title="Delete Tags">
|
||||||
|
Are you sure you want to delete the following tags:
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="tag_info in deleteMultipleTagsInfo.tags">
|
||||||
|
<span class="label label-default tag">{{ tag_info.name }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px">
|
||||||
|
<strong>Note: </strong>This operation can take several minutes.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Tag Confirm -->
|
||||||
|
<div class="cor-confirm-dialog"
|
||||||
|
dialog-context="deleteTagInfo"
|
||||||
|
dialog-action="deleteTag(info.tag, callback)"
|
||||||
|
dialog-title="Delete Tag"
|
||||||
|
dialog-action-title="Delete Tag">
|
||||||
|
Are you sure you want to delete tag
|
||||||
|
<span class="label label-default tag">{{ deleteTagInfo.tag }}</span>?
|
||||||
|
|
||||||
|
<div class="tag-specific-images-view" tag="deleteTagInfo.tag" repository="repository"
|
||||||
|
images="images" style="margin-top: 20px">
|
||||||
|
The following images and any other images not referenced by a tag will be deleted:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,5 +1,14 @@
|
||||||
<div class="triggered-build-description-element">
|
<div class="triggered-build-description-element">
|
||||||
<span ng-switch on="build.trigger.service">
|
|
||||||
|
<span class="manual" ng-if="!build.trigger && !build.job_config.manual_user">
|
||||||
|
(Manually Triggered Build)
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span 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 -->
|
<!-- GitHub -->
|
||||||
<span ng-switch-when="github">
|
<span ng-switch-when="github">
|
||||||
<!-- Full Commit Information -->
|
<!-- Full Commit Information -->
|
||||||
|
|
|
@ -162,6 +162,51 @@ angular.module("core-ui", [])
|
||||||
return directiveDefinitionObject;
|
return directiveDefinitionObject;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.directive('corConfirmDialog', function() {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 1,
|
||||||
|
templateUrl: '/static/directives/cor-confirm-dialog.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'dialogTitle': '@dialogTitle',
|
||||||
|
'dialogActionTitle': '@dialogActionTitle',
|
||||||
|
|
||||||
|
'dialogContext': '=dialogContext',
|
||||||
|
'dialogAction': '&dialogAction'
|
||||||
|
},
|
||||||
|
controller: function($rootScope, $scope, $element) {
|
||||||
|
$scope.working = false;
|
||||||
|
|
||||||
|
$scope.$watch('dialogContext', function(dc) {
|
||||||
|
if (!dc) { return; }
|
||||||
|
$scope.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.performAction = function() {
|
||||||
|
$scope.working = true;
|
||||||
|
$scope.dialogAction({
|
||||||
|
'info': $scope.dialogContext,
|
||||||
|
'callback': function(result) {
|
||||||
|
$scope.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.show = function() {
|
||||||
|
$scope.working = false;
|
||||||
|
$element.find('.modal').modal({});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.hide = function() {
|
||||||
|
$element.find('.modal').modal('hide');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
})
|
||||||
|
|
||||||
.directive('corTabPanel', function() {
|
.directive('corTabPanel', function() {
|
||||||
var directiveDefinitionObject = {
|
var directiveDefinitionObject = {
|
||||||
priority: 1,
|
priority: 1,
|
||||||
|
@ -581,4 +626,86 @@ angular.module("core-ui", [])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return directiveDefinitionObject;
|
return directiveDefinitionObject;
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('corCheckableMenu', function() {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 1,
|
||||||
|
templateUrl: '/static/directives/cor-checkable-menu.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'controller': '=controller'
|
||||||
|
},
|
||||||
|
controller: function($rootScope, $scope, $element) {
|
||||||
|
$scope.getClass = function(items, checked) {
|
||||||
|
if (checked.length == 0) {
|
||||||
|
return 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checked.length == items.length) {
|
||||||
|
return 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'some';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleItems = function($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
$scope.controller.toggleItems();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.checkByFilter = function(filter) {
|
||||||
|
$scope.controller.checkByFilter(function(tag) {
|
||||||
|
return filter({'tag': tag});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('corCheckableMenuItem', function() {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 1,
|
||||||
|
templateUrl: '/static/directives/cor-checkable-menu-item.html',
|
||||||
|
replace: true,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
require: '^corCheckableMenu',
|
||||||
|
scope: {
|
||||||
|
'itemFilter': '&itemFilter'
|
||||||
|
},
|
||||||
|
link: function($scope, $element, $attr, parent) {
|
||||||
|
$scope.parent = parent;
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: function($rootScope, $scope, $element) {
|
||||||
|
$scope.selected = function() {
|
||||||
|
$scope.parent.checkByFilter(this.itemFilter);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('corCheckableItem', function() {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 1,
|
||||||
|
templateUrl: '/static/directives/cor-checkable-item.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'item': '=item',
|
||||||
|
'controller': '=controller'
|
||||||
|
},
|
||||||
|
controller: function($rootScope, $scope, $element) {
|
||||||
|
$scope.toggleItem = function() {
|
||||||
|
$scope.controller.toggleItem($scope.item);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
});
|
});
|
232
static/js/directives/repo-view/repo-panel-builds.js
Normal file
232
static/js/directives/repo-view/repo-panel-builds.js
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the builds panel for a repository view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoPanelBuilds', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-view/repo-panel-builds.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'builds': '=builds'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, $filter, $routeParams, ApiService, TriggerService) {
|
||||||
|
var orderBy = $filter('orderBy');
|
||||||
|
|
||||||
|
$scope.TriggerService = TriggerService;
|
||||||
|
|
||||||
|
$scope.options = {
|
||||||
|
'filter': 'recent',
|
||||||
|
'reverse': false,
|
||||||
|
'predicate': 'started_datetime'
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.currentFilter = null;
|
||||||
|
|
||||||
|
$scope.currentStartTrigger = null;
|
||||||
|
$scope.currentSetupTrigger = null;
|
||||||
|
|
||||||
|
$scope.showBuildDialogCounter = 0;
|
||||||
|
$scope.showTriggerStartDialogCounter = 0;
|
||||||
|
$scope.showTriggerSetupCounter = 0;
|
||||||
|
|
||||||
|
var updateBuilds = function() {
|
||||||
|
if (!$scope.allBuilds) { return; }
|
||||||
|
|
||||||
|
var unordered = $scope.allBuilds.map(function(build_info) {
|
||||||
|
var commit_sha = null;
|
||||||
|
|
||||||
|
if (build_info.job_config.trigger_metadata) {
|
||||||
|
commit_sha = build_info.job_config.trigger_metadata.commit_sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.extend(build_info, {
|
||||||
|
'started_datetime': (new Date(build_info.started)).valueOf() * (-1),
|
||||||
|
'building_tags': build_info.job_config.docker_tags,
|
||||||
|
'commit_sha': commit_sha
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.fullBuilds = orderBy(unordered, $scope.options.predicate, $scope.options.reverse);
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadBuilds = function() {
|
||||||
|
if (!$scope.builds || !$scope.repository || !$scope.options.filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: We only refresh if the filter has changed.
|
||||||
|
var filter = $scope.options.filter;
|
||||||
|
if ($scope.buildsResource && filter == $scope.currentFilter) { return; }
|
||||||
|
|
||||||
|
var since = null;
|
||||||
|
|
||||||
|
if ($scope.options.filter == '48hours') {
|
||||||
|
since = Math.floor(moment().subtract(2, 'days').valueOf() / 1000);
|
||||||
|
} else if ($scope.options.filter == '30days') {
|
||||||
|
since = Math.floor(moment().subtract(30, 'days').valueOf() / 1000);
|
||||||
|
} else {
|
||||||
|
since = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'limit': 100,
|
||||||
|
'since': since
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.buildsResource = ApiService.getRepoBuildsAsResource(params).get(function(resp) {
|
||||||
|
$scope.allBuilds = resp.builds;
|
||||||
|
$scope.currentFilter = filter;
|
||||||
|
updateBuilds();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildsChanged = function() {
|
||||||
|
if (!$scope.allBuilds) {
|
||||||
|
loadBuilds();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$scope.builds || !$scope.repository) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace any build records with updated records from the server.
|
||||||
|
$scope.builds.map(function(build) {
|
||||||
|
for (var i = 0; i < $scope.allBuilds.length; ++i) {
|
||||||
|
var current = $scope.allBuilds[i];
|
||||||
|
if (current.id == build.id && current.phase != build.phase) {
|
||||||
|
$scope.allBuilds[i] = build;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateBuilds();
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadBuildTriggers = function() {
|
||||||
|
if (!$scope.repository || !$scope.repository.can_admin) { return; }
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.triggersResource = ApiService.listBuildTriggersAsResource(params).get(function(resp) {
|
||||||
|
$scope.triggers = resp.triggers;
|
||||||
|
|
||||||
|
// Check to see if we need to setup any trigger.
|
||||||
|
var newTriggerId = $routeParams.newtrigger;
|
||||||
|
if (newTriggerId) {
|
||||||
|
$scope.triggers.map(function(trigger) {
|
||||||
|
if (trigger['id'] == newTriggerId && !trigger['is_active']) {
|
||||||
|
$scope.setupTrigger(trigger);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('repository', loadBuildTriggers);
|
||||||
|
$scope.$watch('repository', loadBuilds);
|
||||||
|
|
||||||
|
$scope.$watch('builds', buildsChanged);
|
||||||
|
|
||||||
|
$scope.$watch('options.filter', loadBuilds);
|
||||||
|
$scope.$watch('options.predicate', updateBuilds);
|
||||||
|
$scope.$watch('options.reverse', updateBuilds);
|
||||||
|
|
||||||
|
$scope.tablePredicateClass = function(name, predicate, reverse) {
|
||||||
|
if (name != predicate) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'current ' + (reverse ? 'reversed' : '');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.orderBy = function(predicate) {
|
||||||
|
if (predicate == $scope.options.predicate) {
|
||||||
|
$scope.options.reverse = !$scope.options.reverse;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.options.reverse = false;
|
||||||
|
$scope.options.predicate = predicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askDeleteTrigger = function(trigger) {
|
||||||
|
$scope.deleteTriggerInfo = {
|
||||||
|
'trigger': trigger
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askRunTrigger = function(trigger) {
|
||||||
|
$scope.currentStartTrigger = trigger;
|
||||||
|
$scope.showTriggerStartDialogCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancelSetupTrigger = function(trigger) {
|
||||||
|
if ($scope.currentSetupTrigger != trigger) { return; }
|
||||||
|
|
||||||
|
$scope.currentSetupTrigger = null;
|
||||||
|
$scope.deleteTrigger(trigger);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setupTrigger = function(trigger) {
|
||||||
|
$scope.currentSetupTrigger = trigger;
|
||||||
|
$scope.showTriggerSetupCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.startTrigger = function(trigger, opt_custom) {
|
||||||
|
var parameters = TriggerService.getRunParameters(trigger.service);
|
||||||
|
if (parameters.length && !opt_custom) {
|
||||||
|
$scope.currentStartTrigger = trigger;
|
||||||
|
$scope.showTriggerStartDialogCounter++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'trigger_uuid': trigger.id
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.manuallyStartBuildTrigger(opt_custom || {}, params).then(function(resp) {
|
||||||
|
$scope.allBuilds.push(resp);
|
||||||
|
updateBuilds();
|
||||||
|
}, ApiService.errorDisplay('Could not start build'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteTrigger = function(trigger, opt_callback) {
|
||||||
|
if (!trigger) { return; }
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'trigger_uuid': trigger.id
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorHandler = ApiService.errorDisplay('Could not delete build trigger', function() {
|
||||||
|
opt_callback && opt_callback(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
ApiService.deleteBuildTrigger(null, params).then(function(resp) {
|
||||||
|
$scope.triggers.splice($scope.triggers.indexOf(trigger), 1);
|
||||||
|
opt_callback && opt_callback(true);
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showNewBuildDialog = function() {
|
||||||
|
$scope.showBuildDialogCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleBuildStarted = function(build) {
|
||||||
|
$scope.allBuilds.push(build);
|
||||||
|
updateBuilds();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
206
static/js/directives/repo-view/repo-panel-changes.js
Normal file
206
static/js/directives/repo-view/repo-panel-changes.js
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the changes visualization panel for a repository view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoPanelChanges', function () {
|
||||||
|
var RepositoryImageTracker = function(repository, images) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.images = images;
|
||||||
|
|
||||||
|
// Build a map of image ID -> image.
|
||||||
|
var imageIDMap = {};
|
||||||
|
this.images.map(function(image) {
|
||||||
|
imageIDMap[image.id] = image;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.imageMap_ = imageIDMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.imageLink = function(image) {
|
||||||
|
return '/repository/' + this.repository.namespace + '/' +
|
||||||
|
this.repository.name + '/image/' + image;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.getImageForTag = function(tag) {
|
||||||
|
var tagData = this.lookupTag(tag);
|
||||||
|
if (!tagData) { return null; }
|
||||||
|
|
||||||
|
return this.imageMap_[tagData.image_id];
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.lookupTag = function(tag) {
|
||||||
|
return this.repository.tags[tag];
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.lookupImage = function(image) {
|
||||||
|
return this.imageMap_[image];
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.forAllTagImages = function(tag, callback) {
|
||||||
|
var tagData = this.lookupTag(tag);
|
||||||
|
if (!tagData) { return; }
|
||||||
|
|
||||||
|
var tagImage = this.imageMap_[tagData.image_id];
|
||||||
|
if (!tagImage) { return; }
|
||||||
|
|
||||||
|
// Callback the tag's image itself.
|
||||||
|
callback(tagImage);
|
||||||
|
|
||||||
|
// Callback any parent images.
|
||||||
|
if (!tagImage.ancestors) { return; }
|
||||||
|
|
||||||
|
var ancestors = tagImage.ancestors.split('/');
|
||||||
|
for (var i = 0; i < ancestors.length; ++i) {
|
||||||
|
var image = this.imageMap_[ancestors[i]];
|
||||||
|
if (image) {
|
||||||
|
callback(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.getTotalSize = function(tag) {
|
||||||
|
var size = 0;
|
||||||
|
this.forAllTagImages(tag, function(image) {
|
||||||
|
size += image.size;
|
||||||
|
});
|
||||||
|
return size;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryImageTracker.prototype.getImagesForTagBySize = function(tag) {
|
||||||
|
var images = [];
|
||||||
|
this.forAllTagImages(tag, function(image) {
|
||||||
|
images.push(image);
|
||||||
|
});
|
||||||
|
|
||||||
|
images.sort(function(a, b) {
|
||||||
|
return b.size - a.size;
|
||||||
|
});
|
||||||
|
|
||||||
|
return images;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-view/repo-panel-changes.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'selectedTags': '=selectedTags',
|
||||||
|
'isEnabled': '=isEnabled'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, $timeout, ApiService, UtilService, ImageMetadataService) {
|
||||||
|
|
||||||
|
var update = function() {
|
||||||
|
if (!$scope.repository || !$scope.selectedTags) { return; }
|
||||||
|
|
||||||
|
$scope.currentImage = null;
|
||||||
|
$scope.currentTag = null;
|
||||||
|
|
||||||
|
if (!$scope.imagesResource) {
|
||||||
|
loadImages();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('selectedTags', update)
|
||||||
|
$scope.$watch('repository', update);
|
||||||
|
|
||||||
|
$scope.$watch('isEnabled', function(isEnabled) {
|
||||||
|
if (isEnabled) {
|
||||||
|
refreshTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var refreshTree = function() {
|
||||||
|
if (!$scope.repository || !$scope.images) { return; }
|
||||||
|
|
||||||
|
$('#image-history-container').empty();
|
||||||
|
|
||||||
|
var tree = new ImageHistoryTree(
|
||||||
|
$scope.repository.namespace,
|
||||||
|
$scope.repository.name,
|
||||||
|
$scope.images,
|
||||||
|
UtilService.getFirstMarkdownLineAsText,
|
||||||
|
$scope.getTimeSince,
|
||||||
|
ImageMetadataService.getEscapedFormattedCommand,
|
||||||
|
function(tag) {
|
||||||
|
return $.inArray(tag, $scope.selectedTags) >= 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.tree = tree.draw('image-history-container');
|
||||||
|
if ($scope.tree) {
|
||||||
|
// Give enough time for the UI to be drawn before we resize the tree.
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.tree.notifyResized();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Listen for changes to the selected tag and image in the tree.
|
||||||
|
$($scope.tree).bind('tagChanged', function(e) {
|
||||||
|
$scope.$apply(function() { $scope.setTag(e.tag); });
|
||||||
|
});
|
||||||
|
|
||||||
|
$($scope.tree).bind('imageChanged', function(e) {
|
||||||
|
$scope.$apply(function() { $scope.setImage(e.image.id); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadImages = function(opt_callback) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||||
|
$scope.images = resp.images;
|
||||||
|
$scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images);
|
||||||
|
|
||||||
|
if ($scope.selectedTags && $scope.selectedTags.length) {
|
||||||
|
refreshTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_callback && opt_callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setImage = function(image_id) {
|
||||||
|
$scope.currentTag = null;
|
||||||
|
$scope.currentImage = image_id;
|
||||||
|
$scope.tree.setImage(image_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setTag = function(tag) {
|
||||||
|
$scope.currentTag = tag;
|
||||||
|
$scope.currentImage = null;
|
||||||
|
$scope.tree.setTag(tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.parseDate = function(dateString) {
|
||||||
|
return Date.parse(dateString);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getTimeSince = function(createdTime) {
|
||||||
|
return moment($scope.parseDate(createdTime)).fromNow();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleTagChanged = function(data) {
|
||||||
|
$scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images);
|
||||||
|
|
||||||
|
data.removed.map(function(tag) {
|
||||||
|
$scope.currentImage = null;
|
||||||
|
$scope.currentTag = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
data.added.map(function(tag) {
|
||||||
|
$scope.selectedTags.push(tag);
|
||||||
|
$scope.currentTag = tag;
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshTree();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
24
static/js/directives/repo-view/repo-panel-info.js
Normal file
24
static/js/directives/repo-view/repo-panel-info.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the information panel for a repository view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoPanelInfo', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-view/repo-panel-info.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'builds': '=builds'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService) {
|
||||||
|
$scope.updateDescription = function(content) {
|
||||||
|
$scope.repository.description = content;
|
||||||
|
$scope.repository.put();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
84
static/js/directives/repo-view/repo-panel-settings.js
Normal file
84
static/js/directives/repo-view/repo-panel-settings.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the settings panel for a repository view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoPanelSettings', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-view/repo-panel-settings.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService, Config) {
|
||||||
|
$scope.getBadgeFormat = function(format, repository) {
|
||||||
|
if (!repository) { return ''; }
|
||||||
|
|
||||||
|
var imageUrl = Config.getUrl('/repository/' + repository.namespace + '/' + repository.name + '/status');
|
||||||
|
if (!$scope.repository.is_public) {
|
||||||
|
imageUrl += '?token=' + repository.status_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkUrl = Config.getUrl('/repository/' + repository.namespace + '/' + repository.name);
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case 'svg':
|
||||||
|
return imageUrl;
|
||||||
|
|
||||||
|
case 'md':
|
||||||
|
return '[](' + linkUrl + ')';
|
||||||
|
|
||||||
|
case 'asciidoc':
|
||||||
|
return 'image:' + imageUrl + '["Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '", link="' + linkUrl + '"]';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askDelete = function() {
|
||||||
|
bootbox.confirm('Are you sure you want delete this repository?', function(r) {
|
||||||
|
if (!r) { return; }
|
||||||
|
$scope.deleteRepo();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteRepo = function() {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.deleteRepository(null, params).then(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
document.location = '/repository/';
|
||||||
|
}, 100);
|
||||||
|
}, ApiService.errorDisplay('Could not delete repository'));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.askChangeAccess = function(newAccess) {
|
||||||
|
bootbox.confirm('Are you sure you want to make this repository ' + newAccess + '?', function(r) {
|
||||||
|
if (!r) { return; }
|
||||||
|
$scope.changeAccess(newAccess);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.changeAccess = function(newAccess) {
|
||||||
|
var visibility = {
|
||||||
|
'visibility': newAccess
|
||||||
|
};
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.changeRepoVisibility(visibility, params).then(function() {
|
||||||
|
$scope.repository.is_public = newAccess == 'public';
|
||||||
|
}, ApiService.errorDisplay('Could not change repository visibility'));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
213
static/js/directives/repo-view/repo-panel-tags.js
Normal file
213
static/js/directives/repo-view/repo-panel-tags.js
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the tags panel for a repository view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoPanelTags', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-view/repo-panel-tags.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'selectedTags': '=selectedTags'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, $filter, $location, ApiService, UIService) {
|
||||||
|
var orderBy = $filter('orderBy');
|
||||||
|
|
||||||
|
$scope.checkedTags = UIService.createCheckStateController([]);
|
||||||
|
$scope.options = {
|
||||||
|
'predicate': 'last_modified_datetime',
|
||||||
|
'reverse': false
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.iterationState = {};
|
||||||
|
$scope.tagActionHandler = null;
|
||||||
|
|
||||||
|
var loadImages = function() {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||||
|
$scope.images = resp.images;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var setTagState = function() {
|
||||||
|
if (!$scope.repository || !$scope.selectedTags) { return; }
|
||||||
|
|
||||||
|
var tags = [];
|
||||||
|
var allTags = [];
|
||||||
|
var imageMap = {};
|
||||||
|
var imageTracks = [];
|
||||||
|
|
||||||
|
// Build a list of tags and filtered tags.
|
||||||
|
for (var tag in $scope.repository.tags) {
|
||||||
|
if (!$scope.repository.tags.hasOwnProperty(tag)) { continue; }
|
||||||
|
|
||||||
|
var tagInfo = $.extend($scope.repository.tags[tag], {
|
||||||
|
'name': tag,
|
||||||
|
'last_modified_datetime': new Date($scope.repository.tags[tag].last_modified)
|
||||||
|
});
|
||||||
|
|
||||||
|
allTags.push(tagInfo);
|
||||||
|
|
||||||
|
if (!$scope.options.tagFilter || tag.indexOf($scope.options.tagFilter) >= 0 ||
|
||||||
|
tagInfo.image_id.indexOf($scope.options.tagFilter) >= 0) {
|
||||||
|
tags.push(tagInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the tags by the predicate and the reverse, and map the information.
|
||||||
|
var imageIDs = [];
|
||||||
|
var ordered = orderBy(tags, $scope.options.predicate, $scope.options.reverse);
|
||||||
|
var checked = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < ordered.length; ++i) {
|
||||||
|
var tagInfo = ordered[i];
|
||||||
|
if (!imageMap[tagInfo.image_id]) {
|
||||||
|
imageMap[tagInfo.image_id] = [];
|
||||||
|
imageIDs.push(tagInfo.image_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
imageMap[tagInfo.image_id].push(tagInfo);
|
||||||
|
|
||||||
|
if ($.inArray(tagInfo.name, $scope.selectedTags) >= 0) {
|
||||||
|
checked.push(tagInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the image tracks.
|
||||||
|
var colors = d3.scale.category10();
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
imageIDs.sort().map(function(image_id) {
|
||||||
|
if (imageMap[image_id].length >= 2){
|
||||||
|
imageTracks.push({
|
||||||
|
'image_id': image_id,
|
||||||
|
'color': colors(index),
|
||||||
|
'count': imageMap[image_id].length,
|
||||||
|
'tags': imageMap[image_id]
|
||||||
|
});
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.imageMap = imageMap;
|
||||||
|
$scope.imageTracks = imageTracks;
|
||||||
|
|
||||||
|
$scope.tags = ordered;
|
||||||
|
$scope.allTags = allTags;
|
||||||
|
|
||||||
|
$scope.checkedTags = UIService.createCheckStateController(ordered, checked);
|
||||||
|
$scope.checkedTags.listen(function(checked) {
|
||||||
|
$scope.selectedTags = checked.map(function(tag_info) {
|
||||||
|
return tag_info.name;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.$watch('options.predicate', setTagState);
|
||||||
|
$scope.$watch('options.reverse', setTagState);
|
||||||
|
$scope.$watch('options.tagFilter', setTagState);
|
||||||
|
|
||||||
|
$scope.$watch('selectedTags', function(selectedTags) {
|
||||||
|
if (!selectedTags || !$scope.repository || !$scope.imageMap) { return; }
|
||||||
|
|
||||||
|
$scope.checkedTags.checked = selectedTags.map(function(tag) {
|
||||||
|
return $scope.repository.tags[tag];
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
$scope.$watch('repository', function(repository) {
|
||||||
|
if (!repository) { return; }
|
||||||
|
|
||||||
|
// Load the repository's images.
|
||||||
|
loadImages();
|
||||||
|
|
||||||
|
// Process each of the tags.
|
||||||
|
setTagState();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.trackLineClass = function(index, track_info) {
|
||||||
|
var startIndex = $.inArray(track_info.tags[0], $scope.tags);
|
||||||
|
var endIndex = $.inArray(track_info.tags[track_info.tags.length - 1], $scope.tags);
|
||||||
|
|
||||||
|
if (index == startIndex) {
|
||||||
|
return 'start';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == endIndex) {
|
||||||
|
return 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > startIndex && index < endIndex) {
|
||||||
|
return 'middle';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < startIndex) {
|
||||||
|
return 'before';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > endIndex) {
|
||||||
|
return 'after';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.tablePredicateClass = function(name, predicate, reverse) {
|
||||||
|
if (name != predicate) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'current ' + (reverse ? 'reversed' : '');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askDeleteTag = function(tag) {
|
||||||
|
$scope.tagActionHandler.askDeleteTag(tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askDeleteMultipleTags = function(tags) {
|
||||||
|
if (tags.length == 1) {
|
||||||
|
$scope.askDeleteTag(tags[0].name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.tagActionHandler.askDeleteMultipleTags(tags);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.orderBy = function(predicate) {
|
||||||
|
if (predicate == $scope.options.predicate) {
|
||||||
|
$scope.options.reverse = !$scope.options.reverse;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.options.reverse = false;
|
||||||
|
$scope.options.predicate = predicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.commitTagFilter = function(tag) {
|
||||||
|
var r = new RegExp('^[0-9a-f]{7}$');
|
||||||
|
return tag.name.match(r);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.allTagFilter = function(tag) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.noTagFilter = function(tag) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.imageIDFilter = function(image_id, tag) {
|
||||||
|
return tag.image_id == image_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setTab = function(tab) {
|
||||||
|
$location.search('tab', tab);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
|
@ -10,7 +10,8 @@ angular.module('quay').directive('buildInfoBar', function () {
|
||||||
restrict: 'C',
|
restrict: 'C',
|
||||||
scope: {
|
scope: {
|
||||||
'build': '=build',
|
'build': '=build',
|
||||||
'showTime': '=showTime'
|
'showTime': '=showTime',
|
||||||
|
'hideId': '=hideId'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element) {
|
controller: function($scope, $element) {
|
||||||
}
|
}
|
||||||
|
|
22
static/js/directives/ui/build-mini-status.js
Normal file
22
static/js/directives/ui/build-mini-status.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* An element which displays the status of a build as a mini-bar.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('buildMiniStatus', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/build-mini-status.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'build': '=build'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.isBuilding = function(build) {
|
||||||
|
if (!build) { return true; }
|
||||||
|
return build.phase != 'complete' && build.phase != 'error';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
22
static/js/directives/ui/build-state-icon.js
Normal file
22
static/js/directives/ui/build-state-icon.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* An element which displays an icon representing the state of the build.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('buildStateIcon', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/build-state-icon.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'build': '=build'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.isBuilding = function(build) {
|
||||||
|
if (!build) { return true; }
|
||||||
|
return build.phase != 'complete' && build.phase != 'error';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* An element which displays the status of a build.
|
* DEPRECATED: An element which displays the status of a build.
|
||||||
*/
|
*/
|
||||||
angular.module('quay').directive('buildStatus', function () {
|
angular.module('quay').directive('buildStatus', function () {
|
||||||
var directiveDefinitionObject = {
|
var directiveDefinitionObject = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* An element which displays controls and information about a defined external notification on
|
* DEPRECATED: An element which displays controls and information about a defined external notification on
|
||||||
* a repository.
|
* a repository.
|
||||||
*/
|
*/
|
||||||
angular.module('quay').directive('externalNotificationView', function () {
|
angular.module('quay').directive('externalNotificationView', function () {
|
||||||
|
|
22
static/js/directives/ui/filter-control.js
Normal file
22
static/js/directives/ui/filter-control.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a link to change a lookup filter, and shows whether it is selected.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('filterControl', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/filter-control.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'filter': '=filter',
|
||||||
|
'value': '@value'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.setFilter = function() {
|
||||||
|
$scope.filter = $scope.value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
34
static/js/directives/ui/image-changes-view.js
Normal file
34
static/js/directives/ui/image-changes-view.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* An element which loads and displays UI representing the changes made in an image.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('imageChangesView', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/image-changes-view.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'image': '=image',
|
||||||
|
'hasChanges': '=hasChanges'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService) {
|
||||||
|
$scope.$watch('image', function() {
|
||||||
|
if (!$scope.image || !$scope.repository) { return; }
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'image_id': $scope.image
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.hasChanges = true;
|
||||||
|
$scope.imageChangesResource = ApiService.getImageChangesAsResource(params).get(function(resp) {
|
||||||
|
$scope.changeData = resp;
|
||||||
|
$scope.hasChanges = resp.added.length || resp.removed.length || resp.changed.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
32
static/js/directives/ui/image-info-sidebar.js
Normal file
32
static/js/directives/ui/image-info-sidebar.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* An element which displays sidebar information for a image. Primarily used in the repo view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('imageInfoSidebar', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/image-info-sidebar.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'tracker': '=tracker',
|
||||||
|
'image': '=image',
|
||||||
|
|
||||||
|
'tagSelected': '&tagSelected',
|
||||||
|
'addTagRequested': '&addTagRequested'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ImageMetadataService) {
|
||||||
|
$scope.$watch('image', function(image) {
|
||||||
|
if (!image || !$scope.tracker) { return; }
|
||||||
|
$scope.imageData = $scope.tracker.lookupImage(image);
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.parseDate = function(dateString) {
|
||||||
|
return Date.parse(dateString);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -13,7 +13,7 @@ angular.module('quay').directive('repoListGrid', function () {
|
||||||
starred: '=starred',
|
starred: '=starred',
|
||||||
user: "=user",
|
user: "=user",
|
||||||
namespace: '=namespace',
|
namespace: '=namespace',
|
||||||
toggleStar: '&toggleStar'
|
starToggled: '&starToggled'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, UserService) {
|
controller: function($scope, $element, UserService) {
|
||||||
$scope.isOrganization = function(namespace) {
|
$scope.isOrganization = function(namespace) {
|
||||||
|
|
53
static/js/directives/ui/repo-star.js
Normal file
53
static/js/directives/ui/repo-star.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* An element that displays the star status of a repository and allows it to be toggled.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repoStar', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repo-star.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
repository: '=repository',
|
||||||
|
starToggled: '&starToggled'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, UserService, ApiService) {
|
||||||
|
// Star a repository or unstar a repository.
|
||||||
|
$scope.toggleStar = function() {
|
||||||
|
if ($scope.repository.is_starred) {
|
||||||
|
unstarRepo();
|
||||||
|
} else {
|
||||||
|
starRepo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Star a repository and update the UI.
|
||||||
|
var starRepo = function() {
|
||||||
|
var data = {
|
||||||
|
'namespace': $scope.repository.namespace,
|
||||||
|
'repository': $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.createStar(data).then(function(result) {
|
||||||
|
$scope.repository.is_starred = true;
|
||||||
|
$scope.starToggled({'repository': $scope.repository});
|
||||||
|
}, ApiService.errorDisplay('Could not star repository'));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unstar a repository and update the UI.
|
||||||
|
var unstarRepo = function(repo) {
|
||||||
|
var data = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.deleteStar(null, data).then(function(result) {
|
||||||
|
$scope.repository.is_starred = false;
|
||||||
|
$scope.starToggled({'repository': $scope.repository});
|
||||||
|
}, ApiService.errorDisplay('Could not unstar repository'));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
91
static/js/directives/ui/repository-events-table.js
Normal file
91
static/js/directives/ui/repository-events-table.js
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a table of events on a repository and allows them to be
|
||||||
|
* edited.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repositoryEventsTable', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repository-events-table.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService, Restangular, UtilService, ExternalNotificationData) {
|
||||||
|
$scope.showNewNotificationCounter = 0;
|
||||||
|
|
||||||
|
var loadNotifications = function() {
|
||||||
|
if (!$scope.repository || $scope.notificationsResource) { return; }
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.notificationsResource = ApiService.listRepoNotificationsAsResource(params).get(
|
||||||
|
function(resp) {
|
||||||
|
$scope.notifications = resp.notifications;
|
||||||
|
return $scope.notifications;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('repository', loadNotifications);
|
||||||
|
loadNotifications();
|
||||||
|
|
||||||
|
$scope.handleNotificationCreated = function(notification) {
|
||||||
|
$scope.notifications.push(notification);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.askCreateNotification = function() {
|
||||||
|
$scope.showNewNotificationCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getEventInfo = function(notification) {
|
||||||
|
return ExternalNotificationData.getEventInfo(notification.event);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getMethodInfo = function(notification) {
|
||||||
|
return ExternalNotificationData.getMethodInfo(notification.method);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteNotification = function(notification) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'uuid': notification.uuid
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.deleteRepoNotification(null, params).then(function() {
|
||||||
|
var index = $.inArray(notification, $scope.notifications);
|
||||||
|
if (index < 0) { return; }
|
||||||
|
$scope.notifications.splice(index, 1);
|
||||||
|
}, ApiService.errorDisplay('Cannot delete notification'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showWebhookInfo = function(notification) {
|
||||||
|
var eventId = notification.event;
|
||||||
|
document.location = 'http://docs.quay.io/guides/notifications.html#webhook_' + eventId;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.testNotification = function(notification) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'uuid': notification.uuid
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.testRepoNotification(null, params).then(function() {
|
||||||
|
bootbox.dialog({
|
||||||
|
"title": "Test Notification Queued",
|
||||||
|
"message": "A test version of this notification has been queued and should appear shortly",
|
||||||
|
"buttons": {
|
||||||
|
"close": {
|
||||||
|
"label": "Close",
|
||||||
|
"className": "btn-primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, ApiService.errorDisplay('Could not issue test notification'));
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
147
static/js/directives/ui/repository-permissions-table.js
Normal file
147
static/js/directives/ui/repository-permissions-table.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a table of permissions on a repository and allows them to be
|
||||||
|
* edited.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repositoryPermissionsTable', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repository-permissions-table.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService, Restangular, UtilService) {
|
||||||
|
$scope.roles = [
|
||||||
|
{ 'id': 'read', 'title': 'Read', 'kind': 'success' },
|
||||||
|
{ 'id': 'write', 'title': 'Write', 'kind': 'success' },
|
||||||
|
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
|
||||||
|
];
|
||||||
|
|
||||||
|
$scope.permissionResources = {'team': {}, 'user': {}};
|
||||||
|
$scope.permissionCache = {};
|
||||||
|
$scope.permissions = {};
|
||||||
|
$scope.addPermissionInfo = {};
|
||||||
|
|
||||||
|
var loadAllPermissions = function() {
|
||||||
|
if (!$scope.repository) { return; }
|
||||||
|
fetchPermissions('user');
|
||||||
|
fetchPermissions('team');
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchPermissions = function(kind) {
|
||||||
|
if ($scope.permissionResources[kind]['loading'] != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
var Kind = kind[0].toUpperCase() + kind.substring(1);
|
||||||
|
var r = ApiService['listRepo' + Kind + 'PermissionsAsResource'](params).get(function(resp) {
|
||||||
|
$scope.permissions[kind] = resp.permissions;
|
||||||
|
return resp.permissions;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.permissionResources[kind] = r;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('repository', loadAllPermissions);
|
||||||
|
loadAllPermissions();
|
||||||
|
|
||||||
|
var getPermissionEndpoint = function(entityName, kind) {
|
||||||
|
var namespace = $scope.repository.namespace;
|
||||||
|
var name = $scope.repository.name;
|
||||||
|
var url = UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName);
|
||||||
|
return Restangular.one(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.buildEntityForPermission = function(name, permission, kind) {
|
||||||
|
var key = name + ':' + kind;
|
||||||
|
if ($scope.permissionCache[key]) {
|
||||||
|
return $scope.permissionCache[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $scope.permissionCache[key] = {
|
||||||
|
'kind': kind,
|
||||||
|
'name': name,
|
||||||
|
'is_robot': permission.is_robot,
|
||||||
|
'is_org_member': permission.is_org_member
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addPermission = function() {
|
||||||
|
$scope.addPermissionInfo['working'] = true;
|
||||||
|
$scope.addNewPermission($scope.addPermissionInfo.entity, $scope.addPermissionInfo.role)
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.grantPermission = function(entity, callback) {
|
||||||
|
$scope.addRole(entity.name, 'read', entity.kind, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addNewPermission = function(entity, opt_role) {
|
||||||
|
// Don't allow duplicates.
|
||||||
|
if (!entity || !entity.kind || $scope.permissions[entity.kind][entity.name]) {
|
||||||
|
$scope.addPermissionInfo = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.is_org_member === false) {
|
||||||
|
$scope.grantPermissionInfo = {
|
||||||
|
'entity': entity
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.addRole(entity.name, opt_role || 'read', entity.kind);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteRole = function(entityName, kind) {
|
||||||
|
var errorHandler = ApiService.errorDisplay('Cannot change permission', function(resp) {
|
||||||
|
if (resp.status == 409) {
|
||||||
|
return 'Cannot change permission as you do not have the authority';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var endpoint = getPermissionEndpoint(entityName, kind);
|
||||||
|
endpoint.customDELETE().then(function() {
|
||||||
|
delete $scope.permissions[kind][entityName];
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addRole = function(entityName, role, kind, opt_callback) {
|
||||||
|
var permission = {
|
||||||
|
'role': role,
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorHandler = ApiService.errorDisplay('Cannot change permission', function() {
|
||||||
|
opt_callback && opt_callback(false);
|
||||||
|
$scope.addPermissionInfo = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
var endpoint = getPermissionEndpoint(entityName, kind);
|
||||||
|
endpoint.customPUT(permission).then(function(result) {
|
||||||
|
$scope.permissions[kind][entityName] = result;
|
||||||
|
$scope.addPermissionInfo = {};
|
||||||
|
opt_callback && opt_callback(true)
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setRole = function(role, entityName, kind) {
|
||||||
|
var errorDisplay = ApiService.errorDisplay(function(resp) {
|
||||||
|
$scope.permissions[kind][entityName] = {'role': currentRole};
|
||||||
|
});
|
||||||
|
|
||||||
|
var permission = $scope.permissions[kind][entityName];
|
||||||
|
var currentRole = permission.role;
|
||||||
|
permission.role = role;
|
||||||
|
|
||||||
|
var endpoint = getPermissionEndpoint(entityName, kind);
|
||||||
|
endpoint.customPUT(permission).then(function() {}, errorDisplay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
68
static/js/directives/ui/repository-tokens-table.js
Normal file
68
static/js/directives/ui/repository-tokens-table.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a table of tokens on a repository and allows them to be
|
||||||
|
* edited.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('repositoryTokensTable', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/repository-tokens-table.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'hasTokens': '=hasTokens'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, ApiService, Restangular, UtilService) {
|
||||||
|
$scope.roles = [
|
||||||
|
{ 'id': 'read', 'title': 'Read', 'kind': 'success' },
|
||||||
|
{ 'id': 'write', 'title': 'Write', 'kind': 'success' },
|
||||||
|
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
|
||||||
|
];
|
||||||
|
|
||||||
|
$scope.hasTokens = false;
|
||||||
|
|
||||||
|
var loadTokens = function() {
|
||||||
|
if (!$scope.repository || $scope.tokensResource) { return; }
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.tokensResource = ApiService.listRepoTokensAsResource(params).get(function(resp) {
|
||||||
|
$scope.tokens = resp.tokens;
|
||||||
|
$scope.hasTokens = Object.keys($scope.tokens).length >= 1;
|
||||||
|
}, ApiService.errorDisplay('Could not load access tokens'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('repository', loadTokens);
|
||||||
|
loadTokens();
|
||||||
|
|
||||||
|
$scope.deleteToken = function(tokenCode) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'code': tokenCode
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.deleteToken(null, params).then(function() {
|
||||||
|
delete $scope.tokens[tokenCode];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.changeTokenAccess = function(tokenCode, newAccess) {
|
||||||
|
var role = {
|
||||||
|
'role': newAccess
|
||||||
|
};
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'code': tokenCode
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.changeToken(role, params).then(function(updated) {
|
||||||
|
$scope.tokens[updated.code] = updated;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -21,13 +21,17 @@ angular.module('quay').directive('resourceView', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
var resources = $scope.resources || [$scope.resource];
|
var resources = $scope.resources || [$scope.resource];
|
||||||
|
if (!resources.length) {
|
||||||
|
return 'loading';
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < resources.length; ++i) {
|
for (var i = 0; i < resources.length; ++i) {
|
||||||
var current = resources[i];
|
var current = resources[i];
|
||||||
if (current.loading) {
|
if (current.loading) {
|
||||||
return 'loading';
|
return 'loading';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.error) {
|
if (current.hasError) {
|
||||||
return 'error';
|
return 'error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
static/js/directives/ui/tag-info-sidebar.js
Normal file
28
static/js/directives/ui/tag-info-sidebar.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* An element which displays sidebar information for a tag. Primarily used in the repo view.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('tagInfoSidebar', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/tag-info-sidebar.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'tracker': '=tracker',
|
||||||
|
'tag': '=tag',
|
||||||
|
|
||||||
|
'imageSelected': '&imageSelected',
|
||||||
|
'deleteTagRequested': '&deleteTagRequested'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.$watch('tag', function(tag) {
|
||||||
|
if (!tag || !$scope.tracker) { return; }
|
||||||
|
|
||||||
|
$scope.tagImage = $scope.tracker.getImageForTag(tag);
|
||||||
|
$scope.tagData = $scope.tracker.lookupTag(tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
148
static/js/directives/ui/tag-operations-dialog.js
Normal file
148
static/js/directives/ui/tag-operations-dialog.js
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
* An element which adds a series of dialogs for performing operations for tags (adding, moving
|
||||||
|
* deleting).
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/tag-operations-dialog.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'repository': '=repository',
|
||||||
|
'images': '=images',
|
||||||
|
'actionHandler': '=actionHandler',
|
||||||
|
|
||||||
|
'tagChanged': '&tagChanged'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, $timeout, ApiService) {
|
||||||
|
$scope.addingTag = false;
|
||||||
|
|
||||||
|
var markChanged = function(added, removed) {
|
||||||
|
// Reload the repository and the images.
|
||||||
|
$scope.repository.get().then(function(resp) {
|
||||||
|
$scope.repository = resp;
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': resp.namespace + '/' + resp.name
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.listRepositoryImages(null, params).then(function(resp) {
|
||||||
|
$scope.images = resp.images;
|
||||||
|
|
||||||
|
// Note: We need the timeout here so that Angular can $digest the images change
|
||||||
|
// on the parent scope before the tagChanged callback occurs.
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.tagChanged({
|
||||||
|
'data': { 'added': added, 'removed': removed }
|
||||||
|
});
|
||||||
|
}, 1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.isAnotherImageTag = function(image, tag) {
|
||||||
|
if (!$scope.repository || !$scope.images) { return; }
|
||||||
|
|
||||||
|
var found = $scope.repository.tags[tag];
|
||||||
|
if (found == null) { return false; }
|
||||||
|
return found.image_id != image;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.isOwnedTag = function(image, tag) {
|
||||||
|
if (!$scope.repository || !$scope.images) { return; }
|
||||||
|
|
||||||
|
var found = $scope.repository.tags[tag];
|
||||||
|
if (found == null) { return false; }
|
||||||
|
return found.image_id == image;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.createOrMoveTag = function(image, tag) {
|
||||||
|
if (!$scope.repository.can_write) { return; }
|
||||||
|
|
||||||
|
$scope.addingTag = true;
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'tag': tag
|
||||||
|
};
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
'image': image
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorHandler = ApiService.errorDisplay('Cannot create or move tag', function(resp) {
|
||||||
|
$element.find('#createOrMoveTagModal').modal('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
ApiService.changeTagImage(data, params).then(function(resp) {
|
||||||
|
$element.find('#createOrMoveTagModal').modal('hide');
|
||||||
|
$scope.addingTag = false;
|
||||||
|
markChanged([tag], []);
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteMultipleTags = function(tags, callback) {
|
||||||
|
var count = tags.length;
|
||||||
|
var perform = function(index) {
|
||||||
|
if (index >= count) {
|
||||||
|
callback(true);
|
||||||
|
markChanged([], tags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tag_info = tags[index];
|
||||||
|
$scope.deleteTag(tag_info.name, function(result) {
|
||||||
|
if (!result) {
|
||||||
|
callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
perform(index + 1);
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
perform(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteTag = function(tag, callback, opt_skipmarking) {
|
||||||
|
if (!$scope.repository.can_write) { return; }
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||||
|
'tag': tag
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorHandler = ApiService.errorDisplay('Cannot delete tag', callback);
|
||||||
|
ApiService.deleteFullTag(null, params).then(function() {
|
||||||
|
callback(true);
|
||||||
|
!opt_skipmarking && markChanged([], [tag]);
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.actionHandler = {
|
||||||
|
'askDeleteTag': function(tag) {
|
||||||
|
$scope.deleteTagInfo = {
|
||||||
|
'tag': tag
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
'askDeleteMultipleTags': function(tags) {
|
||||||
|
$scope.deleteMultipleTagsInfo = {
|
||||||
|
'tags': tags
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
'askAddTag': function(image) {
|
||||||
|
$scope.tagToCreate = '';
|
||||||
|
$scope.toTagImage = image;
|
||||||
|
$scope.addingTag = false;
|
||||||
|
$scope.addTagForm.$setPristine();
|
||||||
|
$element.find('#createOrMoveTagModal').modal('show');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -31,7 +31,9 @@ var DEPTH_WIDTH = 140;
|
||||||
/**
|
/**
|
||||||
* Based off of http://mbostock.github.io/d3/talk/20111018/tree.html by Mike Bostock (@mbostock)
|
* Based off of http://mbostock.github.io/d3/talk/20111018/tree.html by Mike Bostock (@mbostock)
|
||||||
*/
|
*/
|
||||||
function ImageHistoryTree(namespace, name, images, formatComment, formatTime, formatCommand) {
|
function ImageHistoryTree(namespace, name, images, formatComment, formatTime, formatCommand,
|
||||||
|
opt_tagFilter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The namespace of the repo.
|
* The namespace of the repo.
|
||||||
*/
|
*/
|
||||||
|
@ -62,6 +64,11 @@ function ImageHistoryTree(namespace, name, images, formatComment, formatTime, fo
|
||||||
*/
|
*/
|
||||||
this.formatCommand_ = formatCommand;
|
this.formatCommand_ = formatCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for filtering the tags and image paths displayed in the tree.
|
||||||
|
*/
|
||||||
|
this.tagFilter_ = opt_tagFilter || function() { return true; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current tag (if any).
|
* The current tag (if any).
|
||||||
*/
|
*/
|
||||||
|
@ -153,7 +160,7 @@ ImageHistoryTree.prototype.updateDimensions_ = function() {
|
||||||
$('#' + container).removeOverscroll();
|
$('#' + container).removeOverscroll();
|
||||||
var viewportHeight = $(window).height();
|
var viewportHeight = $(window).height();
|
||||||
var boundingBox = document.getElementById(container).getBoundingClientRect();
|
var boundingBox = document.getElementById(container).getBoundingClientRect();
|
||||||
document.getElementById(container).style.maxHeight = (viewportHeight - boundingBox.top - 150) + 'px';
|
document.getElementById(container).style.maxHeight = (viewportHeight - boundingBox.top - 100) + 'px';
|
||||||
|
|
||||||
this.setupOverscroll_();
|
this.setupOverscroll_();
|
||||||
|
|
||||||
|
@ -526,7 +533,14 @@ ImageHistoryTree.prototype.pruneUnreferenced_ = function(node) {
|
||||||
return node.children.length == 0;
|
return node.children.length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (node.children.length == 0 && node.tags.length == 0);
|
var tags = [];
|
||||||
|
for (var i = 0; i < node.tags.length; ++i) {
|
||||||
|
if (this.tagFilter_(node.tags[i])) {
|
||||||
|
tags.push(node.tags[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (node.children.length == 0 && tags.length == 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -554,7 +568,7 @@ ImageHistoryTree.prototype.collapseNodes_ = function(node) {
|
||||||
var current = node;
|
var current = node;
|
||||||
var previous = node;
|
var previous = node;
|
||||||
var encountered = [];
|
var encountered = [];
|
||||||
while (current.children.length == 1) {
|
while (current.children.length == 1 && current.tags.length == 0) {
|
||||||
encountered.push(current);
|
encountered.push(current);
|
||||||
previous = current;
|
previous = current;
|
||||||
current = current.children[0];
|
current = current.children[0];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function() {
|
(function() {
|
||||||
/**
|
/**
|
||||||
* Repository admin/settings page.
|
* DEPRECATED: Repository admin/settings page.
|
||||||
*/
|
*/
|
||||||
angular.module('quayPages').config(['pages', function(pages) {
|
angular.module('quayPages').config(['pages', function(pages) {
|
||||||
pages.create('repo-admin', 'repo-admin.html', RepoAdminCtrl);
|
pages.create('repo-admin', 'repo-admin.html', RepoAdminCtrl);
|
||||||
|
|
|
@ -52,38 +52,14 @@
|
||||||
return !!UserService.getOrganization(namespace);
|
return !!UserService.getOrganization(namespace);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Star a repository or unstar a repository.
|
$scope.starToggled = function(repo) {
|
||||||
$scope.toggleStar = function(repo) {
|
|
||||||
if (repo.is_starred) {
|
if (repo.is_starred) {
|
||||||
unstarRepo(repo);
|
|
||||||
} else {
|
|
||||||
starRepo(repo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Star a repository and update the UI.
|
|
||||||
var starRepo = function(repo) {
|
|
||||||
var data = {
|
|
||||||
'namespace': repo.namespace,
|
|
||||||
'repository': repo.name
|
|
||||||
};
|
|
||||||
ApiService.createStar(data).then(function(result) {
|
|
||||||
repo.is_starred = true;
|
|
||||||
$scope.starred_repositories.value.push(repo);
|
$scope.starred_repositories.value.push(repo);
|
||||||
}, ApiService.errorDisplay('Could not star repository'));
|
} else {
|
||||||
};
|
|
||||||
|
|
||||||
// Unstar a repository and update the UI.
|
|
||||||
var unstarRepo = function(repo) {
|
|
||||||
var data = {
|
|
||||||
'repository': repo.namespace + '/' + repo.name
|
|
||||||
};
|
|
||||||
ApiService.deleteStar(null, data).then(function(result) {
|
|
||||||
repo.is_starred = false;
|
|
||||||
$scope.starred_repositories.value = $scope.starred_repositories.value.filter(function(repo) {
|
$scope.starred_repositories.value = $scope.starred_repositories.value.filter(function(repo) {
|
||||||
return repo.is_starred;
|
return repo.is_starred;
|
||||||
});
|
});
|
||||||
}, ApiService.errorDisplay('Could not unstar repository'));
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Finds a duplicate repo if it exists. If it doesn't, inserts the repo.
|
// Finds a duplicate repo if it exists. If it doesn't, inserts the repo.
|
||||||
|
|
|
@ -3,10 +3,110 @@
|
||||||
* Repository view page.
|
* Repository view page.
|
||||||
*/
|
*/
|
||||||
angular.module('quayPages').config(['pages', function(pages) {
|
angular.module('quayPages').config(['pages', function(pages) {
|
||||||
pages.create('repo-view', 'repo-view.html', RepoCtrl);
|
pages.create('repo-view', 'repo-view.html', RepoViewCtrl, {
|
||||||
|
'newLayout': true,
|
||||||
|
'title': '{{ namespace }}/{{ name }}',
|
||||||
|
'description': 'Repository {{ namespace }}/{{ name }}'
|
||||||
|
}, ['layout'])
|
||||||
|
|
||||||
|
pages.create('repo-view', 'old-repo-view.html', OldRepoViewCtrl, {
|
||||||
|
}, ['old-layout']);
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiService, $routeParams, $rootScope, $location, $timeout, Config, UtilService) {
|
function RepoViewCtrl($scope, $routeParams, $location, ApiService, UserService, AngularPollChannel) {
|
||||||
|
$scope.namespace = $routeParams.namespace;
|
||||||
|
$scope.name = $routeParams.name;
|
||||||
|
|
||||||
|
$scope.logsShown = 0;
|
||||||
|
$scope.viewScope = {
|
||||||
|
'selectedTags': [],
|
||||||
|
'repository': null,
|
||||||
|
'builds': null,
|
||||||
|
'changesVisible': false
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildPollChannel = null;
|
||||||
|
|
||||||
|
// Make sure we track the current user.
|
||||||
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
|
// Watch the selected tags and update the URL accordingly.
|
||||||
|
$scope.$watch('viewScope.selectedTags', function(selectedTags) {
|
||||||
|
if (!selectedTags || !$scope.viewScope.repository) { return; }
|
||||||
|
|
||||||
|
var tags = filterTags(selectedTags);
|
||||||
|
if (!tags.length) {
|
||||||
|
$location.search('tag', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$location.search('tag', tags.join(','));
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// Watch the repository to filter any tags removed.
|
||||||
|
$scope.$watch('viewScope.repository', function(repository) {
|
||||||
|
if (!repository) { return; }
|
||||||
|
$scope.viewScope.selectedTags = filterTags($scope.viewScope.selectedTags);
|
||||||
|
});
|
||||||
|
|
||||||
|
var filterTags = function(tags) {
|
||||||
|
return (tags || []).filter(function(tag) {
|
||||||
|
return !!$scope.viewScope.repository.tags[tag];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadRepository = function() {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.namespace + '/' + $scope.name
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.repositoryResource = ApiService.getRepoAsResource(params).get(function(repo) {
|
||||||
|
$scope.viewScope.repository = repo;
|
||||||
|
$scope.setTags($routeParams.tag);
|
||||||
|
|
||||||
|
// Track builds.
|
||||||
|
buildPollChannel = AngularPollChannel.create($scope, loadRepositoryBuilds, 5000 /* 5s */);
|
||||||
|
buildPollChannel.start();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadRepositoryBuilds = function(callback) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.namespace + '/' + $scope.name
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorHandler = function() {
|
||||||
|
callback(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.repositoryBuildsResource = ApiService.getRepoBuildsAsResource(params, /* background */true).get(function(resp) {
|
||||||
|
$scope.viewScope.builds = resp.builds;
|
||||||
|
callback(true);
|
||||||
|
}, errorHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the repository.
|
||||||
|
loadRepository();
|
||||||
|
|
||||||
|
$scope.setTags = function(tagNames) {
|
||||||
|
if (!tagNames) {
|
||||||
|
$scope.viewScope.selectedTags = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.viewScope.selectedTags = $.unique(tagNames.split(','));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showLogs = function() {
|
||||||
|
$scope.logsShown++;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleChangesState = function(value) {
|
||||||
|
$scope.viewScope.changesVisible = value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function OldRepoViewCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiService, $routeParams, $rootScope, $location, $timeout, Config, UtilService) {
|
||||||
$scope.Config = Config;
|
$scope.Config = Config;
|
||||||
|
|
||||||
var namespace = $routeParams.namespace;
|
var namespace = $routeParams.namespace;
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
* Helper service for defining the various kinds of build triggers and retrieving information
|
* Helper service for defining the various kinds of build triggers and retrieving information
|
||||||
* about them.
|
* about them.
|
||||||
*/
|
*/
|
||||||
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService',
|
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService', 'Features', 'CookieService',
|
||||||
function(UtilService, $sanitize, KeyService) {
|
function(UtilService, $sanitize, KeyService, Features, CookieService) {
|
||||||
var triggerService = {};
|
var triggerService = {};
|
||||||
|
|
||||||
var triggerTypes = {
|
var triggerTypes = {
|
||||||
|
@ -28,15 +28,46 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
var redirect_uri = KeyService['githubRedirectUri'] + '/trigger/' +
|
var redirect_uri = KeyService['githubRedirectUri'] + '/trigger/' +
|
||||||
namespace + '/' + repository;
|
namespace + '/' + repository;
|
||||||
|
|
||||||
|
// TODO(jschorr): Remove once the new layout is in place.
|
||||||
|
if (CookieService.get('quay.exp-new-layout') == 'true') {
|
||||||
|
redirect_uri += '/__new';
|
||||||
|
}
|
||||||
|
|
||||||
var authorize_url = KeyService['githubTriggerAuthorizeUrl'];
|
var authorize_url = KeyService['githubTriggerAuthorizeUrl'];
|
||||||
var client_id = KeyService['githubTriggerClientId'];
|
var client_id = KeyService['githubTriggerClientId'];
|
||||||
|
|
||||||
return authorize_url + 'client_id=' + client_id +
|
return authorize_url + 'client_id=' + client_id +
|
||||||
'&scope=repo,user:email&redirect_uri=' + redirect_uri;
|
'&scope=repo,user:email&redirect_uri=' + redirect_uri;
|
||||||
|
},
|
||||||
|
|
||||||
|
'is_enabled': function() {
|
||||||
|
return Features.GITHUB_BUILD;
|
||||||
|
},
|
||||||
|
|
||||||
|
'icon': 'fa-github',
|
||||||
|
|
||||||
|
'title': function() {
|
||||||
|
var isEnterprise = KeyService.isEnterprise('github-trigger');
|
||||||
|
if (isEnterprise) {
|
||||||
|
return 'GitHub Enterprise Repository Push';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'GitHub Repository Push';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggerService.getTypes = function() {
|
||||||
|
var types = [];
|
||||||
|
for (var key in triggerTypes) {
|
||||||
|
if (!triggerTypes.hasOwnProperty(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
types.push(key);
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
};
|
||||||
|
|
||||||
triggerService.getRedirectUrl = function(name, namespace, repository) {
|
triggerService.getRedirectUrl = function(name, namespace, repository) {
|
||||||
var type = triggerTypes[name];
|
var type = triggerTypes[name];
|
||||||
if (!type) {
|
if (!type) {
|
||||||
|
@ -45,6 +76,14 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
return type['get_redirect_url'](namespace, repository);
|
return type['get_redirect_url'](namespace, repository);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
triggerService.getTitle = function(name) {
|
||||||
|
var type = triggerTypes[name];
|
||||||
|
if (!type) {
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
return type['title']();
|
||||||
|
};
|
||||||
|
|
||||||
triggerService.getDescription = function(name, config) {
|
triggerService.getDescription = function(name, config) {
|
||||||
var type = triggerTypes[name];
|
var type = triggerTypes[name];
|
||||||
if (!type) {
|
if (!type) {
|
||||||
|
@ -53,6 +92,10 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
||||||
return type['description'](config);
|
return type['description'](config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
triggerService.getMetadata = function(name) {
|
||||||
|
return triggerTypes[name];
|
||||||
|
};
|
||||||
|
|
||||||
triggerService.getRunParameters = function(name, config) {
|
triggerService.getRunParameters = function(name, config) {
|
||||||
var type = triggerTypes[name];
|
var type = triggerTypes[name];
|
||||||
if (!type) {
|
if (!type) {
|
||||||
|
|
|
@ -2,6 +2,61 @@
|
||||||
* Service which provides helper methods for performing some simple UI operations.
|
* Service which provides helper methods for performing some simple UI operations.
|
||||||
*/
|
*/
|
||||||
angular.module('quay').factory('UIService', [function() {
|
angular.module('quay').factory('UIService', [function() {
|
||||||
|
var CheckStateController = function(items, opt_checked) {
|
||||||
|
this.items = items;
|
||||||
|
this.checked = opt_checked || [];
|
||||||
|
this.listeners_ = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.listen = function(callback) {
|
||||||
|
this.listeners_.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.isChecked = function(item) {
|
||||||
|
return $.inArray(item, this.checked) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.toggleItem = function(item) {
|
||||||
|
if (this.isChecked(item)) {
|
||||||
|
this.uncheckItem(item);
|
||||||
|
} else {
|
||||||
|
this.checkItem(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.toggleItems = function() {
|
||||||
|
if (this.checked.length) {
|
||||||
|
this.checked = [];
|
||||||
|
} else {
|
||||||
|
this.checked = this.items.slice();
|
||||||
|
}
|
||||||
|
this.callListeners_();
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.checkByFilter = function(filter) {
|
||||||
|
this.checked = $.grep(this.items, filter);
|
||||||
|
this.callListeners_();
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.checkItem = function(item) {
|
||||||
|
this.checked.push(item);
|
||||||
|
this.callListeners_();
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.uncheckItem = function(item) {
|
||||||
|
this.checked = $.grep(this.checked, function(cItem) {
|
||||||
|
return cItem != item;
|
||||||
|
});
|
||||||
|
this.callListeners_();
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckStateController.prototype.callListeners_ = function() {
|
||||||
|
var checked = this.checked;
|
||||||
|
this.listeners_.map(function(listener) {
|
||||||
|
listener(checked);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var uiService = {};
|
var uiService = {};
|
||||||
|
|
||||||
uiService.hidePopover = function(elem) {
|
uiService.hidePopover = function(elem) {
|
||||||
|
@ -33,5 +88,9 @@ angular.module('quay').factory('UIService', [function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uiService.createCheckStateController = function(items, opt_checked) {
|
||||||
|
return new CheckStateController(items, opt_checked);
|
||||||
|
};
|
||||||
|
|
||||||
return uiService;
|
return uiService;
|
||||||
}]);
|
}]);
|
||||||
|
|
3
static/lib/angular-moment.min.js
vendored
3
static/lib/angular-moment.min.js
vendored
|
@ -1 +1,2 @@
|
||||||
angular.module("angularMoment",[]).directive("amTimeAgo",["$window","$timeout",function(a,b){"use strict";return function(c,d,e){function f(){k&&(b.cancel(k),k=null)}function g(c){d.text(c.fromNow());var e=a.moment().diff(c,"minute"),f=3600;1>e?f=1:60>e?f=30:180>e&&(f=300),k=b(function(){g(c)},1e3*f,!1)}function h(){f(),g(a.moment(i,j))}var i,j,k=null;c.$watch(e.amTimeAgo,function(a){"undefined"!=typeof a&&null!==a&&(angular.isNumber(a)&&(a=new Date(a)),i=a,h())}),e.$observe("amFormat",function(a){j=a,h()}),c.$on("$destroy",function(){f()})}}]).filter("amDateFormat",["$window",function(a){"use strict";return function(b,c){return"undefined"==typeof b||null===b?"":(angular.isNumber(b)&&(b=new Date(b)),a.moment(b).format(c))}}]);
|
"format global";"deps angular";"deps moment";!function(){"use strict";function a(a,b){return a.module("angularMoment",[]).constant("angularMomentConfig",{preprocess:null,timezone:"",format:null,statefulFilters:!0}).constant("moment",b).constant("amTimeAgoConfig",{withoutSuffix:!1,serverTime:null,titleFormat:null}).directive("amTimeAgo",["$window","moment","amMoment","amTimeAgoConfig","angularMomentConfig",function(b,c,d,e,f){return function(g,h,i){function j(){var a;if(e.serverTime){var b=(new Date).getTime(),d=b-u+e.serverTime;a=c(d)}else a=c();return a}function k(){q&&(b.clearTimeout(q),q=null)}function l(a){if(h.text(a.from(j(),s)),t&&!h.attr("title")&&h.attr("title",a.local().format(t)),!x){var c=Math.abs(j().diff(a,"minute")),d=3600;1>c?d=1:60>c?d=30:180>c&&(d=300),q=b.setTimeout(function(){l(a)},1e3*d)}}function m(a){y&&h.attr("datetime",a)}function n(){if(k(),o){var a=d.preprocessDate(o,v,r);l(a),m(a.toISOString())}}var o,p,q=null,r=f.format,s=e.withoutSuffix,t=e.titleFormat,u=(new Date).getTime(),v=f.preprocess,w=i.amTimeAgo.replace(/^::/,""),x=0===i.amTimeAgo.indexOf("::"),y="TIME"===h[0].nodeName.toUpperCase();p=g.$watch(w,function(a){return"undefined"==typeof a||null===a||""===a?(k(),void(o&&(h.text(""),m(""),o=null))):(o=a,n(),void(void 0!==a&&x&&p()))}),a.isDefined(i.amWithoutSuffix)&&g.$watch(i.amWithoutSuffix,function(a){"boolean"==typeof a?(s=a,n()):s=e.withoutSuffix}),i.$observe("amFormat",function(a){"undefined"!=typeof a&&(r=a,n())}),i.$observe("amPreprocess",function(a){v=a,n()}),g.$on("$destroy",function(){k()}),g.$on("amMoment:localeChanged",function(){n()})}}]).service("amMoment",["moment","$rootScope","$log","angularMomentConfig",function(b,c,d,e){this.preprocessors={utc:b.utc,unix:b.unix},this.changeLocale=function(d,e){var f=b.locale(d,e);return a.isDefined(d)&&c.$broadcast("amMoment:localeChanged"),f},this.changeTimezone=function(a){e.timezone=a,c.$broadcast("amMoment:timezoneChanged")},this.preprocessDate=function(c,f,g){return a.isUndefined(f)&&(f=e.preprocess),this.preprocessors[f]?this.preprocessors[f](c,g):(f&&d.warn("angular-moment: Ignoring unsupported value for preprocess: "+f),!isNaN(parseFloat(c))&&isFinite(c)?b(parseInt(c,10)):b(c,g))},this.applyTimezone=function(a){var b=e.timezone;return a&&b&&(a.tz?a=a.tz(b):d.warn("angular-moment: timezone specified but moment.tz() is undefined. Did you forget to include moment-timezone.js?")),a}}]).filter("amCalendar",["moment","amMoment","angularMomentConfig",function(a,b,c){function d(c,d){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,d);var e=a(c);return e.isValid()?b.applyTimezone(e).calendar():""}return d.$stateful=c.statefulFilters,d}]).filter("amDateFormat",["moment","amMoment","angularMomentConfig",function(a,b,c){function d(c,d,e){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,e);var f=a(c);return f.isValid()?b.applyTimezone(f).format(d):""}return d.$stateful=c.statefulFilters,d}]).filter("amDurationFormat",["moment","angularMomentConfig",function(a,b){function c(b,c,d){return"undefined"==typeof b||null===b?"":a.duration(b,c).humanize(d)}return c.$stateful=b.statefulFilters,c}]).filter("amTimeAgo",["moment","amMoment","angularMomentConfig",function(a,b,c){function d(c,d,e){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,d);var f=a(c);return f.isValid()?b.applyTimezone(f).fromNow(e):""}return d.$stateful=c.statefulFilters,d}])}"function"==typeof define&&define.amd?define(["angular","moment"],a):"undefined"!=typeof module&&module&&module.exports?a(angular,require("moment")):a(angular,window.moment)}();
|
||||||
|
//# sourceMappingURL=angular-moment.min.js.map
|
425
static/partials/old-repo-view.html
Normal file
425
static/partials/old-repo-view.html
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
<div id="tagContextMenu" class="dropdown clearfix" tabindex="-1">
|
||||||
|
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
|
||||||
|
<li><a tabindex="-1" href="javascript:void(0)" ng-click="askDeleteTag(currentMenuTag)">Delete Tag</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
|
||||||
|
<div class="container-fluid repo repo-view">
|
||||||
|
<!-- Repo Header -->
|
||||||
|
<div class="header">
|
||||||
|
<h3>
|
||||||
|
<span class="repo-circle no-background" repo="repo"></span>
|
||||||
|
<span class="repo-breadcrumb" repo="repo"></span>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="repo-controls">
|
||||||
|
<!-- Builds -->
|
||||||
|
<div class="dropdown" data-placement="top" style="display: inline-block"
|
||||||
|
bs-tooltip=""
|
||||||
|
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
|
||||||
|
quay-show="Features.BUILD_SUPPORT && (repo.can_write || buildHistory.length)">
|
||||||
|
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<i class="fa fa-tasks fa-lg"></i>
|
||||||
|
<span class="count" ng-class="runningBuilds.length ? 'visible' : ''"><span>{{ runningBuilds.length ? runningBuilds.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 ng-show="repo.can_admin">
|
||||||
|
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/admin?tab=trigger">
|
||||||
|
<i class="fa fa-bolt" style="margin-left: 3px; margin-right: 10px;"></i>Build Triggers
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation" class="divider" ng-show="buildHistory && repo.can_write"></li>
|
||||||
|
<li role="presentation" class="dropdown-header" ng-show="buildHistory.length">Recent Builds</li>
|
||||||
|
<li ng-repeat="buildInfo in buildHistory">
|
||||||
|
<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" data-title="Repository Settings" bs-tooltip="tooltip" 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" ng-show="currentPullCommand">
|
||||||
|
<button class="pull-selector dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<i class="fa" ng-class="currentPullCommand.icon"></i>
|
||||||
|
{{ currentPullCommand.shortTitle }}
|
||||||
|
<b class="caret"></b>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="pullCommand in pullCommands">
|
||||||
|
<a href="javascript:void(0)" ng-click="setCurrentPullCommand(pullCommand)"><i class="fa" ng-class="pullCommand.icon"></i>
|
||||||
|
{{ pullCommand.title }}
|
||||||
|
<sup ng-if="pullCommand.experimental">Experimental</sup>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="copy-box" hovering-message="true" value="currentPullCommand.command"></div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<div class="description markdown-input" content="repo.description" can-write="repo.can_write"
|
||||||
|
content-changed="updateForDescription" field-title="'repository description'"></div>
|
||||||
|
|
||||||
|
<!-- Empty messages -->
|
||||||
|
<div ng-if="!currentTag.image_id && !currentImage">
|
||||||
|
<!-- !building && !pushing -->
|
||||||
|
<div class="repo-content" ng-show="!repo.is_building && !isPushing(images)">
|
||||||
|
<div class="empty-message">
|
||||||
|
This repository is empty
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-description" ng-show="repo.can_write">
|
||||||
|
<div class="panel-default">
|
||||||
|
<div class="panel-heading">How to push a new image to this repository:</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
First login to the registry (if you have not done so already):
|
||||||
|
<pre class="command">sudo docker login {{ Config.getDomain() }}</pre>
|
||||||
|
|
||||||
|
Tag an image to this repository:
|
||||||
|
<pre class="command">sudo docker tag <i>0u123imageidgoeshere</i> {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
||||||
|
|
||||||
|
Push the image to this repository:
|
||||||
|
<pre class="command">sudo docker push {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- building -->
|
||||||
|
<div class="repo-content" ng-show="repo.is_building">
|
||||||
|
<div class="empty-message">
|
||||||
|
A build is currently processing. If this takes longer than an hour, please <a href="/contact">contact us</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- pushing -->
|
||||||
|
<div class="repo-content" ng-show="!repo.is_building && isPushing(images)">
|
||||||
|
<div class="empty-message">
|
||||||
|
A push to this repository is in progress.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content view -->
|
||||||
|
<div class="repo-content" ng-show="currentTag.image_id || currentImage">
|
||||||
|
<!-- Image History -->
|
||||||
|
<div id="image-history">
|
||||||
|
<div class="row">
|
||||||
|
<!-- Tree View container -->
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<!-- Image history tree -->
|
||||||
|
<div class="resource-view" resource="imageHistory">
|
||||||
|
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Side Panel -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div id="side-panel" class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<!-- Dropdown -->
|
||||||
|
<div id="side-panel-dropdown" class="tag-dropdown dropdown" data-placement="top">
|
||||||
|
<i class="fa fa-tag current-context-icon" ng-show="currentTag"></i>
|
||||||
|
<i class="fa fa-archive current-context-icon" ng-show="!currentTag"></i>
|
||||||
|
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<span class="current-context">
|
||||||
|
{{currentTag ? currentTag.name : currentImage.id.substr(0, 12)}}
|
||||||
|
</span>
|
||||||
|
<b class="caret"></b></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="tag in repo.tags">
|
||||||
|
<a href="javascript:void(0)" ng-click="setTag(tag.name, true)">
|
||||||
|
<i class="fa fa-tag"></i>{{tag.name}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li ng-repeat="image in imageHistory.value">
|
||||||
|
<a href="javascript:void(0)" ng-click="setImage(image.id, true)">
|
||||||
|
{{image.id.substr(0, 12)}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<span class="right-tag-controls">
|
||||||
|
<i class="fa fa-tag" data-title="Tags" bs-tooltip="title">
|
||||||
|
<span class="tag-count">{{getTagCount(repo)}}</span>
|
||||||
|
</i>
|
||||||
|
<i class="fa fa-archive" data-title="Images" bs-tooltip="title">
|
||||||
|
<span class="tag-count">{{imageHistory.value.length}}</span>
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-body">
|
||||||
|
<!-- Current Tag -->
|
||||||
|
<div id="current-tag" ng-show="currentTag">
|
||||||
|
<dl class="dl-normal">
|
||||||
|
<dt>Last Modified</dt>
|
||||||
|
<dd ng-if="!findImageForTag(currentTag, images)">
|
||||||
|
<span class="quay-spinner"></span>
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dd am-time-ago="parseDate(findImageForTag(currentTag, images).created)"
|
||||||
|
ng-if="findImageForTag(currentTag, images)">
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Total Compressed Size</dt>
|
||||||
|
<dd><span class="context-tooltip"
|
||||||
|
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
||||||
|
bs-tooltip="tooltip.title" data-container="body">{{ getTotalSize(currentTag) | bytes }}</span>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<div class="tag-image-sizes">
|
||||||
|
<div class="tag-image-size" ng-repeat="image in getImagesForTagBySize(currentTag) | limitTo: 10">
|
||||||
|
<span class="size-limiter">
|
||||||
|
<span class="size-bar" style="{{ 'width:' + (image.size / getTotalSize(currentTag)) * 100 + '%' }}"
|
||||||
|
bs-tooltip="" data-title="{{ image.size | bytes }}"></span>
|
||||||
|
</span>
|
||||||
|
<span class="size-title">
|
||||||
|
<a class="image-size-link" href="javascript:void(0)" ng-click="setImage(image.id, true)"
|
||||||
|
data-image="{{ image.id.substr(0, 12) }}">
|
||||||
|
{{ image.id.substr(0, 12) }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-bar" ng-show="repo.can_admin">
|
||||||
|
<button class="btn btn-default" ng-click="askDeleteTag(currentTag.name)">
|
||||||
|
Delete Tag
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Current Image -->
|
||||||
|
<div id="current-image" ng-show="currentImage && !currentTag">
|
||||||
|
<div class="image-comment" ng-if="currentImage.comment">
|
||||||
|
<blockquote style="margin-top: 10px;">
|
||||||
|
<span class="markdown-view" content="currentImage.comment"></span>
|
||||||
|
</blockquote>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-code section-icon" bs-tooltip="tooltip.title" data-title="Full Image ID"></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<a class="image-link" href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">{{ currentImage.id }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-tag section-icon" bs-tooltip="tooltip.title" data-title="Current Tags"></i>
|
||||||
|
<span class="section-info section-info-with-dropdown">
|
||||||
|
<a class="label tag label-default" ng-repeat="tag in currentImage.tags"
|
||||||
|
href="/repository/{{ repo.namespace }}/{{ repo.name }}?tag={{ tag }}">
|
||||||
|
{{ tag }}
|
||||||
|
</a>
|
||||||
|
<span style="color: #ccc;" ng-if="!currentImage.tags.length">(No Tags)</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="dropdown" data-placement="top" ng-if="repo.can_write || currentImage.tags">
|
||||||
|
<a href="javascript:void(0)" class="dropdown-button" data-toggle="dropdown" bs-tooltip="tooltip.title" data-title="Manage Tags"
|
||||||
|
data-container="body">
|
||||||
|
<b class="caret"></b>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu pull-right">
|
||||||
|
<li ng-repeat="tag in currentImage.tags">
|
||||||
|
<a href="javascript:void(0)" ng-click="setTag(tag, true)">
|
||||||
|
<i class="fa fa-tag"></i>{{tag}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider" role="presentation" ng-if="repo.can_write && currentImage.tags"></li>
|
||||||
|
<li>
|
||||||
|
<a href="javascript:void(0)" ng-click="showAddTag(currentImage)" ng-if="repo.can_write">
|
||||||
|
<i class="fa fa-plus"></i>Add New Tag
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section" ng-if="currentImage.command && currentImage.command.length">
|
||||||
|
<i class="fa fa-terminal section-icon" bs-tooltip="tooltip.title" data-title="Image Command"></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<span class="formatted-command trimmed"
|
||||||
|
data-html="true"
|
||||||
|
bs-tooltip="" data-title="{{ getTooltipCommand(currentImage) }}"
|
||||||
|
data-placement="top">{{ getFormattedCommand(currentImage) }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-calendar section-icon" bs-tooltip="tooltip.title" data-title="Created"></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<dd am-time-ago="parseDate(currentImage.created)"></dd>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-cloud-upload section-icon" bs-tooltip="tooltip.title"
|
||||||
|
data-title="The amount of data sent between Docker and the registry when pushing/pulling"></i>
|
||||||
|
<span class="section-info">{{ currentImage.size | bytes }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-section">
|
||||||
|
<i class="fa fa-map-marker section-icon" bs-tooltip="tooltip.title"
|
||||||
|
data-title="The geographic region(s) in which this image data is located"></i>
|
||||||
|
<span class="section-info">
|
||||||
|
<span class="location-view" location="location" ng-repeat="location in currentImage.locations"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image changes loading -->
|
||||||
|
<div class="resource-view" resource="currentImageChangeResource">
|
||||||
|
<div class="changes-container small-changes-container section-info"
|
||||||
|
ng-show="currentImageChanges.changed.length || currentImageChanges.added.length || currentImageChanges.removed.length">
|
||||||
|
<div class="changes-count-container image-section">
|
||||||
|
<i class="fa fa-code-fork section-icon" bs-tooltip="tooltip.title" data-title="File Changes"></i>
|
||||||
|
<div style="float: right; display: inline-block">
|
||||||
|
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" data-title="Files Added"
|
||||||
|
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||||
|
<i class="fa fa-plus-square"></i>
|
||||||
|
<b>{{currentImageChanges.added.length}}</b>
|
||||||
|
</span>
|
||||||
|
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" data-title="Files Removed"
|
||||||
|
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||||
|
<i class="fa fa-minus-square"></i>
|
||||||
|
<b>{{currentImageChanges.removed.length}}</b>
|
||||||
|
</span>
|
||||||
|
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" data-title="Files Changed"
|
||||||
|
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||||
|
<i class="fa fa-pencil-square"></i>
|
||||||
|
<b>{{currentImageChanges.changed.length}}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="collapseChanges" style="padding-top: 24px;">
|
||||||
|
<div class="well well-sm">
|
||||||
|
<div class="change added" ng-repeat="file in currentImageChanges.added | limitTo:2">
|
||||||
|
<i class="fa fa-plus-square"></i>
|
||||||
|
<span title="{{file}}">{{file}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="change removed" ng-repeat="file in currentImageChanges.removed | limitTo:2">
|
||||||
|
<i class="fa fa-minus-square"></i>
|
||||||
|
<span title="{{file}}">{{file}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="change changed" ng-repeat="file in currentImageChanges.changed | limitTo:2">
|
||||||
|
<i class="fa fa-pencil-square"></i>
|
||||||
|
<span title="{{file}}">{{file}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="more-changes" ng-show="getMoreCount(currentImageChanges) > 0">
|
||||||
|
<a href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">
|
||||||
|
And {{getMoreCount(currentImageChanges)}} more...
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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="addTagModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-show="!creatingTag">×</button>
|
||||||
|
<h4 class="modal-title">{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move' : 'Add' }} Tag to Image {{ toTagImage.id.substr(0, 12) }}</h4>
|
||||||
|
</div>
|
||||||
|
<form name="addTagForm" ng-submit="createOrMoveTag(toTagImage, tagToCreate, addTagForm.$invalid); addTagForm.$setPristine(); tagToCreate=''">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="text" class="form-control" id="tagName" placeholder="Enter tag name"
|
||||||
|
ng-model="tagToCreate" ng-pattern="/^([a-z0-9_\.-]){3,30}$/" required
|
||||||
|
ng-disabled="creatingTag" autofocus>
|
||||||
|
<div style="margin: 10px; margin-top: 20px;" ng-show="isOwnedTag(toTagImage, tagToCreate)">
|
||||||
|
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to this image.
|
||||||
|
</div>
|
||||||
|
<div style="margin: 10px; margin-top: 20px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||||
|
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to another image. This will <b>move</b> the tag.
|
||||||
|
</div>
|
||||||
|
<div class="tag-specific-images-view" tag="tagToCreate" repository="repo" images="images" image-cutoff="toTagImage"
|
||||||
|
style="margin: 10px; margin-top: 20px; margin-bottom: -10px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||||
|
This will also delete any unattach images and delete the following images:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary"
|
||||||
|
ng-disabled="!tagToCreate || addTagForm.$invalid || isOwnedTag(toTagImage, tagToCreate)"
|
||||||
|
ng-class="isAnotherImageTag(toTagImage, tagToCreate) ? 'btn-warning' : 'btn-primary'" ng-show="!creatingTag">
|
||||||
|
{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move Tag' : 'Create Tag' }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-default" data-dismiss="modal" ng-show="!creatingTag">Cancel</button>
|
||||||
|
<div class="quay-spinner" ng-show="creatingTag"></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
<!-- Modal message dialog -->
|
||||||
|
<div class="modal fade" id="confirmdeleteTagModal">
|
||||||
|
<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">Delete tag
|
||||||
|
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
||||||
|
{{ tagToDelete }}
|
||||||
|
</span>?
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" ng-show="deletingTag">
|
||||||
|
<div class="quay-spinner"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" ng-show="!deletingTag">
|
||||||
|
Are you sure you want to delete tag
|
||||||
|
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
||||||
|
{{ tagToDelete }}
|
||||||
|
</span>?
|
||||||
|
|
||||||
|
<div class="tag-specific-images-view" tag="tagToDelete" repository="repo" images="images" style="margin-top: 20px">
|
||||||
|
The following images and any other images not referenced by a tag will be deleted:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" ng-show="!deletingTag">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="deleteTag(tagToDelete)">Delete Tag</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
|
@ -50,14 +50,15 @@
|
||||||
<div class="repo-list-panel co-main-content-panel">
|
<div class="repo-list-panel co-main-content-panel">
|
||||||
<!-- Starred Repository Listing -->
|
<!-- Starred Repository Listing -->
|
||||||
<div class="repo-list-grid" repositories-resource="starred_repositories"
|
<div class="repo-list-grid" repositories-resource="starred_repositories"
|
||||||
starred="true" toggle-star="toggleStar(repository)">
|
starred="true"
|
||||||
|
star-toggled="starToggled(repository)">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- User and Org Repository Listings -->
|
<!-- User and Org Repository Listings -->
|
||||||
<div ng-repeat="namespace in namespaces">
|
<div ng-repeat="namespace in namespaces">
|
||||||
<div class="repo-list-grid" repositories-resource="namespace.repositories"
|
<div class="repo-list-grid" repositories-resource="namespace.repositories"
|
||||||
starred="false" user="user" namespace="namespace"
|
starred="false" user="user" namespace="namespace"
|
||||||
toggle-star="toggleStar(repository)">
|
star-toggled="starToggled(repository)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,425 +1,88 @@
|
||||||
<div id="tagContextMenu" class="dropdown clearfix" tabindex="-1">
|
<div class="resource-view repository-view"
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
|
resource="repositoryResource"
|
||||||
<li><a tabindex="-1" href="javascript:void(0)" ng-click="askDeleteTag(currentMenuTag)">Delete Tag</a></li>
|
error-message="'Repository not found'">
|
||||||
</ul>
|
<div class="page-content">
|
||||||
</div>
|
<div class="cor-title">
|
||||||
|
<span class="cor-title-link"></span>
|
||||||
|
<span class="cor-title-content">
|
||||||
|
<span class="repo-circle no-background" repo="viewScope.repository"></span>
|
||||||
|
{{ namespace }} / {{ name }}
|
||||||
|
<span class="repo-star" repository="viewScope.repository" ng-if="!user.anonymous"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
|
<div class="cor-tab-panel">
|
||||||
<div class="container-fluid repo repo-view">
|
<div class="cor-tabs">
|
||||||
<!-- Repo Header -->
|
<span class="cor-tab" tab-active="true" tab-title="Information" tab-target="#info">
|
||||||
<div class="header">
|
<i class="fa fa-info-circle"></i>
|
||||||
<h3>
|
</span>
|
||||||
<span class="repo-circle no-background" repo="repo"></span>
|
|
||||||
<span class="repo-breadcrumb" repo="repo"></span>
|
<span class="cor-tab" tab-title="Tags" tab-target="#tags">
|
||||||
</h3>
|
<i class="fa fa-tags"></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="cor-tab" tab-title="Builds" tab-target="#builds"
|
||||||
|
quay-show="viewScope.repository.can_write && Features.BUILD_SUPPORT">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="cor-tab" tab-title="Visualize" tab-target="#changes"
|
||||||
|
tab-shown="handleChangesState(true)" tab-hidden="handleChangesState(false)">
|
||||||
|
<i class="fa fa-code-fork"></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Admin Only Tabs -->
|
||||||
|
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="showLogs()"
|
||||||
|
ng-if="viewScope.repository.can_admin">
|
||||||
|
<i class="fa fa-bar-chart"></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="cor-tab" tab-title="Settings" tab-target="#settings"
|
||||||
|
ng-if="viewScope.repository.can_admin">
|
||||||
|
<i class="fa fa-gear"></i>
|
||||||
|
</span>
|
||||||
|
</div> <!-- /cor-tabs -->
|
||||||
|
|
||||||
|
<div class="cor-tab-content">
|
||||||
|
<!-- Information -->
|
||||||
|
<div id="info" class="tab-pane active">
|
||||||
|
<div class="repo-panel-info"
|
||||||
|
repository="viewScope.repository"
|
||||||
|
builds="viewScope.builds"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tags -->
|
||||||
|
<div id="tags" class="tab-pane">
|
||||||
|
<div class="repo-panel-tags"
|
||||||
|
repository="viewScope.repository"
|
||||||
|
selected-tags="viewScope.selectedTags"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="repo-controls">
|
|
||||||
<!-- Builds -->
|
<!-- Builds -->
|
||||||
<div class="dropdown" data-placement="top" style="display: inline-block"
|
<div id="builds" class="tab-pane">
|
||||||
bs-tooltip=""
|
<div class="repo-panel-builds"
|
||||||
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
|
repository="viewScope.repository"
|
||||||
quay-show="Features.BUILD_SUPPORT && (repo.can_write || buildHistory.length)">
|
builds="viewScope.builds"></div>
|
||||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
|
||||||
<i class="fa fa-tasks fa-lg"></i>
|
|
||||||
<span class="count" ng-class="runningBuilds.length ? 'visible' : ''"><span>{{ runningBuilds.length ? runningBuilds.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 ng-show="repo.can_admin">
|
|
||||||
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/admin?tab=trigger">
|
|
||||||
<i class="fa fa-bolt" style="margin-left: 3px; margin-right: 10px;"></i>Build Triggers
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li role="presentation" class="divider" ng-show="buildHistory && repo.can_write"></li>
|
|
||||||
<li role="presentation" class="dropdown-header" ng-show="buildHistory.length">Recent Builds</li>
|
|
||||||
<li ng-repeat="buildInfo in buildHistory">
|
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<!-- Admin -->
|
<!-- Changes -->
|
||||||
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}"
|
<div id="changes" class="tab-pane">
|
||||||
ng-show="repo.can_admin">
|
<div class="repo-panel-changes"
|
||||||
<button class="btn btn-default" data-title="Repository Settings" bs-tooltip="tooltip" data-placement="top">
|
repository="viewScope.repository"
|
||||||
<i class="fa fa-cog fa-lg"></i></button></a>
|
selected-tags="viewScope.selectedTags"
|
||||||
|
is-enabled="viewScope.changesVisible"></div>
|
||||||
<!-- Pull Command -->
|
|
||||||
<span class="pull-command visible-md-inline">
|
|
||||||
<div class="pull-container" ng-show="currentPullCommand">
|
|
||||||
<button class="pull-selector dropdown-toggle" data-toggle="dropdown">
|
|
||||||
<i class="fa" ng-class="currentPullCommand.icon"></i>
|
|
||||||
{{ currentPullCommand.shortTitle }}
|
|
||||||
<b class="caret"></b>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li ng-repeat="pullCommand in pullCommands">
|
|
||||||
<a href="javascript:void(0)" ng-click="setCurrentPullCommand(pullCommand)"><i class="fa" ng-class="pullCommand.icon"></i>
|
|
||||||
{{ pullCommand.title }}
|
|
||||||
<sup ng-if="pullCommand.experimental">Experimental</sup>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="copy-box" hovering-message="true" value="currentPullCommand.command"></div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Description -->
|
<!-- Usage Logs -->
|
||||||
<div class="description markdown-input" content="repo.description" can-write="repo.can_write"
|
<div id="logs" class="tab-pane" ng-if="viewScope.repository.can_admin">
|
||||||
content-changed="updateForDescription" field-title="'repository description'"></div>
|
<div class="logs-view" repository="viewScope.repository" makevisible="logsShown"></div>
|
||||||
|
|
||||||
<!-- Empty messages -->
|
|
||||||
<div ng-if="!currentTag.image_id && !currentImage">
|
|
||||||
<!-- !building && !pushing -->
|
|
||||||
<div class="repo-content" ng-show="!repo.is_building && !isPushing(images)">
|
|
||||||
<div class="empty-message">
|
|
||||||
This repository is empty
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="empty-description" ng-show="repo.can_write">
|
<!-- Settings -->
|
||||||
<div class="panel-default">
|
<div id="settings" class="tab-pane" ng-if="viewScope.repository.can_admin">
|
||||||
<div class="panel-heading">How to push a new image to this repository:</div>
|
<div class="repo-panel-settings" repository="viewScope.repository"></div>
|
||||||
<div class="panel-body">
|
|
||||||
First login to the registry (if you have not done so already):
|
|
||||||
<pre class="command">sudo docker login {{ Config.getDomain() }}</pre>
|
|
||||||
|
|
||||||
Tag an image to this repository:
|
|
||||||
<pre class="command">sudo docker tag <i>0u123imageidgoeshere</i> {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
|
||||||
|
|
||||||
Push the image to this repository:
|
|
||||||
<pre class="command">sudo docker push {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- building -->
|
|
||||||
<div class="repo-content" ng-show="repo.is_building">
|
|
||||||
<div class="empty-message">
|
|
||||||
A build is currently processing. If this takes longer than an hour, please <a href="/contact">contact us</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- pushing -->
|
|
||||||
<div class="repo-content" ng-show="!repo.is_building && isPushing(images)">
|
|
||||||
<div class="empty-message">
|
|
||||||
A push to this repository is in progress.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Content view -->
|
|
||||||
<div class="repo-content" ng-show="currentTag.image_id || currentImage">
|
|
||||||
<!-- Image History -->
|
|
||||||
<div id="image-history">
|
|
||||||
<div class="row">
|
|
||||||
<!-- Tree View container -->
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<!-- Image history tree -->
|
|
||||||
<div class="resource-view" resource="imageHistory">
|
|
||||||
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Side Panel -->
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div id="side-panel" class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<!-- Dropdown -->
|
|
||||||
<div id="side-panel-dropdown" class="tag-dropdown dropdown" data-placement="top">
|
|
||||||
<i class="fa fa-tag current-context-icon" ng-show="currentTag"></i>
|
|
||||||
<i class="fa fa-archive current-context-icon" ng-show="!currentTag"></i>
|
|
||||||
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">
|
|
||||||
<span class="current-context">
|
|
||||||
{{currentTag ? currentTag.name : currentImage.id.substr(0, 12)}}
|
|
||||||
</span>
|
|
||||||
<b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li ng-repeat="tag in repo.tags">
|
|
||||||
<a href="javascript:void(0)" ng-click="setTag(tag.name, true)">
|
|
||||||
<i class="fa fa-tag"></i>{{tag.name}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="divider"></li>
|
|
||||||
<li ng-repeat="image in imageHistory.value">
|
|
||||||
<a href="javascript:void(0)" ng-click="setImage(image.id, true)">
|
|
||||||
{{image.id.substr(0, 12)}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<span class="right-tag-controls">
|
|
||||||
<i class="fa fa-tag" data-title="Tags" bs-tooltip="title">
|
|
||||||
<span class="tag-count">{{getTagCount(repo)}}</span>
|
|
||||||
</i>
|
|
||||||
<i class="fa fa-archive" data-title="Images" bs-tooltip="title">
|
|
||||||
<span class="tag-count">{{imageHistory.value.length}}</span>
|
|
||||||
</i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-body">
|
|
||||||
<!-- Current Tag -->
|
|
||||||
<div id="current-tag" ng-show="currentTag">
|
|
||||||
<dl class="dl-normal">
|
|
||||||
<dt>Last Modified</dt>
|
|
||||||
<dd ng-if="!findImageForTag(currentTag, images)">
|
|
||||||
<span class="quay-spinner"></span>
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<dd am-time-ago="parseDate(findImageForTag(currentTag, images).created)"
|
|
||||||
ng-if="findImageForTag(currentTag, images)">
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<dt>Total Compressed Size</dt>
|
|
||||||
<dd><span class="context-tooltip"
|
|
||||||
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
|
||||||
bs-tooltip="tooltip.title" data-container="body">{{ getTotalSize(currentTag) | bytes }}</span>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<div class="tag-image-sizes">
|
|
||||||
<div class="tag-image-size" ng-repeat="image in getImagesForTagBySize(currentTag) | limitTo: 10">
|
|
||||||
<span class="size-limiter">
|
|
||||||
<span class="size-bar" style="{{ 'width:' + (image.size / getTotalSize(currentTag)) * 100 + '%' }}"
|
|
||||||
bs-tooltip="" data-title="{{ image.size | bytes }}"></span>
|
|
||||||
</span>
|
|
||||||
<span class="size-title">
|
|
||||||
<a class="image-size-link" href="javascript:void(0)" ng-click="setImage(image.id, true)"
|
|
||||||
data-image="{{ image.id.substr(0, 12) }}">
|
|
||||||
{{ image.id.substr(0, 12) }}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-bar" ng-show="repo.can_admin">
|
|
||||||
<button class="btn btn-default" ng-click="askDeleteTag(currentTag.name)">
|
|
||||||
Delete Tag
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Current Image -->
|
|
||||||
<div id="current-image" ng-show="currentImage && !currentTag">
|
|
||||||
<div class="image-comment" ng-if="currentImage.comment">
|
|
||||||
<blockquote style="margin-top: 10px;">
|
|
||||||
<span class="markdown-view" content="currentImage.comment"></span>
|
|
||||||
</blockquote>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section">
|
|
||||||
<i class="fa fa-code section-icon" bs-tooltip="tooltip.title" data-title="Full Image ID"></i>
|
|
||||||
<span class="section-info">
|
|
||||||
<a class="image-link" href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">{{ currentImage.id }}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section">
|
|
||||||
<i class="fa fa-tag section-icon" bs-tooltip="tooltip.title" data-title="Current Tags"></i>
|
|
||||||
<span class="section-info section-info-with-dropdown">
|
|
||||||
<a class="label tag label-default" ng-repeat="tag in currentImage.tags"
|
|
||||||
href="/repository/{{ repo.namespace }}/{{ repo.name }}?tag={{ tag }}">
|
|
||||||
{{ tag }}
|
|
||||||
</a>
|
|
||||||
<span style="color: #ccc;" ng-if="!currentImage.tags.length">(No Tags)</span>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="dropdown" data-placement="top" ng-if="repo.can_write || currentImage.tags">
|
|
||||||
<a href="javascript:void(0)" class="dropdown-button" data-toggle="dropdown" bs-tooltip="tooltip.title" data-title="Manage Tags"
|
|
||||||
data-container="body">
|
|
||||||
<b class="caret"></b>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu pull-right">
|
|
||||||
<li ng-repeat="tag in currentImage.tags">
|
|
||||||
<a href="javascript:void(0)" ng-click="setTag(tag, true)">
|
|
||||||
<i class="fa fa-tag"></i>{{tag}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="divider" role="presentation" ng-if="repo.can_write && currentImage.tags"></li>
|
|
||||||
<li>
|
|
||||||
<a href="javascript:void(0)" ng-click="showAddTag(currentImage)" ng-if="repo.can_write">
|
|
||||||
<i class="fa fa-plus"></i>Add New Tag
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section" ng-if="currentImage.command && currentImage.command.length">
|
|
||||||
<i class="fa fa-terminal section-icon" bs-tooltip="tooltip.title" data-title="Image Command"></i>
|
|
||||||
<span class="section-info">
|
|
||||||
<span class="formatted-command trimmed"
|
|
||||||
data-html="true"
|
|
||||||
bs-tooltip="" data-title="{{ getTooltipCommand(currentImage) }}"
|
|
||||||
data-placement="top">{{ getFormattedCommand(currentImage) }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section">
|
|
||||||
<i class="fa fa-calendar section-icon" bs-tooltip="tooltip.title" data-title="Created"></i>
|
|
||||||
<span class="section-info">
|
|
||||||
<dd am-time-ago="parseDate(currentImage.created)"></dd>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section">
|
|
||||||
<i class="fa fa-cloud-upload section-icon" bs-tooltip="tooltip.title"
|
|
||||||
data-title="The amount of data sent between Docker and the registry when pushing/pulling"></i>
|
|
||||||
<span class="section-info">{{ currentImage.size | bytes }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="image-section">
|
|
||||||
<i class="fa fa-map-marker section-icon" bs-tooltip="tooltip.title"
|
|
||||||
data-title="The geographic region(s) in which this image data is located"></i>
|
|
||||||
<span class="section-info">
|
|
||||||
<span class="location-view" location="location" ng-repeat="location in currentImage.locations"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Image changes loading -->
|
|
||||||
<div class="resource-view" resource="currentImageChangeResource">
|
|
||||||
<div class="changes-container small-changes-container section-info"
|
|
||||||
ng-show="currentImageChanges.changed.length || currentImageChanges.added.length || currentImageChanges.removed.length">
|
|
||||||
<div class="changes-count-container image-section">
|
|
||||||
<i class="fa fa-code-fork section-icon" bs-tooltip="tooltip.title" data-title="File Changes"></i>
|
|
||||||
<div style="float: right; display: inline-block">
|
|
||||||
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" data-title="Files Added"
|
|
||||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
|
||||||
<i class="fa fa-plus-square"></i>
|
|
||||||
<b>{{currentImageChanges.added.length}}</b>
|
|
||||||
</span>
|
|
||||||
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" data-title="Files Removed"
|
|
||||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
|
||||||
<i class="fa fa-minus-square"></i>
|
|
||||||
<b>{{currentImageChanges.removed.length}}</b>
|
|
||||||
</span>
|
|
||||||
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" data-title="Files Changed"
|
|
||||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
|
||||||
<i class="fa fa-pencil-square"></i>
|
|
||||||
<b>{{currentImageChanges.changed.length}}</b>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div id="collapseChanges" style="padding-top: 24px;">
|
|
||||||
<div class="well well-sm">
|
|
||||||
<div class="change added" ng-repeat="file in currentImageChanges.added | limitTo:2">
|
|
||||||
<i class="fa fa-plus-square"></i>
|
|
||||||
<span title="{{file}}">{{file}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="change removed" ng-repeat="file in currentImageChanges.removed | limitTo:2">
|
|
||||||
<i class="fa fa-minus-square"></i>
|
|
||||||
<span title="{{file}}">{{file}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="change changed" ng-repeat="file in currentImageChanges.changed | limitTo:2">
|
|
||||||
<i class="fa fa-pencil-square"></i>
|
|
||||||
<span title="{{file}}">{{file}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="more-changes" ng-show="getMoreCount(currentImageChanges) > 0">
|
|
||||||
<a href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">
|
|
||||||
And {{getMoreCount(currentImageChanges)}} more...
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div> <!-- /cor-tab-content -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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="addTagModal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-show="!creatingTag">×</button>
|
|
||||||
<h4 class="modal-title">{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move' : 'Add' }} Tag to Image {{ toTagImage.id.substr(0, 12) }}</h4>
|
|
||||||
</div>
|
|
||||||
<form name="addTagForm" ng-submit="createOrMoveTag(toTagImage, tagToCreate, addTagForm.$invalid); addTagForm.$setPristine(); tagToCreate=''">
|
|
||||||
<div class="modal-body">
|
|
||||||
<input type="text" class="form-control" id="tagName" placeholder="Enter tag name"
|
|
||||||
ng-model="tagToCreate" ng-pattern="/^([a-z0-9_\.-]){3,30}$/" required
|
|
||||||
ng-disabled="creatingTag" autofocus>
|
|
||||||
<div style="margin: 10px; margin-top: 20px;" ng-show="isOwnedTag(toTagImage, tagToCreate)">
|
|
||||||
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to this image.
|
|
||||||
</div>
|
|
||||||
<div style="margin: 10px; margin-top: 20px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
|
||||||
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to another image. This will <b>move</b> the tag.
|
|
||||||
</div>
|
|
||||||
<div class="tag-specific-images-view" tag="tagToCreate" repository="repo" images="images" image-cutoff="toTagImage"
|
|
||||||
style="margin: 10px; margin-top: 20px; margin-bottom: -10px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
|
||||||
This will also delete any unattach images and delete the following images:
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="btn btn-primary"
|
|
||||||
ng-disabled="!tagToCreate || addTagForm.$invalid || isOwnedTag(toTagImage, tagToCreate)"
|
|
||||||
ng-class="isAnotherImageTag(toTagImage, tagToCreate) ? 'btn-warning' : 'btn-primary'" ng-show="!creatingTag">
|
|
||||||
{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move Tag' : 'Create Tag' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-default" data-dismiss="modal" ng-show="!creatingTag">Cancel</button>
|
|
||||||
<div class="quay-spinner" ng-show="creatingTag"></div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
|
||||||
<div class="modal fade" id="confirmdeleteTagModal">
|
|
||||||
<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">Delete tag
|
|
||||||
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
|
||||||
{{ tagToDelete }}
|
|
||||||
</span>?
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body" ng-show="deletingTag">
|
|
||||||
<div class="quay-spinner"></div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body" ng-show="!deletingTag">
|
|
||||||
Are you sure you want to delete tag
|
|
||||||
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
|
||||||
{{ tagToDelete }}
|
|
||||||
</span>?
|
|
||||||
|
|
||||||
<div class="tag-specific-images-view" tag="tagToDelete" repository="repo" images="images" style="margin-top: 20px">
|
|
||||||
The following images and any other images not referenced by a tag will be deleted:
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer" ng-show="!deletingTag">
|
|
||||||
<button type="button" class="btn btn-primary" ng-click="deleteTag(tagToDelete)">Delete Tag</button>
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/elasticsearch-0.90.5.tar.gz"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/opt/elasticsearch-0.90.5/LICENSE.txt",
|
||||||
|
"/opt/elasticsearch-0.90.5/NOTICE.txt",
|
||||||
|
"/opt/elasticsearch-0.90.5/README.textile",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/elasticsearch",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/elasticsearch.in.sh",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/plugin",
|
||||||
|
"/opt/elasticsearch-0.90.5/config/elasticsearch.yml",
|
||||||
|
"/opt/elasticsearch-0.90.5/config/logging.yml",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/elasticsearch-0.90.5.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/jna-3.3.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/jts-1.12.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/log4j-1.2.17.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-analyzers-common-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-codecs-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-core-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-grouping-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-highlighter-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-join-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-memory-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-misc-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-queries-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-queryparser-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-sandbox-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-spatial-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-suggest-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-freebsd-6.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-ia64-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-sparc-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-sparc64-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-universal-macosx.dylib",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-universal64-macosx.dylib",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-freebsd-5.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-freebsd-6.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/sigar-1.6.4.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/spatial4j-0.3.jar"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_Release",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_Release.gpg",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_source_Sources",
|
||||||
|
"/var/lib/apt/lists/lock",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_Release",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_Release.gpg",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_source_Sources"
|
||||||
|
],
|
||||||
|
"changed": [
|
||||||
|
"/var/cache/apt/pkgcache.bin",
|
||||||
|
"/var/cache/apt/srcpkgcache.bin"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_Release",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_Release.gpg",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_main_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_multiverse_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_restricted_source_Sources",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_raring_universe_source_Sources",
|
||||||
|
"/var/lib/apt/lists/lock",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_Release",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_Release.gpg",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_main_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_multiverse_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_restricted_source_Sources",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_binary-amd64_Packages",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_i18n_Translation-en",
|
||||||
|
"/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_raring-security_universe_source_Sources"
|
||||||
|
],
|
||||||
|
"changed": [
|
||||||
|
"/var/cache/apt/pkgcache.bin",
|
||||||
|
"/var/cache/apt/srcpkgcache.bin"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/elasticsearch-0.90.5.tar.gz"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/opt/elasticsearch-0.90.5/LICENSE.txt",
|
||||||
|
"/opt/elasticsearch-0.90.5/NOTICE.txt",
|
||||||
|
"/opt/elasticsearch-0.90.5/README.textile",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/elasticsearch",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/elasticsearch.in.sh",
|
||||||
|
"/opt/elasticsearch-0.90.5/bin/plugin",
|
||||||
|
"/opt/elasticsearch-0.90.5/config/elasticsearch.yml",
|
||||||
|
"/opt/elasticsearch-0.90.5/config/logging.yml",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/elasticsearch-0.90.5.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/jna-3.3.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/jts-1.12.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/log4j-1.2.17.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-analyzers-common-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-codecs-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-core-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-grouping-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-highlighter-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-join-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-memory-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-misc-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-queries-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-queryparser-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-sandbox-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-spatial-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/lucene-suggest-4.4.0.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-freebsd-6.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-amd64-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-ia64-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-sparc-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-sparc64-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-universal-macosx.dylib",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-universal64-macosx.dylib",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-freebsd-5.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-freebsd-6.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-linux.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/libsigar-x86-solaris.so",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/sigar/sigar-1.6.4.jar",
|
||||||
|
"/opt/elasticsearch-0.90.5/lib/spatial4j-0.3.jar"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/elasticsearch-0.90.5.tar.gz"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"removed": [],
|
||||||
|
"added": [
|
||||||
|
"/elasticsearch-0.90.5.tar.gz"
|
||||||
|
],
|
||||||
|
"changed": []
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue