mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
refactor: cli/upgrade async/await (#9558)
* refactor: cli/upgrade async/await async/await cli/upgrade-plugins remove unused payload.files * fix: add missing await
This commit is contained in:
committed by
GitHub
parent
1ce595083a
commit
ac86937c88
@@ -66,7 +66,7 @@ function installAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore
|
console.error(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cproc.execSync(command + (prod ? ' --production' : ''), {
|
cproc.execSync(command + (prod ? ' --production' : ''), {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const async = require('async');
|
|
||||||
const prompt = require('prompt');
|
const prompt = require('prompt');
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
const cproc = require('child_process');
|
const cproc = require('child_process');
|
||||||
@@ -8,6 +7,7 @@ const semver = require('semver');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
const { paths, pluginNamePattern } = require('../constants');
|
const { paths, pluginNamePattern } = require('../constants');
|
||||||
|
|
||||||
@@ -22,190 +22,147 @@ if (process.platform === 'win32') {
|
|||||||
packageManagerExecutable += '.cmd';
|
packageManagerExecutable += '.cmd';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModuleVersions(modules, callback) {
|
async function getModuleVersions(modules) {
|
||||||
const versionHash = {};
|
const versionHash = {};
|
||||||
|
const batch = require('../batch');
|
||||||
async.eachLimit(modules, 50, (module, next) => {
|
await batch.processArray(modules, async (moduleNames) => {
|
||||||
fs.readFile(path.join(paths.nodeModules, module, 'package.json'), { encoding: 'utf-8' }, (err, pkg) => {
|
await Promise.all(moduleNames.map(async (module) => {
|
||||||
if (err) {
|
let pkg = await fs.promises.readFile(
|
||||||
return next(err);
|
path.join(paths.nodeModules, module, 'package.json'), { encoding: 'utf-8' }
|
||||||
}
|
);
|
||||||
|
|
||||||
try {
|
|
||||||
pkg = JSON.parse(pkg);
|
|
||||||
versionHash[module] = pkg.version;
|
|
||||||
next();
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, (err) => {
|
|
||||||
callback(err, versionHash);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInstalledPlugins(callback) {
|
|
||||||
async.parallel({
|
|
||||||
files: async.apply(fs.readdir, paths.nodeModules),
|
|
||||||
deps: async.apply(fs.readFile, paths.currentPackage, { encoding: 'utf-8' }),
|
|
||||||
bundled: async.apply(fs.readFile, paths.installPackage, { encoding: 'utf-8' }),
|
|
||||||
}, (err, payload) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.files = payload.files.filter(file => pluginNamePattern.test(file));
|
|
||||||
|
|
||||||
try {
|
|
||||||
payload.deps = Object.keys(JSON.parse(payload.deps).dependencies);
|
|
||||||
payload.bundled = Object.keys(JSON.parse(payload.bundled).dependencies);
|
|
||||||
} catch (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.bundled = payload.bundled.filter(pkgName => pluginNamePattern.test(pkgName));
|
|
||||||
payload.deps = payload.deps.filter(pkgName => pluginNamePattern.test(pkgName));
|
|
||||||
|
|
||||||
// Whittle down deps to send back only extraneously installed plugins/themes/etc
|
|
||||||
const checklist = payload.deps.filter((pkgName) => {
|
|
||||||
if (payload.bundled.includes(pkgName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore git repositories
|
|
||||||
try {
|
|
||||||
fs.accessSync(path.join(paths.nodeModules, pkgName, '.git'));
|
|
||||||
return false;
|
|
||||||
} catch (e) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getModuleVersions(checklist, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentVersion(callback) {
|
|
||||||
fs.readFile(paths.installPackage, { encoding: 'utf-8' }, (err, pkg) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
pkg = JSON.parse(pkg);
|
pkg = JSON.parse(pkg);
|
||||||
} catch (err) {
|
versionHash[module] = pkg.version;
|
||||||
return callback(err);
|
}));
|
||||||
}
|
}, {
|
||||||
callback(null, pkg.version);
|
batch: 50,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return versionHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPlugins(standalone, callback) {
|
async function getInstalledPlugins() {
|
||||||
if (standalone) {
|
let [deps, bundled] = await Promise.all([
|
||||||
process.stdout.write('Checking installed plugins and themes for updates... ');
|
fs.promises.readFile(paths.currentPackage, { encoding: 'utf-8' }),
|
||||||
}
|
fs.promises.readFile(paths.installPackage, { encoding: 'utf-8' }),
|
||||||
|
]);
|
||||||
|
|
||||||
async.waterfall([
|
deps = Object.keys(JSON.parse(deps).dependencies)
|
||||||
async.apply(async.parallel, {
|
.filter(pkgName => pluginNamePattern.test(pkgName));
|
||||||
plugins: getInstalledPlugins,
|
bundled = Object.keys(JSON.parse(bundled).dependencies)
|
||||||
version: getCurrentVersion,
|
.filter(pkgName => pluginNamePattern.test(pkgName));
|
||||||
}),
|
|
||||||
function (payload, next) {
|
|
||||||
const toCheck = Object.keys(payload.plugins);
|
|
||||||
|
|
||||||
if (!toCheck.length) {
|
|
||||||
process.stdout.write(' OK'.green + ''.reset);
|
|
||||||
return next(null, []); // no extraneous plugins installed
|
|
||||||
}
|
|
||||||
|
|
||||||
request({
|
// Whittle down deps to send back only extraneously installed plugins/themes/etc
|
||||||
method: 'GET',
|
const checklist = deps.filter((pkgName) => {
|
||||||
url: `https://packages.nodebb.org/api/v1/suggest?version=${payload.version}&package[]=${toCheck.join('&package[]=')}`,
|
if (bundled.includes(pkgName)) {
|
||||||
json: true,
|
return false;
|
||||||
}, (err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
process.stdout.write('error'.red + ''.reset);
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
process.stdout.write(' OK'.green + ''.reset);
|
|
||||||
|
|
||||||
if (!Array.isArray(body) && toCheck.length === 1) {
|
|
||||||
body = [body];
|
|
||||||
}
|
|
||||||
|
|
||||||
let current;
|
|
||||||
let suggested;
|
|
||||||
const upgradable = body.map((suggestObj) => {
|
|
||||||
current = payload.plugins[suggestObj.package];
|
|
||||||
suggested = suggestObj.version;
|
|
||||||
|
|
||||||
if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) {
|
|
||||||
return {
|
|
||||||
name: suggestObj.package,
|
|
||||||
current: current,
|
|
||||||
suggested: suggested,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).filter(Boolean);
|
|
||||||
|
|
||||||
next(null, upgradable);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradePlugins(callback) {
|
|
||||||
let standalone = false;
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
callback = function () {};
|
|
||||||
standalone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPlugins(standalone, (err, found) => {
|
|
||||||
if (err) {
|
|
||||||
console.log('Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability'.reset);
|
|
||||||
return callback(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore git repositories
|
||||||
|
try {
|
||||||
|
fs.accessSync(path.join(paths.nodeModules, pkgName, '.git'));
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return await getModuleVersions(checklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCurrentVersion() {
|
||||||
|
let pkg = await fs.promises.readFile(paths.installPackage, { encoding: 'utf-8' });
|
||||||
|
pkg = JSON.parse(pkg);
|
||||||
|
return pkg.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSuggestedModules = util.promisify((nbbVersion, toCheck, cb) => {
|
||||||
|
request({
|
||||||
|
method: 'GET',
|
||||||
|
url: `https://packages.nodebb.org/api/v1/suggest?version=${nbbVersion}&package[]=${toCheck.join('&package[]=')}`,
|
||||||
|
json: true,
|
||||||
|
}, (err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
process.stdout.write('error'.red + ''.reset);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
if (!Array.isArray(body) && toCheck.length === 1) {
|
||||||
|
body = [body];
|
||||||
|
}
|
||||||
|
cb(null, body);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function checkPlugins() {
|
||||||
|
process.stdout.write('Checking installed plugins and themes for updates... ');
|
||||||
|
const [plugins, nbbVersion] = await Promise.all([
|
||||||
|
getInstalledPlugins,
|
||||||
|
getCurrentVersion,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const toCheck = Object.keys(plugins);
|
||||||
|
if (!toCheck.length) {
|
||||||
|
process.stdout.write(' OK'.green + ''.reset);
|
||||||
|
return []; // no extraneous plugins installed
|
||||||
|
}
|
||||||
|
const suggestedModules = await getSuggestedModules(nbbVersion, toCheck);
|
||||||
|
process.stdout.write(' OK'.green + ''.reset);
|
||||||
|
|
||||||
|
let current;
|
||||||
|
let suggested;
|
||||||
|
const upgradable = suggestedModules.map((suggestObj) => {
|
||||||
|
current = plugins[suggestObj.package];
|
||||||
|
suggested = suggestObj.version;
|
||||||
|
|
||||||
|
if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) {
|
||||||
|
return {
|
||||||
|
name: suggestObj.package,
|
||||||
|
current: current,
|
||||||
|
suggested: suggested,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
return upgradable;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upgradePlugins() {
|
||||||
|
try {
|
||||||
|
const found = await checkPlugins();
|
||||||
if (found && found.length) {
|
if (found && found.length) {
|
||||||
process.stdout.write(`\n\nA total of ${String(found.length).bold} package(s) can be upgraded:\n\n`);
|
process.stdout.write(`\n\nA total of ${String(found.length).bold} package(s) can be upgraded:\n\n`);
|
||||||
found.forEach((suggestObj) => {
|
found.forEach((suggestObj) => {
|
||||||
process.stdout.write(`${' * '.yellow + suggestObj.name.reset} (${suggestObj.current.yellow}${' -> '.reset}${suggestObj.suggested.green}${')\n'.reset}`);
|
process.stdout.write(`${' * '.yellow + suggestObj.name.reset} (${suggestObj.current.yellow}${' -> '.reset}${suggestObj.suggested.green}${')\n'.reset}`);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (standalone) {
|
console.log('\nAll packages up-to-date!'.green + ''.reset);
|
||||||
console.log('\nAll packages up-to-date!'.green + ''.reset);
|
return;
|
||||||
}
|
|
||||||
return callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt.message = '';
|
prompt.message = '';
|
||||||
prompt.delimiter = '';
|
prompt.delimiter = '';
|
||||||
|
|
||||||
|
const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback));
|
||||||
prompt.start();
|
prompt.start();
|
||||||
prompt.get({
|
const result = await promptGet({
|
||||||
name: 'upgrade',
|
name: 'upgrade',
|
||||||
description: '\nProceed with upgrade (y|n)?'.reset,
|
description: '\nProceed with upgrade (y|n)?'.reset,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
}, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['y', 'Y', 'yes', 'YES'].includes(result.upgrade)) {
|
|
||||||
console.log('\nUpgrading packages...');
|
|
||||||
const args = packageManagerInstallArgs.concat(found.map(suggestObj => `${suggestObj.name}@${suggestObj.suggested}`));
|
|
||||||
|
|
||||||
cproc.execFile(packageManagerExecutable, args, { stdio: 'ignore' }, (err) => {
|
|
||||||
callback(err, false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log('Package upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade -p'.green + '".'.reset);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
if (['y', 'Y', 'yes', 'YES'].includes(result.upgrade)) {
|
||||||
|
console.log('\nUpgrading packages...');
|
||||||
|
const args = packageManagerInstallArgs.concat(found.map(suggestObj => `${suggestObj.name}@${suggestObj.suggested}`));
|
||||||
|
|
||||||
|
cproc.execFileSync(packageManagerExecutable, args, { stdio: 'ignore' });
|
||||||
|
} else {
|
||||||
|
console.log('Package upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade -p'.green + '".'.reset);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability'.reset);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.upgradePlugins = upgradePlugins;
|
exports.upgradePlugins = upgradePlugins;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const async = require('async');
|
|
||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
|
|
||||||
const packageInstall = require('./package-install');
|
const packageInstall = require('./package-install');
|
||||||
@@ -9,61 +8,52 @@ const { upgradePlugins } = require('./upgrade-plugins');
|
|||||||
const steps = {
|
const steps = {
|
||||||
package: {
|
package: {
|
||||||
message: 'Updating package.json file with defaults...',
|
message: 'Updating package.json file with defaults...',
|
||||||
handler: function (next) {
|
handler: function () {
|
||||||
packageInstall.updatePackageFile();
|
packageInstall.updatePackageFile();
|
||||||
packageInstall.preserveExtraneousPlugins();
|
packageInstall.preserveExtraneousPlugins();
|
||||||
process.stdout.write(' OK\n'.green);
|
process.stdout.write(' OK\n'.green);
|
||||||
next();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
install: {
|
install: {
|
||||||
message: 'Bringing base dependencies up to date...',
|
message: 'Bringing base dependencies up to date...',
|
||||||
handler: function (next) {
|
handler: function () {
|
||||||
process.stdout.write(' started\n'.green);
|
process.stdout.write(' started\n'.green);
|
||||||
packageInstall.installAll();
|
packageInstall.installAll();
|
||||||
next();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
message: 'Checking installed plugins for updates...',
|
message: 'Checking installed plugins for updates...',
|
||||||
handler: function (next) {
|
handler: async function () {
|
||||||
async.series([
|
await require('../database').init();
|
||||||
require('../database').init,
|
await upgradePlugins();
|
||||||
upgradePlugins,
|
|
||||||
], next);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
message: 'Updating NodeBB data store schema...',
|
message: 'Updating NodeBB data store schema...',
|
||||||
handler: function (next) {
|
handler: async function () {
|
||||||
async.series([
|
await require('../database').init();
|
||||||
require('../database').init,
|
await require('../meta').configs.init();
|
||||||
require('../meta').configs.init,
|
await require('../upgrade').run();
|
||||||
require('../upgrade').run,
|
|
||||||
], next);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
message: 'Rebuilding assets...',
|
message: 'Rebuilding assets...',
|
||||||
handler: require('../meta/build').buildAll,
|
handler: async function () {
|
||||||
|
await require('../meta/build').buildAll();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function runSteps(tasks) {
|
async function runSteps(tasks) {
|
||||||
tasks = tasks.map((key, i) => function (next) {
|
try {
|
||||||
process.stdout.write(`\n${(`${i + 1}. `).bold}${steps[key].message.yellow}`);
|
for (let i = 0; i < tasks.length; i++) {
|
||||||
return steps[key].handler((err) => {
|
const step = steps[tasks[i]];
|
||||||
if (err) { return next(err); }
|
if (step && step.message && step.handler) {
|
||||||
next();
|
process.stdout.write(`\n${(`${i + 1}. `).bold}${step.message.yellow}`);
|
||||||
});
|
/* eslint-disable-next-line */
|
||||||
});
|
await step.handler();
|
||||||
|
}
|
||||||
async.series(tasks, (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(`Error occurred during upgrade: ${err.stack}`);
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = 'NodeBB Upgrade Complete!';
|
const message = 'NodeBB Upgrade Complete!';
|
||||||
// some consoles will return undefined/zero columns,
|
// some consoles will return undefined/zero columns,
|
||||||
// so just use 2 spaces in upgrade script if we can't get our column count
|
// so just use 2 spaces in upgrade script if we can't get our column count
|
||||||
@@ -73,10 +63,13 @@ function runSteps(tasks) {
|
|||||||
console.log(`\n\n${spaces}${message.green.bold}${'\n'.reset}`);
|
console.log(`\n\n${spaces}${message.green.bold}${'\n'.reset}`);
|
||||||
|
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
} catch (err) {
|
||||||
|
console.error(`Error occurred during upgrade: ${err.stack}`);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runUpgrade(upgrades, options) {
|
async function runUpgrade(upgrades, options) {
|
||||||
console.log('\nUpdating NodeBB...'.cyan);
|
console.log('\nUpdating NodeBB...'.cyan);
|
||||||
options = options || {};
|
options = options || {};
|
||||||
// disable mongo timeouts during upgrade
|
// disable mongo timeouts during upgrade
|
||||||
@@ -88,23 +81,14 @@ function runUpgrade(upgrades, options) {
|
|||||||
options.plugins || options.schema || options.build) {
|
options.plugins || options.schema || options.build) {
|
||||||
tasks = tasks.filter(key => options[key]);
|
tasks = tasks.filter(key => options[key]);
|
||||||
}
|
}
|
||||||
runSteps(tasks);
|
await runSteps(tasks);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async.series([
|
await require('../database').init();
|
||||||
require('../database').init,
|
await require('../meta').configs.init();
|
||||||
require('../meta').configs.init,
|
await require('../upgrade').runParticular(upgrades);
|
||||||
async function () {
|
process.exit(0);
|
||||||
await require('../upgrade').runParticular(upgrades);
|
|
||||||
},
|
|
||||||
], (err) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.upgrade = runUpgrade;
|
exports.upgrade = runUpgrade;
|
||||||
|
|||||||
Reference in New Issue
Block a user