diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000000..5c5ae7d7630
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,43 @@
+{
+ env: {
+ development: {
+ plugins: [
+ [
+ "transform-async-to-module-method",
+ {
+ module: "bluebird-lst-c",
+ method: "coroutine"
+ }
+ ],
+ "transform-es2015-parameters",
+ "transform-es2015-spread",
+ "transform-es2015-destructuring",
+ "array-includes",
+ [
+ "transform-inline-imports-commonjs",
+ {
+ excludeModules: ["path"]
+ }
+ ],
+ ],
+ },
+ test: {
+ sourceMaps: "inline",
+ plugins: [
+ [
+ "transform-async-to-module-method",
+ {
+ module: "bluebird-lst-c",
+ method: "coroutine"
+ }
+ ],
+ [
+ "transform-inline-imports-commonjs",
+ {
+ excludeModules: ["path"]
+ }
+ ],
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 6cd89979bf8..8f131770e95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,31 @@
node_modules/
*.log
-out/
+
+#**/out/**/*
+#!**/*js.snap
+
dist/
+
/.idea/compiler.xml
/.idea/encodings.xml
-/.idea/copyright/profiles_settings.xml
/.idea/workspace.xml
+/.idea/copyright/
/.idea/deployment.xml
+/.idea/shelf/
+
+/docs/.idea/
+
/typings/browser/
/typings/browser.d.ts
/typings/main.d.ts
+
.DS_Store
-.idea/shelf/
+
/test/typings/electron-builder.d.ts
-/test/typings/electron-auto-updater.d.ts
\ No newline at end of file
+/test/typings/electron-auto-updater.d.ts
+
+/nsis-auto-updater/out/
+/out/
+# to not exclude .js.snap (jest snapshots)
+/test/out/**/*.js
+/test/out/**/*.map
\ No newline at end of file
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
index d52229a2c3d..1038133a841 100644
--- a/.idea/codeStyleSettings.xml
+++ b/.idea/codeStyleSettings.xml
@@ -20,8 +20,12 @@
+
+
+
+
diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml
index dd56977cc42..f0a00dae295 100644
--- a/.idea/dictionaries/develar.xml
+++ b/.idea/dictionaries/develar.xml
@@ -16,6 +16,8 @@
appxmanifestarchiverarchs
+ armv
+ asaraspxatexitatime
@@ -40,6 +42,7 @@
debiandepcheckdevel
+ develardevmodediffletdigester
@@ -72,6 +75,7 @@
hklmhomedirhrtime
+ icnsicnsutilsicnviconset
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index a4ab52713ac..16cea617f7b 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -3,8 +3,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/rc-producer.yml b/.idea/rc-producer.yml
new file mode 100644
index 00000000000..d32be87c1a5
--- /dev/null
+++ b/.idea/rc-producer.yml
@@ -0,0 +1,14 @@
+- &defaults
+ files: ["test/src/**/*", "!**/helpers/**/*"]
+ script: "node_modules/.bin/jest"
+ scriptArgs: ["-i", "--env", "jest-environment-node-debug", &filePattern '/${fileNameWithoutExt}\.\w+$']
+ rcName: "${fileNameWithoutExt}"
+ beforeRun: typescript
+
+-
+ <<: *defaults
+ lineRegExp: '^\s*(?:test|it)(?:\.\w+)?\("([^"'']+)'
+ scriptArgs: ["-i", "--env", "jest-environment-node-debug", "-t", "${0regExp}", *filePattern]
+ rcName: "${fileNameWithoutExt}.${0}"
+ env:
+ TEST_APP_TMP_DIR: /tmp/electron-builder-test
\ No newline at end of file
diff --git a/.idea/runConfigurations/ArtifactPublisherTest.xml b/.idea/runConfigurations/ArtifactPublisherTest.xml
deleted file mode 100644
index 78bee45d503..00000000000
--- a/.idea/runConfigurations/ArtifactPublisherTest.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/BuildTest.xml b/.idea/runConfigurations/BuildTest.xml
deleted file mode 100644
index 84eb02dad06..00000000000
--- a/.idea/runConfigurations/BuildTest.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/CodeSignTest.xml b/.idea/runConfigurations/CodeSignTest.xml
deleted file mode 100644
index 4322aa815c4..00000000000
--- a/.idea/runConfigurations/CodeSignTest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/RepoSlugTest.xml b/.idea/runConfigurations/RepoSlugTest.xml
deleted file mode 100644
index 62b3caea04d..00000000000
--- a/.idea/runConfigurations/RepoSlugTest.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/winPackagerTest__debug_logging_.xml b/.idea/runConfigurations/winPackagerTest__debug_logging_.xml
deleted file mode 100644
index 18875c1d738..00000000000
--- a/.idea/runConfigurations/winPackagerTest__debug_logging_.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/scopes/json5.xml b/.idea/scopes/json5.xml
new file mode 100644
index 00000000000..e11bd99491d
--- /dev/null
+++ b/.idea/scopes/json5.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.mention-bot b/.mention-bot
new file mode 100644
index 00000000000..78cb7b19b29
--- /dev/null
+++ b/.mention-bot
@@ -0,0 +1,3 @@
+{
+ "userBlacklist": ["develar"]
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index b8dd1a06788..193c169a9ff 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
matrix:
include:
- os: osx
- env: TEST_FILES=BuildTest,extraMetadataTest,globTest,filesTest,ignoreTest,linux.* NODE_VERSION=7 PUBLISH_TO_NPM=true
+ env: TEST_FILES=BuildTest,extraMetadataTest,globTest,filesTest,ignoreTest,linux.* NODE_VERSION=6 PUBLISH_TO_NPM=true
- os: osx
env: TEST_FILES=windows.*,mac.* NODE_VERSION=7
@@ -29,9 +29,7 @@ before_install:
install:
- nvm install $NODE_VERSION
- nvm use --delete-prefix $NODE_VERSION
-- mkdir -p ~/Library/Caches/Yarn
-- curl -o- -L https://yarnpkg.com/install.sh | bash
-- export PATH="$PATH:$HOME/.yarn/bin"
+- ln -sf $PWD/test/yarn.js /usr/local/bin/yarn
- yarn install --pure-lockfile
- if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && "$AUTO_PUBLISH" != "false" && "$TRAVIS_TAG" == "" && "$PUBLISH_TO_NPM" == "true" ]]; then yarn add @develar/semantic-release@next --dev ; fi
diff --git a/MAINTAINING.md b/MAINTAINING.md
index f8578cc7f9c..3f972e32c89 100644
--- a/MAINTAINING.md
+++ b/MAINTAINING.md
@@ -6,7 +6,7 @@ new version with tag `next` will be published if `fix`, `feat` or `BREAKING CHAN
# Sync wiki
To add `wiki` upstream:
```
-git remote add upstream https://github.com/electron-userland/electron-builder.wiki.git
+git remote add wiki git@github.com:electron-userland/electron-builder.wiki.git
```
-To sync: `npm run update-wiki`
\ No newline at end of file
+To sync: `yarn update-wiki`
\ No newline at end of file
diff --git a/README.md b/README.md
index c14aa725562..1686f053dcd 100755
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ For an app that will be shipped to production, you should sign your application.
"build": {
"appId": "your.id",
"mac": {
- "category": "your.app.category.type",
+ "category": "your.app.category.type"
},
"win": {
"iconUrl": "(windows-only) https link to icon"
diff --git a/appveyor.yml b/appveyor.yml
index f4f5064fc71..8c06a195bcb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -6,15 +6,13 @@ cache:
- '%USERPROFILE%\.electron'
environment:
- RUN_IN_BAND: true
+ TEST_FILES: BuildTest,extraMetadataTest,filesTest,globTest
install:
- ps: Install-Product node 6 x64
- # unknown yarn error
- - npm install
- - npm prune
+ - node ./test/yarn.js
build: off
test_script:
- - npm run test
\ No newline at end of file
+ - node ./test/yarn.js test
\ No newline at end of file
diff --git a/docker/7/Dockerfile b/docker/7/Dockerfile
index 71aa8514924..96a6466c4d6 100644
--- a/docker/7/Dockerfile
+++ b/docker/7/Dockerfile
@@ -1,6 +1,6 @@
FROM electronuserland/electron-builder:base
-ENV NODE_VERSION 7.1.0
+ENV NODE_VERSION 7.2.0
# https://github.com/npm/npm/issues/4531
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile
index 1eb54385db5..1f9fa2ffd8b 100644
--- a/docker/base/Dockerfile
+++ b/docker/base/Dockerfile
@@ -1,4 +1,4 @@
-FROM buildpack-deps:yakkety-curl
+FROM buildpack-deps:xenial-curl
# rpm is required for FPM to build rpm package
# yasm is required to build p7zip
@@ -15,9 +15,8 @@ ENV USE_SYSTEM_7ZA true
ENV DEBUG_COLORS true
ENV FORCE_COLOR true
-RUN apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg && echo "deb http://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
-apt-get update -y && \
- apt-get install --no-install-recommends -y git snapcraft yarn xorriso bsdtar build-essential autoconf libssl-dev icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev lzip rpm yasm && \
+RUN curl -L https://yarnpkg.com/latest.tar.gz | tar xvz && mv dist yarn && ln -s /yarn/bin/yarn /usr/local/bin/yarn && apt-get update -y && \
+ apt-get install --no-install-recommends -y git snapcraft xorriso bsdtar build-essential autoconf libssl-dev icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev lzip rpm yasm && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
curl -L http://tukaani.org/xz/xz-$XZ_VERSION.tar.xz | tar -xJ && cd xz-$XZ_VERSION && ./configure && make && make install && cd .. && rm -rf xz-$XZ_VERSION && ldconfig && \
@@ -48,4 +47,5 @@ WORKDIR /project
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
-ENV LC_ALL en_US.UTF-8
\ No newline at end of file
+ENV LC_ALL en_US.UTF-8
+ENV PATH "$HOME/.yarn/bin:$PATH"
\ No newline at end of file
diff --git a/docker/wine/Dockerfile b/docker/wine/Dockerfile
index df96a42f3a5..6967f5c7796 100644
--- a/docker/wine/Dockerfile
+++ b/docker/wine/Dockerfile
@@ -2,7 +2,7 @@ FROM electronuserland/electron-builder:latest
# libgnome-keyring-dev — to build keytar
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F9CB8DB0 && \
-echo "deb http://ppa.launchpad.net/ubuntu-wine/ppa/ubuntu yakkety main " | tee /etc/apt/sources.list.d/wine.list && \
+echo "deb http://ppa.launchpad.net/ubuntu-wine/ppa/ubuntu xenial main " | tee /etc/apt/sources.list.d/wine.list && \
dpkg --add-architecture i386 && \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list && \
diff --git a/docs/Multi Platform Build.md b/docs/Multi Platform Build.md
index be04daa33ec..766caf8b83c 100755
--- a/docs/Multi Platform Build.md
+++ b/docs/Multi Platform Build.md
@@ -81,4 +81,4 @@ dist: trusty
## Windows
-Use [Docker](https://github.com/electron-userland/electron-builder/wiki/Docker).
+Please use [Docker](https://github.com/electron-userland/electron-builder/wiki/Docker).
diff --git a/docs/NSIS.md b/docs/NSIS.md
index 796b6f2fdc5..64e575507b9 100644
--- a/docs/NSIS.md
+++ b/docs/NSIS.md
@@ -38,4 +38,8 @@ So, it is better to use [GUID](http://stackoverflow.com/a/246935/1910191).
You are not forced to explicitly specify it — name-based [UUID v5](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_5_.28SHA-1_hash_.26_namespace.29) will be generated from your [appId](https://github.com/electron-userland/electron-builder/wiki/Options#BuildMetadata-appId) or [name](https://github.com/electron-userland/electron-builder/wiki/Options#AppMetadata-name).
It means that you **should not change appId** once your application in use (or name if `appId` was not set). Application product name (title) or description can be safely changed.
-You can explicitly set guid using option [nsis.guid](https://github.com/electron-userland/electron-builder/wiki/Options#NsisOptions-guid), but it is not recommended — consider using [appId](https://github.com/electron-userland/electron-builder/wiki/Options#BuildMetadata-appId).
\ No newline at end of file
+You can explicitly set guid using option [nsis.guid](https://github.com/electron-userland/electron-builder/wiki/Options#NsisOptions-guid), but it is not recommended — consider using [appId](https://github.com/electron-userland/electron-builder/wiki/Options#BuildMetadata-appId).
+
+It is also important to set the Application User Model ID (AUMID) to the [appId](https://github.com/electron-userland/electron-builder/wiki/Options#BuildMetadata-appId) of the application, in order for notifications on Windows 8/8.1 to function and for Window 10 notifications to display the app icon within the notifications by default. The AUIMD should be set within the Main process and before any BrowserWindows have been opened, it is normally the first piece of code executed.
+
+`app.setAppUserModelId(appId)`
diff --git a/docs/Options.md b/docs/Options.md
index 245bfe8e4c5..f6366ca2129 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -27,6 +27,7 @@ Don't customize paths to background and icon, — just follow conventions.
* [Application package.json](#AppMetadata)
* [Development package.json](#DevMetadata)
* [.build](#BuildMetadata)
+ * [.build.appx](#AppXOptions)
* [.build.dmg](#DmgOptions)
* [.build.dmg.window](#DmgWindow)
* [.build.fileAssociations](#FileAssociation)
@@ -58,7 +59,6 @@ Don't customize paths to background and icon, — just follow conventions.
| Name | Description
| --- | ---
| **build** | See [.build](#BuildMetadata).
-| directories | See [.directories](#MetadataDirectories)
## `.build`
@@ -70,7 +70,7 @@ Don't customize paths to background and icon, — just follow conventions.
| files |
A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package.
See [File Patterns](#multiple-glob-patterns).
| extraResources |
A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app’s resources directory (Contents/Resources for MacOS, resources for Linux/Windows).
Glob rules the same as for [files](#multiple-glob-patterns).
| extraFiles | The same as [extraResources](#BuildMetadata-extraResources) but copy into the app's content directory (`Contents` for MacOS, root directory for Linux/Windows).
-| asar |
Whether to package the application’s source code into an archive, using [Electron’s archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to true. Reasons why you may want to disable this feature are described in [an application packaging tutorial in Electron’s documentation](http://electron.atom.io/docs/tutorial/application-packaging/#limitations-of-the-node-api).
Or you can pass object of any asar options.
Node modules, that must be unpacked, will be detected automatically, you don’t need to explicitly set asarUnpack - please file issue if this doesn’t work.
+| asar |
Whether to package the application’s source code into an archive, using [Electron’s archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to true. Node modules, that must be unpacked, will be detected automatically, you don’t need to explicitly set [asarUnpack](#BuildMetadata-asarUnpack) - please file issue if this doesn’t work.
Or you can pass object of asar options.
| asarUnpack | A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to unpack when creating the [asar](http://electron.atom.io/docs/tutorial/application-packaging/) archive.
| fileAssociations | The file associations. See [.build.fileAssociations](#FileAssociation).
| protocols | The URL protocol scheme(s) to associate the app with. See [.build.protocol](#Protocol).
@@ -91,6 +91,21 @@ Don't customize paths to background and icon, — just follow conventions.
| electronDist | The path to custom Electron build (e.g. `~/electron/out/R`). Only macOS supported, file issue if need for Linux or Windows.
| electronDownload | The [electron-download](https://github.com/electron-userland/electron-download#usage) options.
| publish | See [.build.publish](#PublishConfiguration).
+| forceCodeSigning | Whether to fail if application will be not signed (to prevent unsigned app if code signing configuration is not correct).
+| directories | See [.directories](#MetadataDirectories)
+
+
+### `.build.appx`
+
+Please see [Windows AppX docs](https://msdn.microsoft.com/en-us/library/windows/apps/br211453.aspx).
+
+| Name | Description
+| --- | ---
+| backgroundColor | The background color of the app tile. Please see [Visual Elements](https://msdn.microsoft.com/en-us/library/windows/apps/br211471.aspx).
+| publisher | Describes the publisher information. The Publisher attribute must match the publisher subject information of the certificate used to sign a package. For now, required.
+| displayName | A friendly name that can be displayed to users. Corresponds to [Properties.DisplayName](https://msdn.microsoft.com/en-us/library/windows/apps/br211432.aspx).
+| publisherDisplayName | A friendly name for the publisher that can be displayed to users. Corresponds to [Properties.PublisherDisplayName](https://msdn.microsoft.com/en-us/library/windows/apps/br211460.aspx).
+| identityName | Describes the contents of the package. The Name attribute is case-sensitive. Corresponds to [Identity.Name](https://msdn.microsoft.com/en-us/library/windows/apps/br211441.aspx).
### `.build.dmg`
@@ -132,7 +147,8 @@ macOS and NSIS only. Array of option objects.
| **name** | The name. e.g. `PNG`.
| description | *windows-only.* The description.
| icon | The path to icon (`.icns` for MacOS and `.ico` for Windows), relative to `build` (build resources directory). Defaults to `${firstExt}.icns`/`${firstExt}.ico` (if several extensions specified, first is used) or to application icon.
-| role | *macOS-only* The app’s role with respect to the type. The value can be `Editor`, `Viewer`, `Shell`, or `None`. Defaults to `Editor`.
+| role | *macOS-only* The app’s role with respect to the type. The value can be `Editor`, `Viewer`, `Shell`, or `None`. Defaults to `Editor`. Corresponds to `CFBundleTypeRole`.
+| isPackage | *macOS-only* Whether the document is distributed as a bundle. If set to true, the bundle directory is treated as a file. Corresponds to `LSTypeIsPackage`.
### `.build.linux`
@@ -191,6 +207,7 @@ See [NSIS target notes](https://github.com/electron-userland/electron-builder/wi
| allowElevation | *boring installer only.* Allow requesting for elevation. If false, user will have to restart installer with elevated permissions. Defaults to `true`.
| runAfterFinish | *one-click installer only.* Run application after finish. Defaults to `true`.
| guid | See [GUID vs Application Name](https://github.com/electron-userland/electron-builder/wiki/NSIS#guid-vs-application-name).
+| installerIcon | The path to installer icon. Defaults to `build/installerIcon.ico` or application icon.
| installerHeader | *boring installer only.* `MUI_HEADERIMAGE`, relative to the project directory. Defaults to `build/installerHeader.bmp`
| installerHeaderIcon | *one-click installer only.* The path to header icon (above the progress bar), relative to the project directory. Defaults to `build/installerHeaderIcon.ico` or application icon.
| include | The path to NSIS include script to customize installer. Defaults to `build/installer.nsh`. See [Custom NSIS script](https://github.com/electron-userland/electron-builder/wiki/NSIS#custom-nsis-script).
@@ -270,14 +287,15 @@ Windows specific build options.
| Name | Description
| --- | ---
-| target | Target package type: list of `nsis`, `squirrel`, `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir`. Defaults to `nsis`.
-| signingHashAlgorithms | Array of signing algorithms used. Defaults to `['sha1', 'sha256']`
+| target |
Target package type: list of nsis, appx, squirrel, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir. Defaults to nsis.
AppX package can be built only on Windows 10.
+| signingHashAlgorithms |
Array of signing algorithms used. Defaults to ['sha1', 'sha256']
For AppX sha256 is always used.
| icon | The path to application icon. Defaults to `build/icon.ico` (consider using this convention instead of complicating your configuration).
| legalTrademarks | The trademarks and registered trademarks.
-| certificateFile | The path to the *.pfx certificate you want to sign with. Required only if you build on macOS and need different certificate than the one set in `CSC_LINK` - see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).
-| certificatePassword | The password to the certificate provided in `certificateFile`. Required only if you build on macOS and need to use a different password than the one set in `CSC_KEY_PASSWORD` - see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).
+| certificateFile |
The path to the *.pfx certificate you want to sign with. Please use it only if you cannot use env variable CSC_LINK (WIN_CSC_LINK) for some reason. Please see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).
+| certificatePassword |
The password to the certificate provided in certificateFile. Please use it only if you cannot use env variable CSC_KEY_PASSWORD (WIN_CSC_KEY_PASSWORD) for some reason. Please see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).
| certificateSubjectName | The name of the subject of the signing certificate. Required only for EV Code Signing and works only on Windows.
| rfc3161TimeStampServer | The URL of the RFC 3161 time stamp server. Defaults to `http://timestamp.comodoca.com/rfc3161`.
+| timeStampServer | The URL of the time stamp server. Defaults to `http://timestamp.verisign.com/scripts/timstamp.dll`.
## `.directories`
@@ -306,9 +324,8 @@ Development dependencies are never copied in any case. You don't need to ignore
[Multiple patterns](#multiple-glob-patterns) are supported. You can use `${os}` (expanded to mac, linux or win according to current platform) and `${arch}` in the pattern.
If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory.
-Remember that default pattern `**/*` is not added to your custom, so, you have to add it explicitly — e.g. `["**/*", "!ignoreMe${/*}"]`.
-
-`package.json` is added to your custom in any case.
+Remember that default pattern `**/*` **is not added to your custom** if some of your patterns is not ignore (i.e. not starts with `!`).
+ `package.json` is added to your custom in any case.
May be specified in the platform options (e.g. in the `build.mac`).
diff --git a/docs/Two package.json Structure.md b/docs/Two package.json Structure.md
index d8ab02e6e5f..b37996de8e9 100644
--- a/docs/Two package.json Structure.md
+++ b/docs/Two package.json Structure.md
@@ -6,7 +6,7 @@ Since version 8 electron-builder rebuilds only production dependencies, so, you
2. For your application (`./app/package.json`)
- The `package.json` resides in the `app` directory. Declare your application dependencies (`depencencies`) here. *Only this directory is distributed with the final, packaged application.*
+ The `package.json` resides in the `app` directory. Declare your application dependencies (`dependencies`) here. *Only this directory is distributed with the final, packaged application.*
Why?
@@ -15,4 +15,4 @@ Why?
Please see [Loading App Dependencies Manually](https://github.com/electron-userland/electron-builder/wiki/Loading-App-Dependencies-Manually) and [#379](https://github.com/electron-userland/electron-builder/issues/379#issuecomment-218503881).
-If you use the two-package.json project structure, you'll only have your `devDependencies` in your development `package.json` and your `dependencies` in your app `package.json`. To ensure your dependencies are always updated based on both files, simply add `"postinstall": "install-app-deps"` to your development `package.json`. This will basically automatically trigger an `npm install` within your app directory so you don't have to do this work every time you install/update your dependencies.
\ No newline at end of file
+If you use the two-package.json project structure, you'll only have your `devDependencies` in your development `package.json` and your `dependencies` in your app `package.json`. To ensure your dependencies are always updated based on both files, simply add `"postinstall": "install-app-deps"` to your development `package.json`. This will basically automatically trigger an `npm install` within your app directory so you don't have to do this work every time you install/update your dependencies.
diff --git a/docs/programmaticUsage.js b/docs/programmaticUsage.js
index d658d95d3ad..093d67a56f7 100644
--- a/docs/programmaticUsage.js
+++ b/docs/programmaticUsage.js
@@ -6,7 +6,7 @@ const builder = require("electron-builder")
builder.build({
platform: [builder.Platform.MAC],
"//": "platform, arch and other properties, see PackagerOptions in the node_modules/electron-builder/out/electron-builder.d.ts",
- devMetadata: {
+ config: {
"//": "build and other properties, see https://goo.gl/5jVxoO"
}
})
diff --git a/nsis-auto-updater/package.json b/nsis-auto-updater/package.json
index f365102bbc3..f2e78726186 100644
--- a/nsis-auto-updater/package.json
+++ b/nsis-auto-updater/package.json
@@ -1,6 +1,6 @@
{
"name": "electron-auto-updater",
- "version": "0.6.0",
+ "version": "0.7.2",
"description": "NSIS Auto Updater",
"main": "out/nsis-auto-updater/src/main.js",
"author": "Vladimir Krivosheev",
@@ -13,13 +13,11 @@
],
"dependencies": {
"bluebird-lst-c": "^1.0.5",
- "debug": "^2.3.2",
- "fs-extra-p": "^2.0.7",
- "ini": "^1.3.4",
+ "debug": "^2.4.5",
+ "fs-extra-p": "^3.0.3",
"js-yaml": "^3.7.0",
"semver": "^5.3.0",
- "source-map-support": "^0.4.6",
- "tunnel-agent": "^0.4.3"
+ "source-map-support": "^0.4.6"
},
"typings": "./out/electron-auto-updater.d.ts"
}
diff --git a/nsis-auto-updater/src/BintrayProvider.ts b/nsis-auto-updater/src/BintrayProvider.ts
index a3179ff0ca1..08a8083ce57 100644
--- a/nsis-auto-updater/src/BintrayProvider.ts
+++ b/nsis-auto-updater/src/BintrayProvider.ts
@@ -1,6 +1,5 @@
import { Provider, FileInfo } from "./api"
-import { BintrayClient } from "../../src/publish/bintray"
-import { HttpError } from "../../src/publish/restApiRequest"
+import { BintrayClient, HttpError } from "../../src/publish/bintray"
import { BintrayOptions, VersionInfo } from "../../src/options/publishOptions"
export class BintrayProvider implements Provider {
@@ -18,7 +17,7 @@ export class BintrayProvider implements Provider {
}
}
catch (e) {
- if (e instanceof HttpError && e.response.statusCode === 404) {
+ if ("response" in e && e.response.statusCode === 404) {
throw new Error(`No latest version, please ensure that user, package and repository correctly configured. Or at least one version is published. ${e.stack || e.message}`)
}
throw e
@@ -29,7 +28,7 @@ export class BintrayProvider implements Provider {
try {
const files = await this.client.getVersionFiles(versionInfo.version)
const suffix = `${versionInfo.version}.exe`
- for (let file of files) {
+ for (const file of files) {
if (file.name.endsWith(suffix) && file.name.includes("Setup")) {
return {
name: file.name,
diff --git a/nsis-auto-updater/src/GenericProvider.ts b/nsis-auto-updater/src/GenericProvider.ts
index 9f0d65a5b56..7e284925118 100644
--- a/nsis-auto-updater/src/GenericProvider.ts
+++ b/nsis-auto-updater/src/GenericProvider.ts
@@ -1,8 +1,8 @@
import { Provider, FileInfo } from "./api"
-import { HttpError, request } from "../../src/publish/restApiRequest"
import { GenericServerOptions, UpdateInfo } from "../../src/options/publishOptions"
import * as url from "url"
import * as path from "path"
+import { HttpError, request } from "../../src/util/httpExecutor"
export class GenericProvider implements Provider {
private readonly baseUrl = url.parse(this.configuration.url)
diff --git a/nsis-auto-updater/src/GitHubProvider.ts b/nsis-auto-updater/src/GitHubProvider.ts
index d4e9a105e24..1d3f0e52462 100644
--- a/nsis-auto-updater/src/GitHubProvider.ts
+++ b/nsis-auto-updater/src/GitHubProvider.ts
@@ -1,8 +1,8 @@
import { Provider, FileInfo } from "./api"
import { VersionInfo, GithubOptions, UpdateInfo } from "../../src/options/publishOptions"
-import { request, HttpError } from "../../src/publish/restApiRequest"
import { validateUpdateInfo } from "./GenericProvider"
import * as path from "path"
+import { HttpError, request } from "../../src/util/httpExecutor"
export class GitHubProvider implements Provider {
constructor(private readonly options: GithubOptions) {
diff --git a/nsis-auto-updater/src/NsisUpdater.ts b/nsis-auto-updater/src/NsisUpdater.ts
index f05354a13b7..51d4174d765 100644
--- a/nsis-auto-updater/src/NsisUpdater.ts
+++ b/nsis-auto-updater/src/NsisUpdater.ts
@@ -12,6 +12,8 @@ import { readFile } from "fs-extra-p"
import { safeLoad } from "js-yaml"
import { GenericProvider } from "./GenericProvider"
import { GitHubProvider } from "./GitHubProvider"
+import { executorHolder } from "../../src/util/httpExecutor"
+import { ElectronHttpExecutor } from "./electronHttpExecutor"
export class NsisUpdater extends EventEmitter {
private setupPath: string | null
@@ -21,6 +23,8 @@ export class NsisUpdater extends EventEmitter {
private clientPromise: Promise>
+ private readonly untilAppReady: Promise
+
private readonly app: any
private quitHandlerAdded = false
@@ -28,12 +32,25 @@ export class NsisUpdater extends EventEmitter {
constructor(options?: PublishConfiguration | BintrayOptions | GithubOptions) {
super()
- this.app = (global).__test_app || require("electron").app
-
- if (options == null) {
- this.clientPromise = this.loadUpdateConfig()
+ if ((global).__test_app) {
+ this.app = (global).__test_app
+ this.untilAppReady = BluebirdPromise.resolve()
}
else {
+ this.app = require("electron").app
+ executorHolder.httpExecutor = new ElectronHttpExecutor()
+ this.untilAppReady = new BluebirdPromise((resolve, reject) => {
+ if (this.app.isReady()) {
+ resolve()
+ }
+ else {
+ this.app.on("ready", resolve)
+ }
+ })
+ }
+
+
+ if (options != null) {
this.setFeedURL(options)
}
}
@@ -47,15 +64,12 @@ export class NsisUpdater extends EventEmitter {
}
async checkForUpdates(): Promise {
- if (this.clientPromise == null) {
- const message = "Update URL is not set"
- const error = new Error(message)
- this.emit("error", error, message)
- throw error
- }
-
+ await this.untilAppReady
this.emit("checking-for-update")
try {
+ if (this.clientPromise == null) {
+ this.clientPromise = NsisUpdater.loadUpdateConfig()
+ }
return await this.doCheckForUpdates()
}
catch (e) {
@@ -145,7 +159,11 @@ export class NsisUpdater extends EventEmitter {
// prevent calling several times
this.quitAndInstallCalled = true
- spawn(setupPath, isSilent ? ["/S"] : [], {
+ const args = ["--updated"]
+ if (isSilent) {
+ args.push("/S")
+ }
+ spawn(setupPath, args, {
detached: true,
stdio: "ignore",
}).unref()
@@ -153,14 +171,8 @@ export class NsisUpdater extends EventEmitter {
return true
}
- async loadUpdateConfig() {
- try {
- return createClient(safeLoad(await readFile(path.join((global).__test_resourcesPath || (process).resourcesPath, "app-update.yml"), "utf-8")))
- }
- catch (e) {
- this.emit("error", e, (e.stack || e).toString())
- throw e
- }
+ private static async loadUpdateConfig() {
+ return createClient(safeLoad(await readFile(path.join((global).__test_resourcesPath || (process).resourcesPath, "app-update.yml"), "utf-8")))
}
}
@@ -168,13 +180,16 @@ function createClient(data: string | PublishConfiguration | BintrayOptions | Git
if (typeof data === "string") {
throw new Error("Please pass PublishConfiguration object")
}
- else {
- const provider = (data).provider
- switch (provider) {
- case "github": return new GitHubProvider(data)
- case "generic": return new GenericProvider(data)
- case "bintray": return new BintrayProvider(data)
- default: throw new Error(`Unsupported provider: ${provider}`)
- }
+
+ const provider = (data).provider
+ switch (provider) {
+ case "github":
+ return new GitHubProvider(data)
+ case "generic":
+ return new GenericProvider(data)
+ case "bintray":
+ return new BintrayProvider(data)
+ default:
+ throw new Error(`Unsupported provider: ${provider}`)
}
}
\ No newline at end of file
diff --git a/nsis-auto-updater/src/electronHttpExecutor.ts b/nsis-auto-updater/src/electronHttpExecutor.ts
new file mode 100644
index 00000000000..22d0ff87f9a
--- /dev/null
+++ b/nsis-auto-updater/src/electronHttpExecutor.ts
@@ -0,0 +1,222 @@
+import { Socket } from "net"
+import { net } from "electron"
+import { createWriteStream, ensureDir } from "fs-extra-p"
+import BluebirdPromise from "bluebird-lst-c"
+import * as path from "path"
+import { HttpExecutor, DownloadOptions, HttpError, DigestTransform } from "../../src/util/httpExecutor"
+import { Url } from "url"
+import { safeLoad } from "js-yaml"
+import _debug from "debug"
+import Debugger = debug.Debugger
+import { parse as parseUrl } from "url"
+
+export class ElectronHttpExecutor implements HttpExecutor {
+ private readonly debug: Debugger = _debug("electron-builder")
+
+ private readonly maxRedirects = 10
+
+ request(url: Url, token: string | null = null, data: {[name: string]: any; } | null = null, method: string = "GET"): Promise {
+ const options: any = Object.assign({
+ method: method,
+ headers: {
+ "User-Agent": "electron-builder"
+ }
+ }, url)
+
+ if (url.hostname!!.includes("github") && !url.path!.endsWith(".yml")) {
+ options.headers.Accept = "application/vnd.github.v3+json"
+ }
+
+ const encodedData = data == null ? undefined : new Buffer(JSON.stringify(data))
+ if (encodedData != null) {
+ options.method = "post"
+ options.headers["Content-Type"] = "application/json"
+ options.headers["Content-Length"] = encodedData.length
+ }
+ return this.doApiRequest(options, token, it => it.end(encodedData))
+ }
+
+ download(url: string, destination: string, options?: DownloadOptions | null): Promise {
+ return new BluebirdPromise( (resolve, reject) => {
+ this.doDownload(url, destination, 0, options || {}, (error: Error) => {
+ if (error == null) {
+ resolve(destination)
+ }
+ else {
+ reject(error)
+ }
+ })
+ })
+ }
+
+ private addTimeOutHandler(request: Electron.ClientRequest, callback: (error: Error) => void) {
+ request.on("socket", function (socket: Socket) {
+ socket.setTimeout(60 * 1000, () => {
+ callback(new Error("Request timed out"))
+ request.abort()
+ })
+ })
+ }
+
+ private doDownload(url: string, destination: string, redirectCount: number, options: DownloadOptions, callback: (error: Error | null) => void) {
+ const ensureDirPromise = options.skipDirCreation ? BluebirdPromise.resolve() : ensureDir(path.dirname(destination))
+
+ const parsedUrl = parseUrl(url)
+ // user-agent must be specified, otherwise some host can return 401 unauthorised
+
+ //FIXME hack, the electron typings specifies Protocol with capital but the code actually uses with small case
+ const requestOpts = {
+ protocol: parsedUrl.protocol,
+ hostname: parsedUrl.hostname,
+ path: parsedUrl.path,
+ headers: {
+ "User-Agent": "electron-builder"
+ },
+ }
+
+ const request = net.request(requestOpts, (response: Electron.IncomingMessage) => {
+ if (response.statusCode >= 400) {
+ callback(new Error(`Cannot download "${url}", status ${response.statusCode}: ${response.statusMessage}`))
+ return
+ }
+
+ const redirectUrl = this.safeGetHeader(response, "location")
+ if (redirectUrl != null) {
+ if (redirectCount < this.maxRedirects) {
+ this.doDownload(redirectUrl, destination, redirectCount++, options, callback)
+ }
+ else {
+ callback(new Error("Too many redirects (> " + this.maxRedirects + ")"))
+ }
+ return
+ }
+
+ const sha2Header = this.safeGetHeader(response, "X-Checksum-Sha2")
+ if (sha2Header != null && options.sha2 != null) {
+ // todo why bintray doesn't send this header always
+ if (sha2Header == null) {
+ throw new Error("checksum is required, but server response doesn't contain X-Checksum-Sha2 header")
+ }
+ else if (sha2Header !== options.sha2) {
+ throw new Error(`checksum mismatch: expected ${options.sha2} but got ${sha2Header} (X-Checksum-Sha2 header)`)
+ }
+ }
+
+ ensureDirPromise
+ .then(() => {
+ const fileOut = createWriteStream(destination)
+ if (options.sha2 == null) {
+ response.pipe(fileOut)
+ }
+ else {
+ response
+ .pipe(new DigestTransform(options.sha2))
+ .pipe(fileOut)
+ }
+
+ fileOut.on("finish", () => (fileOut.close)(callback))
+ })
+ .catch(callback)
+
+ let ended = false
+ response.on("end", () => {
+ ended = true
+ })
+
+ response.on("close", () => {
+ if (!ended) {
+ callback(new Error("Request aborted"))
+ }
+ })
+ })
+ this.addTimeOutHandler(request, callback)
+ request.on("error", callback)
+ request.end()
+ }
+
+ private safeGetHeader(response: Electron.IncomingMessage, headerKey: string) {
+ return response.headers[headerKey] ? response.headers[headerKey].pop() : null
+ }
+
+
+ doApiRequest(options: Electron.RequestOptions, token: string | null, requestProcessor: (request: Electron.ClientRequest, reject: (error: Error) => void) => void, redirectCount: number = 0): Promise {
+ const requestOptions: any = options
+ this.debug(`HTTPS request: ${JSON.stringify(requestOptions, null, 2)}`)
+
+ if (token != null) {
+ (requestOptions.headers).authorization = token.startsWith("Basic") ? token : `token ${token}`
+ }
+
+ requestOptions.protocol = "https:"
+ return new BluebirdPromise((resolve, reject, onCancel) => {
+ const request = net.request(options, (response: Electron.IncomingMessage) => {
+ try {
+ if (response.statusCode === 404) {
+ // error is clear, we don't need to read detailed error description
+ reject(new HttpError(response, `method: ${options.method} url: https://${options.hostname}${options.path}
+
+Please double check that your authentication token is correct. Due to security reasons actual status maybe not reported, but 404.
+`))
+ }
+ else if (response.statusCode === 204) {
+ // on DELETE request
+ resolve()
+ return
+ }
+
+ const redirectUrl = this.safeGetHeader(response, "location")
+ if (redirectUrl != null) {
+ if (redirectCount > 10) {
+ reject(new Error("Too many redirects (> 10)"))
+ return
+ }
+
+ if (options.path!.endsWith("/latest")) {
+ resolve({location: redirectUrl})
+ }
+ else {
+ this.doApiRequest(Object.assign({}, options, parseUrl(redirectUrl)), token, requestProcessor)
+ .then(resolve)
+ .catch(reject)
+ }
+ return
+ }
+
+ let data = ""
+ response.setEncoding("utf8")
+ response.on("data", (chunk: string) => {
+ data += chunk
+ })
+
+ response.on("end", () => {
+ try {
+ const contentType = response.headers["content-type"]
+ const isJson = contentType != null && contentType.includes("json")
+ if (response.statusCode >= 400) {
+ if (isJson) {
+ reject(new HttpError(response, JSON.parse(data)))
+ }
+ else {
+ reject(new HttpError(response))
+ }
+ }
+ else {
+ resolve(data.length === 0 ? null : (isJson || !options.path!.includes(".yml")) ? JSON.parse(data) : safeLoad(data))
+ }
+ }
+ catch (e) {
+ reject(e)
+ }
+ })
+ }
+ catch (e) {
+ reject(e)
+ }
+ })
+ this.addTimeOutHandler(request, reject)
+ request.on("error", reject)
+ requestProcessor(request, reject)
+ onCancel!(() => request.abort())
+ })
+ }
+}
\ No newline at end of file
diff --git a/nsis-auto-updater/tsconfig.json b/nsis-auto-updater/tsconfig.json
index ab901c69e6b..2022ac98c0c 100755
--- a/nsis-auto-updater/tsconfig.json
+++ b/nsis-auto-updater/tsconfig.json
@@ -24,11 +24,7 @@
"../typings/debug.d.ts",
"../node_modules/@types/node/index.d.ts",
"../node_modules/fs-extra-p/index.d.ts",
- "../node_modules/bluebird-lst-c/index.d.ts",
- "../src/util/httpRequest.ts",
- "../src/publish/restApiRequest.ts",
- "../src/publish/restApiRequest.ts",
- "../src/publish/bintray.ts"
+ "../node_modules/bluebird-lst-c/index.d.ts"
],
"include": [
"src/**/*.ts"
diff --git a/package.json b/package.json
index bee4294bdf0..77932296275 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"update-wiki": "git subtree split -b wiki --prefix docs/ && git push -f wiki wiki:master",
"whitespace": "whitespace 'src/**/*.ts'",
"docker-images": "docker/build.sh",
- "precommit": "validate-commit-msg"
+ "test-deps-mac": "brew install rpm dpkg mono lzip gnu-tar graphicsmagick xz && brew install wine --without-x11"
},
"repository": "electron-userland/electron-builder",
"engines": {
@@ -49,7 +49,8 @@
"Windows",
"OS X",
"MacOS",
- "Mac"
+ "Mac",
+ "appx"
],
"author": "Stefan Judis",
"license": "MIT",
@@ -58,26 +59,26 @@
"dependencies": {
"7zip-bin": "^2.0.4",
"ansi-escapes": "^1.4.0",
- "archiver": "^1.2.0",
- "archiver-utils": "^1.3.0",
+ "archiver": "^1.3.0",
"asar-electron-builder": "^0.13.5",
"bluebird-lst-c": "^1.0.5",
"chalk": "^1.1.3",
"chromium-pickle-js": "^0.2.0",
"cli-cursor": "^1.0.2",
"cuint": "^0.2.2",
- "debug": "^2.3.3",
+ "debug": "2.5.1",
"electron-download-tf": "3.1.0",
"electron-macos-sign": "^1.3.4",
- "fs-extra-p": "^2.0.7",
+ "fs-extra-p": "^3.0.3",
"hosted-git-info": "^2.1.5",
"ini": "^1.3.4",
+ "is-ci": "^1.0.10",
"isbinaryfile": "^3.0.1",
"js-yaml": "^3.7.0",
"lodash.template": "^4.4.0",
"mime": "^1.3.4",
"minimatch": "^3.0.3",
- "node-emoji": "^1.4.1",
+ "node-emoji": "^1.4.3",
"normalize-package-data": "^2.3.5",
"parse-color": "^1.0.0",
"plist": "^2.0.1",
@@ -88,59 +89,35 @@
"sanitize-filename": "^1.6.1",
"semver": "^5.3.0",
"source-map-support": "^0.4.6",
+ "stat-mode": "^0.2.2",
"tunnel-agent": "^0.4.3",
- "update-notifier": "^1.0.2",
+ "update-notifier": "^1.0.3",
"uuid-1345": "^0.99.6",
- "yargs": "^6.4.0"
+ "yargs": "^6.5.0"
},
"devDependencies": {
- "@develar/semantic-release": "^6.3.21",
+ "@develar/semantic-release": "^6.3.26",
+ "@types/electron": "^1.4.30",
"@types/ini": "^1.3.29",
- "@types/jest": "^16.0.0",
- "@types/js-yaml": "^3.5.28",
+ "@types/jest": "^16.0.2",
+ "@types/js-yaml": "^3.5.29",
"@types/source-map-support": "^0.2.28",
"babel-plugin-array-includes": "^2.0.3",
"babel-plugin-transform-async-to-module-method": "^6.16.0",
"babel-plugin-transform-es2015-destructuring": "^6.19.0",
- "babel-plugin-transform-es2015-parameters": "^6.18.0",
+ "babel-plugin-transform-es2015-parameters": "^6.21.0",
"babel-plugin-transform-es2015-spread": "^6.8.0",
"babel-plugin-transform-inline-imports-commonjs": "^1.2.0",
"decompress-zip": "^0.3.0",
- "depcheck": "^0.6.5",
- "diff": "^3.0.1",
- "husky": "^0.11.9",
- "jest-cli": "^17.0.3",
- "json8": "^0.9.2",
+ "depcheck": "^0.6.7",
+ "jest-cli": "^18.0.0",
+ "jest-environment-node-debug": "^0.0.2",
"path-sort": "^0.1.0",
- "ts-babel": "^1.1.4",
- "tslint": "4.0.0-dev.1",
- "typescript": "^2.1.1",
- "validate-commit-msg": "^2.8.2",
+ "ts-babel": "^1.2.2",
+ "tslint": "^4.1.1",
+ "typescript": "^2.1.4",
"whitespace": "^2.1.0"
},
- "babel": {
- "plugins": [
- [
- "transform-async-to-module-method",
- {
- "module": "bluebird-lst-c",
- "method": "coroutine"
- }
- ],
- "transform-es2015-parameters",
- "transform-es2015-spread",
- "transform-es2015-destructuring",
- "array-includes",
- [
- "transform-inline-imports-commonjs",
- {
- "excludeModules": [
- "path"
- ]
- }
- ]
- ]
- },
"jest": {
"testEnvironment": "node",
"testPathDirs": [
@@ -153,10 +130,7 @@
"modulePaths": [
""
],
- "setupTestFrameworkScriptFile": "test/jestSetup.js"
- },
- "release": {
- "verifyConditions": []
+ "setupTestFrameworkScriptFile": "/test/jestSetup.js"
},
"typings": "./out/electron-builder.d.ts",
"publishConfig": {
diff --git a/src/appInfo.ts b/src/appInfo.ts
index 8b9a686718f..7db028c4d55 100644
--- a/src/appInfo.ts
+++ b/src/appInfo.ts
@@ -1,4 +1,4 @@
-import { DevMetadata, AppMetadata } from "./metadata"
+import { DevMetadata, AppMetadata, BuildMetadata } from "./metadata"
import { warn } from "./util/log"
import { smarten } from "./platformPackager"
import { isEmptyOrSpaces } from "./util/util"
@@ -15,10 +15,14 @@ export class AppInfo {
readonly productName: string
readonly productFilename: string
+ private get config(): BuildMetadata {
+ return this.devMetadata.build
+ }
+
constructor(public metadata: AppMetadata, private devMetadata: DevMetadata, buildVersion?: string | null) {
this.version = metadata.version!
- this.buildNumber = (this.devMetadata.build)["build-version"] || process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM || process.env.BUILD_NUMBER
+ this.buildNumber = (this.config)["build-version"] || process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM || process.env.BUILD_NUMBER
if (isEmptyOrSpaces(buildVersion)) {
buildVersion = this.version
@@ -31,7 +35,7 @@ export class AppInfo {
this.buildVersion = buildVersion!
}
- this.productName = getProductName(this.metadata, this.devMetadata)
+ this.productName = this.config.productName || metadata.productName || metadata.name
this.productFilename = sanitizeFileName(this.productName)
}
@@ -45,13 +49,13 @@ export class AppInfo {
}
get id(): string {
- let appId = this.devMetadata.build["app-bundle-id"]
+ let appId = this.config["app-bundle-id"]
if (appId != null) {
warn("app-bundle-id is deprecated, please use appId")
}
- if (this.devMetadata.build.appId != null) {
- appId = this.devMetadata.build.appId
+ if (this.config.appId != null) {
+ appId = this.config.appId
}
const generateDefaultAppId = () => {
@@ -72,7 +76,7 @@ export class AppInfo {
}
get copyright(): string {
- const copyright = this.devMetadata.build.copyright
+ const copyright = this.config.copyright
if (copyright != null) {
return copyright
}
@@ -80,19 +84,12 @@ export class AppInfo {
}
async computePackageUrl(): Promise {
- const url = this.metadata.homepage || this.devMetadata.homepage
+ const url = this.metadata.homepage
if (url != null) {
return url
}
const info = await getRepositoryInfo(this.metadata, this.devMetadata)
- if (info != null) {
- return `https://github.com/${info.user}/${info.project}`
- }
- return null
+ return info == null ? null : `https://github.com/${info.user}/${info.project}`
}
-}
-
-function getProductName(metadata: AppMetadata, devMetadata: DevMetadata) {
- return devMetadata.build.productName || metadata.productName || metadata.name
}
\ No newline at end of file
diff --git a/src/asarUtil.ts b/src/asarUtil.ts
index 7b4ad9dd677..5d12dbfd0e2 100644
--- a/src/asarUtil.ts
+++ b/src/asarUtil.ts
@@ -1,80 +1,24 @@
import { AsarFileInfo, listPackage, statFile, AsarOptions } from "asar-electron-builder"
-import { statOrNull, debug } from "./util/util"
-import {
- lstat, readdir, readFile, Stats, createWriteStream, ensureDir, createReadStream, readJson,
- writeFile, realpath
-} from "fs-extra-p"
+import { debug } from "./util/util"
+import { readFile, Stats, createWriteStream, ensureDir, createReadStream, readJson, writeFile, realpath } from "fs-extra-p"
import BluebirdPromise from "bluebird-lst-c"
import * as path from "path"
import { log } from "./util/log"
-import { Minimatch } from "minimatch"
import { deepAssign } from "./util/deepAssign"
-import { Filter } from "./util/filter"
+import { walk, statOrNull, CONCURRENCY, MAX_FILE_REQUESTS, Filter, FileCopier } from "./util/fs"
const isBinaryFile: any = BluebirdPromise.promisify(require("isbinaryfile"))
const pickle = require ("chromium-pickle-js")
const Filesystem = require("asar-electron-builder/lib/filesystem")
const UINT64 = require("cuint").UINT64
-const MAX_FILE_REQUESTS = 8
-const concurrency = {concurrency: MAX_FILE_REQUESTS}
const NODE_MODULES_PATTERN = path.sep + "node_modules" + path.sep
-export async function walk(initialDirPath: string, consumer?: (file: string, stat: Stats) => void, filter?: Filter): Promise> {
- const result: Array = []
- const queue: Array = [initialDirPath]
- let addDirToResult = false
- while (queue.length > 0) {
- const dirPath = queue.pop()!
- if (addDirToResult) {
- result.push(dirPath)
- }
- else {
- addDirToResult = true
- }
-
- const childNames = await readdir(dirPath)
- childNames.sort()
-
- const dirs: Array = []
- await BluebirdPromise.map(childNames, name => {
- const filePath = dirPath + path.sep + name
- return lstat(filePath)
- .then(stat => {
- if (filter != null && !filter(filePath, stat)) {
- return
- }
-
- if (consumer != null) {
- consumer(filePath, stat)
- }
-
- if (stat.isDirectory()) {
- dirs.push(filePath)
- }
- else {
- result.push(filePath)
- }
- })
- }, concurrency)
-
- for (let i = dirs.length - 1; i > -1; i--) {
- queue.push(dirs[i])
- }
- }
-
- return result
-}
-
export async function createAsarArchive(src: string, resourcesPath: string, options: AsarOptions, filter: Filter, unpackPattern: Filter | null): Promise {
// sort files to minimize file change (i.e. asar file is not changed dramatically on small change)
await new AsarPackager(src, resourcesPath, options, unpackPattern).pack(filter)
}
-function isUnpackDir(path: string, pattern: Minimatch, rawPattern: string): boolean {
- return path.startsWith(rawPattern) || pattern.match(path)
-}
-
function addValue(map: Map>, key: string, value: string) {
let list = map.get(key)
if (list == null) {
@@ -86,6 +30,24 @@ function addValue(map: Map>, key: string, value: string) {
}
}
+interface UnpackedFileTask {
+ stats: Stats
+ src?: string
+ data?: string
+ destination: string
+}
+
+function writeUnpackedFiles(filesToUnpack: Array, fileCopier: FileCopier): Promise {
+ return BluebirdPromise.map(filesToUnpack, it => {
+ if (it.data == null) {
+ return fileCopier.copy(it.src!, it.destination, it.stats)
+ }
+ else {
+ return writeFile(it.destination, it.data)
+ }
+ })
+}
+
class AsarPackager {
private readonly toPack: Array = []
private readonly fs = new Filesystem(this.src)
@@ -94,16 +56,13 @@ class AsarPackager {
private srcRealPath: Promise
- constructor(private readonly src: string, private readonly resourcesPath: string, private readonly options: AsarOptions, private readonly unpackPattern: Filter | null) {
- this.outFile = path.join(this.resourcesPath, "app.asar")
+ constructor(private readonly src: string, destination: string, private readonly options: AsarOptions, private readonly unpackPattern: Filter | null) {
+ this.outFile = path.join(destination, "app.asar")
}
async pack(filter: Filter) {
const metadata = new Map()
- const files = await walk(this.src, (it, stat) => {
- metadata.set(it, stat)
- }, filter)
-
+ const files = await walk(this.src, filter, (it, stat) => metadata.set(it, stat))
await this.createPackageFromFiles(this.options.ordering == null ? files : await this.order(files), metadata)
await this.writeAsarFile()
}
@@ -119,6 +78,7 @@ class AsarPackager {
const packageJsonStringLength = "package.json".length
const dirToCreate = new Map>()
+ /* tslint:disable:rule1 prefer-const */
for (let i = 0, n = files.length; i < n; i++) {
const file = files[i]
const index = file.lastIndexOf(NODE_MODULES_PATTERN)
@@ -138,7 +98,7 @@ class AsarPackager {
const nodeModuleDir = file.substring(0, nextSlashIndex)
if (file.length === (nodeModuleDir.length + 1 + packageJsonStringLength) && file.endsWith("package.json")) {
- fileIndexToModulePackageData.set(i, readJson(file).then(it => cleanupPackageJson(it)))
+ fileIndexToModulePackageData.set(i, >readJson(file).then(it => cleanupPackageJson(it)))
}
if (autoUnpackDirs.has(nodeModuleDir)) {
@@ -187,39 +147,31 @@ class AsarPackager {
const base = path.join(unpackedDest, it)
await ensureDir(base)
await BluebirdPromise.each(dirToCreate.get(it)!, it => ensureDir(path.join(base, it)))
- }, concurrency)
+ }, CONCURRENCY)
}
}
async createPackageFromFiles(files: Array, metadata: Map) {
// search auto unpacked dir
- const autoUnpackDirs = new Set()
+ const unpackedDirs = new Set()
const unpackedDest = `${this.outFile}.unpacked`
const fileIndexToModulePackageData = new Map>()
+ await ensureDir(path.dirname(this.outFile))
+
if (this.options.smartUnpack !== false) {
- await this.detectUnpackedDirs(files, metadata, autoUnpackDirs, unpackedDest, fileIndexToModulePackageData)
+ await this.detectUnpackedDirs(files, metadata, unpackedDirs, unpackedDest, fileIndexToModulePackageData)
}
- const unpackDir = this.options.unpackDir == null ? null : new Minimatch(this.options.unpackDir)
- const unpack = this.options.unpack == null ? null : new Minimatch(this.options.unpack, {
- matchBase: true
- })
-
- const createDirPromises: Array> = [ensureDir(path.dirname(this.outFile))]
- const copyPromises: Array> = []
+ const filesToUnpack: Array = []
const mainPackageJson = path.join(this.src, "package.json")
+ const fileCopier = new FileCopier()
+ /* tslint:disable:rule1 prefer-const */
for (let i = 0, n = files.length; i < n; i++) {
const file = files[i]
const stat = metadata.get(file)!
if (stat.isFile()) {
const fileParent = path.dirname(file)
const dirNode = this.fs.searchNodeFromPath(fileParent)
-
- if (dirNode.unpacked && createDirPromises.length > 0) {
- await BluebirdPromise.all(createDirPromises)
- createDirPromises.length = 0
- }
-
const packageDataPromise = fileIndexToModulePackageData.get(i)
let newData: any | null = null
if (packageDataPromise == null) {
@@ -234,26 +186,19 @@ class AsarPackager {
const fileSize = newData == null ? stat.size : Buffer.byteLength(newData)
const node = this.fs.searchNodeFromPath(file)
node.size = fileSize
- if (dirNode.unpacked || (this.unpackPattern != null && this.unpackPattern(file, stat)) || (unpack != null && unpack.match(file))) {
+ if (dirNode.unpacked || (this.unpackPattern != null && this.unpackPattern(file, stat))) {
node.unpacked = true
- if (!dirNode.unpacked) {
- const promise = ensureDir(path.join(unpackedDest, path.relative(this.src, fileParent)))
- if (createDirPromises.length === 0) {
- await createDirPromises
- }
- else {
- createDirPromises.push(promise)
- await BluebirdPromise.all(createDirPromises)
- createDirPromises.length = 0
- }
+ if (!dirNode.unpacked && !unpackedDirs.has(fileParent)) {
+ unpackedDirs.add(fileParent)
+ await ensureDir(fileParent.replace(this.src, unpackedDest))
}
- const unpackedFile = path.join(unpackedDest, path.relative(this.src, file))
- copyPromises.push(newData == null ? copyFile(file, unpackedFile, stat) : writeFile(unpackedFile, newData))
- if (copyPromises.length > MAX_FILE_REQUESTS) {
- await BluebirdPromise.all(copyPromises)
- copyPromises.length = 0
+ const unpackedFile = file.replace(this.src, unpackedDest)
+ filesToUnpack.push(newData == null ? {src: file, destination: unpackedFile, stats: stat} : {destination: unpackedFile, data: newData, stats: stat})
+ if (filesToUnpack.length > MAX_FILE_REQUESTS) {
+ await writeUnpackedFiles(filesToUnpack, fileCopier)
+ filesToUnpack.length = 0
}
}
else {
@@ -276,24 +221,18 @@ class AsarPackager {
}
else if (stat.isDirectory()) {
let unpacked = false
- if (autoUnpackDirs.has(file)) {
+ if (unpackedDirs.has(file)) {
unpacked = true
}
else {
- unpacked = unpackDir != null && isUnpackDir(path.relative(this.src, file), unpackDir, this.options.unpackDir!)
- if (unpacked) {
- createDirPromises.push(ensureDir(path.join(unpackedDest, path.relative(this.src, file))))
- }
- else {
- for (let d of autoUnpackDirs) {
- if (file.length > (d.length + 2) && file[d.length] === path.sep && file.startsWith(d)) {
- unpacked = true
- autoUnpackDirs.add(file)
- // not all dirs marked as unpacked after first iteration - because node module dir can be marked as unpacked after processing node module dir content
- // e.g. node-notifier/example/advanced.js processed, but only on process vendor/terminal-notifier.app module will be marked as unpacked
- createDirPromises.push(ensureDir(path.join(unpackedDest, path.relative(this.src, file))))
- break
- }
+ for (const dir of unpackedDirs) {
+ if (file.length > (dir.length + 2) && file[dir.length] === path.sep && file.startsWith(dir)) {
+ unpacked = true
+ unpackedDirs.add(file)
+ // not all dirs marked as unpacked after first iteration - because node module dir can be marked as unpacked after processing node module dir content
+ // e.g. node-notifier/example/advanced.js processed, but only on process vendor/terminal-notifier.app module will be marked as unpacked
+ await ensureDir(file.replace(this.src, unpackedDest))
+ break
}
}
}
@@ -304,8 +243,8 @@ class AsarPackager {
}
}
- if (copyPromises.length > 0) {
- await BluebirdPromise.all(copyPromises)
+ if (filesToUnpack.length > 0) {
+ await writeUnpackedFiles(filesToUnpack, fileCopier)
}
}
@@ -362,7 +301,7 @@ class AsarPackager {
})
}
- async order(filenames: Array) {
+ private async order(filenames: Array) {
const orderingFiles = (await readFile(this.options.ordering!, "utf8")).split("\n").map(line => {
if (line.indexOf(":") !== -1) {
line = line.split(":").pop()!
@@ -375,10 +314,10 @@ class AsarPackager {
})
const ordering: Array = []
- for (let file of orderingFiles) {
- let pathComponents = file.split(path.sep)
+ for (const file of orderingFiles) {
+ const pathComponents = file.split(path.sep)
let str = this.src
- for (let pathComponent of pathComponents) {
+ for (const pathComponent of pathComponents) {
str = path.join(str, pathComponent)
ordering.push(str)
}
@@ -387,12 +326,12 @@ class AsarPackager {
const filenamesSorted: Array = []
let missing = 0
const total = filenames.length
- for (let file of ordering) {
+ for (const file of ordering) {
if (!filenamesSorted.includes(file) && filenames.includes(file)) {
filenamesSorted.push(file)
}
}
- for (let file of filenames) {
+ for (const file of filenames) {
if (!filenamesSorted.includes(file)) {
filenamesSorted.push(file)
missing += 1
@@ -406,7 +345,7 @@ class AsarPackager {
function cleanupPackageJson(data: any): any {
try {
let changed = false
- for (let prop of Object.getOwnPropertyNames(data)) {
+ for (const prop of Object.getOwnPropertyNames(data)) {
if (prop[0] === "_" || prop === "dist" || prop === "gitHead" || prop === "keywords") {
delete data[prop]
changed = true
@@ -457,19 +396,3 @@ export async function checkFileInArchive(asarFile: string, relativeFile: string,
throw error(`is corrupted: size 0`)
}
}
-
-function copyFile(src: string, dest: string, stats: Stats) {
- return new BluebirdPromise(function (resolve, reject) {
- const readStream = createReadStream(src)
- const writeStream = createWriteStream(dest, {mode: stats.mode})
-
- readStream.on("error", reject)
- writeStream.on("error", reject)
-
- writeStream.on("open", function () {
- readStream.pipe(writeStream)
- })
-
- writeStream.once("finish", resolve)
- })
-}
\ No newline at end of file
diff --git a/src/builder.ts b/src/builder.ts
index 0f0f0c4ea17..e63c2b23938 100644
--- a/src/builder.ts
+++ b/src/builder.ts
@@ -4,12 +4,13 @@ import { PublishOptions, Publisher } from "./publish/publisher"
import { GitHubPublisher } from "./publish/gitHubPublisher"
import { executeFinally } from "./util/promise"
import BluebirdPromise from "bluebird-lst-c"
-import { isEmptyOrSpaces, isCi, debug } from "./util/util"
+import { isEmptyOrSpaces, debug } from "./util/util"
import { log } from "./util/log"
import { Platform, Arch, archFromString } from "./metadata"
import { DIR_TARGET } from "./targets/targetFactory"
import { BintrayPublisher } from "./publish/BintrayPublisher"
import { PublishConfiguration, GithubOptions, BintrayOptions } from "./options/publishOptions"
+import isCi from "is-ci"
export interface BuildOptions extends PackagerOptions, PublishOptions {
}
@@ -82,14 +83,14 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
archToType.set(Arch.x64, defaultTargetValue)
}
else {
- for (let arch of commonArch()) {
+ for (const arch of commonArch()) {
archToType.set(arch, defaultTargetValue)
}
}
return
}
- for (let type of types) {
+ for (const type of types) {
let arch: string
if (platform === Platform.MAC) {
arch = "x64"
@@ -101,7 +102,7 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
addValue(archToType, archFromString(type.substring(suffixPos + 1)), type.substring(0, suffixPos))
}
else {
- for (let arch of commonArch()) {
+ for (const arch of commonArch()) {
addValue(archToType, arch, type)
}
}
@@ -162,12 +163,12 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
export function createTargets(platforms: Array, type?: string | null, arch?: string | null): Map>> {
const targets = new Map>>()
- for (let platform of platforms) {
+ for (const platform of platforms) {
const archs = platform === Platform.MAC ? [Arch.x64] : (arch === "all" ? [Arch.x64, Arch.ia32] : [archFromString(arch == null ? process.arch : arch)])
const archToType = new Map>()
targets.set(platform, archToType)
- for (let arch of archs) {
+ for (const arch of archs) {
archToType.set(arch, type == null ? [] : [type])
}
}
@@ -209,7 +210,7 @@ export async function build(rawOptions?: CliOptions): Promise> {
options.publish = "onTag"
isPublishOptionGuessed = true
}
- else if (isCi()) {
+ else if (isCi) {
log("CI detected, so artifacts will be published if draft release exists")
options.publish = "onTagOrDraft"
isPublishOptionGuessed = true
@@ -225,7 +226,7 @@ export async function build(rawOptions?: CliOptions): Promise> {
if (isAuthTokenSet()) {
publishManager(packager, publishTasks, options, isPublishOptionGuessed)
}
- else if (isCi()) {
+ else if (isCi) {
log(`CI detected, publish is set to ${options.publish}, but neither GH_TOKEN nor BT_TOKEN is not set, so artifacts will be not published`)
}
}
@@ -239,7 +240,7 @@ export async function build(rawOptions?: CliOptions): Promise> {
return await executeFinally(packager.build().then(() => artifactPaths), errorOccurred => {
if (errorOccurred) {
- for (let task of publishTasks) {
+ for (const task of publishTasks) {
task!.cancel()
}
return BluebirdPromise.resolve(null)
@@ -274,7 +275,7 @@ function publishManager(packager: Packager, publishTasks: Array {
const notifier = updateNotifier({pkg: it})
diff --git a/src/cli/create-self-signed-cert.ts b/src/cli/create-self-signed-cert.ts
index cc263358828..4cf33fc69fe 100644
--- a/src/cli/create-self-signed-cert.ts
+++ b/src/cli/create-self-signed-cert.ts
@@ -1,11 +1,12 @@
import yargs from "yargs"
import { printErrorAndExit } from "../util/promise"
-import { exec, spawn, unlinkIfExists } from "../util/util"
+import { exec, spawn } from "../util/util"
import { getSignVendorPath } from "../windowsCodeSign"
import * as path from "path"
import sanitizeFileName from "sanitize-filename"
import { log } from "../util/log"
import { TmpDir } from "../util/tmp"
+import { unlinkIfExists } from "../util/fs"
async function main() {
const args: any = yargs
@@ -15,7 +16,7 @@ async function main() {
const tmpDir = new TmpDir()
const targetDir = process.cwd()
- const tempPrefix = path.join(await tmpDir.getTempFile(null), sanitizeFileName(args.publisher))
+ const tempPrefix = path.join(await tmpDir.getTempFile(""), sanitizeFileName(args.publisher))
const cer = `${tempPrefix}.cer`
const pvk = `${tempPrefix}.pvk`
@@ -31,7 +32,7 @@ async function main() {
log(`${pfx} created. Please see https://github.com/electron-userland/electron-builder/wiki/Code-Signing how do use it to sign.`)
const certLocation = "Cert:\\LocalMachine\\TrustedPeople"
- log(`${pfx} will be imported into ${certLocation} Operation will be succeded only if runned from root. Otherwise import file manually.`)
+ log(`${pfx} will be imported into ${certLocation} Operation will be succeed only if runned from root. Otherwise import file manually.`)
await spawn("powershell.exe", ["Import-PfxCertificate", "-FilePath", `"${pfx}"`, "-CertStoreLocation", ""])
tmpDir.cleanup()
}
diff --git a/src/cli/install-app-deps.ts b/src/cli/install-app-deps.ts
index 9e8c08d0c8c..79353635a76 100644
--- a/src/cli/install-app-deps.ts
+++ b/src/cli/install-app-deps.ts
@@ -1,5 +1,5 @@
#! /usr/bin/env node
-import { computeDefaultAppDirectory, getElectronVersion, use } from "../util/util"
+import { computeDefaultAppDirectory, getElectronVersion, use, getDirectoriesConfig } from "../util/util"
import { printErrorAndExit } from "../util/promise"
import * as path from "path"
import BluebirdPromise from "bluebird-lst-c"
@@ -10,21 +10,27 @@ import { installOrRebuild } from "../yarn"
async function main() {
const args: any = yargs
+ .option("platform", {
+ choices: ["linux", "darwin", "win32"],
+ default: process.platform,
+ })
.option("arch", {
choices: ["ia32", "x64", "all"],
- }).argv
+ default: process.arch,
+ })
+ .argv
const projectDir = process.cwd()
const devPackageFile = path.join(projectDir, "package.json")
const devMetadata: DevMetadata = await readPackageJson(devPackageFile)
const results: Array = await BluebirdPromise.all([
- computeDefaultAppDirectory(projectDir, use(devMetadata.directories, it => it!.app)),
+ computeDefaultAppDirectory(projectDir, use(getDirectoriesConfig(devMetadata), it => it!.app)),
getElectronVersion(devMetadata, devPackageFile)
])
// if two package.json — force full install (user wants to install/update app deps in addition to dev)
- await installOrRebuild(devMetadata.build, results[0], results[1], args.arch, process.platform, results[0] !== projectDir)
+ await installOrRebuild(devMetadata.build, results[0], results[1], args.platform, args.arch, results[0] !== projectDir)
}
main()
diff --git a/src/cli/node-gyp-rebuild.ts b/src/cli/node-gyp-rebuild.ts
index 97b85465bfc..1a91423e455 100644
--- a/src/cli/node-gyp-rebuild.ts
+++ b/src/cli/node-gyp-rebuild.ts
@@ -8,18 +8,22 @@ import { log } from "../util/log"
import { getGypEnv } from "../yarn"
const args: any = yargs
+ .option("platform", {
+ choices: ["linux", "darwin", "win32"],
+ default: process.platform,
+ })
.option("arch", {
choices: ["ia32", "x64", "armv7l"],
+ default: process.arch,
}).argv
const projectDir = process.cwd()
const devPackageFile = path.join(projectDir, "package.json")
async function main() {
- const arch = args.arch || process.arch
- log(`Execute node-gyp rebuild for arch ${arch}`)
+ log(`Execute node-gyp rebuild for ${args.platform}:${args.arch}`)
await exec(process.platform === "win32" ? "node-gyp.cmd" : "node-gyp", ["rebuild"], {
- env: getGypEnv(await getElectronVersion(await readPackageJson(devPackageFile), devPackageFile), arch, process.platform),
+ env: getGypEnv(await getElectronVersion(await readPackageJson(devPackageFile), devPackageFile), args.platform, args.arch, true),
})
}
diff --git a/src/codeSign.ts b/src/codeSign.ts
index f9577d0601f..fdebaf92fb4 100644
--- a/src/codeSign.ts
+++ b/src/codeSign.ts
@@ -1,4 +1,4 @@
-import { exec, getTempName, isEmptyOrSpaces, isCi, getCacheDirectory, statOrNull } from "./util/util"
+import { exec, getTempName, isEmptyOrSpaces, getCacheDirectory } from "./util/util"
import { deleteFile, outputFile, copy, rename } from "fs-extra-p"
import { download } from "./util/httpRequest"
import * as path from "path"
@@ -7,6 +7,8 @@ import BluebirdPromise from "bluebird-lst-c"
import { randomBytes } from "crypto"
import { TmpDir } from "./util/tmp"
import { homedir } from "os"
+import { statOrNull } from "./util/fs"
+import isCi from "is-ci"
export const appleCertificatePrefixes = ["Developer ID Application:", "Developer ID Installer:", "3rd Party Mac Developer Application:", "3rd Party Mac Developer Installer:"]
@@ -68,7 +70,7 @@ async function createCustomCertKeychain() {
const list = results[0]
.split("\n")
.map(it => {
- let r = it.trim()
+ const r = it.trim()
return r.substring(1, r.length - 1)
})
.filter(it => it.length > 0)
@@ -140,7 +142,7 @@ async function getValidIdentities(keychain?: string | null): Promise>([
exec("security", addKeychain(["find-identity", "-v"]))
.then(it => it.trim().split("\n").filter(it => {
- for (let prefix of appleCertificatePrefixes) {
+ for (const prefix of appleCertificatePrefixes) {
if (it.includes(prefix)) {
return true
}
@@ -170,7 +172,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain
//noinspection SpellCheckingInspection
const lines = await getValidIdentities(keychain)
const namePrefix = `${type}:`
- for (let line of lines) {
+ for (const line of lines) {
if (qualifier != null && !line.includes(qualifier)) {
continue
}
@@ -183,7 +185,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain
if (type === "Developer ID Application") {
// find non-Apple certificate
// https://github.com/electron-userland/electron-builder/issues/458
- l: for (let line of lines) {
+ l: for (const line of lines) {
if (qualifier != null && !line.includes(qualifier)) {
continue
}
@@ -192,7 +194,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain
continue
}
- for (let prefix of appleCertificatePrefixes) {
+ for (const prefix of appleCertificatePrefixes) {
if (line.includes(prefix)) {
continue l
}
@@ -207,7 +209,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain
export async function findIdentity(certType: CertType, qualifier?: string | null, keychain?: string | null): Promise {
let identity = process.env.CSC_NAME || qualifier
if (isEmptyOrSpaces(identity)) {
- if (keychain == null && !isCi() && (process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false")) {
+ if (keychain == null && !isCi && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
return null
}
else {
@@ -216,7 +218,7 @@ export async function findIdentity(certType: CertType, qualifier?: string | null
}
else {
identity = identity.trim()
- for (let prefix of appleCertificatePrefixes) {
+ for (const prefix of appleCertificatePrefixes) {
checkPrefix(identity, prefix)
}
const result = await _findIdentity(certType, identity, keychain)
diff --git a/src/fileMatcher.ts b/src/fileMatcher.ts
index 355f9c28a0d..a061e04c46d 100644
--- a/src/fileMatcher.ts
+++ b/src/fileMatcher.ts
@@ -1,7 +1,8 @@
import * as path from "path"
-import { createFilter, hasMagic, Filter } from "./util/filter"
+import { createFilter, hasMagic } from "./util/filter"
import { Minimatch } from "minimatch"
import { asArray } from "./util/util"
+import { Filter } from "./util/fs"
export interface FilePattern {
from?: string
@@ -30,10 +31,19 @@ export class FileMatcher {
this.patterns.push(pattern)
}
+ addAllPattern() {
+ // must be first, see minimatchAll implementation
+ this.patterns.unshift("**/*")
+ }
+
isEmpty() {
return this.patterns.length === 0
}
+ containsOnlyIgnore(): boolean {
+ return !this.isEmpty() && this.patterns.find(it => !it.startsWith("!")) == null
+ }
+
getParsedPatterns(fromDir?: string): Array {
// https://github.com/electron-userland/electron-builder/issues/733
const minimatchOptions = {dot: true}
@@ -41,9 +51,9 @@ export class FileMatcher {
const parsedPatterns: Array = []
const pathDifference = fromDir ? path.relative(fromDir, this.from) : null
- for (let i = 0; i < this.patterns.length; i++) {
- let expandedPattern = this.expandPattern(this.patterns[i])
- if (pathDifference) {
+ for (const p of this.patterns) {
+ let expandedPattern = this.expandPattern(p)
+ if (pathDifference != null) {
expandedPattern = path.join(pathDifference, expandedPattern)
}
@@ -72,10 +82,10 @@ export class FileMatcher {
}
}
-export function deprecatedUserIgnoreFilter(ignore: any, appDir: string) {
+export function deprecatedUserIgnoreFilter(ignore: Array | ((file: string) => boolean), appDir: string) {
let ignoreFunc: any
- if (typeof (ignore) === "function") {
- ignoreFunc = function (file: string) { return !ignore(file) }
+ if (typeof ignore === "function") {
+ ignoreFunc = function (file: string) { return !(ignore)(file) }
}
else {
if (!Array.isArray(ignore)) {
@@ -83,8 +93,8 @@ export function deprecatedUserIgnoreFilter(ignore: any, appDir: string) {
}
ignoreFunc = function (file: string) {
- for (let i = 0; i < ignore.length; i++) {
- if (file.match(ignore[i])) {
+ for (const i of >ignore) {
+ if (file.match(i)) {
return false
}
}
diff --git a/src/linuxPackager.ts b/src/linuxPackager.ts
index 96943a5955a..d9299babb7b 100755
--- a/src/linuxPackager.ts
+++ b/src/linuxPackager.ts
@@ -16,7 +16,7 @@ export class LinuxPackager extends PlatformPackager {
constructor(info: BuildInfo) {
super(info)
- let executableName = this.platformSpecificBuildOptions.executableName
+ const executableName = this.platformSpecificBuildOptions.executableName
this.executableName = sanitizeFileName(executableName == null ? this.appInfo.name : executableName).toLowerCase()
}
@@ -44,7 +44,7 @@ export class LinuxPackager extends PlatformPackager {
return helper
}
- for (let name of targets) {
+ for (const name of targets) {
if (name === DIR_TARGET) {
continue
}
diff --git a/src/macPackager.ts b/src/macPackager.ts
index b68219bd612..58e6bfdbfe2 100644
--- a/src/macPackager.ts
+++ b/src/macPackager.ts
@@ -32,11 +32,11 @@ export default class MacPackager extends PlatformPackager {
}
protected prepareAppInfo(appInfo: AppInfo): AppInfo {
- return new AppInfo(appInfo.metadata, this.devMetadata, this.platformSpecificBuildOptions.bundleVersion)
+ return new AppInfo(appInfo.metadata, this.info.devMetadata, this.platformSpecificBuildOptions.bundleVersion)
}
async getIconPath(): Promise {
- let iconPath = this.platformSpecificBuildOptions.icon || this.devMetadata.build.icon
+ let iconPath = this.platformSpecificBuildOptions.icon || this.config.icon
if (iconPath != null && !iconPath.endsWith(".icns")) {
iconPath += ".icns"
}
@@ -44,7 +44,7 @@ export default class MacPackager extends PlatformPackager {
}
createTargets(targets: Array, mapper: (name: string, factory: (outDir: string) => Target) => void, cleanupTasks: Array<() => Promise>): void {
- for (let name of targets) {
+ for (const name of targets) {
switch (name) {
case DIR_TARGET:
break
@@ -82,7 +82,7 @@ export default class MacPackager extends PlatformPackager {
if (hasMas) {
const appOutDir = path.join(outDir, "mas")
- const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (this.devMetadata.build).mas)
+ const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (this.config).mas)
await this.doPack(outDir, appOutDir, "mas", arch, masBuildOptions)
await this.sign(appOutDir, masBuildOptions)
}
@@ -112,8 +112,8 @@ export default class MacPackager extends PlatformPackager {
}
if (name == null) {
- let message = `App is not signed: cannot find valid ${isMas ? '"3rd Party Mac Developer Application" identity' : `"Developer ID Application" identity or custom non-Apple code signing certificate`}, see https://github.com/electron-userland/electron-builder/wiki/Code-Signing`
- if (isMas) {
+ const message = `App is not signed: cannot find valid ${isMas ? '"3rd Party Mac Developer Application" identity' : `"Developer ID Application" identity or custom non-Apple code signing certificate`}, see https://github.com/electron-userland/electron-builder/wiki/Code-Signing`
+ if (isMas || this.platformSpecificBuildOptions.forceCodeSigning) {
throw new Error(message)
}
else {
@@ -172,9 +172,9 @@ export default class MacPackager extends PlatformPackager {
}
async findInstallerIdentity(isMas: boolean, keychainName: string | n): Promise {
- const targetSpecificOptions: MacOptions = (this.devMetadata.build)[isMas ? "mas" : "pkg"] || this.platformSpecificBuildOptions
+ const targetSpecificOptions: MacOptions = (this.config)[isMas ? "mas" : "pkg"] || this.platformSpecificBuildOptions
const name = isMas ? "3rd Party Mac Developer Installer" : "Developer ID Installer"
- let installerName = await findIdentity(name, targetSpecificOptions.identity, keychainName)
+ const installerName = await findIdentity(name, targetSpecificOptions.identity, keychainName)
if (installerName != null) {
return installerName
}
diff --git a/src/metadata.ts b/src/metadata.ts
index 830538172b2..29ae81bf80e 100755
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -58,17 +58,6 @@ export interface DevMetadata extends Metadata {
See [.build](#BuildMetadata).
*/
readonly build: BuildMetadata
-
- // deprecated
- readonly homepage?: string | null
-
- // deprecated
- readonly license?: string | null
-
- /*
- See [.directories](#MetadataDirectories)
- */
- readonly directories?: MetadataDirectories | null
}
export interface RepositoryInfo {
@@ -115,26 +104,24 @@ export interface BuildMetadata {
*/
readonly files?: Array | string | null
- /**
+ /*
A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app's resources directory (`Contents/Resources` for MacOS, `resources` for Linux/Windows).
Glob rules the same as for [files](#multiple-glob-patterns).
*/
readonly extraResources?: Array | string | null
- /**
+ /*
The same as [extraResources](#BuildMetadata-extraResources) but copy into the app's content directory (`Contents` for MacOS, root directory for Linux/Windows).
*/
readonly extraFiles?: Array | string | null
/*
- Whether to package the application's source code into an archive, using [Electron's archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to `true`.
- Reasons why you may want to disable this feature are described in [an application packaging tutorial in Electron's documentation](http://electron.atom.io/docs/tutorial/application-packaging/#limitations-of-the-node-api).
-
- Or you can pass object of any asar options.
+ Whether to package the application's source code into an archive, using [Electron's archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to `true`.
+ Node modules, that must be unpacked, will be detected automatically, you don't need to explicitly set [asarUnpack](#BuildMetadata-asarUnpack) - please file issue if this doesn't work.
- Node modules, that must be unpacked, will be detected automatically, you don't need to explicitly set `asarUnpack` - please file issue if this doesn't work.
- */
+ Or you can pass object of asar options.
+ */
readonly asar?: AsarOptions | boolean | null
/**
@@ -239,12 +226,20 @@ export interface BuildMetadata {
// deprecated
readonly "app-bundle-id"?: string | null
- readonly dereference?: boolean
-
/*
See [.build.publish](#PublishConfiguration).
*/
readonly publish?: Publish
+
+ /*
+ Whether to fail if application will be not signed (to prevent unsigned app if code signing configuration is not correct).
+ */
+ readonly forceCodeSigning?: boolean
+
+ /*
+ See [.directories](#MetadataDirectories)
+ */
+ readonly directories?: MetadataDirectories | null
}
export interface AfterPackContext {
@@ -283,9 +278,14 @@ export interface FileAssociation {
readonly icon?: string
/*
- *macOS-only* The app’s role with respect to the type. The value can be `Editor`, `Viewer`, `Shell`, or `None`. Defaults to `Editor`.
+ *macOS-only* The app’s role with respect to the type. The value can be `Editor`, `Viewer`, `Shell`, or `None`. Defaults to `Editor`. Corresponds to `CFBundleTypeRole`.
*/
readonly role?: string
+
+ /*
+ *macOS-only* Whether the document is distributed as a bundle. If set to true, the bundle directory is treated as a file. Corresponds to `LSTypeIsPackage`.
+ */
+ readonly isPackage?: boolean
}
/*
@@ -348,6 +348,8 @@ export interface PlatformSpecificBuildOptions {
readonly fileAssociations?: Array | FileAssociation
readonly publish?: Publish
+
+ readonly forceCodeSigning?: boolean
}
export class Platform {
@@ -372,7 +374,7 @@ export class Platform {
archs = [Arch.x64]
}
- for (let arch of (archs == null || archs.length === 0 ? [archFromString(process.arch)] : archs)) {
+ for (const arch of (archs == null || archs.length === 0 ? [archFromString(process.arch)] : archs)) {
archToType.set(arch, type == null ? [] : (Array.isArray(type) ? type : [type]))
}
return new Map([[this, archToType]])
diff --git a/src/options/winOptions.ts b/src/options/winOptions.ts
index 4fd4c688521..7ece539d905 100644
--- a/src/options/winOptions.ts
+++ b/src/options/winOptions.ts
@@ -16,7 +16,7 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions {
/*
Array of signing algorithms used. Defaults to `['sha1', 'sha256']`
- Fo AppX `sha256` is always used.
+ For AppX `sha256` is always used.
*/
readonly signingHashAlgorithms?: Array | null
@@ -51,6 +51,11 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions {
The URL of the RFC 3161 time stamp server. Defaults to `http://timestamp.comodoca.com/rfc3161`.
*/
readonly rfc3161TimeStampServer?: string
+
+ /*
+ The URL of the time stamp server. Defaults to `http://timestamp.verisign.com/scripts/timstamp.dll`.
+ */
+ readonly timeStampServer?: string
}
/*
@@ -93,6 +98,11 @@ export interface NsisOptions {
*/
readonly guid?: string | null
+ /*
+ The path to installer icon. Defaults to `build/installerIcon.ico` or application icon.
+ */
+ readonly installerIcon?: string | null
+
/*
*boring installer only.* `MUI_HEADERIMAGE`, relative to the project directory. Defaults to `build/installerHeader.bmp`
*/
@@ -180,10 +190,36 @@ export interface SquirrelWindowsOptions extends WinBuildOptions {
readonly useAppIdAsId?: boolean
}
+/*
+ ### `.build.appx`
+
+ Please see [Windows AppX docs](https://msdn.microsoft.com/en-us/library/windows/apps/br211453.aspx).
+ */
export interface AppXOptions {
- // readonly flatten?: boolean
+ /*
+ The background color of the app tile. Please see [Visual Elements](https://msdn.microsoft.com/en-us/library/windows/apps/br211471.aspx).
+ */
readonly backgroundColor?: string | null
+
readonly makeappxArgs?: Array | null
+ /*
+ Describes the publisher information. The Publisher attribute must match the publisher subject information of the certificate used to sign a package. For now, required.
+ */
readonly publisher?: string | null
-}
\ No newline at end of file
+
+ /*
+ A friendly name that can be displayed to users. Corresponds to [Properties.DisplayName](https://msdn.microsoft.com/en-us/library/windows/apps/br211432.aspx).
+ */
+ readonly displayName?: string | null
+
+ /*
+ A friendly name for the publisher that can be displayed to users. Corresponds to [Properties.PublisherDisplayName](https://msdn.microsoft.com/en-us/library/windows/apps/br211460.aspx).
+ */
+ readonly publisherDisplayName?: string | null
+
+ /*
+ Describes the contents of the package. The Name attribute is case-sensitive. Corresponds to [Identity.Name](https://msdn.microsoft.com/en-us/library/windows/apps/br211441.aspx).
+ */
+ readonly identityName?: string | null
+}
diff --git a/src/packager.ts b/src/packager.ts
index 8edfb0d9bbe..73db795e82c 100644
--- a/src/packager.ts
+++ b/src/packager.ts
@@ -1,9 +1,9 @@
import * as path from "path"
-import { computeDefaultAppDirectory, getElectronVersion, use, exec, isEmptyOrSpaces } from "./util/util"
+import { computeDefaultAppDirectory, getElectronVersion, use, exec, isEmptyOrSpaces, getDirectoriesConfig } from "./util/util"
import { all, executeFinally } from "./util/promise"
import { EventEmitter } from "events"
import BluebirdPromise from "bluebird-lst-c"
-import { AppMetadata, DevMetadata, Platform, Arch } from "./metadata"
+import { AppMetadata, DevMetadata, Platform, Arch, BuildMetadata } from "./metadata"
import { PlatformPackager, BuildInfo, ArtifactCreated } from "./platformPackager"
import { WinPackager } from "./winPackager"
import * as errorMessages from "./errorMessages"
@@ -28,8 +28,13 @@ export class Packager implements BuildInfo {
appDir: string
metadata: AppMetadata
+
devMetadata: DevMetadata
+ get config(): BuildMetadata {
+ return this.devMetadata.build
+ }
+
isTwoPackageJsonProjectLayoutUsed = true
electronVersion: string
@@ -55,12 +60,21 @@ export class Packager implements BuildInfo {
}
async build(): Promise