Add preview on PRs (#274)

This commit is contained in:
Simon Lecoq
2021-04-28 21:48:28 +02:00
committed by GitHub
parent 5b63ecbd96
commit b4dcf89f67
9 changed files with 121 additions and 5 deletions

78
.github/preview.mjs vendored Normal file
View File

@@ -0,0 +1,78 @@
//Imports
import fs from "fs/promises"
import paths from "path"
import url from "url"
import setup from "../source/app/metrics/setup.mjs"
//Paths
const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..")
const __templates = paths.join(paths.join(__metrics, "source/templates/"))
const __node_modules = paths.join(paths.join(__metrics, "node_modules"))
const __web = paths.join(paths.join(__metrics, "source/app/web/statics"))
const __web_about = paths.join(paths.join(__web, "about"))
const __preview = paths.join(paths.join(__web, "preview"))
const __preview_js = paths.join(__preview, ".js")
const __preview_css = paths.join(__preview, ".css")
const __preview_templates = paths.join(__preview, ".templates")
const __preview_templates_ = paths.join(__preview, ".templates_")
const __preview_about = paths.join(__preview, "about/.statics")
//Extract from web server
const {conf, Templates} = await setup({nosettings:true, log:false})
const templates = Object.entries(Templates).map(([name]) => ({name, enabled:true}))
const metadata = Object.fromEntries(Object.entries(conf.metadata.plugins)
.map(([key, value]) => [key, Object.fromEntries(Object.entries(value).filter(([key]) => ["name", "icon", "categorie", "web", "supports"].includes(key)))])
.map(([key, value]) => [key, key === "core" ? {...value, web:Object.fromEntries(Object.entries(value.web).filter(([key]) => /^config[.]/.test(key)).map(([key, value]) => [key.replace(/^config[.]/, ""), value]))} : value]))
//Directories
await fs.mkdir(__preview, {recursive:true})
await fs.mkdir(__preview_js, {recursive:true})
await fs.mkdir(__preview_css, {recursive:true})
await fs.mkdir(__preview_templates, {recursive:true})
await fs.mkdir(__preview_templates_, {recursive:true})
await fs.mkdir(__preview_about, {recursive:true})
//Web
fs.copyFile(paths.join(__web, "index.html"), paths.join(__preview, "index.html"))
fs.copyFile(paths.join(__web, "favicon.png"), paths.join(__preview, ".favicon.png"))
fs.copyFile(paths.join(__web, "opengraph.png"), paths.join(__preview, ".opengraph.png"))
//Plugins and templates
fs.writeFile(paths.join(__preview, ".plugins"), JSON.stringify(Object.entries(metadata).filter(([_name, {categorie}]) => categorie !== "core").map(([name]) => ({name, enabled:false}))))
fs.writeFile(paths.join(__preview, ".plugins.base"), JSON.stringify(conf.settings.plugins.base.parts))
fs.writeFile(paths.join(__preview, ".plugins.metadata"), JSON.stringify(metadata))
fs.writeFile(paths.join(__preview, ".templates__"), JSON.stringify(templates))
for (const template in conf.templates) {
fs.writeFile(paths.join(__preview_templates_, template), JSON.stringify(conf.templates[template]))
const __partials = paths.join(__templates, template, "partials")
const __preview_partials = paths.join(__preview_templates, template, "partials")
await fs.mkdir(__preview_partials, {recursive:true})
for (const file of await fs.readdir(__partials))
fs.copyFile(paths.join(__partials, file), paths.join(__preview_partials, file))
}
//Styles
fs.copyFile(paths.join(__web, "style.css"), paths.join(__preview_css, "style.css"))
fs.copyFile(paths.join(__web, "style.vars.css"), paths.join(__preview_css, "style.vars.css"))
fs.copyFile(paths.join(__node_modules, "prismjs/themes/prism-tomorrow.css"), paths.join(__preview_css, "style.prism.css"))
//Scripts
fs.writeFile(paths.join(__preview_js, "app.js"), `${await fs.readFile(paths.join(__web, "app.js"))}`)
fs.writeFile(paths.join(__preview_js, "app.placeholder.js"), `${await fs.readFile(paths.join(__web, "app.placeholder.js"))}`)
fs.copyFile(paths.join(__node_modules, "ejs/ejs.min.js"), paths.join(__preview_js, "ejs.min.js"))
fs.copyFile(paths.join(__node_modules, "faker/dist/faker.min.js"), paths.join(__preview_js, "faker.min.js"))
fs.copyFile(paths.join(__node_modules, "axios/dist/axios.min.js"), paths.join(__preview_js, "axios.min.js"))
fs.copyFile(paths.join(__node_modules, "axios/dist/axios.min.map"), paths.join(__preview_js, "axios.min.map"))
fs.copyFile(paths.join(__node_modules, "vue/dist/vue.min.js"), paths.join(__preview_js, "vue.min.js"))
fs.copyFile(paths.join(__node_modules, "vue-prism-component/dist/vue-prism-component.min.js"), paths.join(__preview_js, "vue.prism.min.js"))
fs.copyFile(paths.join(__node_modules, "vue-prism-component/dist/vue-prism-component.min.js.map"), paths.join(__preview_js, "vue-prism-component.min.js.map"))
fs.copyFile(paths.join(__node_modules, "prismjs/prism.js"), paths.join(__preview_js, "prism.min.js"))
fs.copyFile(paths.join(__node_modules, "prismjs/components/prism-yaml.min.js"), paths.join(__preview_js, "prism.yaml.min.js"))
fs.copyFile(paths.join(__node_modules, "prismjs/components/prism-markdown.min.js"), paths.join(__preview_js, "prism.markdown.min.js"))
//Meta
fs.writeFile(paths.join(__preview, ".version"), JSON.stringify(`${conf.package.version}-preview`))
fs.writeFile(paths.join(__preview, ".hosted"), JSON.stringify({by:"metrics", link:"https://github.com/lowlighter/metrics"}))
//About
fs.copyFile(paths.join(__web, "about", "index.html"), paths.join(__preview, "about", "index.html"))
for (const file of await fs.readdir(__web_about))
if (file !== ".statics")
fs.copyFile(paths.join(__web_about, file), paths.join(__preview_about, file))

5
.gitignore vendored
View File

@@ -108,4 +108,7 @@ settings.json
# Community templates # Community templates
source/templates/.community source/templates/.community
source/templates/@* source/templates/@*
# Preview
source/app/web/statics/preview

View File

@@ -8,6 +8,7 @@
"test": "jest --runInBand", "test": "jest --runInBand",
"index": "node .github/index.mjs", "index": "node .github/index.mjs",
"quickstart": "node .github/quickstart/index.mjs", "quickstart": "node .github/quickstart/index.mjs",
"preview": "node .github/preview.mjs",
"linter": "eslint source/**/*.mjs", "linter": "eslint source/**/*.mjs",
"dev": "nodemon source/app/web/index.mjs -e mjs,css,ejs,json", "dev": "nodemon source/app/web/index.mjs -e mjs,css,ejs,json",
"postinstall": "node node_modules/puppeteer/install.js" "postinstall": "node node_modules/puppeteer/install.js"

View File

@@ -28,7 +28,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
Search a GitHub user Search a GitHub user
</h2> </h2>
<small>{{ requests.remaining }} GitHub requests remaining</small> <small :class="{'error-text':!requests.remaining}">{{ requests.remaining }} GitHub requests remaining</small>
<small>Send feedback on <a href="https://github.com/lowlighter/metrics/discussions/229" target="_blank">GitHub discussions</a>!</small> <small>Send feedback on <a href="https://github.com/lowlighter/metrics/discussions/229" target="_blank">GitHub discussions</a>!</small>
</div> </div>
<div class="inputs"> <div class="inputs">
@@ -58,6 +58,10 @@
An error occurred while generating metrics :(<br> An error occurred while generating metrics :(<br>
<small>{{ error.message }}</small> <small>{{ error.message }}</small>
</div> </div>
<small class="warning" v-if="preview">
Metrics insights are rendered by <a href="https://metrics.lecoq.io/">metrics.lecoq.io</a> in preview mode.<br>
Any backend editions won't be reflected but client-side rendering can still be tested.
</small>
</section> </section>
<template v-if="metrics"> <template v-if="metrics">

View File

@@ -122,6 +122,9 @@
url() { url() {
return `${window.location.protocol}//${window.location.host}/about/${this.user}` return `${window.location.protocol}//${window.location.host}/about/${this.user}`
}, },
preview() {
return /-preview$/.test(this.version)
}
}, },
//Data initialization //Data initialization
data:{ data:{

View File

@@ -207,6 +207,10 @@
//Return object //Return object
const configure = Object.fromEntries(entries) const configure = Object.fromEntries(entries)
return Object.keys(configure).length ? configure : null return Object.keys(configure).length ? configure : null
},
//Is in preview mode
preview() {
return /-preview$/.test(this.version)
} }
}, },
//Methods //Methods

