Skip to content

Commit cedc458

Browse files
authored
Merge pull request #741 from darkdoc/external_badges
feat: Support externally stored CI badges Only show the pattern repo as a clickable link if it is: - https - hosted on github - is in either validatedatterns or validatedpatterns-sandbox GH organization
2 parents e0a5479 + 2ff7df0 commit cedc458

4 files changed

Lines changed: 119 additions & 24 deletions

File tree

‎content/patterns/rhoso-gitops/_index.adoc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ industries:
1212
focus_areas:
1313
- DevSecOps
1414
aliases: /rhoso-gitops/
15+
ci: rhoso-gitops
16+
ci_sources:
17+
- base: https://raw.githubusercontent.com/validatedpatterns-sandbox/rhoso-gitops/reporting/
18+
files:
19+
- rhoso-gitops-baremetal-4.18-stable-badge.json
1520
links:
1621
github: https://github.com/validatedpatterns-sandbox/rhoso-gitops
1722
install: getting-started

‎layouts/ci/single.html‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ <h1>{{- .Title -}}</h1>
2020
{{ partial "spinner.html" . }}
2121
</div>
2222
<script>
23-
{{ $internal := .Param "internal" }}
24-
obtainBadges({ 'target': 'ci-dataset' {{ if (eq $internal true ) }}, 'links': 'internal'{{ end }} });
23+
{{ $ciSources := slice }}
24+
{{ range .Site.Pages }}
25+
{{ if ne .Params.tier "archived" }}
26+
{{ range .Params.ci_sources }}
27+
{{ $ciSources = $ciSources | append . }}
28+
{{ end }}
29+
{{ end }}
30+
{{ end }}
31+
obtainBadges({
32+
'target': 'ci-dataset',
33+
'ci_sources': {{ $ciSources | jsonify | safeJS }}
34+
});
2535
</script>
2636
</div>
2737
</div>

‎layouts/partials/ci.html‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
21
<script type="text/javascript" src="/js/dashboard.v2.js"></script>
32
<div class='results'>
43
<div id="ci-dataset">
54
{{ partial "spinner.html" . }}
65
</div>
76
<script>
8-
obtainBadges({ 'target':'ci-dataset', 'filter_field':'pattern', 'filter_value': '{{ .Params.ci }}', 'disable_buttons': true });
7+
obtainBadges({
8+
'target': 'ci-dataset',
9+
'filter_field': 'pattern',
10+
'filter_value': '{{ .Params.ci }}',
11+
'disable_buttons': true,
12+
'ci_sources': {{ .Params.ci_sources | default (slice) | jsonify | safeJS }}
13+
});
914
</script>
1015
</div>

‎static/js/dashboard.v2.js‎

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44

