Skip to content

Commit

Permalink
Merge pull request #696 from Availity/fix/upgrade-workflow-error
Browse files Browse the repository at this point in the history
fix(workflow-upgrade): fix error getting peerDeps and buffer overflow
  • Loading branch information
jordan-a-young authored Feb 16, 2024
2 parents 8e57811 + 774fe15 commit 460385c
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 75 deletions.
184 changes: 111 additions & 73 deletions packages/workflow-upgrade/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,92 @@ const path = require('path');
const readPkg = require('read-pkg');
const rimraf = require('rimraf');
const Logger = require('@availity/workflow-logger');
const inquirer = require('inquirer');

const asyncExec = promisify(exec);
const reinstallTimeout = 30 * 1000 * 60; // 30 minutes

const REINSTALL_TIMEOUT = 30 * 1000 * 60; // 30 minutes
const MAX_BUFFER_SIZE = 1024 * 5000; // 5MB

const reinstallNodeModules = async (installer, peerInfoReceived) => {
Logger.info('Reinstalling packages..');

// Run install command
await asyncExec(`${installer} install`, { timeout: REINSTALL_TIMEOUT, maxBuffer: MAX_BUFFER_SIZE }, () => {
Logger.success('\nCongratulations! Welcome to the new @availity/workflow.');

if (!peerInfoReceived) {
Logger.warn(
'To complete your upgrade, please install the peerDependencies from eslint-config-availity as devDependencies in your project.'
);
}
});
};

const getLatestNpmVersion = async (pkgName, defaultVersion) => {
try {
let { stdout } = await asyncExec(`npm view ${pkgName} version`);
return stdout.trim();
} catch {
Logger.warn(`There was an error getting the latest ${pkgName} version. Defaulting to ${defaultVersion}`);
latestWorkflowVersion = defaultVersion;
}
};

