/* This file is part of »munin«. Copyright 2025 'Fenris Wolf' »munin« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »munin« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »munin«. If not, see . */ namespace _munin { /** * @todo outsource */ function execute( command : string, { "log_only": log_only = false, "working_directory": working_directory = null, } : { log_only ?: boolean; working_directory ?: (null | string); } = { } ) : Promise< { code : int; stdout : string; stderr : string; } > { const nm_child_process = require("child_process"); return ( new Promise( (resolve, reject) => { lib_plankton.log._info( "execute", { "details": { "command": command, } } ); if (log_only) { resolve( { "code": 0, "stdout": "", "stderr": "", } ); } else { nm_child_process.exec( command, { "cwd": ((working_directory === null) ? undefined : working_directory), }, (error, stdout, stderr) => { if (error) { reject(error); } else { resolve( { /** * @todo assign correct value */ "code": 0, "stdout": stdout, "stderr": stderr, } ); } } ); } } ) ); } /** */ export async function main( args_raw : Array ) : Promise { // args const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler( { "action": lib_plankton.args.class_argument.positional({ "index": 0, "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, "default": "help", "info": "what to do : fetch | clear | build | deploy", "name": "action", }), "data_path": lib_plankton.args.class_argument.volatile({ "indicators_long": ["data-path"], "indicators_short": ["d"], "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, "default": "ivaldi.json", "info": "path to data file", "name": "data-path", }), "verbosity": lib_plankton.args.class_argument.volatile({ "indicators_long": ["verbosity"], "indicators_short": ["v"], "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, "default": "notice", "info": "error | warning | notice | info | debug", "name": "verbosity", }), "help": lib_plankton.args.class_argument.volatile({ "indicators_long": ["help"], "indicators_short": ["h"], "type": lib_plankton.args.enum_type.boolean, "mode": lib_plankton.args.enum_mode.replace, "default": false, "info": "alias for action 'help'", "name": "help", }), } ); const args : Record = arg_handler.read( lib_plankton.args.enum_environment.cli, args_raw.join(" ") ); // init lib_plankton.log.set_main_logger( [ { "kind": "filtered", "data": { "core": { "kind": "std", "data": { "target": "stdout", "format": { "kind": "human_readable", "data": { } } } }, "predicate": [ [ { "item": { "kind": "level", "data": { "threshold": args.verbosity, } }, } ] ], } }, ] ); // exec if (args.help) { process.stdout.write( arg_handler.generate_help( { "programname": "lixer-event-reminder", "description": "a telegram bot, which sends reminders about upcoming events", "executable": "node build/event-reminder", } ) ); } else { const content : string = await lib_plankton.file.read(args.data_path); const data_raw : any = lib_plankton.json.decode(content); const data : _munin.type_data = { "version": data_raw["version"], "name": data_raw["name"], "libs": (data_raw["libs"] ?? {}), "sources": data_raw["sources"], }; /** * @todo outsource */ const conf = { "directories": { "source": "source", "libs": "libs", "temp": "temp", "build": "build", }, "command_tsc": "tsc", "makefile_path": "/tmp/ivaldi-makefile", }; switch (args.action) { default: { return Promise.reject((new Error("unhandled action: " + args.action))); break; } case "fetch": { for (const lib of data.libs) { switch (lib.kind) { case "plankton": { const directory : string = lib_plankton.string.coin( "{{directory}}/plankton", { "directory": conf.directories.libs, } ); /** * @todo do not use "execute" for that */ await execute( lib_plankton.string.coin( "mkdir -p {{directory}}", { "directory": directory, } ) ); await execute( lib_plankton.string.coin( "ptk bundle node {{modules}}", { "modules": lib.data.modules.join(" "), } ), { "working_directory": directory, } ); break; } case "node": { /** * @todo */ throw (new Error("unhanled lib kind: " + "node")); break; } default: { throw (new Error("unhanled lib kind: " + lib["kind"])); break; } } } return Promise.resolve(undefined); break; } case "clear": { const commands : Array = [ lib_plankton.string.coin( "rm -r -f {{directory}}", { "directory": conf.directories.temp, } ), lib_plankton.string.coin( "rm -r -f {{directory}}", { "directory": conf.directories.build, } ), ]; /** * @todo do not use "execute" for that */ for (const command of commands) { await execute(command); } return Promise.resolve(undefined); break; } case "build": { const has_plankton : boolean = data.libs.some(lib => (lib.kind === "plankton")); const path_plankton_declaration : string = lib_plankton.string.coin( "{{directory_libs}}/plankton/plankton.d.ts", { "directory_libs": conf.directories.libs, } ); const path_plankton_implementation : string = lib_plankton.string.coin( "{{directory_libs}}/plankton/plankton.js", { "directory_libs": conf.directories.libs, } ); const paths_sources : Array = ( data.sources .map( source => lib_plankton.string.coin( "{{directory}}/{{name}}", { "directory": conf.directories.source, "name": source, } ) ) ); const path_unlinked : string = lib_plankton.string.coin( "{{directory_temp}}/{{name}}-unlinked.js", { "directory_temp": conf.directories.temp, "name": data.name, } ); const path_head : string = lib_plankton.string.coin( "{{directory_temp}}/head.js", { "directory_temp": conf.directories.temp, } ); const path_result : string = lib_plankton.string.coin( "{{directory_build}}/{{name}}", { "directory_build": conf.directories.build, "name": data.name, } ); const dependencies_compilation : Array = ( [] .concat(has_plankton ? [path_plankton_declaration] : []) .concat(paths_sources) ); const gnu_make_sheet : lib_plankton.gnu_make.type_sheet = { "rules": [ // root { "kind": "meta", "data": { "phony": true, "name": "_root", "dependencies": [ path_result, ], } }, // compile { "kind": "concrete", "data": { "targets": [ path_unlinked, ], "dependencies": dependencies_compilation, "actions": [ // log lib_plankton.string.coin( "echo '-- {{message}}'", { "message": "compiling …", } ), // mkdir lib_plankton.string.coin( "mkdir -p $(dir $@)", { } ), // command lib_plankton.string.coin( "{{command}} $^ --lib es2020,dom --target es6 --outFile $@", { "command": conf.command_tsc, } ), ], } }, // head { "kind": "concrete", "data": { "targets": [ path_head, ], "dependencies": [ ], "actions": [ // log lib_plankton.string.coin( "echo '-- {{message}}'", { "message": "creating head …", } ), // mkdir lib_plankton.string.coin( "mkdir -p $(dir $@)", { } ), // create lib_plankton.string.coin( "echo '#!/usr/bin/env node' > {{path}}", { "path": path_head, } ), ] } }, // link { "kind": "concrete", "data": { "targets": [ path_result, ], "dependencies": ( [] .concat([path_head]) .concat(has_plankton ? [path_plankton_implementation] : []) .concat([path_unlinked]) ), "actions": [ // log lib_plankton.string.coin( "echo '-- {{message}}'", { "message": "linking …", } ), // mkdir lib_plankton.string.coin( "mkdir -p $(dir $@)", { } ), // command lib_plankton.string.coin( "cat $^ > $@", { } ), // make executable lib_plankton.string.coin( "chmod +x {{path}}", { "path": path_result, } ), ], }, }, ], }; const makefile : string = lib_plankton.gnu_make.render_sheet( gnu_make_sheet, { "break_dependencies": true, "silent_actions": true, } ); await lib_plankton.file.write( conf.makefile_path, makefile ); const command : string = lib_plankton.string.coin( "make -f {{path}}", { "path": conf.makefile_path, } ); await execute(command); return Promise.resolve(undefined); break; } case "deploy": { return Promise.reject(new Error("not yet implemented")); break; } } } } } _munin.main(process.argv.slice(2)) .then(() => {}) .catch((reason) => {process.stderr.write(String(reason) + "\n");}) ;