From b062ec0bee1330e7757b07c269abe35ed7407bcf Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Thu, 27 Dec 2018 01:56:47 -0800 Subject: [PATCH 1/4] Update Electron's dependencies for RPMs --- README.md | 5 ++- package.json | 2 +- src/dependencies.js | 44 ++++++++++++++++++++++++++ src/installer.js | 19 ++++++----- test/fixtures/app-without-asar/version | 2 +- 5 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 src/dependencies.js diff --git a/README.md b/README.md index 13a27e8..3afb6d5 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,17 @@ ## Requirements -This tool requires Node 6 or greater and `rpmbuild` to build the `.rpm` package. On Fedora you can do something like this: +This tool requires Node 6 or greater and `rpmbuild` 4.13 or greater to build the `.rpm` package. On Fedora you can do something like this: ``` $ sudo dnf install rpm-build ``` -While on Ubuntu you'll need to do this instead: +While on Debian/Ubuntu you'll need to do this instead: ``` $ sudo apt-get install rpm ``` -In order to use [boolean dependencies](http://rpm.org/user_doc/boolean_dependencies.html),`rpm >= 4.13` is required. ## Installation diff --git a/package.json b/package.json index 7199232..80e9942 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "debug": "^3.1.0", - "electron-installer-common": "^0.2.0", + "electron-installer-common": "^0.4.0", "fs-extra": "^5.0.0", "lodash": "^4.17.4", "nodeify": "^1.0.1", diff --git a/src/dependencies.js b/src/dependencies.js new file mode 100644 index 0000000..9ebaadd --- /dev/null +++ b/src/dependencies.js @@ -0,0 +1,44 @@ +'use strict' + +const dependencies = require('electron-installer-common/src/dependencies') + +const dependencyMap = { + gconf: 'GConf2', + glib2: 'glib2', + gtk2: 'gtk2', + gtk3: 'gtk3', + gvfs: 'gvfs-client', + kdeCliTools: 'kde-cli-tools', + kdeRuntime: 'kde-runtime', + notify: 'libnotify', + nss: 'nss', + trashCli: 'trash-cli', + uuid: 'libuuid', + xdgUtils: 'xdg-utils', + xss: 'libXScrnSaver', + xtst: 'libXtst' +} + +/** + * Transforms the list of trash requires into an RPM boolean dependency list. + */ +function trashRequiresAsBoolean (electronVersion, dependencyMap) { + const trashDepends = dependencies.getTrashDepends(electronVersion, dependencyMap) + if (trashDepends.length === 1) { + return [trashDepends[0]] + } else { + return [`(${trashDepends.join(' or ')})`] + } +} + +module.exports = { + /** + * The dependencies for Electron itself, given an Electron version. + */ + forElectron: function dependenciesForElectron (electronVersion) { + return { + requires: dependencies.getDepends(electronVersion, dependencyMap) + .concat(trashRequiresAsBoolean(electronVersion, dependencyMap)) + } + } +} diff --git a/src/installer.js b/src/installer.js index ff2dbaa..a461126 100644 --- a/src/installer.js +++ b/src/installer.js @@ -7,8 +7,10 @@ const dependencies = require('electron-installer-common/src/dependencies') const fs = require('fs-extra') const nodeify = require('nodeify') const path = require('path') +const readElectronVersion = require('electron-installer-common/src/readelectronversion') const wrap = require('word-wrap') +const redhatDependencies = require('./dependencies') const spawn = require('./spawn') const util = require('./util') @@ -23,26 +25,23 @@ const defaultRename = function (dest, src) { * read from `package.json`, and some are hardcoded. */ const getDefaults = function (data) { - return common.readMeta(data) - .then(pkg => { - pkg = pkg || {} + return Promise.all([common.readMeta(data), readElectronVersion(data.src)]) + .then(([pkg, electronVersion]) => { + if (!pkg) { + pkg = {} + } return Object.assign(common.getDefaultsFromPackageJSON(pkg), { version: pkg.version || '0.0.0', license: pkg.license, - requires: [ - 'lsb', - 'libXScrnSaver' - ], compressionLevel: 2, - execArguments: [], icon: path.resolve(__dirname, '../resources/icon.png'), pre: undefined, post: undefined, preun: undefined, postun: undefined - }) + }, redhatDependencies.forElectron(electronVersion)) }) } @@ -93,7 +92,7 @@ function getOptions (data, defaults) { * * See: https://fedoraproject.org/wiki/How_to_create_an_RPM_package */ -const createSpec = function (options, dir) { +function createSpec (options, dir) { const specSrc = path.resolve(__dirname, '../resources/spec.ejs') const specDest = path.join(dir, 'SPECS', options.name + '.spec') options.logger(`Creating spec file at ${specDest}`) diff --git a/test/fixtures/app-without-asar/version b/test/fixtures/app-without-asar/version index 9f27816..ef96e41 100644 --- a/test/fixtures/app-without-asar/version +++ b/test/fixtures/app-without-asar/version @@ -1 +1 @@ -v0.29.2 \ No newline at end of file +v1.8.5 \ No newline at end of file From dd224f6203617f5a11237786dae3f4b006d42215 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Thu, 27 Dec 2018 02:06:15 -0800 Subject: [PATCH 2/4] Update requires docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3afb6d5..9a18d7f 100644 --- a/README.md +++ b/README.md @@ -271,7 +271,7 @@ Machine architecture the package is targeted to, used to set the `--target` opti #### options.requires Type: `Array[String]` -Default: `["lsb", "libXScrnSaver"]` +Default: The minimum list of packages needed for Electron to run Packages that are required when the program starts, used in the [`Requires` field of the `spec` file](https://fedoraproject.org/wiki/How_to_create_an_RPM_package#Creating_a_SPEC_file). From ce28e6dd8aec19b6948c2a37ba0e7bc52c3cf417 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Fri, 28 Dec 2018 13:08:13 -0800 Subject: [PATCH 3/4] Emit a warning when RPM does not support boolean dependencies --- README.md | 6 ++++- package.json | 3 ++- src/dependencies.js | 43 ++++++++++++++++++++++++++++----- src/installer.js | 7 +++--- test/dependencies.js | 57 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 test/dependencies.js diff --git a/README.md b/README.md index 9a18d7f..ad0f61e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,11 @@ ## Requirements -This tool requires Node 6 or greater and `rpmbuild` 4.13 or greater to build the `.rpm` package. On Fedora you can do something like this: +This tool requires Node 6 or greater and `rpmbuild` to build the `.rpm` package. + +**Note**: If your application uses the [Electron API's `shell.moveItemToTrash` method](https://electronjs.org/docs/api/shell#shellmoveitemtotrashfullpath), RPM 4.13.0 or greater is required, due to the [boolean dependency feature](http://rpm.org/user_doc/boolean_dependencies.html). + +On Fedora you can do something like this: ``` $ sudo dnf install rpm-build diff --git a/package.json b/package.json index 80e9942..1e5766c 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "eslint-plugin-standard": "^3.0.1", "mocha": "^5.0.4", "mz": "^2.7.0", - "promise-retry": "^1.1.1" + "promise-retry": "^1.1.1", + "sinon": "^7.2.2" } } diff --git a/src/dependencies.js b/src/dependencies.js index 9ebaadd..e315bcf 100644 --- a/src/dependencies.js +++ b/src/dependencies.js @@ -1,6 +1,7 @@ 'use strict' const dependencies = require('electron-installer-common/src/dependencies') +const spawn = require('./spawn') const dependencyMap = { gconf: 'GConf2', @@ -19,6 +20,25 @@ const dependencyMap = { xtst: 'libXtst' } +/** + * Retrieves the RPM version number and determines whether it has support for boolean + * dependencies (>= 4.13.0). + */ +function rpmSupportsBooleanDependencies (logger) { + return spawn('rpmbuild', ['--version'], logger) + .then(output => rpmVersionSupportsBooleanDependencies(output.trim().split(' ')[2])) +} + +/** + * Determine whether the RPM version string has support for boolean dependencies (>= 4.13.0). + * + * RPM does not follow semantic versioning, so `semver` cannot be used. + */ +function rpmVersionSupportsBooleanDependencies (rpmVersionString) { + const rpmVersion = rpmVersionString.split('.').slice(0, 3).map(n => parseInt(n)) + return rpmVersion >= [4, 13, 0] +} + /** * Transforms the list of trash requires into an RPM boolean dependency list. */ @@ -32,13 +52,24 @@ function trashRequiresAsBoolean (electronVersion, dependencyMap) { } module.exports = { + dependencyMap, /** * The dependencies for Electron itself, given an Electron version. */ - forElectron: function dependenciesForElectron (electronVersion) { - return { - requires: dependencies.getDepends(electronVersion, dependencyMap) - .concat(trashRequiresAsBoolean(electronVersion, dependencyMap)) - } - } + forElectron: function dependenciesForElectron (electronVersion, logger) { + const requires = dependencies.getDepends(electronVersion, dependencyMap) + return module.exports.rpmSupportsBooleanDependencies(logger) + .then(supportsBooleanDependencies => { + if (supportsBooleanDependencies) { + const trashRequires = trashRequiresAsBoolean(electronVersion, dependencyMap) + return { requires: requires.concat(trashRequires) } + } else { + console.warn("You are using RPM < 4.13, which does not support boolean dependencies. This is required to express the dependencies needed for the 'shell.moveItemToTrash' API.\nIf you do not use this API, you can safely ignore this warning.\nIf you do use this API, please upgrade to RPM 4.13 or above to have the trash dependencies added to your RPM's requires section.") + return { requires } + } + }) + }, + rpmSupportsBooleanDependencies, + rpmVersionSupportsBooleanDependencies, + trashRequiresAsBoolean } diff --git a/src/installer.js b/src/installer.js index a461126..22640eb 100644 --- a/src/installer.js +++ b/src/installer.js @@ -25,8 +25,9 @@ const defaultRename = function (dest, src) { * read from `package.json`, and some are hardcoded. */ const getDefaults = function (data) { - return Promise.all([common.readMeta(data), readElectronVersion(data.src)]) - .then(([pkg, electronVersion]) => { + return readElectronVersion(data.src) + .then(electronVersion => Promise.all([common.readMeta(data), redhatDependencies.forElectron(electronVersion, data.logger)])) + .then(([pkg, requires]) => { if (!pkg) { pkg = {} } @@ -41,7 +42,7 @@ const getDefaults = function (data) { post: undefined, preun: undefined, postun: undefined - }, redhatDependencies.forElectron(electronVersion)) + }, requires) }) } diff --git a/test/dependencies.js b/test/dependencies.js new file mode 100644 index 0000000..e76f4a5 --- /dev/null +++ b/test/dependencies.js @@ -0,0 +1,57 @@ +const dependencies = require('../src/dependencies') +const { expect } = require('chai') +const sinon = require('sinon') + +describe('dependencies', () => { + describe('forElectron', () => { + beforeEach(() => { + sinon.spy(console, 'warn') + }) + + afterEach(() => { + sinon.restore() + }) + + it('uses an RPM that does not support boolean dependencies', () => { + sinon.stub(dependencies, 'rpmSupportsBooleanDependencies').resolves(false) + return dependencies.forElectron('v1.0.0') + .then(result => { + expect(console.warn.calledWithMatch(/^You are using RPM < 4.13/)).to.equal(true) + }) + }) + + it('uses an RPM that supports boolean dependencies', () => { + sinon.stub(dependencies, 'rpmSupportsBooleanDependencies').resolves(true) + return dependencies.forElectron('v1.0.0') + .then(result => { + expect(console.warn.calledWithMatch(/^You are using RPM < 4.13/)).to.equal(false) + }) + }) + }) + + describe('rpmVersionSupportsBooleanDependencies', () => { + it('works with release candidates', () => { + expect(dependencies.rpmVersionSupportsBooleanDependencies('4.13.0-rc1')).to.equal(true) + }) + + it('works with git snapshots', () => { + expect(dependencies.rpmVersionSupportsBooleanDependencies('4.11.90-git12844')).to.equal(false) + }) + + it('works with 4 part versions (1.2.3.4)', () => { + expect(dependencies.rpmVersionSupportsBooleanDependencies('4.1.12.2')).to.equal(false) + }) + }) + + describe('trashRequiresAsBoolean', () => { + it('does not use parentheses for one item', () => { + const trashDeps = dependencies.trashRequiresAsBoolean('1.0.0', dependencies.dependencyMap)[0] + expect(trashDeps[0]).to.not.match(/^\(/) + }) + + it('ORs more than one item', () => { + const trashDeps = dependencies.trashRequiresAsBoolean('1.5.0', dependencies.dependencyMap)[0] + expect(trashDeps).to.match(/^\(.* or .*\)$/) + }) + }) +}) From 34bfdad164b79c3521e1f2180b90bd002acdeb70 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Fri, 28 Dec 2018 16:26:53 -0800 Subject: [PATCH 4/4] Adjust requires description per code review --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ad0f61e..2171f9a 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,8 @@ Default: The minimum list of packages needed for Electron to run Packages that are required when the program starts, used in the [`Requires` field of the `spec` file](https://fedoraproject.org/wiki/How_to_create_an_RPM_package#Creating_a_SPEC_file). +All user requirements will be appended to the default array of requirements, and any duplicates will be removed. + #### options.homepage Type: `String` Default: `package.homepage || package.author.url`