From 5852ebb1dbc8081ba88352eac9ec143f83bda135 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Sun, 25 Feb 2018 14:07:01 -0800 Subject: [PATCH 1/2] Add support for customizing the login helper on MAS See: https://github.com/electron/electron/pull/10856 --- mac.js | 72 +++++++++++++++++++++++++++++++++++++++++------------ test/mas.js | 30 ++++++++++++++++++++-- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/mac.js b/mac.js index 780bb9e6..16507592 100644 --- a/mac.js +++ b/mac.js @@ -84,6 +84,14 @@ class MacApp extends App { return path.join(this.contentsPath, 'Frameworks') } + get loginItemsPath () { + return path.join(this.contentsPath, 'Library', 'LoginItems') + } + + get loginHelperPath () { + return path.join(this.loginItemsPath, 'Electron Login Helper.app') + } + updatePlist (base, displayName, identifier, name) { return Object.assign(base, { CFBundleDisplayName: displayName, @@ -130,17 +138,19 @@ class MacApp extends App { } ehPlistFilename (helper) { - return path.join(this.frameworksPath, helper, 'Contents', 'Info.plist') + return this.helperPlistFilename(path.join(this.frameworksPath, helper)) } - updatePlistFiles () { + helperPlistFilename (helperApp) { + return path.join(helperApp, 'Contents', 'Info.plist') + } + + determinePlistFilesToUpdate () { const appPlistFilename = path.join(this.contentsPath, 'Info.plist') const helperPlistFilename = this.ehPlistFilename('Electron Helper.app') const helperEHPlistFilename = this.ehPlistFilename('Electron Helper EH.app') const helperNPPlistFilename = this.ehPlistFilename('Electron Helper NP.app') - - const appBundleIdentifier = filterCFBundleIdentifier(this.opts.appBundleId || this.defaultBundleName) - this.helperBundleIdentifier = filterCFBundleIdentifier(this.opts.helperBundleId || `${appBundleIdentifier}.helper`) + const loginHelperPlistFilename = this.helperPlistFilename(this.loginHelperPath) const plists = [ [appPlistFilename, 'appPlist'], @@ -149,14 +159,39 @@ class MacApp extends App { [helperNPPlistFilename, 'helperNPPlist'] ] - return Promise.all(plists.map(plistArgs => this.loadPlist.apply(this, plistArgs))) - .then(() => this.extendAppPlist(this.opts.extendInfo)) + return fs.pathExists(loginHelperPlistFilename) + .then(exists => { + if (exists) { + plists.push([loginHelperPlistFilename, 'loginHelperPlist']) + } + return plists + }) + } + + updatePlistFiles () { + let plists + + const appBundleIdentifier = filterCFBundleIdentifier(this.opts.appBundleId || this.defaultBundleName) + this.helperBundleIdentifier = filterCFBundleIdentifier(this.opts.helperBundleId || `${appBundleIdentifier}.helper`) + + return this.determinePlistFilesToUpdate() + .then(plistsToUpdate => { + plists = plistsToUpdate + return Promise.all(plists.map(plistArgs => this.loadPlist.apply(this, plistArgs))) + }).then(() => this.extendAppPlist(this.opts.extendInfo)) .then(() => { this.appPlist = this.updatePlist(this.appPlist, this.executableName, appBundleIdentifier, this.appName) this.helperPlist = this.updateHelperPlist(this.helperPlist) this.helperEHPlist = this.updateHelperPlist(this.helperEHPlist, 'EH') this.helperNPPlist = this.updateHelperPlist(this.helperNPPlist, 'NP') + if (this.loginHelperPlist) { + const loginHelperName = common.sanitizeAppName(`${this.appName} Login Helper`) + this.loginHelperPlist.CFBundleExecutable = loginHelperName + this.loginHelperPlist.CFBundleIdentifier = `${appBundleIdentifier}.loginhelper` + this.loginHelperPlist.CFBundleName = loginHelperName + } + if (this.appVersion) { this.appPlist.CFBundleShortVersionString = this.appPlist.CFBundleVersion = '' + this.appVersion } @@ -186,15 +221,20 @@ class MacApp extends App { } moveHelpers () { - return Promise.all([' Helper', ' Helper EH', ' Helper NP'].map((suffix) => { - const originalBasename = `Electron${suffix}` - const newBasename = `${common.sanitizeAppName(this.appName)}${suffix}` - const originalAppname = `${originalBasename}.app` - const executableBasePath = path.join(this.frameworksPath, originalAppname, 'Contents', 'MacOS') - - return this.relativeRename(executableBasePath, originalBasename, newBasename) - .then(() => this.relativeRename(this.frameworksPath, originalAppname, `${newBasename}.app`)) - })) + const helpers = [' Helper', ' Helper EH', ' Helper NP'] + return Promise.all(helpers.map(suffix => this.moveHelper(this.frameworksPath, suffix))) + .then(() => fs.pathExists(this.loginItemsPath)) + .then(exists => exists ? this.moveHelper(this.loginItemsPath, ' Login Helper') : null) + } + + moveHelper (helperDirectory, suffix) { + const originalBasename = `Electron${suffix}` + const newBasename = `${common.sanitizeAppName(this.appName)}${suffix}` + const originalAppname = `${originalBasename}.app` + const executableBasePath = path.join(helperDirectory, originalAppname, 'Contents', 'MacOS') + + return this.relativeRename(executableBasePath, originalBasename, newBasename) + .then(() => this.relativeRename(helperDirectory, originalAppname, `${newBasename}.app`)) } copyIcon () { diff --git a/test/mas.js b/test/mas.js index f6e25c78..1e1bebbc 100644 --- a/test/mas.js +++ b/test/mas.js @@ -1,14 +1,16 @@ 'use strict' -const config = require('./config.json') +const fs = require('fs-extra') const packager = require('..') +const path = require('path') +const plist = require('plist') const util = require('./_util') if (!(process.env.CI && process.platform === 'win32')) { const masOpts = { name: 'masTest', dir: util.fixtureSubdir('basic'), - electronVersion: config.version, + electronVersion: '2.0.0-beta.1', arch: 'x64', platform: 'mas' } @@ -29,4 +31,28 @@ if (!(process.env.CI && process.platform === 'win32')) { ).then(finalize) .catch(finalize) }) + + util.packagerTest('update Login Helper if it exists', (t, baseOpts) => { + let contentsPath + let plistPath + const helperName = `${masOpts.name} Login Helper` + return packager(Object.assign({}, baseOpts, masOpts)) + .then(paths => { + contentsPath = path.join(paths[0], `${masOpts.name}.app`, 'Contents', 'Library', 'LoginItems', `${helperName}.app`, 'Contents') + return fs.pathExists(contentsPath) + }).then(exists => { + t.true(exists, 'renamed Login Helper app exists') + plistPath = path.join(contentsPath, 'Info.plist') + return fs.pathExists(contentsPath) + }).then(exists => { + t.true(exists, 'Login Helper Info.plist exists') + return fs.readFile(plistPath, 'utf8') + }).then(plistXML => { + const plistData = plist.parse(plistXML) + t.is(plistData.CFBundleExecutable, helperName, 'CFBundleExecutable is renamed Login Helper') + t.is(plistData.CFBundleName, helperName, 'CFBundleName is renamed Login Helper') + t.is(plistData.CFBundleIdentifier, 'com.electron.mastest.loginhelper') + return fs.pathExists(path.join(contentsPath, 'MacOS', helperName)) + }).then(exists => t.true(exists, 'renamed Login Helper executable exists')) + }) } From c82cc36a49b094f195e3e9c8a3d5b1c29a68c4be Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Sun, 25 Feb 2018 14:24:03 -0800 Subject: [PATCH 2/2] Pre-download the MAS version being tested --- test/_setup.js | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/test/_setup.js b/test/_setup.js index 34235ef5..a9b2f6bf 100644 --- a/test/_setup.js +++ b/test/_setup.js @@ -13,26 +13,42 @@ function fixtureSubdir (subdir) { return path.join(__dirname, 'fixtures', subdir) } +/** + * Skip testing darwin/mas target on Windows since Electron Packager itself skips it + * (see https://github.com/electron-userland/electron-packager/issues/71) + */ +function skipDownloadingMacZips (platform, arch) { + return common.isPlatformMac(platform) && process.platform === 'win32' +} + function downloadAll (version) { console.log(`Calling electron-download for ${version} before running tests...`) - const combinations = download.createDownloadCombos({electronVersion: config.version, all: true}, targets.officialPlatforms, targets.officialArchs, (platform, arch) => { - // Skip testing darwin/mas target on Windows since electron-packager itself skips it - // (see https://github.com/electron-userland/electron-packager/issues/71) - return common.isPlatformMac(platform) && process.platform === 'win32' - }) + const combinations = download.createDownloadCombos({electronVersion: config.version, all: true}, targets.officialPlatforms, targets.officialArchs, skipDownloadingMacZips) + + return Promise.all(combinations.map(combination => downloadElectronZip(version, combination))) +} - return Promise.all(combinations.map(combination => { - return download.downloadElectronZip(Object.assign({}, combination, { - cache: path.join(os.homedir(), '.electron'), - quiet: !!process.env.CI, - version: version - })) +function downloadElectronZip (version, options) { + return download.downloadElectronZip(Object.assign({}, options, { + cache: path.join(os.homedir(), '.electron'), + quiet: !!process.env.CI, + version: version })) } -// Download all Electron distributions before running tests to avoid timing out due to network -// speed. Most tests run with the config.json version, but we have some tests using 0.37.4, and an -// electron module specific test using 1.3.1. +function downloadMASLoginHelperElectronZip () { + if (process.platform !== 'win32') { + const version = '2.0.0-beta.1' + console.log(`Calling electron-download for ${version} (MAS only) before running tests...`) + return downloadElectronZip(version, { platform: 'mas', arch: 'x64' }) + } +} + +/** + * Download all Electron distributions before running tests to avoid timing out due to network + * speed. Most tests run with the config.json version, but we have some tests using 0.37.4, an + * `electron` module specific test using 1.3.1., and an MAS-specific test using 2.0.0-beta.1. + */ function preDownloadElectron () { const versions = [ config.version, @@ -40,6 +56,7 @@ function preDownloadElectron () { '1.3.1' ] return Promise.all(versions.map(downloadAll)) + .then(downloadMASLoginHelperElectronZip) } function npmInstallForFixture (fixture) {