Skip to content

Commit

Permalink
Throw on incorrect pnpm resolution-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
lolmaus committed Sep 26, 2023
1 parent 188b123 commit db322b3
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 189 deletions.
92 changes: 26 additions & 66 deletions lib/dependency-manager-adapters/pnpm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ const fs = require('fs-extra');
const path = require('path');
const debug = require('debug')('ember-try:dependency-manager-adapter:pnpm');
const Backup = require('../utils/backup');
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);
const semverLt = require('semver/functions/lt');
const semverGte = require('semver/functions/gte');

const PACKAGE_JSON = 'package.json';
const PNPM_LOCKFILE = 'pnpm-lock.yaml';
const NPMRC_CONFIG = '.npmrc';

module.exports = CoreObject.extend({
// This still needs to be `npm` because we're still reading the dependencies
Expand All @@ -26,8 +23,8 @@ module.exports = CoreObject.extend({
},

async setup() {
await this._throwOnResolutionMode();
await this.backup.addFiles([PACKAGE_JSON, PNPM_LOCKFILE]);
await this._updateNpmRc();
},

async changeToDependencySet(depSet) {
Expand All @@ -54,7 +51,6 @@ module.exports = CoreObject.extend({
await this.backup.restoreFiles([PACKAGE_JSON, PNPM_LOCKFILE]);
await this.backup.cleanUp();
await this._install();
await this._revertNpmRc();
} catch (e) {
console.log('Error cleaning up scenario:', e); // eslint-disable-line no-console
}
Expand All @@ -81,76 +77,40 @@ module.exports = CoreObject.extend({
* - pnpm version is within dangerous range and .npmrc exists — backup the .npmrc file and
* append` resolution-mode = highest` to .npmrc
*
* @param {undefined | string} version — this is only used in testing. Call this fucntion without
* arguments
* @returns Promise<void>
*/
async _updateNpmRc(version) {
if (!version) {
version = await this._getPnpmVersion();
}

if (!this._doesPnpmRequireResolutionModeFix(version)) {
return;
}

let npmrcPath = path.join(this.cwd, NPMRC_CONFIG);

if (fs.existsSync(npmrcPath)) {
await this.backup.addFile(NPMRC_CONFIG);

await fs.appendFileSync(npmrcPath, '\nresolution-mode = highest\n');
} else {
fs.writeFileSync(npmrcPath, 'resolution-mode = highest\n');
async _throwOnResolutionMode() {
let version = await this._getPnpmVersion();
let resolutionMode = await this._getResolutionMode();

if (this._isResolutionModeWrong(version, resolutionMode)) {
throw new Error(
'You are using an old version of pnpm that uses wrong resolution mode that violates ember-try expectations. Please either upgrade pnpm or set `resolution-mode` to `highest` in `.npmrc`.'
);
}
},

/**
* pnpm versions 8.0.0 through 8.6.* have the `resolution-mode` setting inverted to
* `lowest-direct`, which breaks ember-try. This method conditionally adds the necessary config to
* .npmrc to fix this.
*
* It covers the following cases:
* - pnpm version is out of dangerious range or cannot be retrieved — do not do anything
* - pnpm version is within dangerous range and the backup does not exist — delete the .npmrc
* - pnpm version is within dangerous range and the backup exists — rename the backup to .npmrc,
* overwriting the current .npmrc
*
* @param {undefined | string} version — this is only used in testing. Call this fucntion without
* arguments
* @returns Promise<void>
*/
async _revertNpmRc(version) {
if (!version) {
version = await this._getPnpmVersion();
}

if (!this._doesPnpmRequireResolutionModeFix(version)) {
return;
}

let npmrcPath = path.join(this.cwd, NPMRC_CONFIG);

if (this.backup.hasFile(NPMRC_CONFIG)) {
await this.backup.restoreFile(NPMRC_CONFIG);
} else {
if (fs.existsSync(npmrcPath)) {
fs.removeSync(npmrcPath);
}
}
async _getPnpmVersion() {
let result = await this.run('pnpm', ['--version'], { cwd: this.cwd });
return result.stdout.split('\n')[0];
},

async _getPnpmVersion(command = 'pnpm --version') {
try {
let result = await exec(command);
return result.stdout.split('\n')[0];
} catch (error) {
return null;
}
async _getResolutionMode() {
let result = await this.run('pnpm', ['config', 'get', 'resolution-mode'], { cwd: this.cwd });

return result.stdout.split('\n')[0];
},

_doesPnpmRequireResolutionModeFix(versionStr) {
return versionStr ? semverGte(versionStr, '8.0.0') && semverLt(versionStr, '8.7.0') : false;
_isResolutionModeWrong(versionStr, resolutionMode) {
// prettier-ignore
return (
resolutionMode.length
&& resolutionMode !== 'highest'
) || (
!resolutionMode.length
&& semverGte(versionStr, '8.0.0')
&& semverLt(versionStr, '8.7.0')
);
},

async _install(depSet) {
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const execa = require('execa');
const debug = require('debug')('ember-try:utils:run');

module.exports = async function run(command, args, _options) {
let options = Object.assign({ stdio: 'inherit', shell: true }, _options);
let options = Object.assign({ stdio: 'pipe', shell: true }, _options);

if (process.env.SHUT_UP) {
options.stdio = 'ignore';
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"devDependencies": {
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"codecov": "^3.8.3",
"ember-cli": "~3.22.0",
"eslint": "^7.31.0",
Expand Down
Loading

0 comments on commit db322b3

Please sign in to comment.