Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add buildManagerOptions config option (supports opting out of --ignore-lockfile) #409

Merged
merged 1 commit into from Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Expand Up @@ -126,6 +126,15 @@ module.exports = function() {
dependencies will be restored to their prior state.
*/
useYarn: true,

/*
buildManagerOptions allows you to opt-out of the default options such as `--ignore-engines --no-lockfile`.
The buildManagerOptions function is aware of each scenario so you can customize your options.
*/
buildManagerOptions(scenario) {
return ['--ignore-engines'];
}

scenarios: [
{
name: 'Ember 1.10 with ember-data',
Expand Down
82 changes: 69 additions & 13 deletions lib/dependency-manager-adapters/npm.js
Expand Up @@ -16,11 +16,16 @@ module.exports = CoreObject.extend({
},
useYarnCommand: false,
yarnLock: 'yarn.lock',
yarnLockBackupFileName: 'yarn.lock.ember-try',
configKey: 'npm',
packageJSON: 'package.json',
packageJSONBackupFileName: 'package.json.ember-try',
nodeModules: 'node_modules',
nodeModulesBackupLocation: '.node_modules.ember-try',
npmShrinkWrap: 'npm-shrinkwrap.json',
npmShrinkWrapBackupFileName: 'npm-shrinkwrap.json.ember-try',
packageLock: 'package-lock.json',
packageLockBackupFileName: 'package-lock.json.ember-try',
setup(options) {
if (!options) {
options = {};
Expand All @@ -33,7 +38,7 @@ module.exports = CoreObject.extend({

adapter.applyDependencySet(depSet);

return adapter._install().then(() => {
return adapter._install(depSet).then(() => {
let deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {});
let currentDeps = Object.keys(deps).map((dep) => {
return {
Expand All @@ -58,6 +63,18 @@ module.exports = CoreObject.extend({
let cleanupTasks = [rimraf(path.join(adapter.cwd, adapter.packageJSONBackupFileName)),
rimraf(path.join(adapter.cwd, adapter.nodeModulesBackupLocation))];

if (fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.yarnLockBackupFileName)));
rwjblue marked this conversation as resolved.
Show resolved Hide resolved
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrapBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.npmShrinkWrapBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLockBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.packageLockBackupFileName)));
}

return RSVP.all(cleanupTasks);
}).catch((e) => {
console.log('Error cleaning up npm scenario:', e); // eslint-disable-line no-console
Expand All @@ -82,26 +99,35 @@ module.exports = CoreObject.extend({
return null;
}
},
_install() {
_install(depSet) {
let adapter = this;
let mgrOptions = this.managerOptions || [];
let cmd = this.useYarnCommand ? 'yarn' : 'npm';

debug('Run npm install with options %s', mgrOptions);
// buildManagerOptions overrides all default
if (typeof this.buildManagerOptions === 'function') {
mgrOptions = this.buildManagerOptions(depSet);

let cmd = this.useYarnCommand ? 'yarn' : 'npm';
if (this.useYarnCommand) {
if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
if (!Array.isArray(mgrOptions)) {
throw new Error('buildManagerOptions must return an array of options');
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
} else {
if (this.useYarnCommand) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we intend to mandate the buildManagerOptions in the future? If so, we can add a deprecation here that encourages to use the manager options for these defaults.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we intend to mandate the buildManagerOptions in the future?

No, I believe that it will always be a "power user" option. I would expect that most users would use whatever our default command line options are, and you'd only specify buildManagerOptions if you had to deviate from that for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree; it is backward compatible and users do not need to use it if there isn't a need.

if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
}
} else if (mgrOptions.indexOf('--no-shrinkwrap') === -1) {
mgrOptions = mgrOptions.concat(['--no-shrinkwrap']);
}
} else if (mgrOptions.indexOf('--no-shrinkwrap') === -1) {
mgrOptions = mgrOptions.concat(['--no-shrinkwrap']);
}

debug('Run npm/yarn install with options %s', mgrOptions);

return this.run(cmd, [].concat(['install'], mgrOptions), { cwd: this.cwd }).then(() => {
if (!adapter.useYarnCommand) {
return adapter.run('npm', ['--version'], { cwd: this.cwd, stdio: 'pipe' }).then((res) => {
Expand Down Expand Up @@ -171,6 +197,21 @@ module.exports = CoreObject.extend({
path.join(this.cwd, this.nodeModules), { clobber: true }),
];

if (fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.yarnLockBackupFileName),
path.join(this.cwd, this.yarnLock)));
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrapBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.npmShrinkWrapBackupFileName),
path.join(this.cwd, this.npmShrinkWrap)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLockBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.packageLockBackupFileName),
path.join(this.cwd, this.packageLock)));
}

return RSVP.all(restoreTasks);
},
_backupOriginalDependencies() {
Expand All @@ -184,6 +225,21 @@ module.exports = CoreObject.extend({
copy(path.join(this.cwd, this.nodeModules),
path.join(this.cwd, this.nodeModulesBackupLocation), { clobber: true })];

if (fs.existsSync(path.join(this.cwd, this.yarnLock))) {
backupTasks.push(copy(path.join(this.cwd, this.yarnLock),
path.join(this.cwd, this.yarnLockBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrap))) {
backupTasks.push(copy(path.join(this.cwd, this.npmShrinkWrap),
path.join(this.cwd, this.npmShrinkWrapBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLock))) {
backupTasks.push(copy(path.join(this.cwd, this.packageLock),
path.join(this.cwd, this.packageLockBackupFileName)));
}

return RSVP.all(backupTasks);
},
});
32 changes: 21 additions & 11 deletions lib/dependency-manager-adapters/workspace.js
Expand Up @@ -47,6 +47,7 @@ module.exports = CoreObject.extend({
run: this.run,
managerOptions: this.managerOptions,
useYarnCommand: true,
buildManagerOptions: this.buildManagerOptions,
});
});

Expand All @@ -59,7 +60,7 @@ module.exports = CoreObject.extend({
adapter.applyDependencySet(depSet);
});

return this._install().then(() => {
return this._install(depSet).then(() => {
let deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {});
let currentDeps = Object.keys(deps).map((dep) => {
return {
Expand All @@ -80,20 +81,29 @@ module.exports = CoreObject.extend({
return RSVP.all(this._packageAdapters.map(adapter => adapter.cleanup()));
},

_install() {
_install(depSet) {
let mgrOptions = this.managerOptions || [];

debug('Run yarn install with options %s', mgrOptions);

if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
// buildManagerOptions overrides all default
if (typeof this.buildManagerOptions === 'function') {
mgrOptions = this.buildManagerOptions(depSet);

if (!Array.isArray(mgrOptions)) {
throw new Error('buildManagerOptions must return an array of options');
}
} else {
if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
}
}

debug('Run yarn install with options %s', mgrOptions);

return this.run('yarn', [].concat(['install'], mgrOptions), { cwd: this.cwd });
},

Expand Down
5 changes: 3 additions & 2 deletions lib/utils/dependency-manager-adapter-factory.js
Expand Up @@ -31,11 +31,12 @@ module.exports = {
new WorkspaceAdapter({
cwd: root,
managerOptions: config.npmOptions,
useYarnCommand: config.useYarn
useYarnCommand: config.useYarn,
buildManagerOptions: config.buildManagerOptions
})
);
} else if (hasNpm || hasBower) {
adapters.push(new NpmAdapter({ cwd: root, managerOptions: config.npmOptions, useYarnCommand: config.useYarn }));
adapters.push(new NpmAdapter({ cwd: root, managerOptions: config.npmOptions, useYarnCommand: config.useYarn, buildManagerOptions: config.buildManagerOptions }));
adapters.push(new BowerAdapter({ cwd: root, managerOptions: config.bowerOptions }));
}

Expand Down
114 changes: 114 additions & 0 deletions test/dependency-manager-adapters/npm-adapter-test.js
Expand Up @@ -39,6 +39,24 @@ describe('npmAdapter', () => {
assertFileContainsJSON(path.join(tmpdir, '.node_modules.ember-try/prove-it.json'), { originalNodeModules: true });
});
});

it('backs up the yarn.lock file, npm-shrinkwrap.json and package-lock.json if they exist', () => {
fs.mkdirSync('node_modules');
writeJSONFile('node_modules/prove-it.json', { originalNodeModules: true });
writeJSONFile('package.json', { originalPackageJSON: true });
writeJSONFile('yarn.lock', { originalYarnLock: true });
writeJSONFile('npm-shrinkwrap.json', { originalNpmShrinkWrap: true });
writeJSONFile('package-lock.json', { originalPackageLock: true });
return new NpmAdapter({
cwd: tmpdir,
}).setup().then(() => {
assertFileContainsJSON(path.join(tmpdir, 'package.json.ember-try'), { originalPackageJSON: true });
assertFileContainsJSON(path.join(tmpdir, '.node_modules.ember-try/prove-it.json'), { originalNodeModules: true });
assertFileContainsJSON(path.join(tmpdir, 'yarn.lock.ember-try'), { originalYarnLock: true });
assertFileContainsJSON(path.join(tmpdir, 'npm-shrinkwrap.json.ember-try'), { originalNpmShrinkWrap: true });
assertFileContainsJSON(path.join(tmpdir, 'package-lock.json.ember-try'), { originalPackageLock: true });
});
});
});

describe('#_install', () => {
Expand Down Expand Up @@ -127,6 +145,46 @@ describe('npmAdapter', () => {
expect(runCount).to.equal(2);
});
});

it('uses buildManagerOptions for npm commands', () => {
writeJSONFile('package.json', fixturePackage);
let runCount = 0;
let stubbedRun = generateMockRun([{
command: 'npm install --flat',
callback() {
runCount++;
return RSVP.resolve();
},
}, {
command: 'npm --version',
callback() {
runCount++;
return RSVP.resolve({stdout: '5.7.1'});
},
}], { allowPassthrough: false });

return new NpmAdapter({
cwd: tmpdir,
run: stubbedRun,
buildManagerOptions: function() {
return ['--flat'];
},
})._install().then(() => {
expect(runCount).to.equal(2, 'npm install should run with buildManagerOptions');
});
});

it('throws an error if buildManagerOptions does not return an array', () => {
expect(() => {
new NpmAdapter({
cwd: tmpdir,
run: () => {},
buildManagerOptions: function() {
return 'string';
},
})._install()
}).to.throw(/buildManagerOptions must return an array of options/);
});
});

describe('with yarn', () => {
Expand Down Expand Up @@ -171,6 +229,42 @@ describe('npmAdapter', () => {
expect(runCount).to.equal(1, 'Only yarn install should run with manager options');
});
});

it('uses buildManagerOptions for yarn commands', () => {
writeJSONFile('package.json', fixturePackage);
let runCount = 0;
let stubbedRun = generateMockRun([{
command: 'yarn install --flat',
callback() {
runCount++;
return RSVP.resolve();
},
}], { allowPassthrough: false });

return new NpmAdapter({
cwd: tmpdir,
run: stubbedRun,
useYarnCommand: true,
buildManagerOptions: function() {
return ['--flat'];
},
})._install().then(() => {
expect(runCount).to.equal(1, 'Only yarn install should run with buildManagerOptions');
});
});

it('throws an error if buildManagerOptions does not return an array', () => {
expect(() => {
new NpmAdapter({
cwd: tmpdir,
run: () => {},
useYarnCommand: true,
buildManagerOptions: function() {
return 'string';
},
})._install()
}).to.throw(/buildManagerOptions must return an array of options/);
});
});
});

Expand All @@ -185,6 +279,26 @@ describe('npmAdapter', () => {
assertFileContainsJSON(path.join(tmpdir, 'node_modules/prove-it.json'), { originalNodeModules: true });
});
});

it('replaces the yarn.lock, npm-shrinkwrap.json and package-lock.json with the backed up version if they exist', () => {
writeJSONFile('package.json.ember-try', { originalPackageJSON: true });
writeJSONFile('package.json', { originalPackageJSON: false });
fs.mkdirSync('.node_modules.ember-try');
writeJSONFile('.node_modules.ember-try/prove-it.json', { originalNodeModules: true });
writeJSONFile('yarn.lock.ember-try', { originalYarnLock: true });
writeJSONFile('yarn.lock', { originalYarnLock: false });
writeJSONFile('npm-shrinkwrap.json.ember-try', { originalNpmShrinkWrap: true });
writeJSONFile('npm-shrinkwrap.json', { originalNpmShrinkWrap: false });
writeJSONFile('package-lock.json.ember-try', { originalPackageLock: true });
writeJSONFile('package-lock.json', { originalPackageLock: false });
return new NpmAdapter({ cwd: tmpdir })._restoreOriginalDependencies().then(() => {
assertFileContainsJSON(path.join(tmpdir, 'package.json'), { originalPackageJSON: true });
assertFileContainsJSON(path.join(tmpdir, 'node_modules/prove-it.json'), { originalNodeModules: true });
assertFileContainsJSON(path.join(tmpdir, 'yarn.lock'), { originalYarnLock: true });
assertFileContainsJSON(path.join(tmpdir, 'npm-shrinkwrap.json'), { originalNpmShrinkWrap: true });
assertFileContainsJSON(path.join(tmpdir, 'package-lock.json'), { originalPackageLock: true });
});
});
});

describe('#_packageJSONForDependencySet', () => {
Expand Down