mirror of
https://github.com/MichMich/MagicMirror.git
synced 2026-07-23 15:54:34 -07:00
refactor(updatenotification): replace pm2 usage with node logic (#4134)
We can rely on PM2's native restart-on-exit behavior instead of the programmatic pm2 API. Fixes https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/82 by removing pm2. Note: Originally this PR was intended to update pm2, but after discussion, we decided to replace it instead. See the discussion below.
This commit is contained in:
committed by
GitHub
parent
0905d66a91
commit
f2759ad4f6
@@ -49,7 +49,6 @@ module.exports = NodeHelper.create({
|
||||
case "CONFIG":
|
||||
this.config = payload;
|
||||
this.updateHelper = new UpdateHelper(this.config);
|
||||
await this.updateHelper.check_PM2_Process();
|
||||
break;
|
||||
case "MODULES":
|
||||
// if this is the 1st time thru the update check process
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const Exec = require("node:child_process").exec;
|
||||
const Spawn = require("node:child_process").spawn;
|
||||
const fs = require("node:fs");
|
||||
|
||||
const Log = require("logger");
|
||||
|
||||
@@ -47,8 +46,6 @@ class Updater {
|
||||
this.autoRestart = config.updateAutorestart;
|
||||
this.moduleList = {};
|
||||
this.updating = false;
|
||||
this.usePM2 = false; // don't use pm2 by default
|
||||
this.PM2Id = null; // pm2 process number
|
||||
this.version = global.version;
|
||||
this.root_path = global.root_path;
|
||||
Log.info("Updater Class Loaded!");
|
||||
@@ -122,7 +119,7 @@ class Updater {
|
||||
Result.updated = true;
|
||||
if (this.autoRestart) {
|
||||
Log.info("Update done");
|
||||
setTimeout(() => this.restart(), 3000);
|
||||
setTimeout(() => this.nodeRestart(), 3000);
|
||||
} else {
|
||||
Log.info("Update done, don't forget to restart MagicMirror!");
|
||||
Result.needRestart = true;
|
||||
@@ -133,23 +130,6 @@ class Updater {
|
||||
});
|
||||
}
|
||||
|
||||
// restart rules (pm2 or node --run start)
|
||||
restart () {
|
||||
if (this.usePM2) this.pm2Restart();
|
||||
else this.nodeRestart();
|
||||
}
|
||||
|
||||
// restart MagicMirror with "pm2": use PM2Id for restart it
|
||||
pm2Restart () {
|
||||
Log.info("[PM2] restarting MagicMirror...");
|
||||
const pm2 = require("pm2");
|
||||
pm2.restart(this.PM2Id, (err) => {
|
||||
if (err) {
|
||||
Log.error("[PM2] restart Error", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// restart MagicMirror with "node --run start"
|
||||
nodeRestart () {
|
||||
Log.info("Restarting MagicMirror...");
|
||||
@@ -160,58 +140,6 @@ class Updater {
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Check using pm2
|
||||
check_PM2_Process () {
|
||||
Log.info("Checking PM2 using...");
|
||||
return new Promise((resolve) => {
|
||||
if (fs.existsSync("/.dockerenv")) {
|
||||
Log.info("[PM2] Running in docker container, not using PM2 ...");
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.unique_id === undefined) {
|
||||
Log.info("[PM2] You are not using pm2");
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug(`[PM2] Search for pm2 id: ${process.env.pm_id} -- name: ${process.env.name} -- unique_id: ${process.env.unique_id}`);
|
||||
|
||||
const pm2 = require("pm2");
|
||||
pm2.connect((err) => {
|
||||
if (err) {
|
||||
Log.error("[PM2]", err);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
pm2.list((err, list) => {
|
||||
if (err) {
|
||||
Log.error("[PM2] Can't get process List!");
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
list.forEach((pm) => {
|
||||
Log.debug(`[PM2] found pm2 process id: ${pm.pm_id} -- name: ${pm.name} -- unique_id: ${pm.pm2_env.unique_id}`);
|
||||
if (pm.pm2_env.status === "online" && process.env.name === pm.name && +process.env.pm_id === +pm.pm_id && process.env.unique_id === pm.pm2_env.unique_id) {
|
||||
this.PM2Id = pm.pm_id;
|
||||
this.usePM2 = true;
|
||||
Log.info(`[PM2] You are using pm2 with id: ${this.PM2Id} (${pm.name})`);
|
||||
resolve(true);
|
||||
} else {
|
||||
Log.debug(`[PM2] pm2 process id: ${pm.pm_id} don't match...`);
|
||||
}
|
||||
});
|
||||
pm2.disconnect();
|
||||
if (!this.usePM2) {
|
||||
Log.info("[PM2] You are not using pm2");
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// check if module is MagicMirror
|
||||
isMagicMirror (module) {
|
||||
if (module === "MagicMirror") return true;
|
||||
|
||||
Generated
+21
-1332
File diff suppressed because it is too large
Load Diff
@@ -105,7 +105,6 @@
|
||||
"moment-timezone": "^0.6.2",
|
||||
"node-ical": "^0.26.0",
|
||||
"nunjucks": "^3.2.4",
|
||||
"pm2": "^6.0.14",
|
||||
"socket.io": "^4.8.3",
|
||||
"suncalc": "^1.9.0",
|
||||
"systeminformation": "^5.31.5",
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("UpdateHelper", () => {
|
||||
const originalEnv = { ...process.env };
|
||||
const tempRoots = [];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
global.version = "test";
|
||||
global.root_path = process.cwd();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
for (const tempRoot of tempRoots) {
|
||||
rmSync(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
tempRoots.length = 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a temporary MagicMirror-like root with a module directory.
|
||||
* @param {string} moduleName - Name of the module directory to create.
|
||||
* @returns {{ root: string, modulePath: string }} Created paths.
|
||||
*/
|
||||
function createTempModuleRoot (moduleName) {
|
||||
const root = mkdtempSync(join(tmpdir(), "mm-updater-"));
|
||||
const modulePath = join(root, "modules", moduleName);
|
||||
mkdirSync(modulePath, { recursive: true });
|
||||
tempRoots.push(root);
|
||||
return { root, modulePath };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fresh UpdateHelper instance for testing.
|
||||
* @param {object} config - Optional config overrides.
|
||||
* @returns {Promise<object>} Resolved UpdateHelper instance.
|
||||
*/
|
||||
async function createUpdater (config = {}) {
|
||||
const updateHelperModule = await import("../../../defaultmodules/updatenotification/update_helper");
|
||||
const UpdateHelper = updateHelperModule.default || updateHelperModule;
|
||||
return new UpdateHelper({ updates: [], updateTimeout: 1000, updateAutorestart: false, ...config });
|
||||
}
|
||||
|
||||
it("marks update as requiring manual restart when autoRestart is disabled", async () => {
|
||||
const moduleName = "MMM-Test";
|
||||
const { root } = createTempModuleRoot(moduleName);
|
||||
global.root_path = root;
|
||||
|
||||
const updater = await createUpdater({ updateAutorestart: false });
|
||||
const result = await updater.updateProcess({
|
||||
name: moduleName,
|
||||
updateCommand: `"${process.execPath}" -p 1`
|
||||
});
|
||||
|
||||
expect(result.error).toBe(false);
|
||||
expect(result.updated).toBe(true);
|
||||
expect(result.needRestart).toBe(true);
|
||||
});
|
||||
|
||||
it("schedules node restart when autoRestart is enabled", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const moduleName = "MMM-Test";
|
||||
const { root } = createTempModuleRoot(moduleName);
|
||||
global.root_path = root;
|
||||
|
||||
const updater = await createUpdater({ updateAutorestart: true });
|
||||
const nodeRestartSpy = vi.spyOn(updater, "nodeRestart").mockImplementation(() => {});
|
||||
|
||||
const result = await updater.updateProcess({
|
||||
name: moduleName,
|
||||
updateCommand: `"${process.execPath}" -p 1`
|
||||
});
|
||||
|
||||
expect(result.error).toBe(false);
|
||||
expect(result.updated).toBe(true);
|
||||
expect(result.needRestart).toBe(false);
|
||||
expect(nodeRestartSpy).not.toHaveBeenCalled();
|
||||
|
||||
vi.advanceTimersByTime(3000);
|
||||
expect(nodeRestartSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user