electron-osx-sign
Advanced tools
Comparing version 0.1.5 to 0.1.6
221
index.js
@@ -7,2 +7,31 @@ var fs = require('fs') | ||
function detectElectronPlatform (opts) { | ||
var appFrameworksPath = generateAppFrameworksPath(opts) | ||
if (!fs.existsSync(path.join(appFrameworksPath, 'Squirrel.framework'))) { | ||
// The presence of Squirrel.framework identifies a Mac App Store build as | ||
// used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md | ||
opts.platform = 'mas' | ||
} else { | ||
opts.platform = 'darwin' | ||
} | ||
} | ||
function findIdentity (opts, identity, cb) { | ||
child.exec('security find-identity', function (err, stdout, stderr) { | ||
if (err) return cb(new Error('Error in finding an identity.')) | ||
var lines = stdout.split('\n') | ||
var location | ||
for (var i = 0, l = lines.length; i < l && !opts.identity; i++) { | ||
var line = lines[i] | ||
location = line.indexOf(identity) | ||
if (location >= 0) { | ||
opts.identity = line.substring(location, line.length - 1) | ||
break | ||
} | ||
} | ||
if (!opts.identity) cb(new Error('No identity found for signing.')) | ||
cb() | ||
}) | ||
} | ||
function generateAppBasename (opts) { | ||
@@ -17,9 +46,9 @@ return path.basename(opts.app, '.app') | ||
function generateHelperAppPath (opts, opt, suffix, callback) { | ||
var appFrameworksPath = generateAppFrameworksPath(opts) | ||
var appBasename = generateAppBasename(opts) | ||
if (opts[opt]) { | ||
// Use helper path if specified | ||
if (fs.existsSync(opts['helper-path'])) return opts['helper-path'] | ||
if (fs.existsSync(opts[opt])) return opts[opt] | ||
else return callback(new Error('Specified Electron Helper not found.')) | ||
} else { | ||
var appFrameworksPath = generateAppFrameworksPath(opts) | ||
var appBasename = generateAppBasename(opts) | ||
var helperPath | ||
@@ -41,15 +70,21 @@ if (fs.existsSync(helperPath = path.join(appFrameworksPath, appBasename + ' Helper' + (suffix || '') + '.app'))) { | ||
function generateHelperAppExecutablePath (opts, helperPath, suffix, callback) { | ||
var appBasename = generateAppBasename(opts) | ||
var helperExecutablePath | ||
if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', appBasename + ' Helper' + (suffix || '')))) { | ||
// Try to look for helper named after app (assume renamed) | ||
return helperExecutablePath | ||
} else if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', 'Electron Helper' + (suffix || '')))) { | ||
// Try to look for helper by default | ||
return helperExecutablePath | ||
function generateHelperAppExecutablePath (opts, opt, helperPath, suffix, callback) { | ||
if (opts[opt]) { | ||
// Use helper executable path if specified | ||
if (fs.existsSync(opts[opt])) return opts[opt] | ||
else return callback(new Error('Specified Electron Helper executable not found.')) | ||
} else { | ||
// Helper not found | ||
callback(new Error('Electron Helper' + (suffix || '') + ' executable not found.')) | ||
return null | ||
var appBasename = generateAppBasename(opts) | ||
var helperExecutablePath | ||
if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', appBasename + ' Helper' + (suffix || '')))) { | ||
// Try to look for helper named after app (assume renamed) | ||
return helperExecutablePath | ||
} else if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', 'Electron Helper' + (suffix || '')))) { | ||
// Try to look for helper by default | ||
return helperExecutablePath | ||
} else { | ||
// Helper not found | ||
callback(new Error('Electron Helper' + (suffix || '') + ' executable not found.')) | ||
return null | ||
} | ||
} | ||
@@ -82,6 +117,7 @@ } | ||
} | ||
if (opts.binaries) childPaths = childPaths.concat(opts.binaries) | ||
var helperPath = generateHelperAppPath(opts, 'helper-path', null, callback) | ||
if (helperPath) { | ||
var helperExecutablePath = generateHelperAppExecutablePath(opts, helperPath, null, callback) | ||
var helperExecutablePath = generateHelperAppExecutablePath(opts, 'helper-executable-path', helperPath, null, callback) | ||
if (helperExecutablePath) childPaths.unshift(helperExecutablePath, helperPath) | ||
@@ -93,3 +129,3 @@ else return callback(new Error('Missing Electron Helper, stopped.')) | ||
if (helperEHPath) { | ||
var helperEHExecutablePath = generateHelperAppExecutablePath(opts, helperEHPath, ' EH', callback) | ||
var helperEHExecutablePath = generateHelperAppExecutablePath(opts, 'helper-eh-executable-path', helperEHPath, ' EH', callback) | ||
if (helperEHExecutablePath) childPaths.unshift(helperEHExecutablePath, helperEHPath) | ||
@@ -101,3 +137,3 @@ else return callback(new Error('Missing Electron Helper EH, stopped.')) | ||
if (helperNPPath) { | ||
var helperNPExecutablePath = generateHelperAppExecutablePath(opts, helperNPPath, ' NP', callback) | ||
var helperNPExecutablePath = generateHelperAppExecutablePath(opts, 'helper-np-executable-path', helperNPPath, ' NP', callback) | ||
if (helperNPExecutablePath) childPaths.unshift(helperNPExecutablePath, helperNPPath) | ||
@@ -112,13 +148,21 @@ else return callback(new Error('Missing Electron Helper NP, stopped.')) | ||
operations.push(function (cb) { | ||
child.exec('codesign -f -s "' + opts.identity + '" -fv \ ' | ||
+ '--entitlements "' + opts['entitlements-inherit'] + '" \ ' | ||
+ '"' + path + '"' | ||
, cb) | ||
child.exec([ | ||
'codesign', | ||
'-s', '"' + opts.identity + '"', | ||
'-fv', | ||
'--entitlements', '"' + opts['entitlements-inherit'] + '"', | ||
'"' + path.replace(/"/g, '\\"') + '"' | ||
].join(' '), cb) | ||
if (opts.verbose) console.log('Signing...', path) | ||
}) | ||
}) | ||
operations.push(function (cb) { | ||
child.exec('codesign -f -s "' + opts.identity + '" -fv \ ' | ||
+ '--entitlements "' + opts.entitlements + '" \ ' | ||
+ '"' + opts.app + '"' | ||
, cb) | ||
child.exec([ | ||
'codesign', | ||
'-s', '"' + opts.identity + '"', | ||
'-fv', | ||
'--entitlements', '"' + opts.entitlements + '"', | ||
'"' + opts.app.replace(/"/g, '\\"') + '"' | ||
].join(' '), cb) | ||
if (opts.verbose) console.log('Signing...', opts.app) | ||
}) | ||
@@ -132,11 +176,19 @@ } else if (opts.platform === 'darwin') { | ||
operations.push(function (cb) { | ||
child.exec('codesign -f -s "' + opts.identity + '" -fv \ ' | ||
+ '"' + path + '"' | ||
, cb) | ||
child.exec([ | ||
'codesign', | ||
'-s', '"' + opts.identity + '"', | ||
'-fv', | ||
'"' + path.replace(/"/g, '\\"') + '"' | ||
].join(' '), cb) | ||
if (opts.verbose) console.log('Signing...', path) | ||
}) | ||
}) | ||
operations.push(function (cb) { | ||
child.exec('codesign -f -s "' + opts.identity + '" -fv \ ' | ||
+ '"' + opts.app + '"' | ||
, cb) | ||
child.exec([ | ||
'codesign', | ||
'-s', '"' + opts.identity + '"', | ||
'-fv', | ||
'"' + opts.app.replace(/"/g, '\\"') + '"' | ||
].join(' '), cb) | ||
if (opts.verbose) console.log('Signing...', opts.app) | ||
}) | ||
@@ -147,5 +199,11 @@ } | ||
operations.push(function (cb) { | ||
child.exec('codesign -v --verbose=4 \ ' | ||
+ '"' + opts.app + '"' | ||
, cb) | ||
child.exec([ | ||
'codesign', | ||
'-v', | ||
'"' + opts.app.replace(/"/g, '\\"') + '"' | ||
].join(' '), function (err, stdout, stderr) { | ||
if (err) return cb(err) | ||
cb() | ||
}) | ||
if (opts.verbose) console.log('Verifying sign...') | ||
}) | ||
@@ -155,11 +213,15 @@ if (opts.entitlements) { | ||
operations.push(function (cb) { | ||
child.exec('codesign -d --entitlements - \ ' | ||
+ '"' + opts.app + '"' | ||
, function (err, stdout, stderr) { | ||
child.exec([ | ||
'codesign', | ||
'-d', | ||
'--entitlements', '-', | ||
'"' + opts.app.replace(/"/g, '\\"') + '"' | ||
].join(' '), function (err, stdout, stderr) { | ||
if (err) return cb(err) | ||
if (!stdout) return cb(new Error('Entitlements failed to be signed.')) | ||
cb() | ||
}) | ||
if (opts.verbose) console.log('Verifying entitlements...') | ||
}) | ||
} | ||
series(operations, function (err) { | ||
@@ -172,15 +234,23 @@ if (err) return callback(err) | ||
module.exports = function sign (opts, cb) { | ||
if (!cb) cb = function () {} | ||
// Default callback function if none provided | ||
if (!cb) { | ||
cb = function (err) { | ||
if (err) { | ||
if (opts.verbose) { | ||
console.error('Sign failed.') | ||
if (err.message) console.error(err.message) | ||
else console.error(err, err.stack) | ||
} | ||
return | ||
} | ||
if (opts.verbose) console.log('Application signed:', opts.app) | ||
} | ||
} | ||
if (!opts.app) return cb(new Error('Path to aplication must be specified.')) | ||
if (path.extname(opts.app) !== '.app') return cb(new Error('Extension of application must be `.app`.')) | ||
if (!fs.existsSync(opts.app)) return cb(new Error('Application not found.')) | ||
// Match platform if none is provided | ||
if (!opts.platform) { | ||
var appFrameworksPath = generateAppFrameworksPath(opts) | ||
if (!fs.existsSync(path.join(appFrameworksPath, 'Squirrel.framework'))) { | ||
// The presence of Squirrel.framework identifies a Mac App Store build as | ||
// used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md | ||
opts.platform = 'mas' | ||
} else { | ||
opts.platform = 'darwin' | ||
} | ||
if (opts.verbose) console.warn('No `--platform` passed in arguments, cheking Electron platform...') | ||
detectElectronPlatform(opts) | ||
} | ||
@@ -194,10 +264,19 @@ if (opts.platform === 'mas') { | ||
// Further reading: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html | ||
if (!opts.entitlements) opts.entitlements = path.join(__dirname, 'mas.default.plist') | ||
if (!opts['entitlements-inherit']) opts['entitlements-inherit'] = path.join(__dirname, 'mas.inherit.default.plist') | ||
if (!opts.entitlements) { | ||
if (opts.verbose) console.warn('No `--entitlements` passed in arguments, will fallback to default settings.') | ||
opts.entitlements = path.join(__dirname, 'mas.default.plist') | ||
} | ||
if (!opts['entitlements-inherit']) { | ||
if (opts.verbose) console.warn('No `--entitlements-inherit` passed in arguments, will fallback to default settings.') | ||
opts['entitlements-inherit'] = path.join(__dirname, 'mas.inherit.default.plist') | ||
} | ||
} else if (opts.platform === 'darwin') { | ||
// Not necessary to have entitlements for non Mac App Store distribution | ||
if (opts.entitlements) return cb(new Error('Unable to sign for darwin platform with entitlements.')) | ||
if (opts.entitlements && opts.verbose) return cb(new Error('Unable to sign for darwin platform with entitlements.')) | ||
} else { | ||
return cb(new Error('Only platform darwin and mas are supported.')) | ||
return cb(new Error('Only platform `darwin` and `mas` are supported.')) | ||
} | ||
if (opts.binaries) { | ||
if (!Array.isArray(opts.binaries)) return cb(new Error('Additional binaries should be an Array.')) | ||
} | ||
series([ | ||
@@ -207,25 +286,8 @@ function (cb) { | ||
if (!opts.identity) { | ||
child.exec('security find-identity', function (err, stdout, stderr) { | ||
if (err) return cb(new Error('Error in finding an identity.')) | ||
var lines = stdout.split('\n') | ||
var location | ||
for (var i = 0, l = lines.length; i < l && !opts.identity; i++) { | ||
var line = lines[i] | ||
if (opts.platform === 'mas') { | ||
location = line.indexOf('3rd Party Mac Developer Application') | ||
if (location >= 0) { | ||
opts.identity = line.substring(location, line.length - 1) | ||
break | ||
} | ||
} else if (opts.platform === 'darwin') { | ||
location = line.indexOf('Developer ID Application') | ||
if (location >= 0) { | ||
opts.identity = line.substring(location, line.length - 1) | ||
break | ||
} | ||
} | ||
} | ||
if (!opts.identity) cb(new Error('No identity found for signing.')) | ||
cb() | ||
}) | ||
if (opts.verbose) console.warn('No `--identity` passed in arguments, matching identities...') | ||
if (opts.platform === 'mas') { | ||
findIdentity(opts, '3rd Party Mac Developer Application', cb) | ||
} else if (opts.platform === 'darwin') { | ||
findIdentity(opts, 'Developer ID Application', cb) | ||
} | ||
} else cb() | ||
@@ -235,4 +297,13 @@ } | ||
if (err) return cb(err) | ||
if (opts.verbose) { | ||
console.log('Signing application...') | ||
console.log('> application ', opts.app) | ||
console.log('> platform ', opts.platform) | ||
console.log('> entitlements ', opts.entitlements || false) | ||
console.log('> child-entitlements ', opts['entitlements-inherit'] || false) | ||
console.log('> additional-binaries', opts.binaries) | ||
console.log('> identity ', opts.identity) | ||
} | ||
return signApplication(opts, cb) | ||
}) | ||
} |
{ | ||
"name": "electron-osx-sign", | ||
"version": "0.1.5", | ||
"description": "Codesign for Electron-packed apps", | ||
"version": "0.1.6", | ||
"description": "Code-signing for Electron-packed OS X apps.", | ||
"main": "index.js", | ||
"bin": { | ||
"electron-osx-sign": "cli.js" | ||
"electron-osx-sign": "bin/electron-osx-sign.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/sethlu/electron-sign.git" | ||
"url": "git+https://github.com/sethlu/electron-osx-sign.git" | ||
}, | ||
"author": "sethlu", | ||
"license": "MIT", | ||
"license": "BSD-2-Clause", | ||
"bugs": { | ||
"url": "https://github.com/sethlu/electron-sign/issues" | ||
"url": "https://github.com/sethlu/electron-osx-sign/issues" | ||
}, | ||
"homepage": "https://github.com/sethlu/electron-sign", | ||
"homepage": "https://github.com/sethlu/electron-osx-sign", | ||
"dependencies": { | ||
@@ -20,0 +20,0 @@ "minimist": "^1.1.1", |
# electron-osx-sign [![npm][npm_img]][npm_url] | ||
Code signing for Electron-packed OS X apps | ||
Code-signing for Electron-packed OS X apps. | ||
The signing procedure follows what described in [Mac App Store Submission Guide](https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md). | ||
## Installation | ||
@@ -22,3 +24,3 @@ | ||
```sh | ||
electron-osx-sign <app> [optional flags...] | ||
electron-osx-sign <app> [additional-binaries...] [--options...] | ||
``` | ||
@@ -32,3 +34,3 @@ | ||
For details on the optional flags, run `electron-osx-sign --help` or see [usage.txt](https://github.com/sethlu/electron-sign/blob/master/usage.txt). | ||
For details on the optional flags, run `electron-osx-sign --help` or see [electron-osx-sign-usage.txt](https://github.com/sethlu/electron-sign/blob/master/bin/electron-osx-sign-usage.txt). | ||
@@ -39,3 +41,3 @@ ### From the API | ||
var sign = require('electron-osx-sign') | ||
sign(opts[, function done (err) { }]) | ||
sign(opts[, function done (err) {}]) | ||
``` | ||
@@ -70,2 +72,7 @@ | ||
`binaries` - *Array* | ||
Path to additional binaries that will be signed along with built-ins of Electron. | ||
Default to `null`. | ||
`entitlements` - *String* | ||
@@ -86,2 +93,7 @@ | ||
`helper-executable-path` - *String* | ||
Path to `Electron Helper`, which may be renamed, in `Electron Helper.app`. | ||
Default to detect from application package. | ||
`helper-eh-path` - *String* | ||
@@ -92,2 +104,7 @@ | ||
`helper-eh-executable-path` - *String* | ||
Path to `Electron Helper EH`, which may be renamed, in `Electron Helper EH.app`. | ||
Default to detect from application package. | ||
`helper-np-path` - *String* | ||
@@ -98,2 +115,7 @@ | ||
`helper-np-executable-path` - *String* | ||
Path to `Electron Helper NP`, which may be renamed, in `Electron Helper NP.app`. | ||
Default to detect from application package. | ||
*Note: `helper-path`, `helper-eh-path`, `helper-np-path` needn't provided unless error thrown for not able to find any of them automatically.* | ||
@@ -114,2 +136,7 @@ | ||
`verbose` - *Boolean* | ||
Verbose flag, to display every action through `console.log()`. | ||
Allowed values: `true`, `false`. | ||
##### callback | ||
@@ -121,10 +148,22 @@ | ||
If error persists with `A timestamp was expected but was not found.` or `The timestamp service is not available.`, please try code sign the application later. The intermittent nature of the failures is a networking issue in communicating with the timestamp server. | ||
If error persists with `A timestamp was expected but was not found.` or `The timestamp service is not available.`, please try code-sign the application later. The intermittent nature of the failures is a networking issue in communicating with the timestamp server. | ||
## Electron | ||
## Notes | ||
Note: The Mac App Store builds of Electron started from v0.34.0. | ||
- Accidental halt of `codesign` may result in files with `.cstemp` extension created within the application. Please manually remove those files to avoid any issues that may occur during signing. | ||
Note: From v0.36.0 there was a bug preventing GPU process to start after the app being sandboxed, so it is recommended to use v0.35.x before this bug gets fixed. You can find more about this in issue [atom/electron#3871](https://github.com/atom/electron/issues/3871), referred here at https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md. | ||
- As `productbuild` is not yet incorporated into this code-signing module, please refer to [#5](https://github.com/sethlu/electron-osx-sign/issues/5) for packing signed applications for iTunes Connect submission. | ||
- In current version, binaries except built in within Electron will not be signed automatically. Please manually sign those before running this code-sign module. This issue may be resolved in future development, refer to [#6](https://github.com/sethlu/electron-osx-sign/issues/6). | ||
- The Mac App Store builds of Electron started from v0.34.0. | ||
- From v0.36.0 there was a bug preventing GPU process to start after the app being sandboxed, so it is recommended to use v0.35.x before this bug gets fixed. You can find more about this in issue [atom/electron#3871](https://github.com/atom/electron/issues/3871), refer to [Mac App Store Submission Guide](https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md). | ||
- To verify Gatekeeper acceptance of signed application package, currently not included in the automation, for distribution outside the Mac App Store (`darwin` only), enter the following command in Terminal: | ||
``` | ||
spctl --ignore-cache --no-cache -a -vvvv --type execute "path/to/my/app.app" | ||
``` | ||
For more details, please refer to [Distributing Apps Outside the Mac App Store]( https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/DistributingApplicationsOutside/DistributingApplicationsOutside.html). | ||
## Test | ||
@@ -147,3 +186,3 @@ | ||
> electron-sign@0.1.5 test electron-osx-sign | ||
> electron-sign@0.1.6 test electron-osx-sign | ||
> standard && tape test | ||
@@ -198,3 +237,3 @@ | ||
Thanks to [seanchas116](https://github.com/seanchas116) for improving the usability of this project implementation. | ||
Thanks to [seanchas116](https://github.com/seanchas116), and [jasonhinkle](https://github.com/jasonhinkle) for improving the usability of this project implementation. | ||
@@ -201,0 +240,0 @@ ## Related |
@@ -13,4 +13,5 @@ var sign = require('..') | ||
var opts = { | ||
app: util.generateAppPath(release) | ||
} // test with no options | ||
app: util.generateAppPath(release), | ||
verbose: config.verbose | ||
} // test with no other options for self discovery | ||
@@ -17,0 +18,0 @@ waterfall([ |
{ | ||
"timeout": 60000, | ||
"versions": [ | ||
@@ -17,3 +18,3 @@ "0.24.0", | ||
], | ||
"timeout": 30000 | ||
"verbose": false | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
27912
14
0
438
235