Contributors plugin: Display contributors per contribution category (#443)

This commit is contained in:
Simon Lecoq
2021-08-02 13:41:03 +02:00
committed by GitHub
parent 938bc5c869
commit 2676954194
8 changed files with 148 additions and 22 deletions

View File

@@ -6,7 +6,10 @@ It's especially useful to acknowledge contributors on release notes.
<table>
<td align="center">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.contributors.svg">
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.contributors.categories.svg">
<details><summary>Raw list with names</summary>
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.contributors.svg">
</details>
<details><summary>With number of contributions</summary>
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.contributors.contributions.svg">
</details>
@@ -14,6 +17,24 @@ It's especially useful to acknowledge contributors on release notes.
</td>
</table>
**Displaying contributors per categories**
> 🔣 On web instances, sorting contributors per categories is an extra feature and must be enabled globally in `settings.json`
To configure contributions categories, pass a JSON object to `plugin_contributors_categories` (use `|` multiline operator for better readability) with categories names as keys and an array of file glob as values:
```yaml
plugin_contributors_categories: |
{
"📚 Documentation": ["README.md", "docs/**"],
"💻 Code": ["source/**", "src/**"],
"#️⃣ Others": ["*"]
}
```
Each time a file modified by a contributor match a fileglob, they will be added in said category.
Matching is performed in keys order.
#### Examples workflows
[➡️ Available options for this plugin](metadata.yml)
@@ -23,8 +44,9 @@ It's especially useful to acknowledge contributors on release notes.
with:
# ... other options
plugin_contributors: yes
plugin_contributors_base: "" # Base reference (commit, tag, branch, etc.)
plugin_contributors_head: master # Head reference (commit, tag, branch, etc.)
plugin_contributors_ignored: bot # Ignore "bot" user
plugin_contributors_contributions: yes # Display number of contributions for each contributor
plugin_contributors_base: "" # Base reference (commit, tag, branch, etc.)
plugin_contributors_head: main # Head reference (commit, tag, branch, etc.)
plugin_contributors_ignored: bot # Ignore "bot" user
plugin_contributors_contributions: yes # Display number of contributions for each contributor
plugin_contributors_sections: contributors # Display contributors sections
```

View File

@@ -1,5 +1,5 @@
//Setup
export default async function({login, q, imports, data, rest, graphql, queries, account}, {enabled = false} = {}) {
export default async function({login, q, imports, data, rest, graphql, queries, account}, {enabled = false, extras = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
@@ -7,7 +7,7 @@ export default async function({login, q, imports, data, rest, graphql, queries,
return null
//Load inputs
let {head, base, ignored, contributions} = imports.metadata.plugins.contributors.inputs({data, account, q})
let {head, base, ignored, contributions, sections, categories} = imports.metadata.plugins.contributors.inputs({data, account, q})
const repo = {owner:data.repo.owner.login, repo:data.repo.name}
//Retrieve head and base commits
@@ -67,8 +67,57 @@ export default async function({login, q, imports, data, rest, graphql, queries,
for (const contributor of Object.values(contributors))
contributor.pr = [...new Set(contributor.pr)]
//Contributions categories
const types = Object.fromEntries([...new Set(Object.keys(categories))].map(type => [type, new Set()]))
if ((sections.includes("categories"))&&(extras)) {
//Temporary directory
const repository = `${repo.owner}/${repo.repo}`
const path = imports.paths.join(imports.os.tmpdir(), `${repository.replace(/[^\w]/g, "_")}`)
console.debug(`metrics/compute/${login}/plugins > contributors > cloning ${repository} to temp dir ${path}`)
try {
//Git clone into temporary directory
await imports.fs.rm(path, {recursive:true, force:true})
await imports.fs.mkdir(path, {recursive:true})
const git = await imports.git(path)
await git.clone(`https://github.com/${repository}`, ".").status()
//Analyze contributors' contributions
for (const contributor in contributors) {
//Load edited files by contributor
const files = []
await imports.spawn("git", ["--no-pager", "log", `--author="${contributor}"`, "--regexp-ignore-case", "--no-merges", "--name-only", '--pretty=format:""'], {cwd:path}, {
stdout(line) {
if (line.trim().length)
files.push(line)
}
})
//Search for contributions type in specified categories
filesloop: for (const file of files) {
for (const [category, globs] of Object.entries(categories)) {
for (const glob of [globs].flat(Infinity)) {
if (imports.minimatch(file, glob, {nocase:true})) {
types[category].add(contributor)
continue filesloop
}
}
}
}
}
}
catch (error) {
console.debug(error)
console.debug(`metrics/compute/${login}/plugins > contributors > an error occured while processing ${repository}`)
}
finally {
//Cleaning
console.debug(`metrics/compute/${login}/plugins > contributors > cleaning temp dir ${path}`)
await imports.fs.rm(path, {recursive:true, force:true})
}
}
//Results
return {head, base, ref, list:contributors, contributions}
return {head, base, ref, list:contributors, categories:types, contributions, sections}
}
//Handle errors
catch (error) {

View File

@@ -37,3 +37,30 @@ inputs:
description: Display contributions
type: boolean
default: no
# Sections to display
plugin_contributors_sections:
description: Sections to display
type: array
format: comma-separated
default: contributors
example: contributors
values:
- contributors # Display all contributors
- categories # Display contributors per contributions categories
# Contributions categories
# This requires "plugin_contributors_sections" to have "categories" in it to be effective
#
# Pass a JSON object which contains a mapping of category with fileglobs.
# Contributors will be sorted into each category to reflect their contributions.
# Note that order a file will only match the first category matching
plugin_contributors_categories:
description: Contributions categories
type: json
default: |
{
"📚 Documentation": ["README.md", "docs/**"],
"💻 Code": ["source/**", "src/**"],
"#️⃣ Others": ["*"]
}