Add migration for plugins installed in node_modules and load plugins on start

pull/6018/head
Stefan Müller 2023-11-01 14:31:21 +01:00
parent 2160fd4c52
commit 4863f10d24
3 changed files with 64 additions and 21 deletions

View File

@ -49,6 +49,7 @@ const express = require('./hooks/express');
const hooks = require('../static/js/pluginfw/hooks');
const pluginDefs = require('../static/js/pluginfw/plugin_defs');
const plugins = require('../static/js/pluginfw/plugins');
const installer = require('../static/js/pluginfw/installer');
const {Gate} = require('./utils/promises');
const stats = require('./stats');
@ -139,6 +140,7 @@ exports.start = async () => {
}
await db.init();
await installer.checkForMigration();
await plugins.update();
const installedPlugins = Object.values(pluginDefs.plugins)
.filter((plugin) => plugin.package.name !== 'ep_etherpad-lite')

View File

@ -6,11 +6,14 @@ const hooks = require('./hooks');
const runCmd = require('../../../node/utils/run_cmd');
const settings = require('../../../node/utils/Settings');
const axios = require('axios');
const {PluginManager} = require('live-plugin-manager');
const {PluginManager} = require("live-plugin-manager");
const {promises: fs} = require("fs");
const path = require("path");
const logger = log4js.getLogger('plugins');
exports.manager = new PluginManager();
const installedPluginsPath = path.join(settings.root, 'var/installed_plugins.json');
const onAllTasksFinished = async () => {
settings.reloadSettings();
@ -30,6 +33,48 @@ const wrapTaskCb = (cb) => {
};
};
const migratePluginsFromNodeModules = async () => {
logger.info('start migration of plugins in node_modules')
// Notes:
// * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`.
// * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be
// unset or set to `development`) because otherwise `npm ls` will not mention any packages
// that are not included in `package.json` (which is expected to not exist).
const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production'];
const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']}));
await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => {
if (pkg.startsWith(plugins.prefix) && pkg !== 'ep_etherpad-lite') {
await exports.install(pkg)
}
}));
}
exports.checkForMigration = async () => {
logger.info('check installed plugins for migration')
try {
await fs.access(installedPluginsPath, fs.constants.F_OK)
} catch (err) {
await migratePluginsFromNodeModules();
}
const fileContent = await fs.readFile(installedPluginsPath);
const installedPlugins = JSON.parse(fileContent.toString());
for (const plugin of installedPlugins.plugins) {
await exports.manager.install(plugin)
}
};
const persistInstalledPlugins = async () => {
let installedPlugins = { plugins: []};
for (const pkg of Object.keys(await plugins.getPackages())) {
installedPlugins.plugins.push(pkg)
}
installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
await fs.writeFile(installedPluginsPath, JSON.stringify(installedPlugins));
}
exports.uninstall = async (pluginName, cb = null) => {
cb = wrapTaskCb(cb);
logger.info(`Uninstalling plugin ${pluginName}...`);
@ -37,6 +82,7 @@ exports.uninstall = async (pluginName, cb = null) => {
logger.info(`Successfully uninstalled plugin ${pluginName}`);
await hooks.aCallAll('pluginUninstall', {pluginName});
await plugins.update();
await persistInstalledPlugins();
cb(null);
};
@ -47,6 +93,7 @@ exports.install = async (pluginName, cb = null) => {
logger.info(`Successfully installed plugin ${pluginName}`);
await hooks.aCallAll('pluginInstall', {pluginName});
await plugins.update();
await persistInstalledPlugins();
cb(null);
};

View File

@ -9,6 +9,7 @@ const tsort = require('./tsort');
const pluginUtils = require('./shared');
const defs = require('./plugin_defs');
const {manager} = require('./installer');
const settings = require("../../../node/utils/Settings");
const logger = log4js.getLogger('plugins');
@ -84,8 +85,6 @@ exports.pathNormalization = (part, hookFnName, hookName) => {
};
exports.update = async () => {
await manager.install('ep_bookmark')
await manager.install('ep_align')
const packages = await exports.getPackages();
const parts = {}; // Key is full name. sortParts converts this into a topologically sorted array.
const plugins = {};
@ -108,31 +107,26 @@ exports.update = async () => {
};
exports.getPackages = async () => {
logger.info('Running npm to get a list of installed plugins...');
let plugins = manager.list()
let newDependencies = {}
for (const plugin of plugins) {
if (!plugin.name.startsWith(exports.prefix)) {
continue;
}
plugin.realPath = await fs.realpath(plugin.location);
plugin.path = plugin.realPath;
newDependencies[plugin.name] = plugin
}
// Notes:
// * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`.
// * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be
// unset or set to `development`) because otherwise `npm ls` will not mention any packages
// that are not included in `package.json` (which is expected to not exist).
const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production'];
const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']}));
await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => {
if (!pkg.startsWith(exports.prefix)) {
delete dependencies[pkg];
return;
}
info.realPath = await fs.realpath(info.path);
}));
let newList = Object.assign({}, dependencies, newDependencies)
console.log('blub', newList)
return newList;
newDependencies['ep_etherpad-lite'] = {
name: 'ep_etherpad-lite',
version: settings.getEpVersion(),
path: path.join(settings.root, 'node_modules/ep_etherpad-lite'),
realPath: path.join(settings.root, 'src'),
}
return newDependencies;
};
const loadPlugin = async (packages, pluginName, plugins, parts) => {