From 7c25bf6d5b59875830a5c10558a26f59f9bd10e1 Mon Sep 17 00:00:00 2001 From: develar Date: Wed, 17 Feb 2016 17:38:07 +0100 Subject: [PATCH] =?UTF-8?q?Document=20new=20API=20don't=20include=20app=20?= =?UTF-8?q?name=20in=20the=20intermediate=20directory=20name=20=E2=80=94?= =?UTF-8?q?=20name=20in=20the=20artifact=20file=20is=20enough?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/electron-builder.iml | 1 + .npmignore | 2 +- README.md | 308 ++++++++----------------- cli.js | 3 +- docs/deployment.md | 34 +++ usage.txt => docs/deprecated-usage.txt | 0 docs/multi-platform-build.md | 33 +++ package.json | 7 +- src/build-cli.ts | 16 +- src/errorMessages.ts | 11 + src/linuxPackager.ts | 12 +- src/macPackager.ts | 12 +- src/packager.ts | 29 +-- src/platformPackager.ts | 70 +++--- src/repositoryInfo.ts | 2 +- src/util.ts | 6 +- src/winPackager.ts | 37 +-- tsconfig.json | 1 + typings/command-line-args.d.ts | 5 +- typings/json-parse-helpfulerror.d.ts | 2 +- 20 files changed, 287 insertions(+), 304 deletions(-) create mode 100644 docs/deployment.md rename usage.txt => docs/deprecated-usage.txt (100%) create mode 100644 docs/multi-platform-build.md create mode 100644 src/errorMessages.ts diff --git a/.idea/electron-builder.iml b/.idea/electron-builder.iml index 248596ee632..0174957a41c 100644 --- a/.idea/electron-builder.iml +++ b/.idea/electron-builder.iml @@ -5,6 +5,7 @@ + diff --git a/.npmignore b/.npmignore index d21a1ff3671..93c5ee5b05e 100644 --- a/.npmignore +++ b/.npmignore @@ -4,7 +4,7 @@ example-app typings/ src/ tsconfig.json -tsd.json +typings.json npm-debug.log test/ docs/ \ No newline at end of file diff --git a/README.md b/README.md index 3d320292dcf..53a9f6bc418 100644 --- a/README.md +++ b/README.md @@ -1,241 +1,133 @@ -[![Build Status](https://img.shields.io/travis/loopline-systems/electron-builder.svg?style=flat)](https://travis-ci.org/loopline-systems/electron-builder) [![npm version](http://img.shields.io/npm/v/electron-builder.svg?style=flat)](https://www.npmjs.org/package/electron-builder) [![npm downloads](http://img.shields.io/npm/dm/electron-builder.svg?style=flat)](https://www.npmjs.org/package/electron-builder) [![Uses greenkeeper.io](https://img.shields.io/badge/Uses-greenkeeper.io-green.svg)](http://greenkeeper.io/) +Complete solution to build ready for distribution and "auto update" installers of your app for OS X, Windows and Linux. +* [Native application dependencies](http://electron.atom.io/docs/latest/tutorial/using-native-node-modules/) compilation (only if two-package.json project layout used). +* [Auto Update](#auto-update) ready application packaging. +* [Code Signing](#code-signing) on a CI server or development machine. +* [Build version management](#build-version-management). +* [Publishing artifacts to GitHub Releases](./docs/deployment.md). -# electron-builder +[electron-packager](https://github.com/maxogden/electron-packager), +[appdmg](https://github.com/LinusU/node-appdmg) and +[windows-installer](https://github.com/electronjs/windows-installer) are used under the hood. -The electron-builder project is used to create installers for the platforms Windows, OS X and Linux. -It's built to work together with [electron-packager](https://github.com/maxogden/electron-packager). +Real project example — [onshape-desktop-shell](https://github.com/develar/onshape-desktop-shell). -If you are looking for a complete set up on how to use [electron-packager](https://github.com/maxogden/electron-packager) and [electron-builder](https://github.com/loopline-systems/electron-builder), check the ["How we use it"](https://github.com/loopline-systems/electron-builder#how-we-use-it-so-far) section below. +# Configuration +## In short +1. Ensure that required fields are specified in the application `package.json`: -The project has been tested successfully on OS X, Windows and Linux (Debian-based) machines. + Standard `name`, `description`, `version` and `author`. -## Install - -You can go the global installation route. ;) - -``` -$ npm install -g electron-builder electron-packager -``` - -**Oooooor...** You can wrap `electron-builder` with `npm scripts` to not have a global dependency. - -``` -# install electron builder and electron-packager as dependencies of your project -$ npm install --save-dev electron-builder electron-packager -``` - -After that, you can easily use the `electron-builder` binary in your `package.json`: - -```json -{ - "scripts" : { - "pack:osx": "electron-packager . MyApp --platform=darwin --arch=x64 --version=0.36.7 --icon=assets/MyApp.icns --out=dist --ignore=dist", - "build:osx": "npm run pack:osx && electron-builder dist/osx/MyApp.app --platform=osx --out=\"dist/osx\" --config=config.json" + Custom `build` field must be specified: + ```json + "build": { + "app-bundle-id": "your.id", + "app-category-type": "your.app.category.type", + "iconUrl": "(windows only) A URL to an ICO file to use as the application icon, see details below" } -} -``` - -**Note:** Executables with spaces in their name **must** be written as `\"My App\"`. - -## Pre-requisites - -- Node.js 0.12 or higher. You can check by running: - -``` -$ node --version -v0.12.0 -``` - -- [electron-packager](https://github.com/maxogden/electron-packager) - -### Creating Installers - -For further documentation depending on the target please check the details sites: - -- [Create installers for Windows](./docs/win.md) -- [Create installers for OS X](./docs/osx.md) -- [Create installers for Linux](./docs/linux.md) - -## Parameters - -``` -Usage - $ electron-builder --platform= --config= --out= - - Required options: - platform: win, osx, linux - - Optional options: - config: path to config file - -> if not provided `builder` property in `package.json` - in current working directory will be read - out: path to output the installer (must exist) -``` + ``` + This object will be used as a source of [electron-packager](https://www.npmjs.com/package/electron-packager#packageropts-callback) options. You can specify any other options here. + +2. Create directory `build` in the root of the project and put your `background.png` (OS X DMG background), `icon.icns` (OS X app icon) and `icon.ico` (Windows app icon). + Linux icon set will be generated automatically on the fly from the OS X `icns` file. + +3. Add [scripts](https://docs.npmjs.com/cli/run-script) to the development `package.json`: + ```json + "scripts": { + "postinstall": "install-app-deps", + "pack": "build", + "dist": "build" + } + ``` + And then you can run `npm run pack` or `npm run dist` (to package in a distributable format (e.g. DMG, windows installer, NuGet package)). -Here's an example of what your `config.json` might look like: +4. Install [required system packages](./docs/multi-platform-build.md). -**Note**: `nsiTemplate` and `fileAssociation` are completely optional. +## iconUrl +Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http. +* If you don't plan to build windows installer, you can omit it. +* If your project repository is public on GitHub, it will be `https://raw.githubusercontent.com/${info.user}/${info.project}/master/build/icon.ico` by default. +## Distributable Format Configuration +In the development `package.json` custom `build` field can be specified to customize distributable format: ```json -{ - "osx" : { - "title": "Loopline Systems", - "background": "assets/osx/installer.png", - "icon": "assets/osx/mount.icns", +"build": { + "osx": { + "title": "computed name from app package.js, you can overwrite", + "icon": "build/icon.icns", "icon-size": 80, + "background": "build/background.png", "contents": [ - { "x": 438, "y": 344, "type": "link", "path": "/Applications" }, - { "x": 192, "y": 344, "type": "file" } + { + "x": 410, + "y": 220, + "type": "link", + "path": "/Applications" + }, + { + "x": 130, + "y": 220, + "type": "file", + "path": "computed path to artifact, do not specify it - will be overwritten" + } ] }, - "win" : { - "title" : "Loopline Systems", - "version" : "x.x.x.x", - "publisher": "Publisher Info", - "icon" : "assets/win/icon.ico", - "verbosity": 1, - "nsiTemplate" : "path/to/custom/installer.nsi.tpl", - "fileAssociation": { - "extension": ".loop", - "fileType": "Loopline Systems File" - } - }, - "linux" : { - "arch" : 64, - "target" : "deb", - "version" : "x.x.x.x", - "title" : "Loopline Systems", - "comment" : "This is a comment", - "executable" : "myExec", - "maintainer": "Dummy Maintainer " - } + "win": "see https://github.com/electronjs/windows-installer#usage" } ``` -Or what your `package.json` might look like: - -```json -{ - "name": "Loopline App", - "version": "2.6.0", - "builder": { - "osx" : { - "title": "Loopline Systems", - "background": "assets/osx/installer.png", - "icon": "assets/osx/mount.icns", - "icon-size": 80, - "contents": [ - { "x": 438, "y": 344, "type": "link", "path": "/Applications" }, - { "x": 192, "y": 344, "type": "file" } - ] - }, - "win" : { - "title" : "Loopline Systems", - "version" : "x.x.x.x", - "publisher": "Publisher Info", - "icon" : "assets/win/icon.ico", - "verbosity": 1, - "nsiTemplate" : "path/to/custom/installer.nsi.tpl", - "fileAssociation": { - "extension": ".loop", - "fileType": "Loopline Systems File" - } - }, - "linux" : { - "arch" : 64, - "target" : "deb", - "version" : "x.x.x.x", - "title" : "Loopline Systems", - "comment" : "This is a comment", - "executable" : "myExec", - "maintainer": "Dummy Maintainer " - } - } -} -``` +As you can see, you need to customize OS X options only if you want to provide custom `x, y`. +Don't customize paths to background and icon, — just follow conventions (if you don't want to use `build` as directory of resources — please create issue to ask ability to customize it). -**Note:** Need to add something that might have value for others? Consider a [Pull Request](https://github.com/loopline-systems/electron-builder/pulls)! ;) +See [OS X options](https://www.npmjs.com/package/appdmg#json-specification) and [Windows options](https://github.com/electronjs/windows-installer#usage). +# Auto Update +`electron-builder` produces all required artifacts: -## How we use it so far +* `.dmg`: OS X installer, required for OS X user to initial install. +* `-mac.zip`: required for Squirrel.Mac. +* `.exe` and `-x64.exe`: Windows installer, required for Windows user to initial install. Please note — [your app must handle Squirrel.Windows events](https://github.com/electronjs/windows-installer#handling-squirrel-events). See [real example](https://github.com/develar/onshape-desktop-shell/blob/master/src/WinSquirrelStartupEventHandler.ts). +* `.full-nupkg`: required for Squirrel.Windows. +* `-amd64.deb` and `-i386.deb`: Linux Debian package. Please note — by default the most effective [xz](https://en.wikipedia.org/wiki/Xz) compression format used. -When you run `npm run pack`, it will create executables for the Windows and OS X platforms inside of the `dist` directory. It grabs the generated executables afterwards to create the installers out of it. +You need to deploy somewhere [releases/downloads server](https://github.com/GitbookIO/nuts). +In general, there is a possibility to setup it as a service for all (it is boring to setup own if cloud service is possible). +May be it will be soon (feel free to file an issue to track progress). +It is safe since you should sign your app in any case (so, even if server will be compromised, users will not be affected because OS X will just block unsigned/unidentified app). -directory structure +# Code signing +OS X and Windows code singing is supported. +On a development machine set environment variable `CSC_NAME` to your identity (recommended). Or pass `--sign` parameter. ``` -desktop - |-- app // actual electron application - | - |-- assets // build related assets - |-- osx // build assets for OS X - |-- installer.png // -> referenced in builder.json ( dmg background ) - |-- mount.icns // -> use by electron-packager ( actual app icon ) - |-- loopline.icns // -> referenced in builder.json ( dmg background ) - |-- win // build assets for Windows - |-- icon.ico // -> referenced in builder.json - | - |-- dist // out put folder - |-- osx // generated executables for OS X - |-- Loopline Systems.app - |-- Loopline Systems.dmg - |-- win // generated executables for Windows - |-- Loopline Systems-win32 - |-- Loopline Systems Setup.exe - |-- package.json - |-- config.json +export CSC_NAME="Developer ID Application: Your Name (code)" ``` -`package.json`: - -```json -{ - "name": "loopline-desktop", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "dev": "electron ./app", - - "clean": "rm -rf ./dist", - "clean:osx": "rm -rf ./dist/osx", - "clean:win": "rm -rf ./dist/win", - - "pack": "npm run clean && npm run pack:osx && npm run pack:win", - "pack:osx": "npm run clean:osx && electron-packager ./app \"Loopline Systems\" --out=dist/osx --platform=darwin --arch=x64 --version=0.36.1 --icon=assets/osx/loopline.icns", - "pack:win": "npm run clean:win && electron-packager ./app \"Loopline Systems\" --out=dist/win --platform=win32 --arch=ia32 --version=0.36.1 --icon=assets/win/icon.ico", - - "build": "npm run build:osx && npm run build:win", - "build:osx": "npm run pack:osx && electron-builder \"dist/osx/Loopline Systems.app\" --platform=osx --out=\"dist/osx\" --config=builder.json", - "build:win": "npm run pack:win && electron-builder \"dist/win/Loopline Systems-win32\" --platform=win --out=\"dist/win\" --config=builder.json" - }, - "dependencies": { - "electron-packager": "^4.0.2", - "electron-prebuilt": "^0.36.7", - "electron-builder": "^2.7.2" - } -} +## Travis, AppVeyor and other CI servers +To sign app on build server: +1. [Export](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW7) certificate. + [Strong password](http://security.stackexchange.com/a/54773) must be used. Consider to not use special characters (for bash) because “*values are not escaped when your builds are executed*”. +2. Upload `*.p12` file (e.g. on [Google Drive](http://www.syncwithtech.org/p/direct-download-link-generator.html)). +3. Set ([Travis](https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables) or [AppVeyor](https://ci.appveyor.com/tools/encrypt)) `CSC_LINK` and `CSC_KEY_PASSWORD` environment variables: +``` +travis encrypt "CSC_LINK='https://drive.google.com/uc?export=download&id=***'" --add +travis encrypt 'CSC_KEY_PASSWORD=beAwareAboutBashEscaping!!!' --add ``` -**Important note for Windows users:** *If the build process throws an error like `"rm" is not recognized as an internal or external command, -operable program or batch file.` you may want to use windows counter part `rmdir` or `rimraf` (cross platform) to clean up the distribution folder.* - - -## Contribution - -You want to help out and have ideas to make it better? Great! - -Create an [issue](https://github.com/loopline-systems/electron-builder/issues) and we will tackle it. - -If you decide to propose a pull request (even better) make sure `npm test` is succeeding. - -## Releases +# Build Version Management +`CFBundleVersion` (OS X) and `FileVersion` (Windows) will be set automatically to `version`.`build_number` on CI server (Travis, AppVeyor and CircleCI supported). -For releases, we like to give release names via [adj-noun](https://github.com/btford/adj-noun). -You'll find proper release notes [here](https://github.com/loopline-systems/electron-builder/releases). +# CLI usage +Execute `node_modules/.bin/build --help` to get actual CLI usage guide. +In most cases you should not explicitly pass flags, so, we don't want to promote it here ([npm lifecycle](https://docs.npmjs.com/misc/scripts#current-lifecycle-event) is supported and script name is taken in account). +Want more — please file issue. -## Related packages +# Programmatic usage +See `node_modules/electron-builder/out/electron-builder.d.ts`. [Typings](https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages) is supported. -[grunt-electron-builder-wrapper](https://www.npmjs.com/package/grunt-electron-builder-wrapper) - grunt plugin for electron-builder. +# Old API (< 2.8) +Old API is deprecated, but not dropped. You can use it as before. Please note — new API by default produces Squirrel.Windows installer, set `target` to build NSIS: + ``` + build --target=nsis + ``` \ No newline at end of file diff --git a/cli.js b/cli.js index 5220b9d9867..09629350469 100755 --- a/cli.js +++ b/cli.js @@ -14,7 +14,8 @@ var meow = require( 'meow' ); var path = require( 'path' ); var builder = ( require( './' ) ).init(); -var usage = fs.readFileSync( path.join( __dirname, 'usage.txt' ) ).toString(); +var usage = fs.readFileSync( + path.join( __dirname, 'docs', 'deprecated-usage.txt' ), 'utf8' ); var cli = meow( usage, { help : usage, diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 00000000000..a5d6582f98f --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,34 @@ +# Publishing artifacts + +Travis and AppVeyor support publishing artifacts. But it requires additional configuration. For each CI (since AppVeyor can build only Windows and Travis only OS X / Linux). + +`electron-builder` allows you to just add `GH_TOKEN` environment variable and that's all. + +Currently, only GitHub Releases is supported. + +`publish` option values: + +| Value | Description +| -------------- | ----------- +| `onTag` | on tag push only +| `onTagOrDraft` | on tag push or if draft release exists +| `always` | always publish +| `never` | never publish + +But please consider to use automatic rules and don't specify `publish` explicitly: + +* If CI server detected, — `onTagOrDraft`. + + What does it means? [Draft a new release](https://help.github.com/articles/creating-releases/) (version (`v1.0`) must be equals to version (`1.0`) in the application `package.json`) and each CI build will update artifacts. It allows you to get latest artifacts in any time and publish release once it is ready. + +* If CI server reports that tag was pushed, — `onTag`. + + Release will be drafted (if doesn't exists) and artifacts published only and only if tag was pushed. + +* If [npm script](https://docs.npmjs.com/misc/scripts) named `release`, — `always`. + + Add to `scripts` in the development `package.json`: + ```json +"release": "build" +``` + and if you run `npm run release`, release will be drafted (if doesn't exists) and artifacts published. \ No newline at end of file diff --git a/usage.txt b/docs/deprecated-usage.txt similarity index 100% rename from usage.txt rename to docs/deprecated-usage.txt diff --git a/docs/multi-platform-build.md b/docs/multi-platform-build.md new file mode 100644 index 00000000000..a137944b6d7 --- /dev/null +++ b/docs/multi-platform-build.md @@ -0,0 +1,33 @@ +# Multi-platform build + +Don't expect that you can build app for all platforms on one platform. + +* If your app has native dependencies, it can be compiled only on the target platform. +[prebuild](https://www.npmjs.com/package/prebuild) is a solution, but most node modules [don't provide](https://github.com/atom/node-keytar/issues/27) prebuilt binaries. + +* OS Code Signing works only OS X. [Cannot be fixed](http://stackoverflow.com/a/12156576). +* Windows Code Signing works only on Windows. We are going [to fix it](https://developer.mozilla.org/en/docs/Signing_an_executable_with_Authenticode) soon. + +Don't think that mentioned issues are major, you should use build servers — e.g. [AppVeyor](http://www.appveyor.com/) to build Windows app and [Travis](https://travis-ci.org) to build OS X/Linux apps. + +See [sample appveyor.yml](https://github.com/develar/onshape-desktop-shell/blob/master/appveyor.yml) to build Electron app for Windows. +And [sample .travis.yml](https://github.com/develar/onshape-desktop-shell/blob/master/.travis.yml) to build Electron app for OS X. + +## OS X + +Use [brew](http://brew.sh) to install required packages. + +To build app in distributable format for Windows on OS X: +``` +brew install Caskroom/cask/xquartz wine mono +``` + +To build app in distributable format for Linux on OS X: +``` +brew install ruby gnu-tar libicns +gem install fpm +``` + +Please note — Windows codesign supported currently only on Windows. + +Linux and Windows: not documented yet. \ No newline at end of file diff --git a/package.json b/package.json index 917382c895a..bf5c1bf98dc 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "test-win": "npm run pretest && ava", "test-build": "result=\"$(tape cli.spec.js)\"; echo \"$result\" | tap-spec; echo \"$result\" | tnyan;", "publish-please": "publish-please", - "release": "npm prune && npm test && npm run publish-please" + "declaration": "dts-generator --name electron-builder --project . --out out/electron-builder.d.ts --indent=' '", + "release": "npm prune && npm test && npm run declaration && npm run publish-please" }, "repository": { "type": "git", @@ -75,6 +76,7 @@ "ava-tf": "^0.11.2-beta.0", "babel-plugin-array-includes": "^2.0.3", "babel-plugin-transform-es2015-parameters": "^6.5.0", + "dts-generator-tf": "^1.7.0-beta.0", "eslint": "^2.1.0", "path-sort": "^0.1.0", "plist": "^1.2.0", @@ -98,5 +100,6 @@ }, "ava": { "verbose": true - } + }, + "typings": "./out/electron-builder.d.ts" } diff --git a/src/build-cli.ts b/src/build-cli.ts index 5649ef31337..4b3a855594b 100644 --- a/src/build-cli.ts +++ b/src/build-cli.ts @@ -7,6 +7,8 @@ import { commonArgs } from "./util" import { printErrorAndExit } from "./promise" import { tsAwaiter } from "./awaiter" import cla = require("command-line-args") +import { readFileSync } from "fs" +import * as path from "path" const __awaiter = tsAwaiter Array.isArray(__awaiter) @@ -16,19 +18,23 @@ interface CliOptions extends PackagerOptions, PublishOptions { } const cli = cla(commonArgs.concat( - {name: "arch", type: String, description: "ia32, x64 or all (by default)."}, {name: "dist", type: Boolean, alias: "d", description: "Whether to package in a distributable format (e.g. DMG, windows installer, NuGet package)."}, {name: "publish", type: String, alias: "p", description: "Publish artifacts (to GitHub Releases): onTag (on tag push only) or onTagOrDraft (on tag push or if draft release exists)."}, - {name: "sign", type: String}, {name: "platform", type: String, multiple: true, description: "darwin, linux, win32 or all. Current platform (" + process.platform + ") by default."}, - {name: "target", type: String, multiple: true, description: "Installer or package type. For win32 - squirrel (default) or nsis."}, - {name: "help", alias: "h", type: Boolean} + {name: "arch", type: String, description: "ia32, x64 or all (by default)."}, + {name: "target", type: String, multiple: true, description: "Installer or package type. For win32: squirrel (default) or nsis (deprecated)."}, + {name: "sign", type: String}, + {name: "help", alias: "h", type: Boolean, description: "Display this usage guide."} )) const args: CliOptions = cli.parse() if (args.help) { - console.log(cli.getUsage()) + const version = process.env.npm_package_version || JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8")).version + console.log(cli.getUsage({ + title: "electron-builder " + version, + footer: "Project home: [underline]{https://github.com/loopline-systems/electron-builder}" + })) } else { build(args) diff --git a/src/errorMessages.ts b/src/errorMessages.ts new file mode 100644 index 00000000000..dc2f2145d5d --- /dev/null +++ b/src/errorMessages.ts @@ -0,0 +1,11 @@ +export const buildIsMissed = `Please specify 'build' configuration in the application package.json ('%s'), at least + + build: { + "app-bundle-id": "your.id", + "app-category-type": "your.app.category.type", + "iconUrl": "see https://github.com/develar/electron-builder#in-short", + } +} + +is required. +` \ No newline at end of file diff --git a/src/linuxPackager.ts b/src/linuxPackager.ts index 12f74e0f86e..1df823ad9f4 100644 --- a/src/linuxPackager.ts +++ b/src/linuxPackager.ts @@ -13,7 +13,7 @@ Array.isArray(__awaiter) const buildDeb = BluebirdPromise.promisify(init().build) const tmpDir = BluebirdPromise.promisify(<(config: TmpOptions, callback: (error: Error, path: string, cleanupCallback: () => void) => void) => void>_tpmDir) -export default class LinuxPackager extends PlatformPackager { +export class LinuxPackager extends PlatformPackager { desktopIcons: Promise> constructor(info: BuildInfo) { @@ -63,13 +63,13 @@ export default class LinuxPackager extends PlatformPackager { return "linux" } - async packageInDistributableFormat(outDir: string, arch: string): Promise { + async packageInDistributableFormat(outDir: string, appOutDir: string): Promise { const specification: DebOptions = { version: this.metadata.version, title: this.metadata.name, comment: this.metadata.description, maintainer: this.metadata.author, - arch: arch === "ia32" ? 32 : 64, + arch: this.currentArch === "ia32" ? 32 : 64, target: "deb", executable: this.metadata.name, desktop: `[Desktop Entry] @@ -88,8 +88,8 @@ export default class LinuxPackager extends PlatformPackager { } return await buildDeb({ log: function emptyLog() {/* ignore out */}, - appPath: outDir, - out: path.dirname(outDir), + appPath: appOutDir, + out: outDir, config: { linux: specification } @@ -98,7 +98,7 @@ export default class LinuxPackager extends PlatformPackager { } } -interface DebOptions { +export interface DebOptions { title: string comment: string diff --git a/src/macPackager.ts b/src/macPackager.ts index 12dbbb23ab9..128e7c84d07 100644 --- a/src/macPackager.ts +++ b/src/macPackager.ts @@ -28,10 +28,10 @@ export default class MacPackager extends PlatformPackager return "osx" } - async pack(platform: string, arch: string, outDir: string): Promise { - await super.pack(platform, arch, outDir) + async pack(platform: string, outDir: string, appOutDir: string): Promise { + await super.pack(platform, outDir, appOutDir) let codeSigningInfo = await this.codeSigningInfo - return await this.signMac(path.join(outDir, this.metadata.name + ".app"), codeSigningInfo) + return await this.signMac(path.join(appOutDir, this.metadata.name + ".app"), codeSigningInfo) } private signMac(distPath: string, codeSigningInfo: CodeSigningInfo): Promise { @@ -49,7 +49,7 @@ export default class MacPackager extends PlatformPackager } } - packageInDistributableFormat(outDir: string, arch: string): Promise { + packageInDistributableFormat(outDir: string, appOutDir: string): Promise { const artifactPath = path.join(outDir, this.metadata.name + "-" + this.metadata.version + ".dmg") return BluebirdPromise.all([ new BluebirdPromise((resolve, reject) => { @@ -78,7 +78,7 @@ export default class MacPackager extends PlatformPackager specification.title = this.metadata.name } - specification.contents[1].path = path.join(outDir, this.metadata.name + ".app") + specification.contents[1].path = path.join(appOutDir, this.metadata.name + ".app") const appDmg = require("appdmg") const emitter = appDmg({ @@ -91,7 +91,7 @@ export default class MacPackager extends PlatformPackager }) .then(() => this.dispatchArtifactCreated(artifactPath)), - this.zipMacApp(outDir) + this.zipMacApp(appOutDir) .then(it => this.dispatchArtifactCreated(it)) ]) } diff --git a/src/packager.ts b/src/packager.ts index 4c88d4e52df..58be17be730 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -10,7 +10,8 @@ import { AppMetadata, InfoRetriever } from "./repositoryInfo" import { PackagerOptions, PlatformPackager, BuildInfo, DevMetadata } from "./platformPackager" import MacPackager from "./macPackager" import WinPackager from "./winPackager" -import LinuxPackager from "./linuxPackager" +import * as errorMessages from "./errorMessages" +import * as util from "util" const __awaiter = tsAwaiter Array.isArray(__awaiter) @@ -72,10 +73,12 @@ export class Packager implements BuildInfo { for (let arch of archs) { await this.installAppDependencies(arch) - const outDir = path.join(this.projectDir, "dist", this.metadata.name + "-" + platform + "-" + arch) - await helper.pack(platform, arch, outDir) + helper.currentArch = arch + const outDir = path.join(this.projectDir, "dist") + const appOutDir = path.join(outDir, this.metadata.name + "-" + platform + "-" + arch) + await helper.pack(platform, outDir, appOutDir) if (this.options.dist) { - distTasks.push(helper.packageInDistributableFormat(outDir, arch)) + distTasks.push(helper.packageInDistributableFormat(outDir, appOutDir)) } } } @@ -101,10 +104,7 @@ export class Packager implements BuildInfo { } case "linux": - { - const helperClass: typeof LinuxPackager = require("./linuxPackager").default - return new helperClass(this) - } + return new (require("./linuxPackager").LinuxPackager)(this) default: throw new Error("Unsupported platform: " + platform) @@ -120,7 +120,7 @@ export class Packager implements BuildInfo { required = false } - let absoluteAppPath = path.join(this.projectDir, customAppPath) + const absoluteAppPath = path.join(this.projectDir, customAppPath) try { fs.accessSync(absoluteAppPath) } @@ -152,14 +152,7 @@ export class Packager implements BuildInfo { reportError("version") } else if (metadata.build == null) { - throw new Error("Please specify 'build' configuration in the application package.json ('" + appPackageFile + "'), at least\n\n" + - JSON.stringify({ - build: { - "app-bundle-id": "your.id", - "app-category-type": "your.app.category.type", - "iconUrl": "see https://github.com/develar/electron-complete-builder#in-short", - } - }, null, " ") + "\n\n is required.\n") + throw new Error(util.format(errorMessages.buildIsMissed, appPackageFile)) } else if (metadata.author == null) { reportError("author") @@ -172,7 +165,7 @@ export class Packager implements BuildInfo { } else { log("Skipping app dependencies installation because dev and app dependencies are not separated") - return Promise.resolve(null) + return BluebirdPromise.resolve(null) } } } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 9ad4c48256b..4df0b31a0b5 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -8,6 +8,8 @@ import packager = require("electron-packager-tf") const __awaiter = tsAwaiter Array.isArray(__awaiter) +const pack = BluebirdPromise.promisify(packager) + export interface DevMetadata extends Metadata { build: DevBuildMetadata } @@ -61,6 +63,8 @@ export abstract class PlatformPackager implements ProjectMetadataProvider { customDistOptions: DC + currentArch: string + protected abstract getBuildConfigurationKey(): string constructor(protected info: BuildInfo) { @@ -79,42 +83,40 @@ export abstract class PlatformPackager implements ProjectMetadataProvider { this.info.eventEmitter.emit("artifactCreated", path) } - pack(platform: string, arch: string, outDir: string): Promise { - return new BluebirdPromise((resolve, reject) => { - const version = this.metadata.version - let buildVersion = version - const buildNumber = process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM - if (buildNumber != null) { - buildVersion += "." + buildNumber + pack(platform: string, outDir: string, appOutDir: string): Promise { + const version = this.metadata.version + let buildVersion = version + const buildNumber = process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM + if (buildNumber != null) { + buildVersion += "." + buildNumber + } + + const options = Object.assign({ + dir: this.info.appDir, + out: outDir, + name: this.metadata.name, + platform: platform, + arch: this.currentArch, + version: this.info.electronVersion, + icon: path.join(this.projectDir, "build", "icon"), + asar: true, + overwrite: true, + "app-version": version, + "build-version": buildVersion, + "version-string": { + CompanyName: this.metadata.author, + FileDescription: this.metadata.description, + ProductVersion: version, + FileVersion: buildVersion, + ProductName: this.metadata.name, + InternalName: this.metadata.name, } + }, this.metadata.build, {"tmpdir": false}) - const options = Object.assign({ - dir: this.info.appDir, - out: path.dirname(outDir), - name: this.metadata.name, - platform: platform, - arch: arch, - version: this.info.electronVersion, - icon: path.join(this.projectDir, "build", "icon"), - asar: true, - overwrite: true, - "app-version": version, - "build-version": buildVersion, - "version-string": { - CompanyName: this.metadata.author, - FileDescription: this.metadata.description, - ProductVersion: version, - FileVersion: buildVersion, - ProductName: this.metadata.name, - InternalName: this.metadata.name, - } - }, this.metadata.build, {"tmpdir": false}) - - // this option only for windows-installer - delete options.iconUrl - packager(options, error => error == null ? resolve(null) : reject(error)) - }) + // this option only for windows-installer + delete options.iconUrl + return pack(options) } - abstract packageInDistributableFormat(outDir: string, arch: string): Promise + abstract packageInDistributableFormat(outDir: string, appOutDir: string): Promise } \ No newline at end of file diff --git a/src/repositoryInfo.ts b/src/repositoryInfo.ts index 397d447e6a7..97e9c478089 100644 --- a/src/repositoryInfo.ts +++ b/src/repositoryInfo.ts @@ -6,7 +6,7 @@ import * as path from "path" const __awaiter = tsAwaiter Array.isArray(__awaiter) -interface RepositoryInfo { +export interface RepositoryInfo { url: string } diff --git a/src/util.ts b/src/util.ts index dce100b6ff8..9d385166abc 100644 --- a/src/util.ts +++ b/src/util.ts @@ -49,13 +49,13 @@ export function installDependencies(appDir: string, arch: string, electronVersio }) } -interface BaseExecOptions { +export interface BaseExecOptions { cwd?: string env?: any stdio?: any } -interface ExecOptions extends BaseExecOptions { +export interface ExecOptions extends BaseExecOptions { customFds?: any encoding?: string timeout?: number @@ -63,7 +63,7 @@ interface ExecOptions extends BaseExecOptions { killSignal?: string } -interface SpawnOptions extends BaseExecOptions { +export interface SpawnOptions extends BaseExecOptions { custom?: any detached?: boolean } diff --git a/src/winPackager.ts b/src/winPackager.ts index 7730417c530..27a36c517b8 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -49,21 +49,25 @@ export default class WinPackager extends PlatformPackager { return "win" } - pack(platform: string, arch: string, outDir: string): Promise { + pack(platform: string, outDir: string, appOutDir: string): Promise { if (this.options.dist && !this.isNsis) { - const installerOut = outDir + "-installer" + const installerOut = this.computeDistOut(outDir) log("Removing %s", installerOut) return BluebirdPromise.all([ - super.pack(platform, arch, outDir), + super.pack(platform, outDir, appOutDir), emptyDir(installerOut) ]) } else { - return super.pack(platform, arch, outDir) + return super.pack(platform, outDir, appOutDir) } } - async packageInDistributableFormat(outDir: string, arch: string): Promise { + private computeDistOut(outDir: string): string { + return path.join(outDir, (this.isNsis ? "nsis" : "win") + (this.currentArch === "x64" ? "-x64" : "")) + } + + async packageInDistributableFormat(outDir: string, appOutDir: string): Promise { let iconUrl = this.metadata.build.iconUrl if (!iconUrl) { if (this.customDistOptions != null) { @@ -85,14 +89,14 @@ export default class WinPackager extends PlatformPackager { const certificateFile = await this.certFilePromise const version = this.metadata.version - const outputDirectory = outDir + "-" + (this.isNsis ? "nsis" : "installer") + const installerOutDir = this.computeDistOut(outDir) const appName = this.metadata.name - const archSuffix = arch === "x64" ? "-x64" : "" - const installerExePath = path.join(outputDirectory, appName + "Setup-" + version + archSuffix + ".exe") + const archSuffix = this.currentArch === "x64" ? "-x64" : "" + const installerExePath = path.join(installerOutDir, appName + "Setup-" + version + archSuffix + ".exe") const options = Object.assign({ name: this.metadata.name, - appDirectory: outDir, - outputDirectory: outputDirectory, + appDirectory: appOutDir, + outputDirectory: installerOutDir, productName: appName, version: version, description: this.metadata.description, @@ -108,9 +112,8 @@ export default class WinPackager extends PlatformPackager { } try { - await new BluebirdPromise((resolve, reject) => { - require("electron-winstaller-temp-fork").build(options, (error: Error) => error == null ? resolve(null) : reject(error)) - }) + const build = <(options: any, callback: (error: Error) => void) => void>require("electron-winstaller-temp-fork").build + await BluebirdPromise.promisify(build)(options) } catch (e) { if (!e.message.includes("Unable to set icon")) { @@ -132,18 +135,18 @@ export default class WinPackager extends PlatformPackager { } return await BluebirdPromise.all([ - renameFile(path.join(outputDirectory, appName + "Setup.exe"), installerExePath) + renameFile(path.join(installerOutDir, appName + "Setup.exe"), installerExePath) .then(it => this.dispatchArtifactCreated(it)), - renameFile(path.join(outputDirectory, appName + "-" + version + "-full.nupkg"), path.join(outputDirectory, appName + "-" + version + archSuffix + "-full.nupkg")) + renameFile(path.join(installerOutDir, appName + "-" + version + "-full.nupkg"), path.join(installerOutDir, appName + "-" + version + archSuffix + "-full.nupkg")) .then(it => this.dispatchArtifactCreated(it)) ]) } private async nsis(options: any, installerFile: string) { - const nsisBuild = <(options: any, callback: (error: Error) => void) => void>require("../lib/win").init().build + const build = <(options: any, callback: (error: Error) => void) => void>require("../lib/win").init().build // nsis cannot create dir await emptyDir(options.outputDirectory) - return await BluebirdPromise.promisify(nsisBuild)(Object.assign(options, { + return await BluebirdPromise.promisify(build)(Object.assign(options, { log: console.log, appPath: options.appDirectory, out: options.outputDirectory, diff --git a/tsconfig.json b/tsconfig.json index 921c8cb7944..6a9a0ab4c4a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -47,6 +47,7 @@ "src/build-cli.ts", "src/builder.ts", "src/codeSign.ts", + "src/errorMessages.ts", "src/gitHubPublisher.ts", "src/gitHubRequest.ts", "src/httpRequest.ts", diff --git a/typings/command-line-args.d.ts b/typings/command-line-args.d.ts index ba806231347..40829248b38 100644 --- a/typings/command-line-args.d.ts +++ b/typings/command-line-args.d.ts @@ -6,7 +6,10 @@ declare module "command-line-args" { } interface UsageOptions { - hide: Array + hide?: Array + + title?: string + footer?: string } function describe(options: Array): Options diff --git a/typings/json-parse-helpfulerror.d.ts b/typings/json-parse-helpfulerror.d.ts index 773f1cf7152..814303a6818 100644 --- a/typings/json-parse-helpfulerror.d.ts +++ b/typings/json-parse-helpfulerror.d.ts @@ -1,3 +1,3 @@ declare module "json-parse-helpfulerror" { - export function parse(data: string): any + function parse(data: string): any } \ No newline at end of file