Add new plugin topics (#12)

This commit is contained in:
Simon Lecoq
2020-12-08 12:37:09 +01:00
committed by GitHub
parent 7f77d33bb9
commit 9a3f05dab0
12 changed files with 221 additions and 10 deletions

View File

@@ -180,3 +180,47 @@ jobs:
plugin_gists: yes
plugins_errors_fatal: yes
dryrun: yes
- name: Topics plugin (stars)
uses: lowlighter/metrics@master
with:
token: ${{ secrets.METRICS_TOKEN }}
base: ""
repositories: 1
plugin_topics: yes
plugin_topics_sort: stars
plugins_errors_fatal: yes
dryrun: yes
- name: Topics plugin (activity)
uses: lowlighter/metrics@master
with:
token: ${{ secrets.METRICS_TOKEN }}
base: ""
repositories: 1
plugin_topics: yes
plugin_topics_sort: activity
plugins_errors_fatal: yes
dryrun: yes
- name: Topics plugin (starred)
uses: lowlighter/metrics@master
with:
token: ${{ secrets.METRICS_TOKEN }}
base: ""
repositories: 1
plugin_topics: yes
plugin_topics_sort: starred
plugins_errors_fatal: yes
dryrun: yes
- name: Topics plugin (random)
uses: lowlighter/metrics@master
with:
token: ${{ secrets.METRICS_TOKEN }}
base: ""
repositories: 1
plugin_topics: yes
plugin_topics_sort: random
plugins_errors_fatal: yes
dryrun: yes

View File

@@ -206,6 +206,29 @@ inputs:
description: Display gists metrics
default: no
# Topics plugins
# Display starred topics
plugin_topics:
description: Display starred topics
default: no
# Sorting method of topics
# Supported values are :
# - "stars" to sort them from most starred to least starred
# - "activity" to sort them from most recent activity to least recent activity
# - "starred" to sort them from your most recently starred to your least recently starred
# - "random" to sort them randomly
plugin_topics_sort:
description: Sorting method of starred topics
default: "stars"
# Limit the number of topics displayed
# Between 1 and 20
# If more topics must be displayed, they will be grouped in an ellipsis
plugin_topics_limit:
description: Number of starred topics to display
default: 15
# Enable debug mode
# Be sure to put all secrets in your repository secrets before to avoid any leaks !
debug:

16
action/dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -83,6 +83,7 @@
posts:{enabled:bool(core.getInput("plugin_posts"))},
isocalendar:{enabled:bool(core.getInput("plugin_isocalendar"))},
gists:{enabled:bool(core.getInput("plugin_gists"))},
topics:{enabled:bool(core.getInput("plugin_topics"))},
}
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
console.log(`Plugins enabled | ${Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).join(", ")}`)
@@ -115,6 +116,13 @@
q["isocalendar.duration"] = core.getInput("plugin_isocalendar_duration") ?? "half-year"
console.log(`Isocalendar duration| ${q["isocalendar.duration"]}`)
}
//Topics
if (plugins.topics.enabled) {
for (const option of ["sort", "limit"])
q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null
console.log(`Topics sort mode | ${q["topics.sort"]}`)
console.log(`Topics limit | ${q["topics.limit"]}`)
}
//Repositories to use
const repositories = Number(core.getInput("repositories")) || 100

View File

@@ -1,6 +1,6 @@
{
"name": "metrics",
"version": "2.5.0",
"version": "2.6.0",
"description": "Generate an user's GitHub metrics as SVG image format to embed somewhere else",
"main": "index.mjs",
"scripts": {

View File

@@ -47,6 +47,9 @@
},
"gists":{ "//":"Gists plugin",
"enabled":false, "//":"Enable or disable gists metrics"
},
"topics":{ "//":"Topics plugin",
"enabled":false, "//":"Enable or disable starred topics display"
}
}
}

View File

@@ -32,6 +32,7 @@
posts:"Recent posts",
isocalendar:"Isometric commit calendar",
gists:"Gists metrics",
topics:"Starred topics",
"base.header":"Header",
"base.activity":"Account activity",
"base.community":"Community stats",

View File

@@ -48,7 +48,7 @@
//Compute metrics
console.debug(`metrics/compute/${login} > compute`)
const computer = Templates[template].default || Templates[template]
await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, format, bytes, shuffle}})
await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, format, bytes, shuffle, htmlescape}})
const promised = await Promise.all(pending)
//Check plugins errors
@@ -113,6 +113,16 @@
return array
}
/** Escape html */
function htmlescape(string) {
return string
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;")
}
/** Placeholder generator */
function placeholder({data, conf, q}) {
//Proxifier
@@ -154,6 +164,7 @@
followup:{issues:{count:0}, pr:{count:0}},
habits:{indents:{style:`########`}},
languages:{favorites:new Array(7).fill(null).map((_, x) => ({x, name:`######`, color:"#ebedf0", value:1/(x+1)}))},
topics:{list:[...new Array(12).fill(null).map(() => ({name:`######`, description:`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`, icon:null})), {name:`And ## more...`, description:"", icon:null}]},
}[key]??{})]
)),
token:{scopes:[]},

View File

@@ -11,6 +11,7 @@
import pagespeed from "./pagespeed/index.mjs"
import posts from "./posts/index.mjs"
import selfskip from "./selfskip/index.mjs"
import topics from "./topics/index.mjs"
import traffic from "./traffic/index.mjs"
//Exports
@@ -25,5 +26,6 @@
pagespeed,
posts,
selfskip,
topics,
traffic,
}

View File

