diff --git a/packages/workflow-upgrade/index.js b/packages/workflow-upgrade/index.js index e18d9b68..2514f1f1 100755 --- a/packages/workflow-upgrade/index.js +++ b/packages/workflow-upgrade/index.js @@ -5,9 +5,37 @@ 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'); @@ -15,44 +43,54 @@ module.exports = async (cwd) => { 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 @@ -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'); } }; diff --git a/packages/workflow-upgrade/package.json b/packages/workflow-upgrade/package.json index 4c3ede63..afe51ba0 100644 --- a/packages/workflow-upgrade/package.json +++ b/packages/workflow-upgrade/package.json @@ -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" } } diff --git a/packages/workflow-upgrade/project.json b/packages/workflow-upgrade/project.json index 51d4d9d2..6277bb8a 100644 --- a/packages/workflow-upgrade/project.json +++ b/packages/workflow-upgrade/project.json @@ -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}@" } } diff --git a/yarn.lock b/yarn.lock index 86f3dc5e..17005cf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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: @@ -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" @@ -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" @@ -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: