Kristjan ESPERANTO 0b70274a1a
Replace prettier by stylistic to lint JavaScript (#3303)
In the latest versions of ESLint, more and more formatting rules were
removed or declared deprecated. These rules have been integrated into
the new Stylistic package (https://eslint.style/guide/why) and expanded.

Stylistic acts as a better formatter  for JavaScript as Prettier.

With this PR there are many changes that make the code more uniform, but
it may be difficult to review due to the large amount. Even if I have no
worries about the changes, perhaps this would be something for the
release after next.

Let me know what you think.
2023-12-25 08:17:11 +01:00

231 lines
6.6 KiB
JavaScript

const Exec = require("child_process").exec;
const Spawn = require("child_process").spawn;
const commandExists = require("command-exists");
const Log = require("logger");
/* class Updater
* Allow to self updating 3rd party modules from command defined in config
*
* [constructor] read value in config:
* updates: [ // array of modules update commands
* {
* <module name>: <update command>
* },
* {
* ...
* }
* ],
* updateTimeout: 2 * 60 * 1000, // max update duration
* updateAutorestart: false // autoRestart MM when update done ?
*
* [main command]: parse(<Array of modules>):
* parse if module update is needed
* --> Apply ONLY one update (first of the module list)
* --> auto-restart MagicMirror or wait manual restart by user
* return array with modules update state information for `updatenotification` module displayer information
* [
* {
* name = <module-name>, // name of the module
* updateCommand = <update command>, // update command (if found)
* inProgress = <boolean>, // an update if in progress for this module
* error = <boolean>, // an error if detected when updating
* updated = <boolean>, // updated successfully
* needRestart = <boolean> // manual restart of MagicMirror is required by user
* },
* {
* ...
* }
* ]
*/
class Updater {
constructor (config) {
this.updates = config.updates;
this.timeout = config.updateTimeout;
this.autoRestart = config.updateAutorestart;
this.moduleList = {};
this.updating = false;
this.usePM2 = false;
this.PM2 = null;
this.version = global.version;
this.root_path = global.root_path;
Log.info("updatenotification: Updater Class Loaded!");
}
// [main command] parse if module update is needed
async parse (modules) {
var parser = modules.map(async (module) => {
if (this.moduleList[module.module] === undefined) {
this.moduleList[module.module] = {};
this.moduleList[module.module].name = module.module;
this.moduleList[module.module].updateCommand = await this.applyCommand(module.module);
this.moduleList[module.module].inProgress = false;
this.moduleList[module.module].error = null;
this.moduleList[module.module].updated = false;
this.moduleList[module.module].needRestart = false;
}
if (!this.moduleList[module.module].inProgress) {
if (!this.updating) {
if (!this.moduleList[module.module].updateCommand) {
this.updating = false;
} else {
this.updating = true;
this.moduleList[module.module].inProgress = true;
Object.assign(this.moduleList[module.module], await this.updateProcess(this.moduleList[module.module]));
}
}
}
});
await Promise.all(parser);
let updater = Object.values(this.moduleList);
Log.debug("updatenotification Update Result:", updater);
return updater;
}
// module updater with his proper command
// return object as result
//{
// error: <boolean>, // if error detected
// updated: <boolean>, // if updated successfully
// needRestart: <boolean> // if magicmirror restart required
//};
updateProcess (module) {
let Result = {
error: false,
updated: false,
needRestart: false
};
let Command = null;
const Path = `${this.root_path}/modules/`;
const modulePath = Path + module.name;
if (module.updateCommand) {
Command = module.updateCommand;
} else {
Log.warn(`updatenotification: Update of ${module.name} is not supported.`);
return Result;
}
Log.info(`updatenotification: Updating ${module.name}...`);
return new Promise((resolve) => {
Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout, stderr) => {
if (error) {
Log.error(`updatenotification: exec error: ${error}`);
Result.error = true;
} else {
Log.info(`updatenotification: Update logs of ${module.name}: ${stdout}`);
Result.updated = true;
if (this.autoRestart) {
Log.info("updatenotification: Update done");
setTimeout(() => this.restart(), 3000);
} else {
Log.info("updatenotification: Update done, don't forget to restart MagicMirror!");
Result.needRestart = true;
}
}
resolve(Result);
});
});
}
// restart rules (pm2 or npm start)
restart () {
if (this.usePM2) this.pm2Restart();
else this.npmRestart();
}
// restart MagicMiror with "pm2"
pm2Restart () {
Log.info("updatenotification: PM2 will restarting MagicMirror...");
Exec(`pm2 restart ${this.PM2}`, (err, std, sde) => {
if (err) {
Log.error("updatenotification:[PM2] restart Error", err);
}
});
}
// restart MagicMiror with "npm start"
npmRestart () {
Log.info("updatenotification: Restarting MagicMirror...");
const out = process.stdout;
const err = process.stderr;
const subprocess = Spawn("npm start", { cwd: this.root_path, shell: true, detached: true, stdio: ["ignore", out, err] });
subprocess.unref();
process.exit();
}
// Check using pm2
check_PM2_Process () {
Log.info("updatenotification: Checking PM2 using...");
return new Promise((resolve) => {
commandExists("pm2")
.then(async () => {
var PM2_List = await this.PM2_GetList();
if (!PM2_List) {
Log.error("updatenotification: [PM2] Can't get process List!");
this.usePM2 = false;
resolve(false);
return;
}
PM2_List.forEach((pm) => {
if (pm.pm2_env.version === this.version && pm.pm2_env.status === "online" && pm.pm2_env.PWD.includes(this.root_path)) {
this.PM2 = pm.name;
this.usePM2 = true;
Log.info("updatenotification: You are using pm2 with", this.PM2);
resolve(true);
}
});
if (!this.PM2) {
Log.info("updatenotification: You are not using pm2");
this.usePM2 = false;
resolve(false);
}
})
.catch(() => {
Log.info("updatenotification: You are not using pm2");
this.usePM2 = false;
resolve(false);
});
});
}
// Get the list of pm2 process
PM2_GetList () {
return new Promise((resolve) => {
Exec("pm2 jlist", (err, std, sde) => {
if (err) {
resolve(null);
return;
}
try {
let result = JSON.parse(std);
resolve(result);
} catch (e) {
Log.error("updatenotification: [PM2] can't GetList!");
Log.debug("updatenotification: [PM2] GetList is not an JSON format", e);
resolve(null);
}
});
});
}
// check if module is MagicMirror
isMagicMirror (module) {
if (module === "MagicMirror") return true;
return false;
}
// search update module command
applyCommand (module) {
if (this.isMagicMirror(module.module)) return null;
let command = null;
this.updates.forEach((updater) => {
if (updater[module]) command = updater[module];
});
return command;
}
}
module.exports = Updater;