Many packager improvements; Windows signing! (#413)
* Many packager improvements; Windows signing! * Windows signing works now! (Certs are on an external USB stick that must be plugged into the build machine during build. We can't do the same for OS X because certs need to exist in the login Keychain to be found.) Fixes #219 * Signing is now optional (so OS X and Windows contributors can run `npm run package` without errors) * zip, dmg, and deb arguments are now passed in as e.g. "--package=dmg" * Print a huge warning when signing is disabled so we're less likely to ship unsigned binaries to users. * Make console.logs during packaging consistent and parallel ("creating..." followed by "created.") * More aggressive signing warnings * Warn when building OS X app on non-OS X platform (because signing will never work on non-OS X platforms) * Warn when building Windows app on non-Windows platform (because signing doesn't work yet on non-Windows platforms)
This commit is contained in:
@@ -50,12 +50,15 @@ $ npm run package
|
||||
To build for one platform:
|
||||
|
||||
```
|
||||
$ npm run package -- [platform] [package-type]
|
||||
$ npm run package -- [platform]
|
||||
```
|
||||
|
||||
Where `[platform]` is `darwin`, `linux`, or `win32`
|
||||
Where `[platform]` is `darwin`, `linux`, `win32`, or `all` (default).
|
||||
|
||||
and `[package-type]` is `all` (default), `deb` or `zip` (`linux` platform only)
|
||||
The following optional arguments are available:
|
||||
|
||||
- `--package=[type]` - Package only one output file. `type` is `deb`, `dmg`, `zip`, or `all` (default)
|
||||
- `--sign` - Sign the application (OS X, Windows)
|
||||
|
||||
#### Windows build notes
|
||||
|
||||
|
||||
198
bin/package.js
198
bin/package.js
@@ -4,32 +4,55 @@
|
||||
* Builds app binaries for OS X, Linux, and Windows.
|
||||
*/
|
||||
|
||||
var config = require('../config')
|
||||
var cp = require('child_process')
|
||||
var electronPackager = require('electron-packager')
|
||||
var fs = require('fs')
|
||||
var minimist = require('minimist')
|
||||
var path = require('path')
|
||||
var pkg = require('../package.json')
|
||||
var rimraf = require('rimraf')
|
||||
|
||||
var config = require('../config')
|
||||
var pkg = require('../package.json')
|
||||
|
||||
var BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION
|
||||
|
||||
/*
|
||||
* Path to folder with the following files:
|
||||
* - Windows Authenticode private key and cert (authenticode.p12)
|
||||
* - Windows Authenticode password file (authenticode.txt)
|
||||
*/
|
||||
var CERT_PATH = process.platform === 'win32'
|
||||
? 'D:'
|
||||
: '/Volumes/Certs'
|
||||
|
||||
var argv = minimist(process.argv.slice(2), {
|
||||
boolean: [
|
||||
'sign'
|
||||
],
|
||||
default: {
|
||||
package: 'all',
|
||||
sign: false
|
||||
},
|
||||
string: [
|
||||
'package'
|
||||
]
|
||||
})
|
||||
|
||||
function build () {
|
||||
rimraf.sync(path.join(config.ROOT_PATH, 'dist'))
|
||||
var platform = process.argv[2]
|
||||
var packageType = process.argv.length > 3 ? process.argv[3] : 'all'
|
||||
var platform = argv._[0]
|
||||
if (platform === 'darwin') {
|
||||
buildDarwin(printDone)
|
||||
} else if (platform === 'win32') {
|
||||
buildWin32(printDone)
|
||||
} else if (platform === 'linux') {
|
||||
buildLinux(packageType, printDone)
|
||||
buildLinux(printDone)
|
||||
} else {
|
||||
buildDarwin(function (err, buildPath) {
|
||||
printDone(err, buildPath)
|
||||
buildWin32(function (err, buildPath) {
|
||||
printDone(err, buildPath)
|
||||
buildLinux(packageType, printDone)
|
||||
buildLinux(printDone)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -140,13 +163,25 @@ var linux = {
|
||||
// Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a large warning when signing is disabled so we are less likely to accidentally
|
||||
* ship unsigned binaries to users.
|
||||
*/
|
||||
process.on('exit', function () {
|
||||
if (!argv.sign) {
|
||||
printWarning()
|
||||
}
|
||||
})
|
||||
|
||||
build()
|
||||
|
||||
function buildDarwin (cb) {
|
||||
var plist = require('plist')
|
||||
|
||||
console.log('OS X: Packaging electron...')
|
||||
electronPackager(Object.assign({}, all, darwin), function (err, buildPath) {
|
||||
if (err) return cb(err)
|
||||
console.log('OS X: Packaged electron.')
|
||||
|
||||
var appPath = path.join(buildPath[0], config.APP_NAME + '.app')
|
||||
var contentsPath = path.join(appPath, 'Contents')
|
||||
@@ -189,11 +224,24 @@ function buildDarwin (cb) {
|
||||
cp.execSync(`cp ${config.APP_FILE_ICON + '.icns'} ${resourcesPath}`)
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
var appDmg = require('appdmg')
|
||||
if (argv.sign) {
|
||||
signApp(function (err) {
|
||||
if (err) return cb(err)
|
||||
pack()
|
||||
})
|
||||
} else {
|
||||
printWarning()
|
||||
pack()
|
||||
}
|
||||
} else {
|
||||
printWarning()
|
||||
}
|
||||
|
||||
function signApp (cb) {
|
||||
var sign = require('electron-osx-sign')
|
||||
|
||||
/*
|
||||
* Sign the app with Apple Developer ID certificate. We sign the app for 2 reasons:
|
||||
* Sign the app with Apple Developer ID certificates. We sign the app for 2 reasons:
|
||||
* - So the auto-updater (Squirrrel.Mac) can check that app updates are signed by
|
||||
* the same author as the current version.
|
||||
* - So users will not a see a warning about the app coming from an "Unidentified
|
||||
@@ -211,48 +259,68 @@ function buildDarwin (cb) {
|
||||
verbose: true
|
||||
}
|
||||
|
||||
console.log('OS X: Signing app...')
|
||||
sign(signOpts, function (err) {
|
||||
if (err) return cb(err)
|
||||
console.log('OS X: Signed app.')
|
||||
cb(null)
|
||||
})
|
||||
}
|
||||
|
||||
// Create .zip file (used by the auto-updater)
|
||||
var zipPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '-darwin.zip')
|
||||
cp.execSync(`cd ${buildPath[0]} && zip -r -y ${zipPath} ${config.APP_NAME + '.app'}`)
|
||||
console.log('Created OS X .zip file.')
|
||||
function pack () {
|
||||
if (argv.package === 'zip' || argv.package === 'all') {
|
||||
packageZip()
|
||||
}
|
||||
if (argv.package === 'dmg' || argv.package === 'all') {
|
||||
packageDmg()
|
||||
}
|
||||
}
|
||||
|
||||
var targetPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '.dmg')
|
||||
rimraf.sync(targetPath)
|
||||
function packageZip () {
|
||||
// Create .zip file (used by the auto-updater)
|
||||
var zipPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '-darwin.zip')
|
||||
console.log('OS X: Creating zip...')
|
||||
cp.execSync(`cd ${buildPath[0]} && zip -r -y ${zipPath} ${config.APP_NAME + '.app'}`)
|
||||
console.log('OS X: Created zip.')
|
||||
}
|
||||
|
||||
// Create a .dmg (OS X disk image) file, for easy user installation.
|
||||
var dmgOpts = {
|
||||
basepath: config.ROOT_PATH,
|
||||
target: targetPath,
|
||||
specification: {
|
||||
title: config.APP_NAME,
|
||||
icon: config.APP_ICON + '.icns',
|
||||
background: path.join(config.STATIC_PATH, 'appdmg.png'),
|
||||
'icon-size': 128,
|
||||
contents: [
|
||||
{ x: 122, y: 240, type: 'file', path: appPath },
|
||||
{ x: 380, y: 240, type: 'link', path: '/Applications' },
|
||||
// Hide hidden icons out of view, for users who have hidden files shown.
|
||||
// https://github.com/LinusU/node-appdmg/issues/45#issuecomment-153924954
|
||||
{ x: 50, y: 500, type: 'position', path: '.background' },
|
||||
{ x: 100, y: 500, type: 'position', path: '.DS_Store' },
|
||||
{ x: 150, y: 500, type: 'position', path: '.Trashes' },
|
||||
{ x: 200, y: 500, type: 'position', path: '.VolumeIcon.icns' }
|
||||
]
|
||||
}
|
||||
function packageDmg () {
|
||||
var appDmg = require('appdmg')
|
||||
|
||||
var targetPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '.dmg')
|
||||
rimraf.sync(targetPath)
|
||||
|
||||
// Create a .dmg (OS X disk image) file, for easy user installation.
|
||||
var dmgOpts = {
|
||||
basepath: config.ROOT_PATH,
|
||||
target: targetPath,
|
||||
specification: {
|
||||
title: config.APP_NAME,
|
||||
icon: config.APP_ICON + '.icns',
|
||||
background: path.join(config.STATIC_PATH, 'appdmg.png'),
|
||||
'icon-size': 128,
|
||||
contents: [
|
||||
{ x: 122, y: 240, type: 'file', path: appPath },
|
||||
{ x: 380, y: 240, type: 'link', path: '/Applications' },
|
||||
// Hide hidden icons out of view, for users who have hidden files shown.
|
||||
// https://github.com/LinusU/node-appdmg/issues/45#issuecomment-153924954
|
||||
{ x: 50, y: 500, type: 'position', path: '.background' },
|
||||
{ x: 100, y: 500, type: 'position', path: '.DS_Store' },
|
||||
{ x: 150, y: 500, type: 'position', path: '.Trashes' },
|
||||
{ x: 200, y: 500, type: 'position', path: '.VolumeIcon.icns' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var dmg = appDmg(dmgOpts)
|
||||
dmg.on('error', cb)
|
||||
dmg.on('progress', function (info) {
|
||||
if (info.type === 'step-begin') console.log(info.title + '...')
|
||||
})
|
||||
dmg.on('finish', function (info) {
|
||||
console.log('Created OS X disk image (.dmg) file.')
|
||||
cb(null, buildPath)
|
||||
})
|
||||
console.log('OS X: Creating dmg...')
|
||||
var dmg = appDmg(dmgOpts)
|
||||
dmg.on('error', cb)
|
||||
dmg.on('progress', function (info) {
|
||||
if (info.type === 'step-begin') console.log(info.title + '...')
|
||||
})
|
||||
dmg.on('finish', function (info) {
|
||||
console.log('OS X: Created dmg.')
|
||||
cb(null, buildPath)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -261,60 +329,79 @@ function buildDarwin (cb) {
|
||||
function buildWin32 (cb) {
|
||||
var installer = require('electron-winstaller')
|
||||
|
||||
console.log('Windows: Packaging electron...')
|
||||
electronPackager(Object.assign({}, all, win32), function (err, buildPath) {
|
||||
if (err) return cb(err)
|
||||
console.log('Windows: Packaged electron.')
|
||||
|
||||
console.log('Creating Windows installer...')
|
||||
var signWithParams
|
||||
if (process.platform === 'win32') {
|
||||
if (argv.sign) {
|
||||
var certificateFile = path.join(CERT_PATH, 'authenticode.p12')
|
||||
var certificatePassword = fs.readFileSync(path.join(CERT_PATH, 'authenticode.txt'), 'utf8')
|
||||
var timestampServer = 'http://timestamp.comodoca.com'
|
||||
signWithParams = `/a /f "${certificateFile}" /p "${certificatePassword}" /tr "${timestampServer}" /td sha256`
|
||||
} else {
|
||||
printWarning()
|
||||
}
|
||||
} else {
|
||||
printWarning()
|
||||
}
|
||||
|
||||
console.log('Windows: Creating installer...')
|
||||
installer.createWindowsInstaller({
|
||||
appDirectory: buildPath[0],
|
||||
authors: config.APP_TEAM,
|
||||
// certificateFile: '', // TODO
|
||||
description: config.APP_NAME,
|
||||
exe: config.APP_NAME + '.exe',
|
||||
iconUrl: config.GITHUB_URL_RAW + '/static/' + config.APP_NAME + '.ico',
|
||||
loadingGif: path.join(config.STATIC_PATH, 'loading.gif'),
|
||||
remoteReleases: config.GITHUB_URL,
|
||||
name: config.APP_NAME,
|
||||
noMsi: true,
|
||||
outputDirectory: path.join(config.ROOT_PATH, 'dist'),
|
||||
productName: config.APP_NAME,
|
||||
remoteReleases: config.GITHUB_URL,
|
||||
setupExe: config.APP_NAME + 'Setup-v' + config.APP_VERSION + '.exe',
|
||||
setupIcon: config.APP_ICON + '.ico',
|
||||
signWithParams: signWithParams,
|
||||
title: config.APP_NAME,
|
||||
usePackageJson: false,
|
||||
version: pkg.version
|
||||
}).then(function () {
|
||||
console.log('Created Windows installer.')
|
||||
console.log('Windows: Created installer.')
|
||||
cb(null, buildPath)
|
||||
}).catch(cb)
|
||||
})
|
||||
}
|
||||
|
||||
function buildLinux (packageType, cb) {
|
||||
function buildLinux (cb) {
|
||||
var distPath = path.join(config.ROOT_PATH, 'dist')
|
||||
|
||||
console.log('Linux: Packaging electron...')
|
||||
electronPackager(Object.assign({}, all, linux), function (err, buildPath) {
|
||||
if (err) return cb(err)
|
||||
console.log('Linux: Packaged electron.')
|
||||
|
||||
for (var i = 0; i < buildPath.length; i++) {
|
||||
var filesPath = buildPath[i]
|
||||
var destArch = filesPath.split('-').pop()
|
||||
|
||||
if (packageType === 'deb' || packageType === 'all') {
|
||||
if (argv.package === 'deb' || argv.package === 'all') {
|
||||
packageDeb(filesPath, destArch)
|
||||
}
|
||||
|
||||
if (packageType === 'zip' || packageType === 'all') {
|
||||
if (argv.package === 'zip' || argv.package === 'all') {
|
||||
packageZip(filesPath, destArch)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function packageDeb (filesPath, destArch) {
|
||||
// Create .deb file for debian based platforms
|
||||
// Create .deb file for Debian-based platforms
|
||||
var deb = require('nobin-debian-installer')()
|
||||
var destPath = path.join('/opt', pkg.name)
|
||||
|
||||
console.log(`Linux: Creating ${destArch} deb...`)
|
||||
deb.pack({
|
||||
package: pkg,
|
||||
info: {
|
||||
@@ -333,7 +420,7 @@ function buildLinux (packageType, cb) {
|
||||
cwd: filesPath
|
||||
}], function (err) {
|
||||
if (err) return console.error(err.message || err)
|
||||
console.log('Created Linux ' + destArch + ' .deb file.')
|
||||
console.log(`Linux: Created ${destArch} deb.`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -341,8 +428,9 @@ function buildLinux (packageType, cb) {
|
||||
// Create .zip file for Linux
|
||||
var zipPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '-linux-' + destArch + '.zip')
|
||||
var appFolderName = path.basename(filesPath)
|
||||
console.log(`Linux: Creating ${destArch} zip...`)
|
||||
cp.execSync(`cd ${distPath} && zip -r -y ${zipPath} ${appFolderName}`)
|
||||
console.log('Created Linux ' + destArch + ' .zip file.')
|
||||
console.log(`Linux: Created ${destArch} zip.`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,3 +438,7 @@ function printDone (err, buildPath) {
|
||||
if (err) console.error(err.message || err)
|
||||
else console.log('Built ' + buildPath[0])
|
||||
}
|
||||
|
||||
function printWarning () {
|
||||
console.log(fs.readFileSync(path.join(__dirname, 'warning.txt'), 'utf8'))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
git diff --exit-code
|
||||
npm run package
|
||||
npm run package -- --sign
|
||||
git push
|
||||
git push --tags
|
||||
npm publish
|
||||
|
||||
12
bin/warning.txt
Normal file
12
bin/warning.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
*********************************************************
|
||||
_ _ ___ ______ _ _ _____ _ _ _____
|
||||
| | | |/ _ \ | ___ \ \ | |_ _| \ | | __ \
|
||||
| | | / /_\ \| |_/ / \| | | | | \| | | \/
|
||||
| |/\| | _ || /| . ` | | | | . ` | | __
|
||||
\ /\ / | | || |\ \| |\ |_| |_| |\ | |_\ \
|
||||
\/ \/\_| |_/\_| \_\_| \_/\___/\_| \_/\____/
|
||||
|
||||
Application is NOT signed. Do not ship this to users!
|
||||
|
||||
*********************************************************
|
||||
@@ -47,6 +47,7 @@
|
||||
"electron-packager": "^6.0.2",
|
||||
"electron-winstaller": "feross/windows-installer#build",
|
||||
"gh-release": "^2.0.3",
|
||||
"minimist": "^1.2.0",
|
||||
"nobin-debian-installer": "^0.0.9",
|
||||
"plist": "^1.2.0",
|
||||
"standard": "^6.0.5"
|
||||
@@ -71,7 +72,6 @@
|
||||
"scripts": {
|
||||
"clean": "node ./bin/clean.js",
|
||||
"package": "npm install && npm prune && npm dedupe && node ./bin/package.js",
|
||||
"size": "npm run package -- darwin && du -ch dist/WebTorrent-darwin-x64 | grep total",
|
||||
"start": "electron .",
|
||||
"test": "standard",
|
||||
"update-authors": "./bin/update-authors.sh"
|
||||
|
||||
Reference in New Issue
Block a user