refactor: standardize external SVG badge parsing (#493)

* refactor(codacy): standardize svg parsing

* refactor(melpa): standardize svg parsing

* refactor(synk): standardize svg parsing

* refactor: introduce `is-badge` helper
pull/496/head^2
Dario Vladović 2021-02-14 03:02:28 +01:00 zatwierdzone przez GitHub
rodzic dad2a6ff56
commit 9ad05785d2
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 54 dodań i 33 usunięć

Wyświetl plik

@ -1,5 +1,5 @@
import got from '../libs/got'
import { coverage as cov, coverageColor } from '../libs/utils'
import { isBadge, coverage as cov, coverageColor } from '../libs/utils'
import { createBadgenHandler, PathArgs } from '../libs/create-badgen-handler'
const CODACY_API_URL = 'https://api.codacy.com/'
@ -38,35 +38,37 @@ async function handler ({ type, projectId, branch }: PathArgs) {
const searchParams = new URLSearchParams()
if (branch) searchParams.append('branch', branch)
const endpoint = `project/badge/${type}/${projectId}`
const svg = await client.get(endpoint, { searchParams }).text()
const subject = SUBJECT_BY_TYPE[type] || 'codacy'
const status = svg.match(/\/>\s*<text.*?>([^<]+)<\//i)?.[1].trim() ?? ''
const resp = await client.get(endpoint, { searchParams })
const params = isBadge(resp) && parseBadge(resp.body, type)
return params || {
subject: 'codacy',
status: 'unknown',
color: 'grey'
}
}
function parseBadge(svg: string, type: string) {
const subject = SUBJECT_BY_TYPE[type]
switch (type) {
case 'coverage': {
const status = svg.match(/text-anchor=[^>]*?>([^<]+)<\//i)?.[1].trim() ?? ''
const percentage = parseFloat(status)
if (Number.isNaN(percentage)) break
if (Number.isNaN(percentage)) return
return {
subject,
subject: subject || 'codacy',
status: cov(percentage),
color: coverageColor(percentage)
}
}
case 'grade': {
const status = svg.match(/visibility=[^>]*?>([^<]+)<\//i)?.[1].trim() ?? ''
const color = COLORS_BY_GRADE[status]
if (!color) break
if (!status || !color) return
return {
subject,
subject: subject || 'codacy',
status,
color
}
}
}
return {
subject,
status: 'invalid',
color: 'grey'
}
}

Wyświetl plik

@ -1,5 +1,5 @@
import got from '../libs/got'
import { version, versionColor } from '../libs/utils'
import { isBadge, version, versionColor } from '../libs/utils'
import { createBadgenHandler, PathArgs } from '../libs/create-badgen-handler'
export default createBadgenHandler({
@ -8,18 +8,29 @@ export default createBadgenHandler({
'/melpa/v/magit': 'version'
},
handlers: {
'/melpa/:topic<v>/:pkg': handler
'/melpa/:topic<v|version>/:pkg': handler
}
})
async function handler ({ topic, pkg }: PathArgs) {
const badgeUrl = `https://melpa.org/packages/${pkg}-badge.svg`
const svg = await got(badgeUrl).text()
const title = svg.match(/<title>([^<]+)<\//i)?.[1].trim()
const ver = title?.split(':')?.[1]
const resp = await got(badgeUrl)
const params = isBadge(resp) && parseBadge(resp.body, topic)
return params || {
subject: 'melpa',
status: 'unknown',
color: 'grey'
}
}
function parseBadge(svg: string, topic: string) {
const title = svg.match(/<title>([^<]+)<\//i)?.[1].trim() ?? ''
const [_, ver] = title.split(':')
if (!ver) return
switch (topic) {
case 'v':
case 'version':
return {
subject: 'melpa',
status: version(ver),

Wyświetl plik

@ -1,4 +1,5 @@
import got from '../libs/got'
import { isBadge } from '../libs/utils'
import { createBadgenHandler, PathArgs } from '../libs/create-badgen-handler'
export default createBadgenHandler({
@ -18,23 +19,24 @@ async function handler ({ user, repo, branch, targetFile }: PathArgs) {
const badgeUrl = `https://snyk.io/test/github/${path}/badge.svg`
const searchParams = new URLSearchParams()
if (targetFile) searchParams.set('targetFile', targetFile)
const svg = await got(badgeUrl, { searchParams, timeout: 3500 }).text()
const subject = svg.match(/<text x="45"[^>]*?>([^<]+)<\//i)?.[1].trim()
const status = svg.match(/<text x="100"[^>]*?>([^<]+)<\//i)?.[1].trim()
const color = svg.match(/<path fill="([^"]+)" d="M90/i)?.[1].trim()
if (!status || !color) {
const context = [
`${user}/${repo}/${branch}`,
targetFile && `targetFile=${targetFile}`
].filter(Boolean).join(' ')
throw new Error(`Unknown Synk status: ${context}`)
const resp = await got(badgeUrl, { searchParams, timeout: 3500 })
const params = isBadge(resp) && parseBadge(resp.body)
return params || {
subject: 'snyk',
status: 'unknown',
color: 'grey'
}
}
function parseBadge(svg: string) {
const [subject, status] = [...svg.matchAll(/fill-opacity=[^>]*?>([^<]+)<\//ig)]
.map(match => match[1].trim())
const color = svg.match(/<path[^>]*?fill="([^"]+)"[^>]*?d="M[^0]/i)?.[1]
.trim().replace(/^#/, '')
if (!status || !color) return
return {
subject: subject || 'vulnerabilities',
status,
color: color.replace(/^#/, '')
color
}
}

Wyświetl plik

@ -2,6 +2,7 @@ import millify from 'millify'
import coverage from './coverage'
import coverageColor from './coverage-color'
import isBadge from './is-badge'
import scale from './scale'
import size from './size'
import stars from './stars'
@ -13,6 +14,7 @@ export {
millify,
coverage,
coverageColor,
isBadge,
scale,
size,
stars,

Wyświetl plik

@ -0,0 +1,4 @@
export default (resp: import('got').Response) => {
const contentType = resp.headers['content-type'] || ''
return contentType.includes('image/svg+xml')
}