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

fix: fully support 'extends' for configurations #4407

Closed
wants to merge 2 commits into from

Conversation

mf-pherlihy
Copy link

Requirements

  • Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
  • All new code requires tests to ensure against regressions.

Description of the Change

There have been multiple issues reported where configurations do not actually support the extends property:

#3916
#4057

I did some digging and found the root cause to be the options.parse() method. This method relies on yargs-parser and NOT yargs which does the actual configuration extending.

It was a pretty straightforward fix. I imported the yargs function that extends the configuration objects and then applied it to all the arguments passed to parse().

I added a test to support this case and verified the test fails when this fix is not applied.

Alternate Designs

There was already an existing PR open that modified external methods to do this, so I wanted to avoid that.

Why should this be in core?

It fixes a long standing bug.

Benefits

Configuration files can now be extended properly.

Possible Drawbacks

I dont see any?

Applicable issues

@jsf-clabot
Copy link

jsf-clabot commented Aug 13, 2020

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@boneskull boneskull left a comment

Choose a reason for hiding this comment

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

great work, thanks! I'm a little concerned that yargs/lib/apply-extends isn't a public API. If it isn't, maybe we can vendor it (e.g., just copy/paste it into a function in lib/cli/options.js). @bcoe can you comment?

@boneskull boneskull added type: bug a defect, confirmed by a maintainer semver-patch implementation requires increase of "patch" version number; "bug fixes" labels Aug 18, 2020
Copy link
Contributor

@outsideris outsideris left a comment

Choose a reason for hiding this comment

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

The test seems unit test rather than integration tests. So, test/node-unit/cli/ is better place where we put the test in. What do you think?

@boneskull
Copy link
Contributor

Yeah, I think that test should probably live in test/node-unit/cli/options.spec.js instead.

@bcoe
Copy link
Contributor

bcoe commented Aug 28, 2020

I'm a little concerned that yargs/lib/apply-extends isn't a public API. If it isn't, maybe we can vendor it (e.g., just copy/paste it into a function in lib/cli/options.js). @bcoe can you comment?

I would be happy to make this a public API, but as this PR stands today, it will be broken by the next major version of yargs, which moves to using an export map.

I'd be happy to export this helper though.

@boneskull
Copy link
Contributor

@bcoe If you want to add it to the export map, that'd work for us--we can change our code to use it when we upgrade.

@boneskull
Copy link
Contributor

@mf-pherlihy Are you interested in doing further work on this? I'd like to merge the PR, but the test should probably move. If I don't hear back in a couple days, I'll merge this as-is and fix it after.

@mf-pherlihy
Copy link
Author

mf-pherlihy commented Aug 31, 2020

@boneskull Yeah I can finish this up. Just to confirm:

1 . Move to test/node-unit/cli/options.spec.js
2a. Copy applyExtends from yargs to here with a TODO to swap out once yargs is updated?
2b. Use applyExtends as-is with a TODO to swap out once yargs is updated?

@boneskull
Copy link
Contributor

  1. Move the test
  2. AFAICT you are free to use applyExtends as it has been made part of yargs' public API. But it sounds like when we upgrade yargs we will need to change it? Unclear, since we're pulling it out of yargs-parser and not yargs itself, right? @bcoe?

@boneskull
Copy link
Contributor

ping @bcoe @mf-pherlihy

@bcoe
Copy link
Contributor

bcoe commented Nov 3, 2020

AFAICT you are free to use applyExtends

Yes, my intention was that it's a blessed public helper now. The caveat is that we need to fix this.

@mf-pherlihy
Copy link
Author

Just tried upgrading yargs and using the defined helpers. Unfortunately however, the latest version breaks a bunch of other tests.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 94.157% when pulling 105d1aa on mf-pherlihy:extends-fix into 30d5b66 on mochajs:master.

@bcoe
Copy link
Contributor

bcoe commented Jan 27, 2021

@mf-pherlihy [email protected] introduces ESM, and was a somewhat major shuffling around of code. I'd be interested in making sure we address any issues you're bumping into with tests.

@juergba
Copy link
Contributor

juergba commented Jan 29, 2021

@bcoe I did upgrade yargs and yargs-parser with #4543. There have been changes in yargs' array handling with yargs.config() and combinig array values. I had to remove aliased options from our pre-parsed result (by yargs-parser) in order to prevent double entries.
I do not consider the new behavior as a bug, it seems more consistent now than before.

@bcoe
Copy link
Contributor

bcoe commented Jan 29, 2021

I do not consider the new behavior as a bug, it seems more consistent now than before.

@juergba that's good to hear 👍

Sound like we perhaps just need to rebase this and give the helpers another try?

@juergba
Copy link
Contributor

juergba commented Jan 29, 2021

Sound like we perhaps just need to rebase this and give the helpers another try?

I guess so, but I haven't taken a look at this PR though. But please go ahead @mf-pherlihy.
The browser tests will fail anyway, but that's off topic. Github actions does not have access to GH secrets (login data for SauceLabs) on PR's from forked repos. We haven't solved that, yet.

@juergba
Copy link
Contributor

juergba commented Feb 13, 2021

@mf-pherlihy I rebased and tests are passing with yargs public helpers.

@juergba
Copy link
Contributor

juergba commented Feb 13, 2021

