feat(plugins/code): add new code plugin (#526)
This commit is contained in:
27
source/plugins/code/README.md
Normal file
27
source/plugins/code/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
### ♐ Code snippet of the day
|
||||
|
||||
> ⚠️ When improperly configured, this plugin could display private code. If you work with sensitive data or company code, it is advised to keep this plugin disabled. *Metrics* and its authors cannot be held responsible for any resulting code leaks, use at your own risk.
|
||||
|
||||
Display a random code snippet from your recent activity history.
|
||||
|
||||
<table>
|
||||
<td align="center">
|
||||
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.code.svg">
|
||||
<img width="900" height="1" alt="">
|
||||
</td>
|
||||
</table>
|
||||
|
||||
#### ℹ️ Examples workflows
|
||||
|
||||
[➡️ Available options for this plugin](metadata.yml)
|
||||
|
||||
```yaml
|
||||
- uses: lowlighter/metrics@latest
|
||||
with:
|
||||
# ... other options
|
||||
plugin_code: yes
|
||||
plugin_code_lines: 12 # Only display snippets with less than 12 lines
|
||||
plugin_code_load: 100 # Fetch 100 events from activity
|
||||
plugin_code_visibility: public # Only display snippets from public activity
|
||||
plugin_code_skipped: github/octocat # Skip github/octocat repository
|
||||
```
|
||||
66
source/plugins/code/index.mjs
Normal file
66
source/plugins/code/index.mjs
Normal file
@@ -0,0 +1,66 @@
|
||||
//Setup
|
||||
export default async function({login, q, imports, data, rest, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
if ((!enabled)||(!q.code))
|
||||
return null
|
||||
|
||||
//Context
|
||||
let context = {mode:"user"}
|
||||
if (q.repo) {
|
||||
console.debug(`metrics/compute/${login}/plugins > code > switched to repository mode`)
|
||||
const {owner, repo} = data.user.repositories.nodes.map(({name:repo, owner:{login:owner}}) => ({repo, owner})).shift()
|
||||
context = {...context, mode:"repository", owner, repo}
|
||||
}
|
||||
|
||||
//Load inputs
|
||||
let {load, lines, visibility, skipped} = imports.metadata.plugins.code.inputs({data, q, account})
|
||||
skipped.push(...data.shared["repositories.skipped"])
|
||||
const pages = Math.ceil(load / 100)
|
||||
|
||||
//Get user recent code
|
||||
console.debug(`metrics/compute/${login}/plugins > code > querying api`)
|
||||
const events = []
|
||||
try {
|
||||
for (let page = 1; page <= pages; page++) {
|
||||
console.debug(`metrics/compute/${login}/plugins > code > loading page ${page}/${pages}`)
|
||||
events.push(...[...await Promise.all([...(context.mode === "repository" ? await rest.activity.listRepoEvents({owner:context.owner, repo:context.repo}) : await rest.activity.listEventsForAuthenticatedUser({username:login, per_page:100, page})).data
|
||||
.filter(({type}) => type === "PushEvent")
|
||||
.filter(({actor}) => account === "organization" ? true : actor.login?.toLocaleLowerCase() === login.toLocaleLowerCase())
|
||||
.filter(({repo:{name:repo}}) => !((skipped.includes(repo.split("/").pop())) || (skipped.includes(repo))))
|
||||
.filter(event => visibility === "public" ? event.public : true)
|
||||
.flatMap(({payload}) => Promise.all(payload.commits.map(async commit => (await rest.request(commit.url)).data)))])]
|
||||
.flat()
|
||||
.filter(({author}) => data.shared["commits.authoring"].filter(authoring => author?.email?.toLocaleLowerCase().includes(authoring)||author?.name?.toLocaleLowerCase().includes(authoring)))
|
||||
)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
console.debug(`metrics/compute/${login}/plugins > code > no more page to load`)
|
||||
}
|
||||
console.debug(`metrics/compute/${login}/plugins > code > ${events.length} events loaded`)
|
||||
|
||||
//Search for a random snippet
|
||||
const files = events
|
||||
.flatMap(({sha, commit:{message, url}, files}) => files.map(({filename, status, additions, deletions, patch}) => ({sha, message, filename, status, additions, deletions, patch, repo:url.match(/repos[/](?<repo>[\s\S]+)[/]git[/]commits/)?.groups?.repo})))
|
||||
.filter(({patch}) => (patch ? (patch.match(/\n/mg)?.length ?? 1) : Infinity) < lines)
|
||||
const snippet = files[Math.floor(Math.random()*files.length)]
|
||||
|
||||
//Trim common indent from content and change line feed
|
||||
if (!snippet.patch.split("\n").shift().endsWith("@@"))
|
||||
snippet.patch = snippet.patch.replace(/^(?<coord>@@.*?@@)/, "$<coord>\n")
|
||||
const indent = Math.min(...(snippet.patch.match(/^[+-]? +/mg)?.map(indent => (indent.length ?? Infinity) - indent.startsWith("+") - indent.startsWith("-")) ?? [])) || 0
|
||||
const content = imports.htmlescape(snippet.patch.replace(/\r\n/mg, "\n").replace(new RegExp(`^([+-]?)${" ".repeat(indent)}`, "mg"), "$1"))
|
||||
|
||||
//Format patch
|
||||
snippet.patch = imports.htmlunescape((await imports.highlight(content, "diff")).trim())
|
||||
|
||||
//Results
|
||||
return {snippet}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
46
source/plugins/code/metadata.yml
Normal file
46
source/plugins/code/metadata.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: "♐ Code snippet of the day"
|
||||
cost: 1 REST request per 100 events fetched
|
||||
category: github
|
||||
supports:
|
||||
- user
|
||||
- organization
|
||||
- repository
|
||||
inputs:
|
||||
|
||||
# Enable or disable plugin
|
||||
plugin_code:
|
||||
description: Display a random code snippet from recent activity
|
||||
type: boolean
|
||||
default: no
|
||||
|
||||
# Maximum number of lines that a code snippet can contain
|
||||
plugin_code_lines:
|
||||
description: Maximum number of line that a code snippet can contain
|
||||
type: number
|
||||
default: 12
|
||||
|
||||
# Number of activity events to load
|
||||
# A high number will consume more requests
|
||||
plugin_code_load:
|
||||
description: Number of events to load
|
||||
type: number
|
||||
default: 100
|
||||
min: 100
|
||||
max: 1000
|
||||
|
||||
# Set events visibility (use this to restrict events when using a "repo" token)
|
||||
plugin_code_visibility:
|
||||
description: Set events visibility
|
||||
type: string
|
||||
default: public
|
||||
values:
|
||||
- public
|
||||
- all
|
||||
|
||||
# List of repositories that will be skipped
|
||||
plugin_code_skipped:
|
||||
description: Repositories to skip
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: ""
|
||||
example: my-repo-1, my-repo-2, owner/repo-3 ...
|
||||
5
source/plugins/code/tests.yml
Normal file
5
source/plugins/code/tests.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Code plugin (default)
|
||||
uses: lowlighter/metrics@latest
|
||||
with:
|
||||
token: MOCKED_TOKEN
|
||||
plugin_code: yes
|
||||
Reference in New Issue
Block a user