//Setup export default async function({login, q, imports, data, rest, graphql, queries, account}, {enabled = false} = {}) { //Plugin execution try { //Check if plugin is enabled and requirements are met if ((!enabled)||(!q.contributors)) return null //Load inputs let {head, base, ignored, contributions} = imports.metadata.plugins.contributors.inputs({data, account, q}) const repo = {owner:data.repo.owner.login, repo:data.repo.name} //Retrieve head and base commits console.debug(`metrics/compute/${login}/plugins > contributors > querying api head and base commits`) const ref = { head:(await graphql(queries.contributors.commit({...repo, expression:head}))).repository.object, base:(await graphql(queries.contributors.commit({...repo, expression:base}))).repository.object, } //Get commit activity console.debug(`metrics/compute/${login}/plugins > contributors > querying api for commits between [${ref.base?.abbreviatedOid ?? null}] and [${ref.head?.abbreviatedOid ?? null}]`) const commits = [] for (let page = 1; ; page++) { console.debug(`metrics/compute/${login}/plugins > contributors > loading page ${page}`) try { const {data:loaded} = await rest.repos.listCommits({...repo, per_page:100, page}) if (loaded.map(({sha}) => sha).includes(ref.base?.oid)) { console.debug(`metrics/compute/${login}/plugins > contributors > reached ${ref.base?.oid}`) commits.push(...loaded.slice(0, loaded.map(({sha}) => sha).indexOf(ref.base.oid))) break } if (!loaded.length) { console.debug(`metrics/compute/${login}/plugins > contributors > no more page to load`) break } commits.push(...loaded) } catch (error) { if (/Git Repository is empty/.test(error)) break throw error } } //Remove commits after head const start = Math.max(0, commits.map(({sha}) => sha).indexOf(ref.head?.oid)) commits.splice(0, start) console.debug(`metrics/compute/${login}/plugins > contributors > ${commits.length} commits loaded (${start} removed)`) //Compute contributors and contributions let contributors = {} for (const {author:{login, avatar_url:avatar}, commit:{message = ""}} of commits) { if ((!login)||(ignored.includes(login))) { console.debug(`metrics/compute/${login}/plugins > contributors > ignored contributor "${login}"`) continue } if (!(login in contributors)) contributors[login] = {avatar:await imports.imgb64(avatar), contributions:1, pr:[]} else { contributors[login].contributions++ contributors[login].pr.push(...(message.match(/(?<=[(])#\d+(?=[)])/g) ?? [])) } } contributors = Object.fromEntries(Object.entries(contributors).sort(([_an, a], [_bn, b]) => b.contributions - a.contributions)) //Filter pull requests for (const contributor of Object.values(contributors)) contributor.pr = [...new Set(contributor.pr)] //Results return {head, base, ref, list:contributors, contributions} } //Handle errors catch (error) { throw {error:{message:"An error occured", instance:error}} } }