|
7 | 7 | import metrics from "../metrics.mjs" |
8 | 8 |
|
9 | 9 | ;((async function () { |
10 | | - //Yaml boolean converter |
11 | | - const bool = (value, defaulted = false) => typeof value === "string" ? /^(?:[Tt]rue|[Oo]n|[Yy]es)$/.test(value) : defaulted |
| 10 | + //Input parser |
| 11 | + const input = { |
| 12 | + get:(name) => decodeURIComponent(`${core.getInput(name)}`.trim()), |
| 13 | + 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, |
| 14 | + number:(name, {default:defaulted = undefined} = {}) => Number.isFinite(Number(input.get(name))) ? Number(input.get(name)) : defaulted, |
| 15 | + string:(name, {default:defaulted = undefined} = {}) => input.get(name) || defaulted, |
| 16 | + array:(name, {separator = ","} = {}) => input.get(name).split(separator).map(value => value.trim()).filter(value => value), |
| 17 | + object:(name) => JSON.parse(input.get(name) || "{}"), |
| 18 | + } |
| 19 | + //Info logger |
| 20 | + const info = (left, right, {token = false} = {}) => console.log(`${`${left}`.padEnd(48)} │ ${ |
| 21 | + Array.isArray(right) ? right.join(", ") || "(none)" : |
| 22 | + right === undefined ? "(default)" : |
| 23 | + token ? /^MOCKED/.test(right) ? "(MOCKED TOKEN)" : (right ? "(provided)" : "(missing)") : |
| 24 | + typeof right === "object" ? JSON.stringify(right) : |
| 25 | + right |
| 26 | + }`) |
12 | 27 | //Debug message buffer |
13 | 28 | const debugged = [] |
14 | 29 | //Runner |
|
29 | 44 |
|
30 | 45 | //Load configuration |
31 | 46 | const {conf, Plugins, Templates} = await setup({log:false}) |
32 | | - console.log(`Configuration │ loaded`) |
33 | | - console.log(`Version │ ${conf.package.version}`) |
| 47 | + info("Setup", "complete") |
| 48 | + info("Version", conf.package.version) |
34 | 49 |
|
35 | 50 | //Debug mode |
36 | | - const debug = bool(core.getInput("debug")) |
| 51 | + const debug = input.bool("debug", {default:false}) |
| 52 | + info("Debug mode", debug) |
37 | 53 | if (!debug) |
38 | 54 | console.debug = message => debugged.push(message) |
39 | | - console.log(`Debug mode │ ${debug}`) |
40 | | - const dflags = (core.getInput("debug_flags") || "").split(" ").filter(flag => flag) |
41 | | - console.log(`Debug flags │ ${dflags.join(" ") || "(none)"}`) |
| 55 | + const dflags = input.array("debug_flags", {separator:" "}) |
| 56 | + info("Debug flags", dflags) |
42 | 57 |
|
43 | 58 | //Load svg template, style, fonts and query |
44 | | - const template = core.getInput("template") || "classic" |
45 | | - console.log(`Template to use │ ${template}`) |
| 59 | + const template = input.string("template", {default:"classic"}) |
| 60 | + info("Template used", template) |
46 | 61 |
|
47 | 62 | //Token for data gathering |
48 | | - const token = core.getInput("token") || "" |
49 | | - console.log(`Github token │ ${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`) |
| 63 | + const token = input.string("token") |
| 64 | + info("GitHub token", token, {token:true}) |
50 | 65 | if (!token) |
51 | 66 | throw new Error("You must provide a valid GitHub token to gather your metrics") |
52 | 67 | const api = {} |
53 | 68 | api.graphql = octokit.graphql.defaults({headers:{authorization: `token ${token}`}}) |
54 | | - console.log(`Github GraphQL API │ ok`) |
| 69 | + info("Github GraphQL API", "ok") |
55 | 70 | api.rest = github.getOctokit(token) |
56 | | - console.log(`Github REST API │ ok`) |
| 71 | + info("Github REST API", "ok") |
57 | 72 | //Apply mocking if needed |
58 | | - if (bool(core.getInput("use_mocked_data"))) { |
| 73 | + if (input.bool("use_mocked_data", {default:false})) { |
59 | 74 | Object.assign(api, await mocks(api)) |
60 | | - console.log(`Mocked Github API │ ok`) |
| 75 | + info("Use mocked API", true) |
61 | 76 | } |
62 | 77 | //Extract octokits |
63 | 78 | const {graphql, rest} = api |
64 | 79 |
|
65 | 80 | //SVG output |
66 | | - const filename = core.getInput("filename") || "github-metrics.svg" |
67 | | - console.log(`SVG output file │ ${filename}`) |
| 81 | + const filename = input.string("filename", {default:"github-metrics.svg"}) |
| 82 | + info("SVG output", filename) |
68 | 83 |
|
69 | 84 | //SVG optimization |
70 | | - const optimize = bool(core.getInput("optimize"), true) |
| 85 | + const optimize = input.bool("optimize", {default:true}) |
71 | 86 | conf.optimize = optimize |
72 | | - console.log(`SVG optimization │ ${optimize}`) |
| 87 | + info("SVG optimization", optimize) |
| 88 | + |
| 89 | + //Verify svg |
| 90 | + const verify = input.bool("verify") |
| 91 | + info("SVG verification after generation", verify) |
73 | 92 |
|
74 | 93 | //GitHub user |
75 | 94 | let authenticated |
|
79 | 98 | catch { |
80 | 99 | authenticated = github.context.repo.owner |
81 | 100 | } |
82 | | - const user = core.getInput("user") || authenticated |
83 | | - console.log(`GitHub user │ ${user}`) |
| 101 | + const user = input.string("user", {default:authenticated}) |
| 102 | + info("Target GitHub user", user) |
84 | 103 |
|
85 | 104 | //Base elements |
86 | 105 | const base = {} |
87 | | - let parts = (core.getInput("base") || "").split(",").map(part => part.trim()) |
| 106 | + const parts = input.array("base") |
88 | 107 | for (const part of conf.settings.plugins.base.parts) |
89 | 108 | base[`base.${part}`] = parts.includes(part) |
90 | | - console.log(`Base parts │ ${parts.join(", ") || "(none)"}`) |
| 109 | + info("Base parts", parts) |
91 | 110 |
|
92 | 111 | //Config |
93 | 112 | const config = { |
94 | | - "config.timezone":core.getInput("config_timezone") || "" |
| 113 | + "config.timezone":input.string("config_timezone") |
95 | 114 | } |
96 | | - console.log(`Timezone │ ${config["config.timezone"] || "(system default)"}`) |
| 115 | + info("Timezone", config["config.timezone"] ?? "(system default)") |
97 | 116 |
|
98 | 117 | //Additional plugins |
99 | 118 | const plugins = { |
100 | | - lines:{enabled:bool(core.getInput("plugin_lines"))}, |
101 | | - traffic:{enabled:bool(core.getInput("plugin_traffic"))}, |
102 | | - pagespeed:{enabled:bool(core.getInput("plugin_pagespeed"))}, |
103 | | - habits:{enabled:bool(core.getInput("plugin_habits"))}, |
104 | | - languages:{enabled:bool(core.getInput("plugin_languages"))}, |
105 | | - followup:{enabled:bool(core.getInput("plugin_followup"))}, |
106 | | - music:{enabled:bool(core.getInput("plugin_music"))}, |
107 | | - posts:{enabled:bool(core.getInput("plugin_posts"))}, |
108 | | - isocalendar:{enabled:bool(core.getInput("plugin_isocalendar"))}, |
109 | | - gists:{enabled:bool(core.getInput("plugin_gists"))}, |
110 | | - topics:{enabled:bool(core.getInput("plugin_topics"))}, |
111 | | - projects:{enabled:bool(core.getInput("plugin_projects"))}, |
112 | | - tweets:{enabled:bool(core.getInput("plugin_tweets"))}, |
| 119 | + lines:{enabled:input.bool("plugin_lines")}, |
| 120 | + traffic:{enabled:input.bool("plugin_traffic")}, |
| 121 | + pagespeed:{enabled:input.bool("plugin_pagespeed")}, |
| 122 | + habits:{enabled:input.bool("plugin_habits")}, |
| 123 | + languages:{enabled:input.bool("plugin_languages")}, |
| 124 | + followup:{enabled:input.bool("plugin_followup")}, |
| 125 | + music:{enabled:input.bool("plugin_music")}, |
| 126 | + posts:{enabled:input.bool("plugin_posts")}, |
| 127 | + isocalendar:{enabled:input.bool("plugin_isocalendar")}, |
| 128 | + gists:{enabled:input.bool("plugin_gists")}, |
| 129 | + topics:{enabled:input.bool("plugin_topics")}, |
| 130 | + projects:{enabled:input.bool("plugin_projects")}, |
| 131 | + tweets:{enabled:input.bool("plugin_tweets")}, |
113 | 132 | } |
114 | 133 | let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true])) |
115 | | - console.log(`Plugins enabled │ ${Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).join(", ")}`) |
| 134 | + info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key)) |
116 | 135 | //Additional plugins options |
117 | 136 | //Pagespeed |
118 | 137 | if (plugins.pagespeed.enabled) { |
119 | | - plugins.pagespeed.token = core.getInput("plugin_pagespeed_token") || "" |
120 | | - q[`pagespeed.detailed`] = bool(core.getInput(`plugin_pagespeed_detailed`)) |
121 | | - q[`pagespeed.screenshot`] = bool(core.getInput(`plugin_pagespeed_screenshot`)) |
122 | | - console.log(`Pagespeed token │ ${/^MOCKED/.test(plugins.pagespeed.token) ? "(MOCKED)" : plugins.pagespeed.token ? "provided" : "missing"}`) |
123 | | - console.log(`Pagespeed detailed │ ${q["pagespeed.detailed"]}`) |
124 | | - console.log(`Pagespeed screenshot │ ${q["pagespeed.screenshot"]}`) |
| 138 | + plugins.pagespeed.token = input.string("plugin_pagespeed_token") |
| 139 | + info("Pagespeed token", plugins.pagespeed.token, {token:true}) |
| 140 | + for (const option of ["detailed", "screenshot"]) |
| 141 | + info(`Pagespeed ${option}`, q[`pagespeed.${option}`] = input.bool(`plugin_pagespeed_${option}`)) |
125 | 142 | } |
126 | 143 | //Languages |
127 | 144 | if (plugins.languages.enabled) { |
128 | 145 | for (const option of ["ignored", "skipped"]) |
129 | | - q[`languages.${option}`] = core.getInput(`plugin_languages_${option}`) || null |
130 | | - console.log(`Languages ignored │ ${q["languages.ignored"] || "(none)"}`) |
131 | | - console.log(`Languages skipped repos │ ${q["languages.skipped"] || "(none)"}`) |
| 146 | + info(`Languages ${option}`, q[`languages.${option}`] = input.array(`plugin_languages_${option}`)) |
132 | 147 | } |
133 | 148 | //Habits |
134 | 149 | if (plugins.habits.enabled) { |
| 150 | + for (const option of ["facts", "charts"]) |
| 151 | + info(`Habits ${option}`, q[`habits.${option}`] = input.bool(`plugin_habits_${option}`)) |
135 | 152 | for (const option of ["from", "days"]) |
136 | | - q[`habits.${option}`] = core.getInput(`plugin_habits_${option}`) || null |
137 | | - q[`habits.facts`] = bool(core.getInput(`plugin_habits_facts`)) |
138 | | - q[`habits.charts`] = bool(core.getInput(`plugin_habits_charts`)) |
139 | | - console.log(`Habits facts │ ${q["habits.facts"]}`) |
140 | | - console.log(`Habits charts │ ${q["habits.charts"]}`) |
141 | | - console.log(`Habits events to use │ ${q["habits.from"] || "(default)"}`) |
142 | | - console.log(`Habits days to keep │ ${q["habits.days"] || "(default)"}`) |
| 153 | + info(`Habits ${option}`, q[`habits.${option}`] = input.number(`plugin_habits_${option}`)) |
143 | 154 | } |
144 | 155 | //Music |
145 | 156 | if (plugins.music.enabled) { |
146 | | - plugins.music.token = core.getInput("plugin_music_token") || "" |
147 | | - for (const option of ["provider", "mode", "playlist", "limit"]) |
148 | | - q[`music.${option}`] = core.getInput(`plugin_music_${option}`) || null |
149 | | - console.log(`Music provider │ ${q["music.provider"] || "(none)"}`) |
150 | | - console.log(`Music plugin mode │ ${q["music.mode"] || "(none)"}`) |
151 | | - console.log(`Music playlist │ ${q["music.playlist"] || "(none)"}`) |
152 | | - console.log(`Music tracks limit │ ${q["music.limit"] || "(default)"}`) |
153 | | - console.log(`Music token │ ${/^MOCKED/.test(plugins.music.token) ? "(MOCKED)" : plugins.music.token ? "provided" : "missing"}`) |
| 157 | + plugins.music.token = input.string("plugin_music_token") |
| 158 | + info("Music token", plugins.music.token, {token:true}) |
| 159 | + for (const option of ["provider", "mode", "playlist"]) |
| 160 | + info(`Music ${option}`, q[`music.${option}`] = input.string(`plugin_music_${option}`)) |
| 161 | + for (const option of ["limit"]) |
| 162 | + info(`Music ${option}`, q[`music.${option}`] = input.number(`plugin_music_${option}`)) |
154 | 163 | } |
155 | 164 | //Posts |
156 | 165 | if (plugins.posts.enabled) { |
157 | | - for (const option of ["source", "limit"]) |
158 | | - q[`posts.${option}`] = core.getInput(`plugin_posts_${option}`) || null |
159 | | - console.log(`Posts source │ ${q["posts.source"] || "(none)"}`) |
160 | | - console.log(`Posts limit │ ${q["posts.limit"] || "(default)"}`) |
| 166 | + for (const option of ["source"]) |
| 167 | + info(`Posts ${option}`, q[`posts.${option}`] = input.string(`plugin_posts_${option}`)) |
| 168 | + for (const option of ["limit"]) |
| 169 | + info(`Posts ${option}`, q[`posts.${option}`] = input.number(`plugin_posts_${option}`)) |
161 | 170 | } |
162 | 171 | //Isocalendar |
163 | 172 | if (plugins.isocalendar.enabled) { |
164 | | - q["isocalendar.duration"] = core.getInput("plugin_isocalendar_duration") || "half-year" |
165 | | - console.log(`Isocalendar duration │ ${q["isocalendar.duration"]}`) |
| 173 | + for (const option of ["duration"]) |
| 174 | + info(`Isocalendar ${option}`, q[`isocalendar.${option}`] = input.string(`plugin_isocalendar_${option}`)) |
166 | 175 | } |
167 | 176 | //Topics |
168 | 177 | if (plugins.topics.enabled) { |
169 | | - for (const option of ["mode", "sort", "limit"]) |
170 | | - q[`topics.${option}`] = core.getInput(`plugin_topics_${option}`) || null |
171 | | - console.log(`Topics mode │ ${q["topics.mode"] || "(default)"}`) |
172 | | - console.log(`Topics sort mode │ ${q["topics.sort"] || "(default)"}`) |
173 | | - console.log(`Topics limit │ ${q["topics.limit"] || "(default)"}`) |
| 178 | + for (const option of ["mode", "sort"]) |
| 179 | + info(`Topics ${option}`, q[`topics.${option}`] = input.string(`plugin_topics_${option}`)) |
| 180 | + for (const option of ["limit"]) |
| 181 | + info(`Topics ${option}`, q[`topics.${option}`] = input.number(`plugin_topics_${option}`)) |
174 | 182 | } |
175 | 183 | //Projects |
176 | 184 | if (plugins.projects.enabled) { |
177 | | - for (const option of ["limit", "repositories"]) |
178 | | - q[`projects.${option}`] = core.getInput(`plugin_projects_${option}`) || null |
179 | | - console.log(`Projects limit │ ${q["projects.limit"] || "(default)"}`) |
180 | | - console.log(`Projects repositories │ ${q["projects.repositories"] || "(none)"}`) |
| 185 | + for (const option of ["repositories"]) |
| 186 | + info(`Projects ${option}`, q[`projects.${option}`] = input.string(`plugin_projects_${option}`)) |
| 187 | + for (const option of ["limit"]) |
| 188 | + info(`Projects ${option}`, q[`projects.${option}`] = input.number(`plugin_projects_${option}`)) |
181 | 189 | } |
182 | 190 | //Tweets |
183 | 191 | if (plugins.tweets.enabled) { |
184 | | - plugins.tweets.token = core.getInput("plugin_tweets_token") || null |
| 192 | + plugins.tweets.token = input.string("plugin_tweets_token") |
| 193 | + info("Tweets token", plugins.tweets.token, {token:true}) |
185 | 194 | for (const option of ["limit"]) |
186 | | - q[`tweets.${option}`] = core.getInput(`plugin_tweets_${option}`) || null |
187 | | - console.log(`Twitter token │ ${/^MOCKED/.test(plugins.tweets.token) ? "(MOCKED)" : plugins.tweets.token ? "provided" : "missing"}`) |
188 | | - console.log(`Tweets limit │ ${q["tweets.limit"] || "(default)"}`) |
| 195 | + info(`Tweets ${option}`, q[`tweets.${option}`] = input.number(`plugin_tweets_${option}`)) |
189 | 196 | } |
190 | 197 |
|
191 | 198 | //Repositories to use |
192 | | - const repositories = Number(core.getInput("repositories")) || 100 |
193 | | - console.log(`Repositories to use │ ${repositories}`) |
| 199 | + const repositories = input.number("repositories") |
| 200 | + info("Repositories to process", repositories) |
194 | 201 |
|
195 | 202 | //Die on plugins errors |
196 | | - const die = bool(core.getInput("plugins_errors_fatal")) |
197 | | - console.log(`Plugin errors │ ${die ? "die" : "warn"}`) |
198 | | - |
199 | | - //Verify svg |
200 | | - const verify = bool(core.getInput("verify")) |
201 | | - console.log(`Verify SVG │ ${verify}`) |
| 203 | + const die = input.bool("plugins_errors_fatal") |
| 204 | + info("Plugin errors", die ? "(exit with error)" : "(displayed in generated SVG)") |
202 | 205 |
|
203 | 206 | //Build query |
204 | | - const query = JSON.parse(core.getInput("query") || "{}") |
205 | | - console.log(`Query additional params │ ${JSON.stringify(query)}`) |
| 207 | + const query = input.object("query") |
| 208 | + info("Query additional params", query) |
206 | 209 | q = {...query, ...q, base:false, ...base, ...config, repositories, template} |
207 | 210 |
|
208 | 211 | //Render metrics |
209 | 212 | const rendered = await metrics({login:user, q, dflags}, {graphql, rest, plugins, conf, die, verify}, {Plugins, Templates}) |
| 213 | + info("Rendering", "complete") |
210 | 214 | console.log(`Render │ complete`) |
211 | 215 |
|
212 | 216 | //Commit to repository |
213 | | - const dryrun = bool(core.getInput("dryrun")) |
| 217 | + const dryrun = input.bool("dryrun") |
214 | 218 | if (dryrun) |
215 | | - console.log(`Dry-run │ complete`) |
| 219 | + info("Dry-run", "complete") |
216 | 220 | else { |
217 | 221 | //Repository and branch |
218 | 222 | const branch = github.context.ref.replace(/^refs[/]heads[/]/, "") |
219 | | - console.log(`Repository │ ${github.context.repo.owner}/${github.context.repo.repo}`) |
220 | | - console.log(`Branch │ ${branch}`) |
| 223 | + info("Current repository", `${github.context.repo.owner}/${github.context.repo.repo}`) |
| 224 | + info("Current branch", branch) |
221 | 225 | //Committer token |
222 | | - const token = core.getInput("committer_token") || core.getInput("token") || "" |
223 | | - console.log(`Committer token │ ${/^MOCKED/.test(token) ? "(MOCKED)" : token ? "provided" : "missing"}`) |
| 226 | + const token = input.string("committer_token", {default:input.string("token")}) |
| 227 | + info("Committer token", token, {token:true}) |
224 | 228 | if (!token) |
225 | 229 | throw new Error("You must provide a valid GitHub token to commit your metrics") |
226 | 230 | const rest = github.getOctokit(token) |
227 | | - console.log(`Committer REST API │ ok`) |
| 231 | + info("Committer REST API", "ok") |
228 | 232 | try { |
229 | | - console.log(`Committer │ ${(await rest.users.getAuthenticated()).data.login}`) |
| 233 | + info("Committer", (await rest.users.getAuthenticated()).data.login) |
230 | 234 | } |
231 | 235 | catch { |
232 | | - console.log(`Committer │ (github-actions)`) |
| 236 | + info("Committer", "(github-actions)") |
233 | 237 | } |
234 | 238 | //Retrieve previous render SHA to be able to update file content through API |
235 | 239 | let sha = null |
|
244 | 248 | ) |
245 | 249 | sha = oid |
246 | 250 | } catch (error) { console.debug(error) } |
247 | | - console.log(`Previous render sha │ ${sha ?? "(none)"}`) |
| 251 | + info("Previous render sha", sha ?? "(none)") |
248 | 252 | //Update file content through API |
249 | 253 | await rest.repos.createOrUpdateFileContents({ |
250 | 254 | ...github.context.repo, path:filename, message:`Update ${filename} - [Skip GitHub Action]`, |
251 | 255 | content:Buffer.from(rendered).toString("base64"), |
252 | 256 | ...(sha ? {sha} : {}) |
253 | 257 | }) |
254 | | - console.log(`Commit to repo │ ok`) |
| 258 | + info("Commit to current repository", "ok") |
255 | 259 | } |
256 | 260 |
|
257 | 261 | //Success |
258 | 262 | console.log(`Success, thanks for using metrics !`) |
259 | 263 | process.exit(0) |
260 | | - |
261 | 264 | } |
262 | 265 | //Errors |
263 | 266 | catch (error) { |
264 | 267 | console.error(error) |
265 | | - if (!bool(core.getInput("debug"))) |
| 268 | + if (!input.bool("debug")) |
266 | 269 | for (const log of ["─".repeat(64), "An error occured, logging debug message :", ...debugged]) |
267 | 270 | console.log(log) |
268 | 271 | core.setFailed(error.message) |
|
0 commit comments