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 MAS Login Helper support #807

Merged
merged 2 commits into from Feb 27, 2018
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
72 changes: 56 additions & 16 deletions mac.js
Expand Up @@ -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,
Expand Down Expand Up @@ -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'],
Expand All @@ -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
}
Expand Down Expand Up @@ -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 () {
Expand Down
45 changes: 31 additions & 14 deletions test/_setup.js
Expand Up @@ -13,33 +13,50 @@ 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,
'0.37.4',
'1.3.1'
]
return Promise.all(versions.map(downloadAll))
.then(downloadMASLoginHelperElectronZip)
}

function npmInstallForFixture (fixture) {
Expand Down
30 changes: 28 additions & 2 deletions 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'
}
Expand All @@ -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'))
})
}