Version 2.1
- No plugins are enabled by default - Logs from setup can be hidden - Number of repositories to inspect can be configured (default to 100) - Default events for habits plugin is now 100 - Number of events for habits can be overriden in query - Server app improvments - Test improvements - Better test
This commit is contained in:
1 parent
543c6f8f98
commit
2cf152d7d1
16 files changed
+254
-143
No files matched your search
@@ -0,0 +1,86 @@
|
|||||||
|
# 📊 GitHub metrics
|
||||||
|
|
||||||
|
## 💪 Interested in contributing ?
|
||||||
|
|
||||||
|
Nice ! Read the few sections below to understand how this project is structured.
|
||||||
|
|
||||||
|
### 👨💻 General informations
|
||||||
|
|
||||||
|
#### Adding new metrics through GraphQL API, REST API or Third-Party service
|
||||||
|
|
||||||
|
To use [GitHub GraphQL API](https://docs.github.com/en/graphql), update the GraphQL query from `templates/*/query.graphql`.
|
||||||
|
Raw queried data should be exposed in `data.user` whereas computed data should be in `data.computed`, and code should be updated through `templates/*/template.mjs`.
|
||||||
|
|
||||||
|
To use [GitHub Rest API](https://docs.github.com/en/rest) or a third-party service instead, create a new plugin in `src/plugins`.
|
||||||
|
Plugins should be self-sufficient and re-exported from [src/plugins/index.mjs](https://github.com/lowlighter/metrics/blob/master/src/plugins/index.mjs), to be later included in the `//Plugins` section of `templates/*/template.mjs`.
|
||||||
|
Data generated should be exposed in `data.computed.plugins[plugin]` where `plugin` is your plugin's name.
|
||||||
|
|
||||||
|
#### Updating the SVG template
|
||||||
|
|
||||||
|
The SVG template is located in `templates/*/image.svg` and include the CSS from `templates/*/style.css`.
|
||||||
|
|
||||||
|
It is rendered with [EJS](https://github.com/mde/ejs) so you can actually include variables (e.g. `<%= user.name %>`) and execute simple code, like control statements.
|
||||||
|
|
||||||
|
#### Metrics server and GitHub action
|
||||||
|
|
||||||
|
Most of the time, you won't need to edit these, unless you're integrating features directly tied to them.
|
||||||
|
Remember that SVG image is actually generated from `src/metrics.mjs`, independently from metrics server and GitHub action.
|
||||||
|
|
||||||
|
Metrics server code is located in `src/app.mjs` and instantiates an `express` server app, `octokit`s instances, middlewares (like rate-limiter) and routes.
|
||||||
|
|
||||||
|
GitHub action code is located in `action/index.mjs` and instantiates `octokit`s instances and retrieves action parameters.
|
||||||
|
It then use directly `src/metrics.mjs` to generate the SVG image and commit them to user's repository.
|
||||||
|
You must run `npm run build` to rebuild the GitHub action.
|
||||||
|
|
||||||
|
#### Testing new features
|
||||||
|
|
||||||
|
To test new features, setup a metrics server with a test token and `debug` mode enabled.
|
||||||
|
This way you'll be able to rapidly test SVG renders with your browser.
|
||||||
|
|
||||||
|
### 🗂️ Project structure
|
||||||
|
|
||||||
|
#### Metrics generator
|
||||||
|
|
||||||
|
* `src/setup.mjs` contains the configuration setup
|
||||||
|
* `src/metrics.mjs` contains the metrics renderer
|
||||||
|
* `src/templates/*` contains templates files
|
||||||
|
* `src/templates/*/image.svg` contains the template used by the generated SVG image
|
||||||
|
* `src/templates/*/query.graphql` is the GraphQL query sent to GitHub GraphQL API
|
||||||
|
* `src/templates/*/style.css` contains the style used by the generated SVG image
|
||||||
|
* `src/templates/*/template.mjs` contains the code which prepares data for rendering
|
||||||
|
* `src/plugins/*` contains the source code of metrics plugins
|
||||||
|
|
||||||
|
#### Metrics server instance
|
||||||
|
|
||||||
|
* `index.mjs` contains the metrics server entry point
|
||||||
|
* `src/app.mjs` contains the metrics server code which serves, renders, restricts/rate limit, etc.
|
||||||
|
|
||||||
|
#### GitHub action
|
||||||
|
|
||||||
|
* `action.yml` contains the GitHub action descriptor
|
||||||
|
* `action/index.mjs` contains the GitHub action code
|
||||||
|
* `action/dist/index.js` contains compiled the GitHub action code
|
||||||
|
* `utils/build.mjs` contains the GitHub action builder
|
||||||
|
|
||||||
|
### 📦 Used packages
|
||||||
|
|
||||||
|
* [express/express.js](https://github.com/expressjs/express) and [expressjs/compression](https://github.com/expressjs/compression)
|
||||||
|
* To serve, compute and render a GitHub user's metrics
|
||||||
|
* [nfriedly/express-rate-limit](https://github.com/nfriedly/express-rate-limit)
|
||||||
|
* To apply rate limiting on server and avoid spams and hitting GitHub API's own rate limit
|
||||||
|
* [octokit/graphql.js](https://github.com/octokit/graphql.js/) and [octokit/rest.js](https://github.com/octokit/rest.js)
|
||||||
|
* To perform request to GitHub GraphQL API and GitHub REST API
|
||||||
|
* [mde/ejs](https://github.com/mde/ejs)
|
||||||
|
* To render SVG images
|
||||||
|
* [ptarjan/node-cache](https://github.com/ptarjan/node-cache)
|
||||||
|
* To cache generated content
|
||||||
|
* [renanbastos93/image-to-base64](https://github.com/renanbastos93/image-to-base64)
|
||||||
|
* To generate base64 representation of users' avatars
|
||||||
|
* [svg/svgo](https://github.com/svg/svgo)
|
||||||
|
* To optimize generated SVG
|
||||||
|
* [axios/axios](https://github.com/axios/axios)
|
||||||
|
* To make HTTP/S requests
|
||||||
|
* [actions/toolkit](https://github.com/actions/toolkit/tree/master) and [vercel/ncc](https://github.com/vercel/ncc)
|
||||||
|
* To build the GitHub Action
|
||||||
|
* [vuejs/vue](https://github.com/vuejs/vue)
|
||||||
|
* To display server application
|
||||||
@@ -3,13 +3,19 @@
|
|||||||
 
|
 
|
||||||
|
|
||||||
Generates your own GitHub metrics as an SVG image to put them on your profile page or elsewhere !
|
Generates your own GitHub metrics as an SVG image to put them on your profile page or elsewhere !
|
||||||
See what it looks like below :
|
|
||||||
|

|
||||||
|
|
||||||
|
Still not enough data for you ? You can enable additional plugins for even more metrics !
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 🦑 Interested to get your own ?
|
### 🦑 Interested to get your own ?
|
||||||
|
|
||||||
Try it now at [metrics.lecoq.io](https://metrics.lecoq.io/) with your GitHub username !
|
Try it now at [metrics.lecoq.io](https://metrics.lecoq.io/) with your GitHub username !
|
||||||
|
|
||||||
|
For a fully-featured experience, setup this as a [GitHub Action](https://github.com/marketplace/actions/github-metrics-as-svg-image) !
|
||||||
|
|
||||||
## 📜 How to use ?
|
## 📜 How to use ?
|
||||||
|
|
||||||
### ⚙️ Using GitHub Action on your profile repo (~5 min setup)
|
### ⚙️ Using GitHub Action on your profile repo (~5 min setup)
|
||||||
@@ -150,9 +156,10 @@ Since GitHub API has rate limitations, the shared instance has a few limitations
|
|||||||
* Your generated metrics won't be updated during this amount of time
|
* Your generated metrics won't be updated during this amount of time
|
||||||
* If you enable or disable plugins in url parameters, you'll need to wait for cache expiration before these changes are applied
|
* If you enable or disable plugins in url parameters, you'll need to wait for cache expiration before these changes are applied
|
||||||
* The rate limiter is enabled, although it won't affect already cached users metrics
|
* The rate limiter is enabled, although it won't affect already cached users metrics
|
||||||
* Plugins which consume additional requests or require elevated token rights are disabled
|
* Plugins which consume additional requests or require elevated token rights are disabled. The following plugins are available :
|
||||||
* PageSpeed plugin can still be enabled by passing `?pagespeed=1`, but metrics generation can take up some time when it has not been cached yet
|
* PageSpeed plugin can be enabled by passing `?pagespeed=1`, but metrics generation can take up some time when it has not been cached yet
|
||||||
* Languages and Follow-up plugins are enabled by default by can be disabled respectively with `?languages=0` and `?followup=0`
|
* Languages plugin can be enabled by passing `?languages=1`
|
||||||
|
* Follow-up plugin can be enabled by passing `?followup=1`
|
||||||
|
|
||||||
To ensure maximum availability, consider deploying your own instance or use the GitHub Action.
|
To ensure maximum availability, consider deploying your own instance or use the GitHub Action.
|
||||||
|
|
||||||
@@ -267,7 +274,7 @@ Open and edit `settings.json` to configure your instance using a text editor of
|
|||||||
//Enable or disable this plugin. Pass "?habits=1" in url to generate coding habits based on your recent activity
|
//Enable or disable this plugin. Pass "?habits=1" in url to generate coding habits based on your recent activity
|
||||||
"enabled":true,
|
"enabled":true,
|
||||||
//Number of events used to compute coding habits (capped at 100 by GitHub API)
|
//Number of events used to compute coding habits (capped at 100 by GitHub API)
|
||||||
"from":50,
|
"from":100,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,8 +518,6 @@ The *follow-up* plugin allows you to compute the ratio of opened/closed issues a
|
|||||||
<details>
|
<details>
|
||||||
<summary>💬 About</summary>
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
This plugin is enabled by default. To disable it, explicitly opt-out.
|
|
||||||
|
|
||||||
##### Setup with GitHub actions
|
##### Setup with GitHub actions
|
||||||
|
|
||||||
Add the following to your workflow :
|
Add the following to your workflow :
|
||||||
@@ -545,8 +550,6 @@ The *languages* plugin allows you to compute which languages you use the most in
|
|||||||
<details>
|
<details>
|
||||||
<summary>💬 About</summary>
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
This plugin is enabled by default. To disable it, explicitly opt-out.
|
|
||||||
|
|
||||||
##### Setup with GitHub actions
|
##### Setup with GitHub actions
|
||||||
|
|
||||||
Add the following to your workflow :
|
Add the following to your workflow :
|
||||||
@@ -591,69 +594,13 @@ Add the following to your workflow :
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### 🗂️ Project structure
|
|
||||||
|
|
||||||
#### Metrics generator
|
|
||||||
|
|
||||||
* `src/setup.mjs` contains the configuration setup
|
|
||||||
* `src/metrics.mjs` contains the metrics renderer
|
|
||||||
* `src/templates/*` contains templates files
|
|
||||||
* `src/templates/*/image.svg` contains the template used by the generated SVG image
|
|
||||||
* `src/templates/*/query.graphql` is the GraphQL query sent to GitHub GraphQL API
|
|
||||||
* `src/templates/*/style.css` contains the style used by the generated SVG image
|
|
||||||
* `src/templates/*/template.mjs` contains the code which prepares data for rendering
|
|
||||||
* `src/plugins/*` contains the source code of metrics plugins
|
|
||||||
|
|
||||||
#### Metrics server instance
|
|
||||||
|
|
||||||
* `index.mjs` contains the metrics server entry point
|
|
||||||
* `src/app.mjs` contains the metrics server code which serves, renders, restricts/rate limit, etc.
|
|
||||||
|
|
||||||
#### GitHub action
|
|
||||||
|
|
||||||
* `action.yml` contains the GitHub action descriptor
|
|
||||||
* `action/index.mjs` contains the GitHub action code
|
|
||||||
* `action/dist/index.js` contains compiled the GitHub action code
|
|
||||||
* `utils/build.mjs` contains the GitHub action builder
|
|
||||||
|
|
||||||
### 💪 Contributing and customizing
|
### 💪 Contributing and customizing
|
||||||
|
|
||||||
If you would like to suggest a new feature, find a bug or need help, you can fill an [issue](https://github.com/lowlighter/metrics/issues) describing your problem.
|
If you would like to suggest a new feature, find a bug or need help, you can fill an [issue](https://github.com/lowlighter/metrics/issues) describing your problem.
|
||||||
|
|
||||||
If you're motivated enough, you can submit a [pull request](https://github.com/lowlighter/metrics/pulls) to integrate new features or to solve open issues.
|
If you're motivated enough, you can submit a [pull request](https://github.com/lowlighter/metrics/pulls) to integrate new features or to solve open issues.
|
||||||
|
|
||||||
Read the few sections below to get started with project structure.
|
Read [contributing.md](https://github.com/lowlighter/metrics/blob/master/CONTRIBUTING.md) for more information about this.
|
||||||
|
|
||||||
#### Adding new metrics through GraphQL API, REST API or Third-Party service
|
|
||||||
|
|
||||||
To use [GitHub GraphQL API](https://docs.github.com/en/graphql), update the GraphQL query from `templates/*/query.graphql`.
|
|
||||||
Raw queried data should be exposed in `data.user` whereas computed data should be in `data.computed`, and code should be updated through `templates/*/template.mjs`.
|
|
||||||
|
|
||||||
To use [GitHub Rest API](https://docs.github.com/en/rest) or a third-party service instead, create a new plugin in `src/plugins`.
|
|
||||||
Plugins should be self-sufficient and re-exported from [src/plugins/index.mjs](https://github.com/lowlighter/metrics/blob/master/src/plugins/index.mjs), to be later included in the `//Plugins` section of `templates/*/template.mjs`.
|
|
||||||
Data generated should be exposed in `data.computed.plugins[plugin]` where `plugin` is your plugin's name.
|
|
||||||
|
|
||||||
#### Updating the SVG template
|
|
||||||
|
|
||||||
The SVG template is located in `templates/*/image.svg` and include the CSS from `templates/*/style.css`.
|
|
||||||
|
|
||||||
It is rendered with [EJS](https://github.com/mde/ejs) so you can actually include variables (e.g. `<%= user.name %>`) and execute simple code, like control statements.
|
|
||||||
|
|
||||||
#### Metrics server and GitHub action
|
|
||||||
|
|
||||||
Most of the time, you won't need to edit these, unless you're integrating features directly tied to them.
|
|
||||||
Remember that SVG image is actually generated from `src/metrics.mjs`, independently from metrics server and GitHub action.
|
|
||||||
|
|
||||||
Metrics server code is located in `src/app.mjs` and instantiates an `express` server app, `octokit`s instances, middlewares (like rate-limiter) and routes.
|
|
||||||
|
|
||||||
GitHub action code is located in `action/index.mjs` and instantiates `octokit`s instances and retrieves action parameters.
|
|
||||||
It then use directly `src/metrics.mjs` to generate the SVG image and commit them to user's repository.
|
|
||||||
You must run `npm run build` to rebuild the GitHub action.
|
|
||||||
|
|
||||||
#### Testing new features
|
|
||||||
|
|
||||||
To test new features, setup a metrics server with a test token and `debug` mode enabled.
|
|
||||||
This way you'll be able to rapidly test SVG renders with your browser.
|
|
||||||
|
|
||||||
### 📖 Useful references
|
### 📖 Useful references
|
||||||
|
|
||||||
@@ -661,29 +608,6 @@ This way you'll be able to rapidly test SVG renders with your browser.
|
|||||||
* [GitHub GraphQL Explorer](https://developer.github.com/v4/explorer/)
|
* [GitHub GraphQL Explorer](https://developer.github.com/v4/explorer/)
|
||||||
* [GitHub Rest API](https://docs.github.com/en/rest)
|
* [GitHub Rest API](https://docs.github.com/en/rest)
|
||||||
|
|
||||||
### 📦 Used packages
|
|
||||||
|
|
||||||
* [express/express.js](https://github.com/expressjs/express) and [expressjs/compression](https://github.com/expressjs/compression)
|
|
||||||
* To serve, compute and render a GitHub user's metrics
|
|
||||||
* [nfriedly/express-rate-limit](https://github.com/nfriedly/express-rate-limit)
|
|
||||||
* To apply rate limiting on server and avoid spams and hitting GitHub API's own rate limit
|
|
||||||
* [octokit/graphql.js](https://github.com/octokit/graphql.js/) and [octokit/rest.js](https://github.com/octokit/rest.js)
|
|
||||||
* To perform request to GitHub GraphQL API and GitHub REST API
|
|
||||||
* [mde/ejs](https://github.com/mde/ejs)
|
|
||||||
* To render SVG images
|
|
||||||
* [ptarjan/node-cache](https://github.com/ptarjan/node-cache)
|
|
||||||
* To cache generated content
|
|
||||||
* [renanbastos93/image-to-base64](https://github.com/renanbastos93/image-to-base64)
|
|
||||||
* To generate base64 representation of users' avatars
|
|
||||||
* [svg/svgo](https://github.com/svg/svgo)
|
|
||||||
* To optimize generated SVG
|
|
||||||
* [axios/axios](https://github.com/axios/axios)
|
|
||||||
* To make HTTP/S requests
|
|
||||||
* [actions/toolkit](https://github.com/actions/toolkit/tree/master) and [vercel/ncc](https://github.com/vercel/ncc)
|
|
||||||
* To build the GitHub Action
|
|
||||||
* [vuejs/vue](https://github.com/vuejs/vue)
|
|
||||||
* To display server application
|
|
||||||
|
|
||||||
All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub.
|
All icons were ripped across GitHub's site, but still remains the intellectual property of GitHub.
|
||||||
See [GitHub Logos and Usage](https://github.com/logos) for more information.
|
See [GitHub Logos and Usage](https://github.com/logos) for more information.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -43,10 +43,10 @@ inputs:
|
|||||||
default: no
|
default: no
|
||||||
plugin_languages:
|
plugin_languages:
|
||||||
description: Enable most used languages metrics
|
description: Enable most used languages metrics
|
||||||
default: yes
|
default: no
|
||||||
plugin_followup:
|
plugin_followup:
|
||||||
description: Enable owned repositories issues and pull requests metrics
|
description: Enable owned repositories issues and pull requests metrics
|
||||||
default: yes
|
default: no
|
||||||
debug:
|
debug:
|
||||||
description: Enable debug logs
|
description: Enable debug logs
|
||||||
default: no
|
default: no
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
module.exports=(()=>{var __webpack_modules__={56506:(e,t,r)=>{"use strict";r.r(t);var n={};r.r(n);r.d(n,{default:()=>E_Users_lecoq_Documents_GitHub_gitstats_src_setup});var i={};r.r(i);r.d(i,{default:()=>metrics});var o=r(35747);var a=r.n(o);var s=r(85622);var l=r.n(s);async function E_Users_lecoq_Documents_GitHub_gitstats_src_setup(){console.debug(`metrics/setup > setup`);const e="src/templates";const t={templates:{},settings:{},statics:r.ab+"html",node_modules:l().resolve("node_modules")};console.debug(`metrics/setup > load settings.json`);if(a().existsSync(r.ab+"settings.json")){t.settings=JSON.parse(`${await a().promises.readFile(l().resolve("settings.json"))}`);console.debug(`metrics/setup > load settings.json > success`)}else console.debug(`metrics/setup > load settings.json > (missing)`);if(!t.settings.templates)t.settings.templates={default:"classic",enabled:[]};if(t.settings.debug)console.debug(t.settings);if(a().existsSync(r.ab+"templates")){for(const r of await a().promises.readdir(e)){if(/^index.mjs$/.test(r))continue;console.debug(`metrics/setup > load template [${r}]`);const n=[`${e}/${r}/query.graphql`,`${e}/${r}/image.svg`,`${e}/${r}/placeholder.svg`,`${e}/${r}/style.css`];const[i,o,s,c]=await Promise.all(n.map(async e=>`${await a().promises.readFile(l().resolve(e))}`));t.templates[r]={query:i,image:o,placeholder:s,style:c};console.debug(`metrics/setup > load template [${r}] > success`);if(t.settings.debug){Object.defineProperty(t.templates,r,{get(){console.debug(`metrics/setup > reload template [${r}]`);const[e,t,i,o]=n.map(e=>`${a().readFileSync(l().resolve(e))}`);console.debug(`metrics/setup > reload template [${r}] > success`);return{query:e,image:t,placeholder:i,style:o}}})}}}else{console.debug(`metrics/setup > load templates from build`);t.templates=JSON.parse(Buffer.from(`eyJjbGFzc2ljIjp7InF1ZXJ5IjoicXVlcnkgTWV0cmljcyB7XHJcbiAgdXNlcihsb2dpbjogJGxvZ2luKSB7XHJcbiAgICBuYW1lXHJcbiAgICBsb2dpblxyXG4gICAgY3JlYXRlZEF0XHJcbiAgICBhdmF0YXJVcmxcclxuICAgIHdlYnNpdGVVcmxcclxuICAgIGdpc3RzIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgcmVwb3NpdG9yaWVzKGxhc3Q6IDEwMCwgaXNGb3JrOiBmYWxzZSwgb3duZXJBZmZpbGlhdGlvbnM6IE9XTkVSKSB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgICAgbm9kZXMge1xyXG4gICAgICAgIG5hbWVcclxuICAgICAgICB3YXRjaGVycyB7XHJcbiAgICAgICAgICB0b3RhbENvdW50XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHN0YXJnYXplcnMge1xyXG4gICAgICAgICAgdG90YWxDb3VudFxyXG4gICAgICAgIH1cclxuICAgICAgICBsYW5ndWFnZXMoZmlyc3Q6IDQpIHtcclxuICAgICAgICAgIGVkZ2VzIHtcclxuICAgICAgICAgICAgc2l6ZVxyXG4gICAgICAgICAgICBub2RlIHtcclxuICAgICAgICAgICAgICBjb2xvclxyXG4gICAgICAgICAgICAgIG5hbWVcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpc3N1ZXNfb3BlbjogaXNzdWVzKHN0YXRlczogT1BFTikge1xyXG4gICAgICAgICAgdG90YWxDb3VudFxyXG4gICAgICAgIH1cclxuICAgICAgICBpc3N1ZXNfY2xvc2VkOiBpc3N1ZXMoc3RhdGVzOiBDTE9TRUQpIHtcclxuICAgICAgICAgIHRvdGFsQ291bnRcclxuICAgICAgICB9XHJcbiAgICAgICAgcHJfb3BlbjogcHVsbFJlcXVlc3RzKHN0YXRlczogT1BFTikge1xyXG4gICAgICAgICAgdG90YWxDb3VudFxyXG4gICAgICAgIH1cclxuICAgICAgICBwcl9tZXJnZWQ6IHB1bGxSZXF1ZXN0cyhzdGF0ZXM6IE1FUkdFRCkge1xyXG4gICAgICAgICAgdG90YWxDb3VudFxyXG4gICAgICAgIH1cclxuICAgICAgICBmb3JrQ291bnRcclxuICAgICAgICBsaWNlbnNlSW5mbyB7XHJcbiAgICAgICAgICBzcGR4SWRcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHBhY2thZ2VzIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgc3RhcnJlZFJlcG9zaXRvcmllcyB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIHdhdGNoaW5nIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgc3BvbnNvcnNoaXBzQXNTcG9uc29yIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgc3BvbnNvcnNoaXBzQXNNYWludGFpbmVyIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgY29udHJpYnV0aW9uc0NvbGxlY3Rpb24ge1xyXG4gICAgICB0b3RhbFJlcG9zaXRvcmllc1dpdGhDb250cmlidXRlZENvbW1pdHNcclxuICAgICAgdG90YWxDb21taXRDb250cmlidXRpb25zXHJcbiAgICAgIHJlc3RyaWN0ZWRDb250cmlidXRpb25zQ291bnRcclxuICAgICAgdG90YWxJc3N1ZUNvbnRyaWJ1dGlvbnNcclxuICAgICAgdG90YWxQdWxsUmVxdWVzdENvbnRyaWJ1dGlvbnNcclxuICAgICAgdG90YWxQdWxsUmVxdWVzdFJldmlld0NvbnRyaWJ1dGlvbnNcclxuICAgIH1cclxuICAgIGNhbGVuZGFyOmNvbnRyaWJ1dGlvbnNDb2xsZWN0aW9uKGZyb206ICRjYWxlbmRhci5mcm9tLCB0bzogJGNhbGVuZGFyLnRvKSB7XHJcbiAgICAgIGNvbnRyaWJ1dGlvbkNhbGVuZGFyIHtcclxuICAgICAgICB3ZWVrcyB7XHJcbiAgICAgICAgICBjb250cmlidXRpb25EYXlzIHtcclxuICAgICAgICAgICAgY29sb3JcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJlcG9zaXRvcmllc0NvbnRyaWJ1dGVkVG8ge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICB9XHJcbiAgICBmb2xsb3dlcnMge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICB9XHJcbiAgICBmb2xsb3dpbmcge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICB9XHJcbiAgICBpc3N1ZUNvbW1lbnRzIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgb3JnYW5pemF0aW9ucyB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICB9XHJcbn1cclxuIiwiaW1hZ2UiOiI8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB3aWR0aD1cIjwlPSBjb21wdXRlZC5zdmcud2lkdGggJT5cIiBoZWlnaHQ9XCI8JT0gY29tcHV0ZWQuc3ZnLmhlaWdodCAlPlwiPlxyXG4gIDxzdHlsZT5cclxuICAgIDwlPSBzdHlsZSAlPlxyXG4gIDwvc3R5bGU+XHJcblxyXG4gIDxmb3JlaWduT2JqZWN0IHg9XCIwXCIgeT1cIjBcIiB3aWR0aD1cIjEwMCVcIiBoZWlnaHQ9XCIxMDAlXCI+X
Line truncated
|
module.exports=(()=>{var __webpack_modules__={56506:(e,t,r)=>{"use strict";r.r(t);var n={};r.r(n);r.d(n,{default:()=>E_Users_lecoq_Documents_GitHub_gitstats_src_setup});var i={};r.r(i);r.d(i,{default:()=>metrics});var o=r(35747);var a=r.n(o);var s=r(85622);var l=r.n(s);async function E_Users_lecoq_Documents_GitHub_gitstats_src_setup({log:e=true}={}){const t=e?console.debug:()=>null;t(`metrics/setup > setup`);const n="src/templates";const i={templates:{},settings:{},statics:r.ab+"html",node_modules:l().resolve("node_modules")};t(`metrics/setup > load settings.json`);if(a().existsSync(r.ab+"settings.json")){i.settings=JSON.parse(`${await a().promises.readFile(l().resolve("settings.json"))}`);t(`metrics/setup > load settings.json > success`)}else t(`metrics/setup > load settings.json > (missing)`);if(!i.settings.templates)i.settings.templates={default:"classic",enabled:[]};if(!i.settings.plugins)i.settings.plugins={};if(i.settings.debug)t(i.settings);if(a().existsSync(r.ab+"templates")){for(const e of await a().promises.readdir(n)){if(/^index.mjs$/.test(e))continue;t(`metrics/setup > load template [${e}]`);const r=[`${n}/${e}/query.graphql`,`${n}/${e}/image.svg`,`${n}/${e}/placeholder.svg`,`${n}/${e}/style.css`];const[o,s,c,u]=await Promise.all(r.map(async e=>`${await a().promises.readFile(l().resolve(e))}`));i.templates[e]={query:o,image:s,placeholder:c,style:u};t(`metrics/setup > load template [${e}] > success`);if(i.settings.debug){Object.defineProperty(i.templates,e,{get(){t(`metrics/setup > reload template [${e}]`);const[n,i,o,s]=r.map(e=>`${a().readFileSync(l().resolve(e))}`);t(`metrics/setup > reload template [${e}] > success`);return{query:n,image:i,placeholder:o,style:s}}})}}}else{t(`metrics/setup > load templates from build`);i.templates=JSON.parse(Buffer.from(`eyJjbGFzc2ljIjp7InF1ZXJ5IjoicXVlcnkgTWV0cmljcyB7XHJcbiAgdXNlcihsb2dpbjogJGxvZ2luKSB7XHJcbiAgICBuYW1lXHJcbiAgICBsb2dpblxyXG4gICAgY3JlYXRlZEF0XHJcbiAgICBhdmF0YXJVcmxcclxuICAgIHdlYnNpdGVVcmxcclxuICAgIGdpc3RzIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgcmVwb3NpdG9yaWVzKGxhc3Q6ICRyZXBvc2l0b3JpZXMsIGlzRm9yazogZmFsc2UsIG93bmVyQWZmaWxpYXRpb25zOiBPV05FUikge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICAgIG5vZGVzIHtcclxuICAgICAgICBuYW1lXHJcbiAgICAgICAgd2F0Y2hlcnMge1xyXG4gICAgICAgICAgdG90YWxDb3VudFxyXG4gICAgICAgIH1cclxuICAgICAgICBzdGFyZ2F6ZXJzIHtcclxuICAgICAgICAgIHRvdGFsQ291bnRcclxuICAgICAgICB9XHJcbiAgICAgICAgbGFuZ3VhZ2VzKGZpcnN0OiA0KSB7XHJcbiAgICAgICAgICBlZGdlcyB7XHJcbiAgICAgICAgICAgIHNpemVcclxuICAgICAgICAgICAgbm9kZSB7XHJcbiAgICAgICAgICAgICAgY29sb3JcclxuICAgICAgICAgICAgICBuYW1lXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgaXNzdWVzX29wZW46IGlzc3VlcyhzdGF0ZXM6IE9QRU4pIHtcclxuICAgICAgICAgIHRvdGFsQ291bnRcclxuICAgICAgICB9XHJcbiAgICAgICAgaXNzdWVzX2Nsb3NlZDogaXNzdWVzKHN0YXRlczogQ0xPU0VEKSB7XHJcbiAgICAgICAgICB0b3RhbENvdW50XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHByX29wZW46IHB1bGxSZXF1ZXN0cyhzdGF0ZXM6IE9QRU4pIHtcclxuICAgICAgICAgIHRvdGFsQ291bnRcclxuICAgICAgICB9XHJcbiAgICAgICAgcHJfbWVyZ2VkOiBwdWxsUmVxdWVzdHMoc3RhdGVzOiBNRVJHRUQpIHtcclxuICAgICAgICAgIHRvdGFsQ291bnRcclxuICAgICAgICB9XHJcbiAgICAgICAgZm9ya0NvdW50XHJcbiAgICAgICAgbGljZW5zZUluZm8ge1xyXG4gICAgICAgICAgc3BkeElkXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBwYWNrYWdlcyB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIHN0YXJyZWRSZXBvc2l0b3JpZXMge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICB9XHJcbiAgICB3YXRjaGluZyB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIHNwb25zb3JzaGlwc0FzU3BvbnNvciB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIHNwb25zb3JzaGlwc0FzTWFpbnRhaW5lciB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIGNvbnRyaWJ1dGlvbnNDb2xsZWN0aW9uIHtcclxuICAgICAgdG90YWxSZXBvc2l0b3JpZXNXaXRoQ29udHJpYnV0ZWRDb21taXRzXHJcbiAgICAgIHRvdGFsQ29tbWl0Q29udHJpYnV0aW9uc1xyXG4gICAgICByZXN0cmljdGVkQ29udHJpYnV0aW9uc0NvdW50XHJcbiAgICAgIHRvdGFsSXNzdWVDb250cmlidXRpb25zXHJcbiAgICAgIHRvdGFsUHVsbFJlcXVlc3RDb250cmlidXRpb25zXHJcbiAgICAgIHRvdGFsUHVsbFJlcXVlc3RSZXZpZXdDb250cmlidXRpb25zXHJcbiAgICB9XHJcbiAgICBjYWxlbmRhcjpjb250cmlidXRpb25zQ29sbGVjdGlvbihmcm9tOiAkY2FsZW5kYXIuZnJvbSwgdG86ICRjYWxlbmRhci50bykge1xyXG4gICAgICBjb250cmlidXRpb25DYWxlbmRhciB7XHJcbiAgICAgICAgd2Vla3Mge1xyXG4gICAgICAgICAgY29udHJpYnV0aW9uRGF5cyB7XHJcbiAgICAgICAgICAgIGNvbG9yXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXBvc2l0b3JpZXNDb250cmlidXRlZFRvIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgZm9sbG93ZXJzIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgZm9sbG93aW5nIHtcclxuICAgICAgdG90YWxDb3VudFxyXG4gICAgfVxyXG4gICAgaXNzdWVDb21tZW50cyB7XHJcbiAgICAgIHRvdGFsQ291bnRcclxuICAgIH1cclxuICAgIG9yZ2FuaXphdGlvbnMge1xyXG4gICAgICB0b3RhbENvdW50XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcbiIsImltYWdlIjoiPHN2ZyB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIgd2lkdGg9XCI8JT0gY29tcHV0ZWQuc3ZnLndpZHRoICU+XCIgaGVpZ2h0PVwiPCU9IGNvbXB1dGVkLnN2Zy5oZWlnaHQgJT5cIj5cclxuICA8c3R5bGU+XHJcbiAgICA8JT0gc3R5bGUgJT5cclxuICA8L3N0eWxlPlxyXG5cclxuICA8Zm9yZWlnbk9iamVjdCB4PVwiMFwiIHk9XCIwXCIgd2lkdGg9XCIxMDAlXCIgaGVpZ2h0PVwiMTAwJVwiPlxyXG4gICAgPGRp
Line truncated
|
||||||
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Load configuration
|
//Load configuration
|
||||||
const conf = await setup()
|
const conf = await setup({log:false})
|
||||||
console.log(`Configuration | loaded`)
|
console.log(`Configuration | loaded`)
|
||||||
|
|
||||||
//Load svg template, style and query
|
//Load svg template, style and query
|
||||||
|
|||||||
Generated
+60
-18
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "metrics",
|
"name": "metrics",
|
||||||
"version": "1.9.0",
|
"version": "2.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -37,9 +37,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@octokit/core": {
|
"@octokit/core": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.3.tgz",
|
||||||
"integrity": "sha512-AInOFULmwOa7+NFi9F8DlDkm5qtZVmDQayi7TUgChE3yeIGPq0Y+6cAEXPexQ3Ea+uZy66hKEazR7DJyU+4wfw==",
|
"integrity": "sha512-s5UyENGUQBB+ocEOulXq6UH5J16fxuKY2J7ZYrIu9oJYAn0nCwM8hC8o4L23HEzU0SFzNEX86+ffc1T3Vr2ybg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@octokit/auth-token": "^2.4.0",
|
"@octokit/auth-token": "^2.4.0",
|
||||||
"@octokit/graphql": "^4.3.1",
|
"@octokit/graphql": "^4.3.1",
|
||||||
@@ -78,9 +78,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@octokit/plugin-request-log": {
|
"@octokit/plugin-request-log": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.1.tgz",
|
||||||
"integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw=="
|
"integrity": "sha512-d8vmiGAUGswxErdIGfpd0I2UHo2Cs7EaBDpFUZQ9UqYmA0s5/4XoMO4HBld73xGpCj2BvyVyQe2qd9e+/nvKwQ=="
|
||||||
},
|
},
|
||||||
"@octokit/plugin-rest-endpoint-methods": {
|
"@octokit/plugin-rest-endpoint-methods": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
@@ -136,9 +136,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.11.10",
|
"version": "14.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.2.tgz",
|
||||||
"integrity": "sha512-yV1nWZPlMFpoXyoknm4S56y2nlTAuFYaJuQtYRAOU7xA/FJ9RY0Xm7QOkaYMMmr8ESdHIuUb6oQgR/0+2NqlyA=="
|
"integrity": "sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg=="
|
||||||
},
|
},
|
||||||
"@types/q": {
|
"@types/q": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
@@ -1421,21 +1421,63 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string.prototype.trimend": {
|
"string.prototype.trimend": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz",
|
||||||
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
|
"integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"define-properties": "^1.1.3",
|
"define-properties": "^1.1.3",
|
||||||
"es-abstract": "^1.17.5"
|
"es-abstract": "^1.18.0-next.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"es-abstract": {
|
||||||
|
"version": "1.18.0-next.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
|
||||||
|
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
|
||||||
|
"requires": {
|
||||||
|
"es-to-primitive": "^1.2.1",
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-symbols": "^1.0.1",
|
||||||
|
"is-callable": "^1.2.2",
|
||||||
|
"is-negative-zero": "^2.0.0",
|
||||||
|
"is-regex": "^1.1.1",
|
||||||
|
"object-inspect": "^1.8.0",
|
||||||
|
"object-keys": "^1.1.1",
|
||||||
|
"object.assign": "^4.1.1",
|
||||||
|
"string.prototype.trimend": "^1.0.1",
|
||||||
|
"string.prototype.trimstart": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string.prototype.trimstart": {
|
"string.prototype.trimstart": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz",
|
||||||
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
|
"integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"define-properties": "^1.1.3",
|
"define-properties": "^1.1.3",
|
||||||
"es-abstract": "^1.17.5"
|
"es-abstract": "^1.18.0-next.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"es-abstract": {
|
||||||
|
"version": "1.18.0-next.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
|
||||||
|
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
|
||||||
|
"requires": {
|
||||||
|
"es-to-primitive": "^1.2.1",
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-symbols": "^1.0.1",
|
||||||
|
"is-callable": "^1.2.2",
|
||||||
|
"is-negative-zero": "^2.0.0",
|
||||||
|
"is-regex": "^1.1.1",
|
||||||
|
"object-inspect": "^1.8.0",
|
||||||
|
"object-keys": "^1.1.1",
|
||||||
|
"object.assign": "^4.1.1",
|
||||||
|
"string.prototype.trimend": "^1.0.1",
|
||||||
|
"string.prototype.trimstart": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "metrics",
|
"name": "metrics",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"description": "Generate an user's GitHub metrics as SVG image format to embed somewhere else",
|
"description": "Generate an user's GitHub metrics as SVG image format to embed somewhere else",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"port":3000, "//":"Listening port",
|
"port":3000, "//":"Listening port",
|
||||||
"optimize":true, "//":"Optimize SVG image",
|
"optimize":true, "//":"Optimize SVG image",
|
||||||
"debug":false, "//":"Debug mode",
|
"debug":false, "//":"Debug mode",
|
||||||
|
"repositories":100, "//":"Number of repositories to use to compute metrics",
|
||||||
|
|
||||||
"templates":{ "//":"Template configuration",
|
"templates":{ "//":"Template configuration",
|
||||||
"default":"classic", "//":"Default template",
|
"default":"classic", "//":"Default template",
|
||||||
@@ -15,24 +16,24 @@
|
|||||||
|
|
||||||
"plugins":{ "//":"Additional plugins (optional)",
|
"plugins":{ "//":"Additional plugins (optional)",
|
||||||
"pagespeed":{ "//":"Pagespeed plugin",
|
"pagespeed":{ "//":"Pagespeed plugin",
|
||||||
"enabled":false, "//":"Enable or disable PageSpeed metrics",
|
"enabled":true, "//":"Enable or disable PageSpeed metrics",
|
||||||
"token":"******", "//":"Pagespeed token"
|
"token":null, "//":"Pagespeed token"
|
||||||
},
|
},
|
||||||
"traffic":{ "//":"Traffic plugin (GitHub API token must be RW for this to work)",
|
"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"
|
"enabled":false, "//":"Enable or disable repositories total page views is last two weeks"
|
||||||
},
|
},
|
||||||
"lines":{ "//":"Lines plugin",
|
"lines":{ "//":"Lines plugin",
|
||||||
"enabled":true, "//":"Enable or disable repositories total lines added/removed"
|
"enabled":false, "//":"Enable or disable repositories total lines added/removed"
|
||||||
},
|
},
|
||||||
"habits":{ "//":"Habits plugin",
|
"habits":{ "//":"Habits plugin",
|
||||||
"enabled":true, "//":"Enable or disable coding habits metrics",
|
"enabled":false, "//":"Enable or disable coding habits metrics",
|
||||||
"from":50, "//":"Number of activity events to base habits on (up to 100)"
|
"from":100, "//":"Number of activity events to base habits on (up to 100)"
|
||||||
},
|
},
|
||||||
"languages":{ "//":"Languages plugins",
|
"languages":{ "//":"Languages plugins",
|
||||||
"enabled":true, "//":"Enable or disable most used languages metrics (*this plugin is enabled by default)"
|
"enabled":true, "//":"Enable or disable most used languages metrics"
|
||||||
},
|
},
|
||||||
"followup":{ "//":"Follow-up plugin",
|
"followup":{ "//":"Follow-up plugin",
|
||||||
"enabled":true, "//":"Enable owned repositories issues and pull requests metrics (*this plugin is enabled by default)"
|
"enabled":true, "//":"Enable owned repositories issues and pull requests metrics"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-4
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
<h1><a href="https://github.com/lowlighter/metrics">Generate your metrics !</a></h1>
|
<h1><a href="https://github.com/lowlighter/metrics">Generate your metrics !</a></h1>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>1. Enter your GitHub username</h2>
|
<h2>1. Enter your GitHub username</h2>
|
||||||
<label>
|
<label>
|
||||||
@@ -21,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>2. Select a template and enable additional plugins</h2>
|
<h2>2. Select a template {{ plugins.list.length ? "and enable additional plugins" : "" }}</h2>
|
||||||
<div class="templates">
|
<div class="templates">
|
||||||
<label v-for="template in templates.list" :key="template">
|
<label v-for="template in templates.list" :key="template">
|
||||||
<input type="radio" v-model="templates.selected" :value="template" @change="load" :disabled="generated.pending">
|
<input type="radio" v-model="templates.selected" :value="template" @change="load" :disabled="generated.pending">
|
||||||
@@ -34,7 +36,7 @@
|
|||||||
{{ plugins.descriptions[plugin] || plugin }}
|
{{ plugins.descriptions[plugin] || plugin }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="cache-notice">
|
<div class="cache-notice" v-if="plugins.list.length">
|
||||||
*To reduce server overhead, metrics are cached. Changes may not be reflected until cache expiration.
|
*To reduce server overhead, metrics are cached. Changes may not be reflected until cache expiration.
|
||||||
</div>
|
</div>
|
||||||
<div class="palette">
|
<div class="palette">
|
||||||
@@ -74,6 +76,8 @@
|
|||||||
For even more features, setup <a href="https://github.com/lowlighter/metrics">lowlighter/metrics</a> as a <a href="https://github.com/marketplace/actions/github-metrics-as-svg-image">GitHub action</a> !
|
For even more features, setup <a href="https://github.com/lowlighter/metrics">lowlighter/metrics</a> as a <a href="https://github.com/marketplace/actions/github-metrics-as-svg-image">GitHub action</a> !
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="/axios.min.js"></script>
|
<script src="/axios.min.js"></script>
|
||||||
@@ -91,7 +95,7 @@
|
|||||||
palette:"light",
|
palette:"light",
|
||||||
plugins:{
|
plugins:{
|
||||||
list:(await axios.get("/plugins.list")).data,
|
list:(await axios.get("/plugins.list")).data,
|
||||||
enabled:{languages:true, followup:true},
|
enabled:{},
|
||||||
descriptions:{
|
descriptions:{
|
||||||
pagespeed:"Website performances",
|
pagespeed:"Website performances",
|
||||||
languages:"Most used languages",
|
languages:"Most used languages",
|
||||||
@@ -122,7 +126,7 @@
|
|||||||
},
|
},
|
||||||
url() {
|
url() {
|
||||||
const plugins = Object.entries(this.plugins.enabled)
|
const plugins = Object.entries(this.plugins.enabled)
|
||||||
.filter(([key, value]) => /^(?:languages|followup)$/.test(key) ? !value : value)
|
.filter(([key, value]) => value)
|
||||||
.map(([key, value]) => `${key}=${+value}`)
|
.map(([key, value]) => `${key}=${+value}`)
|
||||||
.join("&")
|
.join("&")
|
||||||
return `${window.location.href}${this.user}${plugins.length ? `?${plugins}` : ""}`
|
return `${window.location.href}${this.user}${plugins.length ? `?${plugins}` : ""}`
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
console.debug(`metrics/compute/${login} > start`)
|
console.debug(`metrics/compute/${login} > start`)
|
||||||
console.debug(JSON.stringify(q))
|
console.debug(JSON.stringify(q))
|
||||||
const template = q.template || conf.settings.templates.default
|
const template = q.template || conf.settings.templates.default
|
||||||
|
const repositories = Math.max(0, Number(q.repositories)) || conf.settings.repositories || 100
|
||||||
const pending = []
|
const pending = []
|
||||||
const s = (value, end = "") => value > 1 ? {y:"ies", "":"s"}[end] : end
|
const s = (value, end = "") => value > 1 ? {y:"ies", "":"s"}[end] : end
|
||||||
if ((!(template in Templates))||(!(template in conf.templates))||((conf.settings.templates.enabled.length)&&(!conf.settings.templates.enabled.includes(template))))
|
if ((!(template in Templates))||(!(template in conf.templates))||((conf.settings.templates.enabled.length)&&(!conf.settings.templates.enabled.includes(template))))
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
console.debug(`metrics/compute/${login} > query`)
|
console.debug(`metrics/compute/${login} > query`)
|
||||||
const data = await graphql(query
|
const data = await graphql(query
|
||||||
.replace(/[$]login/, `"${login}"`)
|
.replace(/[$]login/, `"${login}"`)
|
||||||
|
.replace(/[$]repositories/, `${repositories}`)
|
||||||
.replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`)
|
.replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`)
|
||||||
.replace(/[$]calendar.from/, `"${(new Date(Date.now()-14*24*60*60*1000)).toISOString()}"`)
|
.replace(/[$]calendar.from/, `"${(new Date(Date.now()-14*24*60*60*1000)).toISOString()}"`)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//Setup
|
//Setup
|
||||||
export default function ({login, data, computed, pending, q}, {enabled = true} = {}) {
|
export default function ({login, data, computed, pending, q}, {enabled = false} = {}) {
|
||||||
//Check if plugin is enabled and requirements are met
|
//Check if plugin is enabled and requirements are met
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return computed.plugins.followup = null
|
return computed.plugins.followup = null
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//Setup
|
//Setup
|
||||||
export default function ({login, rest, computed, pending, q}, {enabled = false, from = 50} = {}) {
|
export default function ({login, rest, computed, pending, q}, {enabled = false, from = 100} = {}) {
|
||||||
//Check if plugin is enabled and requirements are met
|
//Check if plugin is enabled and requirements are met
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return computed.plugins.habits = null
|
return computed.plugins.habits = null
|
||||||
@@ -8,6 +8,12 @@
|
|||||||
console.debug(`metrics/compute/${login}/plugins > habits`)
|
console.debug(`metrics/compute/${login}/plugins > habits`)
|
||||||
computed.svg.height += 70
|
computed.svg.height += 70
|
||||||
|
|
||||||
|
//Parameter override
|
||||||
|
if (typeof q["habits.from"] === "number") {
|
||||||
|
from = Math.max(0, Math.min(from, q["habits.from"]))
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > habits > events = ${from}`)
|
||||||
|
}
|
||||||
|
|
||||||
//Plugin execution
|
//Plugin execution
|
||||||
pending.push(new Promise(async solve => {
|
pending.push(new Promise(async solve => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//Setup
|
//Setup
|
||||||
export default function ({login, data, computed, pending, q}, {enabled = true} = {}) {
|
export default function ({login, data, computed, pending, q}, {enabled = false} = {}) {
|
||||||
//Check if plugin is enabled and requirements are met
|
//Check if plugin is enabled and requirements are met
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return computed.plugins.languages = null
|
return computed.plugins.languages = null
|
||||||
|
|||||||
+15
-12
@@ -3,10 +3,11 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
/** Setup */
|
/** Setup */
|
||||||
export default async function () {
|
export default async function ({log = true} = {}) {
|
||||||
|
|
||||||
//Init
|
//Init
|
||||||
console.debug(`metrics/setup > setup`)
|
const logger = log ? console.debug : () => null
|
||||||
|
logger(`metrics/setup > setup`)
|
||||||
const templates = "src/templates"
|
const templates = "src/templates"
|
||||||
const conf = {
|
const conf = {
|
||||||
templates:{},
|
templates:{},
|
||||||
@@ -16,17 +17,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Load settings
|
//Load settings
|
||||||
console.debug(`metrics/setup > load settings.json`)
|
logger(`metrics/setup > load settings.json`)
|
||||||
if (fs.existsSync(path.resolve("settings.json"))) {
|
if (fs.existsSync(path.resolve("settings.json"))) {
|
||||||
conf.settings = JSON.parse(`${await fs.promises.readFile(path.resolve("settings.json"))}`)
|
conf.settings = JSON.parse(`${await fs.promises.readFile(path.resolve("settings.json"))}`)
|
||||||
console.debug(`metrics/setup > load settings.json > success`)
|
logger(`metrics/setup > load settings.json > success`)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
console.debug(`metrics/setup > load settings.json > (missing)`)
|
logger(`metrics/setup > load settings.json > (missing)`)
|
||||||
if (!conf.settings.templates)
|
if (!conf.settings.templates)
|
||||||
conf.settings.templates = {default:"classic", enabled:[]}
|
conf.settings.templates = {default:"classic", enabled:[]}
|
||||||
|
if (!conf.settings.plugins)
|
||||||
|
conf.settings.plugins = {}
|
||||||
if (conf.settings.debug)
|
if (conf.settings.debug)
|
||||||
console.debug(conf.settings)
|
logger(conf.settings)
|
||||||
|
|
||||||
//Load templates
|
//Load templates
|
||||||
if (fs.existsSync(path.resolve(templates))) {
|
if (fs.existsSync(path.resolve(templates))) {
|
||||||
@@ -34,7 +37,7 @@
|
|||||||
//Cache templates
|
//Cache templates
|
||||||
if (/^index.mjs$/.test(name))
|
if (/^index.mjs$/.test(name))
|
||||||
continue
|
continue
|
||||||
console.debug(`metrics/setup > load template [${name}]`)
|
logger(`metrics/setup > load template [${name}]`)
|
||||||
const files = [
|
const files = [
|
||||||
`${templates}/${name}/query.graphql`,
|
`${templates}/${name}/query.graphql`,
|
||||||
`${templates}/${name}/image.svg`,
|
`${templates}/${name}/image.svg`,
|
||||||
@@ -43,14 +46,14 @@
|
|||||||
]
|
]
|
||||||
const [query, image, placeholder, style] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
const [query, image, placeholder, style] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
||||||
conf.templates[name] = {query, image, placeholder, style}
|
conf.templates[name] = {query, image, placeholder, style}
|
||||||
console.debug(`metrics/setup > load template [${name}] > success`)
|
logger(`metrics/setup > load template [${name}] > success`)
|
||||||
//Debug
|
//Debug
|
||||||
if (conf.settings.debug) {
|
if (conf.settings.debug) {
|
||||||
Object.defineProperty(conf.templates, name, {
|
Object.defineProperty(conf.templates, name, {
|
||||||
get() {
|
get() {
|
||||||
console.debug(`metrics/setup > reload template [${name}]`)
|
logger(`metrics/setup > reload template [${name}]`)
|
||||||
const [query, image, placeholder, style] = files.map(file => `${fs.readFileSync(path.resolve(file))}`)
|
const [query, image, placeholder, style] = files.map(file => `${fs.readFileSync(path.resolve(file))}`)
|
||||||
console.debug(`metrics/setup > reload template [${name}] > success`)
|
logger(`metrics/setup > reload template [${name}] > success`)
|
||||||
return {query, image, placeholder, style}
|
return {query, image, placeholder, style}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -58,12 +61,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.debug(`metrics/setup > load templates from build`)
|
logger(`metrics/setup > load templates from build`)
|
||||||
conf.templates = JSON.parse(Buffer.from(`<#assets>`, "base64").toString("utf8"))
|
conf.templates = JSON.parse(Buffer.from(`<#assets>`, "base64").toString("utf8"))
|
||||||
}
|
}
|
||||||
|
|
||||||
//Conf
|
//Conf
|
||||||
console.debug(`metrics/setup > setup > success`)
|
logger(`metrics/setup > setup > success`)
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ query Metrics {
|
|||||||
gists {
|
gists {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
repositories(last: 100, isFork: false, ownerAffiliations: OWNER) {
|
repositories(last: $repositories, isFork: false, ownerAffiliations: OWNER) {
|
||||||
totalCount
|
totalCount
|
||||||
nodes {
|
nodes {
|
||||||
name
|
name
|
||||||
|
|||||||
+46
-3
@@ -20,23 +20,66 @@
|
|||||||
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||||
const rest = new OctokitRest.Octokit({auth:token})
|
const rest = new OctokitRest.Octokit({auth:token})
|
||||||
|
|
||||||
|
//Perform tests
|
||||||
|
await test.build()
|
||||||
|
for (const template of [
|
||||||
|
"classic"
|
||||||
|
]) {
|
||||||
|
for (const q of [
|
||||||
|
{},
|
||||||
|
{followup:1},
|
||||||
|
{languages:1},
|
||||||
|
{followup:1, languages:1},
|
||||||
|
{habits:1, "habits.events":1},
|
||||||
|
{lines:1},
|
||||||
|
{traffic:1},
|
||||||
|
{selfskip:1},
|
||||||
|
{pagespeed:1},
|
||||||
|
{followup:1, languages:1, habits:1, "habits.events":1, lines:1, traffic:1, selfskip:1, pagespeed:1}
|
||||||
|
]) {
|
||||||
|
await test.metrics({graphql, rest, q:{template, repositories:1, ...q}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Metrics tests */
|
||||||
|
test.metrics = async function ({graphql, rest, q}) {
|
||||||
|
//Preparation
|
||||||
|
console.log(`### Checking metrics with plugins [${Object.keys(q).filter(key => /^\w+$/.test(key)).join(", ")}]`)
|
||||||
|
const plugins = {
|
||||||
|
lines:{enabled:true},
|
||||||
|
traffic:{enabled:true},
|
||||||
|
pagespeed:{enabled:true},
|
||||||
|
habits:{enabled:true},
|
||||||
|
selfskip:{enabled:true},
|
||||||
|
languages:{enabled:true},
|
||||||
|
followup:{enabled:true},
|
||||||
|
}
|
||||||
|
|
||||||
//Compute render
|
//Compute render
|
||||||
const conf = await setup()
|
console.log("#### Checking that SVG can be generated")
|
||||||
const rendered = await metrics({login:"lowlighter", q:{}}, {graphql, rest, plugins:{}, conf})
|
const conf = await setup({log:false})
|
||||||
|
const rendered = await metrics({login:"lowlighter", q}, {graphql, rest, plugins, conf})
|
||||||
|
|
||||||
//Ensure it's a well-formed SVG image
|
//Ensure it's a well-formed SVG image
|
||||||
|
console.log("#### Checking that generated SVG can be parsed")
|
||||||
const parsed = libxmljs.parseXml(rendered)
|
const parsed = libxmljs.parseXml(rendered)
|
||||||
if (parsed.errors.length)
|
if (parsed.errors.length)
|
||||||
throw new Error(`Malformed SVG : \n${parsed.errors.join("\n")}`)
|
throw new Error(`Malformed SVG : \n${parsed.errors.join("\n")}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build test */
|
||||||
|
test.build = async function () {
|
||||||
//Ensure that action has been rebuild
|
//Ensure that action has been rebuild
|
||||||
|
console.log("### Checking that code has been rebuild")
|
||||||
const action = `${await fs.promises.readFile(`${__dirname}/dist/index.js`)}`
|
const action = `${await fs.promises.readFile(`${__dirname}/dist/index.js`)}`
|
||||||
const code = await build()
|
const code = await build()
|
||||||
if (action !== code)
|
if (action !== code)
|
||||||
throw new Error(`GitHub Action has not been rebuild. Run "npm run build" to solve this issue`)
|
throw new Error(`GitHub Action has not been rebuild. Run "npm run build" to solve this issue`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Main
|
|
||||||
|
//Main
|
||||||
if (/metrics.mjs/.test(process.argv[1])) {
|
if (/metrics.mjs/.test(process.argv[1])) {
|
||||||
//Test
|
//Test
|
||||||
await test()
|
await test()
|
||||||
|
|||||||
Reference in new issue
Block a user