From 2493e29ebc278003bf7b6ad1f38ae0cd5bb1e722 Mon Sep 17 00:00:00 2001
From: lowlighter <22963968+lowlighter@users.noreply.github.com>
Date: Thu, 17 Sep 2020 21:17:04 +0200
Subject: [PATCH] Add support for lines and traffic plugins
---
README.md | 38 +++++++++-
action.yml | 5 +-
action/dist/index.js | 122 +++++++++++++++++++++++++++++---
action/index.mjs | 8 ++-
package-lock.json | 35 +++++++++
package.json | 1 +
settings.example.json | 9 ++-
src/app.mjs | 21 ++++--
src/metrics.mjs | 6 +-
src/plugins/index.mjs | 6 +-
src/plugins/lines/index.mjs | 44 +++++++++++-
src/plugins/pagespeed/index.mjs | 6 +-
src/plugins/traffic/index.mjs | 34 ++++++++-
src/query.graphql | 1 +
src/template.svg | 12 ++++
15 files changed, 322 insertions(+), 26 deletions(-)
diff --git a/README.md b/README.md
index 7efe0a3d..b2fbe9b8 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ A GitHub Action which is run periodically at your convenience which generates an
Assuming your username is `my-github-user`, you can embed your metrics in your personal repository's readme like below :
```markdown

-# Or with a redirection :
+# Or with a redirection :
[](https://github.com/my-github-user/my-github-user)
```
```html
@@ -64,6 +64,10 @@ jobs:
token: ${{ secrets.METRICS_TOKEN }}
# Your GitHub user name
user: my-github-user
+ # If you own a website and you added it to your GitHub profile,
+ # You can provide a PageSpeed token to add your site's performance results on the metrics SVG image
+ # See https://developers.google.com/speed/docs/insights/v5/get-started to obtain a key
+ # pagespeed_token: ${{ secrets.PAGESPEED_TOKEN }}
```
On each run, a new SVG image will be generated and committed to your repository.
@@ -88,7 +92,7 @@ For conveniency, you can use the shared instance available at [metrics.lecoq.io]
Assuming your username is `my-github-user`, you can embed your metrics in your personal repository's readme like below :
```markdown

-# Or with a redirection :
+# Or with a redirection :
[](https://github.com/my-github-user/my-github-user)
```
@@ -99,6 +103,7 @@ Since GitHub API has rate limitations and to avoid abuse, the shared instance ha
* Images are cached for 1 day (meaning that your metrics won't be updated until the next day)
* A maximum of 1000 users can use this service
* You're limited to 3 requests per hour (cached metrics are not counted)
+ * Plugins are not available
You should consider deploying your own instance or use GitHub Action if you're planning using this service.
@@ -197,6 +202,34 @@ Open and edit `settings.json` to configure your instance.
//This is intendend for easier development which allows to see your changes quickly
//Defaults to false
"debug":false,
+
+ //Plugins configuration
+ //Most of plugins are disabled by default
+ //Enabling them can add additional informations and metrics about you, but increases response time
+ "plugins":{
+ //Pagespeed plugin
+ "pagespeed":{
+ //Enable or disable this plugin
+ //When enabled, pass "?pagespeed=1" in url to generate website's performances
+ "enabled":false,
+ //Pagespeed token
+ //See https://developers.google.com/speed/docs/insights/v5/get-started to obtain a key
+ "token":"****************************************"
+ },
+ //Lines plugin
+ "lines":{
+ //Enable or disable this plugin
+ //When enabled, pass "?lines=1" in url to compute total lines added/removed in your repositories by you
+ "enabled":true
+ },
+ //Traffic plugin
+ "traffic":{
+ //Enable or disable this plugin
+ //When enabled, pass "?traffic=1" in url to compute total page views in your repositories in last two weeks
+ //Note that this requires that the passed GitHub API token requires a push access
+ "enabled":true
+ }
+ }
}
```
@@ -289,6 +322,7 @@ Below is a list of useful documentations links :
* [GitHub GraphQL API](https://docs.github.com/en/graphql)
* [GitHub GraphQL Explorer](https://developer.github.com/v4/explorer/)
+* [GitHub Rest API](https://docs.github.com/en/rest)
## 📦 Used packages
diff --git a/action.yml b/action.yml
index fb14394c..b6bd5089 100644
--- a/action.yml
+++ b/action.yml
@@ -15,7 +15,10 @@ inputs:
description: Name of SVG image output
default: github-metrics.svg
pagespeed_token:
- description: Pagespeed Personal Token (optional, will generate user's website performances if provided). See https://developers.google.com/speed/docs/insights/v5/get-started for more information.
+ description: Pagespeed Personal Token (optional, see https://developers.google.com/speed/docs/insights/v5/get-started for more information)
+ plugins:
+ description: List of additional plugins to enabled. Supported values are "lines", "pagespeed" (requires "pagespeed_token") and "traffic" (require "token" with "repository" permissions)
+ default: []
runs:
using: node12
main: action/dist/index.js
\ No newline at end of file
diff --git a/action/dist/index.js b/action/dist/index.js
index fc5ff776..2ca958bd 100644
--- a/action/dist/index.js
+++ b/action/dist/index.js
@@ -142,6 +142,12 @@ __webpack_require__.r(__webpack_exports__);
\${data.user.packages.totalCount} Package\${data.user.packages.totalCount > 1 ? "s" : ""}
+ \${computed.plugins.lines ? \`
+
+
+ \${computed.plugins.lines.added} added, \${computed.plugins.lines.deleted} removed
+
\` : ""
+ }
@@ -153,6 +159,12 @@ __webpack_require__.r(__webpack_exports__);
\${data.computed.repositories.watchers} Watcher\${data.computed.repositories.watchers > 1 ? "s" : ""}
+ \${computed.plugins.traffic ? \`
+
+
+ \${computed.plugins.traffic.views.count} views in last two weeks
+
\` : ""
+ }
@@ -440,6 +452,7 @@ __webpack_require__.r(__webpack_exports__);
repositories(last: 100, isFork: false, ownerAffiliations: OWNER) {
totalCount
nodes {
+ name
watchers {
totalCount
}
@@ -528,11 +541,13 @@ __webpack_require__.r(__webpack_exports__);
const rest = github.getOctokit(token)
//Additional plugins
- const plugins = {}, q = {}
+ const enabled = new Set(core.getInput("plugins", {default:[]}))
+ const plugins = {lines:{enabled:enabled.has("lines")}, traffic:{enabled:enabled.has("traffic")}, pagespeed:{enabled:enabled.has("pagespeed")}}
if (core.getInput("pagespeed_token")) {
- plugins.pagespeed = {enabled:true, token:core.getInput("pagespeed_token")}
- q.pagespeed = true
+ console.log(`Pagespeed token | provided`)
+ plugins.pagespeed.token = core.getInput("pagespeed_token")
}
+ const q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
//Render metrics
const rendered = await metrics({login:user, q}, {template, style, query, graphql, plugins})
@@ -9656,17 +9671,18 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ });
/* harmony import */ var image_to_base64__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7192);
/* harmony import */ var image_to_base64__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(image_to_base64__WEBPACK_IMPORTED_MODULE_0__);
-/* harmony import */ var _plugins_index_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9859);
+/* harmony import */ var _plugins_index_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5820);
//Imports
//Setup
- async function metrics({login, q}, {template, style, query, graphql, plugins}) {
+ async function metrics({login, q}, {template, style, query, graphql, rest, plugins}) {
//Compute rendering
try {
//Query data from GitHub API
+ console.debug(`metrics/metrics/${login} > query`)
const data = await graphql(query
.replace(/[$]login/, `"${login}"`)
.replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`)
@@ -9682,6 +9698,8 @@ __webpack_require__.r(__webpack_exports__);
//Plugins
if (data.user.websiteUrl)
_plugins_index_mjs__WEBPACK_IMPORTED_MODULE_1__/* .default.pagespeed */ .Z.pagespeed({url:data.user.websiteUrl, computed, pending, q}, plugins.pagespeed)
+ _plugins_index_mjs__WEBPACK_IMPORTED_MODULE_1__/* .default.lines */ .Z.lines({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.lines)
+ _plugins_index_mjs__WEBPACK_IMPORTED_MODULE_1__/* .default.traffic */ .Z.traffic({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.traffic)
//Iterate through user's repositories
for (const repository of data.user.repositories.nodes) {
@@ -9728,6 +9746,7 @@ __webpack_require__.r(__webpack_exports__);
await Promise.all(pending)
//Eval rendering and return
+ console.debug(`metrics/metrics/${login} > computed`)
return eval(`\`${template}\``)
}
//Internal error
@@ -9736,7 +9755,7 @@ __webpack_require__.r(__webpack_exports__);
/***/ }),
-/***/ 9859:
+/***/ 5820:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
@@ -9746,6 +9765,51 @@ __webpack_require__.d(__webpack_exports__, {
"Z": () => /* default */ E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_index
});
+// CONCATENATED MODULE: E:\Users\lecoq\Documents\GitHub\gitstats\src\plugins\lines\index.mjs
+//Formatter
+ function format(n) {
+ for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
+ if (n/v >= 1)
+ return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
+ return n
+ }
+
+//Setup
+ /* harmony default export */ function E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_lines_index({login, repositories = [], rest, computed, pending, q}, {enabled = false} = {}) {
+ //Check if plugin is enabled and requirements are met
+ if (!enabled)
+ return computed.plugins.lines = null
+ if (!q.lines)
+ return computed.plugins.lines = null
+ console.debug(`metrics/plugins/lines/${login} > started`)
+
+ //Plugin execution
+ pending.push(new Promise(async solve => {
+ //Get contributors stats from repositories
+ const lines = {added:0, deleted:0}
+ const response = await Promise.all(repositories.map(async repo => await rest.repos.getContributorsStats({owner:login, repo})))
+ //Compute changed lines
+ response.map(({data:repository}) => {
+ //Check if data are available
+ if (!repository)
+ return
+ //Extract author
+ const [contributor] = repository.filter(({author}) => author.login === login)
+ //Compute editions
+ if (contributor)
+ contributor.weeks.forEach(({a, d}) => (lines.added += a, lines.deleted += d))
+ })
+ //Format values
+ lines.added = format(lines.added)
+ lines.deleted = format(lines.deleted)
+ //Save results
+ computed.plugins = {lines}
+ console.debug(`metrics/plugins/lines/${login} > ${JSON.stringify(computed.plugins.lines)}`)
+ solve()
+ }))
+ }
+
+
// EXTERNAL MODULE: E:\Users\lecoq\Documents\GitHub\gitstats\node_modules\axios\index.js
var E_Users_lecoq_Documents_GitHub_gitstats_node_modules_axios_index = __webpack_require__(2390);
var E_Users_lecoq_Documents_GitHub_gitstats_node_modules_axios_index_default = /*#__PURE__*/__webpack_require__.n(E_Users_lecoq_Documents_GitHub_gitstats_node_modules_axios_index);
@@ -9755,14 +9819,17 @@ var E_Users_lecoq_Documents_GitHub_gitstats_node_modules_axios_index_default = /
//Setup
- /* harmony default export */ function E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_pagespeed_index({url, computed, pending, q}, {enabled = false, token = null} = {}) {
+ /* harmony default export */ function E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_pagespeed_index({login, url, computed, pending, q}, {enabled = false, token = null} = {}) {
//Check if plugin is enabled and requirements are met
if (!enabled)
return computed.plugins.pagespeed = null
+ if (!token)
+ return computed.plugins.pagespeed = null
if (!url)
return computed.plugins.pagespeed = null
if (!q.pagespeed)
return computed.plugins.pagespeed = null
+ console.debug(`metrics/plugins/pagespeed/${login} > started`)
//Plugin execution
pending.push(new Promise(async solve => {
@@ -9777,16 +9844,55 @@ var E_Users_lecoq_Documents_GitHub_gitstats_node_modules_axios_index_default = /
}))
//Save results
computed.plugins.pagespeed = {url, scores:[scores.get("performance"), scores.get("accessibility"), scores.get("best-practices"), scores.get("seo")]}
+ console.debug(`metrics/plugins/pagespeed/${login} > ${JSON.stringify(computed.plugins.pagespeed)}`)
+ solve()
+ }))
+ }
+// CONCATENATED MODULE: E:\Users\lecoq\Documents\GitHub\gitstats\src\plugins\traffic\index.mjs
+//Formatter
+ function E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_traffic_index_format(n) {
+ for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
+ if (n/v >= 1)
+ return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
+ return n
+ }
+
+//Setup
+ /* harmony default export */ function E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_traffic_index({login, repositories = [], rest, computed, pending, q}, {enabled = false} = {}) {
+ //Check if plugin is enabled and requirements are met
+ if (!enabled)
+ return computed.plugins.traffic = null
+ if (!q.traffic)
+ return computed.plugins.traffic = null
+ console.debug(`metrics/plugins/traffic/${login} > started`)
+
+ //Plugin execution
+ pending.push(new Promise(async solve => {
+ //Get views stats from repositories
+ const views = {count:0, uniques:0}
+ const response = await Promise.all(repositories.map(async repo => await rest.repos.getViews({owner:login, repo})))
+ //Compute views
+ response.filter(({data}) => data).map(({data:{count, uniques}}) => (views.count += count, views.uniques += uniques))
+ //Format values
+ views.count = E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_traffic_index_format(views.count)
+ views.uniques = E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_traffic_index_format(views.uniques)
+ //Save results
+ computed.plugins.traffic = {views}
+ console.debug(`metrics/plugins/traffic/${login} > ${JSON.stringify(computed.plugins.traffic)}`)
solve()
}))
}
// CONCATENATED MODULE: E:\Users\lecoq\Documents\GitHub\gitstats\src\plugins\index.mjs
//Imports
+
+
//Exports
/* harmony default export */ const E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_index = ({
- pagespeed: E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_pagespeed_index
+ lines: E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_lines_index,
+ pagespeed: E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_pagespeed_index,
+ traffic: E_Users_lecoq_Documents_GitHub_gitstats_src_plugins_traffic_index,
});
/***/ }),
diff --git a/action/index.mjs b/action/index.mjs
index 0260e976..5e85854a 100644
--- a/action/index.mjs
+++ b/action/index.mjs
@@ -30,11 +30,13 @@
const rest = github.getOctokit(token)
//Additional plugins
- const plugins = {}, q = {}
+ const enabled = new Set(core.getInput("plugins", {default:[]}))
+ const plugins = {lines:{enabled:enabled.has("lines")}, traffic:{enabled:enabled.has("traffic")}, pagespeed:{enabled:enabled.has("pagespeed")}}
if (core.getInput("pagespeed_token")) {
- plugins.pagespeed = {enabled:true, token:core.getInput("pagespeed_token")}
- q.pagespeed = true
+ console.log(`Pagespeed token | provided`)
+ plugins.pagespeed.token = core.getInput("pagespeed_token")
}
+ const q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
//Render metrics
const rendered = await metrics({login:user, q}, {template, style, query, graphql, plugins})
diff --git a/package-lock.json b/package-lock.json
index f8076b74..3d286a6a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -77,6 +77,11 @@
"@octokit/types": "^5.3.0"
}
},
+ "@octokit/plugin-request-log": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz",
+ "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw=="
+ },
"@octokit/plugin-rest-endpoint-methods": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.4.tgz",
@@ -111,6 +116,36 @@
"once": "^1.4.0"
}
},
+ "@octokit/rest": {
+ "version": "18.0.6",
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.6.tgz",
+ "integrity": "sha512-ES4lZBKPJMX/yUoQjAZiyFjei9pJ4lTTfb9k7OtYoUzKPDLl/M8jiHqt6qeSauyU4eZGLw0sgP1WiQl9FYeM5w==",
+ "requires": {
+ "@octokit/core": "^3.0.0",
+ "@octokit/plugin-paginate-rest": "^2.2.0",
+ "@octokit/plugin-request-log": "^1.0.0",
+ "@octokit/plugin-rest-endpoint-methods": "4.2.0"
+ },
+ "dependencies": {
+ "@octokit/plugin-rest-endpoint-methods": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.0.tgz",
+ "integrity": "sha512-1/qn1q1C1hGz6W/iEDm9DoyNoG/xdFDt78E3eZ5hHeUfJTLJgyAMdj9chL/cNBHjcjd+FH5aO1x0VCqR2RE0mw==",
+ "requires": {
+ "@octokit/types": "^5.5.0",
+ "deprecation": "^2.3.1"
+ }
+ },
+ "@octokit/types": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz",
+ "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==",
+ "requires": {
+ "@types/node": ">= 8"
+ }
+ }
+ }
+ },
"@octokit/types": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.4.1.tgz",
diff --git a/package.json b/package.json
index 8d556f4d..eac1496a 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@actions/core": "^1.2.5",
"@actions/github": "^4.0.0",
"@octokit/graphql": "^4.5.4",
+ "@octokit/rest": "^18.0.6",
"axios": "^0.20.0",
"express": "^4.17.1",
"express-rate-limit": "^5.1.3",
diff --git a/settings.example.json b/settings.example.json
index 3a0259d5..68fc11c6 100644
--- a/settings.example.json
+++ b/settings.example.json
@@ -6,10 +6,17 @@
"ratelimiter":null, "//":"Rate limiter (see express-rate-limit documentation for options)",
"port":3000, "//":"Listening port",
"debug":false, "//":"Debug mode",
+
"plugins":{ "//":"Additional plugins (optional)",
- "pagespeed":{ "//":"Pagespeed configuration",
+ "pagespeed":{ "//":"Pagespeed plugin",
"enabled":false, "//":"Enable or disable Pagespeed metrics",
"token":"******", "//":"Pagespeed token"
+ },
+ "traffic":{ "//":"Traffic plugin (GitHub API token must be RW for this to work)",
+ "enabled":true, "//":"Enable or disable repositories total page views is last two weeks"
+ },
+ "lines":{ "//":"Lines plugin",
+ "enabled":true, "//":"Enable or disabled repositories total lines added/removed"
}
}
}
\ No newline at end of file
diff --git a/src/app.mjs b/src/app.mjs
index a7ff6ca9..0bab1610 100644
--- a/src/app.mjs
+++ b/src/app.mjs
@@ -3,6 +3,7 @@
import fs from "fs"
import path from "path"
import octokit from "@octokit/graphql"
+ import OctokitRest from "@octokit/rest"
import cache from "memory-cache"
import ratelimit from "express-rate-limit"
import metrics from "./metrics.mjs"
@@ -19,10 +20,11 @@
const settings = JSON.parse((await fs.promises.readFile(path.join("settings.json"))).toString())
const {token, maxusers = 0, restricted = [], debug = false, cached = 30*60*1000, port = 3000, ratelimiter = null, plugins = null} = settings
if (debug)
- console.log(settings)
+ console.debug(settings)
//Load svg template, style and query
let [template, style, query] = await load()
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
+ const rest = new OctokitRest.Octokit({auth:token})
//Setup server
const app = express()
@@ -51,8 +53,10 @@
//Request params
const {login} = req.params
- if ((restricted.length)&&(!restricted.includes(login)))
+ if ((restricted.length)&&(!restricted.includes(login))) {
+ console.debug(`metrics/app/${login} > 403 (not in whitelisted users)`)
return res.sendStatus(403)
+ }
//Read cached data if possible
if ((!debug)&&(cached)&&(cache.get(login))) {
@@ -61,15 +65,17 @@
return
}
//Maximum simultaneous users
- if ((maxusers)&&(cache.size()+1 > maxusers))
+ if ((maxusers)&&(cache.size()+1 > maxusers)) {
+ console.debug(`metrics/app/${login} > 503 (maximum users reached)`)
return res.sendStatus(503)
+ }
//Compute rendering
try {
//Render
if (debug)
[template, style, query] = await load()
- const rendered = await metrics({login, q:req.query}, {template, style, query, graphql, plugins})
+ const rendered = await metrics({login, q:req.query}, {template, style, query, graphql, rest, plugins})
//Cache
if ((!debug)&&(cached))
cache.put(login, rendered, cached)
@@ -80,8 +86,10 @@
//Internal error
catch (error) {
//Not found user
- if ((error instanceof Error)&&(/^user not found$/.test(error.message)))
+ if ((error instanceof Error)&&(/^user not found$/.test(error.message))) {
+ console.debug(`metrics/app/${login} > 404 (user not found)`)
return res.sendStatus(404)
+ }
//General error
console.error(error)
res.sendStatus(500)
@@ -95,6 +103,7 @@
`Restricted to users | ${restricted.size ? [...restricted].join(", ") : "(unrestricted)"}`,
`Cached time | ${cached} seconds`,
`Rate limiter | ${ratelimiter ? JSON.stringify(ratelimiter) : "(enabled)"}`,
- `Max simultaneous users | ${maxusers ? `${maxusers} users` : "(unrestricted)"}`
+ `Max simultaneous users | ${maxusers ? `${maxusers} users` : "(unrestricted)"}`,
+ `Plugins enabled | ${Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).join(", ")}`
].join("\n")))
}
\ No newline at end of file
diff --git a/src/metrics.mjs b/src/metrics.mjs
index d9cc4aee..40b4432c 100644
--- a/src/metrics.mjs
+++ b/src/metrics.mjs
@@ -3,11 +3,12 @@
import Plugins from "./plugins/index.mjs"
//Setup
- export default async function metrics({login, q}, {template, style, query, graphql, plugins}) {
+ export default async function metrics({login, q}, {template, style, query, graphql, rest, plugins}) {
//Compute rendering
try {
//Query data from GitHub API
+ console.debug(`metrics/metrics/${login} > query`)
const data = await graphql(query
.replace(/[$]login/, `"${login}"`)
.replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`)
@@ -23,6 +24,8 @@
//Plugins
if (data.user.websiteUrl)
Plugins.pagespeed({url:data.user.websiteUrl, computed, pending, q}, plugins.pagespeed)
+ Plugins.lines({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.lines)
+ Plugins.traffic({login, repositories:data.user.repositories.nodes.map(({name}) => name), rest, computed, pending, q}, plugins.traffic)
//Iterate through user's repositories
for (const repository of data.user.repositories.nodes) {
@@ -69,6 +72,7 @@
await Promise.all(pending)
//Eval rendering and return
+ console.debug(`metrics/metrics/${login} > computed`)
return eval(`\`${template}\``)
}
//Internal error
diff --git a/src/plugins/index.mjs b/src/plugins/index.mjs
index 39e6a4b9..aa3a4f8c 100644
--- a/src/plugins/index.mjs
+++ b/src/plugins/index.mjs
@@ -1,7 +1,11 @@
//Imports
+ import lines from "./lines/index.mjs"
import pagespeed from "./pagespeed/index.mjs"
+ import traffic from "./traffic/index.mjs"
//Exports
export default {
- pagespeed
+ lines,
+ pagespeed,
+ traffic,
}
\ No newline at end of file
diff --git a/src/plugins/lines/index.mjs b/src/plugins/lines/index.mjs
index dafd505d..df62392a 100644
--- a/src/plugins/lines/index.mjs
+++ b/src/plugins/lines/index.mjs
@@ -1 +1,43 @@
-//Placeholder for https://docs.github.com/en/rest/reference/repos#get-all-contributor-commit-activity
\ No newline at end of file
+//Formatter
+ function format(n) {
+ for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
+ if (n/v >= 1)
+ return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
+ return n
+ }
+
+//Setup
+ export default function ({login, repositories = [], rest, computed, pending, q}, {enabled = false} = {}) {
+ //Check if plugin is enabled and requirements are met
+ if (!enabled)
+ return computed.plugins.lines = null
+ if (!q.lines)
+ return computed.plugins.lines = null
+ console.debug(`metrics/plugins/lines/${login} > started`)
+
+ //Plugin execution
+ pending.push(new Promise(async solve => {
+ //Get contributors stats from repositories
+ const lines = {added:0, deleted:0}
+ const response = await Promise.all(repositories.map(async repo => await rest.repos.getContributorsStats({owner:login, repo})))
+ //Compute changed lines
+ response.map(({data:repository}) => {
+ //Check if data are available
+ if (!repository)
+ return
+ //Extract author
+ const [contributor] = repository.filter(({author}) => author.login === login)
+ //Compute editions
+ if (contributor)
+ contributor.weeks.forEach(({a, d}) => (lines.added += a, lines.deleted += d))
+ })
+ //Format values
+ lines.added = format(lines.added)
+ lines.deleted = format(lines.deleted)
+ //Save results
+ computed.plugins = {lines}
+ console.debug(`metrics/plugins/lines/${login} > ${JSON.stringify(computed.plugins.lines)}`)
+ solve()
+ }))
+ }
+
diff --git a/src/plugins/pagespeed/index.mjs b/src/plugins/pagespeed/index.mjs
index 147700f9..97b3e235 100644
--- a/src/plugins/pagespeed/index.mjs
+++ b/src/plugins/pagespeed/index.mjs
@@ -2,14 +2,17 @@
import axios from "axios"
//Setup
- export default function ({url, computed, pending, q}, {enabled = false, token = null} = {}) {
+ export default function ({login, url, computed, pending, q}, {enabled = false, token = null} = {}) {
//Check if plugin is enabled and requirements are met
if (!enabled)
return computed.plugins.pagespeed = null
+ if (!token)
+ return computed.plugins.pagespeed = null
if (!url)
return computed.plugins.pagespeed = null
if (!q.pagespeed)
return computed.plugins.pagespeed = null
+ console.debug(`metrics/plugins/pagespeed/${login} > started`)
//Plugin execution
pending.push(new Promise(async solve => {
@@ -24,6 +27,7 @@
}))
//Save results
computed.plugins.pagespeed = {url, scores:[scores.get("performance"), scores.get("accessibility"), scores.get("best-practices"), scores.get("seo")]}
+ console.debug(`metrics/plugins/pagespeed/${login} > ${JSON.stringify(computed.plugins.pagespeed)}`)
solve()
}))
}
\ No newline at end of file
diff --git a/src/plugins/traffic/index.mjs b/src/plugins/traffic/index.mjs
index 2402fad3..2ca2a436 100644
--- a/src/plugins/traffic/index.mjs
+++ b/src/plugins/traffic/index.mjs
@@ -1 +1,33 @@
-//Placeholder for https://docs.github.com/en/rest/reference/repos#get-page-views
\ No newline at end of file
+//Formatter
+ function format(n) {
+ for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
+ if (n/v >= 1)
+ return `${(n/v).toFixed(2).substr(0, 4).replace(/[.]0*$/, "")}${u}`
+ return n
+ }
+
+//Setup
+ export default function ({login, repositories = [], rest, computed, pending, q}, {enabled = false} = {}) {
+ //Check if plugin is enabled and requirements are met
+ if (!enabled)
+ return computed.plugins.traffic = null
+ if (!q.traffic)
+ return computed.plugins.traffic = null
+ console.debug(`metrics/plugins/traffic/${login} > started`)
+
+ //Plugin execution
+ pending.push(new Promise(async solve => {
+ //Get views stats from repositories
+ const views = {count:0, uniques:0}
+ const response = await Promise.all(repositories.map(async repo => await rest.repos.getViews({owner:login, repo})))
+ //Compute views
+ response.filter(({data}) => data).map(({data:{count, uniques}}) => (views.count += count, views.uniques += uniques))
+ //Format values
+ views.count = format(views.count)
+ views.uniques = format(views.uniques)
+ //Save results
+ computed.plugins.traffic = {views}
+ console.debug(`metrics/plugins/traffic/${login} > ${JSON.stringify(computed.plugins.traffic)}`)
+ solve()
+ }))
+ }
\ No newline at end of file
diff --git a/src/query.graphql b/src/query.graphql
index 9dd21e7c..8abf4e43 100644
--- a/src/query.graphql
+++ b/src/query.graphql
@@ -8,6 +8,7 @@ query Metrics {
repositories(last: 100, isFork: false, ownerAffiliations: OWNER) {
totalCount
nodes {
+ name
watchers {
totalCount
}
diff --git a/src/template.svg b/src/template.svg
index 0010fdaf..6765e3a4 100644
--- a/src/template.svg
+++ b/src/template.svg
@@ -110,6 +110,12 @@
${data.user.packages.totalCount} Package${data.user.packages.totalCount > 1 ? "s" : ""}
+ ${computed.plugins.lines ? `
+
+
+ ${computed.plugins.lines.added} added, ${computed.plugins.lines.deleted} removed
+
` : ""
+ }
@@ -121,6 +127,12 @@
${data.computed.repositories.watchers} Watcher${data.computed.repositories.watchers > 1 ? "s" : ""}
+ ${computed.plugins.traffic ? `
+
+
+ ${computed.plugins.traffic.views.count} views in last two weeks
+
` : ""
+ }