View File

@@ -49,8 +49,8 @@
<div class="ui-avatar" :style="{backgroundImage:avatar ? `url(${avatar})` : 'none'}"></div> <div class="ui-avatar" :style="{backgroundImage:avatar ? `url(${avatar})` : 'none'}"></div>
<input type="text" v-model="user" placeholder="Your GitHub username" :disabled="generated.pending" @keyup.enter="(!user)||(generated.pending)||(unusable.length > 0) ? null : generate()"> <input type="text" v-model="user" placeholder="Your GitHub username" :disabled="generated.pending" @keyup.enter="(!user)||(generated.pending)||(unusable.length > 0)||(!requests.remaining) ? null : generate()">
<button @click="generate" :disabled="(!user)||(generated.pending)||(unusable.length > 0)"> <button @click="generate" :disabled="(!user)||(generated.pending)||(unusable.length > 0)||(!requests.remaining)">
<template v-if="generated.pending"> <template v-if="generated.pending">
Generating metrics<span class="loading"></span> Generating metrics<span class="loading"></span>
</template> </template>
@@ -58,7 +58,11 @@
Generate your metrics! Generate your metrics!
</template> </template>
</button> </button>
<small>{{ requests.remaining }} GitHub requests remaining</small> <small :class="{'error-text':!requests.remaining}">{{ requests.remaining }} GitHub requests remaining</small>
<small class="warning" v-if="preview">
Metrics are rendered by <a href="https://metrics.lecoq.io/">metrics.lecoq.io</a> in preview mode.
Any backend editions won't be reflected but client-side rendering can still be tested.
</small>
<div class="warning" v-if="unusable.length"> <div class="warning" v-if="unusable.length">
Metrics cannot be generated because the following plugins are not available on this web instance: {{ unusable.join(", ") }} Metrics cannot be generated because the following plugins are not available on this web instance: {{ unusable.join(", ") }}
</div> </div>

View File

@@ -271,6 +271,10 @@
border-radius: 6px; border-radius: 6px;
} }
.error-text {
color: var(--color-alert-error-text);
}
/* Warning */ /* Warning */
.warning { .warning {
padding: .5rem .75rem; padding: .5rem .75rem;

15
vercel.json Normal file
View File

@@ -0,0 +1,15 @@
{
"rewrites":[
{"source": "/.templates", "destination": "/.templates__"},
{"source": "/.templates/:template", "destination": "/.templates_/:template"},
{"source": "/:login([-\\w]+)", "destination": "https://metrics.lecoq.io/:login"},
{"source": "/:login([-\\w]+)/:repository([-\\w]+)", "destination": "https://metrics.lecoq.io/:login/:repository"},
{"source": "/about/query/:login", "destination": "https://metrics.lecoq.io/about/query/:login"},
{"source": "/.uncache", "destination": "https://metrics.lecoq.io/.uncache"},
{"source": "/.requests", "destination": "https://metrics.lecoq.io/.requests"}
],
"github":{
"silent": true,
"autoJobCancelation": true
}
}