Mocked tests and remove ncc compilation (#32)
* Mocked tests and remove ncc compilation * Update workflow.yml * Update Dockerfile
This commit is contained in:
20
.github/workflows/image.yml
vendored
Normal file
20
.github/workflows/image.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Publish Docker image
|
||||
on:
|
||||
release:
|
||||
types: [ published ]
|
||||
jobs:
|
||||
publish:
|
||||
name: Push Docker image to GitHub Packages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Publish image to GitHub Packages
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
registry: docker.pkg.github.com
|
||||
repository: lowlighter/metrics/metrics
|
||||
tag_with_ref: true
|
||||
|
||||
3
.github/workflows/stale.yml
vendored
3
.github/workflows/stale.yml
vendored
@@ -8,7 +8,8 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
- name: Flag stale issues and pull requests
|
||||
uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: This issue has been open 30 days without activity. It will be closed in 5 days if it remains inactive.
|
||||
|
||||
246
.github/workflows/workflow.yml
vendored
246
.github/workflows/workflow.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build
|
||||
name: Build, tests and analyze
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,237 +10,23 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
- name: Install
|
||||
run: npm ci
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Test
|
||||
run: npm test
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Build lowlighter/metrics:${{ github.head_ref || github.base_ref }}
|
||||
run: docker build -t lowlighter/metrics:${{ github.head_ref || github.base_ref }} .
|
||||
- name: Run tests
|
||||
run: docker run --workdir=/metrics --entrypoint="" lowlighter/metrics:${{ github.head_ref || github.base_ref }} npm test
|
||||
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
needs: ["test-master"]
|
||||
needs: [ build ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: javascript
|
||||
config-file: ./.github/config/codeql.yml
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
# Tests cases below are auto generated through `npm run build`
|
||||
# Edit utils/workflow.yml instead if you need to update workflow
|
||||
|
||||
test-master:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
template: ["classic","terminal"]
|
||||
steps:
|
||||
|
||||
- name: ${{ matrix.template }} > Base
|
||||
uses: lowlighter/metrics@master
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: header, activity, community, repositories, metadata
|
||||
plugins_errors_fatal: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > PageSpeed
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_pagespeed: yes
|
||||
plugin_pagespeed_token: ${{ secrets.PAGESPEED_TOKEN }}
|
||||
plugin_pagespeed_detailed: yes
|
||||
plugin_pagespeed_screenshot: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (playlist - apple)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_music: yes
|
||||
plugin_music_playlist: ${{ secrets.MUSIC_PLAYLIST_APPLE }}
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (playlist - spotify)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_music: yes
|
||||
plugin_music_playlist: ${{ secrets.MUSIC_PLAYLIST_SPOTIFY }}
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (recent - spotify)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_music: yes
|
||||
plugin_music_provider: spotify
|
||||
plugin_music_token: ${{ secrets.SPOTIFY_TOKENS }}
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Posts (dev.to)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_posts: yes
|
||||
plugin_posts_source: dev.to
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Isocalendar
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_isocalendar: yes
|
||||
plugin_isocalendar_duration: full-year
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Habits
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_habits: yes
|
||||
plugin_habits_from: 5
|
||||
plugin_habits_charts: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Languages
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_languages: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Follow-up
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_followup: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Lines and Traffic
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_lines: yes
|
||||
plugin_traffic: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Gists
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_gists: yes
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Topics (starred)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_topics: yes
|
||||
plugin_topics_mode: starred
|
||||
plugin_topics_sort: random
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Topics (mastered)
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_topics: yes
|
||||
plugin_topics_mode: mastered
|
||||
plugin_topics_sort: stars
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Projects
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_projects: yes
|
||||
plugin_projects_repositories: lowlighter/metrics/projects/1
|
||||
plugin_projects_limit: 2
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Tweets
|
||||
uses: lowlighter/metrics@master
|
||||
with:
|
||||
token: ${{ secrets.METRICS_TOKEN }}
|
||||
dryrun: yes
|
||||
repositories: 0
|
||||
template: ${{ matrix.template }}
|
||||
base: ""
|
||||
plugins_errors_fatal: yes
|
||||
plugin_tweets: yes
|
||||
plugin_tweets_limit: 2
|
||||
plugin_tweets_token: ${{ secrets.TWITTER_TOKEN }}
|
||||
|
||||
|
||||
languages: javascript
|
||||
config-file: ./.github/config/codeql.yml
|
||||
- name: Analyze code
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
@@ -117,7 +117,7 @@ It should be avoided when possible as it increases drastically the size of gener
|
||||
* 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)
|
||||
* [actions/toolkit](https://github.com/actions/toolkit/tree/master)
|
||||
* To build the GitHub Action
|
||||
* [vuejs/vue](https://github.com/vuejs/vue) and [egoist/vue-prism-component](https://github.com/egoist/vue-prism-component) + [PrismJS/prism](https://github.com/PrismJS/prism)
|
||||
* To display server application
|
||||
@@ -127,5 +127,5 @@ It should be avoided when possible as it increases drastically the size of gener
|
||||
* To test and verify SVG validity
|
||||
* [Marak/colors.js](https://github.com/Marak/colors.js)
|
||||
* To print colors in console
|
||||
* [babel/minify](https://github.com/babel/minify)
|
||||
* To minify code
|
||||
* [facebook/jest](https://github.com/facebook/jest) and [nodeca/js-yaml](https://github.com/nodeca/js-yaml)
|
||||
* For unit testing
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,11 +1,11 @@
|
||||
# Base image
|
||||
FROM node:15-buster-slim
|
||||
|
||||
# Copy GitHub action
|
||||
COPY action/dist/index.js /index.js
|
||||
# Copy repository
|
||||
COPY . /metrics
|
||||
|
||||
# Setup
|
||||
RUN chmod +x /index.js \
|
||||
RUN chmod +x /metrics/action/index.mjs \
|
||||
# Install latest chrome dev package, fonts to support major charsets and skip chromium download on puppeteer install
|
||||
# Based on https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker
|
||||
&& apt-get update \
|
||||
@@ -21,9 +21,15 @@ RUN chmod +x /index.js \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y ruby-full \
|
||||
&& apt-get install -y git g++ cmake pkg-config libicu-dev zlib1g-dev libcurl4-openssl-dev libssl-dev ruby-dev \
|
||||
&& gem install github-linguist
|
||||
&& gem install github-linguist \
|
||||
# Install python for node-gyp
|
||||
&& apt-get update \
|
||||
&& apt-get install -y python3 \
|
||||
# Install node modules
|
||||
&& cd /metrics \
|
||||
&& npm ci
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
|
||||
ENV PUPPETEER_BROWSER_PATH "google-chrome-stable"
|
||||
|
||||
# Execute GitHub action
|
||||
ENTRYPOINT node /index.js
|
||||
ENTRYPOINT node /metrics/action/index.mjs
|
||||
18
action.yml
18
action.yml
@@ -324,18 +324,18 @@ inputs:
|
||||
description: Die on plugins errors
|
||||
default: no
|
||||
|
||||
# Verify SVG after generation
|
||||
# Test whether SVG can be correctly parsed (used for testing)
|
||||
verify:
|
||||
description: Verify SVG after genaration
|
||||
default: no
|
||||
|
||||
# Enable debug mode
|
||||
# Ensure you correctly put all sensitive informations in your repository secrets before !
|
||||
debug:
|
||||
description: Enable debug logs
|
||||
default: no
|
||||
|
||||
# Verify SVG after generation
|
||||
# Test whether SVG can be correctly parsed (used for testing)
|
||||
verify:
|
||||
description: Verify SVG after generation
|
||||
default: no
|
||||
|
||||
# Debug flags (used for testing)
|
||||
debug_flags:
|
||||
description: Debug flags
|
||||
@@ -346,3 +346,9 @@ inputs:
|
||||
dryrun:
|
||||
description: Enable dry-run
|
||||
default: no
|
||||
|
||||
# Use mocked data
|
||||
# Bypass external APIs which requires a token and sent mocked data (used for testing)
|
||||
use_mocked_data:
|
||||
description: Use mocked data instead of real APIs
|
||||
default: no
|
||||
123
action/dist/index.js
vendored
123
action/dist/index.js
vendored
File diff suppressed because one or more lines are too long
149
action/index.mjs
149
action/index.mjs
@@ -1,13 +1,12 @@
|
||||
//Imports
|
||||
import * as _setup from "./../src/setup.mjs"
|
||||
import * as _metrics from "./../src/metrics.mjs"
|
||||
import * as _octokit from "@octokit/graphql"
|
||||
import * as _core from "@actions/core"
|
||||
import * as _github from "@actions/github"
|
||||
import setup from "./../src/setup.mjs"
|
||||
import metrics from "./../src/metrics.mjs"
|
||||
import octokit from "@octokit/graphql"
|
||||
import core from "@actions/core"
|
||||
import github from "@actions/github"
|
||||
import mocks from "./../src/mocks.mjs"
|
||||
|
||||
;((async function () {
|
||||
//Hack because ES modules are not correctly transpiled with ncc
|
||||
const [core, github, octokit, setup, metrics] = [_core, _github, _octokit, _setup, _metrics].map(m => (m && m.default) ? m.default : m)
|
||||
//Yaml boolean converter
|
||||
const bool = (value, defaulted = false) => typeof value === "string" ? /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(value) : defaulted
|
||||
//Debug message buffer
|
||||
@@ -16,12 +15,11 @@
|
||||
try {
|
||||
//Initialization
|
||||
console.log(`GitHub metrics`)
|
||||
console.log(`========================================================`)
|
||||
console.log(`Version | <#version>`)
|
||||
console.log("─".repeat(64))
|
||||
process.on("unhandledRejection", error => { throw error })
|
||||
|
||||
//Skip process if needed
|
||||
if ((github.context.eventName === "push")&&(github.context.payload)&&(github.context.payload.head_commit)) {
|
||||
if ((github.context.eventName === "push")&&(github.context.payload?.head_commit)) {
|
||||
if (/\[Skip GitHub Action\]/.test(github.context.payload.head_commit.message)) {
|
||||
console.log(`Skipped because [Skip GitHub Action] is in commit message`)
|
||||
process.exit(0)
|
||||
@@ -30,30 +28,47 @@
|
||||
|
||||
//Load configuration
|
||||
const conf = await setup({log:false})
|
||||
console.log(`Configuration | loaded`)
|
||||
console.log(`Configuration │ loaded`)
|
||||
console.log(`Version │ ${conf.package.version}`)
|
||||
|
||||
//Debug mode
|
||||
const debug = bool(core.getInput("debug"))
|
||||
if (!debug)
|
||||
console.debug = message => debugged.push(message)
|
||||
console.log(`Debug mode │ ${debug}`)
|
||||
const dflags = (core.getInput("debug_flags") || "").split(" ").filter(flag => flag)
|
||||
console.log(`Debug flags │ ${dflags.join(" ") || "(none)"}`)
|
||||
|
||||
//Load svg template, style, fonts and query
|
||||
const template = core.getInput("template") || "classic"
|
||||
console.log(`Template to use | ${template}`)
|
||||
console.log(`Template to use │ ${template}`)
|
||||
|
||||
//Token for data gathering
|
||||
const token = core.getInput("token") || ""
|
||||
console.log(`Github token | ${token ? "provided" : "missing"}`)
|
||||
console.log(`Github token │ ${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`)
|
||||
if (!token)
|
||||
throw new Error("You must provide a valid GitHub token to gather your metrics")
|
||||
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||
console.log(`Github GraphQL API | ok`)
|
||||
const rest = github.getOctokit(token)
|
||||
console.log(`Github REST API | ok`)
|
||||
const api = {}
|
||||
api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||
console.log(`Github GraphQL API │ ok`)
|
||||
api.rest = github.getOctokit(token)
|
||||
console.log(`Github REST API │ ok`)
|
||||
//Apply mocking if needed
|
||||
if (bool(core.getInput("use_mocked_data"))) {
|
||||
Object.assign(api, await mocks(api))
|
||||
console.log(`Mocked Github API │ ok`)
|
||||
}
|
||||
//Extract octokits
|
||||
const {graphql, rest} = api
|
||||
|
||||
//SVG output
|
||||
const filename = core.getInput("filename") || "github-metrics.svg"
|
||||
console.log(`SVG output file | ${filename}`)
|
||||
console.log(`SVG output file │ ${filename}`)
|
||||
|
||||
//SVG optimization
|
||||
const optimize = bool(core.getInput("optimize"), true)
|
||||
conf.optimize = optimize
|
||||
console.log(`SVG optimization | ${optimize}`)
|
||||
console.log(`SVG optimization │ ${optimize}`)
|
||||
|
||||
//GitHub user
|
||||
let authenticated
|
||||
@@ -64,28 +79,20 @@
|
||||
authenticated = github.context.repo.owner
|
||||
}
|
||||
const user = core.getInput("user") || authenticated
|
||||
console.log(`GitHub user | ${user}`)
|
||||
|
||||
//Debug mode
|
||||
const debug = bool(core.getInput("debug"))
|
||||
if (!debug)
|
||||
console.debug = message => debugged.push(message)
|
||||
console.log(`Debug mode | ${debug}`)
|
||||
const dflags = (core.getInput("debug_flags") || "").split(" ").filter(flag => flag)
|
||||
console.log(`Debug flags | ${dflags.join(" ") || "(none)"}`)
|
||||
console.log(`GitHub user │ ${user}`)
|
||||
|
||||
//Base elements
|
||||
const base = {}
|
||||
let parts = (core.getInput("base") || "").split(",").map(part => part.trim())
|
||||
for (const part of conf.settings.plugins.base.parts)
|
||||
base[`base.${part}`] = parts.includes(part)
|
||||
console.log(`Base parts | ${parts.join(", ") || "(none)"}`)
|
||||
console.log(`Base parts │ ${parts.join(", ") || "(none)"}`)
|
||||
|
||||
//Config
|
||||
const config = {
|
||||
"config.timezone":core.getInput("config_timezone") || ""
|
||||
}
|
||||
console.log(`Timezone | ${config["config.timezone"] || "(system default)"}`)
|
||||
console.log(`Timezone │ ${config["config.timezone"] || "(system default)"}`)
|
||||
|
||||
//Additional plugins
|
||||
const plugins = {
|
||||
@@ -104,23 +111,23 @@
|
||||
tweets:{enabled:bool(core.getInput("plugin_tweets"))},
|
||||
}
|
||||
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(", ")}`)
|
||||
console.log(`Plugins enabled │ ${Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).join(", ")}`)
|
||||
//Additional plugins options
|
||||
//Pagespeed
|
||||
if (plugins.pagespeed.enabled) {
|
||||
plugins.pagespeed.token = core.getInput("plugin_pagespeed_token") || ""
|
||||
q[`pagespeed.detailed`] = bool(core.getInput(`plugin_pagespeed_detailed`))
|
||||
q[`pagespeed.screenshot`] = bool(core.getInput(`plugin_pagespeed_screenshot`))
|
||||
console.log(`Pagespeed token | ${plugins.pagespeed.token ? "provided" : "missing"}`)
|
||||
console.log(`Pagespeed detailed | ${q["pagespeed.detailed"]}`)
|
||||
console.log(`Pagespeed screenshot | ${q["pagespeed.screenshot"]}`)
|
||||
console.log(`Pagespeed token │ ${/^MOCKED/.test(plugins.pagespeed.token) ? "(MOCKED)" : plugins.pagespeed.token ? "provided" : "missing"}`)
|
||||
console.log(`Pagespeed detailed │ ${q["pagespeed.detailed"]}`)
|
||||
console.log(`Pagespeed screenshot │ ${q["pagespeed.screenshot"]}`)
|
||||
}
|
||||
//Languages
|
||||
if (plugins.languages.enabled) {
|
||||
for (const option of ["ignored", "skipped"])
|
||||
q[`languages.${option}`] = core.getInput(`plugin_languages_${option}`) || null
|
||||
console.log(`Languages ignored | ${q["languages.ignored"] || "(none)"}`)
|
||||
console.log(`Languages skipped repos | ${q["languages.skipped"] || "(none)"}`)
|
||||
console.log(`Languages ignored │ ${q["languages.ignored"] || "(none)"}`)
|
||||
console.log(`Languages skipped repos │ ${q["languages.skipped"] || "(none)"}`)
|
||||
}
|
||||
//Habits
|
||||
if (plugins.habits.enabled) {
|
||||
@@ -128,107 +135,107 @@
|
||||
q[`habits.${option}`] = core.getInput(`plugin_habits_${option}`) || null
|
||||
q[`habits.facts`] = bool(core.getInput(`plugin_habits_facts`))
|
||||
q[`habits.charts`] = bool(core.getInput(`plugin_habits_charts`))
|
||||
console.log(`Habits facts | ${q["habits.facts"]}`)
|
||||
console.log(`Habits charts | ${q["habits.charts"]}`)
|
||||
console.log(`Habits events to use | ${q["habits.from"] || "(default)"}`)
|
||||
console.log(`Habits days to keep | ${q["habits.days"] || "(default)"}`)
|
||||
console.log(`Habits facts │ ${q["habits.facts"]}`)
|
||||
console.log(`Habits charts │ ${q["habits.charts"]}`)
|
||||
console.log(`Habits events to use │ ${q["habits.from"] || "(default)"}`)
|
||||
console.log(`Habits days to keep │ ${q["habits.days"] || "(default)"}`)
|
||||
}
|
||||
//Music
|
||||
if (plugins.music.enabled) {
|
||||
plugins.music.token = core.getInput("plugin_music_token") || ""
|
||||
for (const option of ["provider", "mode", "playlist", "limit"])
|
||||
q[`music.${option}`] = core.getInput(`plugin_music_${option}`) || null
|
||||
console.log(`Music provider | ${q["music.provider"] || "(none)"}`)
|
||||
console.log(`Music plugin mode | ${q["music.mode"] || "(none)"}`)
|
||||
console.log(`Music playlist | ${q["music.playlist"] || "(none)"}`)
|
||||
console.log(`Music tracks limit | ${q["music.limit"] || "(default)"}`)
|
||||
console.log(`Music token | ${plugins.music.token ? "provided" : "missing"}`)
|
||||
console.log(`Music provider │ ${q["music.provider"] || "(none)"}`)
|
||||
console.log(`Music plugin mode │ ${q["music.mode"] || "(none)"}`)
|
||||
console.log(`Music playlist │ ${q["music.playlist"] || "(none)"}`)
|
||||
console.log(`Music tracks limit │ ${q["music.limit"] || "(default)"}`)
|
||||
console.log(`Music token │ ${/^MOCKED/.test(plugins.music.token) ? "(MOCKED)" : plugins.music.token ? "provided" : "missing"}`)
|
||||
}
|
||||
//Posts
|
||||
if (plugins.posts.enabled) {
|
||||
for (const option of ["source", "limit"])
|
||||
q[`posts.${option}`] = core.getInput(`plugin_posts_${option}`) || null
|
||||
console.log(`Posts source | ${q["posts.source"] || "(none)"}`)
|
||||
console.log(`Posts limit | ${q["posts.limit"] || "(default)"}`)
|
||||
console.log(`Posts source │ ${q["posts.source"] || "(none)"}`)
|
||||
console.log(`Posts limit │ ${q["posts.limit"] || "(default)"}`)
|
||||
}
|
||||
//Isocalendar
|
||||
if (plugins.isocalendar.enabled) {
|
||||
q["isocalendar.duration"] = core.getInput("plugin_isocalendar_duration") || "half-year"
|
||||
console.log(`Isocalendar duration | ${q["isocalendar.duration"]}`)
|
||||
console.log(`Isocalendar duration │ ${q["isocalendar.duration"]}`)
|
||||
}
|
||||
//Topics
|
||||
if (plugins.topics.enabled) {
|
||||
for (const option of ["mode", "sort", "limit"])
|
||||
q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null
|
||||
console.log(`Topics mode | ${q["topics.mode"] || "(default)"}`)
|
||||
console.log(`Topics sort mode | ${q["topics.sort"] || "(default)"}`)
|
||||
console.log(`Topics limit | ${q["topics.limit"] || "(default)"}`)
|
||||
console.log(`Topics mode │ ${q["topics.mode"] || "(default)"}`)
|
||||
console.log(`Topics sort mode │ ${q["topics.sort"] || "(default)"}`)
|
||||
console.log(`Topics limit │ ${q["topics.limit"] || "(default)"}`)
|
||||
}
|
||||
//Projects
|
||||
if (plugins.projects.enabled) {
|
||||
for (const option of ["limit", "repositories"])
|
||||
q[`projects.${option}`] = core.getInput(`plugin_projects_${option}`) || null
|
||||
console.log(`Projects limit | ${q["projects.limit"] || "(default)"}`)
|
||||
console.log(`Projects repositories | ${q["projects.repositories"] || "(none)"}`)
|
||||
console.log(`Projects limit │ ${q["projects.limit"] || "(default)"}`)
|
||||
console.log(`Projects repositories │ ${q["projects.repositories"] || "(none)"}`)
|
||||
}
|
||||
//Tweets
|
||||
if (plugins.tweets.enabled) {
|
||||
plugins.tweets.token = core.getInput("plugin_tweets_token") || null
|
||||
for (const option of ["limit"])
|
||||
q[`tweets.${option}`] = core.getInput(`plugin_tweets_${option}`) || null
|
||||
console.log(`Twitter token | ${plugins.tweets.token ? "provided" : "missing"}`)
|
||||
console.log(`Tweets limit | ${q["tweets.limit"] || "(default)"}`)
|
||||
console.log(`Twitter token │ ${/^MOCKED/.test(plugins.tweets.token) ? "(MOCKED)" : plugins.tweets.token ? "provided" : "missing"}`)
|
||||
console.log(`Tweets limit │ ${q["tweets.limit"] || "(default)"}`)
|
||||
}
|
||||
|
||||
//Repositories to use
|
||||
const repositories = Number(core.getInput("repositories")) || 100
|
||||
console.log(`Repositories to use | ${repositories}`)
|
||||
console.log(`Repositories to use │ ${repositories}`)
|
||||
|
||||
//Die on plugins errors
|
||||
const die = bool(core.getInput("plugins_errors_fatal"))
|
||||
console.log(`Plugin errors | ${die ? "die" : "warn"}`)
|
||||
console.log(`Plugin errors │ ${die ? "die" : "warn"}`)
|
||||
|
||||
//Build query
|
||||
const query = JSON.parse(core.getInput("query") || "{}")
|
||||
console.log(`Query additional params | ${JSON.stringify(query)}`)
|
||||
console.log(`Query additional params │ ${JSON.stringify(query)}`)
|
||||
q = {...query, ...q, base:false, ...base, ...config, repositories, template}
|
||||
|
||||
//Render metrics
|
||||
const rendered = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die})
|
||||
console.log(`Render | complete`)
|
||||
console.log(`Render │ complete`)
|
||||
|
||||
//Verify svg
|
||||
const verify = bool(core.getInput("verify"))
|
||||
console.log(`Verify SVG | ${verify}`)
|
||||
console.log(`Verify SVG │ ${verify}`)
|
||||
if (verify) {
|
||||
const [libxmljs] = [await import("libxmljs")].map(m => (m && m.default) ? m.default : m)
|
||||
const parsed = libxmljs.parseXml(rendered)
|
||||
if (parsed.errors.length)
|
||||
throw new Error(`Malformed SVG : \n${parsed.errors.join("\n")}`)
|
||||
console.log(`SVG valid | yes`)
|
||||
console.log(`SVG valid │ yes`)
|
||||
}
|
||||
|
||||
//Commit to repository
|
||||
const dryrun = bool(core.getInput("dryrun"))
|
||||
if (dryrun)
|
||||
console.log(`Dry-run | complete`)
|
||||
console.log(`Dry-run │ complete`)
|
||||
else {
|
||||
//Repository and branch
|
||||
const branch = github.context.ref.replace(/^refs[/]heads[/]/, "")
|
||||
console.log(`Repository | ${github.context.repo.owner}/${github.context.repo.repo}`)
|
||||
console.log(`Branch | ${branch}`)
|
||||
console.log(`Repository │ ${github.context.repo.owner}/${github.context.repo.repo}`)
|
||||
console.log(`Branch │ ${branch}`)
|
||||
//Committer token
|
||||
const token = core.getInput("committer_token") || core.getInput("token") || ""
|
||||
console.log(`Committer token | ${token ? "provided" : "missing"}`)
|
||||
console.log(`Committer token │ ${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`)
|
||||
if (!token)
|
||||
throw new Error("You must provide a valid GitHub token to commit your metrics")
|
||||
const rest = github.getOctokit(token)
|
||||
console.log(`Committer REST API | ok`)
|
||||
console.log(`Committer REST API │ ok`)
|
||||
try {
|
||||
console.log(`Committer | ${(await rest.users.getAuthenticated()).data.login}`)
|
||||
console.log(`Committer │ ${(await rest.users.getAuthenticated()).data.login}`)
|
||||
}
|
||||
catch {
|
||||
console.log(`Committer | (github-actions)`)
|
||||
console.log(`Committer │ (github-actions)`)
|
||||
}
|
||||
//Retrieve previous render SHA to be able to update file content through API
|
||||
let sha = null
|
||||
@@ -243,14 +250,14 @@
|
||||
)
|
||||
sha = oid
|
||||
} catch (error) { console.debug(error) }
|
||||
console.log(`Previous render sha | ${sha ?? "(none)"}`)
|
||||
console.log(`Previous render sha │ ${sha ?? "(none)"}`)
|
||||
//Update file content through API
|
||||
await rest.repos.createOrUpdateFileContents({
|
||||
...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`,
|
||||
content:Buffer.from(rendered).toString("base64"),
|
||||
...(sha ? {sha} : {})
|
||||
})
|
||||
console.log(`Commit to repo | ok`)
|
||||
console.log(`Commit to repo │ ok`)
|
||||
}
|
||||
|
||||
//Success
|
||||
@@ -262,7 +269,7 @@
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
if (!bool(core.getInput("debug")))
|
||||
for (const log of ["_".repeat(64), "An error occured, logging debug message :", ...debugged])
|
||||
for (const log of ["─".repeat(64), "An error occured, logging debug message :", ...debugged])
|
||||
console.log(log)
|
||||
core.setFailed(error.message)
|
||||
process.exit(1)
|
||||
|
||||
5801
package-lock.json
generated
5801
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"start": "node index.mjs",
|
||||
"build": "node utils/build.mjs",
|
||||
"test": "node tests/metrics.mjs",
|
||||
"upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest colors@latest compression@latest ejs@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest prismjs@latest puppeteer@latest svgo@latest vue@latest vue-prism-component@latest @vercel/ncc@latest babel-minify@latest libxmljs@latest"
|
||||
"test": "npx jest",
|
||||
"upgrade": "npm install @actions/core@latest @actions/github@latest @octokit/graphql@latest @octokit/rest@latest axios@latest colors@latest compression@latest ejs@latest express@latest express-rate-limit@latest image-to-base64@latest memory-cache@latest prismjs@latest puppeteer@latest svgo@latest vue@latest vue-prism-component@latest jest@latest js-yaml@latest libxmljs@latest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -39,8 +39,8 @@
|
||||
"vue-prism-component": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "^0.26.1",
|
||||
"babel-minify": "^0.5.1",
|
||||
"jest": "^26.6.3",
|
||||
"js-yaml": "^3.14.1",
|
||||
"libxmljs": "^0.19.7"
|
||||
}
|
||||
}
|
||||
|
||||
765
src/mocks.mjs
Normal file
765
src/mocks.mjs
Normal file
@@ -0,0 +1,765 @@
|
||||
//Imports
|
||||
import axios from "axios"
|
||||
import urls from "url"
|
||||
|
||||
//Mocked state
|
||||
let mocked = false
|
||||
|
||||
//Mocking
|
||||
export default async function ({graphql, rest}) {
|
||||
|
||||
//Check if already mocked
|
||||
if (mocked)
|
||||
return {graphql, rest}
|
||||
mocked = true
|
||||
console.debug(`metrics/compute/mocks > mocking`)
|
||||
|
||||
//GraphQL API mocking
|
||||
{
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api`)
|
||||
const unmocked = graphql
|
||||
graphql = new Proxy(unmocked, {
|
||||
apply(target, that, args) {
|
||||
//Arguments
|
||||
const [query] = args
|
||||
//Common query
|
||||
if (/^query Metrics /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Metrics`)
|
||||
return ({
|
||||
user: {
|
||||
databaseId:22963968,
|
||||
name:"Simon Lecoq",
|
||||
login:"lowlighter",
|
||||
createdAt:"2016-10-20T16:49:29Z",
|
||||
avatarUrl:"https://avatars0.githubusercontent.com/u/22963968?u=f5097de6f06ed2e31906f784163fc1e9fc84ed57&v=4",
|
||||
websiteUrl:"https://simon.lecoq.io",
|
||||
isHireable:false,
|
||||
twitterUsername:"lecoqsimon",
|
||||
repositories:{totalCount:Math.floor(Math.random()*100), totalDiskUsage:Math.floor(Math.random()*100000), nodes:[]},
|
||||
packages:{totalCount:Math.floor(Math.random()*10)},
|
||||
starredRepositories:{totalCount:Math.floor(Math.random()*1000)},
|
||||
watching:{totalCount:Math.floor(Math.random()*100)},
|
||||
sponsorshipsAsSponsor:{totalCount:Math.floor(Math.random()*5)},
|
||||
sponsorshipsAsMaintainer:{totalCount:Math.floor(Math.random()*5)},
|
||||
contributionsCollection:{
|
||||
totalRepositoriesWithContributedCommits:Math.floor(Math.random()*30),
|
||||
totalCommitContributions:Math.floor(Math.random()*1000),
|
||||
restrictedContributionsCount:Math.floor(Math.random()*500),
|
||||
totalIssueContributions:Math.floor(Math.random()*100),
|
||||
totalPullRequestContributions:Math.floor(Math.random()*100),
|
||||
totalPullRequestReviewContributions:Math.floor(Math.random()*100)
|
||||
},
|
||||
calendar:{
|
||||
contributionCalendar:{
|
||||
weeks:[
|
||||
{
|
||||
contributionDays:[
|
||||
{color:"#40c463"},
|
||||
{color:"#ebedf0"},
|
||||
{color:"#9be9a8"},
|
||||
{color:"#ebedf0"},
|
||||
{color:"#ebedf0"}
|
||||
]
|
||||
},
|
||||
{
|
||||
contributionDays:[
|
||||
{color:"#30a14e"},
|
||||
{color:"#9be9a8"},
|
||||
{color:"#40c463"},
|
||||
{color:"#9be9a8"},
|
||||
{color:"#ebedf0"},
|
||||
{color:"#ebedf0"},
|
||||
{color:"#ebedf0"}
|
||||
]
|
||||
},
|
||||
{
|
||||
contributionDays:[
|
||||
{color:"#40c463"},
|
||||
{color:"#216e39"},
|
||||
{color:"#9be9a8"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
repositoriesContributedTo:{totalCount:Math.floor(Math.random()*10)},
|
||||
followers:{totalCount:Math.floor(Math.random()*100)},
|
||||
following:{totalCount:Math.floor(Math.random()*100)},
|
||||
issueComments:{totalCount:Math.floor(Math.random()*100)},
|
||||
organizations:{totalCount:Math.floor(Math.random()*5)}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Repositories query
|
||||
if (/^query Repositories /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Repositories`)
|
||||
return /after: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/m.test(query) ? ({
|
||||
user:{
|
||||
repositories:{
|
||||
edges:[],
|
||||
nodes:[],
|
||||
}
|
||||
}
|
||||
}) : ({
|
||||
user:{
|
||||
repositories:{
|
||||
edges:[
|
||||
{
|
||||
cursor:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
},
|
||||
],
|
||||
nodes:[
|
||||
{
|
||||
name:"metrics",
|
||||
watchers:{totalCount:Math.floor(Math.random()*100)},
|
||||
stargazers:{totalCount:Math.floor(Math.random()*1000)},
|
||||
languages:{
|
||||
edges:[
|
||||
{size:111733, node:{color:"#f1e05a", name:"JavaScript"}
|
||||
},
|
||||
{size:14398, node:{color:"#563d7c", name:"CSS"}},
|
||||
{size:13223, node:{color:"#e34c26", name:"HTML"}},
|
||||
]
|
||||
},
|
||||
issues_open:{totalCount:Math.floor(Math.random()*100)},
|
||||
issues_closed:{totalCount:Math.floor(Math.random()*100)},
|
||||
pr_open:{totalCount:Math.floor(Math.random()*100)},
|
||||
pr_merged:{totalCount:Math.floor(Math.random()*100)},
|
||||
releases:{totalCount:Math.floor(Math.random()*100)},
|
||||
forkCount:Math.floor(Math.random()*100),
|
||||
licenseInfo:{spdxId:"MIT"}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Single repository query
|
||||
if (/^query Repository /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Repository`)
|
||||
return ({
|
||||
user:{
|
||||
repository:{
|
||||
name:"metrics",
|
||||
createdAt:new Date().toISOString(),
|
||||
diskUsage:Math.floor(Math.random()*10000),
|
||||
watchers:{totalCount:Math.floor(Math.random()*100)},
|
||||
stargazers:{totalCount:Math.floor(Math.random()*1000)},
|
||||
languages:{
|
||||
edges:[
|
||||
{size:111733, node:{color:"#f1e05a", name:"JavaScript"}
|
||||
},
|
||||
{size:14398, node:{color:"#563d7c", name:"CSS"}},
|
||||
{size:13223, node:{color:"#e34c26", name:"HTML"}},
|
||||
]
|
||||
},
|
||||
issues_open:{totalCount:Math.floor(Math.random()*100)},
|
||||
issues_closed:{totalCount:Math.floor(Math.random()*100)},
|
||||
pr_open:{totalCount:Math.floor(Math.random()*100)},
|
||||
pr_merged:{totalCount:Math.floor(Math.random()*100)},
|
||||
releases:{totalCount:Math.floor(Math.random()*100)},
|
||||
forkCount:Math.floor(Math.random()*100),
|
||||
licenseInfo:{spdxId:"MIT"}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
//Calendar query
|
||||
if (/^query Calendar /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Calendar`)
|
||||
//Generate calendar
|
||||
const date = new Date(query.match(/from: "(?<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z)"/)?.groups?.date)
|
||||
const to = new Date(query.match(/to: "(?<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z)"/)?.groups?.date)
|
||||
const weeks = []
|
||||
let contributionDays = []
|
||||
for (; date <= to; date.setDate(date.getDate()+1)) {
|
||||
//Create new week on sunday
|
||||
if (date.getDay() === 0) {
|
||||
weeks.push({contributionDays})
|
||||
contributionDays = []
|
||||
}
|
||||
//Random contributions
|
||||
const contributionCount = Math.min(10, Math.max(0, Math.floor(Math.random()*14-4)))
|
||||
contributionDays.push({
|
||||
contributionCount,
|
||||
color:["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e39"][Math.ceil(contributionCount/10/0.25)],
|
||||
date:date.toISOString().substring(0, 10)
|
||||
})
|
||||
}
|
||||
return ({
|
||||
user: {
|
||||
calendar:{
|
||||
contributionCalendar:{
|
||||
weeks
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Gists query
|
||||
if (/^query Gists /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Projects`)
|
||||
return ({
|
||||
user:{
|
||||
gists:{
|
||||
totalCount:1,
|
||||
nodes:[
|
||||
{
|
||||
stargazerCount:Math.floor(Math.random()*10),
|
||||
isFork:false,
|
||||
forks:{totalCount:Math.floor(Math.random()*10)},
|
||||
files:[{name:"example"}],
|
||||
comments:{totalCount:Math.floor(Math.random()*10)}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Projects query
|
||||
if (/^query Projects /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > Projects`)
|
||||
return ({
|
||||
user:{
|
||||
projects:{
|
||||
totalCount:1,
|
||||
nodes:[
|
||||
{
|
||||
name:"User-owned project",
|
||||
updatedAt:new Date().toISOString(),
|
||||
progress:{
|
||||
doneCount:Math.floor(Math.random()*10),
|
||||
inProgressCount:Math.floor(Math.random()*10),
|
||||
todoCount:Math.floor(Math.random()*10),
|
||||
enabled:true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Repository project query
|
||||
if (/^query RepositoryProject /.test(query)) {
|
||||
console.debug(`metrics/compute/mocks > mocking graphql api result > RepositoryProject`)
|
||||
return ({
|
||||
user:{
|
||||
repository:{
|
||||
project:{
|
||||
name:"Repository project example",
|
||||
updatedAt:new Date().toISOString(),
|
||||
progress:{
|
||||
doneCount:Math.floor(Math.random()*10),
|
||||
inProgressCount:Math.floor(Math.random()*10),
|
||||
todoCount:Math.floor(Math.random()*10),
|
||||
enabled:true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//Unmocked call
|
||||
return target(...args)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Rest API mocking
|
||||
{
|
||||
console.debug(`metrics/compute/mocks > mocking rest api`)
|
||||
const unmocked = {
|
||||
request:rest.request,
|
||||
rateLimit:rest.rateLimit.get,
|
||||
listEventsForAuthenticatedUser:rest.activity.listEventsForAuthenticatedUser,
|
||||
getViews:rest.repos.getViews,
|
||||
getContributorsStats:rest.repos.getContributorsStats,
|
||||
listCommits:rest.repos.listCommits,
|
||||
}
|
||||
|
||||
//Raw request
|
||||
rest.request = new Proxy(unmocked.request, {
|
||||
apply:function(target, that, args) {
|
||||
//Arguments
|
||||
const [url] = args
|
||||
//Head request
|
||||
if (/^HEAD .$/.test(url)) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.request HEAD`)
|
||||
return ({
|
||||
status: 200,
|
||||
url:"https://api.github.com/",
|
||||
headers:{
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:undefined
|
||||
})
|
||||
}
|
||||
//Commit content
|
||||
if (/api.github.com.repos.lowlighter.metrics.commits.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/.test(url)) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.request ${url}`)
|
||||
return ({
|
||||
status: 200,
|
||||
url:"https://api.github.com/repos/lowlighter/metrics/commits/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
data:{
|
||||
sha:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
commit:{
|
||||
author:{
|
||||
name:"lowlighter",
|
||||
email:"22963968+lowlighter@users.noreply.github.com",
|
||||
date:new Date().toISOString(),
|
||||
},
|
||||
committer:{
|
||||
name:"lowlighter",
|
||||
email:"22963968+lowlighter@users.noreply.github.com",
|
||||
date:new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
author:{
|
||||
login:"lowlighter",
|
||||
id:22963968,
|
||||
},
|
||||
committer:{
|
||||
login:"lowlighter",
|
||||
id:22963968,
|
||||
},
|
||||
files: [
|
||||
{
|
||||
sha:"5ab8c4fb6a0be4c157419c3b9d7b522dca354b3f",
|
||||
filename:"index.mjs",
|
||||
patch:"@@ -0,0 +1,5 @@\n+//Imports\n+ import app from \"./src/app.mjs\"\n+\n+//Start app\n+ await app()\n\\ No newline at end of file"
|
||||
},
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return target(...args)
|
||||
}
|
||||
})
|
||||
|
||||
//Rate limit
|
||||
rest.rateLimit.get = new Proxy(unmocked.rateLimit, {
|
||||
apply:function(target, that, args) {
|
||||
return ({
|
||||
status: 200,
|
||||
url:"https://api.github.com/rate_limit",
|
||||
headers:{
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:{
|
||||
resources:{
|
||||
core:{limit:5000, used:0, remaining:5000, reset:0 },
|
||||
search:{limit:30, used:0, remaining:30, reset:0 },
|
||||
graphql:{limit:5000, used:0, remaining:5000, reset:0 },
|
||||
integration_manifest:{limit:5000, used:0, remaining:5000, reset:0 },
|
||||
source_import:{limit:100, used:0, remaining:100, reset:0 },
|
||||
code_scanning_upload:{limit:500, used:0, remaining:500, reset:0 },
|
||||
},
|
||||
rate:{limit:5000, used:0, remaining:"MOCKED", reset:0}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//Events list
|
||||
rest.activity.listEventsForAuthenticatedUser = new Proxy(unmocked.listEventsForAuthenticatedUser, {
|
||||
apply:function(target, that, [{page, per_page}]) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.activity.listEventsForAuthenticatedUser`)
|
||||
return ({
|
||||
status:200,
|
||||
url:`https://api.github.com/users/lowlighter/events?per_page=${per_page}&page=${page}`,
|
||||
headers:{
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:page < 1 ? new Array(10).fill(null).map(() =>
|
||||
(false ? {
|
||||
id:"10000000001",
|
||||
type:"IssueCommentEvent",
|
||||
} : {
|
||||
id:"10000000000",
|
||||
type:"PushEvent",
|
||||
actor:{
|
||||
id:22963968,
|
||||
login:"lowlighter",
|
||||
},
|
||||
repo: {
|
||||
id:293860197,
|
||||
name:"lowlighter/metrics",
|
||||
},
|
||||
payload: {
|
||||
ref:"refs/heads/master",
|
||||
commits: [
|
||||
{
|
||||
url:"https://api.github.com/repos/lowlighter/metrics/commits/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
}
|
||||
]
|
||||
},
|
||||
created_at:new Date(Date.now()-Math.floor(-Math.random()*14)*Math.floor(-Math.random()*24)*60*60*1000).toISOString()
|
||||
})
|
||||
) : []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//Repository traffic
|
||||
rest.repos.getViews = new Proxy(unmocked.getViews, {
|
||||
apply:function(target, that, args) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.repos.getViews`)
|
||||
const count = Math.floor(Math.random()*1000)*2
|
||||
const uniques = Math.floor(Math.random()*count)*2
|
||||
return ({
|
||||
status:200,
|
||||
url:"https://api.github.com/repos/lowlighter/metrics/traffic/views",
|
||||
headers:{
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:{
|
||||
count,
|
||||
uniques,
|
||||
views:[
|
||||
{timestamp:new Date().toISOString(), count:count/2, uniques:uniques/2},
|
||||
{timestamp:new Date().toISOString(), count:count/2, uniques:uniques/2},
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//Repository contributions
|
||||
rest.repos.getContributorsStats = new Proxy(unmocked.getContributorsStats, {
|
||||
apply:function(target, that, args) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.repos.getContributorsStats`)
|
||||
return ({
|
||||
status:200,
|
||||
url:"https://api.github.com/repos/lowlighter/metrics/stats/contributors",
|
||||
headers: {
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:[
|
||||
{
|
||||
total:Math.floor(Math.random()*1000),
|
||||
weeks:[
|
||||
{w:1, a:Math.floor(Math.random()*10000), d:Math.floor(Math.random()*10000), c:Math.floor(Math.random()*10000)},
|
||||
{w:2, a:Math.floor(Math.random()*10000), d:Math.floor(Math.random()*10000), c:Math.floor(Math.random()*10000)},
|
||||
{w:3, a:Math.floor(Math.random()*10000), d:Math.floor(Math.random()*10000), c:Math.floor(Math.random()*10000)},
|
||||
{w:4, a:Math.floor(Math.random()*10000), d:Math.floor(Math.random()*10000), c:Math.floor(Math.random()*10000)},
|
||||
],
|
||||
author: {
|
||||
login:"lowlighter",
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//Repository contributions
|
||||
rest.repos.listCommits = new Proxy(unmocked.listCommits, {
|
||||
apply:function(target, that, [{page, per_page}]) {
|
||||
console.debug(`metrics/compute/mocks > mocking rest api result > rest.repos.listCommits`)
|
||||
return ({
|
||||
status:200,
|
||||
url:`https://api.github.com/repos/lowlighter/metrics/commits?per_page=${per_page}&page=${page}`,
|
||||
headers: {
|
||||
server:"GitHub.com",
|
||||
status:"200 OK",
|
||||
"x-oauth-scopes":"repo",
|
||||
},
|
||||
data:page < 2 ? new Array(per_page).fill(null).map(() =>
|
||||
({
|
||||
sha:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
commit:{
|
||||
author:{
|
||||
name:"lowlighter",
|
||||
date:new Date(Date.now()-Math.floor(-Math.random()*14)*24*60*60*1000).toISOString()
|
||||
},
|
||||
committer:{
|
||||
name:"lowlighter",
|
||||
date:new Date(Date.now()-Math.floor(-Math.random()*14)*24*60*60*1000).toISOString()
|
||||
},
|
||||
}
|
||||
})
|
||||
) : []
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Axios mocking
|
||||
{
|
||||
console.debug(`metrics/compute/mocks > mocking axios`)
|
||||
const unmocked = {
|
||||
get:axios.get,
|
||||
post:axios.post,
|
||||
}
|
||||
|
||||
//Post requests
|
||||
axios.post = new Proxy(unmocked.post, {
|
||||
apply:function(target, that, args) {
|
||||
//Arguments
|
||||
const [url, body, options] = args
|
||||
//Spotify api
|
||||
if (/accounts.spotify.com.api.token/.test(url)) {
|
||||
//Access token generator
|
||||
const params = new urls.URLSearchParams(body)
|
||||
if ((params.get("grant_type") === "refresh_token")&&(params.get("client_id") === "MOCKED_CLIENT_ID")&&(params.get("client_secret") === "MOCKED_CLIENT_SECRET")&&(params.get("refresh_token") === "MOCKED_REFRESH_TOKEN")) {
|
||||
console.debug(`metrics/compute/mocks > mocking spotify api result > ${url}`)
|
||||
return ({
|
||||
status:200,
|
||||
data:{
|
||||
access_token:"MOCKED_TOKEN_ACCESS",
|
||||
token_type:"Bearer",
|
||||
expires_in:3600,
|
||||
scope:"user-read-recently-played user-read-private",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return target(...args)
|
||||
}
|
||||
})
|
||||
|
||||
//Get requests
|
||||
axios.get = new Proxy(unmocked.get, {
|
||||
apply:function(target, that, args) {
|
||||
//Arguments
|
||||
const [url, options] = args
|
||||
//Pagespeed api
|
||||
if (/googleapis.com.pagespeedonline.v5/.test(url)) {
|
||||
//Pagespeed result
|
||||
if (/v5.runPagespeed.*&key=MOCKED_TOKEN/.test(url)) {
|
||||
console.debug(`metrics/compute/mocks > mocking pagespeed api result > ${url}`)
|
||||
return ({
|
||||
status:200,
|
||||
data:{
|
||||
captchaResult:"CAPTCHA_NOT_NEEDED",
|
||||
id:"https://simon.lecoq.io/",
|
||||
lighthouseResult:{
|
||||
requestedUrl:"https://simon.lecoq.io/",
|
||||
finalUrl:"https://simon.lecoq.io/",
|
||||
lighthouseVersion:"6.3.0",
|
||||
audits:{
|
||||
"final-screenshot":{
|
||||
id:"final-screenshot",
|
||||
title:"Final Screenshot",
|
||||
score: null,
|
||||
details:{
|
||||
data:"data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==",
|
||||
type:"screenshot",
|
||||
timestamp:Date.now()
|
||||
}
|
||||
},
|
||||
metrics:{
|
||||
id:"metrics",
|
||||
title:"Metrics",
|
||||
score: null,
|
||||
details:{
|
||||
items:[
|
||||
{
|
||||
observedFirstContentfulPaint:283,
|
||||
observedFirstVisualChangeTs:1789259909429,
|
||||
observedFirstContentfulPaintTs:1789259857628,
|
||||
firstContentfulPaint:370,
|
||||
observedDomContentLoaded:251,
|
||||
observedFirstMeaningfulPaint:642,
|
||||
maxPotentialFID:203,
|
||||
observedLoad:330,
|
||||
firstMeaningfulPaint:370,
|
||||
observedCumulativeLayoutShift:0.0028944855967078186,
|
||||
observedSpeedIndex:711,
|
||||
observedSpeedIndexTs:1789260285891,
|
||||
observedTimeOriginTs:1789259574429,
|
||||
observedLargestContentfulPaint:857,
|
||||
cumulativeLayoutShift:0.0028944855967078186,
|
||||
observedFirstPaintTs:1789259857628,
|
||||
observedTraceEndTs:1789261300953,
|
||||
largestContentfulPaint:1085,
|
||||
observedTimeOrigin:0,
|
||||
speedIndex:578,
|
||||
observedTraceEnd:1727,
|
||||
observedDomContentLoadedTs:1789259825567,
|
||||
observedFirstPaint:283,
|
||||
totalBlockingTime:133,
|
||||
observedLastVisualChangeTs:1789260426429,
|
||||
observedFirstVisualChange:335,
|
||||
observedLargestContentfulPaintTs:1789260431554,
|
||||
estimatedInputLatency:13,
|
||||
observedLoadTs:1789259904916,
|
||||
observedLastVisualChange:852,
|
||||
firstCPUIdle:773,
|
||||
interactive:953,
|
||||
observedNavigationStartTs:1789259574429,
|
||||
observedNavigationStart:0,
|
||||
observedFirstMeaningfulPaintTs:1789260216895
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
},
|
||||
categories:{
|
||||
"best-practices":{
|
||||
id:"best-practices",
|
||||
title:"Best Practices",
|
||||
score:Math.floor(Math.random()*100)/100,
|
||||
},
|
||||
seo:{
|
||||
id:"seo",
|
||||
title:"SEO",
|
||||
score:Math.floor(Math.random()*100)/100,
|
||||
},
|
||||
accessibility:{
|
||||
id:"accessibility",
|
||||
title:"Accessibility",
|
||||
score:Math.floor(Math.random()*100)/100,
|
||||
},
|
||||
performance: {
|
||||
id:"performance",
|
||||
title:"Performance",
|
||||
score:Math.floor(Math.random()*100)/100,
|
||||
}
|
||||
},
|
||||
},
|
||||
analysisUTCTimestamp:new Date().toISOString()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
//Spotify api
|
||||
if (/api.spotify.com/.test(url)) {
|
||||
//Get recently played tracks
|
||||
if (/me.player.recently-played/.test(url)&&(options?.headers?.Authorization === "Bearer MOCKED_TOKEN_ACCESS")) {
|
||||
console.debug(`metrics/compute/mocks > mocking spotify api result > ${url}`)
|
||||
return ({
|
||||
status:200,
|
||||
data:{
|
||||
items:[
|
||||
{
|
||||
track:{
|
||||
album:{
|
||||
album_type:"single",
|
||||
artists:[
|
||||
{
|
||||
name:"EGOIST",
|
||||
type:"artist",
|
||||
}
|
||||
],
|
||||
images:[
|
||||
{
|
||||
height:640,
|
||||
url:"https://i.scdn.co/image/ab67616d0000b27366371d0ad05c3f402d9cb2ae",
|
||||
width:640
|
||||
},
|
||||
{
|
||||
height:300,
|
||||
url:"https://i.scdn.co/image/ab67616d00001e0266371d0ad05c3f402d9cb2ae",
|
||||
width:300
|
||||
},
|
||||
{
|
||||
height:64,
|
||||
url:"https://i.scdn.co/image/ab67616d0000485166371d0ad05c3f402d9cb2ae",
|
||||
width:64
|
||||
}
|
||||
],
|
||||
name:"Fallen",
|
||||
release_date:"2014-11-19",
|
||||
type:"album",
|
||||
},
|
||||
artists:[
|
||||
{
|
||||
name:"EGOIST",
|
||||
type:"artist",
|
||||
}
|
||||
],
|
||||
name:"Fallen",
|
||||
preview_url:"https://p.scdn.co/mp3-preview/f30eb6d1c55afa13ce754559a41ab683a1a76b02?cid=fa6ae353840041ee8af3bd1d21a66783",
|
||||
type:"track",
|
||||
},
|
||||
played_at:new Date().toISOString(),
|
||||
context:{
|
||||
type:"album",
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
//Twitter api
|
||||
if (/api.twitter.com/.test(url)) {
|
||||
//Get user profile
|
||||
if ((/users.by.username/.test(url))&&(options?.headers?.Authorization === "Bearer MOCKED_TOKEN")) {
|
||||
console.debug(`metrics/compute/mocks > mocking twitter api result > ${url}`)
|
||||
return ({
|
||||
status:200,
|
||||
data:{
|
||||
data:{
|
||||
profile_image_url:"https://pbs.twimg.com/profile_images/1338344493234286592/C_ujKIUa_normal.png",
|
||||
name:"GitHub",
|
||||
verified:true,
|
||||
id:"13334762",
|
||||
username:"github",
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
//Get recent tweets
|
||||
if ((/tweets.search.recent/.test(url))&&(options?.headers?.Authorization === "Bearer MOCKED_TOKEN")) {
|
||||
console.debug(`metrics/compute/mocks > mocking twitter api result > ${url}`)
|
||||
return ({
|
||||
status:200,
|
||||
data:{
|
||||
data:[
|
||||
{
|
||||
id:"1000000000000000001",
|
||||
created_at:new Date().toISOString(),
|
||||
entities:{
|
||||
mentions:[
|
||||
{start:22, end:33, username:"lowlighter"},
|
||||
],
|
||||
},
|
||||
text:"Checkout metrics from @lowlighter ! #GitHub",
|
||||
},
|
||||
{
|
||||
id:"1000000000000000000",
|
||||
created_at:new Date().toISOString(),
|
||||
text:"Hello world !",
|
||||
}
|
||||
],
|
||||
includes:{
|
||||
users:[
|
||||
{
|
||||
id:"100000000000000000",
|
||||
name:"lowlighter",
|
||||
username:"lowlighter",
|
||||
},
|
||||
]
|
||||
},
|
||||
meta:{
|
||||
newest_id:"1000000000000000001",
|
||||
oldest_id:"1000000000000000000",
|
||||
result_count:2,
|
||||
next_token:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return target(...args)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Return mocked elements
|
||||
return {graphql, rest}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,9 @@
|
||||
{headers:{"Content-Type":"application/x-www-form-urlencoded"}},
|
||||
)
|
||||
console.debug(`metrics/compute/${login}/plugins > music > got access token`)
|
||||
//Retriev tracks
|
||||
//Retrieve tracks
|
||||
console.debug(`metrics/compute/${login}/plugins > music > querying spotify api`)
|
||||
tracks = (await imports.axios(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
|
||||
tracks = (await imports.axios.get(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
|
||||
"Accept":"application/json",
|
||||
"Content-Type":"application/json",
|
||||
"Authorization":`Bearer ${access}`}
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
if (error.error?.message)
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,6 @@ query Metrics {
|
||||
websiteUrl
|
||||
isHireable
|
||||
twitterUsername
|
||||
gists {
|
||||
totalCount
|
||||
}
|
||||
repositories(last: 0, isFork: false, ownerAffiliations: OWNER) {
|
||||
totalCount
|
||||
totalDiskUsage
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
query Projects {
|
||||
query RepositoryProject {
|
||||
user(login: "$user") {
|
||||
repository(name: "$repository") {
|
||||
project(number: $id) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
query Metrics {
|
||||
query Repositories {
|
||||
user(login: "$login") {
|
||||
repositories($after first: $repositories, isFork: false, ownerAffiliations: OWNER, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||
edges {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
query Metrics {
|
||||
query Repository {
|
||||
user(login: "$login") {
|
||||
repository(name: "$repo") {
|
||||
name
|
||||
|
||||
105
src/setup.mjs
105
src/setup.mjs
@@ -37,73 +37,56 @@
|
||||
|
||||
//Load package settings
|
||||
logger(`metrics/setup > load package.json`)
|
||||
if (fs.existsSync(path.resolve("package.json"))) {
|
||||
conf.package = JSON.parse(`${await fs.promises.readFile(path.resolve("package.json"))}`)
|
||||
logger(`metrics/setup > load package.json > success`)
|
||||
}
|
||||
else {
|
||||
logger(`metrics/setup > load package.json > (missing)`)
|
||||
conf.package = {version:"<#version>", author:"lowlighter"}
|
||||
}
|
||||
conf.package = JSON.parse(`${await fs.promises.readFile(path.resolve("package.json"))}`)
|
||||
logger(`metrics/setup > load package.json > success`)
|
||||
|
||||
//Load templates
|
||||
if (fs.existsSync(path.resolve(templates))) {
|
||||
for (const name of await fs.promises.readdir(templates)) {
|
||||
//Cache templates
|
||||
if (/.*[.]mjs$/.test(name))
|
||||
continue
|
||||
logger(`metrics/setup > load template [${name}]`)
|
||||
const files = [
|
||||
`${templates}/${name}/image.svg`,
|
||||
`${templates}/${name}/style.css`,
|
||||
`${templates}/${name}/fonts.css`,
|
||||
].map(file => fs.existsSync(path.resolve(file)) ? file : file.replace(`${templates}/${name}/`, `${templates}/classic/`)).map(file => path.resolve(file))
|
||||
const [image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(file)}`))
|
||||
conf.templates[name] = {image, style, fonts}
|
||||
logger(`metrics/setup > load template [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.templates, name, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload template [${name}]`)
|
||||
const [image, style, fonts] = files.map(file => `${fs.readFileSync(file)}`)
|
||||
logger(`metrics/setup > reload template [${name}] > success`)
|
||||
return {image, style, fonts}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(`metrics/setup > load templates from build`)
|
||||
conf.templates = JSON.parse(Buffer.from(`<#assets>`, "base64").toString("utf8"))
|
||||
for (const name of await fs.promises.readdir(templates)) {
|
||||
//Cache templates
|
||||
if (/.*[.]mjs$/.test(name))
|
||||
continue
|
||||
logger(`metrics/setup > load template [${name}]`)
|
||||
const files = [
|
||||
`${templates}/${name}/image.svg`,
|
||||
`${templates}/${name}/style.css`,
|
||||
`${templates}/${name}/fonts.css`,
|
||||
].map(file => fs.existsSync(path.resolve(file)) ? file : file.replace(`${templates}/${name}/`, `${templates}/classic/`)).map(file => path.resolve(file))
|
||||
const [image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(file)}`))
|
||||
conf.templates[name] = {image, style, fonts}
|
||||
logger(`metrics/setup > load template [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.templates, name, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload template [${name}]`)
|
||||
const [image, style, fonts] = files.map(file => `${fs.readFileSync(file)}`)
|
||||
logger(`metrics/setup > reload template [${name}] > success`)
|
||||
return {image, style, fonts}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//Load queries
|
||||
if (fs.existsSync(path.resolve(queries))) {
|
||||
for (const query of await fs.promises.readdir(queries)) {
|
||||
//Cache queries
|
||||
const name = query.replace(/[.]graphql$/, "")
|
||||
logger(`metrics/setup > load query [${name}]`)
|
||||
conf.queries[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > load query [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.queries, `_${name}`, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload query [${name}]`)
|
||||
const raw = `${fs.readFileSync(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > reload query [${name}] > success`)
|
||||
return raw
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(`metrics/setup > load queries from build`)
|
||||
conf.queries = JSON.parse(Buffer.from(`<#queries>`, "base64").toString("utf8"))
|
||||
for (const query of await fs.promises.readdir(queries)) {
|
||||
//Cache queries
|
||||
const name = query.replace(/[.]graphql$/, "")
|
||||
logger(`metrics/setup > load query [${name}]`)
|
||||
conf.queries[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > load query [${name}] > success`)
|
||||
//Debug
|
||||
if (conf.settings.debug) {
|
||||
Object.defineProperty(conf.queries, `_${name}`, {
|
||||
get() {
|
||||
logger(`metrics/setup > reload query [${name}]`)
|
||||
const raw = `${fs.readFileSync(path.resolve(`${queries}/${query}`))}`
|
||||
logger(`metrics/setup > reload query [${name}] > success`)
|
||||
return raw
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//Create queries formatters
|
||||
Object.keys(conf.queries).map(name => conf.queries[name.substring(1)] = (vars = {}) => {
|
||||
let query = conf.queries[name]
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//Imports
|
||||
import build from "../utils/build.mjs"
|
||||
import colors from "colors"
|
||||
|
||||
//Initialization
|
||||
process.on("unhandledRejection", error => { throw error })
|
||||
colors.enable()
|
||||
|
||||
/** Test function */
|
||||
export default async function test() {
|
||||
//Perform tests
|
||||
await test.build()
|
||||
}
|
||||
|
||||
/** Build test */
|
||||
test.build = async function () {
|
||||
//Ensure that code has been rebuild
|
||||
console.log("TEST : build".cyan)
|
||||
await build({actions:["check"]})
|
||||
}
|
||||
|
||||
//Main
|
||||
if (/metrics.mjs/.test(process.argv[1])) {
|
||||
//Test
|
||||
await test()
|
||||
console.log("Test success !".green)
|
||||
}
|
||||
211
tests/metrics.test.js
Normal file
211
tests/metrics.test.js
Normal file
@@ -0,0 +1,211 @@
|
||||
//Imports
|
||||
const processes = require("child_process")
|
||||
const yaml = require("js-yaml")
|
||||
const fs = require("fs")
|
||||
|
||||
//Github action
|
||||
const action = yaml.safeLoad(fs.readFileSync("action.yml", "utf8"))
|
||||
action.defaults = Object.fromEntries(Object.entries(action.inputs).map(([key, {default:value}]) => [key, /^(yes|no)$/.test(value) ? value === "yes" : value]))
|
||||
action.input = vars => Object.fromEntries([...Object.entries(action.defaults), ...Object.entries(vars)].map(([key, value]) => [`INPUT_${key.toLocaleUpperCase()}`, value]))
|
||||
action.run = async (vars) => await new Promise((solve, reject) => {
|
||||
let [stdout, stderr] = ["", ""]
|
||||
const env = {...process.env, ...action.input(vars), GITHUB_REPOSITORY:"lowlighter/metrics"}
|
||||
const child = processes.spawn("node", ["action/index.mjs"], {env})
|
||||
child.stdout.on("data", data => stdout += data)
|
||||
child.stderr.on("data", data => stderr += data)
|
||||
child.on("close", code => {
|
||||
if (code === 0)
|
||||
return solve(true)
|
||||
console.log(stdout, stderr)
|
||||
reject(stdout)
|
||||
})
|
||||
})
|
||||
|
||||
//Tests run
|
||||
describe.each([
|
||||
["classic", {}],
|
||||
["terminal", {}],
|
||||
["repository", {repo:"metrics"}],
|
||||
])("Template : %s", (template, query) => {
|
||||
for (const [name, input, {skip = []} = {}] of [
|
||||
["Base (header)", {
|
||||
base:"header"
|
||||
}],
|
||||
["Base (activity", {
|
||||
base:"activity"
|
||||
}],
|
||||
["Base (community)", {
|
||||
base:"community"
|
||||
}],
|
||||
["Base (repositories)", {
|
||||
base:"repositories"
|
||||
}],
|
||||
["Base (metadata)", {
|
||||
base:"metadata"
|
||||
}],
|
||||
["Base (complete)", {
|
||||
base:"header, activity, community, repositories, metadata"
|
||||
}],
|
||||
["PageSpeed plugin (default)", {
|
||||
plugin_pagespeed:true,
|
||||
}, {skip:["repository"]}],
|
||||
["PageSpeed plugin (detailed)", {
|
||||
plugin_pagespeed:true,
|
||||
plugin_pagespeed_detailed:true,
|
||||
}, {skip:["repository"]}],
|
||||
["PageSpeed plugin (screenshot)", {
|
||||
plugin_pagespeed:true,
|
||||
plugin_pagespeed_screenshot:true,
|
||||
}, {skip:["repository"]}],
|
||||
["PageSpeed plugin (complete)", {
|
||||
plugin_pagespeed:true,
|
||||
plugin_pagespeed_detailed:true,
|
||||
plugin_pagespeed_screenshot:true,
|
||||
}, {skip:["repository"]}],
|
||||
["Isocalendar plugin (default)", {
|
||||
plugin_isocalendar: true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Isocalendar plugin (half-year)", {
|
||||
plugin_isocalendar: true,
|
||||
plugin_isocalendar_duration: "half-year",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Isocalendar plugin (full-year)", {
|
||||
plugin_isocalendar: true,
|
||||
plugin_isocalendar_duration: "full-year",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Music plugin (playlist - apple)", {
|
||||
plugin_music:true,
|
||||
plugin_music_playlist:"https://embed.music.apple.com/fr/playlist/usr-share/pl.u-V9D7m8Etjmjd0D",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Music plugin (playlist - spotify)", {
|
||||
plugin_music:true,
|
||||
plugin_music_playlist:"https://open.spotify.com/embed/playlist/3nfA87oeJw4LFVcUDjRcqi",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Music plugin (recent - spotify)", {
|
||||
plugin_music:true,
|
||||
plugin_music_provider: "spotify",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Language plugin (default)", {
|
||||
plugin_languages:true,
|
||||
}, {skip:["repository"]}],
|
||||
["Language plugin (ignored languages)", {
|
||||
plugin_languages:true,
|
||||
plugin_languages_ignored:"html, css, dockerfile",
|
||||
}, {skip:["repository"]}],
|
||||
["Language plugin (skipped repositories)", {
|
||||
plugin_languages:true,
|
||||
plugin_languages_skipped:"metrics",
|
||||
}, {skip:["repository"]}],
|
||||
["Language plugin (complete)", {
|
||||
plugin_languages:true,
|
||||
plugin_languages_ignored:"html, css, dockerfile",
|
||||
plugin_languages_skipped:"metrics",
|
||||
}, {skip:["repository"]}],
|
||||
["Follow-up plugin (default)", {
|
||||
plugin_followup:true,
|
||||
}],
|
||||
["Topics plugin (default)", {
|
||||
plugin_topics:true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (starred - starred sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"starred",
|
||||
plugin_topics_sort:"starred",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (starred - activity sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"starred",
|
||||
plugin_topics_sort:"activity",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (starred - stars sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"starred",
|
||||
plugin_topics_sort:"stars",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (starred - random sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"starred",
|
||||
plugin_topics_sort:"random",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (mastered - starred sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"mastered",
|
||||
plugin_topics_sort:"starred",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (mastered - activity sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"mastered",
|
||||
plugin_topics_sort:"activity",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (mastered - stars sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"mastered",
|
||||
plugin_topics_sort:"stars",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Topics plugin (mastered - random sort)", {
|
||||
plugin_topics:true,
|
||||
plugin_topics_mode:"mastered",
|
||||
plugin_topics_sort:"random",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Projects plugin (default)", {
|
||||
plugin_projects:true,
|
||||
}, {skip:["terminal"]}],
|
||||
["Projects plugin (repositories)", {
|
||||
plugin_projects:true,
|
||||
plugin_projects_repositories:"lowlighter/metrics/projects/1",
|
||||
plugin_projects_limit:0,
|
||||
}, {skip:["terminal"]}],
|
||||
["Lines plugin (default)", {
|
||||
base:"repositories",
|
||||
plugin_lines:true,
|
||||
}],
|
||||
["Traffic plugin (default)", {
|
||||
base:"repositories",
|
||||
plugin_traffic:true,
|
||||
}],
|
||||
["Tweets plugin (default)", {
|
||||
plugin_tweets:true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Posts plugin (dev.to)", {
|
||||
user:"lowlighter",
|
||||
plugin_posts:true,
|
||||
plugin_posts_source:"dev.to",
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Habits plugin (default)", {
|
||||
plugin_habits:true,
|
||||
plugin_habits_from:5,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Habits plugin (charts)", {
|
||||
plugin_habits:true,
|
||||
plugin_habits_from:5,
|
||||
plugin_habits_charts:true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Habits plugin (facts)", {
|
||||
plugin_habits:true,
|
||||
plugin_habits_from:5,
|
||||
plugin_habits_facts:true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Habits plugin (complete)", {
|
||||
plugin_habits:true,
|
||||
plugin_habits_from:5,
|
||||
plugin_habits_charts:true,
|
||||
plugin_habits_charts:true,
|
||||
}, {skip:["terminal", "repository"]}],
|
||||
["Gists plugin (default)", {
|
||||
plugin_gists:true,
|
||||
}, {skip:["terminal"]}],
|
||||
])
|
||||
if (skip.includes(template))
|
||||
test.skip(name, () => null)
|
||||
else
|
||||
test(name, async () => expect(await action.run({
|
||||
token:"MOCKED_TOKEN",
|
||||
plugin_pagespeed_token:"MOCKED_TOKEN",
|
||||
plugin_tweets_token:"MOCKED_TOKEN",
|
||||
plugin_music_token:"MOCKED_CLIENT_ID, MOCKED_CLIENT_SECRET, MOCKED_REFRESH_TOKEN",
|
||||
template, base:"", query:JSON.stringify(query),
|
||||
config_timezone:"Europe/Paris",
|
||||
plugins_errors_fatal:true, dryrun:true, use_mocked_data:true, verify:true,
|
||||
...input
|
||||
})).toBe(true), 60*1e3)
|
||||
})
|
||||
117
utils/build.mjs
117
utils/build.mjs
@@ -2,20 +2,13 @@
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import url from "url"
|
||||
import ncc from "@vercel/ncc"
|
||||
import minify from "babel-minify"
|
||||
import colors from "colors"
|
||||
import ejs from "ejs"
|
||||
|
||||
//Initialization
|
||||
const __dirname = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "..")
|
||||
const __action = path.join(__dirname, "action")
|
||||
const __workflows = path.join(__dirname, ".github/workflows")
|
||||
const __utils = path.join(__dirname, "utils")
|
||||
const __src = path.join(__dirname, "src")
|
||||
const __plugins = path.join(__src, "plugins")
|
||||
const __templates = path.join(__src, "templates")
|
||||
const __queries = path.join(__src, "queries")
|
||||
process.on("unhandledRejection", error => { throw error })
|
||||
colors.enable()
|
||||
|
||||
@@ -61,116 +54,6 @@
|
||||
|
||||
}
|
||||
|
||||
//Workflow
|
||||
{
|
||||
//Build
|
||||
const code = await ejs.renderFile(path.join(__utils, "workflow.yml"), {
|
||||
releases:["master"],
|
||||
templates:(await fs.promises.readdir(__templates)).filter(name => !/.*[.]mjs$/.test(name)).filter(name => !["repository"].includes(name)).sort(),
|
||||
testcase(context = {}) {
|
||||
return [`with:`, ...Object.entries({
|
||||
token:"${{ secrets.METRICS_TOKEN }}",
|
||||
dryrun:true,
|
||||
repositories:0,
|
||||
template:"${{ matrix.template }}",
|
||||
base:"",
|
||||
plugins_errors_fatal:true,
|
||||
...context
|
||||
}).map(([key, value]) => `${" ".repeat(5)}${key}: ${
|
||||
typeof value === "boolean" ? (value ? "yes" : "no") :
|
||||
typeof value === "string" ? (!value ? `""` : value) :
|
||||
value
|
||||
}`)].join("\n")
|
||||
},
|
||||
}, {async:true})
|
||||
console.log(`Generated workflow`.grey)
|
||||
|
||||
//Save build
|
||||
if (actions.includes("build")) {
|
||||
fs.promises.writeFile(path.join(__workflows, "workflow.yml"), code)
|
||||
console.log(`Generated workflow saved to ${path.join(__workflows, "dist/index.js")}`.green)
|
||||
}
|
||||
|
||||
//Check build
|
||||
if (actions.includes("check")) {
|
||||
const status = `${await fs.promises.readFile(path.join(__workflows, "workflow.yml"))}` === code
|
||||
if (status)
|
||||
console.log(`Workflow is up-to-date`.grey)
|
||||
else {
|
||||
console.log(`Workflow is outdated`.red)
|
||||
errors.push(`Workflow is outdated, run "npm run build" to fix it`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Action
|
||||
{
|
||||
//Build
|
||||
let {code} = await ncc(`${__action}/index.mjs`, {sourceMap:false, sourceMapRegister:false})
|
||||
console.log(`Generated action`.grey)
|
||||
|
||||
//Perform assets includes
|
||||
{
|
||||
const assets = {}
|
||||
const templates = (await fs.promises.readdir(__templates)).filter(name => !/.*[.]mjs$/.test(name)).sort()
|
||||
for (const name of templates) {
|
||||
const files = [
|
||||
`${__templates}/${name}/image.svg`,
|
||||
`${__templates}/${name}/style.css`,
|
||||
`${__templates}/${name}/fonts.css`,
|
||||
].map(file => fs.existsSync(path.resolve(file)) ? file : file.replace(`${__templates}/${name}/`, `${__templates}/classic/`))
|
||||
const [image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(path.resolve(file))}`))
|
||||
assets[name] = {image, style, fonts}
|
||||
console.log(`Prepared template ${name}`.grey)
|
||||
}
|
||||
code = code.replace(/<#assets>/g, Buffer.from(JSON.stringify(assets)).toString("base64"))
|
||||
console.log(`Included ${templates.length} templates to generated action`.grey)
|
||||
}
|
||||
|
||||
//Perform queries includes
|
||||
{
|
||||
const assets = {}
|
||||
const queries = (await fs.promises.readdir(__queries)).sort()
|
||||
for (const query of queries) {
|
||||
const name = query.replace(/[.]graphql$/, "")
|
||||
assets[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${__queries}/${query}`))}`
|
||||
console.log(`Prepared query ${name}`.grey)
|
||||
}
|
||||
code = code.replace(/<#queries>/g, Buffer.from(JSON.stringify(assets)).toString("base64"))
|
||||
console.log(`Included ${queries.length} queries to generated action`.grey)
|
||||
}
|
||||
|
||||
//Perform version include
|
||||
{
|
||||
const version = JSON.parse(await fs.promises.readFile(path.join(__dirname, "package.json"))).version
|
||||
code = code.replace(/<#version>/g, version)
|
||||
console.log(`Included version number (${version}) to generated action`.grey)
|
||||
}
|
||||
|
||||
//Minify
|
||||
code = minify(code).code
|
||||
console.log(`Minified code`.grey)
|
||||
if (!code)
|
||||
throw new Error(`Failed to minify code`)
|
||||
|
||||
//Save build
|
||||
if (actions.includes("build")) {
|
||||
fs.promises.writeFile(path.join(__action, "dist/index.js"), code)
|
||||
console.log(`Generated action saved to ${path.join(__action, "dist/index.js")}`.green)
|
||||
}
|
||||
|
||||
//Check build
|
||||
if (actions.includes("check")) {
|
||||
const status = `${await fs.promises.readFile(path.join(__action, "dist/index.js"))}` === code
|
||||
if (status)
|
||||
console.log(`Action is up-to-date`.grey)
|
||||
else {
|
||||
console.log(`Action is outdated`.red)
|
||||
errors.push(`Action is outdated, run "npm run build" to fix it`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Throw on errors
|
||||
if (errors.length)
|
||||
throw new Error(`${errors.length} errors occured :\n${errors.map(error => ` - ${error}`).join("\n")}`)
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
- name: Install
|
||||
run: npm ci
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
needs: <%- JSON.stringify(releases.map(release => `test-${release}`)) %>
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: javascript
|
||||
config-file: ./.github/config/codeql.yml
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
# Tests cases below are auto generated through `npm run build`
|
||||
# Edit utils/workflow.yml instead if you need to update workflow
|
||||
<% for (const release of releases) { %>
|
||||
test-<%- release %>:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
template: <%- JSON.stringify(templates) %>
|
||||
steps:
|
||||
|
||||
- name: ${{ matrix.template }} > Base
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
base: "header, activity, community, repositories, metadata",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > PageSpeed
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_pagespeed: true,
|
||||
plugin_pagespeed_token: "${{ secrets.PAGESPEED_TOKEN }}",
|
||||
plugin_pagespeed_detailed: true,
|
||||
plugin_pagespeed_screenshot: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (playlist - apple)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_music: true,
|
||||
plugin_music_playlist: "${{ secrets.MUSIC_PLAYLIST_APPLE }}",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (playlist - spotify)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_music: true,
|
||||
plugin_music_playlist: "${{ secrets.MUSIC_PLAYLIST_SPOTIFY }}",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Music (recent - spotify)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_music: true,
|
||||
plugin_music_provider: "spotify",
|
||||
plugin_music_token: "${{ secrets.SPOTIFY_TOKENS }}",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Posts (dev.to)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_posts: true,
|
||||
plugin_posts_source: "dev.to",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Isocalendar
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_isocalendar: true,
|
||||
plugin_isocalendar_duration: "full-year",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Habits
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_habits: true,
|
||||
plugin_habits_from: 5,
|
||||
plugin_habits_charts: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Languages
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_languages: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Follow-up
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_followup: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Lines and Traffic
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_lines: true,
|
||||
plugin_traffic: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Gists
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_gists: true,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Topics (starred)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_topics: true,
|
||||
plugin_topics_mode: "starred",
|
||||
plugin_topics_sort: "random",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Topics (mastered)
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_topics: true,
|
||||
plugin_topics_mode: "mastered",
|
||||
plugin_topics_sort: "stars",
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Projects
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_projects: true,
|
||||
plugin_projects_repositories: "lowlighter/metrics/projects/1",
|
||||
plugin_projects_limit: 2,
|
||||
}) %>
|
||||
|
||||
- name: ${{ matrix.template }} > Plugin > Tweets
|
||||
uses: lowlighter/metrics@<%- release %>
|
||||
<%- testcase({
|
||||
plugin_tweets: true,
|
||||
plugin_tweets_limit: 2,
|
||||
plugin_tweets_token: "${{ secrets.TWITTER_TOKEN }}",
|
||||
}) %>
|
||||
|
||||
<% } -%>
|
||||
Reference in New Issue
Block a user