diff --git a/.github/examples.mjs b/.github/examples.mjs deleted file mode 100644 index 16ca37f2..00000000 --- a/.github/examples.mjs +++ /dev/null @@ -1,82 +0,0 @@ -//Imports -import fs from "fs/promises" -import fss from "fs" -import paths from "path" -import url from "url" -import metadata from "../source/app/metrics/metadata.mjs" -import yaml from "js-yaml" - -//Paths -const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") -const __templates = paths.join(paths.join(__metrics, "source/templates/")) -const __plugins = paths.join(paths.join(__metrics, "source/plugins/")) -const __test_plugins = paths.join(paths.join(__metrics, "tests/plugins")) - -//Load plugins metadata -const {plugins, templates} = await metadata({log:false, diff:true}) - -async function plugin(id) { - const path = paths.join(__plugins, id) - const readme = paths.join(path, "README.md") - const examples = paths.join(path, "examples.yml") - const tests = paths.join(__test_plugins, `${id}.yml`) - return { - readme:{ - path:readme, - content:`${await fs.readFile(readme)}` - }, - tests:{ - path:tests - }, - examples:fss.existsSync(examples) ? yaml.load(await fs.readFile(examples), "utf8") ?? [] : [], - options:plugins[id].readme.table - } -} - -const secrets = { - $regex:/\$\{\{\s*secrets\.(?\w+)\s*\}\}/, - METRICS_TOKEN:"MOCKED_TOKEN", - METRICS_BOT_TOKEN:"MOCKED_TOKEN", - PAGESPEED_TOKEN:"MOCKED_TOKEN", - SPOTIFY_TOKENS:"MOCKED_CLIENT_ID, MOCKED_CLIENT_SECRET, MOCKED_REFRESH_TOKEN", - YOUTUBE_MUSIC_TOKENS:"SAPISID=MOCKED_COOKIE; OTHER_PARAM=OTHER_VALUE;", - LASTFM_TOKEN:"MOCKED_TOKEN", - NIGHTSCOUT_URL:"https://testapp.herokuapp.com/", - WAKATIME_TOKEN:"MOCKED_TOKEN", - WAKATIME_TOKEN_NO_PROJECTS:"MOCKED_TOKEN_NO_PROJECTS", - TWITTER_TOKEN:"MOCKED_TOKEN", - RAPIDAPI_TOKEN:"MOCKED_TOKEN", -} - -//Plugins -for (const id of Object.keys(plugins)) { - const {examples, options, readme, tests} = await plugin(id) - - //Plugin readme - await fs.writeFile(readme.path, readme.content - .replace(/()[\s\S]*()/g, `$1\n${examples.map(({test, prod, ...step}) => ["```yaml", yaml.dump(step), "```"].join("\n")).join("\n")}\n$2`) - .replace(/()[\s\S]*()/g, `$1\n${options}\n$2`) - ) - //Plugin tests - await fs.writeFile(tests.path, yaml.dump(examples.map(({prod, test = {}, name = "", ...step}) => { - if (test.skip) - return null - const result = {name:`${plugins[id].name} - ${name}`, ...step, ...test} - test.with ??= {} - for (const [k, v] of Object.entries(result.with)) { - if (k in test.with) - result.with[k] = test.with[k] - if (secrets.$regex.test(v)) - result.with[k] = v.replace(secrets.$regex, secrets[v.match(secrets.$regex)?.groups?.secret]) - } - if (!result.with.base) - delete result.with.base - delete result.with.filename - return result - }).filter(t => t))) - -} - -//Templates - -//Workflow \ No newline at end of file diff --git a/.github/index.mjs b/.github/index.mjs deleted file mode 100644 index 2953b9b1..00000000 --- a/.github/index.mjs +++ /dev/null @@ -1,65 +0,0 @@ -//Imports -import ejs from "ejs" -import fs from "fs/promises" -import paths from "path" -import url from "url" -import sgit from "simple-git" -import metadata from "../source/app/metrics/metadata.mjs" - -//Mode -const [mode = "dryrun"] = process.argv.slice(2) -console.log(`Mode: ${mode}`) - -//Paths -const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") -const __action = paths.join(__metrics, "source/app/action") -const __web = paths.join(__metrics, "source/app/web") -const __readme = paths.join(__metrics, ".github/readme") - -//Git setup -const git = sgit(__metrics) -const staged = new Set() - -for (const step of ["config", "documentation"]) { - -//Load plugins metadata -const {plugins, templates, packaged, descriptor} = await metadata({log:false}) - - //Update generated files - async function update({source, output, options = {}}) { - //Regenerate file - console.log(`Generating ${output}`) - const content = await ejs.renderFile(source, {plugins, templates, packaged, descriptor}, {async:true, ...options}) - //Save result - const file = paths.join(__metrics, output) - await fs.writeFile(file, content) - //Add to git - staged.add(file) - } - - //Templating - switch (step) { - case "config": - await update({source:paths.join(__action, "action.yml"), output:"action.yml"}) - await update({source:paths.join(__web, "settings.example.json"), output:"settings.example.json"}) - break - case "documentation": - await update({source:paths.join(__readme, "README.md"), output:"README.md", options:{root:__readme}}) - await update({source:paths.join(__readme, "partials/documentation/plugins.md"), output:"source/plugins/README.md"}) - await update({source:paths.join(__readme, "partials/documentation/templates.md"), output:"source/templates/README.md"}) - break - } -} - -//Commit and push -if (mode === "publish") { - console.log(`Pushing staged changes: \n${[...staged].map(file => ` - ${file}`).join("\n")}`) - const gitted = await git - .addConfig("user.name", "github-actions[bot]") - .addConfig("user.email", "41898282+github-actions[bot]@users.noreply.github.com") - .add([...staged]) - .commit("ci: auto-regenerate files") - .push("origin", "master") - console.log(gitted) -} -console.log("Success!") diff --git a/.github/scripts/build.mjs b/.github/scripts/build.mjs new file mode 100644 index 00000000..5d87beb4 --- /dev/null +++ b/.github/scripts/build.mjs @@ -0,0 +1,130 @@ +//Imports +import ejs from "ejs" +import fs from "fs/promises" +import fss from "fs" +import paths from "path" +import url from "url" +import sgit from "simple-git" +import metadata from "../source/app/metrics/metadata.mjs" +import yaml from "js-yaml" + +//Mode +const [mode = "dryrun"] = process.argv.slice(2) +console.log(`Mode: ${mode}`) + +//Paths +const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") +const __action = paths.join(__metrics, "source/app/action") +const __web = paths.join(__metrics, "source/app/web") +const __readme = paths.join(__metrics, ".github/readme") +const __templates = paths.join(paths.join(__metrics, "source/templates/")) +const __plugins = paths.join(paths.join(__metrics, "source/plugins/")) +const __test_plugins = paths.join(paths.join(__metrics, "tests/plugins")) +const __test_secrets = paths.join(paths.join(__metrics, "tests/secrets.json")) + +//Git setup +const git = sgit(__metrics) +const staged = new Set() + +//Config and general documentation auto-generation +for (const step of ["config", "documentation"]) { + + //Load plugins metadata + const {plugins, templates, packaged, descriptor} = await metadata({log:false}) + + //Update generated files + async function update({source, output, options = {}}) { + //Regenerate file + console.log(`Generating ${output}`) + const content = await ejs.renderFile(source, {plugins, templates, packaged, descriptor}, {async:true, ...options}) + //Save result + const file = paths.join(__metrics, output) + await fs.writeFile(file, content) + //Add to git + staged.add(file) + } + + //Templating + switch (step) { + case "config": + await update({source:paths.join(__action, "action.yml"), output:"action.yml"}) + await update({source:paths.join(__web, "settings.example.json"), output:"settings.example.json"}) + break + case "documentation": + await update({source:paths.join(__readme, "README.md"), output:"README.md", options:{root:__readme}}) + await update({source:paths.join(__readme, "partials/documentation/plugins.md"), output:"source/plugins/README.md"}) + await update({source:paths.join(__readme, "partials/documentation/templates.md"), output:"source/templates/README.md"}) + break + } +} + +{ + //Load plugins metadata and secrets + const {plugins, templates} = await metadata({log:false, diff:true}) + const secrets = Object.assign(JSON.parse(`${await fs.readFile(__test_secrets)}`), {$regex:/\$\{\{\s*secrets\.(?\w+)\s*\}\}/}) + + //Get plugin infos + async function plugin(id) { + const path = paths.join(__plugins, id) + const readme = paths.join(path, "README.md") + const examples = paths.join(path, "examples.yml") + const tests = paths.join(__test_plugins, `${id}.yml`) + return { + readme:{ + path:readme, + content:`${await fs.readFile(readme)}` + }, + tests:{ + path:tests + }, + examples:fss.existsSync(examples) ? yaml.load(await fs.readFile(examples), "utf8") ?? [] : [], + options:plugins[id].readme.table + } + } + + //Plugins + for (const id of Object.keys(plugins)) { + const {examples, options, readme, tests} = await plugin(id) + + //Plugin readme + await fs.writeFile(readme.path, readme.content + .replace(/()[\s\S]*()/g, `$1\n${examples.map(({test, prod, ...step}) => ["```yaml", yaml.dump(step), "```"].join("\n")).join("\n")}\n$2`) + .replace(/()[\s\S]*()/g, `$1\n${options}\n$2`) + ) + console.log(`Generating ${readme.path}`) + + //Plugin tests + await fs.writeFile(tests.path, yaml.dump(examples.map(({prod, test = {}, name = "", ...step}) => { + if (test.skip) + return null + const result = {name:`${plugins[id].name} - ${name}`, ...step, ...test} + test.with ??= {} + for (const [k, v] of Object.entries(result.with)) { + if (k in test.with) + result.with[k] = test.with[k] + if (secrets.$regex.test(v)) + result.with[k] = v.replace(secrets.$regex, secrets[v.match(secrets.$regex)?.groups?.secret]) + } + if (!result.with.base) + delete result.with.base + delete result.with.filename + return result + }).filter(t => t))) + console.log(`Generating ${tests.path}`) + + } + +} + +//Commit and push +if (mode === "publish") { + console.log(`Pushing staged changes: \n${[...staged].map(file => ` - ${file}`).join("\n")}`) + const gitted = await git + .addConfig("user.name", "github-actions[bot]") + .addConfig("user.email", "41898282+github-actions[bot]@users.noreply.github.com") + .add([...staged]) + .commit("ci: auto-regenerate files") + .push("origin", "master") + console.log(gitted) +} +console.log("Success!") diff --git a/.github/markdown_example.mjs b/.github/scripts/markdown_example.mjs similarity index 100% rename from .github/markdown_example.mjs rename to .github/scripts/markdown_example.mjs diff --git a/.github/preview.mjs b/.github/scripts/preview.mjs similarity index 100% rename from .github/preview.mjs rename to .github/scripts/preview.mjs diff --git a/.github/quickstart/index.mjs b/.github/scripts/quickstart/index.mjs similarity index 92% rename from .github/quickstart/index.mjs rename to .github/scripts/quickstart/index.mjs index c038e920..237d6659 100644 --- a/.github/quickstart/index.mjs +++ b/.github/scripts/quickstart/index.mjs @@ -8,8 +8,8 @@ import ejs from "ejs" const [mode, name] = process.argv.slice(2) //Paths -const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "../..") -const __quickstart = paths.join(__metrics, ".github/quickstart") +const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "../../..") +const __quickstart = paths.join(__metrics, ".github/scripts/quickstart") //Check arguments if ((!mode)||(!name)) diff --git a/.github/quickstart/plugin/README.md b/.github/scripts/quickstart/plugin/README.md similarity index 100% rename from .github/quickstart/plugin/README.md rename to .github/scripts/quickstart/plugin/README.md diff --git a/.github/quickstart/plugin/tests.yml b/.github/scripts/quickstart/plugin/examples.yml similarity index 51% rename from .github/quickstart/plugin/tests.yml rename to .github/scripts/quickstart/plugin/examples.yml index 01eb2c39..ababa457 100644 --- a/.github/quickstart/plugin/tests.yml +++ b/.github/scripts/quickstart/plugin/examples.yml @@ -1,5 +1,7 @@ - name: <%= `${name.charAt(0).toLocaleUpperCase()}${name.substring(1)}` %> plugin (default) uses: lowlighter/metrics@latest with: - token: MOCKED_TOKEN - plugin_<%= name %>: yes \ No newline at end of file + filename: metrics.plugin.<%= name %>.svg + token: ${{ secrets.METRICS_TOKEN }} + base: "" + plugin_<%= name %>: yes diff --git a/.github/quickstart/plugin/index.mjs b/.github/scripts/quickstart/plugin/index.mjs similarity index 100% rename from .github/quickstart/plugin/index.mjs rename to .github/scripts/quickstart/plugin/index.mjs diff --git a/.github/quickstart/plugin/metadata.yml b/.github/scripts/quickstart/plugin/metadata.yml similarity index 100% rename from .github/quickstart/plugin/metadata.yml rename to .github/scripts/quickstart/plugin/metadata.yml diff --git a/.github/quickstart/template/README.md b/.github/scripts/quickstart/template/README.md similarity index 100% rename from .github/quickstart/template/README.md rename to .github/scripts/quickstart/template/README.md diff --git a/.github/quickstart/template/image.svg b/.github/scripts/quickstart/template/image.svg similarity index 100% rename from .github/quickstart/template/image.svg rename to .github/scripts/quickstart/template/image.svg diff --git a/.github/quickstart/template/metadata.yml b/.github/scripts/quickstart/template/metadata.yml similarity index 100% rename from .github/quickstart/template/metadata.yml rename to .github/scripts/quickstart/template/metadata.yml diff --git a/.github/quickstart/template/partials/_.json b/.github/scripts/quickstart/template/partials/_.json similarity index 100% rename from .github/quickstart/template/partials/_.json rename to .github/scripts/quickstart/template/partials/_.json diff --git a/.github/release.mjs b/.github/scripts/release.mjs similarity index 98% rename from .github/release.mjs rename to .github/scripts/release.mjs index 55727a1e..71c147fc 100644 --- a/.github/release.mjs +++ b/.github/scripts/release.mjs @@ -5,7 +5,7 @@ import url from "url" import sgit from "simple-git" //Git setup -const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") +const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "../..") const git = sgit(__metrics) //Setup octokit diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 68c50b13..4dc810ba 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -115,7 +115,7 @@ jobs: - name: Setup metrics run: npm ci - name: Publish rebuild metrics indexes - run: npm run index -- publish + run: npm run build -- publish # Rebase main branch on master update-main: @@ -269,7 +269,7 @@ jobs: - name: Setup metrics run: npm ci - name: Publish release - run: node .github/release.mjs + run: node .github/scripts/release.mjs env: GITHUB_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} diff --git a/Dockerfile b/Dockerfile index 7dd6948f..938b10c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN chmod +x /metrics/source/app/action/index.mjs \ && rm -rf /var/lib/apt/lists/* \ # Install node modules and rebuild indexes && npm ci \ - && npm run index + && npm run build # Environment variables ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true diff --git a/package.json b/package.json index db293a47..1e586272 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "start": "node source/app/web/index.mjs", "test": "jest --runInBand", - "index": "node .github/index.mjs", - "quickstart": "node .github/quickstart/index.mjs", - "preview": "node .github/preview.mjs", + "build": "node .github/scripts/build.mjs", + "quickstart": "node .github/scripts/quickstart/index.mjs", + "preview": "node .github/scripts/preview.mjs", "linter": "eslint source/**/*.mjs --quiet", "format": "eslint source/**/*.mjs --fix", "dev": "nodemon source/app/web/index.mjs -e mjs,css,ejs,json", diff --git a/tests/ci.test.js b/tests/ci.test.js index 62b0b80e..523b77f4 100644 --- a/tests/ci.test.js +++ b/tests/ci.test.js @@ -27,11 +27,8 @@ describe("Check files editions (checkout your files if needed)", () => { ".github/readme/partials/documentation/compatibility.md", ".github/readme/partials/introduction.md", ".github/workflows/*", + ".github/scripts/*", ".github/FUNDING.yml", - ".github/examples.mjs", - ".github/index.mjs", - ".github/release.mjs", - ".github/markdown_example.mjs", ".github/architecture.svg", "LICENSE", "ARCHITECTURE.md", diff --git a/tests/secrets.json b/tests/secrets.json new file mode 100644 index 00000000..0d5936f2 --- /dev/null +++ b/tests/secrets.json @@ -0,0 +1,13 @@ +{ + "METRICS_TOKEN":"MOCKED_TOKEN", + "METRICS_BOT_TOKEN":"MOCKED_TOKEN", + "PAGESPEED_TOKEN":"MOCKED_TOKEN", + "SPOTIFY_TOKENS":"MOCKED_CLIENT_ID, MOCKED_CLIENT_SECRET, MOCKED_REFRESH_TOKEN", + "YOUTUBE_MUSIC_TOKENS":"SAPISID=MOCKED_COOKIE; OTHER_PARAM=OTHER_VALUE;", + "LASTFM_TOKEN":"MOCKED_TOKEN", + "NIGHTSCOUT_URL":"https://testapp.herokuapp.com/", + "WAKATIME_TOKEN":"MOCKED_TOKEN", + "WAKATIME_TOKEN_NO_PROJECTS":"MOCKED_TOKEN_NO_PROJECTS", + "TWITTER_TOKEN":"MOCKED_TOKEN", + "RAPIDAPI_TOKEN":"MOCKED_TOKEN" +} \ No newline at end of file