|
| 1 | +//Imports |
| 2 | +import ejs from "ejs" |
| 3 | +import fs from "fs/promises" |
| 4 | +import fss from "fs" |
| 5 | +import paths from "path" |
| 6 | +import url from "url" |
| 7 | +import sgit from "simple-git" |
| 8 | +import metadata from "../source/app/metrics/metadata.mjs" |
| 9 | +import yaml from "js-yaml" |
| 10 | + |
| 11 | +//Mode |
| 12 | +const [mode = "dryrun"] = process.argv.slice(2) |
| 13 | +console.log(`Mode: ${mode}`) |
| 14 | + |
| 15 | +//Paths |
| 16 | +const __metrics = paths.join(paths.dirname(url.fileURLToPath(import.meta.url)), "..") |
| 17 | +const __action = paths.join(__metrics, "source/app/action") |
| 18 | +const __web = paths.join(__metrics, "source/app/web") |
| 19 | +const __readme = paths.join(__metrics, ".github/readme") |
| 20 | +const __templates = paths.join(paths.join(__metrics, "source/templates/")) |
| 21 | +const __plugins = paths.join(paths.join(__metrics, "source/plugins/")) |
| 22 | +const __test_plugins = paths.join(paths.join(__metrics, "tests/plugins")) |
| 23 | +const __test_secrets = paths.join(paths.join(__metrics, "tests/secrets.json")) |
| 24 | + |
| 25 | +//Git setup |
| 26 | +const git = sgit(__metrics) |
| 27 | +const staged = new Set() |
| 28 | + |
| 29 | +//Config and general documentation auto-generation |
| 30 | +for (const step of ["config", "documentation"]) { |
| 31 | + |
| 32 | + //Load plugins metadata |
| 33 | + const {plugins, templates, packaged, descriptor} = await metadata({log:false}) |
| 34 | + |
| 35 | + //Update generated files |
| 36 | + async function update({source, output, options = {}}) { |
| 37 | + //Regenerate file |
| 38 | + console.log(`Generating ${output}`) |
| 39 | + const content = await ejs.renderFile(source, {plugins, templates, packaged, descriptor}, {async:true, ...options}) |
| 40 | + //Save result |
| 41 | + const file = paths.join(__metrics, output) |
| 42 | + await fs.writeFile(file, content) |
| 43 | + //Add to git |
| 44 | + staged.add(file) |
| 45 | + } |
| 46 | + |
| 47 | + //Templating |
| 48 | + switch (step) { |
| 49 | + case "config": |
| 50 | + await update({source:paths.join(__action, "action.yml"), output:"action.yml"}) |
| 51 | + await update({source:paths.join(__web, "settings.example.json"), output:"settings.example.json"}) |
| 52 | + break |
| 53 | + case "documentation": |
| 54 | + await update({source:paths.join(__readme, "README.md"), output:"README.md", options:{root:__readme}}) |
| 55 | + await update({source:paths.join(__readme, "partials/documentation/plugins.md"), output:"source/plugins/README.md"}) |
| 56 | + await update({source:paths.join(__readme, "partials/documentation/templates.md"), output:"source/templates/README.md"}) |
| 57 | + break |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +{ |
| 62 | + //Load plugins metadata and secrets |
| 63 | + const {plugins, templates} = await metadata({log:false, diff:true}) |
| 64 | + const secrets = Object.assign(JSON.parse(`${await fs.readFile(__test_secrets)}`), {$regex:/\$\{\{\s*secrets\.(?<secret>\w+)\s*\}\}/}) |
| 65 | + |
| 66 | + //Get plugin infos |
| 67 | + async function plugin(id) { |
| 68 | + const path = paths.join(__plugins, id) |
| 69 | + const readme = paths.join(path, "README.md") |
| 70 | + const examples = paths.join(path, "examples.yml") |
| 71 | + const tests = paths.join(__test_plugins, `${id}.yml`) |
| 72 | + return { |
| 73 | + readme:{ |
| 74 | + path:readme, |
| 75 | + content:`${await fs.readFile(readme)}` |
| 76 | + }, |
| 77 | + tests:{ |
| 78 | + path:tests |
| 79 | + }, |
| 80 | + examples:fss.existsSync(examples) ? yaml.load(await fs.readFile(examples), "utf8") ?? [] : [], |
| 81 | + options:plugins[id].readme.table |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + //Plugins |
| 86 | + for (const id of Object.keys(plugins)) { |
| 87 | + const {examples, options, readme, tests} = await plugin(id) |
| 88 | + |
| 89 | + //Plugin readme |
| 90 | + await fs.writeFile(readme.path, readme.content |
| 91 | + .replace(/(<!--examples-->)[\s\S]*(<!--\/examples-->)/g, `$1\n${examples.map(({test, prod, ...step}) => ["```yaml", yaml.dump(step), "```"].join("\n")).join("\n")}\n$2`) |
| 92 | + .replace(/(<!--options-->)[\s\S]*(<!--\/options-->)/g, `$1\n${options}\n$2`) |
| 93 | + ) |
| 94 | + console.log(`Generating ${readme.path}`) |
| 95 | + |
| 96 | + //Plugin tests |
| 97 | + await fs.writeFile(tests.path, yaml.dump(examples.map(({prod, test = {}, name = "", ...step}) => { |
| 98 | + if (test.skip) |
| 99 | + return null |
| 100 | + const result = {name:`${plugins[id].name} - ${name}`, ...step, ...test} |
| 101 | + test.with ??= {} |
| 102 | + for (const [k, v] of Object.entries(result.with)) { |
| 103 | + if (k in test.with) |
| 104 | + result.with[k] = test.with[k] |
| 105 | + if (secrets.$regex.test(v)) |
| 106 | + result.with[k] = v.replace(secrets.$regex, secrets[v.match(secrets.$regex)?.groups?.secret]) |
| 107 | + } |
| 108 | + if (!result.with.base) |
| 109 | + delete result.with.base |
| 110 | + delete result.with.filename |
| 111 | + return result |
| 112 | + }).filter(t => t))) |
| 113 | + console.log(`Generating ${tests.path}`) |
| 114 | + |
| 115 | + } |
| 116 | + |
| 117 | +} |
| 118 | + |
| 119 | +//Commit and push |
| 120 | +if (mode === "publish") { |
| 121 | + console.log(`Pushing staged changes: \n${[...staged].map(file => ` - ${file}`).join("\n")}`) |
| 122 | + const gitted = await git |
| 123 | + .addConfig("user.name", "github-actions[bot]") |
| 124 | + .addConfig("user.email", "41898282+github-actions[bot]@users.noreply.github.com") |
| 125 | + .add([...staged]) |
| 126 | + .commit("ci: auto-regenerate files") |
| 127 | + .push("origin", "master") |
| 128 | + console.log(gitted) |
| 129 | +} |
| 130 | +console.log("Success!") |
0 commit comments