@@ -0,0 +1,67 @@
//Setup
export default async function ({login, imports, q}, {enabled = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
if ((!enabled)||(!q.topics))
return null
//Parameters override
let {"topics.sort":sort = "stars", "topics.limit":limit = 15} = q
//Shuffle
const shuffle = (sort === "random")
//Sort method
sort = {starred:"created", activity:"updated", stars:"stars", random:"created"}[sort] ?? "starred"
//Limit
limit = Math.max(1, Math.min(20, Number(limit)))
//Start puppeteer and navigate to topics
let topics = []
console.debug(`metrics/compute/${login}/plugins > topics > starting browser`)
const browser = await imports.puppeteer.launch({headless:true, executablePath:process.env.PUPPETEER_BROWSER_PATH, args:["--no-sandbox", "--disable-extensions", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]})
console.debug(`metrics/compute/${login}/plugins > topics > loaded ${await browser.version()}`)
const page = await browser.newPage()
//Iterate through pages
for (let i = 1; i <= 100; i++) {
//Load page
console.debug(`metrics/compute/${login}/plugins > topics > loading page ${i}`)
await page.goto(`https://github.com/stars/${login}/topics?direction=desc&page=${i}&sort=${sort}`)
const frame = page.mainFrame()
//Extract topics
await Promise.race([frame.waitForSelector("ul.repo-list"), frame.waitForSelector(".blankslate")])
const starred = await frame.evaluate(() => [...document.querySelectorAll("ul.repo-list li")].map(li => ({
name:li.querySelector(".f3").innerText,
description:li.querySelector(".f5").innerText,
icon:li.querySelector("img")?.src ?? null,
})))
//Check if next page exists
if (!starred.length)
break
topics.push(...starred)
}
//Shuffle topics
if (shuffle)
topics = imports.shuffle(topics)
//Limit topics
if (limit > 0) {
console.debug(`metrics/compute/${login}/plugins > topics > keeping only ${limit} topics`)
const removed = topics.slice(limit)
topics = topics.slice(0, limit)
topics.push({name:`And ${removed.length} more...`, description:removed.map(({name}) => name).join(", "), icon:null})
}
//Convert icons to base64
for (const topic of topics) {
if (topic.icon) {
console.debug(`metrics/compute/${login}/plugins > topics > processing ${topic.name}`)
topic.icon = await imports.imgb64(topic.icon)
}
//Escape HTML description
topic.description = imports.htmlescape(topic.description)
}
//Results
return {list:topics}
}
//Handle errors
catch (error) {
console.debug(error)
throw {error:{message:`An error occured`}}
}
}

View File

@@ -12,6 +12,7 @@
+ (!!computed.plugins.posts)*64 + (computed.plugins.posts ? computed.plugins.posts.posts ? Math.max(0, computed.plugins.posts.posts.length)*40 : 0 : 0)
+ (!!computed.plugins.isocalendar)*192 + (computed.plugins.isocalendar ? computed.plugins.isocalendar.duration === 'full-year' ? 100 : 0 : 0)
+ (!!computed.plugins.gists)*68
+ (!!computed.plugins.topics)*160
+ Math.max(0, (((!!base.metadata)+(!!base.header)+((!!base.activity)||(!!base.community))+(!!base.repositories)+((!!computed.plugins.habits))+(!!computed.plugins.pagespeed)+(!!computed.plugins.languages)+(!!computed.plugins.music)+(!!computed.plugins.posts)+(!!computed.plugins.isocalendar)+(!!computed.plugins.gists))-1))*4
%>">
@@ -396,6 +397,33 @@
</section>
<% } %>
<% if (computed.plugins.topics) { %>
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M14.184 1.143a1.75 1.75 0 00-2.502-.57L.912 7.916a1.75 1.75 0 00-.53 2.32l.447.775a1.75 1.75 0 002.275.702l11.745-5.656a1.75 1.75 0 00.757-2.451l-1.422-2.464zm-1.657.669a.25.25 0 01.358.081l1.422 2.464a.25.25 0 01-.108.35l-2.016.97-1.505-2.605 1.85-1.26zM9.436 3.92l1.391 2.41-5.42 2.61-.942-1.63 4.97-3.39zM3.222 8.157l-1.466 1a.25.25 0 00-.075.33l.447.775a.25.25 0 00.325.1l1.598-.769-.83-1.436zm6.253 2.306a.75.75 0 00-.944-.252l-1.809.87a.75.75 0 00-.293.253L4.38 14.326a.75.75 0 101.238.848l1.881-2.75v2.826a.75.75 0 001.5 0v-2.826l1.881 2.75a.75.75 0 001.238-.848l-2.644-3.863z"></path></svg>
Starred topics
</h2>
<div class="row">
<% if (computed.plugins.topics.error) { %>
<section>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= computed.plugins.topics.error.message %>
</div>
</section>
<% } else { %>
<section>
<div class="topics fill-width">
<% for (const {name, description} of computed.plugins.topics.list) { %>
<div class="label" title="<%= description %>"><%= name.toLocaleLowerCase() %></div>
<% } %>
</div>
</section>
<% } %>
</div>
</section>
<% } %>
<% if (computed.plugins.music) { %>
<section>
<h2 class="field">

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -113,6 +113,24 @@
opacity: .7;
}
/* Labels */
.label {
background-color: #F1F8FF;
color: #0366D6;
padding: 0 10px;
font-weight: 500;
line-height: 22px;
margin: 2px 5px;
white-space: nowrap;
border-radius: 32px;
font-size: 12px;
}
.label:hover {
background-color: #DDEEFF;
cursor: pointer;
}
/* Habits */
.habits {
margin: 0;
@@ -242,6 +260,12 @@
padding-top: 1px;
}
/* Topics */
.topics {
display: flex;
flex-wrap: wrap;
}
/* Fade animation */
.af {
opacity: 0;