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

feat: Discover config files in home dir, working dir #1161

Merged
merged 2 commits into from Dec 21, 2017

Conversation

kumar303
Copy link
Contributor

Fixes #730

@kumar303 kumar303 requested a review from rpl December 15, 2017 17:21
@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 99358ed on kumar303:discover-configs-730 into f4ff99a on mozilla:master.

Copy link
Member

@rpl rpl left a comment

Choose a reason for hiding this comment

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

@kumar303 follows a round of review comments (mostly nits and small tweaks on the implementation and new tests)

I was also wondering if it could make sense to apply the tweaks to the flow type annotations related to the global and command options as part of this pull request (e.g. as described in #1144), especially if it doesn't require a big amount of changes to make it flow happy with the "cli options type checking".

I don't see it as a mandatory change for this pull request, but it could be nice addition, given that once we merge this pull request the config file support seems pretty complete (and, as previously discussed, a more precise flow type annotation of the options could be useful to prevent regressions related to the config file loading when we add new command line options).

src/config.js Outdated
argv: Object,
// This is the argv that only has CLI values applies to it.
Copy link
Member

Choose a reason for hiding this comment

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

Nit, s/applies/applied/

// Look for a magic hidden config (preceded by dot) in home dir.
path.join(getHomeDir(), `.${magicConfigName}`),
// Look for a magic config in the current working directory.
path.join(process.cwd(), magicConfigName),
Copy link
Member

Choose a reason for hiding this comment

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

Not an issue (just a proposed "nice to have" for a follow up):

we could also support config options loaded from a section in the package.json file (e.g. a "web-ext" section, similarly to how other nodejs tools support config loading from the package.json file, as an example babel and jest both support it).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. Filed: #1166

src/config.js Outdated
log.debug(
`Discovered config "${resolvedFileName}" does not ` +
'exist or is not readable');
return false;
Copy link
Member

Choose a reason for hiding this comment

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

uhm... I'm surprised that flow is not complaining here :-)

This async arrow function is basically returning a value of type Promise<string | boolean>, I would probably prefer if we return undefined when the file does not exist, so that the returned type is a "Maybe type" like Promise<?string>.

));

const existingConfigs = [];
configs.forEach((f) => {
Copy link
Member

Choose a reason for hiding this comment

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

If we opt for the Maybe type Promise<?string>, we have to change if (f) { ... } into if (typeof(f) === 'string') {...}.

Also how about using configs.filter and returning its result (return configs.filter((f) => typeof(f) === 'string');) so that we don't need to accumulate them in the new existentConfig array?

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'll take another look but I think filter() was failing because Flow doesn't seem to understand that the returned array will not contain undefined values (or something like that).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the best workaround I found:

  const files = (
    (configs.filter((f) => typeof f === 'string'): Array<any>): Array<string>
  );
  return files;

That's too confusing though. I think it's probably best to leave it as is.

The Flow bug: facebook/flow#1414

Copy link
Member

Choose a reason for hiding this comment

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

yeah, I agree, better to leave it as it is (which is also probably much more readable to a contributor that is not so used to the flow syntaxes).

src/program.js Outdated
@@ -37,6 +39,7 @@ type ExecuteOptions = {
loadJSConfigFile?: typeof defaultLoadJSConfigFile,
shouldExitProgram?: boolean,
globalEnv?: string,
discoverConfigFiles?: typeof defaultConfigDiscovery,
Copy link
Member

Choose a reason for hiding this comment

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

Nit, I would prefer it near line 39 or 38.

@@ -474,6 +505,40 @@ describe('config', () => {
assert.strictEqual(newArgv.apiKey, cmdApiKey);
});

it('can load multiple configs', () => {
Copy link
Member

Choose a reason for hiding this comment

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

This test looks pretty similar to the one at line 252, but on the sign command instead of a fake command, aand with commantOpt instead of globalOpt.

if we need both these tests, it would be nice to extract the common part into a test helper function defined in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, they are both necessary. One will apply a global option twice and the other will apply a sub-command option twice. The sub-command test covers some recursion.

I added some comments and improved the test specs to make it more clear. I couldn't really see any code that could be abstracted in a helpful way.

Copy link
Member

Choose a reason for hiding this comment

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

👍 the comments and the changes to the test names are useful and more then enough to quickly notice how the two tests are different in purpose (so that we don't end up to remove them by mistake by missing their different purpose), looks good to me.

},
};
// Instead of loading/parsing a real file, just return an object.
const fakeLoadJSConfigFile = sinon.spy(() => {
Copy link
Member

Choose a reason for hiding this comment

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

This is defined as a sinon.spy, we could use it to assert that it is called with the expected filename, am I right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, fixed

{
commands: fakeCommands,
runOptions: {
discoverConfigFiles: async () => ['fake/config.js'],
Copy link
Member

Choose a reason for hiding this comment

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

we could also wrap this discoverConfigFiles into a sinon.spy and check that it has been called as expected.

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 prefer to check things like that implicitly when possible. For this test case, if discoverConfigFiles is not called, you get this error:

  1) program.main discovers config files:
     AssertionError: expected false to equal true
      at _callee5$ (dist/webpack:/tests/unit/test.program.js:556:12)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
      at GeneratorFunctionPrototype.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
      at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
      at step (dist/webpack:/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:1)
      at dist/webpack:/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:1

When you look up the line of the assertion failure you will see this:

    // This makes sure that the config object was applied
    // to the lint command options.
    assert.equal(
      options.selfHosted, configObject.lint.selfHosted);

I think that will be helpful to diagnose the problem.

lint: () => Promise.resolve(),
});

const discoverConfigFiles = sinon.spy(() => Promise.resolve([]));
Copy link
Member

Choose a reason for hiding this comment

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

Not mandatory, but we could do the same for loadJSConfigFile (defining it as a sinon.spy and assert that it has never been called).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test you commented on is just about disabling config discovery. I think it's enough coverage to just assert that the discovery function is not called. Without that call, there is no way to load a discovered file.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, I was thinking more about a possible "future regression" where some new code could try to call loadJSConfigFile without having called discoverConfigFile first, but that's more likely to be just "me being too paranoid" :-P

}
);

const options = fakeCommands.lint.firstCall.args[0];
Copy link
Member

Choose a reason for hiding this comment

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

This tests should test that the config file are loaded and applied in the expected order, how about wrapping loadJSConfigFile in a sinon.spy and assert that it has been called with the filenames in the expected order?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, yeah, thanks. This test wasn't checking the order at all. Fixed.

@kumar303
Copy link
Contributor Author

I was also wondering if it could make sense to apply the tweaks to the flow type annotations related to the global and command options as part of this pull request

I think we should defer this because I tried it and fell down a rabbit hole. It's a tough change because the test suite needs a ton of fixes to pass the right default CLI params to each command callback. This is something we should have done all along, whoops.

@kumar303 kumar303 requested a review from rpl December 21, 2017 17:37
@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 272b961 on kumar303:discover-configs-730 into f4ff99a on mozilla:master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants