feat(plugins/languages): count verified commits using user's gpg keys (#911)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import linguist from "linguist-js"
|
||||
|
||||
/**Indepth analyzer */
|
||||
export async function indepth({login, data, imports, repositories}, {skipped, categories, timeout}) {
|
||||
export async function indepth({login, data, imports, repositories, gpg}, {skipped, categories, timeout}) {
|
||||
return new Promise(async (solve, reject) => {
|
||||
//Timeout
|
||||
if (Number.isFinite(timeout)) {
|
||||
@@ -9,8 +9,31 @@ export async function indepth({login, data, imports, repositories}, {skipped, ca
|
||||
setTimeout(() => reject(`Reached maximum execution time of ${timeout}m for analysis`), timeout * 60 * 1000)
|
||||
}
|
||||
|
||||
//GPG keys imports
|
||||
for (const {id, pub} of gpg) {
|
||||
const path = imports.paths.join(imports.os.tmpdir(), `${data.user.databaseId}.${id}.gpg`)
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > saving gpg ${id} to ${path}`)
|
||||
try {
|
||||
await imports.fs.writeFile(path, pub)
|
||||
if (process.env.GITHUB_ACTIONS) {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > importing gpg ${id}`)
|
||||
await imports.run(`gpg --import ${path}`)
|
||||
}
|
||||
else
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > skipping import of gpg ${id}`)
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > indepth > an error occured while importing gpg ${id}, skipping...`)
|
||||
}
|
||||
finally {
|
||||
//Cleaning
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > indepth > cleaning ${path}`)
|
||||
await imports.fs.rm(path, {recursive:true, force:true})
|
||||
}
|
||||
}
|
||||
|
||||
//Compute repositories stats from fetched repositories
|
||||
const results = {total:0, lines:{}, stats:{}, colors:{}, commits:0, files:0, missed:0}
|
||||
const results = {total:0, lines:{}, stats:{}, colors:{}, commits:0, files:0, missed:0, verified:{signature:0}}
|
||||
for (const repository of repositories) {
|
||||
//Skip repository if asked
|
||||
if ((skipped.includes(repository.name.toLocaleLowerCase())) || (skipped.includes(`${repository.owner.login}/${repository.name}`.toLocaleLowerCase()))) {
|
||||
@@ -170,6 +193,7 @@ async function analyze({login, imports, data}, {results, path, categories = ["pr
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > indepth > repo seems empty or impossible to git log, skipping`)
|
||||
return
|
||||
}
|
||||
const pending = []
|
||||
for (let page = 0; ; page++) {
|
||||
try {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > indepth > processing commits ${page * per_page} from ${(page + 1) * per_page}`)
|
||||
@@ -182,6 +206,14 @@ async function analyze({login, imports, data}, {results, path, categories = ["pr
|
||||
empty = false
|
||||
//Commits counter
|
||||
if (/^commit [0-9a-f]{40}$/.test(line)) {
|
||||
if (results.verified) {
|
||||
const sha = line.match(/[0-9a-f]{40}/)?.[0]
|
||||
if (sha) {
|
||||
pending.push(imports.run(`git verify-commit ${sha}`, {cwd:path, env:{LANG:"en_GB"}}, {log:false, prefixed:false})
|
||||
.then(() => results.verified.signature++)
|
||||
.catch(() => null))
|
||||
}
|
||||
}
|
||||
results.commits++
|
||||
return
|
||||
}
|
||||
@@ -223,6 +255,7 @@ async function analyze({login, imports, data}, {results, path, categories = ["pr
|
||||
results.missed += per_page
|
||||
}
|
||||
}
|
||||
await Promise.allSettled(pending)
|
||||
results.files += edited.size
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,29 @@ export default async function({login, data, imports, q, rest, account}, {enabled
|
||||
|
||||
//Indepth mode
|
||||
if (indepth) {
|
||||
//Fetch gpg keys (web-flow is GitHub's public key when making changes from web ui)
|
||||
const gpg = []
|
||||
try {
|
||||
for (const username of [login, "web-flow"]) {
|
||||
const {data:keys} = await rest.users.listGpgKeysForUser({username})
|
||||
gpg.push(...keys.map(({key_id:id, raw_key:pub, emails}) => ({id, pub, emails})))
|
||||
if (username === login) {
|
||||
for (const {email} of gpg.flatMap(({emails}) => emails)) {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > auto-adding ${email} to commits_authoring (fetched from gpg)`)
|
||||
data.shared["commits.authoring"].push(email)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > ${error}`)
|
||||
}
|
||||
|
||||
//Analyze languages
|
||||
try {
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > switching to indepth mode (this may take some time)`)
|
||||
const existingColors = languages.colors
|
||||
Object.assign(languages, await indepth_analyzer({login, data, imports, repositories}, {skipped, categories, timeout}))
|
||||
Object.assign(languages, await indepth_analyzer({login, data, imports, repositories, gpg}, {skipped, categories, timeout}))
|
||||
Object.assign(languages.colors, existingColors)
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > indepth analysis missed ${languages.missed} commits`)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (plugins.languages.verified?.signature) { %>
|
||||
<div class="row footnote">
|
||||
<div class="field">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M9.585.52a2.678 2.678 0 00-3.17 0l-.928.68a1.178 1.178 0 01-.518.215L3.83 1.59a2.678 2.678 0 00-2.24 2.24l-.175 1.14a1.178 1.178 0 01-.215.518l-.68.928a2.678 2.678 0 000 3.17l.68.928c.113.153.186.33.215.518l.175 1.138a2.678 2.678 0 002.24 2.24l1.138.175c.187.029.365.102.518.215l.928.68a2.678 2.678 0 003.17 0l.928-.68a1.17 1.17 0 01.518-.215l1.138-.175a2.678 2.678 0 002.241-2.241l.175-1.138c.029-.187.102-.365.215-.518l.68-.928a2.678 2.678 0 000-3.17l-.68-.928a1.179 1.179 0 01-.215-.518L14.41 3.83a2.678 2.678 0 00-2.24-2.24l-1.138-.175a1.179 1.179 0 01-.518-.215L9.585.52zM7.303 1.728c.415-.305.98-.305 1.394 0l.928.68c.348.256.752.423 1.18.489l1.136.174c.51.078.909.478.987.987l.174 1.137c.066.427.233.831.489 1.18l.68.927c.305.415.305.98 0 1.394l-.68.928a2.678 2.678 0 00-.489 1.18l-.174 1.136a1.178 1.178 0 01-.987.987l-1.137.174a2.678 2.678 0 00-1.18.489l-.927.68c-.415.305-.98.305-1.394 0l-.928-.68a2.678 2.678 0 00-1.18-.489l-1.136-.174a1.178 1.178 0 01-.987-.987l-.174-1.137a2.678 2.678 0 00-.489-1.18l-.68-.927a1.178 1.178 0 010-1.394l.68-.928c.256-.348.423-.752.489-1.18l.174-1.136c.078-.51.478-.909.987-.987l1.137-.174a2.678 2.678 0 001.18-.489l.927-.68zM11.28 6.78a.75.75 0 00-1.06-1.06L7 8.94 5.78 7.72a.75.75 0 00-1.06 1.06l1.75 1.75a.75.75 0 001.06 0l3.75-3.75z"></path></svg>
|
||||
<%= plugins.languages.verified.signature %> commit<%= s(plugins.languages.verified.signature) %> verified by GPG
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</section>
|
||||
<% } %>
|
||||
|
||||
@@ -271,6 +271,12 @@
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.footnote {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Follow-up */
|
||||
.followup.legend {
|
||||
font-size: 12px;
|
||||
|
||||
Reference in New Issue
Block a user