Priority list:
 * 1. Command-line args
 * 2. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
 * 3. `mocha` prop of `package.json`
 * 4. default configuration (`lib/mocharc.json`)

I have to check to which configObjects you are applying extends. To RC files only or to package.json mocha field as well.
And when the extends values are applied, wether the priority is respected.
I'm unsure wether this is correct.

Copy link
Contributor

@juergba juergba left a comment

Choose a reason for hiding this comment

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

@mf-pherlihy I did some testing and have found quite a few issues:

  • extends does not work for relative paths
  • array typed options: values are not combined, the configuration of yargs-parser seems to be ignored by applyExtends.
  • boolean typed option: is buggy, eg no-diff: true gets overwritten by default value: diff: true
  • package.json with field mocha: haven't tested yet, but ?

As an alternative without yargs public helper:
we just let options.parse() do the job by passing additional configObjects. The order would be crucial.
configObjects: [{base RC-options}, {extends RC-options}, {base pkg options}, {extends pkg options}]

What do you think?

@mf-pherlihy
Copy link
Author

@mf-pherlihy I did some testing and have found quite a few issues:

  • extends does not work for relative paths
  • array typed options: values are not combined, the configuration of yargs-parser seems to be ignored by applyExtends.
  • boolean typed option: is buggy, eg no-diff: true gets overwritten by default value: diff: true
  • package.json with field mocha: haven't tested yet, but ?

As an alternative without yargs public helper:
we just let options.parse() do the job by passing additional configObjects. The order would be crucial.
configObjects: [{base RC-options}, {extends RC-options}, {base pkg options}, {extends pkg options}]

What do you think?

If we can cover all the issues with yargs-parser and still fix the root issue with extends then im all for it.

@bcoe
Copy link
Contributor

bcoe commented Mar 12, 2021

@mf-pherlihy if any of this behavior would make sense in yargs/yargs-parser, happy to take patches.

@SmashingQuasar
Copy link

👋 Where are we at regarding this PR?
Extending configuration is essential to properly manage mono-repositories.
If you need any help to finish it just say the word and I'll happily look into whatever issue remains. 👍

@JoshuaKGoldberg
Copy link
Member

Note: #4980 also touches this area. We should review them together in case one is a dup.

Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

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

Seems like a reasonable start - and I like the test case!

I'm interested in what you think of this approach compared to #4980's. If possible, it'd be nice to avoid having to reach into lower-level helpers. But I'm not super familiar with yargs and would love to know if that's misguided here. 🙂

const configObject = config ? loadConfig(config) : {};

// Set _configPath for use by parse()
configObject._configPath = config;
Copy link
Member

Choose a reason for hiding this comment

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

[Refactor] I'm not a big fan of setting "magic" properties that are only used by specific other places. They get hard to keep track of and can accidentally end up being exposed to users. Which is what's happening here: loadRc is a @public function and thus part of the public API. https://mochajs.org/api/module-lib_cli#.loadRc

I think the root need for this code change is that the config value also needs to be available in parse(), right? If so: how about extracting the contents of this if into a private shared function, then utilizing that function in two places?

  if (args.config !== false) {
    return myNewFancyFunction(args).configObject;
  }

(roughly that, but maybe with more clear naming?)

Copy link
Member

Choose a reason for hiding this comment

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

[Question] #4980 is also meant to fix extends, and has less code. It looks like #4980 uses the higher-level yargs/yargs -> yargs() instead of this PR's yargs/helpers -> applyExtends. I think it's normally cleaner to use higher-level functions when possible. They tend to do more of the work for consumers - as seen in #4980. Is there an advantage to applyExtends?

Copy link
Member

Choose a reason for hiding this comment

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

👋 @mf-pherlihy was hoping to get your thoughts here. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

[Praise] Really clean test, nice 🙂

var loadOptions = require('../../lib/cli/options').loadOptions;

describe('options', function() {
it('Should support extended options', function() {
Copy link
Member

Choose a reason for hiding this comment

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

[Style] Nit: other tests in this repo generally lower-case the 's'. For consistency...

Suggested change
it('Should support extended options', function() {
it('should support extended options', function() {

@JoshuaKGoldberg JoshuaKGoldberg added the status: waiting for author waiting on response from OP - more information needed label Mar 1, 2024
@JoshuaKGoldberg JoshuaKGoldberg changed the title Fixed a bug where configurations did not actually support extends fix: fully support 'extends' for configurations Mar 1, 2024
@JoshuaKGoldberg JoshuaKGoldberg added semver-major implementation requires increase of "major" version number; "breaking changes" and removed semver-patch implementation requires increase of "patch" version number; "bug fixes" labels Mar 1, 2024
@JoshuaKGoldberg
Copy link
Member

Marking as semver-major because some projects might accidentally be relying on the current, arguably broken behavior.

@JoshuaKGoldberg
Copy link
Member

👋 ping @mf-pherlihy, do you still have time for this?

@JoshuaKGoldberg JoshuaKGoldberg added the stale this has been inactive for a while... label Jul 2, 2024
@JoshuaKGoldberg
Copy link
Member

Closing out as it's been a while since PR activity. If anybody wants to take this over, please do - and post a co-author attribution if you take code from this PR. Cheers! 🤎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver-major implementation requires increase of "major" version number; "breaking changes" stale this has been inactive for a while... status: waiting for author waiting on response from OP - more information needed type: bug a defect, confirmed by a maintainer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants