Refactor action inputs

This commit is contained in:
linguist
2020-12-31 18:56:18 +01:00
parent 54900bcf26
commit 19b3f1490c
2 changed files with 110 additions and 107 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "metrics", "name": "metrics",
"version": "2.10.0-beta", "version": "3.0.0-beta",
"description": "An image generator with 20+ metrics about your GitHub account such as activity, community, repositories, coding habits, website performances, music played, starred topics, etc. that you can put on your profile or elsewhere !", "description": "An image generator with 20+ metrics about your GitHub account such as activity, community, repositories, coding habits, website performances, music played, starred topics, etc. that you can put on your profile or elsewhere !",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {

View File

@@ -7,8 +7,23 @@
import metrics from "../metrics.mjs" import metrics from "../metrics.mjs"
;((async function () { ;((async function () {
//Yaml boolean converter //Input parser
const bool = (value, defaulted = false) => typeof value === "string" ? /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(value) : defaulted const input = {
get:(name) => decodeURIComponent(`${core.getInput(name)}`.trim()),
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 //Debug message buffer
const debugged = [] const debugged = []
//Runner //Runner
@@ -29,47 +44,51 @@
//Load configuration //Load configuration
const {conf, Plugins, Templates} = await setup({log:false}) const {conf, Plugins, Templates} = await setup({log:false})
console.log(`Configuration │ loaded`) info("Setup", "complete")
console.log(`Version │ ${conf.package.version}`) info("Version", conf.package.version)
//Debug mode //Debug mode
const debug = bool(core.getInput("debug")) const debug = input.bool("debug", {default:false})
info("Debug mode", debug)
if (!debug) if (!debug)
console.debug = message => debugged.push(message) console.debug = message => debugged.push(message)
console.log(`Debug mode │ ${debug}`) const dflags = input.array("debug_flags", {separator:" "})
const dflags = (core.getInput("debug_flags") || "").split(" ").filter(flag => flag) info("Debug flags", dflags)
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 = input.string("template", {default:"classic"})
console.log(`Template to use │ ${template}`) info("Template used", template)
//Token for data gathering //Token for data gathering
const token = core.getInput("token") || "" const token = input.string("token")
console.log(`Github token${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`) info("GitHub token", token, {token:true})
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 api = {} const api = {}
api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}}) api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}})
console.log(`Github GraphQL API │ ok`) info("Github GraphQL API", "ok")
api.rest = github.getOctokit(token) api.rest = github.getOctokit(token)
console.log(`Github REST API │ ok`) info("Github REST API", "ok")
//Apply mocking if needed //Apply mocking if needed
if (bool(core.getInput("use_mocked_data"))) { if (input.bool("use_mocked_data", {default:false})) {
Object.assign(api, await mocks(api)) Object.assign(api, await mocks(api))
console.log(`Mocked Github API │ ok`) info("Use mocked API", true)
} }
//Extract octokits //Extract octokits
const {graphql, rest} = api const {graphql, rest} = api
//SVG output //SVG output
const filename = core.getInput("filename") || "github-metrics.svg" const filename = input.string("filename", {default:"github-metrics.svg"})
console.log(`SVG output file │ ${filename}`) info("SVG output", filename)
//SVG optimization //SVG optimization
const optimize = bool(core.getInput("optimize"), true) const optimize = input.bool("optimize", {default:true})
conf.optimize = optimize conf.optimize = optimize
console.log(`SVG optimization │ ${optimize}`) info("SVG optimization", optimize)
//Verify svg
const verify = input.bool("verify")
info("SVG verification after generation", verify)
//GitHub user //GitHub user
let authenticated let authenticated
@@ -79,157 +98,142 @@
catch { catch {
authenticated = github.context.repo.owner authenticated = github.context.repo.owner
} }
const user = core.getInput("user") || authenticated const user = input.string("user", {default:authenticated})
console.log(`GitHub user │ ${user}`) info("Target GitHub user", user)
//Base elements //Base elements
const base = {} const base = {}
let parts = (core.getInput("base") || "").split(",").map(part => part.trim()) const parts = input.array("base")
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)"}`) info("Base parts", parts)
//Config //Config
const config = { const config = {
"config.timezone":core.getInput("config_timezone") || "" "config.timezone":input.string("config_timezone")
} }
console.log(`Timezone │ ${config["config.timezone"] || "(system default)"}`) info("Timezone", config["config.timezone"] ?? "(system default)")
//Additional plugins //Additional plugins
const plugins = { const plugins = {
lines:{enabled:bool(core.getInput("plugin_lines"))}, lines:{enabled:input.bool("plugin_lines")},
traffic:{enabled:bool(core.getInput("plugin_traffic"))}, traffic:{enabled:input.bool("plugin_traffic")},
pagespeed:{enabled:bool(core.getInput("plugin_pagespeed"))}, pagespeed:{enabled:input.bool("plugin_pagespeed")},
habits:{enabled:bool(core.getInput("plugin_habits"))}, habits:{enabled:input.bool("plugin_habits")},
languages:{enabled:bool(core.getInput("plugin_languages"))}, languages:{enabled:input.bool("plugin_languages")},
followup:{enabled:bool(core.getInput("plugin_followup"))}, followup:{enabled:input.bool("plugin_followup")},
music:{enabled:bool(core.getInput("plugin_music"))}, music:{enabled:input.bool("plugin_music")},
posts:{enabled:bool(core.getInput("plugin_posts"))}, posts:{enabled:input.bool("plugin_posts")},
isocalendar:{enabled:bool(core.getInput("plugin_isocalendar"))}, isocalendar:{enabled:input.bool("plugin_isocalendar")},
gists:{enabled:bool(core.getInput("plugin_gists"))}, gists:{enabled:input.bool("plugin_gists")},
topics:{enabled:bool(core.getInput("plugin_topics"))}, topics:{enabled:input.bool("plugin_topics")},
projects:{enabled:bool(core.getInput("plugin_projects"))}, projects:{enabled:input.bool("plugin_projects")},
tweets:{enabled:bool(core.getInput("plugin_tweets"))}, tweets:{enabled:input.bool("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(", ")}`) info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key))
//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 = input.string("plugin_pagespeed_token")
q[`pagespeed.detailed`] = bool(core.getInput(`plugin_pagespeed_detailed`)) info("Pagespeed token", plugins.pagespeed.token, {token:true})
q[`pagespeed.screenshot`] = bool(core.getInput(`plugin_pagespeed_screenshot`)) for (const option of ["detailed", "screenshot"])
console.log(`Pagespeed token │ ${/^MOCKED/.test(plugins.pagespeed.token) ? "(MOCKED)" : plugins.pagespeed.token ? "provided" : "missing"}`) info(`Pagespeed ${option}`, q[`pagespeed.${option}`] = input.bool(`plugin_pagespeed_${option}`))
console.log(`Pagespeed detailed │ ${q["pagespeed.detailed"]}`)
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 info(`Languages ${option}`, q[`languages.${option}`] = input.array(`plugin_languages_${option}`))
console.log(`Languages ignored │ ${q["languages.ignored"] || "(none)"}`)
console.log(`Languages skipped repos │ ${q["languages.skipped"] || "(none)"}`)
} }
//Habits //Habits
if (plugins.habits.enabled) { 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"]) for (const option of ["from", "days"])
q[`habits.${option}`] = core.getInput(`plugin_habits_${option}`) || null info(`Habits ${option}`, q[`habits.${option}`] = input.number(`plugin_habits_${option}`))
q[`habits.facts`] = bool(core.getInput(`plugin_habits_facts`))
q[`habits.charts`] = bool(core.getInput(`plugin_habits_charts`))
console.log(`Habits facts │ ${q["habits.facts"]}`)
console.log(`Habits charts │ ${q["habits.charts"]}`)
console.log(`Habits events to use │ ${q["habits.from"] || "(default)"}`)
console.log(`Habits days to keep │ ${q["habits.days"] || "(default)"}`)
} }
//Music //Music
if (plugins.music.enabled) { if (plugins.music.enabled) {
plugins.music.token = core.getInput("plugin_music_token") || "" plugins.music.token = input.string("plugin_music_token")
for (const option of ["provider", "mode", "playlist", "limit"]) info("Music token", plugins.music.token, {token:true})
q[`music.${option}`] = core.getInput(`plugin_music_${option}`) || null for (const option of ["provider", "mode", "playlist"])
console.log(`Music provider │ ${q["music.provider"] || "(none)"}`) info(`Music ${option}`, q[`music.${option}`] = input.string(`plugin_music_${option}`))
console.log(`Music plugin mode │ ${q["music.mode"] || "(none)"}`) for (const option of ["limit"])
console.log(`Music playlist │ ${q["music.playlist"] || "(none)"}`) info(`Music ${option}`, q[`music.${option}`] = input.number(`plugin_music_${option}`))
console.log(`Music tracks limit │ ${q["music.limit"] || "(default)"}`)
console.log(`Music token │ ${/^MOCKED/.test(plugins.music.token) ? "(MOCKED)" : plugins.music.token ? "provided" : "missing"}`)
} }
//Posts //Posts
if (plugins.posts.enabled) { if (plugins.posts.enabled) {
for (const option of ["source", "limit"]) for (const option of ["source"])
q[`posts.${option}`] = core.getInput(`plugin_posts_${option}`) || null info(`Posts ${option}`, q[`posts.${option}`] = input.string(`plugin_posts_${option}`))
console.log(`Posts source │ ${q["posts.source"] || "(none)"}`) for (const option of ["limit"])
console.log(`Posts limit │ ${q["posts.limit"] || "(default)"}`) info(`Posts ${option}`, q[`posts.${option}`] = input.number(`plugin_posts_${option}`))
} }
//Isocalendar //Isocalendar
if (plugins.isocalendar.enabled) { if (plugins.isocalendar.enabled) {
q["isocalendar.duration"] = core.getInput("plugin_isocalendar_duration") || "half-year" for (const option of ["duration"])
console.log(`Isocalendar duration │ ${q["isocalendar.duration"]}`) info(`Isocalendar ${option}`, q[`isocalendar.${option}`] = input.string(`plugin_isocalendar_${option}`))
} }
//Topics //Topics
if (plugins.topics.enabled) { if (plugins.topics.enabled) {
for (const option of ["mode", "sort", "limit"]) for (const option of ["mode", "sort"])
q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null info(`Topics ${option}`, q[`topics.${option}`] = input.string(`plugin_topics_${option}`))
console.log(`Topics mode │ ${q["topics.mode"] || "(default)"}`) for (const option of ["limit"])
console.log(`Topics sort mode │ ${q["topics.sort"] || "(default)"}`) info(`Topics ${option}`, q[`topics.${option}`] = input.number(`plugin_topics_${option}`))
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 ["repositories"])
q[`projects.${option}`] = core.getInput(`plugin_projects_${option}`) || null info(`Projects ${option}`, q[`projects.${option}`] = input.string(`plugin_projects_${option}`))
console.log(`Projects limit │ ${q["projects.limit"] || "(default)"}`) for (const option of ["limit"])
console.log(`Projects repositories │ ${q["projects.repositories"] || "(none)"}`) info(`Projects ${option}`, q[`projects.${option}`] = input.number(`plugin_projects_${option}`))
} }
//Tweets //Tweets
if (plugins.tweets.enabled) { if (plugins.tweets.enabled) {
plugins.tweets.token = core.getInput("plugin_tweets_token") || null plugins.tweets.token = input.string("plugin_tweets_token")
info("Tweets token", plugins.tweets.token, {token:true})
for (const option of ["limit"]) for (const option of ["limit"])
q[`tweets.${option}`] = core.getInput(`plugin_tweets_${option}`) || null info(`Tweets ${option}`, q[`tweets.${option}`] = input.number(`plugin_tweets_${option}`))
console.log(`Twitter token │ ${/^MOCKED/.test(plugins.tweets.token) ? "(MOCKED)" : plugins.tweets.token ? "provided" : "missing"}`)
console.log(`Tweets limit │ ${q["tweets.limit"] || "(default)"}`)
} }
//Repositories to use //Repositories to use
const repositories = Number(core.getInput("repositories")) || 100 const repositories = input.number("repositories")
console.log(`Repositories to use │ ${repositories}`) info("Repositories to process", repositories)
//Die on plugins errors //Die on plugins errors
const die = bool(core.getInput("plugins_errors_fatal")) const die = input.bool("plugins_errors_fatal")
console.log(`Plugin errors │ ${die ? "die" : "warn"}`) info("Plugin errors", die ? "(exit with error)" : "(displayed in generated SVG)")
//Verify svg
const verify = bool(core.getInput("verify"))
console.log(`Verify SVG │ ${verify}`)
//Build query //Build query
const query = JSON.parse(core.getInput("query") || "{}") const query = input.object("query")
console.log(`Query additional params │ ${JSON.stringify(query)}`) info("Query additional params", 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, verify}, {Plugins, Templates}) const rendered = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die, verify}, {Plugins, Templates})
info("Rendering", "complete")
console.log(`Render │ complete`) console.log(`Render │ complete`)
//Commit to repository //Commit to repository
const dryrun = bool(core.getInput("dryrun")) const dryrun = input.bool("dryrun")
if (dryrun) if (dryrun)
console.log(`Dry-run │ complete`) info("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}`) info("Current repository", `${github.context.repo.owner}/${github.context.repo.repo}`)
console.log(`Branch │ ${branch}`) info("Current branch", branch)
//Committer token //Committer token
const token = core.getInput("committer_token") || core.getInput("token") || "" const token = input.string("committer_token", {default:input.string("token")})
console.log(`Committer token │ ${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`) info("Committer token", token, {token:true})
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`) info("Committer REST API", "ok")
try { try {
console.log(`Committer │ ${(await rest.users.getAuthenticated()).data.login}`) info("Committer", (await rest.users.getAuthenticated()).data.login)
} }
catch { catch {
console.log(`Committer │ (github-actions)`) info("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
@@ -244,25 +248,24 @@
) )
sha = oid sha = oid
} catch (error) { console.debug(error) } } catch (error) { console.debug(error) }
console.log(`Previous render sha${sha ?? "(none)"}`) info("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`) info("Commit to current repository", "ok")
} }
//Success //Success
console.log(`Success, thanks for using metrics !`) console.log(`Success, thanks for using metrics !`)
process.exit(0) process.exit(0)
} }
//Errors //Errors
catch (error) { catch (error) {
console.error(error) console.error(error)
if (!bool(core.getInput("debug"))) if (!input.bool("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)