module.exports = async (cwd) => {
Logger.info('Upgrading @availity/workflow');
const pkgFile = path.join(cwd, 'package.json');

if (fs.existsSync(pkgFile)) {
// Read Package File to JSON
Logger.info('Reading package.json...');
const pkg = readPkg.sync({ cwd, normalize: false });
const { devDependencies, scripts, availityWorkflow } = pkg;

const pkgLock = path.join(cwd, 'package-lock.json');
const yarnLock = path.join(cwd, 'yarn.lock');

const hasPkgLock = fs.existsSync(pkgLock);
const hasYarnLock = fs.existsSync(yarnLock);

let installer = '';
let peerInfoReceived = false;

if (fs.existsSync(pkgLock)) {
// delete package lock, set npm as installer
Logger.info('Deleting Package-Lock');
fs.unlinkSync(pkgLock);
// Determine which pkg manager is used
Logger.info('Looking for lockfiles to determine which package manager is being used...');
if (hasPkgLock && hasYarnLock) {
Logger.warn('We found package-lock.json and yarn.lock files.');
const response = await inquirer.prompt([
{
type: 'list',
name: 'installer',
message: 'Which package manager would you like to use? (We recommend yarn)',
choices: ['yarn', 'npm']
}
]);
installer = response.installer;
} else if (hasPkgLock) {
Logger.info('package-lock.json detected. Using NPM as installer.');
installer = 'npm';
} else if (fs.existsSync(yarnLock)) {
// delete yarn lock, set yarn as installer
Logger.info('Deleting yarn.lock');
fs.unlinkSync(yarnLock);
} else if (hasYarnLock) {
Logger.info('yarn.lock detected. Using Yarn as installer.');
installer = 'yarn';
} else {
Logger.warn('No lockfile detected, using yarn as default installer.');
Logger.warn('No lockfile detected. Using Yarn as default installer.');
installer = 'yarn';
}

// Add this script into the new workflow scripts for the future
scripts['upgrade:workflow'] = './node_modules/.bin/upgrade-workflow';
// Get latest verison of packages
let latestWorkflowVersion = await getLatestNpmVersion('@availity/workflow', '10.0.6');
let latestEslintVersion = await getLatestNpmVersion('eslint-config-availity', '9.0.0');

// Check for deprecated workflow features
if (availityWorkflow?.plugin) {
Logger.warn(`Deprecated plugin feature detected, removing availityWorkflow.plugin entry.`);
delete availityWorkflow.plugin;
}
if (devDependencies) {
Logger.info(`Setting version of @availity/workflow to ${latestWorkflowVersion}`);
devDependencies['@availity/workflow'] = `^${latestWorkflowVersion}`;

// If workflow entry didn't exist, or plugin was its only key
if (!availityWorkflow || Object.keys(availityWorkflow).length === 0) {
Logger.info(`Adding '"availityWorkflow": true' to package.json`);
Object.assign(pkg, { availityWorkflow: true });
}
Logger.info(`Setting version of eslint-config-availity to ${latestEslintVersion}`);
devDependencies['eslint-config-availity'] = `^${latestEslintVersion}`;

if (devDependencies) {
Logger.info('Removing devDependencies that are no longer needed...');

// Delete all deps that were previously required
Expand All @@ -69,67 +107,67 @@ module.exports = async (cwd) => {
delete devDependencies['availity-workflow-angular'];
delete devDependencies['@availity/workflow-plugin-react'];
delete devDependencies['@availity/workflow-plugin-angular'];
delete devDependencies['@typescript-eslint/eslint-plugin'];
delete devDependencies['@typescript-eslint/parser'];

// Get needed dependencies from eslint-config-availity
Logger.info('Adding peerDependencies from eslint-config-availity to devDependencies in project');

const { error, stdout, stderr } = await asyncExec(
`${installer} info eslint-config-availity peerDependencies --json`
);
if (!error && !stderr && stdout) {
// Both npm and yarn will return an object containing key/value pairs of dependencies
// npm will return only the peerDependencies object, but yarn nests them inside a data key
const eslintPkg = JSON.parse(stdout);
const peerDependencies = installer === 'npm' ? eslintPkg : eslintPkg.data;

Object.assign(devDependencies, peerDependencies);
peerInfoReceived = true;
} else {
Logger.info('Adding peerDependencies from eslint-config-availity to devDependencies in project...');

try {
const { error, stdout, stderr } = await asyncExec(
`${installer} info eslint-config-availity peerDependencies --json`
);
if (!error && !stderr && stdout) {
// Both npm and yarn will return an object containing key/value pairs of dependencies
// npm will return only the peerDependencies object, but yarn nests them inside a data key
const eslintPkg = JSON.parse(stdout);
const peerDependencies = installer === 'npm' ? eslintPkg : eslintPkg.data;

Object.assign(devDependencies, peerDependencies);
peerInfoReceived = true;
} else {
Logger.warn('Failed to get peerDependencies from eslint-config-availity');
}
} catch (error) {
Logger.warn('Failed to get peerDependencies from eslint-config-availity');
}
}

// Add this script into the new workflow scripts for the future
scripts['upgrade:workflow'] = './node_modules/.bin/upgrade-workflow';

// Check for deprecated workflow features
if (availityWorkflow?.plugin) {
Logger.warn(`Deprecated plugin feature detected. Removing "availityWorkflow.plugin" entry from package.json`);
delete availityWorkflow.plugin;
}

// If workflow entry didn't exist or plugin was its only key
if (!availityWorkflow || Object.keys(availityWorkflow).length === 0) {
Logger.info(`Adding '"availityWorkflow": true' to package.json`);
Object.assign(pkg, { availityWorkflow: true });
}

// Update package.json
Logger.info('Updating package.json...');
fs.writeFileSync(pkgFile, `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8');

// Delete each lockfile in case both exist
if (hasPkgLock) {
Logger.info('Deleting package-lock.json...');
fs.unlinkSync(pkgLock);
}
if (hasYarnLock) {
Logger.info('Deleting yarn.lock...');
fs.unlinkSync(yarnLock);
}

// Delete node_modules
Logger.info('Deleting node_modules...');
// Delete Node Modules
rimraf.sync(path.join(cwd, 'node_modules'));

const reinstallNodeModules = async () => {
Logger.info('Reinstalling packages..');

// Run install command
await asyncExec(`${installer} install`, { timeout: reinstallTimeout }, () => {
Logger.success('\nCongratulations! Welcome to the new @availity/workflow.');
if (!peerInfoReceived) {
Logger.warn(
'To complete your upgrade, please install the peerDependencies from eslint-config-availity as devDependencies in your project.'
);
}
});
};

Logger.info('Adding latest versions of @availity/workflow and eslint-config-availity');

if (installer === 'yarn') {
// yarn add package will grab the latest version of a package by default
await asyncExec(
`${installer} add @availity/workflow eslint-config-availity --dev`,
{ timeout: reinstallTimeout },
async () => {
await reinstallNodeModules();
}
);
} else if (installer === 'npm') {
// npm install package will respect semver if a package is already listed in project
await asyncExec(
`${installer} install @availity/workflow@latest eslint-config-availity@latest --save-dev`,
{ timeout: reinstallTimeout },
async () => {
await reinstallNodeModules();
}
);
}
await reinstallNodeModules(installer, peerInfoReceived);
} else {
Logger.failed('Could not find package.json');
}
};
4 changes: 4 additions & 0 deletions packages/workflow-upgrade/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@
},
"dependencies": {
"@availity/workflow-logger": "workspace:*",
"inquirer": "^8.2.5",
"read-pkg": "^5.2.0",
"rimraf": "^5.0.1"
},
"devDependencies": {
"@types/inquirer": "^8"
}
}
2 changes: 1 addition & 1 deletion packages/workflow-upgrade/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"executor": "@jscutlery/semver:version",
"options": {
"preset": "angular",
"commitMessageFormat": "chore(${projectName}): release version ${version}",
"commitMessageFormat": "chore(${projectName}): release version ${version} [skip ci]",
"tagPrefix": "@availity/${projectName}@"
}
}
Expand Down
23 changes: 22 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,8 @@ __metadata:
resolution: "@availity/workflow-upgrade@workspace:packages/workflow-upgrade"
dependencies:
"@availity/workflow-logger": "workspace:*"
"@types/inquirer": ^8
inquirer: ^8.2.5
read-pkg: ^5.2.0
rimraf: ^5.0.1
bin:
Expand Down Expand Up @@ -8812,6 +8814,16 @@ __metadata:
languageName: node
linkType: hard

"@types/inquirer@npm:^8":
version: 8.2.10
resolution: "@types/inquirer@npm:8.2.10"
dependencies:
"@types/through": "*"
rxjs: ^7.2.0
checksum: e576823345146e939e93e06fc5a81baa5231f0113b669191155cd5f5925b3e897d3a3c42c0be8b3e7b0b188b7e05d1cf42011cc2da4d123f7e58940caf9cd17f
languageName: node
linkType: hard

"@types/is-ci@npm:^3.0.0":
version: 3.0.0
resolution: "@types/is-ci@npm:3.0.0"
Expand Down Expand Up @@ -9250,6 +9262,15 @@ __metadata:
languageName: node
linkType: hard

"@types/through@npm:*":
version: 0.0.33
resolution: "@types/through@npm:0.0.33"
dependencies:
"@types/node": "*"
checksum: fd0b73f873a64ed5366d1d757c42e5dbbb2201002667c8958eda7ca02fff09d73de91360572db465ee00240c32d50c6039ea736d8eca374300f9664f93e8da39
languageName: node
linkType: hard

"@types/unist@npm:*, @types/unist@npm:^2.0.0, @types/unist@npm:^2.0.2, @types/unist@npm:^2.0.3":
version: 2.0.3
resolution: "@types/unist@npm:2.0.3"
Expand Down Expand Up @@ -25740,7 +25761,7 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard

"rxjs@npm:7.8.1, rxjs@npm:^7.8.0":
"rxjs@npm:7.8.1, rxjs@npm:^7.2.0, rxjs@npm:^7.8.0":
version: 7.8.1
resolution: "rxjs@npm:7.8.1"
dependencies:
Expand Down

0 comments on commit 460385c

Please sign in to comment.