247 lines
7.4 KiB
JavaScript
247 lines
7.4 KiB
JavaScript
const github = require('@actions/github')
|
|
const fs = require('fs/promises')
|
|
const isPlainObject = require('lodash/isPlainObject')
|
|
const orderBy = require('lodash/orderBy')
|
|
const find = require('lodash/find')
|
|
const slice = require('lodash/slice')
|
|
const round = require('lodash/round')
|
|
const { ChartJSNodeCanvas } = require('chartjs-node-canvas')
|
|
|
|
const chartJSNodeCanvas = new ChartJSNodeCanvas({ type: 'svg', width: 850, height: 300, backgroundColour: '#FFFFFF' })
|
|
|
|
async function main () {
|
|
const token = 'YOUR_TOKEN_HERE'
|
|
const gh = github.getOctokit(token)
|
|
const owner = 'ietf-tools'
|
|
const repo = 'datatracker'
|
|
const repoCommon = 'common'
|
|
|
|
// -> Fetch existing releases
|
|
const releases = []
|
|
let hasMoreReleases = false
|
|
let releasesCurPage = 0
|
|
do {
|
|
hasMoreReleases = false
|
|
releasesCurPage++
|
|
const resp = await gh.request('GET /repos/{owner}/{repo}/releases', {
|
|
owner,
|
|
repo,
|
|
page: releasesCurPage,
|
|
per_page: 100
|
|
})
|
|
if (resp?.data?.length > 0) {
|
|
console.info(`Fetching existing releases... ${(releasesCurPage - 1) * 100}`)
|
|
hasMoreReleases = true
|
|
releases.push(...resp.data)
|
|
}
|
|
} while (hasMoreReleases)
|
|
console.info(`Found ${releases.length} existing releases.`)
|
|
|
|
// -> Load full coverage file
|
|
console.info('Loading coverage results file...')
|
|
const rawCoverage = await fs.readFile('data/release-coverage.json', 'utf8')
|
|
const coverage = JSON.parse(rawCoverage)
|
|
|
|
// -> Parse and reorder results
|
|
const versions = []
|
|
for (const [key, value] of Object.entries(coverage)) {
|
|
if (isPlainObject(value)) {
|
|
versions.push({
|
|
tag: key,
|
|
time: value?.time,
|
|
stats: {
|
|
code: value?.code?.coverage || 0,
|
|
template: value?.template?.coverage || 0,
|
|
url: value?.url?.coverage || 0
|
|
}
|
|
})
|
|
}
|
|
}
|
|
const oVersions = orderBy(versions, ['time', 'tag'], ['desc', 'desc'])
|
|
const roVersions = orderBy(versions, ['time', 'tag'], ['asc', 'asc'])
|
|
|
|
// -> Fetch list of existing chart files in common repo
|
|
console.info('Fetching list of existing chart files from common repo...')
|
|
const chartsDirListing = []
|
|
const respDir = await gh.request('GET /repos/{owner}/{repo}/contents/{path}', {
|
|
owner,
|
|
repo: repoCommon,
|
|
path: 'assets/graphs/datatracker'
|
|
})
|
|
if (respDir?.data?.length > 0) {
|
|
chartsDirListing.push(...respDir.data)
|
|
}
|
|
|
|
// -> Upload release coverage
|
|
for (const [idx, value] of oVersions.entries()) {
|
|
const rel = find(releases, ['tag_name', value.tag])
|
|
if (!rel) { continue }
|
|
|
|
// -> Full Coverage File
|
|
if (rel?.assets?.some(a => a.name === 'coverage.json')) {
|
|
console.info(`Coverage file already exists for ${value.tag}, skipping...`)
|
|
} else {
|
|
console.info(`Building coverage object for ${value.tag}...`)
|
|
const covData = Buffer.from(JSON.stringify({
|
|
[value.tag]: coverage[value.tag],
|
|
version: value.tag
|
|
}), 'utf8')
|
|
|
|
console.info(`Uploading coverage file for ${value.tag}...`)
|
|
await gh.rest.repos.uploadReleaseAsset({
|
|
data: covData,
|
|
owner,
|
|
repo,
|
|
release_id: rel.id,
|
|
name: 'coverage.json',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
}
|
|
|
|
// -> Historical Coverage File
|
|
if (rel?.assets?.some(a => a.name === 'historical-coverage.json')) {
|
|
console.info(`Historical Coverage file already exists for ${value.tag}, skipping...`)
|
|
} else {
|
|
console.info(`Building historical coverage object for ${value.tag}...`)
|
|
const final = {}
|
|
for (const obj of slice(oVersions, idx)) {
|
|
final[obj.tag] = obj.stats
|
|
}
|
|
|
|
const covData = Buffer.from(JSON.stringify(final), 'utf8')
|
|
|
|
console.info(`Uploading historical coverage file for ${value.tag}...`)
|
|
await gh.rest.repos.uploadReleaseAsset({
|
|
data: covData,
|
|
owner,
|
|
repo,
|
|
release_id: rel.id,
|
|
name: 'historical-coverage.json',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
}
|
|
|
|
// -> Coverage Chart
|
|
if (chartsDirListing.some(c => c.name === `${rel.id}.svg`)) {
|
|
console.info(`Chart SVG already exists for ${rel.name}, skipping...`)
|
|
} else {
|
|
console.info(`Generating chart SVG for ${rel.name}...`)
|
|
const labels = []
|
|
const datasetCode = []
|
|
const datasetTemplate = []
|
|
const datasetUrl = []
|
|
for (const obj of slice(roVersions, 0, roVersions.length - idx)) {
|
|
labels.push(obj.tag)
|
|
datasetCode.push(round(obj.stats.code * 100, 2))
|
|
datasetTemplate.push(round(obj.stats.template * 100, 2))
|
|
datasetUrl.push(round(obj.stats.url * 100, 2))
|
|
}
|
|
|
|
const outputStream = chartJSNodeCanvas.renderToBufferSync({
|
|
type: 'line',
|
|
options: {
|
|
borderColor: '#CCC',
|
|
layout: {
|
|
padding: 20
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: {
|
|
font: {
|
|
size: 11
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: {
|
|
font: {
|
|
size: 10
|
|
}
|
|
}
|
|
},
|
|
y: {
|
|
ticks: {
|
|
callback: (value) => {
|
|
return `${value}%`
|
|
},
|
|
font: {
|
|
size: 10
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
data: {
|
|
labels,
|
|
datasets: [
|
|
{
|
|
label: 'Code',
|
|
data: datasetCode,
|
|
borderWidth: 2,
|
|
borderColor: '#E53935',
|
|
backgroundColor: '#C6282833',
|
|
fill: false,
|
|
cubicInterpolationMode: 'monotone',
|
|
tension: 0.4,
|
|
pointRadius: 0
|
|
},
|
|
{
|
|
label: 'Templates',
|
|
data: datasetTemplate,
|
|
borderWidth: 2,
|
|
borderColor: '#039BE5',
|
|
backgroundColor: '#0277BD33',
|
|
fill: false,
|
|
cubicInterpolationMode: 'monotone',
|
|
tension: 0.4,
|
|
pointRadius: 0
|
|
},
|
|
{
|
|
label: 'URLs',
|
|
data: datasetUrl,
|
|
borderWidth: 2,
|
|
borderColor: '#7CB342',
|
|
backgroundColor: '#558B2F33',
|
|
fill: false,
|
|
cubicInterpolationMode: 'monotone',
|
|
tension: 0.4,
|
|
pointRadius: 0
|
|
}
|
|
]
|
|
}
|
|
}, 'image/svg+xml')
|
|
const svg = Buffer.from(outputStream).toString('base64')
|
|
|
|
console.info(`Uploading chart SVG for ${rel.name}...`)
|
|
await gh.rest.repos.createOrUpdateFileContents({
|
|
owner,
|
|
repo: repoCommon,
|
|
path: `assets/graphs/datatracker/${rel.id}.svg`,
|
|
message: `chore: update datatracker release chart for release ${rel.name}`,
|
|
content: svg
|
|
})
|
|
}
|
|
|
|
if (rel.body.includes(`${rel.id}.svg`)) {
|
|
console.info(`Release ${rel.name} body already contains the chart SVG, skipping...`)
|
|
} else {
|
|
console.info(`Appending chart SVG to release ${rel.name} body...`)
|
|
await gh.request('PATCH /repos/{owner}/{repo}/releases/{release_id}', {
|
|
owner,
|
|
repo,
|
|
release_id: rel.id,
|
|
body: `${rel.body}\r\n\r\n`
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
main()
|