Skip to content

Commit ba6d817

Browse files
committed
feat: expose some custom labels
1 parent 19e72e4 commit ba6d817

File tree

7 files changed

+182
-129
lines changed

7 files changed

+182
-129
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Some env options are available for use this interface for **only one server**.
100100
- `DEFAULT_REGISTRIES`: List of comma separated registry URLs (e.g `http://registry.example.com,http://registry:5000`), available only when `SINGLE_REGISTRY=false`. (default: ` `).
101101
- `READ_ONLY_REGISTRIES`: Desactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false`. (default: `false`).
102102
- `SHOW_CATALOG_NB_TAGS`: Show number of tags per images on catalog page. This will produce + nb images requests, not recommended on large registries. (default: `false`).
103+
- `HISTORY_CUSTOM_LABELS`: Expose custom labels in history page, custom labels will be processed like maintainer label.
103104

104105
There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-standalone/).
105106

bin/entrypoint

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ sed -i "s~\${SHOW_CONTENT_DIGEST}~${SHOW_CONTENT_DIGEST}~" index.html
99
sed -i "s~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
1010
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
1111
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
12+
sed -i "s~\${HISTORY_CUSTOM_LABELS}~${HISTORY_CUSTOM_LABELS}~" index.html
1213

1314
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
1415
sed -i "s/\${DELETE_IMAGES}/false/" index.html
@@ -64,4 +65,4 @@ if [ "$(whoami)" != "root" ]; then
6465
sed -i "s,/var/run/nginx.pid,/tmp/nginx.pid," /etc/nginx/nginx.conf
6566
fi
6667

67-
sed -i "s,listen 80;,listen $NGINX_LISTEN_PORT;," /etc/nginx/conf.d/default.conf
68+
sed -i "s,listen 80;,listen $NGINX_LISTEN_PORT;," /etc/nginx/conf.d/default.conf

src/components/docker-registry-ui.riot

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
4242
<tag-history registry-url="{ state.registryUrl }" registry-name="{ state.name }" pull-url="{ state.pullUrl }"
4343
image="{ router.getTagHistoryImage() }" tag="{ router.getTagHistoryTag() }"
4444
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }" on-notify="{ notifySnackbar }"
45-
on-authentication="{ onAuthentication }"></tag-history>
45+
on-authentication="{ onAuthentication }" history-custom-labels="{ stringToArray(props.historyCustomLabels) }"></tag-history>
4646
</route>
4747
</router>
4848
<registry-authentication realm="{ state.realm }" scope="{ state.scope }" service="{ state.service }"
@@ -80,7 +80,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8080
stripHttps,
8181
getRegistryServers,
8282
setRegistryServers,
83-
truthy
83+
truthy,
84+
stringToArray
8485
} from '../scripts/utils';
8586
import router from '../scripts/router';
8687
@@ -175,7 +176,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
175176
baseRoute: '([^#]*?)/(\\?[^#]*?)?(#!)?(/?)',
176177
router,
177178
version,
178-
truthy
179+
truthy,
180+
stringToArray
179181
}
180182
</script>
181183
</docker-registry-ui>

src/components/tag-history/tag-history-element.riot

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@ You should have received a copy of the GNU Affero General Public License
1515
along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
-->
1717
<tag-history-element class="{ state.key }">
18-
<div class="headline"><i class="material-icons">{ state.icon }</i>
18+
<div class="headline">
19+
<i class="material-icons">{ state.icon }</i>
1920
<p>{ state.name }</p>
2021
</div>
2122
<div class="content">
22-
<div class="value" if="{ state.value }"> { state.value }</div>
23-
<div class="values value" each="{ value in state.values }" if="{ state.values }"> { value }</div>
23+
<div class="value" if="{ state.value }">{ state.value }</div>
24+
<div class="values value" each="{ value in state.values }" if="{ state.values }">{ value }</div>
2425
</div>
2526
<script>
26-
import {
27-
getHistoryIcon
28-
} from '../../scripts/utils';
27+
import { getHistoryIcon } from '../../scripts/utils';
2928
export default {
3029
onBeforeStart(props, state) {
3130
state.key = props.entry.key;
@@ -48,14 +47,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
4847
return name;
4948
} else if (name === 'os') {
5049
return 'OS';
50+
} else if (name.startsWith('custom-label-')) {
51+
name = name.replace('custom-label-', '');
5152
}
52-
return name.replace(/([a-z])([A-Z])/g, '$1 $2')
53-
.replace('_', ' ')
53+
return name
54+
.replace(/([a-z])([A-Z])/g, '$1 $2')
55+
.replace(/[_-]/g, ' ')
5456
.split(' ')
55-
.map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
57+
.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
5658
.join(' ');
57-
}
58-
}
59+
},
60+
};
5961
</script>
6062
<style>
6163
:host.Labels .value,
@@ -70,7 +72,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
7072
7173
:host.docker_version .headline .material-icons {
7274
background-size: 24px auto;
73-
background-image: url("images/docker-logo.svg");
75+
background-image: url('images/docker-logo.svg');
7476
background-repeat: no-repeat;
7577
}
7678
@@ -103,4 +105,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
103105
font-size: 12px;
104106
}
105107
</style>
106-
</tag-history-element>
108+
</tag-history-element>

src/components/tag-history/tag-history.riot

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -20,60 +20,60 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2020
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ toTaglist }">
2121
<i class="material-icons">arrow_back</i>
2222
</material-button>
23-
<h2>
24-
History of { props.image }:{ props.tag } <i class="material-icons">history</i>
25-
</h2>
23+
<h2>History of { props.image }:{ props.tag } <i class="material-icons">history</i></h2>
2624
</div>
2725
</material-card>
2826
<div if="{ !state.loadend }" class="spinner-wrapper">
29-
<material-spinner />
27+
<material-spinner></material-spinner>
3028
</div>
3129

32-
<material-tabs if="{ state.archs && state.loadend }" useLine="{ true }" tabs="{ state.archs }"
33-
onTabChanged="{ onTabChanged }" />
30+
<material-tabs
31+
if="{ state.archs && state.loadend }"
32+
useLine="{ true }"
33+
tabs="{ state.archs }"
34+
onTabChanged="{ onTabChanged }"
35+
></material-tabs>
3436

3537
<material-card each="{ element in state.elements }" class="tag-history-element">
36-
<tag-history-element each="{ entry in element }" if="{ entry.value && entry.value.length > 0}" entry="{ entry }" />
38+
<tag-history-element
39+
each="{ entry in element }"
40+
if="{ entry.value && entry.value.length > 0}"
41+
entry="{ entry }"
42+
></tag-history-element>
3743
</material-card>
3844
<script>
39-
import {
40-
DockerImage
41-
} from '../../scripts/docker-image';
42-
import {
43-
bytesToSize
44-
} from '../../scripts/utils';
45+
import { DockerImage } from '../../scripts/docker-image';
46+
import { bytesToSize } from '../../scripts/utils';
4547
import router from '../../scripts/router';
46-
import TagHistoryElement from './tag-history-element.riot'
48+
import TagHistoryElement from './tag-history-element.riot';
4749
export default {
4850
components: {
49-
TagHistoryElement
51+
TagHistoryElement,
5052
},
5153
onBeforeMount(props, state) {
5254
state.elements = [];
5355
state.image = new DockerImage(props.image, props.tag, {
5456
list: true,
5557
registryUrl: props.registryUrl,
5658
onNotify: props.onNotify,
57-
onAuthentication: props.onAuthentication
59+
onAuthentication: props.onAuthentication,
5860
});
59-
state.image.fillInfo()
61+
state.image.fillInfo();
6062
},
6163
onMounted(props, state) {
6264
state.image.on('blobs', this.processBlobs);
6365
state.image.on('list', this.multiArchList);
6466
},
6567
onTabChanged(arch, idx) {
6668
const state = this.state;
67-
const {
68-
registryUrl,
69-
onNotify
70-
} = this.props;
71-
state.elements = []
72-
state.image.variants[idx] = state.image.variants[idx] ||
69+
const { registryUrl, onNotify } = this.props;
70+
state.elements = [];
71+
state.image.variants[idx] =
72+
state.image.variants[idx] ||
7373
new DockerImage(this.props.image, arch.digest, {
7474
list: false,
7575
registryUrl,
76-
onNotify
76+
onNotify,
7777
});
7878
if (state.image.variants[idx].blobs) {
7979
return this.processBlobs(state.image.variants[idx].blobs);
@@ -83,48 +83,52 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8383
},
8484
processBlobs(blobs) {
8585
const state = this.state;
86+
const { historyCustomLabels } = this.props;
8687
8788
function exec(elt) {
8889
const guiElements = [];
8990
for (var attribute in elt) {
9091
if (elt.hasOwnProperty(attribute) && attribute != 'empty_layer') {
9192
const value = elt[attribute];
9293
const guiElement = {
93-
"key": attribute,
94-
"value": modifySpecificAttributeTypes(attribute, value)
94+
'key': attribute,
95+
'value': modifySpecificAttributeTypes(attribute, value),
9596
};
9697
guiElements.push(guiElement);
9798
}
9899
}
99100
return guiElements.sort(eltSort);
100101
}
101102
const elements = new Array(blobs.history.length + 1);
102-
elements[0] = exec(getConfig(blobs));
103+
elements[0] = exec(getConfig(blobs, { historyCustomLabels }));
103104
blobs.history.forEach(function (elt, i) {
104-
elements[blobs.history.length - i] = exec(elt)
105+
elements[blobs.history.length - i] = exec(elt);
105106
});
106107
this.update({
107108
elements,
108-
loadend: true
109+
loadend: true,
109110
});
110111
},
111112
multiArchList(manifests) {
112113
manifests = manifests.manifests || manifests;
113114
const archs = manifests.map(function (manifest) {
114115
return {
115-
title: manifest.platform.os + '/' + manifest.platform.architecture + (manifest.platform.variant ?
116-
manifest.platform.variant : ''),
117-
digest: manifest.digest
118-
}
116+
title:
117+
manifest.platform.os +
118+
'/' +
119+
manifest.platform.architecture +
120+
(manifest.platform.variant ? manifest.platform.variant : ''),
121+
digest: manifest.digest,
122+
};
119123
});
120124
this.update({
121-
archs
125+
archs,
122126
});
123127
},
124128
toTaglist() {
125129
router.taglist(this.props.image);
126-
}
127-
}
130+
},
131+
};
128132
const eltIdx = function (e) {
129133
switch (e) {
130134
case 'created':
@@ -158,7 +162,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
158162
return new Date(value).toLocaleString();
159163
case 'created_by':
160164
const cmd = value.match(/\/bin\/sh *-c *#\(nop\) *([A-Z]+)/);
161-
return (cmd && cmd[1]) || 'RUN'
165+
return (cmd && cmd[1]) || 'RUN';
162166
case 'size':
163167
return bytesToSize(value);
164168
case 'Entrypoint':
@@ -175,26 +179,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
175179
return value || '';
176180
};
177181
178-
const getConfig = function (blobs) {
179-
const res = ['architecture', 'User', 'created', 'docker_version', 'os', 'Cmd', 'Entrypoint', 'Env', 'Labels',
180-
'User', 'Volumes', 'WorkingDir', 'author', 'id', 'ExposedPorts'
181-
]
182-
.reduce(function (acc, e) {
183-
const value = blobs[e] || blobs.config[e];
184-
if (value && e === 'architecture' && blobs.variant) {
185-
acc[e] = value + blobs.variant;
186-
} else if (value) {
187-
acc[e] = value;
188-
}
189-
return acc;
190-
}, {});
182+
const getConfig = function (blobs, { historyCustomLabels }) {
183+
console.log(this);
184+
const res = [
185+
'architecture',
186+
'User',
187+
'created',
188+
'docker_version',
189+
'os',
190+
'Cmd',
191+
'Entrypoint',
192+
'Env',
193+
'Labels',
194+
'User',
195+
'Volumes',
196+
'WorkingDir',
197+
'author',
198+
'id',
199+
'ExposedPorts',
200+
].reduce(function (acc, e) {
201+
const value = blobs[e] || blobs.config[e];
202+
if (value && e === 'architecture' && blobs.variant) {
203+
acc[e] = value + blobs.variant;
204+
} else if (value) {
205+
acc[e] = value;
206+
}
207+
return acc;
208+
}, {});
191209
192-
if (!res.author && (res.Labels && res.Labels.maintainer)) {
210+
if (!res.author && res.Labels && res.Labels.maintainer) {
193211
res.author = blobs.config.Labels.maintainer;
194212
delete res.Labels.maintainer;
195213
}
196214
215+
historyCustomLabels
216+
.filter((label) => res.Labels[label])
217+
.forEach((label) => {
218+
res[`custom-label-${label}`] = res.Labels[label];
219+
delete res.Labels[label];
220+
});
221+
197222
return res;
198223
};
199224
</script>
200-
</tag-history>
225+
</tag-history>

0 commit comments

Comments
 (0)