55
class Badge {
66

7-
constructor(base, key, date) {
7+
constructor(base, key, date, useJsonMetadata = true) {
88
this.base = base
99
this.key = key
1010
this._json = null
11+
this.useJsonMetadata = useJsonMetadata
1112
this.operator = 'N/A'
1213
this.date = date.substr(0, 10)
1314
this.pattern = ''
1415
this.platform = ''
1516
this.version = ''
1617

17-
if (isNewCiBadgeBase(base)) {
18+
if (this.useJsonMetadata) {
1819
return
1920
}
2021

@@ -36,7 +37,7 @@ class Badge {
3637
applyJson(json) {
3738
this._json = json
3839

39-
if (!isNewCiBadgeBase(this.base)) {
40+
if (!this.useJsonMetadata) {
4041
return this
4142
}
4243

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

89+
// patternRepo comes from external badge JSON; only link to known GitHub orgs.
90+
var TRUSTED_PATTERN_REPO_PATH_PREFIXES = [
91+
'/validatedpatterns/',
92+
'/validatedpatterns-sandbox/'
93+
]
94+
95+
// Returns true when repoUrl is an https://github.com URL under a trusted org.
96+
function isTrustedPatternRepo(repoUrl) {
97+
if (repoUrl == null || repoUrl === '') {
98+
return false
99+
}
100+
try {
101+
var url = new URL(String(repoUrl).trim())
102+
if (url.protocol !== 'https:') {
103+
return false
104+
}
105+
if (url.username !== '' || url.password !== '') {
106+
return false
107+
}
108+
if (url.hostname.toLowerCase() !== 'github.com') {
109+
return false
110+
}
111+
var path = url.pathname.toLowerCase()
112+
if (path.length > 1 && path.charAt(path.length - 1) !== '/') {
113+
path += '/'
114+
}
115+
for (var i = 0; i < TRUSTED_PATTERN_REPO_PATH_PREFIXES.length; i++) {
116+
var prefix = TRUSTED_PATTERN_REPO_PATH_PREFIXES[i]
117+
if (path.indexOf(prefix) === 0 && path.length > prefix.length) {
118+
return true
119+
}
120+
}
121+
return false
122+
} catch (e) {
123+
return false
124+
}
125+
}
126+
127+
// Only the old bucket uses filename parsing, new CI badge bucket / external raw badges — hydrate metadata from JSON, not filename.
128+
function usesJsonMetadata(base) {
129+
if (base == null || base === '' || String(base).indexOf('storage.googleapis.com/vp-results') !== -1 ) {
130+
return false
131+
}
132+
return true
133+
}
134+
88135
function excludeRetiredOcpVersionsFromDashboard(badges) {
89136
if (!badges || badges.length === 0) {
90137
return badges || []
@@ -157,14 +204,6 @@ var CI_PATTERN_DOC_SLUG = {
157204
vsk: 'virtualization-starter-kit'
158205
}
159206

160-
// New CI badge bucket (S3 keys under ci-badges/) — distinct from legacy vp-results filenames.
161-
162-
var NEW_CI_BADGE_KEY_PREFIX = "https://vp-qe-ci-badges"
163-
164-
function isNewCiBadgeBase(base) {
165-
return base != null && base.startsWith(NEW_CI_BADGE_KEY_PREFIX)
166-
}
167-
168207
function normalizeInfraProvider(provider) {
169208
if (provider == null || provider === '') {
170209
return null
@@ -318,6 +357,9 @@ function stringForKey(key) {
318357
if (key == 'nutanix') {
319358
return 'Nutanix'
320359
}
360+
if (key == 'rhoso-gitops') {
361+
return 'RHOSO GitOps'
362+
}
321363
return key
322364
}
323365

@@ -505,7 +547,7 @@ function renderBadgeHtml(badge, field) {
505547
}
506548

507549
let envLink = encodeURI(badge_url)
508-
let branchLink = json_obj.patternRepo != null ? encodeURI(json_obj.patternRepo) : null
550+
let branchLink = isTrustedPatternRepo(json_obj.patternRepo) ? encodeURI(json_obj.patternRepo) : null
509551
let badgeText = '<span class="ci-label">'
510552
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>'
511553
if (branchLink != null) {
@@ -632,7 +674,8 @@ function platformDisplayName(platform) {
632674
'azr': 'Azure',
633675
'gcp': 'Google Cloud',
634676
'nutanix': 'Nutanix',
635-
'intel': 'On-prem (Intel)'
677+
'intel': 'On-prem (Intel)',
678+
'baremetal': 'Baremetal',
636679
}
637680
return names[platform] || stringForKey(platform)
638681
}
@@ -843,6 +886,7 @@ function renderDashboardTableRow(badge) {
843886
function isProductionSite() {
844887
var host = window.location.hostname
845888
return host === 'validatedpatterns.io' ||
889+
host === 'netlify.app' ||
846890
host === 'localhost' ||
847891
host === '127.0.0.1'
848892
}
@@ -1063,7 +1107,7 @@ function renderPatternCard(pattern, platformBadges, comboBadges, tracker) {
10631107
var platformColor = tracker.platforms[b.platform] || 'unavailable'
10641108
html += '<span class="ci-card-platform" title="' + platformDisplayName(b.platform) + '">'
10651109
html += '<span class="ci-card-platform-dot ' + platformColor + '" id="ci-card-dot-' + patternId + '-' + sanitizeId(b.platform) + '"></span>'
1066-
html += '<span class="ci-card-platform-label">' + stringForKey(b.platform) + '</span>'
1110+
html += '<span class="ci-card-platform-label">' + platformDisplayName(b.platform) + '</span>'
10671111
html += '</span>'
10681112
})
10691113
html += '</div>'
@@ -1283,12 +1327,8 @@ function getBadges(xmlText, bucket_url, badge_set) {
12831327

12841328
for (let i = 0; i < l; i++) {
12851329
let key = entries[i].childNodes[0].nodeValue
1286-
if (badge_set == "GA" && key.endsWith("stable-badge.json")) {
1287-
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
1288-
} else if (badge_set == "early" && (key.endsWith("prerelease-badge.json") || key.endsWith("nightly-badge.json") || key.endsWith("operator-badge.json"))) {
1289-
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
1290-
} else if (badge_set == "all" && key.endsWith("-badge.json")) {
1291-
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i])));
1330+
if (badgeKeyMatchesSet(key, badge_set)) {
1331+
badges.push(new Badge(bucket_url, key, getBadgeDate(entries[i]), usesJsonMetadata(bucket_url)))
12921332
} else {
12931333
console.log("Skipping: " + key);
12941334
}
@@ -1379,6 +1419,36 @@ function getBucketOptions(input) {
13791419
return options
13801420
}
13811421

1422+
function badgeKeyMatchesSet(key, badge_set) {
1423+
if (badge_set === 'GA' && key.endsWith('stable-badge.json')) {
1424+
return true
1425+
}
1426+
if (badge_set === 'early' && (key.endsWith('prerelease-badge.json') || key.endsWith('nightly-badge.json') || key.endsWith('operator-badge.json'))) {
1427+
return true
1428+
}
1429+
if (badge_set === 'all' && key.endsWith('-badge.json')) {
1430+
return true
1431+
}
1432+
return false
1433+
}
1434+
1435+
function createBadgesFromExternalSources(sources, badge_set) {
1436+
const badges = []
1437+
const sourceList = sources || []
1438+
sourceList.forEach(function (source) {
1439+
const base = String(source.base || '').replace(/\/$/, '')
1440+
const files = source.files || []
1441+
files.forEach(function (file) {
1442+
if (!badgeKeyMatchesSet(file, badge_set)) {
1443+
return
1444+
}
1445+
// Date filled from JSON during enrich
1446+
badges.push(new Badge(base, file, '1970-01-01T00:00:00Z'))
1447+
})
1448+
})
1449+
return badges
1450+
}
1451+
13821452
function fetchBucketBadges(bucket, inputs) {
13831453
return new Promise(function (resolve) {
13841454
var req = new XMLHttpRequest()
@@ -1450,6 +1520,11 @@ function obtainBadges(inputs) {
14501520
}
14511521
allBadges.push.apply(allBadges, badges || [])
14521522
})
1523+
var ciSources = createBadgesFromExternalSources(inputs.ci_sources, options.get('sets'))
1524+
if (ciSources.length > 0) {
1525+
console.log('Got ' + ciSources.length + ' external badges')
1526+
allBadges.push.apply(allBadges, ciSources)
1527+
}
14531528

14541529
console.log('All badges:', allBadges)
14551530
return enrichBadgesFromJson(allBadges)

0 commit comments

Comments
 (0)