Skip to content

Commit

Permalink
refactor: cli/upgrade async/await (#9558)
Browse files Browse the repository at this point in the history
* refactor: cli/upgrade async/await

async/await cli/upgrade-plugins
remove unused payload.files

* fix: add missing await
  • Loading branch information
barisusakli committed May 17, 2021
1 parent 1ce5950 commit ac86937
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 197 deletions.
2 changes: 1 addition & 1 deletion src/cli/package-install.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function installAll() {
}
}
} catch (e) {
// ignore
console.error(e);
}
try {
cproc.execSync(command + (prod ? ' --production' : ''), {
Expand Down
255 changes: 106 additions & 149 deletions src/cli/upgrade-plugins.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict';

const async = require('async');
const prompt = require('prompt');
const request = require('request');
const cproc = require('child_process');
const semver = require('semver');
const fs = require('fs');
const path = require('path');
const nconf = require('nconf');
const util = require('util');

const { paths, pluginNamePattern } = require('../constants');

Expand All @@ -22,190 +22,147 @@ if (process.platform === 'win32') {
packageManagerExecutable += '.cmd';
}

function getModuleVersions(modules, callback) {
async function getModuleVersions(modules) {
const versionHash = {};

async.eachLimit(modules, 50, (module, next) => {
fs.readFile(path.join(paths.nodeModules, module, 'package.json'), { encoding: 'utf-8' }, (err, pkg) => {
if (err) {
return next(err);
}

try {
pkg = JSON.parse(pkg);
versionHash[module] = pkg.version;
next();
} catch (err) {
next(err);
}
});
}, (err) => {
callback(err, versionHash);
const batch = require('../batch');
await batch.processArray(modules, async (moduleNames) => {
await Promise.all(moduleNames.map(async (module) => {
let pkg = await fs.promises.readFile(
path.join(paths.nodeModules, module, 'package.json'), { encoding: 'utf-8' }
);
pkg = JSON.parse(pkg);
versionHash[module] = pkg.version;
}));
}, {
batch: 50,
});

return 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);
}
async function getInstalledPlugins() {
let [deps, bundled] = await Promise.all([
fs.promises.readFile(paths.currentPackage, { encoding: 'utf-8' }),
fs.promises.readFile(paths.installPackage, { encoding: 'utf-8' }),
]);

deps = Object.keys(JSON.parse(deps).dependencies)
.filter(pkgName => pluginNamePattern.test(pkgName));
bundled = Object.keys(JSON.parse(bundled).dependencies)
.filter(pkgName => pluginNamePattern.test(pkgName));


payload.files = payload.files.filter(file => pluginNamePattern.test(file));
// Whittle down deps to send back only extraneously installed plugins/themes/etc
const checklist = deps.filter((pkgName) => {
if (bundled.includes(pkgName)) {
return false;
}

// Ignore git repositories
try {
payload.deps = Object.keys(JSON.parse(payload.deps).dependencies);
payload.bundled = Object.keys(JSON.parse(payload.bundled).dependencies);
} catch (err) {
return callback(err);
fs.accessSync(path.join(paths.nodeModules, pkgName, '.git'));
return false;
} catch (e) {
return true;
}
});

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;
}
});
return await getModuleVersions(checklist);
}

getModuleVersions(checklist, callback);
});
async function getCurrentVersion() {
let pkg = await fs.promises.readFile(paths.installPackage, { encoding: 'utf-8' });
pkg = JSON.parse(pkg);
return pkg.version;
}

function getCurrentVersion(callback) {
fs.readFile(paths.installPackage, { encoding: 'utf-8' }, (err, pkg) => {
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) {
return callback(err);
process.stdout.write('error'.red + ''.reset);
return cb(err);
}

try {
pkg = JSON.parse(pkg);
} catch (err) {
return callback(err);
if (!Array.isArray(body) && toCheck.length === 1) {
body = [body];
}
callback(null, pkg.version);
cb(null, body);
});
}

function checkPlugins(standalone, callback) {
if (standalone) {
process.stdout.write('Checking installed plugins and themes for updates... ');
});

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);

async.waterfall([
async.apply(async.parallel, {
plugins: getInstalledPlugins,
version: getCurrentVersion,
}),
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({
method: 'GET',
url: `https://packages.nodebb.org/api/v1/suggest?version=${payload.version}&package[]=${toCheck.join('&package[]=')}`,
json: true,
}, (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);
return upgradable;
}

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);
}

async function upgradePlugins() {
try {
const found = await checkPlugins();
if (found && found.length) {
process.stdout.write(`\n\nA total of ${String(found.length).bold} package(s) can be upgraded:\n\n`);
found.forEach((suggestObj) => {
process.stdout.write(`${' * '.yellow + suggestObj.name.reset} (${suggestObj.current.yellow}${' -> '.reset}${suggestObj.suggested.green}${')\n'.reset}`);
});
} else {
if (standalone) {
console.log('\nAll packages up-to-date!'.green + ''.reset);
}
return callback();
console.log('\nAll packages up-to-date!'.green + ''.reset);
return;
}

prompt.message = '';
prompt.delimiter = '';

const promptGet = util.promisify((schema, callback) => prompt.get(schema, callback));
prompt.start();
prompt.get({
const result = await promptGet({
name: 'upgrade',
description: '\nProceed with upgrade (y|n)?'.reset,
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;

0 comments on commit ac86937

Please sign in to comment.