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:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v3
|
- name: Flag stale issues and pull requests
|
||||||
|
uses: actions/stale@v3
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
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.
|
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:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -10,237 +10,23 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup
|
- name: Build lowlighter/metrics:${{ github.head_ref || github.base_ref }}
|
||||||
uses: actions/setup-node@v1
|
run: docker build -t lowlighter/metrics:${{ github.head_ref || github.base_ref }} .
|
||||||
with:
|
- name: Run tests
|
||||||
node-version: 15.x
|
run: docker run --workdir=/metrics --entrypoint="" lowlighter/metrics:${{ github.head_ref || github.base_ref }} npm test
|
||||||
- name: Install
|
|
||||||
run: npm ci
|
|
||||||
- name: Build
|
|
||||||
run: npm run build
|
|
||||||
- name: Test
|
|
||||||
run: npm test
|
|
||||||
|
|
||||||
analyze:
|
analyze:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: ["test-master"]
|
needs: [ build ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup
|
- name: Setup CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
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
|
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.METRICS_TOKEN }}
|
languages: javascript
|
||||||
dryrun: yes
|
config-file: ./.github/config/codeql.yml
|
||||||
repositories: 0
|
- name: Analyze code
|
||||||
template: ${{ matrix.template }}
|
uses: github/codeql-action/analyze@v1
|
||||||
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 }}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ It should be avoided when possible as it increases drastically the size of gener
|
|||||||
* To optimize generated SVG
|
* To optimize generated SVG
|
||||||
* [axios/axios](https://github.com/axios/axios)
|
* [axios/axios](https://github.com/axios/axios)
|
||||||
* To make HTTP/S requests
|
* 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
|
* 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)
|
* [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
|
* 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
|
* To test and verify SVG validity
|
||||||
* [Marak/colors.js](https://github.com/Marak/colors.js)
|
* [Marak/colors.js](https://github.com/Marak/colors.js)
|
||||||
* To print colors in console
|
* To print colors in console
|
||||||
* [babel/minify](https://github.com/babel/minify)
|
* [facebook/jest](https://github.com/facebook/jest) and [nodeca/js-yaml](https://github.com/nodeca/js-yaml)
|
||||||
* To minify code
|
* For unit testing
|
||||||
16
Dockerfile
16
Dockerfile
@@ -1,11 +1,11 @@
|
|||||||
# Base image
|
# Base image
|
||||||
FROM node:15-buster-slim
|
FROM node:15-buster-slim
|
||||||
|
|
||||||
# Copy GitHub action
|
# Copy repository
|
||||||
COPY action/dist/index.js /index.js
|
COPY . /metrics
|
||||||
|
|
||||||
# Setup
|
# 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
|
# 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
|
# Based on https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
@@ -21,9 +21,15 @@ RUN chmod +x /index.js \
|
|||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install -y ruby-full \
|
&& 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 \
|
&& 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_SKIP_CHROMIUM_DOWNLOAD true
|
||||||
ENV PUPPETEER_BROWSER_PATH "google-chrome-stable"
|
ENV PUPPETEER_BROWSER_PATH "google-chrome-stable"
|
||||||
|
|
||||||
# Execute GitHub action
|
# 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
|
description: Die on plugins errors
|
||||||
default: no
|
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
|
# Enable debug mode
|
||||||
# Ensure you correctly put all sensitive informations in your repository secrets before !
|
# Ensure you correctly put all sensitive informations in your repository secrets before !
|
||||||
debug:
|
debug:
|
||||||
description: Enable debug logs
|
description: Enable debug logs
|
||||||
default: no
|
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 (used for testing)
|
||||||
debug_flags:
|
debug_flags:
|
||||||
description: Debug flags
|
description: Debug flags
|
||||||
@@ -346,3 +346,9 @@ inputs:
|
|||||||
dryrun:
|
dryrun:
|
||||||
description: Enable dry-run
|
description: Enable dry-run
|
||||||
default: no
|
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
|
//Imports
|
||||||
import * as _setup from "./../src/setup.mjs"
|
import setup from "./../src/setup.mjs"
|
||||||
import * as _metrics from "./../src/metrics.mjs"
|
import metrics from "./../src/metrics.mjs"
|
||||||
import * as _octokit from "@octokit/graphql"
|
import octokit from "@octokit/graphql"
|
||||||
import * as _core from "@actions/core"
|
import core from "@actions/core"
|
||||||
import * as _github from "@actions/github"
|
import github from "@actions/github"
|
||||||
|
import mocks from "./../src/mocks.mjs"
|
||||||
|
|
||||||
;((async function () {
|
;((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
|
//Yaml boolean converter
|
||||||
const bool = (value, defaulted = false) => typeof value === "string" ? /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(value) : defaulted
|
const bool = (value, defaulted = false) => typeof value === "string" ? /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(value) : defaulted
|
||||||
//Debug message buffer
|
//Debug message buffer
|
||||||
@@ -16,12 +15,11 @@
|
|||||||
try {
|
try {
|
||||||
//Initialization
|
//Initialization
|
||||||
console.log(`GitHub metrics`)
|
console.log(`GitHub metrics`)
|
||||||
console.log(`========================================================`)
|
console.log("─".repeat(64))
|
||||||
console.log(`Version | <#version>`)
|
|
||||||
process.on("unhandledRejection", error => { throw error })
|
process.on("unhandledRejection", error => { throw error })
|
||||||
|
|
||||||
//Skip process if needed
|
//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)) {
|
if (/\[Skip GitHub Action\]/.test(github.context.payload.head_commit.message)) {
|
||||||
console.log(`Skipped because [Skip GitHub Action] is in commit message`)
|
console.log(`Skipped because [Skip GitHub Action] is in commit message`)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
@@ -30,30 +28,47 @@
|
|||||||
|
|
||||||
//Load configuration
|
//Load configuration
|
||||||
const conf = await setup({log:false})
|
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
|
//Load svg template, style, fonts and query
|
||||||
const template = core.getInput("template") || "classic"
|
const template = core.getInput("template") || "classic"
|
||||||
console.log(`Template to use | ${template}`)
|
console.log(`Template to use │ ${template}`)
|
||||||
|
|
||||||
//Token for data gathering
|
//Token for data gathering
|
||||||
const token = core.getInput("token") || ""
|
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)
|
if (!token)
|
||||||
throw new Error("You must provide a valid GitHub token to gather your metrics")
|
throw new Error("You must provide a valid GitHub token to gather your metrics")
|
||||||
const graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
const api = {}
|
||||||
console.log(`Github GraphQL API | ok`)
|
api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||||
const rest = github.getOctokit(token)
|
console.log(`Github GraphQL API │ ok`)
|
||||||
console.log(`Github REST 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
|
//SVG output
|
||||||
const filename = core.getInput("filename") || "github-metrics.svg"
|
const filename = core.getInput("filename") || "github-metrics.svg"
|
||||||
console.log(`SVG output file | ${filename}`)
|
console.log(`SVG output file │ ${filename}`)
|
||||||
|
|
||||||
//SVG optimization
|
//SVG optimization
|
||||||
const optimize = bool(core.getInput("optimize"), true)
|
const optimize = bool(core.getInput("optimize"), true)
|
||||||
conf.optimize = optimize
|
conf.optimize = optimize
|
||||||
console.log(`SVG optimization | ${optimize}`)
|
console.log(`SVG optimization │ ${optimize}`)
|
||||||
|
|
||||||
//GitHub user
|
//GitHub user
|
||||||
let authenticated
|
let authenticated
|
||||||
@@ -64,28 +79,20 @@
|
|||||||
authenticated = github.context.repo.owner
|
authenticated = github.context.repo.owner
|
||||||
}
|
}
|
||||||
const user = core.getInput("user") || authenticated
|
const user = core.getInput("user") || authenticated
|
||||||
console.log(`GitHub user | ${user}`)
|
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)"}`)
|
|
||||||
|
|
||||||
//Base elements
|
//Base elements
|
||||||
const base = {}
|
const base = {}
|
||||||
let parts = (core.getInput("base") || "").split(",").map(part => part.trim())
|
let parts = (core.getInput("base") || "").split(",").map(part => part.trim())
|
||||||
for (const part of conf.settings.plugins.base.parts)
|
for (const part of conf.settings.plugins.base.parts)
|
||||||
base[`base.${part}`] = parts.includes(part)
|
base[`base.${part}`] = parts.includes(part)
|
||||||
console.log(`Base parts | ${parts.join(", ") || "(none)"}`)
|
console.log(`Base parts │ ${parts.join(", ") || "(none)"}`)
|
||||||
|
|
||||||
//Config
|
//Config
|
||||||
const config = {
|
const config = {
|
||||||
"config.timezone":core.getInput("config_timezone") || ""
|
"config.timezone":core.getInput("config_timezone") || ""
|
||||||
}
|
}
|
||||||
console.log(`Timezone | ${config["config.timezone"] || "(system default)"}`)
|
console.log(`Timezone │ ${config["config.timezone"] || "(system default)"}`)
|
||||||
|
|
||||||
//Additional plugins
|
//Additional plugins
|
||||||
const plugins = {
|
const plugins = {
|
||||||
@@ -104,23 +111,23 @@
|
|||||||
tweets:{enabled:bool(core.getInput("plugin_tweets"))},
|
tweets:{enabled:bool(core.getInput("plugin_tweets"))},
|
||||||
}
|
}
|
||||||
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
|
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
|
//Additional plugins options
|
||||||
//Pagespeed
|
//Pagespeed
|
||||||
if (plugins.pagespeed.enabled) {
|
if (plugins.pagespeed.enabled) {
|
||||||
plugins.pagespeed.token = core.getInput("plugin_pagespeed_token") || ""
|
plugins.pagespeed.token = core.getInput("plugin_pagespeed_token") || ""
|
||||||
q[`pagespeed.detailed`] = bool(core.getInput(`plugin_pagespeed_detailed`))
|
q[`pagespeed.detailed`] = bool(core.getInput(`plugin_pagespeed_detailed`))
|
||||||
q[`pagespeed.screenshot`] = bool(core.getInput(`plugin_pagespeed_screenshot`))
|
q[`pagespeed.screenshot`] = bool(core.getInput(`plugin_pagespeed_screenshot`))
|
||||||
console.log(`Pagespeed token | ${plugins.pagespeed.token ? "provided" : "missing"}`)
|
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 detailed │ ${q["pagespeed.detailed"]}`)
|
||||||
console.log(`Pagespeed screenshot | ${q["pagespeed.screenshot"]}`)
|
console.log(`Pagespeed screenshot │ ${q["pagespeed.screenshot"]}`)
|
||||||
}
|
}
|
||||||
//Languages
|
//Languages
|
||||||
if (plugins.languages.enabled) {
|
if (plugins.languages.enabled) {
|
||||||
for (const option of ["ignored", "skipped"])
|
for (const option of ["ignored", "skipped"])
|
||||||
q[`languages.${option}`] = core.getInput(`plugin_languages_${option}`) || null
|
q[`languages.${option}`] = core.getInput(`plugin_languages_${option}`) || null
|
||||||
console.log(`Languages ignored | ${q["languages.ignored"] || "(none)"}`)
|
console.log(`Languages ignored │ ${q["languages.ignored"] || "(none)"}`)
|
||||||
console.log(`Languages skipped repos | ${q["languages.skipped"] || "(none)"}`)
|
console.log(`Languages skipped repos │ ${q["languages.skipped"] || "(none)"}`)
|
||||||
}
|
}
|
||||||
//Habits
|
//Habits
|
||||||
if (plugins.habits.enabled) {
|
if (plugins.habits.enabled) {
|
||||||
@@ -128,107 +135,107 @@
|
|||||||
q[`habits.${option}`] = core.getInput(`plugin_habits_${option}`) || null
|
q[`habits.${option}`] = core.getInput(`plugin_habits_${option}`) || null
|
||||||
q[`habits.facts`] = bool(core.getInput(`plugin_habits_facts`))
|
q[`habits.facts`] = bool(core.getInput(`plugin_habits_facts`))
|
||||||
q[`habits.charts`] = bool(core.getInput(`plugin_habits_charts`))
|
q[`habits.charts`] = bool(core.getInput(`plugin_habits_charts`))
|
||||||
console.log(`Habits facts | ${q["habits.facts"]}`)
|
console.log(`Habits facts │ ${q["habits.facts"]}`)
|
||||||
console.log(`Habits charts | ${q["habits.charts"]}`)
|
console.log(`Habits charts │ ${q["habits.charts"]}`)
|
||||||
console.log(`Habits events to use | ${q["habits.from"] || "(default)"}`)
|
console.log(`Habits events to use │ ${q["habits.from"] || "(default)"}`)
|
||||||
console.log(`Habits days to keep | ${q["habits.days"] || "(default)"}`)
|
console.log(`Habits days to keep │ ${q["habits.days"] || "(default)"}`)
|
||||||
}
|
}
|
||||||
//Music
|
//Music
|
||||||
if (plugins.music.enabled) {
|
if (plugins.music.enabled) {
|
||||||
plugins.music.token = core.getInput("plugin_music_token") || ""
|
plugins.music.token = core.getInput("plugin_music_token") || ""
|
||||||
for (const option of ["provider", "mode", "playlist", "limit"])
|
for (const option of ["provider", "mode", "playlist", "limit"])
|
||||||
q[`music.${option}`] = core.getInput(`plugin_music_${option}`) || null
|
q[`music.${option}`] = core.getInput(`plugin_music_${option}`) || null
|
||||||
console.log(`Music provider | ${q["music.provider"] || "(none)"}`)
|
console.log(`Music provider │ ${q["music.provider"] || "(none)"}`)
|
||||||
console.log(`Music plugin mode | ${q["music.mode"] || "(none)"}`)
|
console.log(`Music plugin mode │ ${q["music.mode"] || "(none)"}`)
|
||||||
console.log(`Music playlist | ${q["music.playlist"] || "(none)"}`)
|
console.log(`Music playlist │ ${q["music.playlist"] || "(none)"}`)
|
||||||
console.log(`Music tracks limit | ${q["music.limit"] || "(default)"}`)
|
console.log(`Music tracks limit │ ${q["music.limit"] || "(default)"}`)
|
||||||
console.log(`Music token | ${plugins.music.token ? "provided" : "missing"}`)
|
console.log(`Music token │ ${/^MOCKED/.test(plugins.music.token) ? "(MOCKED)" : plugins.music.token ? "provided" : "missing"}`)
|
||||||
}
|
}
|
||||||
//Posts
|
//Posts
|
||||||
if (plugins.posts.enabled) {
|
if (plugins.posts.enabled) {
|
||||||
for (const option of ["source", "limit"])
|
for (const option of ["source", "limit"])
|
||||||
q[`posts.${option}`] = core.getInput(`plugin_posts_${option}`) || null
|
q[`posts.${option}`] = core.getInput(`plugin_posts_${option}`) || null
|
||||||
console.log(`Posts source | ${q["posts.source"] || "(none)"}`)
|
console.log(`Posts source │ ${q["posts.source"] || "(none)"}`)
|
||||||
console.log(`Posts limit | ${q["posts.limit"] || "(default)"}`)
|
console.log(`Posts limit │ ${q["posts.limit"] || "(default)"}`)
|
||||||
}
|
}
|
||||||
//Isocalendar
|
//Isocalendar
|
||||||
if (plugins.isocalendar.enabled) {
|
if (plugins.isocalendar.enabled) {
|
||||||
q["isocalendar.duration"] = core.getInput("plugin_isocalendar_duration") || "half-year"
|
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
|
//Topics
|
||||||
if (plugins.topics.enabled) {
|
if (plugins.topics.enabled) {
|
||||||
for (const option of ["mode", "sort", "limit"])
|
for (const option of ["mode", "sort", "limit"])
|
||||||
q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null
|
q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null
|
||||||
console.log(`Topics mode | ${q["topics.mode"] || "(default)"}`)
|
console.log(`Topics mode │ ${q["topics.mode"] || "(default)"}`)
|
||||||
console.log(`Topics sort mode | ${q["topics.sort"] || "(default)"}`)
|
console.log(`Topics sort mode │ ${q["topics.sort"] || "(default)"}`)
|
||||||
console.log(`Topics limit | ${q["topics.limit"] || "(default)"}`)
|
console.log(`Topics limit │ ${q["topics.limit"] || "(default)"}`)
|
||||||
}
|
}
|
||||||
//Projects
|
//Projects
|
||||||
if (plugins.projects.enabled) {
|
if (plugins.projects.enabled) {
|
||||||
for (const option of ["limit", "repositories"])
|
for (const option of ["limit", "repositories"])
|
||||||
q[`projects.${option}`] = core.getInput(`plugin_projects_${option}`) || null
|
q[`projects.${option}`] = core.getInput(`plugin_projects_${option}`) || null
|
||||||
console.log(`Projects limit | ${q["projects.limit"] || "(default)"}`)
|
console.log(`Projects limit │ ${q["projects.limit"] || "(default)"}`)
|
||||||
console.log(`Projects repositories | ${q["projects.repositories"] || "(none)"}`)
|
console.log(`Projects repositories │ ${q["projects.repositories"] || "(none)"}`)
|
||||||
}
|
}
|
||||||
//Tweets
|
//Tweets
|
||||||
if (plugins.tweets.enabled) {
|
if (plugins.tweets.enabled) {
|
||||||
plugins.tweets.token = core.getInput("plugin_tweets_token") || null
|
plugins.tweets.token = core.getInput("plugin_tweets_token") || null
|
||||||
for (const option of ["limit"])
|
for (const option of ["limit"])
|
||||||
q[`tweets.${option}`] = core.getInput(`plugin_tweets_${option}`) || null
|
q[`tweets.${option}`] = core.getInput(`plugin_tweets_${option}`) || null
|
||||||
console.log(`Twitter token | ${plugins.tweets.token ? "provided" : "missing"}`)
|
console.log(`Twitter token │ ${/^MOCKED/.test(plugins.tweets.token) ? "(MOCKED)" : plugins.tweets.token ? "provided" : "missing"}`)
|
||||||
console.log(`Tweets limit | ${q["tweets.limit"] || "(default)"}`)
|
console.log(`Tweets limit │ ${q["tweets.limit"] || "(default)"}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Repositories to use
|
//Repositories to use
|
||||||
const repositories = Number(core.getInput("repositories")) || 100
|
const repositories = Number(core.getInput("repositories")) || 100
|
||||||
console.log(`Repositories to use | ${repositories}`)
|
console.log(`Repositories to use │ ${repositories}`)
|
||||||
|
|
||||||
//Die on plugins errors
|
//Die on plugins errors
|
||||||
const die = bool(core.getInput("plugins_errors_fatal"))
|
const die = bool(core.getInput("plugins_errors_fatal"))
|
||||||
console.log(`Plugin errors | ${die ? "die" : "warn"}`)
|
console.log(`Plugin errors │ ${die ? "die" : "warn"}`)
|
||||||
|
|
||||||
//Build query
|
//Build query
|
||||||
const query = JSON.parse(core.getInput("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}
|
q = {...query, ...q, base:false, ...base, ...config, repositories, template}
|
||||||
|
|
||||||
//Render metrics
|
//Render metrics
|
||||||
const rendered = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die})
|
const rendered = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die})
|
||||||
console.log(`Render | complete`)
|
console.log(`Render │ complete`)
|
||||||
|
|
||||||
//Verify svg
|
//Verify svg
|
||||||
const verify = bool(core.getInput("verify"))
|
const verify = bool(core.getInput("verify"))
|
||||||
console.log(`Verify SVG | ${verify}`)
|
console.log(`Verify SVG │ ${verify}`)
|
||||||
if (verify) {
|
if (verify) {
|
||||||
const [libxmljs] = [await import("libxmljs")].map(m => (m && m.default) ? m.default : m)
|
const [libxmljs] = [await import("libxmljs")].map(m => (m && m.default) ? m.default : m)
|
||||||
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")}`)
|
||||||
console.log(`SVG valid | yes`)
|
console.log(`SVG valid │ yes`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Commit to repository
|
//Commit to repository
|
||||||
const dryrun = bool(core.getInput("dryrun"))
|
const dryrun = bool(core.getInput("dryrun"))
|
||||||
if (dryrun)
|
if (dryrun)
|
||||||
console.log(`Dry-run | complete`)
|
console.log(`Dry-run │ complete`)
|
||||||
else {
|
else {
|
||||||
//Repository and branch
|
//Repository and branch
|
||||||
const branch = github.context.ref.replace(/^refs[/]heads[/]/, "")
|
const branch = github.context.ref.replace(/^refs[/]heads[/]/, "")
|
||||||
console.log(`Repository | ${github.context.repo.owner}/${github.context.repo.repo}`)
|
console.log(`Repository │ ${github.context.repo.owner}/${github.context.repo.repo}`)
|
||||||
console.log(`Branch | ${branch}`)
|
console.log(`Branch │ ${branch}`)
|
||||||
//Committer token
|
//Committer token
|
||||||
const token = core.getInput("committer_token") || core.getInput("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)
|
if (!token)
|
||||||
throw new Error("You must provide a valid GitHub token to commit your metrics")
|
throw new Error("You must provide a valid GitHub token to commit your metrics")
|
||||||
const rest = github.getOctokit(token)
|
const rest = github.getOctokit(token)
|
||||||
console.log(`Committer REST API | ok`)
|
console.log(`Committer REST API │ ok`)
|
||||||
try {
|
try {
|
||||||
console.log(`Committer | ${(await rest.users.getAuthenticated()).data.login}`)
|
console.log(`Committer │ ${(await rest.users.getAuthenticated()).data.login}`)
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
console.log(`Committer | (github-actions)`)
|
console.log(`Committer │ (github-actions)`)
|
||||||
}
|
}
|
||||||
//Retrieve previous render SHA to be able to update file content through API
|
//Retrieve previous render SHA to be able to update file content through API
|
||||||
let sha = null
|
let sha = null
|
||||||
@@ -243,14 +250,14 @@
|
|||||||
)
|
)
|
||||||
sha = oid
|
sha = oid
|
||||||
} catch (error) { console.debug(error) }
|
} catch (error) { console.debug(error) }
|
||||||
console.log(`Previous render sha | ${sha ?? "(none)"}`)
|
console.log(`Previous render sha │ ${sha ?? "(none)"}`)
|
||||||
//Update file content through API
|
//Update file content through API
|
||||||
await rest.repos.createOrUpdateFileContents({
|
await rest.repos.createOrUpdateFileContents({
|
||||||
...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`,
|
...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`,
|
||||||
content:Buffer.from(rendered).toString("base64"),
|
content:Buffer.from(rendered).toString("base64"),
|
||||||
...(sha ? {sha} : {})
|
...(sha ? {sha} : {})
|
||||||
})
|
})
|
||||||
console.log(`Commit to repo | ok`)
|
console.log(`Commit to repo │ ok`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Success
|
//Success
|
||||||
@@ -262,7 +269,7 @@
|
|||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
if (!bool(core.getInput("debug")))
|
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)
|
console.log(log)
|
||||||
core.setFailed(error.message)
|
core.setFailed(error.message)
|
||||||
process.exit(1)
|
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": {
|
"scripts": {
|
||||||
"start": "node index.mjs",
|
"start": "node index.mjs",
|
||||||
"build": "node utils/build.mjs",
|
"build": "node utils/build.mjs",
|
||||||
"test": "node tests/metrics.mjs",
|
"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 @vercel/ncc@latest babel-minify@latest libxmljs@latest"
|
"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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
"vue-prism-component": "^1.2.0"
|
"vue-prism-component": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vercel/ncc": "^0.26.1",
|
"jest": "^26.6.3",
|
||||||
"babel-minify": "^0.5.1",
|
"js-yaml": "^3.14.1",
|
||||||
"libxmljs": "^0.19.7"
|
"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"}},
|
{headers:{"Content-Type":"application/x-www-form-urlencoded"}},
|
||||||
)
|
)
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > got access token`)
|
console.debug(`metrics/compute/${login}/plugins > music > got access token`)
|
||||||
//Retriev tracks
|
//Retrieve tracks
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > querying spotify api`)
|
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",
|
"Accept":"application/json",
|
||||||
"Content-Type":"application/json",
|
"Content-Type":"application/json",
|
||||||
"Authorization":`Bearer ${access}`}
|
"Authorization":`Bearer ${access}`}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
}
|
}
|
||||||
//Handle errors
|
//Handle errors
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
if (error.error?.message)
|
||||||
|
throw error
|
||||||
throw {error:{message:"An error occured", instance:error}}
|
throw {error:{message:"An error occured", instance:error}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,6 @@ query Metrics {
|
|||||||
websiteUrl
|
websiteUrl
|
||||||
isHireable
|
isHireable
|
||||||
twitterUsername
|
twitterUsername
|
||||||
gists {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
repositories(last: 0, isFork: false, ownerAffiliations: OWNER) {
|
repositories(last: 0, isFork: false, ownerAffiliations: OWNER) {
|
||||||
totalCount
|
totalCount
|
||||||
totalDiskUsage
|
totalDiskUsage
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
query Projects {
|
query RepositoryProject {
|
||||||
user(login: "$user") {
|
user(login: "$user") {
|
||||||
repository(name: "$repository") {
|
repository(name: "$repository") {
|
||||||
project(number: $id) {
|
project(number: $id) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
query Metrics {
|
query Repositories {
|
||||||
user(login: "$login") {
|
user(login: "$login") {
|
||||||
repositories($after first: $repositories, isFork: false, ownerAffiliations: OWNER, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
repositories($after first: $repositories, isFork: false, ownerAffiliations: OWNER, orderBy: {field: UPDATED_AT, direction: DESC}) {
|
||||||
edges {
|
edges {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
query Metrics {
|
query Repository {
|
||||||
user(login: "$login") {
|
user(login: "$login") {
|
||||||
repository(name: "$repo") {
|
repository(name: "$repo") {
|
||||||
name
|
name
|
||||||
|
|||||||
105
src/setup.mjs
105
src/setup.mjs
@@ -37,73 +37,56 @@
|
|||||||
|
|
||||||
//Load package settings
|
//Load package settings
|
||||||
logger(`metrics/setup > load package.json`)
|
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"))}`)
|
||||||
conf.package = JSON.parse(`${await fs.promises.readFile(path.resolve("package.json"))}`)
|
logger(`metrics/setup > load package.json > success`)
|
||||||
logger(`metrics/setup > load package.json > success`)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger(`metrics/setup > load package.json > (missing)`)
|
|
||||||
conf.package = {version:"<#version>", author:"lowlighter"}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Load templates
|
//Load templates
|
||||||
if (fs.existsSync(path.resolve(templates))) {
|
for (const name of await fs.promises.readdir(templates)) {
|
||||||
for (const name of await fs.promises.readdir(templates)) {
|
//Cache templates
|
||||||
//Cache templates
|
if (/.*[.]mjs$/.test(name))
|
||||||
if (/.*[.]mjs$/.test(name))
|
continue
|
||||||
continue
|
logger(`metrics/setup > load template [${name}]`)
|
||||||
logger(`metrics/setup > load template [${name}]`)
|
const files = [
|
||||||
const files = [
|
`${templates}/${name}/image.svg`,
|
||||||
`${templates}/${name}/image.svg`,
|
`${templates}/${name}/style.css`,
|
||||||
`${templates}/${name}/style.css`,
|
`${templates}/${name}/fonts.css`,
|
||||||
`${templates}/${name}/fonts.css`,
|
].map(file => fs.existsSync(path.resolve(file)) ? file : file.replace(`${templates}/${name}/`, `${templates}/classic/`)).map(file => path.resolve(file))
|
||||||
].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)}`))
|
||||||
const [image, style, fonts] = await Promise.all(files.map(async file => `${await fs.promises.readFile(file)}`))
|
conf.templates[name] = {image, style, fonts}
|
||||||
conf.templates[name] = {image, style, fonts}
|
logger(`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() {
|
logger(`metrics/setup > reload template [${name}]`)
|
||||||
logger(`metrics/setup > reload template [${name}]`)
|
const [image, style, fonts] = files.map(file => `${fs.readFileSync(file)}`)
|
||||||
const [image, style, fonts] = files.map(file => `${fs.readFileSync(file)}`)
|
logger(`metrics/setup > reload template [${name}] > success`)
|
||||||
logger(`metrics/setup > reload template [${name}] > success`)
|
return {image, style, fonts}
|
||||||
return {image, style, fonts}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger(`metrics/setup > load templates from build`)
|
|
||||||
conf.templates = JSON.parse(Buffer.from(`<#assets>`, "base64").toString("utf8"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load queries
|
//Load queries
|
||||||
if (fs.existsSync(path.resolve(queries))) {
|
for (const query of await fs.promises.readdir(queries)) {
|
||||||
for (const query of await fs.promises.readdir(queries)) {
|
//Cache queries
|
||||||
//Cache queries
|
const name = query.replace(/[.]graphql$/, "")
|
||||||
const name = query.replace(/[.]graphql$/, "")
|
logger(`metrics/setup > load query [${name}]`)
|
||||||
logger(`metrics/setup > load query [${name}]`)
|
conf.queries[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${queries}/${query}`))}`
|
||||||
conf.queries[`_${name}`] = `${await fs.promises.readFile(path.resolve(`${queries}/${query}`))}`
|
logger(`metrics/setup > load query [${name}] > success`)
|
||||||
logger(`metrics/setup > load query [${name}] > success`)
|
//Debug
|
||||||
//Debug
|
if (conf.settings.debug) {
|
||||||
if (conf.settings.debug) {
|
Object.defineProperty(conf.queries, `_${name}`, {
|
||||||
Object.defineProperty(conf.queries, `_${name}`, {
|
get() {
|
||||||
get() {
|
logger(`metrics/setup > reload query [${name}]`)
|
||||||
logger(`metrics/setup > reload query [${name}]`)
|
const raw = `${fs.readFileSync(path.resolve(`${queries}/${query}`))}`
|
||||||
const raw = `${fs.readFileSync(path.resolve(`${queries}/${query}`))}`
|
logger(`metrics/setup > reload query [${name}] > success`)
|
||||||
logger(`metrics/setup > reload query [${name}] > success`)
|
return raw
|
||||||
return raw
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger(`metrics/setup > load queries from build`)
|
|
||||||
conf.queries = JSON.parse(Buffer.from(`<#queries>`, "base64").toString("utf8"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create queries formatters
|
//Create queries formatters
|
||||||
Object.keys(conf.queries).map(name => conf.queries[name.substring(1)] = (vars = {}) => {
|
Object.keys(conf.queries).map(name => conf.queries[name.substring(1)] = (vars = {}) => {
|
||||||
let query = conf.queries[name]
|
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 fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import url from "url"
|
import url from "url"
|
||||||
import ncc from "@vercel/ncc"
|
|
||||||
import minify from "babel-minify"
|
|
||||||
import colors from "colors"
|
import colors from "colors"
|
||||||
import ejs from "ejs"
|
|
||||||
|
|
||||||
//Initialization
|
//Initialization
|
||||||
const __dirname = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "..")
|
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 __src = path.join(__dirname, "src")
|
||||||
const __plugins = path.join(__src, "plugins")
|
const __plugins = path.join(__src, "plugins")
|
||||||
const __templates = path.join(__src, "templates")
|
const __templates = path.join(__src, "templates")
|
||||||
const __queries = path.join(__src, "queries")
|
|
||||||
process.on("unhandledRejection", error => { throw error })
|
process.on("unhandledRejection", error => { throw error })
|
||||||
colors.enable()
|
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
|
//Throw on errors
|
||||||
if (errors.length)
|
if (errors.length)
|
||||||
throw new Error(`${errors.length} errors occured :\n${errors.map(error => ` - ${error}`).join("\n")}`)
|
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