The great refactor (#82)
This commit is contained in:
103
source/app/action/action.yml
Normal file
103
source/app/action/action.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
# ====================================================================================
|
||||
# Inputs and configuration
|
||||
|
||||
inputs:
|
||||
<% for (const [plugin, {name, action}] of Object.entries(plugins)) { %>
|
||||
# ====================================================================================
|
||||
# <%- name %>
|
||||
<% for (const [input, {comment, descriptor}] of Object.entries(action)) { %>
|
||||
<%- comment.split("\n").map((line, i) => `${i ? " " : ""}${line}`).join("\n").trim() %>
|
||||
<%- descriptor.split("\n").map((line, i) => `${i ? " " : ""}${line}`).join("\n") -%>
|
||||
<% }} %>
|
||||
|
||||
# ====================================================================================
|
||||
# Action metadata
|
||||
name: GitHub metrics as SVG image
|
||||
author: lowlighter
|
||||
description: An SVG generator with 20+ metrics about your GitHub account! Additional plugins are available to display even more!
|
||||
branding:
|
||||
icon: user-check
|
||||
color: gray-dark
|
||||
|
||||
# The action will parse its name to check if it's the official action or if it's a forked one
|
||||
# On the official action, it'll use the docker image published on GitHub registry when using a released version, allowing faster runs
|
||||
# On a forked action, it'll rebuild the docker image from Dockerfile to take into account changes you made
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- run: |
|
||||
# Create environment file from inputs and GitHub variables
|
||||
cd $METRICS_ACTION_PATH
|
||||
touch .env
|
||||
for INPUT in $(echo $INPUTS | jq -r 'to_entries|map("INPUT_\(.key|ascii_upcase)=\(.value|@uri)")|.[]'); do
|
||||
echo $INPUT >> .env
|
||||
done
|
||||
env | grep -E '^(GITHUB|ACTIONS|CI)' >> .env
|
||||
echo "Environment variable: loaded"
|
||||
|
||||
# Source repository (picked from action name)
|
||||
METRICS_SOURCE=$(echo $METRICS_ACTION | sed -E 's/metrics.*?$//g')
|
||||
echo "Source: $METRICS_SOURCE"
|
||||
|
||||
# Version (picked from package.json)
|
||||
METRICS_VERSION=$(grep -Po '(?<="version": ").*(?=")' package.json)
|
||||
echo "Version: $METRICS_VERSION"
|
||||
|
||||
# Image tag (extracted from version or from env)
|
||||
METRICS_TAG=v$(echo $METRICS_VERSION | sed -r 's/^([0-9]+[.][0-9]+).*/\1/')
|
||||
if [[ $METRICS_USE_PREBUILT_IMAGE ]]; then
|
||||
METRICS_TAG=$METRICS_USE_PREBUILT_IMAGE
|
||||
echo "Pre-built image: yes"
|
||||
fi
|
||||
echo "Image tag: $METRICS_TAG"
|
||||
|
||||
# Image name
|
||||
# Pre-built image
|
||||
if [[ $METRICS_USE_PREBUILT_IMAGE ]]; then
|
||||
echo "Using pre-built version $METRICS_TAG, will pull docker image from GitHub registry"
|
||||
METRICS_IMAGE=ghcr.io/lowlighter/metrics:$METRICS_TAG
|
||||
docker image pull $METRICS_IMAGE > /dev/null
|
||||
# Official action
|
||||
elif [[ $METRICS_SOURCE == "lowlighter" ]]; then
|
||||
# Is unreleased version
|
||||
set +e
|
||||
METRICS_IS_RELEASED=$(expr $(expr match $METRICS_VERSION .*-beta) == 0)
|
||||
set -e
|
||||
echo "Is released version: $METRICS_IS_RELEASED"
|
||||
# Rebuild image for unreleased version
|
||||
if [[ "$METRICS_IS_RELEASED" -gt "0" ]]; then
|
||||
echo "Using released version $METRICS_TAG, will pull docker image from GitHub registry"
|
||||
METRICS_IMAGE=ghcr.io/lowlighter/metrics:$METRICS_TAG
|
||||
# Use registry for released version
|
||||
else
|
||||
echo "Using an unreleased version ($METRICS_VERSION)"
|
||||
METRICS_IMAGE=metrics:$METRICS_VERSION
|
||||
fi
|
||||
# Forked action
|
||||
else
|
||||
echo "Using a forked version"
|
||||
METRICS_IMAGE=metrics:forked-$METRICS_VERSION
|
||||
fi
|
||||
echo "Image name: $METRICS_IMAGE"
|
||||
|
||||
# Build image if necessary
|
||||
set +e
|
||||
docker image inspect $METRICS_IMAGE > /dev/null
|
||||
METRICS_IMAGE_NEEDS_BUILD="$?"
|
||||
set -e
|
||||
if [[ "$METRICS_IMAGE_NEEDS_BUILD" -gt "0" ]]; then
|
||||
echo "Image $METRICS_IMAGE is not present locally, rebuilding it from Dockerfile"
|
||||
docker build -t $METRICS_IMAGE . > /dev/null
|
||||
else
|
||||
echo "Image $METRICS_IMAGE is present locally"
|
||||
fi
|
||||
|
||||
# Run docker image with current environment
|
||||
docker run --volume $GITHUB_EVENT_PATH:$GITHUB_EVENT_PATH --env-file .env $METRICS_IMAGE
|
||||
rm .env
|
||||
shell: bash
|
||||
env:
|
||||
METRICS_ACTION: ${{ github.action }}
|
||||
METRICS_ACTION_PATH: ${{ github.action_path }}
|
||||
METRICS_USE_PREBUILT_IMAGE: ${{ inputs.use_prebuilt_image }}
|
||||
INPUTS: ${{ toJson(inputs) }}
|
||||
@@ -2,336 +2,225 @@
|
||||
import core from "@actions/core"
|
||||
import github from "@actions/github"
|
||||
import octokit from "@octokit/graphql"
|
||||
import setup from "../setup.mjs"
|
||||
import mocks from "../mocks.mjs"
|
||||
import metrics from "../metrics.mjs"
|
||||
import setup from "../metrics/setup.mjs"
|
||||
import mocks from "../mocks/index.mjs"
|
||||
import metrics from "../metrics/index.mjs"
|
||||
|
||||
;((async function () {
|
||||
//Input parser
|
||||
const input = {
|
||||
get:(name) => {
|
||||
const value = `${core.getInput(name)}`.trim()
|
||||
try { return decodeURIComponent(value) }
|
||||
catch { return value}
|
||||
},
|
||||
bool:(name, {default:defaulted = undefined} = {}) => /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(input.get(name)) ? true : /^(?:[Ff]alse|[Oo]ff|[Nn]o)$/.test(input.get(name)) ? false : defaulted,
|
||||
number:(name, {default:defaulted = undefined} = {}) => Number.isFinite(Number(input.get(name))) ? Number(input.get(name)) : defaulted,
|
||||
string:(name, {default:defaulted = undefined} = {}) => input.get(name) || defaulted,
|
||||
array:(name, {separator = ","} = {}) => input.get(name).split(separator).map(value => value.trim()).filter(value => value),
|
||||
object:(name) => JSON.parse(input.get(name) || "{}"),
|
||||
}
|
||||
//Info logger
|
||||
const info = (left, right, {token = false} = {}) => console.log(`${`${left}`.padEnd(48)} │ ${
|
||||
Array.isArray(right) ? right.join(", ") || "(none)" :
|
||||
right === undefined ? "(default)" :
|
||||
token ? /^MOCKED/.test(right) ? "(MOCKED TOKEN)" : (right ? "(provided)" : "(missing)") :
|
||||
typeof right === "object" ? JSON.stringify(right) :
|
||||
right
|
||||
}`)
|
||||
//Debug message buffer
|
||||
const debugged = []
|
||||
//Runner
|
||||
try {
|
||||
//Initialization
|
||||
console.log("─".repeat(64))
|
||||
console.log(`Metrics`)
|
||||
console.log("─".repeat(64))
|
||||
process.on("unhandledRejection", error => { throw error })
|
||||
//Debug message buffer
|
||||
let DEBUG = true
|
||||
const debugged = []
|
||||
|
||||
//Skip process if needed
|
||||
if ((github.context.eventName === "push")&&(github.context.payload?.head_commit)) {
|
||||
if (/\[Skip GitHub Action\]/.test(github.context.payload.head_commit.message)) {
|
||||
console.log(`Skipped because [Skip GitHub Action] is in commit message`)
|
||||
process.exit(0)
|
||||
}
|
||||
//Info logger
|
||||
const info = (left, right, {token = false} = {}) => console.log(`${`${left}`.padEnd(56 + 9*(/0m$/.test(left)))} │ ${
|
||||
Array.isArray(right) ? right.join(", ") || "(none)" :
|
||||
right === undefined ? "(default)" :
|
||||
token ? /^MOCKED/.test(right) ? "(MOCKED TOKEN)" : (right ? "(provided)" : "(missing)") :
|
||||
typeof right === "object" ? JSON.stringify(right) :
|
||||
right
|
||||
}`)
|
||||
info.section = (left = "", right = " ") => info(`\x1b[36m${left}\x1b[0m`, right)
|
||||
info.group = ({metadata, name, inputs}) => {
|
||||
info.section(metadata.plugins[name]?.name?.match(/(?<section>[\w\s]+)/i)?.groups?.section?.trim(), " ")
|
||||
for (const [input, value] of Object.entries(inputs))
|
||||
info(metadata.plugins[name]?.inputs[input]?.description ?? input, value, {token:metadata.plugins[name]?.inputs[input]?.type === "token"})
|
||||
}
|
||||
info.break = () => console.log("─".repeat(88))
|
||||
|
||||
//Runner
|
||||
try {
|
||||
//Initialization
|
||||
info.break()
|
||||
info.section(`Metrics`)
|
||||
process.on("unhandledRejection", error => { throw error })
|
||||
|
||||
//Skip process if needed
|
||||
if ((github.context.eventName === "push")&&(github.context.payload?.head_commit)) {
|
||||
if (/\[Skip GitHub Action\]/.test(github.context.payload.head_commit.message)) {
|
||||
console.log(`Skipped because [Skip GitHub Action] is in commit message`)
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
//Pre-Setup
|
||||
const community = {
|
||||
templates:input.array("setup_community_templates")
|
||||
}
|
||||
info("Setup - community templates", community.templates)
|
||||
//Load configuration
|
||||
const {conf, Plugins, Templates} = await setup({log:false, nosettings:true, community:{templates:core.getInput("setup_community_templates")}})
|
||||
const {metadata} = conf
|
||||
info("Setup", "complete")
|
||||
info("Version", conf.package.version)
|
||||
|
||||
//Load configuration
|
||||
const {conf, Plugins, Templates} = await setup({log:false, nosettings:true, community})
|
||||
info("Setup", "complete")
|
||||
info("Version", conf.package.version)
|
||||
//Core inputs
|
||||
const {
|
||||
user:_user, token,
|
||||
template, query, "setup.community.templates":_templates,
|
||||
filename, optimize, verify,
|
||||
debug, "debug.flags":dflags, "use.mocked.data":mocked, dryrun,
|
||||
"plugins.errors.fatal":die,
|
||||
"committer.token":_token, "committer.branch":_branch,
|
||||
"use.prebuilt.image":_image,
|
||||
...config
|
||||
} = metadata.plugins.core.inputs.action({core})
|
||||
const q = {...query, template}
|
||||
|
||||
//Debug mode
|
||||
const debug = input.bool("debug", {default:false})
|
||||
info("Debug mode", debug)
|
||||
if (!debug)
|
||||
console.debug = message => debugged.push(message)
|
||||
const dflags = input.array("debug_flags", {separator:" "})
|
||||
info("Debug flags", dflags)
|
||||
//Docker image
|
||||
if (_image)
|
||||
info("Using prebuilt image", image)
|
||||
|
||||
//Load svg template, style, fonts and query
|
||||
const template = input.string("template", {default:"classic"})
|
||||
info("Template used", template)
|
||||
//Debug mode and flags
|
||||
info("Debug mode", debug)
|
||||
if (!debug) {
|
||||
console.debug = message => debugged.push(message)
|
||||
DEBUG = false
|
||||
}
|
||||
info("Debug flags", dflags)
|
||||
|
||||
//Token for data gathering
|
||||
const token = input.string("token")
|
||||
info("GitHub token", token, {token:true})
|
||||
if (!token)
|
||||
throw new Error("You must provide a valid GitHub token to gather your metrics")
|
||||
const api = {}
|
||||
api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||
info("Github GraphQL API", "ok")
|
||||
api.rest = github.getOctokit(token)
|
||||
info("Github REST API", "ok")
|
||||
//Apply mocking if needed
|
||||
if (input.bool("use_mocked_data", {default:false})) {
|
||||
Object.assign(api, await mocks(api))
|
||||
info("Use mocked API", true)
|
||||
}
|
||||
//Extract octokits
|
||||
const {graphql, rest} = api
|
||||
//Token for data gathering
|
||||
info("GitHub token", token, {token:true})
|
||||
if (!token)
|
||||
throw new Error("You must provide a valid GitHub token to gather your metrics")
|
||||
const api = {}
|
||||
api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
|
||||
info("Github GraphQL API", "ok")
|
||||
api.rest = github.getOctokit(token)
|
||||
info("Github REST API", "ok")
|
||||
//Apply mocking if needed
|
||||
if (mocked) {
|
||||
Object.assign(api, await mocks(api))
|
||||
info("Use mocked API", true)
|
||||
}
|
||||
//Extract octokits
|
||||
const {graphql, rest} = api
|
||||
|
||||
//SVG output
|
||||
const filename = input.string("filename", {default:"github-metrics.svg"})
|
||||
info("SVG output", filename)
|
||||
//GitHub user
|
||||
let authenticated
|
||||
try {
|
||||
authenticated = (await rest.users.getAuthenticated()).data.login
|
||||
}
|
||||
catch {
|
||||
authenticated = github.context.repo.owner
|
||||
}
|
||||
const user = _user || authenticated
|
||||
info("GitHub account", user)
|
||||
|
||||
//SVG optimization
|
||||
const optimize = input.bool("optimize", {default:true})
|
||||
conf.optimize = optimize
|
||||
info("SVG optimization", optimize)
|
||||
//Current repository
|
||||
info("Current repository", `${github.context.repo.owner}/${github.context.repo.repo}`)
|
||||
|
||||
//Verify svg
|
||||
const verify = input.bool("verify")
|
||||
info("SVG verification after generation", verify)
|
||||
|
||||
//GitHub user
|
||||
let authenticated
|
||||
try {
|
||||
authenticated = (await rest.users.getAuthenticated()).data.login
|
||||
}
|
||||
catch {
|
||||
authenticated = github.context.repo.owner
|
||||
}
|
||||
const user = input.string("user", {default:authenticated})
|
||||
info("Target GitHub user", user)
|
||||
|
||||
//Base elements
|
||||
const base = {}
|
||||
const parts = input.array("base")
|
||||
for (const part of conf.settings.plugins.base.parts)
|
||||
base[`base.${part}`] = parts.includes(part)
|
||||
info("Base parts", parts)
|
||||
|
||||
//Config
|
||||
const config = {
|
||||
"config.timezone":input.string("config_timezone"),
|
||||
"config.output":input.string("config_output"),
|
||||
"config.animations":input.bool("config_animations"),
|
||||
"config.padding":input.string("config_padding"),
|
||||
"config.order":input.array("config_order"),
|
||||
}
|
||||
info("Timezone", config["config.timezone"] ?? "(system default)")
|
||||
info("Convert SVG", config["config.output"] ?? "(no)")
|
||||
info("Enable SVG animations", config["config.animations"])
|
||||
info("SVG bottom padding", config["config.padding"])
|
||||
info("Content order", config["config.order"])
|
||||
|
||||
//Additional plugins
|
||||
const plugins = {
|
||||
lines:{enabled:input.bool("plugin_lines")},
|
||||
traffic:{enabled:input.bool("plugin_traffic")},
|
||||
pagespeed:{enabled:input.bool("plugin_pagespeed")},
|
||||
habits:{enabled:input.bool("plugin_habits")},
|
||||
languages:{enabled:input.bool("plugin_languages")},
|
||||
followup:{enabled:input.bool("plugin_followup")},
|
||||
music:{enabled:input.bool("plugin_music")},
|
||||
posts:{enabled:input.bool("plugin_posts")},
|
||||
isocalendar:{enabled:input.bool("plugin_isocalendar")},
|
||||
gists:{enabled:input.bool("plugin_gists")},
|
||||
topics:{enabled:input.bool("plugin_topics")},
|
||||
projects:{enabled:input.bool("plugin_projects")},
|
||||
tweets:{enabled:input.bool("plugin_tweets")},
|
||||
stars:{enabled:input.bool("plugin_stars")},
|
||||
stargazers:{enabled:input.bool("plugin_stargazers")},
|
||||
activity:{enabled:input.bool("plugin_activity")},
|
||||
people:{enabled:input.bool("plugin_people")},
|
||||
anilist:{enabled:input.bool("plugin_anilist")},
|
||||
}
|
||||
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
|
||||
info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key))
|
||||
//Additional plugins options
|
||||
//Pagespeed
|
||||
if (plugins.pagespeed.enabled) {
|
||||
plugins.pagespeed.token = input.string("plugin_pagespeed_token")
|
||||
info("Pagespeed token", plugins.pagespeed.token, {token:true})
|
||||
for (const option of ["url"])
|
||||
info(`Pagespeed ${option}`, q[`pagespeed.${option}`] = input.string(`plugin_pagespeed_${option}`))
|
||||
for (const option of ["detailed", "screenshot"])
|
||||
info(`Pagespeed ${option}`, q[`pagespeed.${option}`] = input.bool(`plugin_pagespeed_${option}`))
|
||||
//Committer
|
||||
const committer = {}
|
||||
if (!dryrun) {
|
||||
//Compute committer informations
|
||||
committer.commit = true
|
||||
committer.token = _token || token
|
||||
committer.branch = _branch || github.context.ref.replace(/^refs[/]heads[/]/, "")
|
||||
info("Committer token", committer.token, {token:true})
|
||||
if (!committer.token)
|
||||
throw new Error("You must provide a valid GitHub token to commit your metrics")
|
||||
info("Committer branch", committer.branch)
|
||||
//Instantiate API for committer
|
||||
committer.rest = github.getOctokit(committer.token)
|
||||
info("Committer REST API", "ok")
|
||||
try {
|
||||
info("Committer account", (await committer.rest.users.getAuthenticated()).data.login)
|
||||
}
|
||||
//Languages
|
||||
if (plugins.languages.enabled) {
|
||||
for (const option of ["ignored", "skipped", "colors"])
|
||||
info(`Languages ${option}`, q[`languages.${option}`] = input.array(`plugin_languages_${option}`))
|
||||
catch {
|
||||
info("Committer account", "(github-actions)")
|
||||
}
|
||||
//Habits
|
||||
if (plugins.habits.enabled) {
|
||||
for (const option of ["facts", "charts"])
|
||||
info(`Habits ${option}`, q[`habits.${option}`] = input.bool(`plugin_habits_${option}`))
|
||||
for (const option of ["from", "days"])
|
||||
info(`Habits ${option}`, q[`habits.${option}`] = input.number(`plugin_habits_${option}`))
|
||||
}
|
||||
//Music
|
||||
if (plugins.music.enabled) {
|
||||
plugins.music.token = input.string("plugin_music_token")
|
||||
info("Music token", plugins.music.token, {token:true})
|
||||
for (const option of ["provider", "mode", "playlist", "user"])
|
||||
info(`Music ${option}`, q[`music.${option}`] = input.string(`plugin_music_${option}`))
|
||||
for (const option of ["limit"])
|
||||
info(`Music ${option}`, q[`music.${option}`] = input.number(`plugin_music_${option}`))
|
||||
}
|
||||
//Posts
|
||||
if (plugins.posts.enabled) {
|
||||
for (const option of ["source", "user"])
|
||||
info(`Posts ${option}`, q[`posts.${option}`] = input.string(`plugin_posts_${option}`))
|
||||
for (const option of ["limit"])
|
||||
info(`Posts ${option}`, q[`posts.${option}`] = input.number(`plugin_posts_${option}`))
|
||||
}
|
||||
//Isocalendar
|
||||
if (plugins.isocalendar.enabled) {
|
||||
for (const option of ["duration"])
|
||||
info(`Isocalendar ${option}`, q[`isocalendar.${option}`] = input.string(`plugin_isocalendar_${option}`))
|
||||
}
|
||||
//Topics
|
||||
if (plugins.topics.enabled) {
|
||||
for (const option of ["mode", "sort"])
|
||||
info(`Topics ${option}`, q[`topics.${option}`] = input.string(`plugin_topics_${option}`))
|
||||
for (const option of ["limit"])
|
||||
info(`Topics ${option}`, q[`topics.${option}`] = input.number(`plugin_topics_${option}`))
|
||||
}
|
||||
//Projects
|
||||
if (plugins.projects.enabled) {
|
||||
for (const option of ["repositories"])
|
||||
info(`Projects ${option}`, q[`projects.${option}`] = input.string(`plugin_projects_${option}`))
|
||||
for (const option of ["limit"])
|
||||
info(`Projects ${option}`, q[`projects.${option}`] = input.number(`plugin_projects_${option}`))
|
||||
for (const option of ["descriptions"])
|
||||
info(`Projects ${option}`, q[`projects.${option}`] = input.bool(`plugin_projects_${option}`))
|
||||
}
|
||||
//Tweets
|
||||
if (plugins.tweets.enabled) {
|
||||
plugins.tweets.token = input.string("plugin_tweets_token")
|
||||
info("Tweets token", plugins.tweets.token, {token:true})
|
||||
for (const option of ["user"])
|
||||
info(`Tweets ${option}`, q[`tweets.${option}`] = input.string(`plugin_tweets_${option}`))
|
||||
for (const option of ["limit"])
|
||||
info(`Tweets ${option}`, q[`tweets.${option}`] = input.number(`plugin_tweets_${option}`))
|
||||
}
|
||||
//Stars
|
||||
if (plugins.stars.enabled) {
|
||||
for (const option of ["limit"])
|
||||
info(`Stars ${option}`, q[`stars.${option}`] = input.number(`plugin_stars_${option}`))
|
||||
}
|
||||
//Activity
|
||||
if (plugins.activity.enabled) {
|
||||
for (const option of ["limit", "days"])
|
||||
info(`Activity ${option}`, q[`activity.${option}`] = input.number(`plugin_activity_${option}`))
|
||||
for (const option of ["filter"])
|
||||
info(`Activity ${option}`, q[`activity.${option}`] = input.array(`plugin_activity_${option}`))
|
||||
}
|
||||
//People
|
||||
if (plugins.people.enabled) {
|
||||
for (const option of ["limit", "size"])
|
||||
info(`People ${option}`, q[`people.${option}`] = input.number(`plugin_people_${option}`))
|
||||
for (const option of ["types", "thanks"])
|
||||
info(`People ${option}`, q[`people.${option}`] = input.array(`plugin_people_${option}`))
|
||||
for (const option of ["identicons"])
|
||||
info(`People ${option}`, q[`people.${option}`] = input.bool(`plugin_people_${option}`))
|
||||
}
|
||||
//Anilist
|
||||
if (plugins.anilist.enabled) {
|
||||
for (const option of ["limit"])
|
||||
info(`Anilist ${option}`, q[`anilist.${option}`] = input.number(`plugin_anilist_${option}`))
|
||||
for (const option of ["medias", "sections"])
|
||||
info(`Anilist ${option}`, q[`anilist.${option}`] = input.array(`plugin_anilist_${option}`))
|
||||
for (const option of ["shuffle"])
|
||||
info(`Anilist ${option}`, q[`anilist.${option}`] = input.bool(`plugin_anilist_${option}`))
|
||||
for (const option of ["user"])
|
||||
info(`Anilist ${option}`, q[`anilist.${option}`] = input.string(`plugin_anilist_${option}`))
|
||||
}
|
||||
|
||||
//Repositories to use
|
||||
const repositories = input.number("repositories")
|
||||
const forks = input.bool("repositories_forks")
|
||||
info("Repositories to process", repositories)
|
||||
info("Include forked repositories", forks)
|
||||
|
||||
//Die on plugins errors
|
||||
const die = input.bool("plugins_errors_fatal")
|
||||
info("Plugin errors", die ? "(exit with error)" : "(displayed in generated SVG)")
|
||||
|
||||
//Build query
|
||||
const query = input.object("query")
|
||||
info("Query additional params", query)
|
||||
q = {...query, ...q, base:false, ...base, ...config, repositories, "repositories.forks":forks, template}
|
||||
|
||||
//Render metrics
|
||||
const {rendered} = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die, verify}, {Plugins, Templates})
|
||||
info("Rendering", "complete")
|
||||
|
||||
//Commit to repository
|
||||
const dryrun = input.bool("dryrun")
|
||||
if (dryrun)
|
||||
info("Dry-run", "complete")
|
||||
else {
|
||||
//Repository and branch
|
||||
const branch = input.string("committer_branch", {default:github.context.ref.replace(/^refs[/]heads[/]/, "")})
|
||||
info("Current repository", `${github.context.repo.owner}/${github.context.repo.repo}`)
|
||||
info("Current branch", branch)
|
||||
//Committer token
|
||||
const token = input.string("committer_token", {default:input.string("token")})
|
||||
info("Committer token", token, {token:true})
|
||||
if (!token)
|
||||
throw new Error("You must provide a valid GitHub token to commit your metrics")
|
||||
const rest = github.getOctokit(token)
|
||||
info("Committer REST API", "ok")
|
||||
try {
|
||||
info("Committer", (await rest.users.getAuthenticated()).data.login)
|
||||
}
|
||||
catch {
|
||||
info("Committer", "(github-actions)")
|
||||
}
|
||||
//Retrieve previous render SHA to be able to update file content through API
|
||||
let sha = null
|
||||
try {
|
||||
const {repository:{object:{oid}}} = await graphql(`
|
||||
query Sha {
|
||||
repository(owner: "${github.context.repo.owner}", name: "${github.context.repo.repo}") {
|
||||
object(expression: "${branch}:${filename}") { ... on Blob { oid } }
|
||||
}
|
||||
//Retrieve previous render SHA to be able to update file content through API
|
||||
committer.sha = null
|
||||
try {
|
||||
const {repository:{object:{oid}}} = await graphql(`
|
||||
query Sha {
|
||||
repository(owner: "${github.context.repo.owner}", name: "${github.context.repo.repo}") {
|
||||
object(expression: "${committer.branch}:${filename}") { ... on Blob { oid } }
|
||||
}
|
||||
`
|
||||
)
|
||||
sha = oid
|
||||
} catch (error) { console.debug(error) }
|
||||
info("Previous render sha", sha ?? "(none)")
|
||||
//Update file content through API
|
||||
await rest.repos.createOrUpdateFileContents({
|
||||
...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`,
|
||||
content:Buffer.from(rendered).toString("base64"),
|
||||
branch,
|
||||
...(sha ? {sha} : {})
|
||||
})
|
||||
info("Commit to current repository", "ok")
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
committer.sha = oid
|
||||
} catch (error) { console.debug(error) }
|
||||
info("Previous render sha", committer.sha ?? "(none)")
|
||||
}
|
||||
else
|
||||
info("Dry-run", true)
|
||||
|
||||
//Success
|
||||
console.log(`Success, thanks for using metrics !`)
|
||||
process.exit(0)
|
||||
}
|
||||
//Errors
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
if (!input.bool("debug"))
|
||||
for (const log of ["─".repeat(64), "An error occured, logging debug message :", ...debugged])
|
||||
//SVG file
|
||||
conf.optimize = optimize
|
||||
info("SVG output", filename)
|
||||
info("SVG optimization", optimize)
|
||||
info("SVG verification after generation", verify)
|
||||
|
||||
//Template
|
||||
info.break()
|
||||
info.section("Templates")
|
||||
info("Community templates", _templates)
|
||||
info("Template used", template)
|
||||
info("Query additional params", query)
|
||||
|
||||
//Core config
|
||||
info.break()
|
||||
info.group({metadata, name:"core", inputs:config})
|
||||
info("Plugin errors", die ? "(exit with error)" : "(displayed in generated SVG)")
|
||||
Object.assign(q, config)
|
||||
|
||||
//Base content
|
||||
info.break()
|
||||
const {base:parts, ...base} = metadata.plugins.base.inputs.action({core})
|
||||
info.group({metadata, name:"base", inputs:base})
|
||||
info("Base sections", parts)
|
||||
base.base = false
|
||||
for (const part of conf.settings.plugins.base.parts)
|
||||
base[`base.${part}`] = parts.includes(part)
|
||||
Object.assign(q, base)
|
||||
|
||||
//Additional plugins
|
||||
const plugins = {}
|
||||
for (const name of Object.keys(Plugins).filter(key => !["base", "core"].includes(key))) {
|
||||
//Parse inputs
|
||||
const {[name]:enabled, ...inputs} = metadata.plugins[name].inputs.action({core})
|
||||
plugins[name] = {enabled}
|
||||
//Register user inputs
|
||||
if (enabled) {
|
||||
info.break()
|
||||
info.group({metadata, name, inputs:enabled ? inputs : {}})
|
||||
q[name] = true
|
||||
for (const [key, value] of Object.entries(inputs)) {
|
||||
//Store token in plugin configuration
|
||||
if (metadata.plugins[name].inputs[key].type === "token")
|
||||
plugins[name][key] = value
|
||||
//Store value in query
|
||||
else
|
||||
q[`${name}.${key}`] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Render metrics
|
||||
info.break()
|
||||
info.section("Rendering")
|
||||
const {rendered} = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die, verify}, {Plugins, Templates})
|
||||
info("Status", "complete")
|
||||
|
||||
//Commit metrics
|
||||
if (committer.commit) {
|
||||
await committer.rest.repos.createOrUpdateFileContents({
|
||||
...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`,
|
||||
content:Buffer.from(rendered).toString("base64"),
|
||||
branch:committer.branch,
|
||||
...(committer.sha ? {sha:committer.sha} : {})
|
||||
})
|
||||
info("Commit to repository", "success")
|
||||
}
|
||||
|
||||
//Success
|
||||
info.break()
|
||||
console.log(`Success, thanks for using metrics!`)
|
||||
process.exit(0)
|
||||
}
|
||||
//Errors
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
//Print debug buffer if debug was not enabled (if it is, it's already logged on the fly)
|
||||
if (!DEBUG)
|
||||
for (const log of [info.break(), "An error occured, logging debug message :", ...debugged])
|
||||
console.log(log)
|
||||
core.setFailed(error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})()).catch(error => process.exit(1))
|
||||
core.setFailed(error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user