163 lines
7.1 KiB
JavaScript
163 lines
7.1 KiB
JavaScript
//Setup
|
|
export default async function({login, q, imports, data, graphql, queries, account}, {enabled = false, extras = false} = {}) {
|
|
//Plugin execution
|
|
try {
|
|
//Check if plugin is enabled and requirements are met
|
|
if ((!enabled) || (!extras) || (!q.licenses))
|
|
return null
|
|
|
|
//Load inputs
|
|
let {setup, ratio, legal} = imports.metadata.plugins.licenses.inputs({data, account, q})
|
|
|
|
//Initialization
|
|
const {user: {repository}} = await graphql(queries.licenses.repository({owner: data.repo.owner.login, name: data.repo.name, account}))
|
|
const result = {ratio, legal, default: repository.licenseInfo, licensed: {available: false}, text: {}, list: [], used: {}, dependencies: [], known: 0, unknown: 0}
|
|
const {used, text} = result
|
|
|
|
//Register existing licenses properties
|
|
const licenses = Object.fromEntries((await graphql(queries.licenses())).licenses.map(license => [license.key, license]))
|
|
for (const license of Object.values(licenses)) {
|
|
//dprint-ignore
|
|
[...license.limitations, ...license.conditions, ...license.permissions].flat().map(({key, label}) => text[key] = label)
|
|
}
|
|
colors(licenses)
|
|
|
|
//Check if licensed exists
|
|
if (await imports.which("licensed")) {
|
|
//Setup for licensed
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > searching dependencies licenses using licensed`)
|
|
const path = imports.paths.join(imports.os.tmpdir(), `${repository.databaseId}`)
|
|
//Create temporary directory
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > creating temp dir ${path}`)
|
|
await imports.fs.rm(path, {recursive: true, force: true})
|
|
await imports.fs.mkdir(path, {recursive: true})
|
|
//Clone repository
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > cloning temp git repository ${repository.url} to ${path}`)
|
|
const git = imports.git(path)
|
|
await git.clone(repository.url, path)
|
|
//Run setup
|
|
if (setup) {
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > running setup [${setup}]`)
|
|
await imports.run(setup, {cwd: path}, {prefixed: false})
|
|
}
|
|
//Create configuration file if needed
|
|
if (!(await imports.fs.stat(imports.paths.join(path, ".licensed.yml")).then(() => 1).catch(() => 0))) {
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > building .licensed.yml configuration file`)
|
|
await imports.fs.writeFile(
|
|
imports.paths.join(path, ".licensed.yml"),
|
|
[
|
|
"cache_path: .licensed",
|
|
].join("\n"),
|
|
)
|
|
}
|
|
else {
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > a .licensed.yml configuration file already exists`)
|
|
}
|
|
|
|
//Spawn licensed process
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > running licensed`)
|
|
JSON.parse(await imports.run("licensed list --format=json --licenses", {cwd: path})).apps
|
|
.map(({sources}) =>
|
|
sources?.flatMap(source =>
|
|
source.dependencies?.map(({dependency, license}) => {
|
|
used[license] = (used[license] ?? 0) + 1
|
|
result.dependencies.push(dependency)
|
|
result.known += license in licenses
|
|
result.unknown += !(license in licenses)
|
|
})
|
|
)
|
|
)
|
|
//Cleaning
|
|
console.debug(`metrics/compute/${login}/plugins > licensed > cleaning temp dir ${path}`)
|
|
await imports.fs.rm(path, {recursive: true, force: true})
|
|
}
|
|
else {
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > licensed not available`)
|
|
}
|
|
|
|
//List licenses properties
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > compute licenses properties`)
|
|
const base = {permissions: new Set(), limitations: new Set(), conditions: new Set()}
|
|
const combined = {permissions: new Set(), limitations: new Set(), conditions: new Set()}
|
|
const detected = Object.entries(used).map(([key, _value]) => ({key}))
|
|
for (const properties of Object.keys(base)) {
|
|
//Base license
|
|
if (repository.licenseInfo)
|
|
licenses[repository.licenseInfo.key]?.[properties]?.map(({key}) => base[properties].add(key))
|
|
//Combined licenses
|
|
for (const {key} of detected)
|
|
licenses[key]?.[properties]?.map(({key}) => combined[properties].add(key))
|
|
}
|
|
|
|
//Merge limitations and conditions
|
|
for (const properties of ["limitations", "conditions"])
|
|
result[properties] = [[...base[properties]].map(key => ({key, text: text[key], inherited: false})), [...combined[properties]].filter(key => !base[properties].has(key)).map(key => ({key, text: text[key], inherited: true}))].flat()
|
|
//Remove base permissions conflicting with inherited limitations
|
|
result.permissions = [...base.permissions].filter(key => !combined.limitations.has(key)).map(key => ({key, text: text[key]}))
|
|
|
|
//Count used licenses
|
|
console.debug(`metrics/compute/${login}/plugins > licenses > computing ratio`)
|
|
const total = Object.values(used).reduce((a, b) => a + b, 0)
|
|
//Format used licenses and compute positions
|
|
const list = Object.entries(used).map(([key, count]) => ({name: licenses[key]?.spdxId ?? `${key.charAt(0).toLocaleUpperCase()}${key.substring(1)}`, key, count, value: count / total, x: 0, color: licenses[key]?.color ?? "#6e7681", order: licenses[key]?.order ?? -1})).sort((
|
|
a,
|
|
b,
|
|
) => a.order === b.order ? b.count - a.count : b.order - a.order)
|
|
for (let i = 0; i < list.length; i++)
|
|
list[i].x = (list[i - 1]?.x ?? 0) + (list[i - 1]?.value ?? 0)
|
|
//Save ratios
|
|
result.list = list
|
|
|
|
//Results
|
|
return result
|
|
}
|
|
//Handle errors
|
|
catch (error) {
|
|
throw {error: {message: "An error occured", instance: error}}
|
|
}
|
|
}
|
|
|
|
/**Licenses colorizer (based on category) */
|
|
function colors(licenses) {
|
|
for (const [license, value] of Object.entries(licenses)) {
|
|
const [permissions, conditions] = [value.permissions, value.conditions].map(properties => properties.map(({key}) => key))
|
|
switch (true) {
|
|
//Other licenses
|
|
case (license === "other"): {
|
|
value.color = "#8b949e"
|
|
value.order = 0
|
|
break
|
|
}
|
|
//Strongly protective licenses and network protective
|
|
case ((conditions.includes("disclose-source")) && (conditions.includes("same-license")) && (conditions.includes("network-use-disclose"))): {
|
|
value.color = "#388bfd"
|
|
value.order = 1
|
|
break
|
|
}
|
|
//Strongly protective licenses
|
|
case ((conditions.includes("disclose-source")) && (conditions.includes("same-license"))): {
|
|
value.color = "#79c0ff"
|
|
value.order = 2
|
|
break
|
|
}
|
|
//Weakly protective licenses
|
|
case ((conditions.includes("disclose-source")) && (conditions.includes("same-license--library"))): {
|
|
value.color = "#7ee787"
|
|
value.order = 3
|
|
break
|
|
}
|
|
//Permissive license
|
|
case ((permissions.includes("private-use")) && (permissions.includes("commercial-use")) && (permissions.includes("modifications")) && (permissions.includes("distribution"))): {
|
|
value.color = "#56d364"
|
|
value.order = 4
|
|
break
|
|
}
|
|
//Unknown
|
|
default: {
|
|
value.color = "#6e7681"
|
|
value.order = -1
|
|
}
|
|
}
|
|
}
|
|
}
|