initial import for Open Source 🎉
This commit is contained in:
parent
1898c361f3
commit
9c0dd3b722
2048 changed files with 218743 additions and 0 deletions
11
static/directives/repo-view/image-tag-tooltip.html
Normal file
11
static/directives/repo-view/image-tag-tooltip.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div class="popover image-tag-tooltip" tabindex="-1">
|
||||
<div class="image-tag-tooltip-header"
|
||||
ng-style="::{'backgroundColor': trackEntryForImage[tag.image_id].color,
|
||||
'color': constrastingColor( trackEntryForImage[tag.image_id].color)}">
|
||||
Image {{ tag.image_id.substr(0, 12) }}
|
||||
</div>
|
||||
<ul class="image-tag-tooltip-tags">
|
||||
<li ng-repeat="tag in imageMap[tag.image_id] | limitTo:5"><i class="fa fa-tag"></i>{{ tag.name }}</li>
|
||||
</ul>
|
||||
<div class="image-tag-tooltip-tags-more" ng-if="imageMap[tag.image_id].length > 5">and {{ imageMap[tag.image_id].length - 5 }} more tags</div>
|
||||
</div>
|
236
static/directives/repo-view/repo-panel-builds.html
Normal file
236
static/directives/repo-view/repo-panel-builds.html
Normal file
|
@ -0,0 +1,236 @@
|
|||
<div class="repo-panel-builds-element">
|
||||
<div class="feedback-bar" feedback="feedback"></div>
|
||||
<div class="tab-header-controls">
|
||||
<button class="btn btn-primary" ng-click="showNewBuildDialog()" ng-if="!repository.tag_operations_disabled"
|
||||
quay-show="repository.can_write && Features.BUILD_SUPPORT && !inReadOnlyMode">
|
||||
<i class="fa fa-play"></i> Start New Build
|
||||
</button>
|
||||
</div>
|
||||
<h3 class="tab-header">Repository Builds</h3>
|
||||
|
||||
<div class="co-alert co-alert-info" ng-if="repository.tag_operations_disabled">
|
||||
Builds cannot be performed on this repository because Quay Trust is
|
||||
enabled, which requires that all operations be signed by a user.
|
||||
</div>
|
||||
|
||||
<!-- Builds -->
|
||||
<div class="co-panel" id="repoBuilds">
|
||||
<!-- 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)"
|
||||
style="min-width: 85px;">
|
||||
<a ng-click="orderBy('id')">Build ID</a>
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('commit_sha', options.predicate, options.reverse)"
|
||||
style="min-width: 115px">
|
||||
<a ng-click="orderBy('commit_sha')">Triggered By</a>
|
||||
</td>
|
||||
<td ng-class="tablePredicateClass('started_datetime', options.predicate, options.reverse)" style="min-width: 120px;">
|
||||
<a ng-click="orderBy('started_datetime')">Date Started</a>
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('tags', options.predicate, options.reverse)"
|
||||
style="min-width: 66px;">
|
||||
<a ng-click="orderBy('tags')">Tags</a>
|
||||
</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 class="hidden-xs">
|
||||
<div class="triggered-build-description" build="build"></div>
|
||||
</td>
|
||||
<td>{{ build.started | amCalendar }}</td>
|
||||
<td class="hidden-xs">
|
||||
<span class="building-tag" ng-repeat="tag in build.building_tags">
|
||||
<span class="tag-span"><i class="fa fa-tag"></i>{{ tag }}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /Builds -->
|
||||
|
||||
<!-- Build Triggers -->
|
||||
<div class="co-panel" ng-if="repository.can_admin && TriggerService.getTypes().length && !repository.tag_operations_disabled" id="repoBuildTriggers">
|
||||
<!-- Builds header controls -->
|
||||
<div class="co-panel-heading">
|
||||
<i class="fa fa-flash"></i>
|
||||
Build Triggers
|
||||
|
||||
<div class="heading-controls hidden-xs">
|
||||
<!-- Add Build Trigger -->
|
||||
<div class="dropdown" id="addBuildTrigger" ng-show="!inReadOnlyMode">
|
||||
<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()" ng-if="TriggerService.isEnabled(type)">
|
||||
<a href="{{ TriggerService.getRedirectUrl(type, repository.namespace, repository.name) }}" target="{{ TriggerService.getMetadata(type).is_external ? '' : '_self' }}">
|
||||
<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="cor-table" ng-if="triggers.length">
|
||||
<thead>
|
||||
<td>Trigger Name</td>
|
||||
<td>Dockerfile Location</td>
|
||||
<td>Context 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">
|
||||
This build trigger has not had its setup completed.
|
||||
<a ng-click="deleteTrigger(trigger)" ng-if="!inReadOnlyMode">Delete Trigger</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tbody ng-repeat="trigger in triggers | filter:{'is_active':true}">
|
||||
<tr ng-class="{'trigger-disabled': !trigger.enabled}">
|
||||
<td><trigger-description trigger="trigger"></trigger-description></td>
|
||||
<td>{{ trigger.config.dockerfile_path || '/Dockerfile' }}</td>
|
||||
<td>{{ trigger.config.context || '/' }}</td>
|
||||
<td>{{ trigger.config.branchtag_regex || 'All' }}</td>
|
||||
<td>
|
||||
<span class="entity-reference" entity="trigger.pull_robot" ng-if="trigger.pull_robot"></span>
|
||||
<span class="empty" ng-if="!trigger.pull_robot">(None)</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="cor-options-menu" ng-show="!inReadOnlyMode">
|
||||
<span ng-if="trigger.config.credentials" class="cor-option" option-click="showTriggerCredentialsModal(trigger)">
|
||||
<i class="fa fa-unlock-alt"></i> View Credentials
|
||||
</span>
|
||||
<span class="cor-option" option-click="askRunTrigger(trigger)"
|
||||
ng-class="trigger.can_invoke && trigger.enabled ? '' : 'disabled'">
|
||||
<i class="fa fa-chevron-right"></i> Run Trigger Now
|
||||
</span>
|
||||
<span class="cor-option" option-click="askToggleTrigger(trigger)">
|
||||
<i class="fa fa-adjust"></i>
|
||||
<span ng-if="trigger.enabled">Disable Trigger</span>
|
||||
<span ng-if="!trigger.enabled">Enable Trigger</span>
|
||||
</span>
|
||||
<span class="cor-option" option-click="askDeleteTrigger(trigger)">
|
||||
<i class="fa fa-times"></i> Delete Trigger
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="trigger-disabled-message" ng-if="!trigger.enabled">
|
||||
<td colspan="5" style="text-align: center"
|
||||
ng-if="trigger.disabled_reason == 'user_toggled'">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
This build trigger is user disabled and will not build.
|
||||
<a ng-click="askToggleTrigger(trigger)" ng-show="!inReadOnlyMode">Re-enable this trigger</a>
|
||||
</td>
|
||||
<td colspan="5" style="text-align: center"
|
||||
ng-if="trigger.disabled_reason == 'successive_build_failures'">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
This build trigger was automatically disabled due to successive failures.
|
||||
<a ng-click="askToggleTrigger(trigger)" ng-show="!inReadOnlyMode">Re-enable this trigger</a>
|
||||
</td>
|
||||
<td colspan="5" style="text-align: center"
|
||||
ng-if="trigger.disabled_reason == 'successive_build_internal_errors'">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
This build trigger was automatically disabled due to successive internal errors.
|
||||
<a ng-click="askToggleTrigger(trigger)" ng-show="!inReadOnlyMode">Re-enable this trigger</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /Build Triggers -->
|
||||
|
||||
<!-- Dialogs -->
|
||||
|
||||
<!-- Trigger Credentials dialog -->
|
||||
<div class="trigger-credentials-dialog" trigger="triggerCredentialsModalTrigger" counter="triggerCredentialsModalCounter"></div>
|
||||
|
||||
<!-- Toggle Trigger Confirm -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="toggleTriggerInfo"
|
||||
dialog-action="toggleTrigger(info.trigger, callback)"
|
||||
dialog-title="Toggle Trigger"
|
||||
dialog-action-title="Toggle Trigger">
|
||||
Are you sure you want to <span ng-if="toggleTriggerInfo.trigger.enabled">disable</span><span ng-if="!toggleTriggerInfo.trigger.enabled">enable</span> this trigger?
|
||||
</div>
|
||||
|
||||
<!-- Delete Trigger 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>
|
||||
|
||||
<!-- Manual trigger dialog -->
|
||||
<div class="manual-trigger-build-dialog"
|
||||
repository="repository"
|
||||
trigger="currentStartTrigger"
|
||||
counter="showTriggerStartDialogCounter"
|
||||
build-started="handleBuildStarted(build)"></div>
|
||||
|
||||
<!-- /Dialogs -->
|
||||
</div>
|
83
static/directives/repo-view/repo-panel-info.html
Normal file
83
static/directives/repo-view/repo-panel-info.html
Normal file
|
@ -0,0 +1,83 @@
|
|||
<div class="repo-panel-info-element">
|
||||
<!-- Repository stats and builds summary -->
|
||||
<div class="repository-stats row">
|
||||
<!-- Stats -->
|
||||
<div class="col-sm-5 stat-col">
|
||||
<div class="stat-title">Repository Activity</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<div class="heatmap hidden-xs hidden-sm" data="repository.stats"
|
||||
item-name="action" domain="month" range="3"
|
||||
start-count="-2" start-domain="months"></div>
|
||||
|
||||
<div class="stat visible-xs visible-sm">
|
||||
<div class="stat-value">{{ getAggregatedUsage(repository.stats, 1) | abbreviated }}</div>
|
||||
<div class="stat-subtitle">Yesterday</div>
|
||||
</div>
|
||||
|
||||
<div class="stat visible-xs visible-sm">
|
||||
<div class="stat-value">{{ getAggregatedUsage(repository.stats, 30) | abbreviated }}</div>
|
||||
<div class="stat-subtitle">Last 30 days</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Builds -->
|
||||
<div class="col-sm-7 builds-list" ng-if="!Features.BUILD_SUPPORT">
|
||||
<div class="right-pull-controls" style="width: 100%">
|
||||
<div>Pull this container with the following Docker command:</div>
|
||||
<div class="copy-box" value="pullCommand"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 builds-list" ng-if="Features.BUILD_SUPPORT">
|
||||
<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 && !repository.tag_operations_disabled">
|
||||
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"
|
||||
can-view="repository.can_write || Features.READER_BUILD_LOGS"></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">
|
||||
<table class="co-fixed-table">
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style="font-size:20px;">Description</h4>
|
||||
<div class="description">
|
||||
<markdown-input content="repository.description"
|
||||
can-write="repository.can_write && !inReadOnlyMode"
|
||||
(content-changed)="updateDescription($event.content)"
|
||||
field-title="repository description"></markdown-input>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 400px;" class="hidden-xs hidden-sm" ng-if="Features.BUILD_SUPPORT">
|
||||
<div class="right-pull-controls">
|
||||
<div>Pull this container with the following Docker command:</div>
|
||||
<div class="copy-box" value="pullCommand"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
484
static/directives/repo-view/repo-panel-mirroring.html
Normal file
484
static/directives/repo-view/repo-panel-mirroring.html
Normal file
|
@ -0,0 +1,484 @@
|
|||
<div class="repo-panel-mirror-element">
|
||||
<h3 class="tab-header">
|
||||
<span class="repository-title" repository="repository"></span>
|
||||
Mirroring
|
||||
</h3>
|
||||
|
||||
<div class="co-panel">
|
||||
|
||||
<!-- View 1: Repository is in wrong state -->
|
||||
<div class="co-panel"
|
||||
ng-show="repository.state != 'MIRROR'"
|
||||
ng-if="repository.state != 'MIRROR'">
|
||||
This repository's state is <strong>{{ repository.state }}</strong>. Use the
|
||||
<a href="/repository/{{ repository.namespace }}/{{ repository.name}}?tab=settings">settings tab</a>
|
||||
and change it to <strong><i class="fa fa-refresh lock-icon"></i> Mirror</strong> to manage its
|
||||
mirroring configuration.
|
||||
</div>
|
||||
|
||||
<!-- View 2: Setup Repository Mirroring -->
|
||||
<!-- TODO: We should use the wizard workflow for initial Setup. -->
|
||||
<div class="co-panel"
|
||||
ng-show="repository.state == 'MIRROR' && !vm.isSetup"
|
||||
ng-if="!vm.isSetup">
|
||||
<p>
|
||||
This feature will convert
|
||||
<a href="/repository/{{ repository.namespace }}/{{ repository.name }}">
|
||||
<strong>{{ repository.namespace }}/{{ repository.name }}</strong>
|
||||
</a> into a mirror. Changes to the external repository will be duplicated here. While
|
||||
enabled, users will be unable to push images to this repository.
|
||||
</p>
|
||||
<!-- TODO: Move custom styles into CSS -->
|
||||
<form name="enableMirroring">
|
||||
<table class="co-table" style="width: 600px">
|
||||
<!-- A little "hack" taken from the settings tab. This should probably be done in CSS. -->
|
||||
<thead>
|
||||
<th style="width: 200px"></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<h4>External Repository</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="external_registry">Registry Location</label></td>
|
||||
<td>
|
||||
<input class="form-control"
|
||||
id="external_reference"
|
||||
type="text"
|
||||
ng-model="vm.externalReference"
|
||||
placeholder="quay.io/redhat/quay"
|
||||
required></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="sync_tag_filter">Tags</label><br>
|
||||
<small>Comma-separated list of tag patterns to synchronize.</small>
|
||||
</td>
|
||||
<td>
|
||||
<input class="form-control"
|
||||
id="tags_input"
|
||||
type="text"
|
||||
ng-model="vm.tags"
|
||||
placeholder="Examples: latest, 3.3*, *"
|
||||
required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_start_date">Start Date</label></td>
|
||||
<td>
|
||||
<span id="sync_start_date"
|
||||
class="datetime-picker"
|
||||
datetime="vm.syncStartDate"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="create_sync_interval">Sync Interval</label></td>
|
||||
<td>
|
||||
<span class="interval-input" seconds="vm.syncInterval"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="choose_robot">Robot User</label></td>
|
||||
<td>
|
||||
<span class="entity-search"
|
||||
namespace="repository.namespace"
|
||||
for-repository="repository"
|
||||
skip-permissions="true"
|
||||
placeholder="'Select a ' + (repository.is_organization ? 'team or ' : '') + 'user...'"
|
||||
current-entity="vm.robot"
|
||||
pull-right="true"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<h4>Credentials</h4>
|
||||
<small style="color: #333">
|
||||
Required if the external repository is private.
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="external_registry_username">Username</label></td>
|
||||
<td><input class="form-control"
|
||||
id="external_registry_username"
|
||||
type="text"
|
||||
ng-model="vm.username"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="external_registry_password">Password</label></td>
|
||||
<td><input class="form-control"
|
||||
id="external_registry_password"
|
||||
type="password"
|
||||
ng-model="vm.password"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<h4>Advanced Settings</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="sync_verify_tls">Verify TLS</label><br>
|
||||
<small>
|
||||
Require HTTPS and verify certificates when talking to the external registry.
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox"
|
||||
ng-model="vm.verifyTLS"
|
||||
id="sync_verify_tls"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_http_proxy">HTTP Proxy</label></td>
|
||||
<td>
|
||||
<input ng-model="vm.httpProxy"
|
||||
placeholder="proxy.example.com"
|
||||
class="form-control"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_https_proxy">HTTPs Proxy</label></td>
|
||||
<td>
|
||||
<input ng-model="vm.httpsProxy"
|
||||
placeholder="proxy.example.com"
|
||||
class="form-control"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_no_proxy">No Proxy</label></td>
|
||||
<td>
|
||||
<input ng-model="vm.noProxy"
|
||||
placeholder="example.com"
|
||||
class="form-control"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<button class="btn btn-primary"
|
||||
ng-click="vm.setupMirror()"
|
||||
ng-disabled="enableMirroring.$invalid || !vm.robot">
|
||||
Enable Mirror
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- View 3: Manage existing Mirror configuration. -->
|
||||
<div class="co-panel"
|
||||
ng-show="repository.state == 'MIRROR' && vm.isSetup"
|
||||
style="width: 600px">
|
||||
<p>
|
||||
This repository is configured as a mirror. While enabled, Quay will
|
||||
periodically replicate any matching images on the external
|
||||
registry. Users cannot manually push to this repository.
|
||||
</p>
|
||||
<table class="co-table" style="width: 600px">
|
||||
<!-- A little "hack" taken from the settings tab. This should probably be done in CSS. -->
|
||||
<thead>
|
||||
<th style="width: 200px"></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center"><h4>Configuration</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="is_enabled">Enabled</label><br>
|
||||
<small ng-show="vm.isEnabled">
|
||||
Scheduled mirroring enabled. Immediate sync available via <i>Sync Now</i>.
|
||||
</small>
|
||||
<small ng-show="!vm.isEnabled">
|
||||
Scheduled mirroring disabled. Immediate sync available via <i>Sync Now</i>.
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<input id="is_enabled"
|
||||
type="checkbox"
|
||||
ng-checked="vm.isEnabled"
|
||||
ng-click="vm.toggleMirroring()"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="external_reference">External Repository</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeExternalRepository()">
|
||||
{{ vm.externalReference }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="tags">Tags</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link"
|
||||
ng-click="vm.showChangeTags()">
|
||||
<!-- TODO: re-use "tag-span" class from other directive(s) -->
|
||||
<span ng-repeat="tag in vm.tags track by $index"
|
||||
style="
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
background: #eee;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
color: #000;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
">
|
||||
{{ tag }}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="sync_interval">Sync Interval</label>
|
||||
</td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeSyncInterval()">
|
||||
{{ vm.syncInterval | humanizeInterval }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="view_sync_start_date">Next Sync Date</label></td>
|
||||
<td>
|
||||
<a style="float: left"
|
||||
class="co-modify-link"
|
||||
ng-click="vm.showChangeSyncStartDate()"
|
||||
id="view_sync_start_date">
|
||||
{{ vm.syncStartDate | humanizeDate }}
|
||||
</a>
|
||||
<button style="float: right"
|
||||
class="btn btn-primary"
|
||||
ng-disabled="vm.status === 'SYNCING' || vm.status === 'SYNC_NOW'"
|
||||
ng-click="vm.syncNow()">Sync Now</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="choose_robot">Robot User</label></td>
|
||||
<td>
|
||||
<span class="entity-search"
|
||||
namespace="repository.namespace"
|
||||
for-repository="repository"
|
||||
skip-permissions="true"
|
||||
placeholder="'Select a ' + (repository.is_organization ? 'team or ' : '') + 'user...'"
|
||||
current-entity="vm.robot"
|
||||
entity-selected="vm.changeRobot(entity)"
|
||||
pull-right="true"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<h4>Advanced Settings</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="external_registry_username">Credentials</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeCredentials()">
|
||||
{{ vm.username || 'None' }}
|
||||
</a>
|
||||
<button style="float: right"
|
||||
class="btn btn-primary"
|
||||
ng-click="vm.deleteCredentials()">Delete Credentials</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="verify_certificate">Verify TLS</label><br>
|
||||
<small>
|
||||
Require HTTPS and verify certificates when talking to the external registry.
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" ng-click="vm.toggleVerifyTLS()" ng-checked="vm.verifyTLS"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>HTTP Proxy</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeHTTPProxy()">
|
||||
{{ vm.httpProxy || 'None' }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>HTTPs Proxy</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeHTTPsProxy()">
|
||||
{{ vm.httpsProxy || 'None' }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>No Proxy</label></td>
|
||||
<td>
|
||||
<a class="co-modify-link" ng-click="vm.showChangeNoProxy()">
|
||||
{{ vm.noProxy || 'None' }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center">
|
||||
<h4>Status</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_status">State</label></td>
|
||||
<td>
|
||||
{{ vm.statusLabels[vm.status] || vm.status }}
|
||||
<button style="float: right"
|
||||
class="btn btn-danger"
|
||||
ng-disabled="vm.status !== 'SYNCING' && vm.status !== 'SYNC_NOW'"
|
||||
ng-click="vm.syncCancel()">Cancel</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Timeout</label></td>
|
||||
<td>
|
||||
{{ vm.expirationDate ? (vm.expirationDate | humanizeDate) : 'None' }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sync_retries_remaining">Retries Remaining</label></td>
|
||||
<td>
|
||||
{{ vm.retriesRemaining }} / 3
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change mirroring sync date dialog -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.syncStartDateChanges"
|
||||
dialog-action="vm.changeSyncStartDate(info, callback)"
|
||||
dialog-title="Update next synchronization date"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
Next Synchronization Date:
|
||||
<span id="change_sync_start_date"
|
||||
class="datetime-picker"
|
||||
datetime="vm.syncStartDateChanges.values.sync_start_date"></span>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change mirroring sync interval -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.syncIntervalChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update next synchronization date"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>Synchronization Interval</label>
|
||||
<div>
|
||||
<span class="interval-input" seconds="vm.syncIntervalChanges.values.sync_interval"></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change external repository -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.externalRepositoryChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update external repository"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
External Repository
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="vm.externalRepositoryChanges.values.external_reference">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change Credentials -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.credentialsChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update Credentials"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>Username</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="vm.credentialsChanges.values.external_registry_username">
|
||||
<label>Password</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
ng-model="vm.credentialsChanges.values.external_registry_password">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change Tag Pattern -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.tagChanges"
|
||||
dialog-action="vm.changeTagRules(info, callback)"
|
||||
dialog-title="Update Tag Filter"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>Tags</label>
|
||||
<input type="text" class="form-control" ng-model="vm.tagChanges.values.rule_value">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change "HTTP Proxy" -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.httpProxyChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update HTTP Proxy"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>HTTP_PROXY</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="vm.httpProxyChanges.values.external_registry_config.proxy.http_proxy">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change "HTTPs Proxy" -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.httpsProxyChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update HTTP Proxy"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>HTTPS_PROXY</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="vm.httpsProxyChanges.values.external_registry_config.proxy.https_proxy">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Change "No Proxy" -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="vm.noProxyChanges"
|
||||
dialog-action="vm.changeConfig(info, callback)"
|
||||
dialog-title="Update HTTP Proxy"
|
||||
dialog-action-title="Update"
|
||||
dialog-form="context.mirroringform">
|
||||
<form name="context.mirroringform" class="co-single-field-dialog">
|
||||
<label>NO_PROXY</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="vm.noProxyChanges.values.external_registry_config.proxy.no_proxy">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
171
static/directives/repo-view/repo-panel-settings.html
Normal file
171
static/directives/repo-view/repo-panel-settings.html
Normal file
|
@ -0,0 +1,171 @@
|
|||
<div class="repo-panel-settings-element">
|
||||
<h3 class="tab-header"><span class="repository-title" repository="repository"></span> Settings</h3>
|
||||
|
||||
<!-- User/Team Permissions -->
|
||||
<div class="co-panel" id="repoPermissions">
|
||||
<div class="co-panel-heading"><i class="fa fa-key"></i> User and Robot Permissions</div>
|
||||
<div class="panel-body" style="padding-top: 5px;">
|
||||
<div class="repository-permissions-table" repository="repository"
|
||||
is-enabled="isEnabled"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Signing and Trust -->
|
||||
<div ng-if="repository.kind == 'image'">
|
||||
<repository-signing-config repository="repository"></repository-signing-config>
|
||||
</div>
|
||||
|
||||
<!-- Events and Notifications -->
|
||||
<div ng-if="repository.kind == 'image'">
|
||||
<div class="repository-events-table" repository="repository"
|
||||
is-enabled="isEnabled"></div>
|
||||
</div>
|
||||
|
||||
<!-- Visibility settings -->
|
||||
<div class="co-panel">
|
||||
<div class="co-panel-heading"><i class="fa fa-unlock-alt"></i>
|
||||
<span class="repository-title" repository="repository"></span> Visibility
|
||||
</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 <span class="repository-title" repository="repository"></span> 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')" ng-show="!inReadOnlyMode">
|
||||
<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 <span class="repository-title" repository="repository"></span> 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')" ng-show="!planRequired && !inReadOnlyMode">
|
||||
<i class="fa fa-lock"></i>Make Private
|
||||
</button>
|
||||
|
||||
<!-- Payment -->
|
||||
<div class="repo-count-checker" namespace="repository.namespace" plan-required="planRequired"
|
||||
is-enabled="repository.is_public">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Repository State -->
|
||||
<div class="co-panel" quay-show="Features.REPO_MIRROR">
|
||||
<div class="co-panel-heading"><i class="fa fa-unlock-alt"></i>
|
||||
<span class="repository-title" repository="repository"></span>State
|
||||
</div>
|
||||
<div class="cor-loader" ng-show="!repository"></div>
|
||||
|
||||
<div ng-show="repository">
|
||||
<div class="panel-body panel-section lock-section">
|
||||
|
||||
<i class="fa fa-hdd-o lock-icon" ng-show="repository.state == 'NORMAL'"></i>
|
||||
<i class="fa fa-archive lock-icon" ng-show="repository.state == 'READ_ONLY'"></i>
|
||||
<i class="fa fa-refresh lock-icon" quay-show="Features.REPO_MIRROR && repository.state == 'MIRROR'" ng-show="repository.state == 'MIRROR'"></i>
|
||||
|
||||
<div>
|
||||
This <span class="repository-title" repository="repository"></span> state is currently <b>{{ repository.state }}</b>.
|
||||
<span ng-show="repository.state == 'NORMAL'">Standard permissions apply.</span>
|
||||
<span ng-show="repository.state == 'READ_ONLY'">Users will not be able to push or modify images.</span>
|
||||
<span ng-show="repository.state == 'MIRROR'" quay-show="Features.REPO_MIRROR && repository.state == 'MIRROR'">The images and tags are maintained by Quay and Users can not push or modify them.</span>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Enhance the appearance of this. Move style to .css file. -->
|
||||
<div style="margin-top: 1em">
|
||||
<form>
|
||||
<select ng-options="option for option in repoStates"
|
||||
ng-model="selectedState"
|
||||
ng-change="changeState()"></select>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete repository -->
|
||||
<div class="co-panel" ng-show="!inReadOnlyMode">
|
||||
<div class="co-panel-heading">
|
||||
<i class="fa fa-trash"></i> Delete <span class="repository-title" repository="repository"></span>
|
||||
</div>
|
||||
<div class="cor-loader" ng-show="!repository"></div>
|
||||
<div ng-show="repository">
|
||||
<div class="panel-body panel-section">
|
||||
<div class="co-alert co-alert-danger">
|
||||
<button class="btn btn-danger delete-btn" ng-click="askDelete()">
|
||||
<i class="fa fa-trash"></i>
|
||||
Delete <span class="repository-title" repository="repository"></span>
|
||||
</button>
|
||||
|
||||
Deleting a <span class="repository-title" repository="repository"></span> <b>cannot be undone</b>. Here be dragons!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Build Status Badge -->
|
||||
<div class="co-panel hidden-xs" ng-if="repository.kind == 'image' && Features.BUILD_SUPPORT">
|
||||
<div class="co-panel-heading"><i class="fa fa-tasks"></i> Build Status Badge</div>
|
||||
<div class="cor-loader" ng-show="!repository"></div>
|
||||
<div ng-show="repository">
|
||||
<div class="panel-body panel-section">
|
||||
<!-- Token Info Banner -->
|
||||
<div class="co-alert co-alert-info" ng-if="!repository.is_public" style="margin-bottom: 20px;">
|
||||
Note: This badge contains a token so the badge can be seen by external users. The token does not grant any other access and is safe to share!
|
||||
</div>
|
||||
|
||||
<!-- 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="Container Repository on Quay">
|
||||
</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" value="getBadgeFormat('svg', repository)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Markdown:</td>
|
||||
<td>
|
||||
<div class="copy-box" value="getBadgeFormat('md', repository)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AsciiDoc:</td>
|
||||
<td>
|
||||
<div class="copy-box" value="getBadgeFormat('asciidoc', repository)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Repository Dialog -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="deleteRepoInfo"
|
||||
dialog-action="deleteRepo(info, callback)"
|
||||
dialog-title="Delete"
|
||||
dialog-action-title="Delete">
|
||||
<div class="co-alert co-alert-danger" style="margin-bottom: 10px;">
|
||||
This action cannot be undone!
|
||||
</div>
|
||||
|
||||
Continue with deletion of this <span class="repository-title" repository="repository"></span>?
|
||||
</div>
|
||||
</div>
|
353
static/directives/repo-view/repo-panel-tags.html
Normal file
353
static/directives/repo-view/repo-panel-tags.html
Normal file
|
@ -0,0 +1,353 @@
|
|||
<div class="repo-panel-tags-element">
|
||||
<div class="tab-header-controls">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn" ng-class="!expandedView ? 'btn-primary active' : 'btn-default'"
|
||||
ng-click="setExpanded(false)">
|
||||
Compact
|
||||
</button>
|
||||
<button class="btn" ng-class="expandedView ? 'btn-info active' : 'btn-default'"
|
||||
ng-click="setExpanded(true)">
|
||||
Expanded
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="tab-header"><span class="hidden-xs">Repository </span>Tags</h3>
|
||||
<div class="co-alert co-alert-danger" ng-if="hasDefcon1">
|
||||
One or more of your tags has an <strong>extremely critical</strong> vulnerability which should be addressed immediately:
|
||||
<a href="{{ vuln.Link }}" ng-repeat="(key, vuln) in defcon1" style="margin-left: 10px;" ng-safenewtab>
|
||||
{{ vuln.Name }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="co-check-bar">
|
||||
<span class="cor-checkable-menu" controller="checkedTags" filter="tags">
|
||||
<div class="cor-checkable-menu-item" item-filter="allTagFilter(item)">
|
||||
<i class="fa fa-check-square-o"></i>All Tags
|
||||
</div>
|
||||
<div class="cor-checkable-menu-item" item-filter="noTagFilter(item)">
|
||||
<i class="fa fa-square-o"></i>No Tags
|
||||
</div>
|
||||
<div class="cor-checkable-menu-item" item-filter="commitTagFilter(item)">
|
||||
<i class="fa fa-git"></i>Commit SHAs
|
||||
</div>
|
||||
|
||||
<div class="cor-checkable-menu-item" item-filter="imageIDFilter(it.image_id, item)"
|
||||
ng-repeat="it in imageTrackEntries" ng-if="::it.visible">
|
||||
<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">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fa fa-cog"></i>
|
||||
Actions
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a ng-click="showHistory(checkedTags.checked)">
|
||||
<i class="fa fa-history"></i><span class="text">View Tags History</span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="repository.can_write && !inReadOnlyMode">
|
||||
<a ng-click="askDeleteMultipleTags(checkedTags.checked)"
|
||||
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||
<i class="fa fa-times"></i><span class="text">Delete Tags</span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="repository.can_write && !inReadOnlyMode">
|
||||
<a ng-click="askChangeTagsExpiration(checkedTags.checked)"
|
||||
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||
<i class="fa fa-clock-o"></i><span class="text">Change Tags Expiration</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<span class="co-filter-box">
|
||||
<span class="page-controls" total-count="tags.length" current-page="options.page" page-size="tagsPerPage"></span>
|
||||
<input class="form-control" type="text" ng-model="options.filter" placeholder="Filter Tags...">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="co-alert co-alert-info" ng-if="allTagsSelected && !fullPageSelected">
|
||||
All <strong>{{ tags.length }}</strong> visible tags are selected.
|
||||
<a ng-click="clearSelectedTags()">Clear selection</a>.
|
||||
</div>
|
||||
|
||||
<div class="co-alert co-alert-info" ng-if="fullPageSelected">
|
||||
All <strong>{{ tagsPerPage }}</strong> tags on this page are selected.
|
||||
<a ng-click="selectAllTags()">Select all {{ tags.length }} tags currently visible</a>.
|
||||
</div>
|
||||
|
||||
<div class="cor-loader" ng-show="!isEnabled"></div>
|
||||
|
||||
<table class="co-table co-fixed-table" id="tagsTable" ng-if="isEnabled" style="margin-top: 20px;">
|
||||
<thead>
|
||||
<td class="checkbox-col"></td>
|
||||
<td ng-class="tablePredicateClass('name', options.predicate, options.reverse)">
|
||||
<a ng-click="orderBy('name')">Tag</a>
|
||||
</td>
|
||||
<td class="hidden-xs unorderable-col"
|
||||
style="width: 46px;"
|
||||
quay-require="['SIGNING']"
|
||||
ng-if="repository.trust_enabled">Sign
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('last_modified_datetime', options.predicate, options.reverse)"
|
||||
style="width: 140px;">
|
||||
<a ng-click="orderBy('last_modified_datetime')">Last Modified</a>
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs"
|
||||
style="width: 200px;"
|
||||
quay-require="['SECURITY_SCANNER']">
|
||||
Security Scan
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('size', options.predicate, options.reverse)"
|
||||
style="width: 90px;">
|
||||
<a ng-click="orderBy('size')" data-title="The compressed size of the tag's image" data-container="body" bs-tooltip>Size</a>
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs hidden-md"
|
||||
ng-class="tablePredicateClass('expiration_date', options.predicate, options.reverse)"
|
||||
style="width: 140px;">
|
||||
<a ng-click="orderBy('expiration_date')" data-title="When the tag expires" data-container="body" bs-tooltip>Expires</a>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm" ng-if="imageTracks.length > maxTrackCount"
|
||||
style="width: 20px; position: relative;">
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm"
|
||||
ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
|
||||
style="width: 140px;">
|
||||
<a ng-click="orderBy('image_id')">Manifest</a>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount"></td>
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col"></td>
|
||||
<td class="hidden-xs hidden-sm" style="width: 4px"></td>
|
||||
</thead>
|
||||
|
||||
<tbody class="co-checkable-row"
|
||||
ng-repeat="tag in tags | slice:(tagsPerPage * options.page):(tagsPerPage * (options.page + 1))"
|
||||
ng-class="checkedTags.isChecked(tag, checkedTags.checked) ? 'checked' : ''"
|
||||
bindonce>
|
||||
<tr ng-class="expandedView ? 'expanded-view': ''">
|
||||
<td><span class="cor-checkable-item" controller="checkedTags" item="tag"></span></td>
|
||||
<td class="co-flowing-col">
|
||||
<span class="tag-span"><span bo-text="tag.name"></span></span>
|
||||
<span class="manifest-list-icons" bo-if="tag.is_manifest_list">
|
||||
<i class="manifest-list-manifest-icon fa fa-{{ manifest.os }}"
|
||||
ng-repeat="manifest in manifestsOf(tag)"
|
||||
data-title="{{ manifest.description }}"
|
||||
bs-tooltip></i>
|
||||
</span>
|
||||
</td>
|
||||
<td class="signing-col hidden-xs"
|
||||
quay-require="['SIGNING']"
|
||||
ng-if="repository.trust_enabled">
|
||||
<tag-signing-display tag="tag" delegations="repoDelegationsInfo" compact="true"></tag-signing-display>
|
||||
</td>
|
||||
|
||||
<!-- Last Modified -->
|
||||
<td class="hidden-xs">
|
||||
<time-ago datetime="tag.last_modified"></time-ago>
|
||||
</td>
|
||||
|
||||
<!-- Security scanning -->
|
||||
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-sm hidden-xs">
|
||||
<!-- Manifest List -->
|
||||
<span class="secscan-manifestlist" ng-if="::tag.is_manifest_list"
|
||||
ng-click="setExpanded(true)"
|
||||
data-title="The tag points to a list of manifests. Click 'Expanded' to view."
|
||||
bs-tooltip>
|
||||
See Child Manifests
|
||||
</span>
|
||||
|
||||
<!-- No Digest -->
|
||||
<span class="nodigest" ng-if="::!tag.manifest_digest"
|
||||
data-title="The tag does not have a V2 digest and so is unsupported for scan"
|
||||
bs-tooltip>
|
||||
<span class="donut-chart" width="22" data="[{'index': 0, 'value': 1, 'color': '#eee'}]"></span>
|
||||
Unsupported
|
||||
</span>
|
||||
|
||||
<!-- Manifest security view -->
|
||||
<manifest-security-view repository="::repository" manifest-digest="::tag.manifest_digest"
|
||||
ng-if="::(tag.manifest_digest && !tag.is_manifest_list)">
|
||||
</manifest-security-view>
|
||||
</td>
|
||||
|
||||
<!-- Size -->
|
||||
<td class="hidden-xs">
|
||||
<span bo-text="tag.size | bytes" bo-if="!tag.is_manifest_list"></span>
|
||||
<span bo-if="tag.is_manifest_list">N/A</span>
|
||||
</td>
|
||||
|
||||
<!-- Expiration -->
|
||||
<td class="hidden-xs hidden-sm hidden-md">
|
||||
<a ng-click="askChangeTagsExpiration([tag])"
|
||||
ng-if="!repository.tag_operations_disabled && repository.can_write">
|
||||
<expiration-status-view expiration-date="tag.expiration_date"></expiration-status-view>
|
||||
</a>
|
||||
<expiration-status-view expiration-date="tag.expiration_date" ng-if="repository.tag_operations_disabled || !repository.can_write"></expiration-status-view>
|
||||
</td>
|
||||
|
||||
<!-- Manifest link -->
|
||||
<td class="hidden-xs hidden-sm hidden-md image-track"
|
||||
ng-if="imageTracks.length > maxTrackCount">
|
||||
<span class="image-track-filled-dot"
|
||||
ng-if="::trackEntryForImage[tag.image_id]"
|
||||
ng-style="::{'backgroundColor': trackEntryForImage[tag.image_id].color}"
|
||||
ng-click="::selectTrack(trackEntryForImage[tag.image_id])"
|
||||
data-template-url="/static/directives/repo-view/image-tag-tooltip.html"
|
||||
data-placement="left"
|
||||
data-trigger="hover"
|
||||
data-animation="am-flip-x"
|
||||
data-auto-close="1"
|
||||
bs-popover></span>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm image-id-col">
|
||||
<manifest-link repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></manifest-link>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount">
|
||||
<span class="image-track-dot"
|
||||
ng-if="::it.entryByImageId[tag.image_id]"
|
||||
ng-style="::{'borderColor': trackEntryForImage[tag.image_id].color}"
|
||||
ng-click="::selectTrack(trackEntryForImage[tag.image_id])"
|
||||
data-template-url="/static/directives/repo-view/image-tag-tooltip.html"
|
||||
data-placement="left"
|
||||
data-trigger="hover"
|
||||
data-animation="am-flip-x"
|
||||
data-auto-close="1"
|
||||
bs-popover></span>
|
||||
<span class="image-track-line"
|
||||
ng-if="::getTrackEntryForIndex(it, $parent.$parent.$index)"
|
||||
ng-class="::trackLineClass(it, $parent.$parent.$parent.$index)"
|
||||
ng-style="::{'borderColor': getTrackEntryForIndex(it, $parent.$parent.$parent.$index).color}"></span>
|
||||
</td>
|
||||
<td class="options-col">
|
||||
<i class="fa fa-download" data-title="Fetch Tag" bs-tooltip
|
||||
ng-click="fetchTagActionHandler.askFetchTag(tag)">
|
||||
</i>
|
||||
</td>
|
||||
<td class="options-col">
|
||||
<span ng-if="repository.can_write && !inReadOnlyMode">
|
||||
<span class="cor-options-menu">
|
||||
<span class="cor-option" option-click="askAddTag(tag)"
|
||||
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||
<i class="fa fa-plus"></i> Add New Tag
|
||||
</span>
|
||||
<span class="cor-option" option-click="showLabelEditor(tag)"
|
||||
ng-if="tag.manifest_digest">
|
||||
<i class="fa fa-tags"></i> Edit Labels
|
||||
</span>
|
||||
<span class="cor-option" option-click="askDeleteTag(tag.name)"
|
||||
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||
<i class="fa fa-times"></i> Delete Tag
|
||||
</span>
|
||||
<span class="cor-option" option-click="askChangeTagsExpiration([tag])"
|
||||
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||
<i class="fa fa-clock-o"></i> Change Expiration
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
|
||||
</tr>
|
||||
|
||||
<!-- Manifest List Expanded View -->
|
||||
<tr class="manifest-list-view" ng-if="expandedView && tag.is_manifest_list && !tag.manifest_list">
|
||||
<td colspan="{{5 + (repository.trust_enabled ? 1 : 0) + (Features.SECURITY_SCANNER ? 1 : 0) }}">
|
||||
<div class="cor-loader"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="manifest-list-view" ng-repeat="manifest in manifestsOf(tag)"
|
||||
ng-if="expandedView && tag.is_manifest_list && tag.manifest_list">
|
||||
<td class="checkbox-col"></td>
|
||||
<td colspan="2">
|
||||
<i class="manifest-list-manifest-icon fa fa-{{ manifest.os }}"></i>
|
||||
{{ manifest.description }}
|
||||
</td>
|
||||
|
||||
<!-- Security scanning -->
|
||||
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-sm hidden-xs">
|
||||
<manifest-security-view repository="::repository" manifest-digest="::manifest.digest">
|
||||
</manifest-security-view>
|
||||
</td>
|
||||
|
||||
<td colspan="1" class="hidden-xs"></td>
|
||||
<td colspan="1" class="hidden-xs hidden-sm hidden-md"></td>
|
||||
|
||||
<td class="hidden-xs hidden-sm image-id-col">
|
||||
<manifest-link repository="repository" manifest-digest="manifest.digest"></manifest-link>
|
||||
</td>
|
||||
|
||||
<td class="hidden-xs hidden-sm hidden-md image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount" bindonce>
|
||||
<span class="image-track-line"
|
||||
ng-if="::getTrackEntryForIndex(it, $parent.$parent.$parent.$parent.$index)"
|
||||
ng-class="::trackLineExpandedClass(it, $parent.$parent.$parent.$parent.$parent.$index)"
|
||||
ng-style="::{'borderColor': getTrackEntryForIndex(it, $parent.$parent.$parent.$parent.$parent.$index).color}"></span>
|
||||
</td>
|
||||
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col"></td>
|
||||
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
|
||||
</tr>
|
||||
|
||||
<!-- Expanded View -->
|
||||
<tr ng-if="expandedView" class="expanded-viewport" ng-class="{'manifest-list': tag.is_manifest_list}">
|
||||
<td class="checkbox-col"></td>
|
||||
<td class="labels-col" colspan="{{5 + (repository.trust_enabled ? 1 : 0) + (Features.SECURITY_SCANNER ? 1 : 0) }}">
|
||||
<!-- Image ID -->
|
||||
<div class="image-id-row">
|
||||
<manifest-link repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></manifest-link>
|
||||
</div>
|
||||
|
||||
<!-- Labels -->
|
||||
<div class="manifest-label-list" repository="repository"
|
||||
manifest-digest="tag.manifest_digest" cache="labelCache"></div>
|
||||
|
||||
<!-- Delegations -->
|
||||
<div class="signing-delegations-list" ng-if="repository.trust_enabled">
|
||||
<tag-signing-display compact="false" tag="tag" delegations="repoDelegationsInfo"></tag-signing-display>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="hidden-xs hidden-sm hidden-md image-track" ng-repeat="it in imageTracks"
|
||||
ng-if="imageTracks.length <= maxTrackCount" bindonce>
|
||||
<span class="image-track-line"
|
||||
ng-if="::getTrackEntryForIndex(it, $parent.$parent.$index)"
|
||||
ng-class="::trackLineExpandedClass(it, $parent.$parent.$parent.$index)"
|
||||
ng-style="::{'borderColor': getTrackEntryForIndex(it, $parent.$parent.$parent.$index).color}"></span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td class="options-col hidden-xs hidden-sm"><!-- Whitespace col --></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</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="cor-loader" ng-show="tagsLoading"></div>
|
||||
<div class="empty" ng-if="!allTags.length && !tagsLoading">
|
||||
<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 class="tag-operations-dialog" repository="repository" repository-tags="repositoryTags"
|
||||
image-loader="imageLoader"
|
||||
action-handler="tagActionHandler"
|
||||
labels-changed="handleLabelsChanged(manifest_digest)"></div>
|
||||
|
||||
<div class="fetch-tag-dialog" repository="repository" action-handler="fetchTagActionHandler"></div>
|
Reference in a new issue