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

Update get-package-info #505

Merged
merged 9 commits into from
Oct 29, 2016

Conversation

rahatarmanahmed
Copy link
Contributor

Have you read the section in CONTRIBUTING.md about pull requests? Yes

Summarize your changes:

As discussed in #461, this updates get-package-info to v1.0.0, which includes more helpful errors when failing to find all info requested. This lets us give better error messages, as well as ignore missing props for optional options.

Are your changes appropriately documented? N/A

Do your changes have sufficient test coverage? Not sure yet.

Does the testsuite pass successfully on your local machine? Yes

This updates `get-package-info` to v1.0.0, which includes more helpful errors when failing to find all info requested. This lets us give better error messages, as well as ignore missing props for optional options.
}
})

err.message = messages.join('\n') + '\n' + err.message
Copy link
Contributor Author

Choose a reason for hiding this comment

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

May want to hide original error message, since the message we add should be descriptive enough without it?

Copy link
Member

Choose a reason for hiding this comment

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

original error message

The one that's sent by get-package-info? If so, perhaps it should be printed in a debug() message.

err.message = messages.join('\n') + '\n' + err.message
return cb(err)
} else {
// Missing props not required, can continue w/ partial result
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Assuming app-version is not a required option.

Copy link
Member

Choose a reason for hiding this comment

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

app-version isn't required. From docs/api.md:

If neither [app-version option nor version from package.json] are provided, the version of Electron will be used.

Copy link
Member

@malept malept left a comment

Choose a reason for hiding this comment

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

In general this looks good. However, there are some style nits (we use Node 4+ compatible ES6) and some new code needs to be covered by tests.

if (missingProps.filter(prop => requiredProps.find(reqProp => prop === reqProp)).length !== 0) {
var messages = missingProps.map(function (missingProp) {
missingProp = Array.isArray(missingProp) ? missingProp[0] : missingProp
if (missingProp === 'productName') {
Copy link
Member

Choose a reason for hiding this comment

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

Looks like you need to add a test for this conditional.

if (missingProp === 'dependencies.electron') {
return 'Unable to determine Electron version. Please specify an Electron version\n\n' +
'For more information, please see\n' +
'https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#version\n'
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if these error messages could be DRY'd up somehow.

err.message = messages.join('\n') + '\n' + err.message
return cb(err)
} else {
// Missing props not required, can continue w/ partial result
Copy link
Member

Choose a reason for hiding this comment

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

app-version isn't required. From docs/api.md:

If neither [app-version option nor version from package.json] are provided, the version of Electron will be used.

}
})

err.message = messages.join('\n') + '\n' + err.message
Copy link
Member

Choose a reason for hiding this comment

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

original error message

The one that's sent by get-package-info? If so, perhaps it should be printed in a debug() message.

basedir: path.dirname(result.source[`dependencies.electron`].src)
}, function (err, res, pkg) {
if (err) return cb(err)
debug(`Inferring target Electron version from ${packageName} dependency or devDependency in package.json`)
Copy link
Member

Choose a reason for hiding this comment

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

Can we make this debug statement more specific now with result.source?

opts.name = result.values.productName
}
if (result.values.productName) {
debug('Inferring application name from productName or name in package.json')
Copy link
Member

Choose a reason for hiding this comment

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

Can we make this debug statement more specific now with result.source?

getPackageInfo(props, dir, (err, result) => {
if (err && err.missingProps) {
var requiredProps = ['productName', 'dependencies.electron']
var missingProps = err.missingProps.map(prop => {
Copy link
Member

Choose a reason for hiding this comment

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

let


// Callback w/ error if there are required props that are missing
if (missingProps.filter(prop => requiredProps.find(reqProp => prop === reqProp)).length !== 0) {
var messages = missingProps.map(function (missingProp) {
Copy link
Member

Choose a reason for hiding this comment

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

Use let + fat arrow syntax here?

// Callback w/ error if there are required props that are missing
if (missingProps.filter(prop => requiredProps.find(reqProp => prop === reqProp)).length !== 0) {
var messages = missingProps.map(function (missingProp) {
missingProp = Array.isArray(missingProp) ? missingProp[0] : missingProp
Copy link
Member

Choose a reason for hiding this comment

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

Didn't you already do this earlier when declaring missingProps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

whoops. i really shouldn't make PRs at midnight on a work day

opts['app-version'] = result.values.version
}
if (result.values.version) {
debug('Inferring app-version from version in package.json')
Copy link
Member

Choose a reason for hiding this comment

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

Can we make this debug statement more specific now with result.source?

@rahatarmanahmed
Copy link
Contributor Author

rahatarmanahmed commented Oct 13, 2016

Made changes from code review input. Looked into the test coverage, and it looks like the issue is stemming from the reason why @zeke opened #461 in the first place: it's picking up values from electron-packager's own package.json.

@malept's suggestions:

  1. Copy a fixture to a temporary directory (so that the electron-packager package.json doesn't get detected by get-package-info) - and even then, there could be inconsistent failures on dev machines where someone puts a package.json in their root temporary directory
  2. Refactor that code to stop at the first package.json - I'm generally in favor of this method, but the concern was that it would break existing code (even though that wasn't an explicit feature)

The issue with option 2 is that the feature of looking at parent package.json's is a required feature for project structures where app code is in a subdirectory with its own package.json. The idea is that the app package.json would have the productName, and the parent package.json would have a devDependency on electron. Not sure what best practice is nowadays, but I imagine a good number of projects do it this way.

I'm not so sure the issue in option 1 of having a package.json above the temp directory is going to be a huge issue. That seems like it'd be an extremely rare occurrence. If we need to, we can explicitly check if there is a stray package.json and fail the test w/ an informative error msg.

@malept
Copy link
Member

malept commented Oct 13, 2016

I'm OK with you implementing option 1 in the way you describe.

@zeke
Copy link
Contributor

zeke commented Oct 14, 2016

looking at parent package.json's is a required feature for project structures where app code is in a subdirectory with its own package.json.

☝️ great point.

I'm OK with you implementing option 1 in the way you describe.

👍

let fixtureDir = path.join(__dirname, 'fixtures', fixtureSubdir)
waterfall([
cb => fs.emptyDir(tmpdir, cb),
(cb1, cb2) => fs.copy(fixtureDir, tmpdir, cb2 || cb1), // inconsistent cb arguments from fs.emptyDir
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 weird b/c fs.emptyDir doesn't return a consistent # of arguments, depending on whether the directory already exists or not. I've made an issue on the fs-extra repo but this is the workaround for now.

Copy link
Member

Choose a reason for hiding this comment

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

I've made an issue on the fs-extra repo

For reference: jprichardson/node-fs-extra#287

@rahatarmanahmed
Copy link
Contributor Author

love too have tests fail at random on CI

I've run the failing tests on loop on my laptop but I can't figure out what's causing those rare failures. Otherwise, the code is ready to be re-reviewed. Hopefully something pops out during review that tells us about the failures.

@eljefedelrodeodeljefe
Copy link

I believe landing this is necessary to fix #513

@eljefedelrodeodeljefe
Copy link

Also highly doubtful that this module should be used at all.

@malept
Copy link
Member

malept commented Oct 22, 2016

Also highly doubtful that this module should be used at all.

Why is that? Do you have an alternative proposal?

@eljefedelrodeodeljefe
Copy link

So using this is the higher-level of it's upstream dependencies which are already high level of try-catch-edJSON.parse(fs.readFile('package.json')) or require('package.json'). Either use its various upstream deps or those of the npm guys or just the above. This would decrease surface area of bugs.

@rahatarmanahmed
Copy link
Contributor Author

If you wanna move the logic from get-package-info into electron-packager, that's fine by me. I initially made it a separate package so it wouldn't muck up the code here, and could be unit tested as well as tested thru electron-packager's integration tests.

(Also I could have sworn the tests were failing before? I don't know how to avoid the coverage decrease, since it's the line that's triggered on some kind of I/O failure looking for package.jsons.

Copy link
Member

@malept malept left a comment

Choose a reason for hiding this comment

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

@rahatarmanahmed sorry about the delay, I've been busy.

If you wanna move the logic from get-package-info into electron-packager, that's fine by me.

I have no intention of moving the logic into Electron Packager. If it's not specific to how Electron Packager works, it shouldn't go in there.

Also I could have sworn the tests were failing before?

I reran the test. That's probably a bad sign about how inferring works that it times out intermittently.

getPackageInfo(props, dir, function (err, result) {
if (err) return cb(err)
return inferNameAndVersionFromInstalled('electron-prebuilt', opts, result, cb)
getPackageInfo(props, dir, (err, result) => {
Copy link
Member

Choose a reason for hiding this comment

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

This function has gotten rather large. Can it be split up?

result = err.result
}
} else if (err) {
return cb(err)
Copy link
Member

Choose a reason for hiding this comment

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

Would an IO error include an invalid package.json? Just trying to think of ways to test this line.

debug(`Inferring target Electron version from ${packageName} dependency or devDependency in package.json`)
opts.version = pkg.version
if (result.values[`dependencies.electron`]) {
var packageName = result.source[`dependencies.electron`].prop.split('.')[1]
Copy link
Member

Choose a reason for hiding this comment

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

let (I need to add ES6 rules to eslint...)

}

function createInferMissingVersionTest (opts) {
return function (t) {
Copy link
Member

Choose a reason for hiding this comment

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

Should use fat arrow function syntax when possible.

@rahatarmanahmed
Copy link
Contributor Author

rahatarmanahmed commented Oct 27, 2016

@malept moved the electron package.json reading into its own function, lmk if you have any better ideas on how to break it up even more.

Good call on the invalid package.json. Added a test for that. Should cover that line.

Tests seem to be failing due to some eslint peerdependency business, tho... 😞

@malept
Copy link
Member

malept commented Oct 27, 2016

Tests seem to be failing due to some eslint peerdependency business, tho... 😞

WTF. I wanted to add Node 7 to CI anyway, I guess I need to mess with other dependencies too.

@malept
Copy link
Member

malept commented Oct 27, 2016

Can't upgrade CI to use Node 7 until next week (blocked on AppVeyor). However, I upgraded eslint-plugin-promise on master and CI all passed the npm install portion.

Copy link
Member

@malept malept left a comment

Choose a reason for hiding this comment

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

Apart from the refactors, 👍 looks good.

return inferNameAndVersionFromInstalled('electron', opts, result, cb)

// Callback w/ error if there are required props that are missing
if (missingProps.filter(prop => requiredProps.find(reqProp => prop === reqProp)).length !== 0) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd extract the conditional into a method for readability (and also lets you get rid of the comment.


// Callback w/ error if there are required props that are missing
if (missingProps.filter(prop => requiredProps.find(reqProp => prop === reqProp)).length !== 0) {
let messages = missingProps.map(missingProp => {
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps extract the map function into a top-level function named errorMessageForProperty?

@rahatarmanahmed
Copy link
Contributor Author

greeeeeeeeen 🤑

@malept malept merged commit ce86dc4 into electron:master Oct 29, 2016
@zeke
Copy link
Contributor

zeke commented Oct 30, 2016

Thanks for persevering, @rahatarmanahmed 👊

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

4 participants