Use a docker image to support puppeteer (#7)

This commit is contained in:
Simon Lecoq
2020-10-26 13:22:51 +01:00
committed by GitHub
parent 198fd7db67
commit 8baedc87a7
11 changed files with 94595 additions and 47 deletions

View File

@@ -35,7 +35,11 @@
"base.repositories":"Repositories metrics",
"base.metadata":"Metadata",
},
options:{},
options:{
"habits.from":100,
"music.playlist":"",
"music.mode":"playlist",
},
},
templates:{
list:templates,
@@ -66,6 +70,10 @@
.flatMap(([key, value]) => key === "base" ? Object.entries(value).map(([key, value]) => [`base.${key}`, value]) : [[key, value]])
.filter(([key, value]) => /^base[.]\w+$/.test(key) ? !value : value)
.map(([key, value]) => `${key}=${+value}`)
//Plugins options
const options = Object.entries(this.plugins.options)
.filter(([key, value]) => `${value}`.length)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
//Template
const template = (this.templates.selected !== templates[0]) ? [`template=${this.templates.selected}`] : []
//Generated url

View File

@@ -21,7 +21,7 @@
<section class="steps">
<div class="step">
<h2>1. Enter your GitHub username</h2>
<input type="text" v-model="user" maxlength="39" placeholder="GitHub username" :disabled="generated.pending">
<input type="text" name="user" v-model="user" maxlength="39" placeholder="GitHub username" :disabled="generated.pending">
</div>
<div class="step">
<h2>2. Select a template</h2>
@@ -48,18 +48,20 @@
{{ plugins.descriptions[plugin] || plugin }}
</label>
</div>
<template v-if="(plugins.enabled.habits)||(plugins.enabled.music)">
<h3>2.3 Configure additional plugins</h3>
<div class="options">
<label v-if="(plugins.enabled.music)&&(plugins.options['music.mode'] === 'playlist')">
Playlist embed link
<input type="text" v-model="plugins.options['music.playlist']" placeholder="https://embed.music.apple.com/en/playlist/">
</label>
<label v-if="plugins.enabled.habits">
Number of events for habits
<input type="number" v-model="plugins.options['habits.from']" min="1" max="100">
</label>
</div>
</template>
</template>
<div class="palette">
Generated metrics use transparency and colors which can be read on both light and dark modes, so everyone can see your stats whatever their preferred color scheme !
<div class="palettes">
<label>
<input type="radio" v-model="palette" value="light"> ☀️ Light mode
</label>
<label>
<input type="radio" v-model="palette" value="dark"> 🌙 Night mode
</label>
</div>
</div>
</div>
<div class="step">
<h2>3. Generate your metrics</h2>
@@ -78,6 +80,17 @@
<template v-if="user">
<button @click="generate" :disabled="generated.pending">{{ generated.pending ? "Working on it :)" : "Generate your metrics !" }}</button>
</template>
<div class="palette">
Generated metrics use transparency and colors which can be read on both light and dark modes, so everyone can see your stats whatever their preferred color scheme !
<div class="palettes">
<label>
<input type="radio" v-model="palette" value="light"> ☀️ Light mode
</label>
<label>
<input type="radio" v-model="palette" value="dark"> 🌙 Night mode
</label>
</div>
</div>
</div>
<div class="step">
<h2>4. Embed these metrics on your GitHub profile</h2>

View File

@@ -56,9 +56,11 @@
input:focus {
outline: none;
}
input[name=user] {
font-size: 1.1rem;
}
input[type=text], select, button {
min-width: 50%;
font-size: 1.1rem;
}
option {
text-align: center;
@@ -133,6 +135,10 @@
.plugins label, .palettes label {
margin: 0 1rem;
}
.options {
display: flex;
flex-direction: column;
}
/* Code snippets */
.code {
display: flex;

View File

@@ -1,5 +1,5 @@
//Setup
export default function ({login, imports, rest, computed, pending, q}, {enabled = false, from:_from = 100} = {}) {
export default function ({login, imports, rest, computed, pending, q}, {enabled = false, from:defaults = 100} = {}) {
//Check if plugin is enabled and requirements are met
if (!enabled)
return computed.plugins.habits = null
@@ -8,9 +8,11 @@
console.debug(`metrics/compute/${login}/plugins > habits`)
//Parameters override
let {"habits.from":from = defaults.from ?? 100} = q
//Events
const from = Math.max(1, Math.min(100, "habits.from" in q ? Number(q["habits.from"])||0 : _from))
console.debug(`metrics/compute/${login}/plugins > habits > events = ${from}`)
from = Math.max(1, Math.min(100, Number(from)))
//Debug
console.debug(`metrics/compute/${login}/plugins > habits > ${JSON.stringify({from})}`)
//Plugin execution
pending.push(new Promise(async solve => {
@@ -27,7 +29,7 @@
//Compute commit hours
const hours = commits.map(({created_at}) => (new Date(created_at)).getHours())
for (const hour of hours)
habits.commits.hours[hour] = (habits.commits.hours[hour] || 0) + 1
habits.commits.hours[hour] = (habits.commits.hours[hour] ?? 0) + 1
//Compute hour with most commits
habits.commits.hour = hours.length ? Object.entries(habits.commits.hours).sort(([an, a], [bn, b]) => b - a).map(([hour, occurence]) => hour)[0] : NaN
}
@@ -42,7 +44,7 @@
edited
.filter(({status}) => status === "fulfilled")
.map(({value}) => value)
.flatMap(files => files.flatMap(file => (file.patch||"").match(/(?<=^[+])((?:\t)|(?: )) /gm)||[]))
.flatMap(files => files.flatMap(file => (file.patch ?? "").match(/(?<=^[+])((?:\t)|(?: )) /gm) ?? []))
.forEach(indent => habits.indents[/^\t/.test(indent) ? "tabs" : "spaces"]++)
//Compute indent style
habits.indents.style = habits.indents.spaces > habits.indents.tabs ? "spaces" : habits.indents.tabs > habits.indents.spaces ? "tabs" : ""

View File

@@ -1,7 +1,13 @@
//Supported providers
const providers = {
apple:"Apple Music",
spotify:"Spotify",
apple:{
name:"Apple Music",
embed:/^https:..embed.music.apple.com.\w+.playlist/,
},
spotify:{
name:"Spotify",
embed:/^https:..open.spotify.com.embed.playlist/,
},
}
//Supported modes
@@ -18,32 +24,39 @@
if (!q.music)
return computed.plugins.music = null
console.debug(`metrics/compute/${login}/plugins > music`)
const raw = {
get provider() { return providers[provider]?.name ?? "" },
get mode() { return modes[mode] ?? "Unconfigured music plugin"},
}
//Parameters override and checks
let {"music.provider":provider = null, "music.mode":mode = null, "music.playlist":playlist = null, "music.limit":limit = 4} = q
//Auto-guess parameters
if ((playlist)&&(mode === null))
mode = "playlist"
if ((playlist)&&(provider === null))
for (const [name, {embed}] of Object.entries(providers))
if (embed.test(playlist))
provider = name
if (!mode)
mode = "recent"
//Provider
const provider = q["music.provider"]||""
if (!(provider in providers))
return computed.plugins.music = {error:provider ? `Unsupported provider "${provider}"` : `Missing provider`, mode:"Unconfigured music plugin"}
console.debug(`metrics/compute/${login}/plugins > music > provider "${provider}"`)
return computed.plugins.music = {...raw, error:provider ? `Unsupported provider "${provider}"` : `Missing provider`}
//Mode
const mode = q["music.mode"]||""
if (!(mode in modes))
return computed.plugins.music = {error:mode ? `Unsupported mode "${mode}"` : `Missing mode`, provider:providers[provider], mode:"Unconfigured music plugin"}
console.debug(`metrics/compute/${login}/plugins > music > mode "${mode}"`)
return computed.plugins.music = {...raw, error:`Unsupported mode "${mode}"`}
//Playlist mode
const playlist = q["music.playlist"]||""
if (mode === "playlist") {
if (!playlist)
return computed.plugins.music = {error:`Missing playlist url`, provider:providers[provider], mode:modes[mode]}
if ((provider === "spotify")&&(!/^https:..open.spotify.com.embed.playlist/.test(playlist)))
return computed.plugins.music = {error:`Unsupported playlist url format`, provider:providers[provider], mode:modes[mode]}
if ((provider === "apple")&&(!/^https:..embed.music.apple.com.\w+.playlist/.test(playlist)))
return computed.plugins.music = {error:`Unsupported playlist url format`, provider:providers[provider], mode:modes[mode]}
console.debug(`metrics/compute/${login}/plugins > music > playlist = ${playlist}`)
return computed.plugins.music = {...raw, error:`Missing playlist url`}
if (!providers[provider].embed.test(playlist))
return computed.plugins.music = {...raw, error:`Unsupported playlist url format`}
}
//Limit
const limit = Math.max(1, Math.min(100, "music.limit" in q ? Number(q["music.limit"])||0 : 4))
console.debug(`metrics/compute/${login}/plugins > music > limit = ${limit}`)
limit = Math.max(1, Math.min(100, Number(limit)))
//Debug
console.debug(`metrics/compute/${login}/plugins > habits > ${JSON.stringify({provider, mode, playlist, limit})}`)
//Plugin execution
pending.push(new Promise(async solve => {
@@ -57,7 +70,8 @@
case "playlist":{
//Start puppeteer and navigate to playlist
console.debug(`metrics/compute/${login}/plugins > music > starting browser`)
const browser = await imports.puppeteer.launch()
const browser = await imports.puppeteer.launch({headless:true, executablePath:process.env.PUPPETEER_BROWSER_PATH, args:["--no-sandbox", "--disable-extensions", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]})
console.debug(`metrics/compute/${login}/plugins > music > loaded ${await browser.version()}`)
const page = await browser.newPage()
console.debug(`metrics/compute/${login}/plugins > music > loading page`)
await page.goto(playlist)
@@ -174,7 +188,7 @@
}
//Save results
console.debug(`metrics/compute/${login}/plugins > music > success`)
computed.plugins.music = {provider:providers[provider], mode:modes[mode], tracks}
computed.plugins.music = {...raw, tracks}
solve()
return
}
@@ -184,12 +198,12 @@
catch (error) {
//Plugin error
if (error.status) {
computed.plugins.music = {provider:providers[provider], mode:modes[mode], error:error.status}
computed.plugins.music = {...raw, error:error.status}
console.debug(`metrics/compute/${login}/plugins > music > error > ${error.status}`)
return solve()
}
//Generic error
computed.plugins.music = {provider:providers[provider], mode:modes[mode], error:`An error occured`}
computed.plugins.music = {...raw, error:`An error occured`}
console.debug(`metrics/compute/${login}/plugins > music > error`)
console.debug(error)
solve()

View File

@@ -9,6 +9,7 @@
+ (!!computed.plugins.habits)*68
+ (!!computed.plugins.languages)*96
+ (!!computed.plugins.music)*64 + (computed.plugins.music ? computed.plugins.music.tracks ? 14+Math.max(0, computed.plugins.music.tracks.length-1)*36 : 0 : 0)
+ Math.max(0, (((!!base.metadata)+(!!base.header)+((!!base.activity)||(!!base.community))+(!!base.repositories)+((!!computed.plugins.habits))+(!!computed.plugins.pagespeed)+(!!computed.plugins.languages)+(!!computed.plugins.music))-1))*4
%>">
<defs><style><%= fonts %></style></defs>

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB