Skip to content

Instantly share code, notes, and snippets.

@Potherca
Last active July 12, 2019 09:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Potherca/644812477bb8f432cb75256adc1a1a75 to your computer and use it in GitHub Desktop.
Save Potherca/644812477bb8f432cb75256adc1a1a75 to your computer and use it in GitHub Desktop.
jsfiddle-5zbdwu9n-dockerhub-pull-for-gitlab-group
.component {
background-color: white;
margin: 0.5em;
}
.component-item[data-label]::before {
content: attr(data-label) ":";
}
.component .component-item.tags {
margin-bottom: 0;
}
.component-item[data-color="blue"] .value,
.component-item[data-color="#066da5"] .value {
background-color: #066da5;
color: white;
}
.component-item[data-color="brightgreen"] .value {
background-color: green;
color: white;
}
.component-item[data-color="lightgrey"] .value {
background-color: goldenrod;
color: white;
}
.component-item[data-color="red"] .value {
background-color: crimson;
color: white;
}
.sorted-by-docker_size .tags:not(.docker_size),
.sorted-by-docker_layers .tags:not(.docker_layers),
.sorted-by-docker_pulls .tags:not(.docker_pulls) {
opacity: 0.35;
}
.component.not-found,
.component-item.docker_stars { display: none; }
<section class="section">
<nav class="level">
<div class="level-left">
<div class="level-item">
<h1 class="title">
<span class="subtitle">Pipeline-components</span>
<span>overview</span>
</h1>
</div>
</div>
<div class="level-right">
<div class="level-item buttons has-addons JS-sort-by-button-group">
<button class="button is-selected is-link" data-sort-by="name">
<span class="icon"><i class="far fa-heart"></i></span>
<span>Name</span>
</button>
<button class="button is-link is-outlined" data-sort-by="docker_pulls" data-sort-descending="true">
<span class="icon"><i class="far fa-arrow-alt-circle-down"></i></span>
<span>Docker Pulls</span>
</button>
<button class="button is-link is-outlined" data-sort-by="docker_size">
<span class="icon"><i class="far fa-file-alt"></i></span>
<span>Image Size</span>
</button>
<button class="button is-link is-outlined" data-sort-by="docker_layers">
<span class="icon"><i class="fas fa-layer-group"></i></span>
<span>Image Layers</span>
</button>
</div>
</div>
</nav>
<div class="container is-fluid">
<div class="component-list"></div>
</div>
</section>
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>Pipeline-components overview</strong> by <a href="https://pother.ca">Potherca</a>.
</p>
</div>
</footer>
/*global fetch, Request, Isotope */
/*/ User Data /*/
const userData = {
docker_group: 'pipelinecomponents',
// gitlab_domain = 'gitlab.com',
gitlab_group: 'pipeline-components',
gitlab_group_id: 3749748,
}
/*/ Application data /*/
const container = document.querySelector('.component-list')
// Make sure resources are cached to avoid HTTP 429 response codes
const requestConfig = {cache: 'force-cache'}
/*/ Application logic /*/
const fetchJson = url =>
fetch( new Request(url, requestConfig) )
.then( response => response.ok
? response.json()
: new Error('Could not retrieve API response, status = ' + response.status)
)
// Fetch list of project names from the Gitlab API
const fetchProjectNames = ({ docker_group, gitlab_group, gitlab_group_id, gitlab_domain = 'gitlab.com' }) =>
fetchJson(`https://${gitlab_domain}/api/v4/groups/${gitlab_group_id}/projects?per_page=100&simple=true`)
// Grab name from each project
.then( projects => projects.flatMap(project => project.name).filter(name => name) )
// Remove blacklisted projects
.then( projects => projects.filter(project => ['global-issues','example','ci-config', 'org'].includes(project) === false) )
// Build URLs for each project
.then( names => names.map( name => ({
name : name,
gitlab_url : `https://${gitlab_domain}/${gitlab_group}/${name}`,
gitlab_build : `https://img.shields.io/gitlab/pipeline/${gitlab_group}/${name}.json?gitlab_url=https%3A%2F%2F${gitlab_domain}`,
docker_pulls : `https://img.shields.io/docker/pulls/${docker_group}/${name}.json`,
docker_size : `https://img.shields.io/microbadger/image-size/${docker_group}/${name}.json`,
docker_layers : `https://img.shields.io/microbadger/layers/${docker_group}/${name}.json`,
docker_stars : `https://img.shields.io/docker/stars/${docker_group}/${name}.json`,
})
))
// Grab information for all URLs
const fetchDockerData = collections => Promise.all(
Object.keys(collections[0]).map(
name => Promise.all(
collections.map( collection =>
// Filter blacklisted values (those that do not need an AJAX call)
['name', 'gitlab_url'].includes(name)
? { name: collection.name, [name]: collection[name] }
: fetchJson( collection[name] )
.then( data => ({
name: collection.name,
[name]: data
}))
)
)
))
// Combine all separate elements into one collection
.then(function (data) {
const combined = []
data.forEach( (collections) => {
collections.forEach( (collection) => {
const name = collection.name
const index = combined.map( item => item.name ).indexOf( collection.name );
if ( index === -1) {
combined.push(collection)
} else {
combined[index] = { ...combined[index],...collection}
}
})
})
return combined
})
const attachHtml = collections => Promise.resolve(collections.map( collection => `
<div class="box component ${collection.docker_pulls.value === 'repo not found'?'not-found':''}">
<h2 class="title is-6 name has-text-centered"><a href="${collection.gitlab_url}">${collection.name}</a></h2>
${Object.keys(collection)
.filter( name => ! ['name', 'gitlab_url'].includes(name) )
.map( name => `
<p class="tags has-addons component-item ${name}"
data-color="${collection[name].color}"
>
<span class="tag">${collection[name].label}</span>
<span class="value tag">${collection[name].value}</span>
</p>`).join('')
}
</div>
`
))
.then( elements => elements.forEach(html => {
container.insertAdjacentHTML( 'beforeend', html )
}))
const addIsotope = async () => (container.isotope = new Isotope( container, {
itemSelector: '.component',
layoutMode: 'fitRows',
stagger: 15,
transitionDuration: 800,
sortBy : 'name',
getSortData: {
// This works for docker_size ONLY as long as all image have a size in MBs.
docker_size: itemElem => parseFloat( itemElem.querySelector('.docker_size .value').textContent ) || 999999999,
docker_layers: itemElem => parseInt( itemElem.querySelector('.docker_layers .value').textContent, 10 ) || 999999999,
name: '.name .value', // text from querySelector
docker_pulls: itemElem =>
parseInt(itemElem.querySelector('.docker_pulls .value').textContent.replace( /k|M/g, (found) => {
if (found === 'k') {return '000'}
if (found === 'M') {return '000000'}
}), 10) || 0
}
}
)
)
/*/ Application actions /*/
fetchProjectNames(userData)
.then(fetchDockerData)
.then(attachHtml)
.then(addIsotope)
.then( () => {
/*/ Handle sorting buttons /*/
const buttonsRoot = document.querySelector('.JS-sort-by-button-group')
buttonsRoot.addEventListener( 'click', event => {
const eventTarget = event.target.closest('button')
if (eventTarget) {
let classList = buttonsRoot.querySelector('.is-selected').classList
classList.remove('is-selected')
classList.add('is-outlined')
eventTarget.classList.add('is-selected')
eventTarget.classList.remove('is-outlined')
const sortItem = eventTarget.getAttribute('data-sort-by')
if (sortItem !== null) {
container.className = `component-lint sorted-by-${sortItem}`
container.isotope.arrange({
sortBy: sortItem,
sortAscending: eventTarget.hasAttribute('data-sort-descending')
? false
: true
})
}
}
})
})
name: Gitlab group Dockerhub data
description:
authors:
- Potherca
resources:
- https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css
- https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css
- https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/3.0.6/isotope.pkgd.min.js
load_type: d
js_wrap: d
- Add license and link to code source
- Add "please wait, fetching data" spinner
- Add filters for 50k+, 10k+, 10k<, etc.
- Add filter for which language/stack a component is for
- Add filter for passing/failing build
- Add text-search filter
- Add "Made by Potherca" style
- Create/Generate sorting/filter buttons from JS
- Create a version with a form so others can (re)use it
- Carefully align color pallet
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment