Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions content/patterns/rhoso-gitops/_index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ industries:
focus_areas:
- DevSecOps
aliases: /rhoso-gitops/
ci: rhoso-gitops
ci_sources:
- base: https://raw.githubusercontent.com/validatedpatterns-sandbox/rhoso-gitops/reporting/
files:
- rhoso-gitops-baremetal-4.18-stable-badge.json
links:
github: https://github.com/validatedpatterns-sandbox/rhoso-gitops
install: getting-started
Expand Down
14 changes: 12 additions & 2 deletions layouts/ci/single.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,18 @@ <h1>{{- .Title -}}</h1>
{{ partial "spinner.html" . }}
</div>
<script>
{{ $internal := .Param "internal" }}
obtainBadges({ 'target': 'ci-dataset' {{ if (eq $internal true ) }}, 'links': 'internal'{{ end }} });
{{ $ciSources := slice }}
{{ range .Site.Pages }}
{{ if ne .Params.tier "archived" }}
{{ range .Params.ci_sources }}
{{ $ciSources = $ciSources | append . }}
{{ end }}
{{ end }}
{{ end }}
obtainBadges({
'target': 'ci-dataset',
'ci_sources': {{ $ciSources | jsonify | safeJS }}
});
</script>
</div>
</div>
Expand Down
9 changes: 7 additions & 2 deletions layouts/partials/ci.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@

<script type="text/javascript" src="/js/dashboard.v2.js"></script>
<div class='results'>
<div id="ci-dataset">
{{ partial "spinner.html" . }}
</div>
<script>
obtainBadges({ 'target':'ci-dataset', 'filter_field':'pattern', 'filter_value': '{{ .Params.ci }}', 'disable_buttons': true });
obtainBadges({
'target': 'ci-dataset',
'filter_field': 'pattern',
'filter_value': '{{ .Params.ci }}',
'disable_buttons': true,
'ci_sources': {{ .Params.ci_sources | default (slice) | jsonify | safeJS }}
});
</script>
</div>
115 changes: 95 additions & 20 deletions static/js/dashboard.v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@

class Badge {

constructor(base, key, date) {
constructor(base, key, date, useJsonMetadata = true) {
this.base = base
this.key = key
this._json = null
this.useJsonMetadata = useJsonMetadata
this.operator = 'N/A'
this.date = date.substr(0, 10)
this.pattern = ''
this.platform = ''
this.version = ''

if (isNewCiBadgeBase(base)) {
if (this.useJsonMetadata) {
return
}

Expand All @@ -36,7 +37,7 @@ class Badge {
applyJson(json) {
this._json = json

if (!isNewCiBadgeBase(this.base)) {
if (!this.useJsonMetadata) {
return this
}

Expand Down Expand Up @@ -85,6 +86,52 @@ class Badge {
// Still present in badge buckets but no longer in the CI matrix (omit from dashboard)
var CI_DASHBOARD_EXCLUDED_OCP_VERSIONS = ['4.19']

// patternRepo comes from external badge JSON; only link to known GitHub orgs.
var TRUSTED_PATTERN_REPO_PATH_PREFIXES = [
'/validatedpatterns/',
'/validatedpatterns-sandbox/'
]

// Returns true when repoUrl is an https://github.com URL under a trusted org.
function isTrustedPatternRepo(repoUrl) {
if (repoUrl == null || repoUrl === '') {
return false
}
try {
var url = new URL(String(repoUrl).trim())
if (url.protocol !== 'https:') {
return false
}
if (url.username !== '' || url.password !== '') {
return false
}
if (url.hostname.toLowerCase() !== 'github.com') {
return false
}
var path = url.pathname.toLowerCase()
if (path.length > 1 && path.charAt(path.length - 1) !== '/') {
path += '/'
}
for (var i = 0; i < TRUSTED_PATTERN_REPO_PATH_PREFIXES.length; i++) {
var prefix = TRUSTED_PATTERN_REPO_PATH_PREFIXES[i]
if (path.indexOf(prefix) === 0 && path.length > prefix.length) {
return true
}
}
return false
} catch (e) {
return false
}
}

// Only the old bucket uses filename parsing, new CI badge bucket / external raw badges — hydrate metadata from JSON, not filename.
function usesJsonMetadata(base) {
if (base == null || base === '' || String(base).indexOf('storage.googleapis.com/vp-results') !== -1 ) {
return false
}
return true
}

function excludeRetiredOcpVersionsFromDashboard(badges) {
if (!badges || badges.length === 0) {
return badges || []
Expand Down Expand Up @@ -157,14 +204,6 @@ var CI_PATTERN_DOC_SLUG = {
vsk: 'virtualization-starter-kit'
}

// New CI badge bucket (S3 keys under ci-badges/) — distinct from legacy vp-results filenames.

var NEW_CI_BADGE_KEY_PREFIX = "https://vp-qe-ci-badges"

function isNewCiBadgeBase(base) {
return base != null && base.startsWith(NEW_CI_BADGE_KEY_PREFIX)
}

function normalizeInfraProvider(provider) {
if (provider == null || provider === '') {
return null
Expand Down Expand Up @@ -318,6 +357,9 @@ function stringForKey(key) {
if (key == 'nutanix') {
return 'Nutanix'
}
if (key == 'rhoso-gitops') {
return 'RHOSO GitOps'
}
return key
}

Expand Down Expand Up @@ -505,7 +547,7 @@ function renderBadgeHtml(badge, field) {
}

let envLink = encodeURI(badge_url)
let branchLink = json_obj.patternRepo != null ? encodeURI(json_obj.patternRepo) : null
let branchLink = isTrustedPatternRepo(json_obj.patternRepo) ? encodeURI(json_obj.patternRepo) : null
let badgeText = '<span class="ci-label">'
badgeText += '<a href="' + envLink + '"><span class="' + badgeClass + '"><i class="ci-icon fas fa-fw fa-brands fa-git-alt" aria-hidden="true"></i>' + envLabel + '</span></a>'
if (branchLink != null) {
Expand Down Expand Up @@ -632,7 +674,8 @@ function platformDisplayName(platform) {
'azr': 'Azure',
'gcp': 'Google Cloud',
'nutanix': 'Nutanix',
'intel': 'On-prem (Intel)'
'intel': 'On-prem (Intel)',
'baremetal': 'Baremetal',
}
return names[platform] || stringForKey(platform)
}
Expand Down Expand Up @@ -843,6 +886,7 @@ function renderDashboardTableRow(badge) {
function isProductionSite() {
var host = window.location.hostname
return host === 'validatedpatterns.io' ||
host === 'netlify.app' ||
host === 'localhost' ||
host === '127.0.0.1'
}
Expand Down Expand Up @@ -1063,7 +1107,7 @@ function renderPatternCard(pattern, platformBadges, comboBadges, tracker) {
var platformColor = tracker.platforms[b.platform] || 'unavailable'
html += '<span class="ci-card-platform" title="' + platformDisplayName(b.platform) + '">'
html += '<span class="ci-card-platform-dot ' + platformColor + '" id="ci-card-dot-' + patternId + '-' + sanitizeId(b.platform) + '"></span>'
html += '<span class="ci-card-platform-label">' + stringForKey(b.platform) + '</span>'
html += '<span class="ci-card-platform-label">' + platformDisplayName(b.platform) + '</span>'
html += '</span>'
})
html += '</div>'
Expand Down Expand Up @@ -1283,12 +1327,8 @@ function getBadges(xmlText, bucket_url, badge_set) {

for (let i = 0; i < l; i++) {
let key = entries[i].childNodes[0].nodeValue
if (badge_set == "GA" && key.endsWith("stable-badge.json")) {
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
} else if (badge_set == "early" && (key.endsWith("prerelease-badge.json") || key.endsWith("nightly-badge.json") || key.endsWith("operator-badge.json"))) {
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
} else if (badge_set == "all" && key.endsWith("-badge.json")) {
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
if (badgeKeyMatchesSet(key, badge_set)) {
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i]), usesJsonMetadata(bucket_url)))
} else {
console.log("Skipping: " + key);
}
Expand Down Expand Up @@ -1379,6 +1419,36 @@ function getBucketOptions(input) {
return options
}

function badgeKeyMatchesSet(key, badge_set) {
if (badge_set === 'GA' && key.endsWith('stable-badge.json')) {
return true
}
if (badge_set === 'early' && (key.endsWith('prerelease-badge.json') || key.endsWith('nightly-badge.json') || key.endsWith('operator-badge.json'))) {
return true
}
if (badge_set === 'all' && key.endsWith('-badge.json')) {
return true
}
return false
}

function createBadgesFromExternalSources(sources, badge_set) {
const badges = []
const sourceList = sources || []
sourceList.forEach(function (source) {
const base = String(source.base || '').replace(/\/$/, '')
const files = source.files || []
files.forEach(function (file) {
if (!badgeKeyMatchesSet(file, badge_set)) {
return
}
// Date filled from JSON during enrich
badges.push(new Badge(base, file, '1970-01-01T00:00:00Z'))
})
})
return badges
}

function fetchBucketBadges(bucket, inputs) {
return new Promise(function (resolve) {
var req = new XMLHttpRequest()
Expand Down Expand Up @@ -1450,6 +1520,11 @@ function obtainBadges(inputs) {
}
allBadges.push.apply(allBadges, badges || [])
})
var ciSources = createBadgesFromExternalSources(inputs.ci_sources, options.get('sets'))
if (ciSources.length > 0) {
console.log('Got ' + ciSources.length + ' external badges')
allBadges.push.apply(allBadges, ciSources)
}

console.log('All badges:', allBadges)
return enrichBadgesFromJson(allBadges)
Expand Down
Loading