datatracker/dev/coverage-action/fix-old-releases.js
2022-03-31 01:56:31 -04:00

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![chart](https://raw.githubusercontent.com/${owner}/${repoCommon}/main/assets/graphs/datatracker/${rel.id}.svg)`
})
}
}
}
main()