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 support for prebuilt asars #823
Conversation
Thanks for opening a pull request! Here are some highlighted action items that will help get it across the finish line, from the
Development and triage is community-driven, so please be patient and we will get back to you as soon as we can. |
Codecov Report
@@ Coverage Diff @@
## master #823 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 12 12
Lines 622 643 +21
=====================================
+ Hits 622 643 +21
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the pull request! This is an excellent starting point. I've made several comments that should hopefully move this PR in the right direction. Let me know if there's anything I need to clarify.
.editorconfig
Outdated
|
||
[package.json] | ||
indent_style = space | ||
indent_size = 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to add this, it should be in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry about that, I didn't mean to add that. I was trying to follow the current style and that file made my editor honor it.
NEWS.md
Outdated
@@ -4,6 +4,8 @@ | |||
|
|||
[Unreleased]: https://github.com/electron-userland/electron-packager/compare/v12.0.0...master | |||
|
|||
* Added `asar.filename` to support pre-built asar files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please adjust this to conform to the Keep a Changelog format?
docs/api.md
Outdated
- `ordering` (*String*): A path to an ordering file for packing files. An explanation can be found on the [Atom issue tracker](https://github.com/atom/atom/issues/10163). | ||
- `unpack` (*String*): A [glob expression](https://github.com/isaacs/minimatch#features), when specified, unpacks the file with matching names to the `app.asar.unpacked` directory. | ||
- `unpackDir` (*String*): Unpacks the dir to the `app.asar.unpacked` directory whose names exactly or pattern match this string. The `asar.unpackDir` is relative to `dir`. | ||
|
||
Some examples: | ||
|
||
- `asar.filename = './dist/app.asar'` will short-circuit creating the asar, and just copy that one into the electron bundle. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there needs to be an example for this, it should be self-explanatory.
docs/api.md
Outdated
@@ -183,12 +183,14 @@ is not restricted to the official set if [`download.mirror`](#download) is set. | |||
*Boolean* or *Object* (default: `false`) | |||
|
|||
Whether to package the application's source code into an archive, using [Electron's archive format](https://github.com/electron/asar). Reasons why you may want to enable this feature are described in [an application packaging tutorial in Electron's documentation](http://electron.atom.io/docs/v0.36.0/tutorial/application-packaging/). When the value is `true`, pass default configuration to the `asar` module. The configuration values listed below can be customized when the value is an `Object`. Supported parameters include, but are not limited to: | |||
- `filename` (*String*): circumvents the asar generation and lets you specify your own pre-built asar file/directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description should start with "A path to", like ordering
below it.
test/_setup.js
Outdated
@@ -67,13 +67,14 @@ function npmInstallForFixture (fixture) { | |||
return true | |||
} else { | |||
console.log(`Running npm install in fixtures/${fixture}...`) | |||
return exec('npm install --no-bin-links', {cwd: fixtureDir}) | |||
return exec('npm install --no-bin-links --no-package-lock', {cwd: fixtureDir}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in a different PR.
{ | ||
"name": "PrebuiltAsar", | ||
"version": "0.0.1", | ||
"description": "A test of the renamed from `electron-prebuilt` to `electron`", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description needs to be updated.
platform.js
Outdated
@@ -154,6 +154,19 @@ class App { | |||
|
|||
const dest = path.join(this.originalResourcesDir, 'app.asar') | |||
debug(`Running asar with the options ${JSON.stringify(asarOptions)}`) | |||
|
|||
if (asarOptions.filename) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that if there are other asarOptions
other than filename
and filename
is set, it should warn that the other options won't be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should that happen here? or in the argument parsing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should happen here. If it happens in argument parsing, people who use the JS API (such as users of Electron Forge or ember-electron) will not get the warning.
platform.js
Outdated
debug(`Copying asar: ${src} to ${target}`) | ||
return fs.copy(src, target, {overwrite: false, errorOnExist: true}) | ||
}) | ||
.then(() => fs.remove(this.originalResourcesAppDir)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for efficiency purposes, if asar.filename
is set, we shouldn't even copy the app directory in the first place. Then we can get rid of this fs.remove
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started to go down that route, but tried to keep the change minimal. I'll short circuit that.
platform.js
Outdated
|
||
return fs.stat(src) | ||
.then(stat => { | ||
const target = stat.isFile() ? dest : this.originalResourcesDir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain how target
is determined? Shouldn't target always be dest
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the user specified a directory (containing their app.asar and additional resources) fs-extra's copy
requires the dest to be a directory... if the src is a file, it requires dest to be a file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear from the docs that you can specify a directory for asar.filename
. Although even if it did, I would disagree with it because the name of the option makes that choice not obvious. I think the value of asar.filename
should only be an ASAR file.
"main": "main.js", | ||
"devDependencies": { | ||
"electron": "^1.8.0", | ||
"electron-prebuilt": "^1.4.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should only be one item in devDependencies
, and it should be "electron": "1.3.1"
. Every time we add a new Electron version to the tests, it takes longer to run the tests due to downloading prebuilt binaries.
NEWS.md
Outdated
@@ -4,6 +4,10 @@ | |||
|
|||
[Unreleased]: https://github.com/electron-userland/electron-packager/compare/v12.0.0...master | |||
|
|||
### Added | |||
|
|||
* `asar.filename` to support pre-built asar files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit, remove the period and add (#823)
at the end.
docs/api.md
Outdated
@@ -183,6 +183,7 @@ is not restricted to the official set if [`download.mirror`](#download) is set. | |||
*Boolean* or *Object* (default: `false`) | |||
|
|||
Whether to package the application's source code into an archive, using [Electron's archive format](https://github.com/electron/asar). Reasons why you may want to enable this feature are described in [an application packaging tutorial in Electron's documentation](http://electron.atom.io/docs/v0.36.0/tutorial/application-packaging/). When the value is `true`, pass default configuration to the `asar` module. The configuration values listed below can be customized when the value is an `Object`. Supported parameters include, but are not limited to: | |||
- `filename` (*String*): A path to a pre-built asar file. This will circumvent the asar generation and lets you specify your own pre-built asar file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of redundant. Let me think about a better way to word this.
platform.js
Outdated
return true | ||
} | ||
}) | ||
const asarOptions = common.createAsarOpts(this.opts) || {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point asarOptions
should be an instance variable, no need to calculate it twice.
: fs.copy(this.opts.dir, this.originalResourcesAppDir, { | ||
filter: ignore.userIgnoreFilter(this.opts), | ||
dereference: this.opts.derefSymlinks | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather have a real if
statement than a multi-line ternary statement.
} else { | ||
return true | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know whether afterPrune and afterCopy hooks should be run, since neither of those operations actually took place, and this.originalResourcesAppDir
doesn't actually exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't know what those hooks were for... I've changed it to just return early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think returning early is the right solution... we may have to adjust the hook signature so that it's either the path to the resources app directory, or the path to the app.asar
file (which would be a breaking change).
@MarshallOfSound since you have dealt with a lot of code relating to the hooks, you might have some insight here.
test/asar.js
Outdated
return fs.pathExists(path.join(resourcesPath, 'app')) | ||
}).then(exists => { | ||
return t.false(exists, 'app subdirectory should NOT exist when app.asar is built') | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be:
}).then(exists => t.false(exists, 'app subdirectory should NOT exist when app.asar is built'))
const src = path.resolve(this.asarOptions.filename) | ||
if (Object.keys(this.asarOptions).length > 1) { | ||
common.warning('asar.filename has been specified, all other asar options will be ignored') | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean? Test that it warns? or test that the other options are ignored? If the later, how should that be done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test that it warns. This is an example of doing that (although it should probably be genericized into a function):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll need to take another pass at the diff later tonight, but one thing I noticed is that it should also be documented in usage.txt
(which I should replace with yargs
declarations...).
I'm also a little worried about the drop in code coverage, but let's see what it looks like after the latest changes go through CI. |
I updated the usage.txt. |
platform.js
Outdated
return fs.stat(src) | ||
.then(stat => { | ||
if (!stat.isFile()) { | ||
throw new Error(`${src} must be an asar file.`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from the suggestions below, the one thing holding back this PR is how to deal with the hooks. I'm pretty sure that omitting the hooks when asar.filename
is set will break Electron Forge, but I don't know exactly what the correct way to resolve that is.
docs/api.md
Outdated
@@ -183,6 +183,7 @@ is not restricted to the official set if [`download.mirror`](#download) is set. | |||
*Boolean* or *Object* (default: `false`) | |||
|
|||
Whether to package the application's source code into an archive, using [Electron's archive format](https://github.com/electron/asar). Reasons why you may want to enable this feature are described in [an application packaging tutorial in Electron's documentation](http://electron.atom.io/docs/v0.36.0/tutorial/application-packaging/). When the value is `true`, pass default configuration to the `asar` module. The configuration values listed below can be customized when the value is an `Object`. Supported parameters include, but are not limited to: | |||
- `filename` (*String*): A path to a pre-built asar file. (This will circumvent the asar generation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A path to a pre-built asar file (this will circumvent asar generation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
test/asar.js
Outdated
sinon.spy(console, 'warn') | ||
return util.packageAndEnsureResourcesPath(t, opts) | ||
.then(generatedResourcesPath => { | ||
console.warn.calledWithExactly('WARNING: asar.filename has been specified, all other asar options will be ignored') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be wrapped in t.true()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, i’ve been in jest land for a while, i assumed sinon made the assertion. i’ll update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
usage.txt
Outdated
@@ -25,6 +25,7 @@ asar whether to package the source code within your app into an ar | |||
--asar and its sub-properties simultaneously. | |||
|
|||
Properties supported: | |||
- filename: path to a pre-built asar file. (This will circumvent the asar generation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
path to a pre-built asar file (this will circumvent asar generation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
None of this is breaking existing configurations. Without adding the The whole point of this option is to take control of the app bundle generation... the hooks can be external to this once the filename is used... if something is needed to be done to the app package, a more general hook can be added? |
Never mind, I think I understand those hooks now. With a pre-built asar, you don't need the afterCopy/Prune hooks... all processing has already happened when the asar was generated outside of this tool. I can't think of anything you would need to hook for when specifying your own asar... so I'm sticking with my last comment. Just add a new pre-finalize hook called near the end before signing to allow last minute changes... |
RE the hooks issue I believe the operation should throw an error if Also I don't think this option should go into the Also @malept we should just make forge error out instantly if this option is used 😄 |
The docs should be updated to note the |
I think I got all the suggestions implemented. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it would be cleaner to deal with prebuiltAsar
this way:
initialize () {
debug(`Initializing app in ${this.stagingPath} from ${this.templatePath} template`)
return fs.move(this.templatePath, this.stagingPath, { clobber: true })
.then(() => {
if (this.opts.prebuiltAsar) {
return this.copyPrebuiltAsar()
.then(() => this.removeDefaultApp())
} else {
return this.copyTemplate()
.then(() => this.removeDefaultApp())
.then(() => this.asarApp())
}
})
}
common.js
Outdated
warning('--prebuiltAsar and --asar options are incompatible. Ignoring --asar') | ||
delete args.asar | ||
} | ||
console.log('\n\n%s\n\n', JSON.stringify(args, null, 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✂️ debug line
common.js
Outdated
warning('--prebuiltAsar and --ignore are incompatible. Ignoring --ignore') | ||
} | ||
|
||
if (!args['deref-symlinks']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be !args.derefSymlinks
common.js
Outdated
warning('--prebuiltAsar and --no-deref-symlinks are incompatible. Ignoring --no-deref-symlinks') | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be outside of the CLI argument parsing, as I mentioned yesterday, because it will not give warnings to consumers of the JavaScript API.
docs/api.md
Outdated
@@ -67,6 +67,10 @@ packager({ | |||
}) | |||
``` | |||
|
|||
**NOTE:** `afterCopy` will not be called if `prebuiltAsar` is set. | |||
|
|||
--- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lines are unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it was a nice visual grouping. I'll drop them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we're going to do that, it should be in a separate PR.
docs/api.md
Outdated
|
||
A path to a pre-built asar file. | ||
|
||
**NOTE:** This option circumvents many options such as `asar`, `afterCopy`, `afterPrune`, `ignore`, `prune`, or any option related to the copy processes. These options are for generating an app directory/asar with this tool. However, if a prebuilt asar file is provided, these options are not needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd get rid of , or any option related to the copy processes
. Also, it's a little too verbose in general, but I need some time to think of a better way to word it.
platform.js
Outdated
if ((this.asarOptions || {}).filename) { | ||
if (this.opts.prebuiltAsar) { | ||
if (this.opts.afterCopy) { | ||
return Promise.reject(new Error('afterCopy is incompatible with prebuiltAsar')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should just be able to throw new Error
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't an async function, I was trying to make as little of a splash as possible. I hadn't looked to see if the callers were setup to handle thrown exceptions and not just promise rejections. I'll make it throw instead.
I've updated to use your suggested initialize() |
looks like a transient error caused the signing test to fail for one of the jobs... but the rest seem to be passing. I haven't looked further into it, since its outside the scope of my changes and it has been passing. |
Anything I should change? |
@malept Would you like anything else? or do you think this is good? |
I rebased from your current master |
NEWS.md
Outdated
@@ -24,6 +24,10 @@ | |||
|
|||
* Upgraded `galactus` to `^0.2.1` to fix a bug with relative paths | |||
|
|||
### Added |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now in the wrong place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, should be fixed now
I hope all is well? Is this still wanted? |
I rebased again, let me know if any docs need touch ups. |
@malpet is there reservations for merging this? |
Your cleanup looks way better than my changes. :) I didn't want to change too much without fully comprehending the codebase. Sorry if I misunderstood your requests. Hope this gets merged 👍 |
I've been using this branch to bundle my apps, the flag works well. Hope we can merge soon. |
Is this blocked by something? Anything I could help with? |
Thanks for your contribution! 🎉 |
🎉 |
Summarize your changes:
I implemented the --asar.filename option referenced in #161