367 lines
9.2 KiB
TypeScript
367 lines
9.2 KiB
TypeScript
/**
|
|
* @author Martin Karkowski
|
|
* @email m.karkowski@zema.de
|
|
* @create date 2021-01-08 15:16:51
|
|
* @modify date 2021-02-09 10:18:08
|
|
* @desc [description]
|
|
*/
|
|
|
|
import { ArgumentParser } from "argparse";
|
|
import { readFileSync } from "fs";
|
|
import * as handlebars from "handlebars";
|
|
import * as inquirer from "inquirer";
|
|
import { camelCase } from "lodash";
|
|
import { type } from "os";
|
|
import { join } from "path";
|
|
import { validLayers } from "../communication/getLayer";
|
|
import { createFile } from "../helpers/fileMethods";
|
|
import { IConfigFile } from "../loader/loadPackages";
|
|
import { getNopeLogger } from "../logger/getLogger";
|
|
import { LoggerLevels } from "../logger/nopeLogger";
|
|
|
|
// Define the Main Function.
|
|
// This function is used as cli tool.
|
|
export async function createService(
|
|
additionalArguments: {
|
|
help: string;
|
|
type: "string" | "number";
|
|
name: string | string;
|
|
defaultValue?: any;
|
|
}[] = []
|
|
) {
|
|
// Flag, to determine the OS
|
|
const runningInLinux = type() === "Linux";
|
|
|
|
const parser = new ArgumentParser({
|
|
version: "1.0.0",
|
|
addHelp: true,
|
|
description: "Command Line interface, which enables creating services."
|
|
});
|
|
|
|
for (const arg of additionalArguments) {
|
|
parser.addArgument(arg.name, {
|
|
help: arg.help,
|
|
defaultValue: arg.defaultValue,
|
|
type: arg.type
|
|
});
|
|
}
|
|
|
|
parser.addArgument(["-f", "--file"], {
|
|
help: "File containing containing the package definitions.",
|
|
defaultValue: "./config/settings.json",
|
|
type: "string",
|
|
dest: "file"
|
|
});
|
|
|
|
parser.addArgument(["-o", "--outputDir"], {
|
|
help: "File containing containing the package definitions.",
|
|
defaultValue: "./services/",
|
|
type: "string",
|
|
dest: "outputDir"
|
|
});
|
|
|
|
const args = parser.parseArgs();
|
|
|
|
// Define a Logger
|
|
const logger = getNopeLogger("Setup-CLI");
|
|
|
|
try {
|
|
logger.info("Reading config File");
|
|
|
|
const data: IConfigFile = JSON.parse(
|
|
readFileSync(args.file).toString("utf-8")
|
|
);
|
|
|
|
const config: IConfigFile = {
|
|
functions: [],
|
|
packages: []
|
|
};
|
|
|
|
const modeSelection = [
|
|
{
|
|
type: "confirm",
|
|
name: "useFunction",
|
|
message: "Do you want to share Functions",
|
|
default: false
|
|
},
|
|
{
|
|
type: "confirm",
|
|
name: "usePackages",
|
|
message: "Do you want to run instances provided in a package?",
|
|
default: true
|
|
},
|
|
{
|
|
type: "confirm",
|
|
name: "detailInstances",
|
|
message:
|
|
"Do you want to detail, which instance should be hosted as Service?",
|
|
default: true
|
|
},
|
|
{
|
|
type: "confirm",
|
|
name: "runExtra",
|
|
message: "Do you want to run every selected option as single Service?",
|
|
default: true
|
|
},
|
|
{
|
|
type: "confirm",
|
|
name: "runExtra",
|
|
message: "Do you want to run every selected option as single Service?",
|
|
default: true
|
|
},
|
|
{
|
|
type: "list",
|
|
name: "layer",
|
|
message: "Select the Communication Layer to use",
|
|
choices: Object.getOwnPropertyNames(validLayers)
|
|
},
|
|
{
|
|
type: "list",
|
|
name: "logLevel",
|
|
message: "Select the Level of the Logger, to use.",
|
|
choices: LoggerLevels
|
|
}
|
|
];
|
|
|
|
const result = await inquirer.prompt(modeSelection);
|
|
|
|
if (result.useFunction) {
|
|
config.functions = (
|
|
await inquirer.prompt([
|
|
{
|
|
type: "checkbox",
|
|
name: "functions",
|
|
message: "Select/Deselect Functions, that should be hosted",
|
|
choices: data.functions.map((func) => {
|
|
return {
|
|
name: func.path,
|
|
value: func,
|
|
checked: false
|
|
};
|
|
})
|
|
}
|
|
])
|
|
).functions;
|
|
}
|
|
|
|
if (result.usePackages) {
|
|
// Determine the Packages which should be hosted.
|
|
config.packages = (
|
|
await inquirer.prompt([
|
|
{
|
|
type: "checkbox",
|
|
name: "packages",
|
|
message: "Select/Deselect Packages, that should be hosted:",
|
|
choices: data.packages.map((p) => {
|
|
return {
|
|
name: p.nameOfPackage,
|
|
value: p,
|
|
checked: true
|
|
};
|
|
})
|
|
}
|
|
])
|
|
).packages;
|
|
|
|
if (result.detailInstances) {
|
|
// Iterate over the Pacakges
|
|
for (const p of config.packages) {
|
|
// Determine the Instances to keep:
|
|
p.defaultInstances = (
|
|
await inquirer.prompt([
|
|
{
|
|
type: "checkbox",
|
|
name: "instances",
|
|
message: "Select/Deselect Instances, that should be hosted:",
|
|
choices: p.defaultInstances.map((instance) => {
|
|
return {
|
|
name: instance.options.identifier,
|
|
value: instance,
|
|
checked: true
|
|
};
|
|
})
|
|
}
|
|
])
|
|
).instances;
|
|
}
|
|
}
|
|
}
|
|
|
|
const serviceTemplates = runningInLinux
|
|
? [
|
|
{
|
|
name: "nopeService.service",
|
|
render: handlebars.compile(
|
|
readFileSync(
|
|
join(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"lib",
|
|
"templates",
|
|
"linux.service.handlebars"
|
|
)
|
|
).toString("utf-8")
|
|
)
|
|
}
|
|
]
|
|
: [
|
|
{
|
|
name: "index.js",
|
|
render: handlebars.compile(
|
|
readFileSync(
|
|
join(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"lib",
|
|
"templates",
|
|
"index.js.handlebars"
|
|
)
|
|
).toString("utf-8")
|
|
)
|
|
},
|
|
{
|
|
name: "service.js",
|
|
render: handlebars.compile(
|
|
readFileSync(
|
|
join(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"..",
|
|
"lib",
|
|
"templates",
|
|
"service.js.handlebars"
|
|
)
|
|
).toString("utf-8")
|
|
)
|
|
},
|
|
{
|
|
name: "01-install.bat",
|
|
render: handlebars.compile(`set DIR=%~dp0
|
|
cd "%DIR%"
|
|
cd ..
|
|
cd ..
|
|
cd ..
|
|
node {{{pathToFolder}}}\\service.js -m install
|
|
`)
|
|
},
|
|
{
|
|
name: "02-restart.bat",
|
|
render: handlebars.compile(`set DIR=%~dp0
|
|
cd "%DIR%"
|
|
cd ..
|
|
cd ..
|
|
cd ..
|
|
node {{{pathToFolder}}}\\service.js -m restart
|
|
`)
|
|
},
|
|
{
|
|
name: "03-uninstall.bat",
|
|
render: handlebars.compile(`set DIR=%~dp0
|
|
cd "%DIR%"
|
|
cd ..
|
|
cd ..
|
|
cd ..
|
|
node {{{pathToFolder}}}\\service.js -m uninstall`)
|
|
},
|
|
{
|
|
name: "00-manual_starter.bat",
|
|
render: handlebars.compile(`set DIR=%~dp0
|
|
cd "%DIR%"
|
|
cd ..
|
|
cd ..
|
|
cd ..
|
|
node {{{pathToFolder}}}\\index.js`)
|
|
}
|
|
];
|
|
|
|
const storeConfig = async (
|
|
config: IConfigFile,
|
|
name: string,
|
|
path: string
|
|
) => {
|
|
// Create the Settings File:
|
|
await createFile(
|
|
join(path, "settings.json"),
|
|
JSON.stringify(config, undefined, 4)
|
|
);
|
|
|
|
// Write the Templates
|
|
for (const template of serviceTemplates) {
|
|
await createFile(
|
|
join(path, template.name),
|
|
template.render({
|
|
name,
|
|
path,
|
|
layer: result.layer,
|
|
pathToFile: join(path, template.name),
|
|
pathToFolder: join(path),
|
|
logLevel: result.logLevel
|
|
})
|
|
);
|
|
|
|
logger.info("Create Service at ", path);
|
|
}
|
|
};
|
|
|
|
if (result.runExtra) {
|
|
const promises: Promise<any>[] = [];
|
|
|
|
for (const _package of config.packages) {
|
|
for (const _instance of _package.defaultInstances) {
|
|
const autostart = {};
|
|
if (_package.autostart[_instance.options.identifier]) {
|
|
autostart[_instance.options.identifier] =
|
|
_package.autostart[_instance.options.identifier];
|
|
}
|
|
|
|
const _config: IConfigFile = {
|
|
functions: [],
|
|
packages: [
|
|
{
|
|
autostart,
|
|
defaultInstances: [_instance],
|
|
nameOfPackage: _package.nameOfPackage,
|
|
path: _package.path
|
|
}
|
|
]
|
|
};
|
|
|
|
promises.push(
|
|
storeConfig(
|
|
_config,
|
|
camelCase(_package.nameOfPackage),
|
|
join(
|
|
args.outputDir,
|
|
camelCase(_package.nameOfPackage),
|
|
camelCase(_instance.options.identifier)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
} else {
|
|
// Create a Syntetic Package, which will be created.
|
|
await storeConfig(
|
|
config,
|
|
"NopeService",
|
|
join(args.outputDir, "combinedService")
|
|
);
|
|
}
|
|
} catch (error) {
|
|
logger.error("Failed generating the Config");
|
|
logger.error(error);
|
|
}
|
|
}
|
|
|
|
// If requested As Main => Perform the Operation.
|
|
if (require.main === module) {
|
|
createService().catch((e) => console.error(e));
|
|
}
|