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 package-manager option #618

Merged
merged 2 commits into from Apr 30, 2017
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
2 changes: 0 additions & 2 deletions .travis.yml
Expand Up @@ -32,5 +32,3 @@ env:
# prevent wine popup dialogs about installing additional packages
- WINEDLLOVERRIDES="mscoree,mshtml="
- WINEDEBUG="-all"
# temporarily enable debug logging to diagnose flaky spec failures
- DEBUG=electron-packager
4 changes: 4 additions & 0 deletions NEWS.md
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Added

* `packageManager` (`--package-manager` via CLI) option (#618)

## [8.6.0] - 2017-03-14

### Added
Expand Down
6 changes: 3 additions & 3 deletions common.js
@@ -1,14 +1,14 @@
'use strict'

const asar = require('asar')
const child = require('child_process')
const debug = require('debug')('electron-packager')
const download = require('electron-download')
const fs = require('fs-extra')
const ignore = require('./ignore')
const minimist = require('minimist')
const os = require('os')
const path = require('path')
const pruneModules = require('./prune').pruneModules
const sanitize = require('sanitize-filename')
const semver = require('semver')
const series = require('run-series')
Expand Down Expand Up @@ -198,6 +198,7 @@ module.exports = {
'app-copyright': 'appCopyright',
'app-version': 'appVersion',
'build-version': 'buildVersion',
'package-manager': 'packageManager',
'app-bundle-id': 'appBundleId',
'app-category-type': 'appCategoryType',
'extend-info': 'extendInfo',
Expand Down Expand Up @@ -293,8 +294,7 @@ module.exports = {
// appPath is predictable (e.g. before .app is renamed for mac)
if (opts.prune || opts.prune === undefined) {
operations.push(function (cb) {
Copy link
Member

Choose a reason for hiding this comment

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

This should come back in some form or another. We need to be able to see when the prune operation occurs and with what package manager. It's pretty helpful for debugging user problems.

debug('Running npm prune --production')
child.exec('npm prune --production', {cwd: appPath}, cb)
pruneModules(opts, appPath, cb)
})
}

Expand Down
17 changes: 15 additions & 2 deletions docs/api.md
Expand Up @@ -211,7 +211,19 @@ The base directory where the finished package(s) are created.

Whether to replace an already existing output directory for a given platform (`true`) or skip recreating it (`false`).

#### `platform`
##### `packageManager`

*String* (default: `npm`)

The package manager used to [prune](#prune) `devDependencies` modules from the outputted Electron
app. Supported package managers:

* [`npm`](https://npmjs.com/)
* [`cnpm`](https://github.com/cnpm/cnpm) (Does not currently work with Windows, see
[GitHub issue](https://github.com/electron-userland/electron-packager/issues/515#issuecomment-297604044))
* [`yarn`](https://yarnpkg.com/)

##### `platform`

*String* (default: the arch of the host computer running Node)

Expand All @@ -227,7 +239,8 @@ The non-`all` values correspond to the platform names used by [Electron releases

*Boolean* (default: `true`)

Runs [`npm prune --production`](https://docs.npmjs.com/cli/prune) before starting to package the app.
Runs the [package manager](#packagemanager) command to remove all of the packages specified in the
`devDependencies` section of `package.json` from the outputted Electron app.

##### `quiet`

Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -44,7 +44,8 @@
"pkg-up": "^1.0.0",
"rimraf": "^2.3.2",
"run-waterfall": "^1.1.1",
"tape": "^4.0.0"
"tape": "^4.0.0",
"which": "^1.2.14"
},
"engines": {
"node": ">= 4.0"
Expand Down
38 changes: 38 additions & 0 deletions prune.js
@@ -0,0 +1,38 @@
'use strict'

const child = require('child_process')
const debug = require('debug')('electron-packager')

const knownPackageManagers = ['npm', 'cnpm', 'yarn']

function pruneCommand (packageManager) {
switch (packageManager) {
case 'npm':
case 'cnpm':
return `${packageManager} prune --production`
case 'yarn':
return `${packageManager} install --production`
}
}

function pruneModules (opts, appPath, cb) {
const packageManager = opts.packageManager || 'npm'

if (packageManager === 'cnpm' && process.platform === 'win32') {
return cb(new Error('cnpm support does not currently work with Windows, see: https://github.com/electron-userland/electron-packager/issues/515#issuecomment-297604044'))
}

const command = pruneCommand(packageManager)

if (command) {
debug(`Pruning modules via: ${command}`)
child.exec(command, { cwd: appPath }, cb)
} else {
cb(new Error(`Unknown package manager "${packageManager}". Known package managers: ${knownPackageManagers.join(', ')}`))
}
}

module.exports = {
pruneCommand: pruneCommand,
pruneModules: pruneModules
}
44 changes: 0 additions & 44 deletions test/basic.js
Expand Up @@ -122,44 +122,6 @@ function createOutTest (opts) {
}
}

function createPruneOptionTest (baseOpts, prune, testMessage) {
return (t) => {
t.timeoutAfter(config.timeout)

let opts = Object.create(baseOpts)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.prune = prune

let finalPath
let resourcesPath

waterfall([
(cb) => {
packager(opts, cb)
}, (paths, cb) => {
finalPath = paths[0]
fs.stat(finalPath, cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'The expected output directory should exist')
resourcesPath = path.join(finalPath, util.generateResourcesPath(opts))
fs.stat(resourcesPath, cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-series'), cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'npm dependency should exist under app/node_modules')
fs.exists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), (exists) => {
t.equal(!prune, exists, testMessage)
cb()
})
}
], (err) => {
t.end(err)
})
}
}

function createOverwriteTest (opts) {
return function (t) {
t.timeoutAfter(config.timeout * 2) // Multiplied since this test packages the application twice
Expand Down Expand Up @@ -370,12 +332,6 @@ test('cannot build apps where the name ends in " Helper"', (t) => {

util.testSinglePlatform('defaults test', createDefaultsTest)
util.testSinglePlatform('out test', createOutTest)
util.testSinglePlatform('prune test', (baseOpts) => {
return createPruneOptionTest(baseOpts, true, 'npm devDependency should NOT exist under app/node_modules')
})
util.testSinglePlatform('prune=false test', (baseOpts) => {
return createPruneOptionTest(baseOpts, false, 'npm devDependency should exist under app/node_modules')
})
util.testSinglePlatform('overwrite test', createOverwriteTest)
util.testSinglePlatform('tmpdir test', createTmpdirTest)
util.testSinglePlatform('tmpdir test', createDisableTmpdirUsingTest)
Expand Down
8 changes: 7 additions & 1 deletion test/ci/before_install.sh
Expand Up @@ -6,9 +6,11 @@ case "$TRAVIS_OS_NAME" in
# Not using Trusty containers because it can't install wine1.6(-i386),
# see: https://github.com/travis-ci/travis-ci/issues/6460
sudo rm /etc/apt/sources.list.d/google-chrome.list
sudo apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg
echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y wine1.6
sudo apt-get install -y wine1.6 yarn
;;
"osx")
# Create CA
Expand All @@ -29,5 +31,9 @@ case "$TRAVIS_OS_NAME" in
npm install wine-darwin@1.9.17-1
# Setup ~/.wine by running a command
./node_modules/.bin/wine hostname
# Install yarn
npm install -g yarn
;;
esac

npm install -g cnpm
1 change: 1 addition & 0 deletions test/index.js
Expand Up @@ -53,6 +53,7 @@ series(setupFuncs, (error) => {
require('./infer')
require('./hooks')
require('./multitarget')
require('./prune')
require('./win32')

if (process.platform !== 'win32') {
Expand Down
97 changes: 97 additions & 0 deletions test/prune.js
@@ -0,0 +1,97 @@
'use strict'

const config = require('./config.json')
const fs = require('fs-extra')
const packager = require('..')
const path = require('path')
const prune = require('../prune')
const test = require('tape')
const util = require('./util')
const waterfall = require('run-waterfall')
const which = require('which')

function createPruneOptionTest (baseOpts, prune, testMessage) {
return (t) => {
t.timeoutAfter(config.timeout)

let opts = Object.create(baseOpts)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.prune = prune

let finalPath
let resourcesPath

waterfall([
(cb) => {
packager(opts, cb)
}, (paths, cb) => {
finalPath = paths[0]
fs.stat(finalPath, cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'The expected output directory should exist')
resourcesPath = path.join(finalPath, util.generateResourcesPath(opts))
fs.stat(resourcesPath, cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-series'), cb)
}, (stats, cb) => {
t.true(stats.isDirectory(), 'package.json dependency should exist under app/node_modules')
fs.exists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), (exists) => {
t.equal(!prune, exists, testMessage)
cb()
})
}
], (err) => {
t.end(err)
})
}
}

test('pruneCommand returns the correct command when passing a known package manager', (t) => {
t.equal(prune.pruneCommand('npm'), 'npm prune --production', 'passing npm gives the npm prune command')
t.equal(prune.pruneCommand('cnpm'), 'cnpm prune --production', 'passing cnpm gives the cnpm prune command')
t.equal(prune.pruneCommand('yarn'), 'yarn install --production', 'passing yarn gives the yarn "prune command"')
t.end()
})

test('pruneCommand returns null when the package manager is unknown', (t) => {
t.notOk(prune.pruneCommand('unknown-package-manager'))
t.end()
})

test('pruneModules returns an error when the package manager is unknown', (t) => {
prune.pruneModules({packageManager: 'unknown-package-manager'}, '/tmp/app-path', (err) => {
t.ok(err, 'error returned')
t.end()
})
})

if (process.platform === 'win32') {
test('pruneModules returns an error when trying to use cnpm on Windows', (t) => {
prune.pruneModules({packageManager: 'cnpm'}, '/tmp/app-path', (err) => {
t.ok(err, 'error returned')
t.end()
})
})
}

// This is not in the below loop so that it tests the default packageManager option.
util.testSinglePlatform('prune test with npm', (baseOpts) => {
return createPruneOptionTest(baseOpts, true, 'package.json devDependency should NOT exist under app/node_modules')
})

for (const packageManager of ['cnpm', 'yarn']) {
which(packageManager, (err, resolvedPath) => {
if (err) return

util.testSinglePlatform(`prune test with ${packageManager}`, (baseOpts) => {
const opts = Object.assign({packageManager: packageManager}, baseOpts)
return createPruneOptionTest(opts, true, 'package.json devDependency should NOT exist under app/node_modules')
})
})
}

util.testSinglePlatform('prune=false test', (baseOpts) => {
return createPruneOptionTest(baseOpts, false, 'npm devDependency should exist under app/node_modules')
})
4 changes: 3 additions & 1 deletion usage.txt
Expand Up @@ -46,10 +46,12 @@ icon the local path to an icon file to use as the icon for the app
ignore do not copy files into app whose filenames regex .match this string. See also:
https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#ignore
and --no-prune.
no-prune do not run `npm prune --production` on the app
no-prune do not prune devDependencies from the packaged app
out the dir to put the app into at the end. defaults to current working dir
overwrite if output directory for a platform already exists, replaces it rather than
skipping it
package-manager the package manager to use when pruning devDependencies. Supported package
managers: npm (default), cnpm, yarn
platform all, or one or more of: darwin, linux, mas, win32 (comma-delimited if multiple).
Defaults to the host platform
quiet Do not print informational or warning messages
Expand Down