mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-26 15:09:53 +03:00
feat(taglist): improve visibility of multi-architecture images (#271)
closes #271
This commit is contained in:
parent
affb0572c9
commit
4091baa341
4 changed files with 88 additions and 6 deletions
54
src/components/tag-list/architectures.riot
Normal file
54
src/components/tag-list/architectures.riot
Normal file
|
@ -0,0 +1,54 @@
|
|||
<architectures>
|
||||
<div class="architecture" each="{ architecture in state.architectures }">{ architecture }</div>
|
||||
<script>
|
||||
import { platformToString } from '../../scripts/docker-image';
|
||||
export default {
|
||||
onMounted(props, state) {
|
||||
this.onLoad(props, state);
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
if (props.image !== state.image) {
|
||||
this.onLoad(props, state);
|
||||
}
|
||||
},
|
||||
onLoad(props, state) {
|
||||
if (props.image.manifests) {
|
||||
return this.onList(props.image.manifests);
|
||||
} else if (props.image.blobs) {
|
||||
return this.onBlobs(props.image.blobs);
|
||||
}
|
||||
props.image.on('blobs', (blobs) => this.onBlobs(blobs, props, state));
|
||||
props.image.on('list', (list) => this.onList(list, props, state));
|
||||
},
|
||||
onBlobs(blobs, props, state) {
|
||||
const architectures = [platformToString(blobs)];
|
||||
if (!props.image.manifests) {
|
||||
this.update({ architectures, image: props.image });
|
||||
}
|
||||
},
|
||||
onList(list, props, state) {
|
||||
const architectures = list.map(({ platform }) => platformToString(platform));
|
||||
this.update({ architectures, image: props.image });
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host architectures {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
architectures .architecture {
|
||||
background-color: var(--hover-background);
|
||||
padding: 2px 4px;
|
||||
border-radius: 12px;
|
||||
margin: 4px 0;
|
||||
text-align: center;
|
||||
}
|
||||
architectures .architecture:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
architectures .architecture:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
</architectures>
|
|
@ -49,6 +49,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
>
|
||||
Tag
|
||||
</th>
|
||||
<th class="architectures">Arch</th>
|
||||
<th class="show-tag-history">History</th>
|
||||
<th
|
||||
class="remove-tag { state.toDelete.size > 0 && !state.singleDeleteAction ? 'delete' : '' }"
|
||||
|
@ -103,6 +104,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
on-notify="{ props.onNotify }"
|
||||
></copy-to-clipboard>
|
||||
</td>
|
||||
<td class="architectures">
|
||||
<architectures image="{ image }"></architectures>
|
||||
</td>
|
||||
<td class="show-tag-history">
|
||||
<tag-history-button image="{ image }"></tag-history-button>
|
||||
</td>
|
||||
|
@ -130,6 +134,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
import CopyToClipboard from './copy-to-clipboard.riot';
|
||||
import TagHistoryButton from './tag-history-button.riot';
|
||||
import RemoveImage from './remove-image.riot';
|
||||
import Architectures from './architectures.riot';
|
||||
import { matchSearch } from '../search-bar.riot';
|
||||
import ConfirmDeleteImage from '../dialogs/confirm-delete-image.riot';
|
||||
const ACTION_CHECK_TO_DELETE = 'CHECK';
|
||||
|
@ -146,6 +151,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
RemoveImage,
|
||||
TagHistoryButton,
|
||||
ConfirmDeleteImage,
|
||||
Architectures,
|
||||
},
|
||||
onBeforeMount(props) {
|
||||
this.state = {
|
||||
|
@ -277,4 +283,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
};
|
||||
export { ACTION_CHECK_TO_DELETE, ACTION_UNCHECK_TO_DELETE, ACTION_DELETE_IMAGE };
|
||||
</script>
|
||||
<style>
|
||||
tag-table table th.architectures {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</tag-table>
|
||||
|
|
|
@ -34,6 +34,13 @@ export const filterWrongManifests = (response) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const platformToString = (platform) => {
|
||||
if (!platform || !platform.architecture) {
|
||||
return 'unknown';
|
||||
}
|
||||
return platform.architecture + (platform.variant ? platform.variant : '');
|
||||
};
|
||||
|
||||
export class DockerImage {
|
||||
constructor(name, tag, { list, registryUrl, onNotify, onAuthentication, useControlCacheHeader }) {
|
||||
this.name = name;
|
||||
|
@ -90,7 +97,9 @@ export class DockerImage {
|
|||
if (this.status === 200 || this.status === 202) {
|
||||
const response = JSON.parse(this.responseText);
|
||||
if (supportListManifest(response) && self.opts.list) {
|
||||
self.trigger('list', filterWrongManifests(response));
|
||||
const manifests = filterWrongManifests(response);
|
||||
self.trigger('list', manifests);
|
||||
self.manifests = manifests;
|
||||
const manifest = response.manifests[0];
|
||||
const image = new DockerImage(self.name, manifest.digest, { ...self.opts, list: false });
|
||||
eventTransfer(image, self);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { supportListManifest, filterWrongManifests } from '../src/scripts/docker-image.js';
|
||||
import { supportListManifest, filterWrongManifests, platformToString } from '../src/scripts/docker-image.js';
|
||||
import { dockerManifestList } from './fixtures/docker-manifest-list.js';
|
||||
import { ociImageIndexLayer } from './fixtures/oci-image-index-layer.js';
|
||||
import { ociImageIndexManifest } from './fixtures/oci-image-index-manifest.js';
|
||||
|
@ -36,10 +36,18 @@ describe('docker-image', () => {
|
|||
);
|
||||
});
|
||||
it('should return all manifests for `application/vnd.oci.image.index.v1+json`', () => {
|
||||
assert.equal(
|
||||
filterWrongManifests(ociImageIndexManifest['application/vnd.oci.image.index.v1+json']).length,
|
||||
2
|
||||
);
|
||||
assert.equal(filterWrongManifests(ociImageIndexManifest['application/vnd.oci.image.index.v1+json']).length, 2);
|
||||
});
|
||||
});
|
||||
describe('platformToString', () => {
|
||||
it('should return unknown when the platform is not found', () => {
|
||||
assert.equal(platformToString(), 'unknown');
|
||||
assert.equal(platformToString({}), 'unknown');
|
||||
});
|
||||
it('should format the platform', () => {
|
||||
assert.equal(platformToString({ os: 'linux', architecture: 'amd64' }), 'amd64');
|
||||
assert.equal(platformToString({ os: 'linux', architecture: 'arm', variant: 'v7' }), 'armv7');
|
||||
assert.equal(platformToString({ architecture: 'arm', variant: 'v7' }), 'armv7');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue