diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 1228080..7cdd810 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -1737,3 +1737,223 @@ declare var printf: typeof lib_plankton.string.printf; declare var eml_log: any; declare var track_exports: any; declare var make_logger: (prefix: any, current_loglevel: any) => (obj: any, lvl: any) => void; +declare namespace lib_plankton.args { + /** + */ + enum enum_environment { + cli = "cli", + url = "url" + } + /** + */ + enum enum_kind { + positional = "positional", + volatile = "volatile" + } + /** + */ + enum enum_type { + boolean = "boolean", + integer = "int", + float = "float", + string = "string" + } + /** + */ + enum enum_mode { + replace = "replace", + accumulate = "accumulate" + } +} +declare namespace lib_plankton.args { + /** + * @author fenris + */ + class class_argument { + /** + * @author fenris + */ + protected name: string; + /** + * @author fenris + */ + protected kind: enum_kind; + /** + * @author fenris + */ + protected type: enum_type; + /** + * @author fenris + */ + protected mode: enum_mode; + /** + * @author fenris + */ + protected default_: any; + /** + * @author fenris + */ + protected info: string; + /** + * @author fenris + */ + protected parameters: Object; + /** + * @author fenris + */ + protected hidden: boolean; + /** + * @author fenris + */ + constructor({ "name": name, "type": type, "kind": kind, "mode": mode, "default": default_, "info": info, "parameters": parameters, "hidden": hidden, }: { + name: string; + type?: enum_type; + kind?: enum_kind; + mode?: enum_mode; + default?: any; + info?: string; + parameters?: Object; + hidden?: boolean; + }); + /** + * @author fenris + */ + static positional({ "name": name, "type": type, "mode": mode, "default": default_, "info": info, "hidden": hidden, "index": index, }: { + name: string; + type?: enum_type; + mode?: enum_mode; + default?: any; + info?: string; + hidden?: boolean; + index: int; + }): class_argument; + /** + * @author fenris + */ + static volatile({ "name": name, "type": type, "mode": mode, "default": default_, "info": info, "hidden": hidden, "indicators_short": indicators_short, "indicators_long": indicators_long, }: { + name: string; + type?: enum_type; + mode?: enum_mode; + default?: any; + info?: string; + hidden?: boolean; + indicators_short: Array; + indicators_long: Array; + }): class_argument; + /** + * @author fenris + */ + check(): boolean; + /** + * @author fenris + */ + name_get(): string; + /** + * @author fenris + */ + kind_get(): enum_kind; + /** + * @author fenris + */ + type_get(): enum_type; + /** + * @author fenris + */ + mode_get(): enum_mode; + /** + * @author fenris + */ + default_get(): any; + /** + * @author fenris + */ + parameters_get(): Object; + /** + * @author fenris + */ + hidden_get(): boolean; + /** + * @author fenris + */ + toString(): string; + /** + * @author fenris + */ + indicator_main(): string; + /** + * @author fenris + */ + pattern_value(): string; + /** + * @author fenris + */ + extract(raw: string): any; + /** + * @author fenris + */ + assign(data: Object, target: string, raw: string): void; + /** + * @author fenris + */ + make(data: Object, target: string): string; + /** + * @author fenris + */ + generate_help(): string; + } +} +declare namespace lib_plankton.args { + /** + * @author fenris + */ + var verbosity: int; + /** + * @author fenris + * @todo check validity + */ + class class_handler { + /** + * @author fenris + */ + protected arguments_: { + [name: string]: class_argument; + }; + /** + * @author fenris + */ + constructor(arguments_: { + [name: string]: class_argument; + }); + /** + * @author fenris + */ + filter(kind: enum_kind): { + [name: string]: class_argument; + }; + /** + * @author fenris + */ + read(environment: enum_environment, input: string, data?: { + [name: string]: any; + }): { + [name: string]: any; + }; + /** + * @author fenris + * @todo handle if the data object doesn't have the required field or the type is wrong or sth. + */ + write(environment: enum_environment, data: { + [name: string]: any; + }): string; + /** + * @desc manpage-like info-sheet + * @author fenris + */ + generate_help({ "programname": programname, "author": author, "description": description, "executable": executable, }: { + programname?: string; + author?: string; + description?: string; + executable?: string; + }): string; + } +} diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index 5b1ab5a..d9d929c 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -5586,3 +5586,783 @@ var make_logger = (function () { } return make_logger; })(); +/* +This file is part of »bacterio-plankton:args«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:args« 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. + +»bacterio-plankton:args« 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 »bacterio-plankton:args«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var args; + (function (args) { + /** + */ + var enum_environment; + (function (enum_environment) { + enum_environment["cli"] = "cli"; + enum_environment["url"] = "url"; + })(enum_environment = args.enum_environment || (args.enum_environment = {})); + ; + /** + */ + var enum_kind; + (function (enum_kind) { + enum_kind["positional"] = "positional"; + enum_kind["volatile"] = "volatile"; + })(enum_kind = args.enum_kind || (args.enum_kind = {})); + ; + /** + */ + var enum_type; + (function (enum_type) { + enum_type["boolean"] = "boolean"; + enum_type["integer"] = "int"; + enum_type["float"] = "float"; + enum_type["string"] = "string"; + })(enum_type = args.enum_type || (args.enum_type = {})); + ; + /** + */ + var enum_mode; + (function (enum_mode) { + enum_mode["replace"] = "replace"; + enum_mode["accumulate"] = "accumulate"; + })(enum_mode = args.enum_mode || (args.enum_mode = {})); + ; + })(args = lib_plankton.args || (lib_plankton.args = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:args«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:args« 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. + +»bacterio-plankton:args« 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 »bacterio-plankton:args«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var args; + (function (args) { + /* + export enum_mode { + replace = "replace", + accumulate = "accumulate", + }; + */ + /** + * @author fenris + */ + var class_argument = /** @class */ (function () { + /** + * @author fenris + */ + function class_argument(_a) { + var name = _a["name"], _b = _a["type"], type = _b === void 0 ? args.enum_type.string : _b, _c = _a["kind"], kind = _c === void 0 ? args.enum_kind.positional : _c, _d = _a["mode"], mode = _d === void 0 ? args.enum_mode.replace : _d, _e = _a["default"], default_ = _e === void 0 ? null : _e, _f = _a["info"], info = _f === void 0 ? null : _f, _g = _a["parameters"], parameters = _g === void 0 ? {} : _g, _h = _a["hidden"], hidden = _h === void 0 ? false : _h; + this.name = name; + this.type = type; + this.kind = kind; + this.mode = mode; + this.default_ = default_; + this.info = info; + this.parameters = parameters; + this.hidden = hidden; + if (!this.check()) { + throw (new Error("invalid argument-setup")); + } + } + /** + * @author fenris + */ + class_argument.positional = function (_a) { + var name = _a["name"], _b = _a["type"], type = _b === void 0 ? args.enum_type.string : _b, _c = _a["mode"], mode = _c === void 0 ? args.enum_mode.replace : _c, _d = _a["default"], default_ = _d === void 0 ? null : _d, _e = _a["info"], info = _e === void 0 ? null : _e, _f = _a["hidden"], hidden = _f === void 0 ? false : _f, index = _a["index"]; + return (new class_argument({ + "name": name, + "kind": args.enum_kind.positional, + "type": type, + "mode": mode, + "default": default_, + "info": info, + "hidden": hidden, + "parameters": { + "index": index + } + })); + }; + /** + * @author fenris + */ + class_argument.volatile = function (_a) { + var name = _a["name"], _b = _a["type"], type = _b === void 0 ? args.enum_type.string : _b, _c = _a["mode"], mode = _c === void 0 ? args.enum_mode.replace : _c, _d = _a["default"], default_ = _d === void 0 ? null : _d, _e = _a["info"], info = _e === void 0 ? null : _e, _f = _a["hidden"], hidden = _f === void 0 ? false : _f, indicators_short = _a["indicators_short"], indicators_long = _a["indicators_long"]; + return (new class_argument({ + "name": name, + "kind": args.enum_kind.volatile, + "type": type, + "mode": mode, + "default": default_, + "info": info, + "hidden": hidden, + "parameters": { + "indicators_short": indicators_short, + "indicators_long": indicators_long + } + })); + }; + /** + * @author fenris + */ + class_argument.prototype.check = function () { + var _this = this; + return [ + function () { return ((!(_this.kind == args.enum_kind.volatile)) + || + (("indicators_long" in _this.parameters) + && + (_this.parameters["indicators_long"]["length"] >= 0))); }, + ].every(function (condition) { return condition(); }); + }; + /** + * @author fenris + */ + class_argument.prototype.name_get = function () { + return this.name; + }; + /** + * @author fenris + */ + class_argument.prototype.kind_get = function () { + return this.kind; + }; + /** + * @author fenris + */ + class_argument.prototype.type_get = function () { + return this.type; + }; + /** + * @author fenris + */ + class_argument.prototype.mode_get = function () { + return this.mode; + }; + /** + * @author fenris + */ + class_argument.prototype.default_get = function () { + return this.default_; + }; + /** + * @author fenris + */ + class_argument.prototype.parameters_get = function () { + return this.parameters; + }; + /** + * @author fenris + */ + class_argument.prototype.hidden_get = function () { + return this.hidden; + }; + /** + * @author fenris + */ + class_argument.prototype.toString = function () { + return "<".concat(this.name, ">"); + }; + /** + * @author fenris + */ + class_argument.prototype.indicator_main = function () { + if (this.kind === args.enum_kind.volatile) { + return this.parameters["indicators_long"][0]; + } + else { + return null; + } + }; + /** + * @author fenris + */ + class_argument.prototype.pattern_value = function () { + switch (this.type) { + case args.enum_type.boolean: { + return "false|true"; + break; + } + case args.enum_type.integer: { + return "[0-9]+"; + break; + } + case args.enum_type.float: { + return "\\d*(?:\\.\\d+)?"; + break; + } + case args.enum_type.string: { + return "\\S+"; + break; + } + default: { + throw (new Error("unhandled type ".concat(this.type))); + break; + } + } + }; + /** + * @author fenris + */ + class_argument.prototype.extract = function (raw) { + switch (this.type) { + case args.enum_type.boolean: { + return (raw != "false"); + break; + } + case args.enum_type.integer: { + return parseInt(raw); + break; + } + case args.enum_type.float: { + return parseFloat(raw); + break; + } + case args.enum_type.string: { + return raw; + break; + } + default: { + throw (new Error("unhandled type ".concat(this.type))); + break; + } + } + }; + /** + * @author fenris + */ + class_argument.prototype.assign = function (data, target, raw) { + var value = this.extract(raw); + switch (this.mode) { + case args.enum_mode.replace: { + data[target] = value; + break; + } + case args.enum_mode.accumulate: { + /* + if (! (this.name in data)) { + data[this.name] = []; + } + */ + data[target].push(value); + break; + } + default: { + throw (new Error("unhandled mode ".concat(this.mode))); + } + } + }; + /** + * @author fenris + */ + class_argument.prototype.make = function (data, target) { + var value = data[target]; + return value.toString(); + }; + /** + * @author fenris + */ + class_argument.prototype.generate_help = function () { + var _this = this; + var _a, _b, _c, _d; + var output = ""; + { + switch (this.kind) { + case args.enum_kind.positional: { + var line = ""; + line += "\t"; + line += "<".concat(this.name, ">"); + line += "\n"; + output += line; + } + case args.enum_kind.volatile: { + var line = ""; + line += "\t"; + if (this.type === args.enum_type.boolean) { + line += ([] + .concat(((_a = this.parameters["indicators_short"]) !== null && _a !== void 0 ? _a : []).map(function (indicator) { return ("-" + indicator); })) + .concat(((_b = this.parameters["indicators_long"]) !== null && _b !== void 0 ? _b : []).map(function (indicator) { return ("--" + indicator); })) + .join(" | ")); + } + else { + line += ([] + .concat(((_c = this.parameters["indicators_short"]) !== null && _c !== void 0 ? _c : []).map(function (indicator) { return ("-" + indicator + " " + ("<" + _this.name + ">")); })) + .concat(((_d = this.parameters["indicators_long"]) !== null && _d !== void 0 ? _d : []).map(function (indicator) { return ("--" + indicator + "=" + ("<" + _this.name + ">")); })) + .join(" | ")); + } + line += "\n"; + output += line; + } + } + } + { + var line = ""; + line += "\t\t"; + var infotext = ((this.info == null) ? "(no info available)" : this.info); + line += infotext; + if ((this.type != "boolean") && (this.default_ != null)) { + line += "; default: ".concat(this.default_.toString()); + } + line += "\n"; + output += line; + } + return output; + }; + return class_argument; + }()); + args.class_argument = class_argument; + })(args = lib_plankton.args || (lib_plankton.args = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:args«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:args« 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. + +»bacterio-plankton:args« 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 »bacterio-plankton:args«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var args; + (function (args) { + /** + * @author fenris + */ + var settings = { + "environment": { + "cli": { + "symbols": { + "delimiter": " ", + "prefix": "--", + "assignment": "=" + } + }, + "url": { + "symbols": { + "delimiter": "&", + "prefix": "", + "assignment": "=" + } + } + } + }; + /** + * @author fenris + */ + args.verbosity = 0; + /** + * @author fenris + * @todo check validity + */ + var class_handler = /** @class */ (function () { + /** + * @author fenris + */ + function class_handler(arguments_) { + this.arguments_ = arguments_; + } + /** + * @author fenris + */ + class_handler.prototype.filter = function (kind) { + var arguments_ = {}; + for (var _i = 0, _a = Object.entries(this.arguments_); _i < _a.length; _i++) { + var _b = _a[_i], name = _b[0], argument = _b[1]; + if (argument.kind_get() == kind) { + arguments_[name] = argument; + } + } + return arguments_; + }; + /** + * @author fenris + */ + class_handler.prototype.read = function (environment, input, data) { + var _this = this; + if (data === void 0) { data = {}; } + switch (environment) { + case args.enum_environment.cli: + case args.enum_environment.url: { + // default values + { + for (var _i = 0, _a = Object.entries(this.arguments_); _i < _a.length; _i++) { + var _b = _a[_i], name = _b[0], argument = _b[1]; + data[name] = argument.default_get(); + } + } + // preprocessing + { + // short indicators (lil hacky ...) + { + if (environment == args.enum_environment.cli) { + for (var _c = 0, _d = Object.entries(this.filter(args.enum_kind.volatile)); _c < _d.length; _c++) { + var _e = _d[_c], name = _e[0], argument = _e[1]; + // console.info(argument.parameters_get()["indicators_short"].join("|")); + var pattern_from = ""; + { + pattern_from += "(?:^|".concat(settings["environment"][environment]["symbols"]["delimiter"], ")"); + pattern_from += "-".concat(argument.parameters_get()["indicators_short"].join("|")); + pattern_from += "(?:$|".concat(settings["environment"][environment]["symbols"]["delimiter"], ")"); + } + var pattern_to = ""; + { + pattern_to += settings["environment"][environment]["symbols"]["delimiter"]; + pattern_to += settings["environment"][environment]["symbols"]["prefix"]; + pattern_to += argument.indicator_main(); + if (argument.type_get() == args.enum_type.boolean) { + pattern_to += settings["environment"][environment]["symbols"]["delimiter"]; + } + else { + pattern_to += settings["environment"][environment]["symbols"]["assignment"]; + } + } + var result = input.replace(new RegExp(pattern_from, "g"), pattern_to); + lib_plankton.log.debug("lib_args:read:replacing", { + "pattern_from": pattern_from, + "pattern_to": pattern_to, + "input": input, + "result": result + }); + input = result; + } + } + } + lib_plankton.log.debug("lib_args:read:current_input", { + "input": input + }); + } + // parsing + { + var parts = input + .split(settings["environment"][environment]["symbols"]["delimiter"]) + .filter(function (x) { return (x != ""); }); + var index_expected_1 = 0; + parts.forEach(function (part) { + lib_plankton.log.debug("lib_args:read:analyzing", { + "part": part + }); + var found = [ + function () { + lib_plankton.log.debug("lib_args:read:probing_as_volatile", { + "part": part + }); + for (var _i = 0, _a = Object.entries(_this.filter(args.enum_kind.volatile)); _i < _a.length; _i++) { + var _b = _a[_i], name = _b[0], argument = _b[1]; + lib_plankton.log.debug("lib_args:read:probing_as_volatile:trying", { + "part": part, + "argument": argument.toString() + }); + var pattern = ""; + { + var pattern_front = ""; + pattern_front += "".concat(settings["environment"][environment]["symbols"]["prefix"]); + pattern_front += "(?:".concat(argument.parameters_get()["indicators_long"].join("|"), ")"); + pattern += pattern_front; + } + { + var pattern_back = ""; + pattern_back += "".concat(settings["environment"][environment]["symbols"]["assignment"]); + pattern_back += "(".concat(argument.pattern_value(), ")"); + if (argument.type_get() == args.enum_type.boolean) { + pattern_back = "(?:".concat(pattern_back, ")?"); + } + pattern += pattern_back; + } + lib_plankton.log.debug("lib_args:read:probing_as_volatile:pattern", { + "pattern": pattern + }); + var regexp = new RegExp(pattern); + var matching = regexp.exec(part); + lib_plankton.log.debug("lib_args:read:probing_as_volatile:matching", { + "matching": matching + }); + if (matching == null) { + // do nothing + } + else { + argument.assign(data, name, matching[1]); + return true; + } + } + return false; + }, + function () { + lib_plankton.log.debug("lib_args:read:probing_as_positional", { + "part": part + }); + var positional = _this.filter(args.enum_kind.positional); + for (var _i = 0, _a = Object.entries(positional); _i < _a.length; _i++) { + var _b = _a[_i], name = _b[0], argument = _b[1]; + if (argument.parameters_get()['index'] !== index_expected_1) { + // do nothing + } + else { + lib_plankton.log.debug("lib_args:read:probing_as_positional:trying", { + "part": part, + "argument": argument.toString() + }); + var pattern = ""; + { + var pattern_back = ""; + pattern_back += "(".concat(argument.pattern_value(), ")"); + pattern += pattern_back; + } + lib_plankton.log.debug("lib_args:read:probing_as_positional:pattern", { + "pattern": pattern + }); + var regexp = new RegExp(pattern); + var matching = regexp.exec(part); + lib_plankton.log.debug("lib_args:read:probing_as_positional:matching", { + "matching": matching + }); + if (matching == null) { + return false; + } + else { + argument.assign(data, name, matching[1]); + index_expected_1 += 1; + return true; + } + } + } + return false; + }, + ].some(function (x) { return x(); }); + if (!found) { + lib_plankton.log.warning("lib_args:read:could_not_parse", { + "part": part + }); + } + }); + } + return data; + break; + } + default: { + throw (new Error("unhandled environment ".concat(environment))); + break; + } + } + }; + /** + * @author fenris + * @todo handle if the data object doesn't have the required field or the type is wrong or sth. + */ + class_handler.prototype.write = function (environment, data) { + switch (environment) { + case args.enum_environment.cli: { + return (([] + .concat(Object.entries(this.filter(args.enum_kind.volatile)).map(function (_a) { + var name = _a[0], argument = _a[1]; + var values; + switch (argument.mode_get()) { + case args.enum_mode.replace: { + values = [data[argument.name_get()]]; + break; + } + case args.enum_mode.accumulate: { + values = data[argument.name_get()]; + break; + } + } + return (values + .map(function (value) { return ((settings["environment"][environment]["symbols"]["prefix"] + + + argument.parameters_get()["indicators_long"][0]) + + + (settings["environment"][environment]["symbols"]["assignment"] + + + value.toString())); }) + .join(" ")); + })) + .concat(Object.entries(this.filter(args.enum_kind.positional)).map(function (_a) { + var name = _a[0], argument = _a[1]; + var raw = ""; + { + var raw_back = ""; + raw_back += argument.make(data, name); + raw += raw_back; + } + return raw; + }))) + .join(settings["environment"][environment]["symbols"]["delimiter"])); + break; + } + default: { + throw (new Error("unhandled environment ".concat(environment))); + break; + } + } + }; + /** + * @desc manpage-like info-sheet + * @author fenris + */ + class_handler.prototype.generate_help = function (_a) { + var _b = _a["programname"], programname = _b === void 0 ? null : _b, _c = _a["author"], author = _c === void 0 ? null : _c, _d = _a["description"], description = _d === void 0 ? null : _d, _e = _a["executable"], executable = _e === void 0 ? null : _e; + var environment = args.enum_environment.cli; + var output = ""; + { + var section = ""; + { + var line = ""; + line += ""; + line += "INFO"; + line += "\n"; + section += line; + } + { + var line = ""; + line += "\t"; + line += "".concat(programname, " -- ").concat(description); + line += "\n"; + section += line; + } + section += "\n"; + output += section; + } + { + if (author != null) { + var section = ""; + { + var line = ""; + line += ""; + line += "AUTHOR"; + line += "\n"; + section += line; + } + { + var line = ""; + line += "\t"; + line += "".concat(author); + line += "\n"; + section += line; + } + section += "\n"; + output += section; + } + } + { + var section = ""; + { + var line = ""; + line += ""; + line += "SYNOPSIS"; + line += "\n"; + section += line; + } + { + var line = ""; + line += "\t"; + line += executable; + line += settings["environment"][environment]["symbols"]["delimiter"]; + line += Object.entries(this.filter(args.enum_kind.positional)) + .map(function (_a) { + var name = _a[0], argument = _a[1]; + var part = ""; + part += "<".concat(argument.name_get(), ">"); + return part; + }) + .join(settings["environment"][environment]["symbols"]["delimiter"]); + line += settings["environment"][environment]["symbols"]["delimiter"]; + line += Object.entries(this.filter(args.enum_kind.volatile)) + .filter(function (_a) { + var name = _a[0], argument = _a[1]; + return (!argument.hidden_get()); + }) + .map(function (_a) { + var name = _a[0], argument = _a[1]; + var part = ""; + // part += settings["environment"][environment]["symbols"]["prefix"]; + part += "-"; + part += argument.parameters_get()["indicators_short"][0]; + if (argument.type_get() != "boolean") { + /* + part += settings["environment"][environment]["symbols"]["assignment"]; + part += `<${argument.name_get()}>`; + */ + part += " "; + part += "<".concat(argument.name_get(), ">"); + } + part = "[".concat(part, "]"); + return part; + }) + .join(settings["environment"][environment]["symbols"]["delimiter"]); + line += "\n"; + section += line; + } + section += "\n"; + output += section; + } + { + var section = ""; + { + var line = ""; + line += ""; + line += "OPTIONS"; + line += "\n"; + section += line; + } + { + section += (Object.entries(this.arguments_) + .filter(function (_a) { + var name = _a[0], argument = _a[1]; + return (!argument.hidden_get()); + }) + .map(function (_a) { + var name = _a[0], argument = _a[1]; + return argument.generate_help(); + }) + .join("\n")); + } + section += "\n"; + output += section; + } + return output; + }; + return class_handler; + }()); + args.class_handler = class_handler; + })(args = lib_plankton.args || (lib_plankton.args = {})); +})(lib_plankton || (lib_plankton = {})); diff --git a/misc/backup.js b/misc/backup.js deleted file mode 100644 index a05c6f7..0000000 --- a/misc/backup.js +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env nodejs - - -function string_coin( - template, - arguments_ -) -{ - let result = template; - Object.entries(arguments_).forEach( - ([key, value]) => { - result = result.replace(new RegExp("{{" + key + "}}", "g"), value); - } - ); - return result; -} - - -function borg_init( - repository_directory, - { - "encryption": encryption = "none", - } = { - } -) -{ - return string_coin( - "borg init --encryption={{encryption}} {{repository_directory}}\n", - { - "repository_directory": repository_directory, - "encryption": encryption, - } - ); -} - - -function borg_create( - repository_directory, - archive_name, - directories, - { - "compression": compression = "none", - } = { - } -) -{ - return string_coin( - "borg create --compression={{compression}} {{repository_directory}}::{{archive_name}} {{directories}}\n", - { - "repository_directory": repository_directory, - "archive_name": archive_name, - "compression": compression, - "directories": directories.join(" "), - } - ) -} - - -function borg_prune( - repository_directory, - age, - { - "keep_weekly": keep_weekly = null, - "keep_yearly": keep_yearly = null, - } = { - } -) -{ - return string_coin( - "borg prune --keep-within=2w{{macro_keep_weekly}}{{macro_keep_yearly}} {{repository_directory}}\n", - { - "repository_directory": repository_directory, - "keep_within": age, - "macro_keep_weekly": ((keep_weekly === null) ? "" : string_coin(" --keep-weekly={{x}}", {"x": keep_weekly.toFixed(0)})), - "macro_keep_yearly": ((keep_yearly === null) ? "" : string_coin(" --keep-yearly={{x}}", {"x": keep_yearly.toFixed(0)})), - } - ) -} - - -function get_conf( -) -{ - const _fs = require("fs"); - const conf = JSON.parse(_fs.readFileSync("conf.json")); - return conf; -} - - -function get_stamp( -) -{ - const date = (new Date(Date.now())); - return string_coin( - "{{year}}{{month}}{{day}}", - { - "year": date.getFullYear().toFixed(0).padStart(4, "0"), - "month": (date.getMonth()+1).toFixed(0).padStart(2, "0"), - "day": date.getDate().toFixed(0).padStart(2, "0"), - } - ); -} - - -function get_repository_directory( - conf -) -{ - return conf.target.data.repository; -} - - -function init( - conf -) -{ - const repository_directory = get_repository_directory(conf); - if (false) { - process.stdout.write( - string_coin( - "mkdir --parents {{repository_directory}}\n", - { - "repository_directory": repository_directory, - } - ) - ); - } - process.stdout.write( - borg_init( - repository_directory, - { - "encryption": "none", - } - ) - ); -} - - -function run( - conf, - stamp -) -{ - const repository_directory = get_repository_directory(conf); - conf.concerns.forEach( - concern => { - process.stdout.write( - string_coin( - "## {{name}}\n", - { - "name": concern.name, - } - ) - ); - process.stdout.write( - borg_create( - repository_directory, - string_coin( - "{{concern_name}}-{{stamp}}", - { - "concern_name": concern.name, - "stamp": stamp, - } - ), - [concern.source_directory], - { - "compression": conf.target.data.compression, - } - ) - ); - process.stdout.write( - borg_prune( - repository_directory, - "2w", - { - "keep_weekly": 7, - "keep_yearly": 2, - } - ) - ); - process.stdout.write( - "\n" - ); - } - ); -} - - -function main( - args -) -{ - // args - const action = (args.shift() ?? "run"); - - // exec - switch (action) { - case "init": { - const conf = get_conf(); - init(conf); - break; - } - case "run": { - const conf = get_conf(); - const stamp = get_stamp(); - run(conf, stamp); - break; - } - default: { - throw (new Error("unhandled action: " + action)); - break; - } - } -} - - -main(process.argv.slice(2)); - diff --git a/misc/conf-ramsch.mmr.json b/misc/conf-ramsch.mmr.json new file mode 100644 index 0000000..5b6d99c --- /dev/null +++ b/misc/conf-ramsch.mmr.json @@ -0,0 +1,129 @@ +{ + "version": "1", + "target": { + "kind": "borg", + "parameters": { + "repository": "ssh://pv-fensalir-kvasir///home/kvasir/repos/ramsch.sx", + "compression": "lz4" + } + }, + "concerns": [ + { + "active": true, + "name": "espe-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "espe_user", + "password": "852a478ece1b66d04d107ae488dd476a5a43b317f62729e25152e4bfba096cac", + "schema": "espe" + }, + "name": "espe-database" + } + }, + { + "active": true, + "name": "zeitbild-files", + "kind": "files", + "parameters": { + "path": "/opt/zeitbild", + "name": "zeitbild-files" + } + }, + { + "active": true, + "name": "synapse-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "synapse_user", + "password": "b810e5beca5358b8baa344a5a4a9cefa5afbc48f4850b5cbcee32e08b2092dd8", + "schema": "synapse" + }, + "name": "synapse-database" + } + }, + { + "active": true, + "name": "hedgedoc-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "hedgedoc_user", + "password": "a2bbdb2de53523b8099b37013f251546f3d65dbe7a0774fa41af0a4176992fd4", + "schema": "hedgedoc" + }, + "name": "hedgedoc-database" + } + }, + { + "active": true, + "name": "wiki_js-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "wiki_js_user", + "password": "4adc33bd9fe74303c344be46e5916d65182fb218e248fe80452ab3f025b06c64", + "schema": "wiki_js" + }, + "name": "wiki_js-database" + } + }, + { + "active": true, + "name": "vikunja-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "vikunja_user", + "password": "8e54b0ca18020275e4aef1ca0eb5e197e066c065c1864817652a8a39c55402cd", + "schema": "vikunja" + }, + "name": "vikunja-database" + } + }, + { + "active": true, + "name": "forgejo-database", + "kind": "postgresql_dump", + "parameters": { + "credentials": { + "host": "localhost", + "port": 5432, + "username": "forgejo_user", + "password": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa", + "schema": "forgejo" + }, + "name": "forgejo-database" + } + }, + { + "active": true, + "name": "forgejo-files", + "kind": "files", + "parameters": { + "path": "/var/forgejo", + "name": "forgejo-files" + } + }, + { + "active": true, + "name": "ownloud-files", + "kind": "files", + "parameters": { + "path": "/opt/owncloud/.ocis/storage", + "name": "owncloud-files" + } + } + ] +} diff --git a/misc/conf.json b/misc/conf.json deleted file mode 100644 index d1aea2c..0000000 --- a/misc/conf.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "target": { - "kind": "borg", - "data": { - "repository": "ssh://pv-fensalir-kvasir///home/kvasir/repos/ramsch.sx", - "compression": "lz4" - } - }, - "concerns": [ - { - "name": "espe-database", - "source_directory": "/tmp/backup/espe-database" - }, - { - "name": "forgejo-database", - "source_directory": "/tmp/backup/forgejo-database" - }, - { - "name": "forgejo-files", - "source_directory": "/tmp/backup/forgejo-files" - }, - { - "name": "hedgedoc-database", - "source_directory": "/tmp/backup/hedgedoc-database" - }, - { - "name": "owncloud-files", - "source_directory": "/tmp/backup/owncloud-files" - }, - { - "name": "synapse-database", - "source_directory": "/tmp/backup/synapse-database" - }, - { - "name": "vikunja-database", - "source_directory": "/tmp/backup/vikunja-database" - }, - { - "name": "wiki_js-database", - "source_directory": "/tmp/backup/wiki_js-database" - }, - { - "name": "zeitbild-files", - "source_directory": "/tmp/backup/zeitbild-files" - } - ] -} - diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d012b65 --- /dev/null +++ b/readme.md @@ -0,0 +1,4 @@ +- `tools/update-plankton` +- `tools/build` +- `/tmp/mimir/mimir -c misc/conf-ramsch.mmr.json borg-run` + diff --git a/source/conf.ts b/source/conf.ts new file mode 100644 index 0000000..38a6727 --- /dev/null +++ b/source/conf.ts @@ -0,0 +1,295 @@ +namespace _mimir.conf +{ + + /** + */ + export const schema : lib_plankton.conf.type_schema = { + "nullable": false, + "type": "object", + "properties": { + "version": { + "nullable": false, + "type": "string" + }, + "target": { + "anyOf": [ + { + "nullable": false, + "type": "object", + "properties": { + "kind": { + "nullable": false, + "type": "string", + "enum": ["keep"] + }, + "parameters": { + "nullable": false, + "type": "object", + "properties": { + }, + "additionalProperties": false, + "required": [ + ] + }, + }, + "additionalProperties": false, + "required": [ + "kind", + "parameters", + ] + }, + { + "nullable": false, + "type": "object", + "properties": { + "kind": { + "nullable": false, + "type": "string", + "enum": ["borg"] + }, + "parameters": { + "nullable": false, + "type": "object", + "properties": { + "repository": { + "nullable": false, + "type": "string" + }, + "compression": { + "nullable": false, + "type": "string", + "enum": [ + "none", + "lz4", + "zlib", + "lzma", + ], + "default": "lz4" + }, + }, + "required": [ + "repository", + ] + }, + }, + "additionalProperties": false, + "required": [ + "kind", + "parameters", + ] + } + ] + }, + /* + "defaults": { + }, + */ + "concerns": { + "nullable": false, + "type": "array", + "items": { + "anyOf": [ + { + "nullable": false, + "type": "object", + "properties": { + "active": { + "nullable": false, + "type": "boolean", + "default": true + }, + "name": { + "nullable": false, + "type": "string" + }, + "kind": { + "nullable": false, + "type": "string", + "enum": ["files"] + }, + "parameters": { + "nullable": false, + "type": "object", + "properties": { + "path": { + "nullable": false, + "type": "string", + }, + "name": { + "nullable": false, + "type": "string", + }, + }, + "additionalProperties": false, + "required": [ + "path", + "name", + ] + } + }, + "additionalProperties": false, + "required": [ + "name", + "kind", + "parameters", + ] + }, + { + "nullable": false, + "type": "object", + "properties": { + "active": { + "nullable": false, + "type": "boolean", + "default": true + }, + "name": { + "nullable": false, + "type": "string" + }, + "kind": { + "nullable": false, + "type": "string", + "enum": ["postgresql_dump"] + }, + "parameters": { + "nullable": false, + "type": "object", + "properties": { + "credentials": { + "nullable": false, + "type": "object", + "properties": { + "host": { + "nullable": false, + "type": "string", + }, + "port": { + "nullable": false, + "type": "integer", + "default": 5432 + }, + "username": { + "nullable": false, + "type": "string", + }, + "password": { + "nullable": false, + "type": "string", + }, + "schema": { + "nullable": false, + "type": "string", + }, + }, + "additionalProperties": false, + "required": [ + "host", + "username", + "password", + "schema", + ] + }, + "name": { + "nullable": false, + "type": "string", + }, + }, + "additionalProperties": false, + "required": [ + "credentials", + "name", + ] + } + }, + "additionalProperties": false, + "required": [ + "name", + "kind", + "parameters", + ] + }, + ] + } + }, + }, + "additionalProperties": false, + "required": [ + "version", + "target", + "concerns", + ] + }; + + + /** + */ + export type type_target_parameters_keep = { + }; + + + /** + */ + export type type_target_parameters_borg = { + repository : string; + compression : string; + }; + + + /** + */ + export type type_concern_parameters_files = { + name : string; + path : string; + }; + + + /** + */ + export type type_concern_parameters_postgresql_dump = { + credentials : { + host : string; + port : int; + username : string; + password : string; + schema : string; + }; + }; + + + /** + */ + export type type_conf = { + target : ( + { + kind : "keep"; + parameters : type_target_parameters_keep; + } + | + { + kind : "borg"; + parameters : type_target_parameters_borg; + } + ); + concerns : Array< + { + active ?: boolean; + name : string; + } + & + ( + { + kind : "files"; + parameters : type_concern_parameters_files; + } + | + { + kind : "postgresql_dump"; + parameters : type_concern_parameters_postgresql_dump; + } + ) + >; + }; + +} + diff --git a/source/helpers/borg.ts b/source/helpers/borg.ts new file mode 100644 index 0000000..b85d264 --- /dev/null +++ b/source/helpers/borg.ts @@ -0,0 +1,101 @@ + +/** + * @todo consider to outsource to plankton + */ +namespace _mimir.helpers.borg +{ + + /** + */ + export function init( + repository_directory : string, + { + "encryption": encryption = "none", + } : { + encryption ?: string; + } = { + } + ) : string + { + return lib_plankton.string.coin( + "borg init --encryption={{encryption}} {{repository_directory}}", + { + "repository_directory": repository_directory, + "encryption": encryption, + } + ); + } + + + /** + */ + export function create( + repository_directory : string, + archive_name : string, + directories : Array, + { + "compression": compression = "none", + } : { + compression ?: string; + } = { + } + ) : string + { + return lib_plankton.string.coin( + "borg create --compression={{compression}} {{repository_directory}}::{{archive_name}} {{directories}}", + { + "repository_directory": repository_directory, + "archive_name": archive_name, + "compression": compression, + "directories": directories.join(" "), + } + ) + } + + + /** + */ + export function prune( + repository_directory : string, + age : string, + { + "keep_weekly": keep_weekly = null, + "keep_yearly": keep_yearly = null, + } : { + keep_weekly ?: (null | int); + keep_yearly ?: (null | int); + } = { + } + ) : string + { + return lib_plankton.string.coin( + "borg prune --keep-within=2w{{macro_keep_weekly}}{{macro_keep_yearly}} {{repository_directory}}", + { + "repository_directory": repository_directory, + "keep_within": age, + "macro_keep_weekly": ( + (keep_weekly === null) + ? + "" + : + lib_plankton.string.coin( + " --keep-weekly={{x}}", + {"x": keep_weekly.toFixed(0)} + ) + ), + "macro_keep_yearly": ( + (keep_yearly === null) + ? + "" + : + lib_plankton.string.coin( + " --keep-yearly={{x}}", + {"x": keep_yearly.toFixed(0)} + ) + ), + } + ) + } + +} + diff --git a/source/logic/serialization/files.ts b/source/logic/serialization/files.ts new file mode 100644 index 0000000..5db4bd6 --- /dev/null +++ b/source/logic/serialization/files.ts @@ -0,0 +1,31 @@ +namespace _mimir.serialization.files +{ + + /** + */ + export function execute( + parameters : _mimir.conf.type_concern_parameters_files, + directory : string + ) : Array + { + const result : Array = []; + result.push( + lib_plankton.string.coin( + "tar --create --directory={{path}} . > {{target_path}}", + { + "path": parameters.path, + "target_path": lib_plankton.string.coin( + "{{directory}}/files.tar", + { + "directory": directory, + "name": parameters.name, + } + ), + } + ) + ); + return result; + } + +} + diff --git a/source/logic/serialization/postgresql_dump.ts b/source/logic/serialization/postgresql_dump.ts new file mode 100644 index 0000000..384f52d --- /dev/null +++ b/source/logic/serialization/postgresql_dump.ts @@ -0,0 +1,60 @@ +namespace _mimir.serialization.postgresql_dump +{ + + /** + */ + export function execute( + parameters : _mimir.conf.type_concern_parameters_postgresql_dump, + directory : string + ) : Array + { + const result : Array = []; + const password_file_path: string = "${HOME}/.pgpass"; + result.push( + lib_plankton.string.coin( + "echo '{{host}}:{{port}}:{{schema}}:{{username}}:{{password}}' > {{path}} && chmod 0600 {{path}}", + { + "path": password_file_path, + "host": parameters.credentials.host, + "port": parameters.credentials.port.toFixed(0), + "username": parameters.credentials.username, + "password": parameters.credentials.password, + "schema": parameters.credentials.schema, + } + ) + ); + /* + result.push( + lib_plankton.string.coin( + "mkdir --parents {{directory}}", + { + "directory": directory + } + ) + ); + */ + result.push( + lib_plankton.string.coin( + "pg_dump --host={{host}} --port={{port}} --username={{username}} {{schema}} > {{target_path}}", + { + "host": parameters.credentials.host, + "port": parameters.credentials.port.toFixed(0), + "username": parameters.credentials.username, + "schema": parameters.credentials.schema, + "target_path": (directory + "/dump.sql"), + } + ) + ); + result.push( + lib_plankton.string.coin( + "rm {{path}}", + { + "path": password_file_path, + } + ) + ); + return result; + } + +} + diff --git a/source/logic/transfer/borg.ts b/source/logic/transfer/borg.ts new file mode 100644 index 0000000..b3c9715 --- /dev/null +++ b/source/logic/transfer/borg.ts @@ -0,0 +1,44 @@ +namespace _mimir.transfer.borg +{ + + /** + */ + export function execute( + parameters : _mimir.conf.type_target_parameters_borg, + name : string, + stamp : string, + directory : string + ) : Array + { + const result : Array = []; + result.push( + _mimir.helpers.borg.create( + parameters.repository, + lib_plankton.string.coin( + "{{stamp}}-{{name}}", + { + "name": name, + "stamp": stamp, + } + ), + [directory], + { + "compression": parameters.compression, + } + ) + ); + result.push( + _mimir.helpers.borg.prune( + parameters.repository, + "2w", + { + "keep_weekly": 7, + "keep_yearly": 2, + } + ) + ); + return result; + } + +} + diff --git a/source/logic/transfer/keep.ts b/source/logic/transfer/keep.ts new file mode 100644 index 0000000..5660bb0 --- /dev/null +++ b/source/logic/transfer/keep.ts @@ -0,0 +1,19 @@ +namespace _mimir.transfer.keep +{ + + /** + */ + export function execute( + parameters : _mimir.conf.type_target_parameters_keep, + name : string, + stamp : string, + directory : string + ) : Array + { + const result : Array = []; + // do noting + return result; + } + +} + diff --git a/source/main.ts b/source/main.ts index 77dd5af..b688ea4 100644 --- a/source/main.ts +++ b/source/main.ts @@ -1,424 +1,257 @@ - -const _conf_schema : lib_plankton.conf.type_schema = { - "nullable": false, - "type": "object", - "properties": { - "version": { - "nullable": false, - "type": "string" - }, - "target": { - "anyOf": [ - { - "nullable": false, - "type": "object", - "properties": { - "kind": { - "nullable": false, - "type": "string", - "enum": ["plain"] - }, - "parameters": { - "nullable": false, - "type": "object", - "properties": { - "directory": { - "nullable": false, - "type": "string" - }, - }, - "additionalProperties": false, - "required": [ - "directory", - ] - }, - }, - "additionalProperties": false, - "required": [ - "kind", - "parameters", - ] - }, - { - "nullable": false, - "type": "object", - "properties": { - "kind": { - "nullable": false, - "type": "string", - "enum": ["borg"] - }, - "parameters": { - "nullable": false, - "type": "object", - "properties": { - "repository": { - "nullable": false, - "type": "string" - }, - "compression": { - "nullable": false, - "type": "string", - "enum": [ - "none", - "lz4", - "zlib", - "lzma", - ], - "default": "lz4" - }, - }, - "required": [ - "repository", - ] - }, - }, - "additionalProperties": false, - "required": [ - "kind", - "parameters", - ] - } - ] - }, - /* - "defaults": { - }, - */ - "concerns": { - "nullable": false, - "type": "array", - "items": { - "anyOf": [ - { - "nullable": false, - "type": "object", - "properties": { - "active": { - "nullable": false, - "type": "boolean", - "default": true - }, - "name": { - "nullable": false, - "type": "string" - }, - "kind": { - "nullable": false, - "type": "string", - "enum": ["files"] - }, - "parameters": { - "nullable": false, - "type": "object", - "properties": { - "path": { - "nullable": false, - "type": "string", - }, - "name": { - "nullable": false, - "type": "string", - }, - }, - "additionalProperties": false, - "required": [ - "path", - "name", - ] - } - }, - "additionalProperties": false, - "required": [ - "name", - "kind", - "parameters", - ] - }, - { - "nullable": false, - "type": "object", - "properties": { - "active": { - "nullable": false, - "type": "boolean", - "default": true - }, - "name": { - "nullable": false, - "type": "string" - }, - "kind": { - "nullable": false, - "type": "string", - "enum": ["postgresql_dump"] - }, - "parameters": { - "nullable": false, - "type": "object", - "properties": { - "credentials": { - "nullable": false, - "type": "object", - "properties": { - "host": { - "nullable": false, - "type": "string", - }, - "port": { - "nullable": false, - "type": "integer", - "default": 5432 - }, - "username": { - "nullable": false, - "type": "string", - }, - "password": { - "nullable": false, - "type": "string", - }, - "schema": { - "nullable": false, - "type": "string", - }, - }, - "additionalProperties": false, - "required": [ - "host", - "username", - "password", - "schema", - ] - }, - "name": { - "nullable": false, - "type": "string", - }, - }, - "additionalProperties": false, - "required": [ - "credentials", - "name", - ] - } - }, - "additionalProperties": false, - "required": [ - "name", - "kind", - "parameters", - ] - }, - ] - } - }, - }, - "additionalProperties": false, - "required": [ - "version", - "target", - "concerns", - ] -}; - - -/** - */ -function get_stamp(): string +namespace _mimir { - let date: Date = (new Date(Date.now())); - return lib_plankton.string.coin( - // "{{year}}{{month}}{{day}}T{{hour}}{{minute}}{{second}}", - "{{year}}-{{month}}-{{day}}", - { - "year": date.getFullYear().toFixed(0).padStart(4, "0"), - "month": (date.getMonth() + 1).toFixed(0).padStart(2, "0"), - "day": date.getDate().toFixed(0).padStart(2, "0"), - "hour": date.getHours().toFixed(0).padStart(2, "0"), - "minute": date.getMinutes().toFixed(0).padStart(2, "0"), - "second": date.getSeconds().toFixed(0).padStart(2, "0"), - } - ); -} - - -/** - */ -async function main(): Promise -{ - // const conf = lib_plankton.json.decode(await lib_plankton.file.read("conf.json")); - const conf : any = await lib_plankton.conf.load( - _conf_schema, - "conf.json" - ); - const stamp: string = get_stamp(); - - switch (conf.target.kind) { - case "plain": { - const target_directory = (conf.target.parameters.directory/* + "/" + stamp*/); - - let commands: Array = []; - const commands_add : (command: string) => void = (command) => { - commands.push(command); - }; - const commands_apply : () => void = () => { - // TODO - process.stdout.write(commands.join("\n") + "\n"); - }; - - commands_add( - lib_plankton.string.coin( - "mkdir --parents {{directory}}", - { - "directory": target_directory, - } - ) - ); - commands_add( - "" - ); - for await (const concern of conf.concerns) { - if (! concern.active) { - // do nothing - } - else { - commands_add( - lib_plankton.string.coin( - "# {{name}}", - { - "name": concern.name, - "kind": concern.kind, - } - ) - ); - commands_add( - lib_plankton.string.coin( - "echo '-- {{name}}' > /dev/stderr", - { - "name": concern.name, - "kind": concern.kind, - } - ) - ); - switch (concern.kind) { - case "files": { - commands_add( - lib_plankton.string.coin( - "mkdir --parents {{directory}}", - { - "directory": lib_plankton.string.coin( - "{{directory}}/{{name}}", - { - "directory": target_directory, - "name": concern.parameters.name, - } - ), - } - ) - ); - commands_add( - lib_plankton.string.coin( - "tar --create --directory={{path}} . > {{target_path}}", - { - "path": concern.parameters.path, - "target_path": lib_plankton.string.coin( - "{{directory}}/{{name}}/data.tar", - { - "directory": target_directory, - "name": concern.parameters.name, - } - ), - } - ) - ); - break; - } - case "postgresql_dump": { - const password_file_path: string = "${HOME}/.pgpass"; - commands_add( - lib_plankton.string.coin( - "echo '{{host}}:{{port}}:{{schema}}:{{username}}:{{password}}' > {{path}} && chmod 0600 {{path}}", - { - "path": password_file_path, - "host": concern.parameters.credentials.host, - "port": concern.parameters.credentials.port.toFixed(0), - "username": concern.parameters.credentials.username, - "password": concern.parameters.credentials.password, - "schema": concern.parameters.credentials.schema, - } - ) - ); - commands_add( - lib_plankton.string.coin( - "mkdir --parents {{directory}}", - { - "directory": lib_plankton.string.coin( - "{{directory}}/{{name}}", - { - "directory": target_directory, - "name": concern.parameters.name, - } - ), - } - ) - ); - commands_add( - lib_plankton.string.coin( - "pg_dump --host={{host}} --port={{port}} --username={{username}} {{schema}} > {{target_path}}", - { - "host": concern.parameters.credentials.host, - "port": concern.parameters.credentials.port.toFixed(0), - "username": concern.parameters.credentials.username, - "schema": concern.parameters.credentials.schema, - "target_path": lib_plankton.string.coin( - "{{directory}}/{{name}}/data.sql", - { - "directory": target_directory, - "name": concern.parameters.name, - } - ), - } - ) - ); - commands_add( - lib_plankton.string.coin( - "rm {{path}}", - { - "path": password_file_path, - } - ) - ); - break; - } - default: { - throw (new Error("unhandled kind: " + concern.kind)); - break; - } - } - commands_add( - "" - ); - } + /** + */ + function get_stamp() : string + { + const date : Date = (new Date(Date.now())); + return lib_plankton.string.coin( + // "{{year}}{{month}}{{day}}T{{hour}}{{minute}}{{second}}", + "{{year}}-{{month}}-{{day}}", + { + "year": date.getFullYear().toFixed(0).padStart(4, "0"), + "month": (date.getMonth() + 1).toFixed(0).padStart(2, "0"), + "day": date.getDate().toFixed(0).padStart(2, "0"), + "hour": date.getHours().toFixed(0).padStart(2, "0"), + "minute": date.getMinutes().toFixed(0).padStart(2, "0"), + "second": date.getSeconds().toFixed(0).padStart(2, "0"), } - commands_add( - lib_plankton.string.coin( - "echo '{{directory}}'", - { - "directory": target_directory, - } - ) - ); - commands_apply(); - break; - } - default: { - throw (new Error("unhandled target kind: " + conf.target.kind)); - break; - } + ); + } + + + /** + */ + 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": "run", + "name": "action", + "info": lib_plankton.string.coin( + "{{description}}:\n{{options}}\n\t\t", + { + "description": "what to do", + "options": ( + [ + { + "name": "run", + "description": "run" + }, + { + "name": "borg-init", + "description": "borg-init" + }, + { + "name": "borg-run", + "description": "borg-run" + }, + ] + .map( + entry => lib_plankton.string.coin( + "\t\t- {{name}}\n\t\t\t{{description}}\n", + { + "name": entry.name, + "description": entry.description, + } + ) + ) + .join("") + ), + } + ), + }), + "conf_path": lib_plankton.args.class_argument.volatile({ + "indicators_long": ["conf_path"], + "indicators_short": ["c"], + "type": lib_plankton.args.enum_type.string, + "mode": lib_plankton.args.enum_mode.replace, + "default": "conf.json", + "info": "conf_path", + "name": "conf-path", + }), + "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": "help", + "name": "help", + }), + }); + const args : Record = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); + + if (args.help) { + process.stdout.write( + arg_handler.generate_help( + { + "programname": "Mimir", + "description": "backup tool", + "executable": "mimir", + } + ) + + + "\n" + ); + } + else { + const conf : _mimir.conf.type_conf = await lib_plankton.conf.load(_mimir.conf.schema, args["conf_path"]); + // process.stdout.write(JSON.stringify(conf, undefined, "\t")); + const stamp: string = get_stamp(); + /** + * @todo get from configuration + */ + const base_directory : string = "/tmp/mimir"; + + switch (args["action"]) { + case "init": { + return Promise.reject(new Error("not implemented")); + break; + } + case "run": { + for await (const concern of conf.concerns) { + let commands : Array = []; + const commands_add : ((command : string) => void) = (command) => { + commands.push(command); + }; + const commands_apply : (() => void) = () => { + // TODO + process.stdout.write(commands.join("\n") + "\n"); + }; + + if (! concern.active) { + // do nothing + } + else { + const directory : string = (base_directory + "/" + stamp + "/" + concern.name); + + // prepare + { + commands_add( + lib_plankton.string.coin( + "## {{name}}\n", + { + "name": concern.name, + } + ) + ); + commands_add( + lib_plankton.string.coin( + "echo '-- {{name}}' > /dev/stderr", + { + "name": concern.name, + "kind": concern.kind, + } + ) + ); + commands_add( + lib_plankton.string.coin( + "mkdir --parents {{directory}}", + { + "directory": directory, + } + ) + ); + } + + // serialize + { + switch (concern.kind) { + case "files": { + commands = commands.concat( + _mimir.serialization.files.execute( + concern.parameters, + directory + ) + ); + break; + } + case "postgresql_dump": { + commands = commands.concat( + _mimir.serialization.postgresql_dump.execute( + concern.parameters, + directory + ) + ); + break; + } + default: { + throw (new Error("unhandled kind: " + concern["kind"])); + break; + } + } + } + + // transfer + { + switch (conf.target.kind) { + case "keep": { + commands = commands.concat( + _mimir.transfer.keep.execute( + conf.target.parameters, + concern.name, + stamp, + directory + ) + ); + break; + } + case "borg": { + commands = commands.concat( + _mimir.transfer.borg.execute( + conf.target.parameters, + concern.name, + stamp, + directory + ) + ); + break; + } + default: { + throw (new Error("unhandled target kind: " + conf.target["kind"])); + break; + } + } + } + + // clean + { + /** + * @todo + */ + } + + commands_add( + "" + ); + commands_add( + "" + ); + commands_apply(); + } + } + + return Promise.resolve(undefined); + break; + } + default: { + throw (new Error("invalid action: " + args["action"])); + + return Promise.resolve(undefined); + break; + } + } + } + } - - return Promise.resolve(undefined); } -main(); +_mimir.main(process.argv.slice(2)); + diff --git a/tools/makefile b/tools/makefile index 81af099..516b6c0 100644 --- a/tools/makefile +++ b/tools/makefile @@ -43,6 +43,12 @@ ${dir_temp}/head.js: ${dir_temp}/mimir-raw.js: \ ${dir_lib}/plankton/plankton.d.ts \ + ${dir_source}/helpers/borg.ts \ + ${dir_source}/conf.ts \ + ${dir_source}/logic/serialization/files.ts \ + ${dir_source}/logic/serialization/postgresql_dump.ts \ + ${dir_source}/logic/transfer/keep.ts \ + ${dir_source}/logic/transfer/borg.ts \ ${dir_source}/main.ts @ ${cmd_log} "compile …" @ ${cmd_mkdir} $(dir $@) diff --git a/tools/update-plankton b/tools/update-plankton index 30591b4..04e904a 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -11,6 +11,7 @@ modules="${modules} file" modules="${modules} call" modules="${modules} json" modules="${modules} string" +modules="${modules} args" ## exec