From a138a86fb7b59098f5dac0c0a6b59c034eb9b222 Mon Sep 17 00:00:00 2001
From: Mike Maietta
Date: Sun, 16 Jan 2022 10:42:43 -0800
Subject: [PATCH] chore: v23.0.0 alpha (#6556)
* Adding INPUTxxx and OUTPUTxxx CHARSETS to makensis
Fixes: #4898 #6232 #6259
* Adding additional details to error console logging
* Breaking change: Removing Bintray support since it was sunset. Ref: https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
* fix: force strip path separators for backslashes on Windows
* fix: Force authentication for local Mac Squirrel update server
* Breaking Change: Fail-fast for signature verification failures. Adding `-LiteralPath` to update file for injected wildcards
* Adding changeset and eslint
* Fix error: `-OUTPUTCHARSET is disabled for non Win32 platforms.`
* feat(mac): ElectronAsarIntegrity in electron@15 (#6511)
* feat(mac): ElectronAsarIntegrity in electron@15
See: https://github.com/electron/electron/pull/30667
Fix: #6506
Fix: #6507
* fix(msi): MSI fails to install when deployed machine-wide via GPO (#6514)
* fix(msi): MSI fails to install when deployed machine-wide via GPO
* Disable advertised shortcuts, since MSIs with advertised Start Menu shortcuts that have a
Shortcut Property fails to install when deployed machine-wide via GPO but works fine in all
other contexts. This might be a bug in Windows or a misdiagnosis; see #6508 for more details.
Closes #6508
* BREAKING CHANGE: Admins using advertisement must apply an MST to re-enable it. See #6508.
* Don't set GitHub Releases draft title since it automatically pulls it from tag name. Fixes #3683
* feat(snap): add lzo to Snap compression options (also as new default) (#6201)
* feat(msi): add fileAssociation support for MSI target (#6530)
* fix(win): iconId sometimes containing invalid characters, and iconId config option being ignored.
* fix(msi): change the fallback value for generated MSI Ids to a unique string for the product.
* BREAKING CHANGE: remove MSI option `iconId`
* fix: stabilizing tests by moving updater tests to its own node to explicitly segment env.___TOKEN integration tests from other standard unit tests
* chore: synchronizing docs and schema plus prettier
* Adding changset to set as alpha
* Updating changeset documentation
* feat(msi): support assisted installer for MSI target (#6550)
* Add basic support for assisted installer, with UI to choose between per-user and per-machine. Supported config settings: runAfterFinish, perMachine, oneClick. Not supported: license (EULA), allowToChangeInstallationDirectory, etc. Also prevent oneClick's runAfterFinish from executing when installed silently.
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
Co-authored-by: Alex Plumley
Co-authored-by: Mike Maietta
Co-authored-by: Omer Akram
Co-authored-by: Maximilian Federle
---
.changeset/cool-ladybugs-matter.md | 5 +
.changeset/giant-dryers-beg.md | 5 +
.changeset/giant-dryers-copy.md | 6 +
.changeset/healthy-peaches-deliver.md | 5 +
.changeset/nervous-buckets-dance.md | 18 ++
.changeset/nervous-pandas-film.md | 5 +
.changeset/pre.json | 20 ++
.changeset/serious-peas-help.md | 5 +
.changeset/spicy-crabs-draw.md | 12 +
.github/actions/pretest/action.yml | 42 ++++
.github/workflows/test.yaml | 85 +++----
docker/base/Dockerfile | 2 +-
docs/api/electron-builder.md | 6 +-
docs/configuration/snap.md | 3 +
docs/generated/LinuxTargetSpecificOptions.md | 2 +-
.../generated/PlatformSpecificBuildOptions.md | 2 +-
package.json | 5 +-
packages/app-builder-lib/scheme.json | 234 ++----------------
packages/app-builder-lib/src/asar/asar.ts | 29 ++-
packages/app-builder-lib/src/asar/asarUtil.ts | 6 +-
.../app-builder-lib/src/asar/integrity.ts | 104 ++++++--
.../src/electron/electronMac.ts | 2 +-
.../src/options/FileAssociation.ts | 6 +-
.../app-builder-lib/src/options/MsiOptions.ts | 5 -
.../options/PlatformSpecificBuildOptions.ts | 3 +-
.../src/options/SnapOptions.ts | 6 +
.../src/options/linuxOptions.ts | 2 +-
.../app-builder-lib/src/platformPackager.ts | 4 +-
.../src/publish/BintrayPublisher.ts | 104 --------
.../src/publish/PublishManager.ts | 27 +-
.../src/publish/s3/s3Publisher.ts | 2 +-
.../src/publish/s3/spacesPublisher.ts | 2 +-
.../src/publish/updateInfoBuilder.ts | 6 +-
.../app-builder-lib/src/targets/MsiTarget.ts | 63 +++--
.../src/targets/nsis/NsisTarget.ts | 1 +
packages/app-builder-lib/src/targets/snap.ts | 6 +
.../templates/msi/WixUI_Assisted.wxs | 134 ----------
.../templates/msi/template.xml | 57 +++--
.../templates/snap/snapcraft.yaml | 1 +
packages/builder-util-runtime/src/bintray.ts | 88 -------
packages/builder-util-runtime/src/index.ts | 1 -
.../src/publishOptions.ts | 54 +---
packages/builder-util/package.json | 2 +-
packages/builder-util/src/util.ts | 2 +-
.../electron-publish/src/gitHubPublisher.ts | 3 +-
packages/electron-publish/src/publisher.ts | 4 +-
packages/electron-updater/src/AppUpdater.ts | 2 +-
packages/electron-updater/src/MacUpdater.ts | 70 ++++--
.../electron-updater/src/providerFactory.ts | 5 -
.../src/providers/BintrayProvider.ts | 54 ----
.../windowsExecutableCodeSignatureVerifier.ts | 11 +-
pnpm-lock.yaml | 8 +-
test/snapshots/globTest.js.snap | 2 +-
test/snapshots/linux/snapTest.js.snap | 155 ++++++++++++
.../windows/assistedInstallerTest.js.snap | 130 ++++++++++
test/snapshots/windows/msiTest.js.snap | 12 +
.../windows/oneClickInstallerTest.js.snap | 7 +-
test/src/BuildTest.ts | 8 +-
test/src/globTest.ts | 3 +-
test/src/helpers/packTester.ts | 31 ++-
test/src/linux/snapTest.ts | 41 +++
test/src/windows/oneClickInstallerTest.ts | 6 +-
62 files changed, 875 insertions(+), 856 deletions(-)
create mode 100644 .changeset/cool-ladybugs-matter.md
create mode 100644 .changeset/giant-dryers-beg.md
create mode 100644 .changeset/giant-dryers-copy.md
create mode 100644 .changeset/healthy-peaches-deliver.md
create mode 100644 .changeset/nervous-buckets-dance.md
create mode 100644 .changeset/nervous-pandas-film.md
create mode 100644 .changeset/pre.json
create mode 100644 .changeset/serious-peas-help.md
create mode 100644 .changeset/spicy-crabs-draw.md
create mode 100644 .github/actions/pretest/action.yml
delete mode 100644 packages/app-builder-lib/src/publish/BintrayPublisher.ts
delete mode 100644 packages/app-builder-lib/templates/msi/WixUI_Assisted.wxs
delete mode 100644 packages/builder-util-runtime/src/bintray.ts
delete mode 100644 packages/electron-updater/src/providers/BintrayProvider.ts
create mode 100644 test/snapshots/windows/assistedInstallerTest.js.snap
diff --git a/.changeset/cool-ladybugs-matter.md b/.changeset/cool-ladybugs-matter.md
new file mode 100644
index 00000000000..f42457a9b47
--- /dev/null
+++ b/.changeset/cool-ladybugs-matter.md
@@ -0,0 +1,5 @@
+---
+"app-builder-lib": minor
+---
+
+feat(msi): support assisted installer for MSI target
diff --git a/.changeset/giant-dryers-beg.md b/.changeset/giant-dryers-beg.md
new file mode 100644
index 00000000000..517f30ba5e6
--- /dev/null
+++ b/.changeset/giant-dryers-beg.md
@@ -0,0 +1,5 @@
+---
+"app-builder-lib": minor
+---
+
+feat(msi): add fileAssociation support for MSI target
diff --git a/.changeset/giant-dryers-copy.md b/.changeset/giant-dryers-copy.md
new file mode 100644
index 00000000000..d599f14d0bc
--- /dev/null
+++ b/.changeset/giant-dryers-copy.md
@@ -0,0 +1,6 @@
+---
+"app-builder-lib": patch
+---
+
+fix(win): iconId sometimes containing invalid characters, and iconId config option being ignored.
+fix(msi): change the fallback value for generated MSI Ids to a unique string for the product.
\ No newline at end of file
diff --git a/.changeset/healthy-peaches-deliver.md b/.changeset/healthy-peaches-deliver.md
new file mode 100644
index 00000000000..2dddfe5c267
--- /dev/null
+++ b/.changeset/healthy-peaches-deliver.md
@@ -0,0 +1,5 @@
+---
+"app-builder-lib": patch
+---
+
+fix(msi): MSI fails to install when deployed machine-wide via GPO
diff --git a/.changeset/nervous-buckets-dance.md b/.changeset/nervous-buckets-dance.md
new file mode 100644
index 00000000000..fcc7fa1ad10
--- /dev/null
+++ b/.changeset/nervous-buckets-dance.md
@@ -0,0 +1,18 @@
+---
+"app-builder-lib": major
+"builder-util": major
+"builder-util-runtime": major
+"electron-publish": major
+"electron-updater": major
+---
+
+Breaking changes
+Removing Bintray support since it was sunset. Ref: https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
+Fail-fast for windows signature verification failures. Adding `-LiteralPath` to update file path to disregard injected wildcards
+Force strip path separators for backslashes on Windows during update process
+Force authentication for local mac squirrel update server
+
+Fixes:
+fix(nsis): Adding --INPUTCHARSET to makensis. (#4898 #6232 #6259)
+
+Adding additional details to error console logging
diff --git a/.changeset/nervous-pandas-film.md b/.changeset/nervous-pandas-film.md
new file mode 100644
index 00000000000..1caf13eb2a1
--- /dev/null
+++ b/.changeset/nervous-pandas-film.md
@@ -0,0 +1,5 @@
+---
+"app-builder-lib": minor
+---
+
+feat(mac): ElectronAsarIntegrity in electron@15
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 00000000000..1c76fbe24db
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,20 @@
+{
+ "mode": "pre",
+ "tag": "alpha",
+ "initialVersions": {
+ "app-builder-lib": "22.14.13",
+ "builder-util": "22.14.13",
+ "builder-util-runtime": "8.9.2",
+ "dmg-builder": "22.14.13",
+ "electron-builder": "22.14.13",
+ "electron-builder-squirrel-windows": "22.14.13",
+ "electron-forge-maker-appimage": "22.14.13",
+ "electron-forge-maker-nsis": "22.14.13",
+ "electron-forge-maker-nsis-web": "22.14.13",
+ "electron-forge-maker-snap": "22.14.13",
+ "electron-publish": "22.14.13",
+ "electron-updater": "4.6.5",
+ "@electron-builder/test": "0.0.0"
+ },
+ "changesets": []
+}
diff --git a/.changeset/serious-peas-help.md b/.changeset/serious-peas-help.md
new file mode 100644
index 00000000000..9dc1fc01af8
--- /dev/null
+++ b/.changeset/serious-peas-help.md
@@ -0,0 +1,5 @@
+---
+"app-builder-lib": major
+---
+
+BREAKING CHANGE: remove MSI option `iconId`
diff --git a/.changeset/spicy-crabs-draw.md b/.changeset/spicy-crabs-draw.md
new file mode 100644
index 00000000000..dd860ed685c
--- /dev/null
+++ b/.changeset/spicy-crabs-draw.md
@@ -0,0 +1,12 @@
+---
+"app-builder-lib": minor
+"builder-util": major
+---
+
+Default to LZO compression for snap packages.
+This greatly improves cold startup performance (https://snapcraft.io/blog/why-lzo-was-chosen-as-the-new-compression-method).
+LZO has already been adopted by most desktop-oriented snaps outside of the Electron realm.
+
+For the rare case where developers prefer a smaller file size (XZ) to vastly improved decompression performance (LZO), provided an option to override the default compression method.
+
+Consumers do not need to update their configuration unless they specifically want to stick to XZ compression.
diff --git a/.github/actions/pretest/action.yml b/.github/actions/pretest/action.yml
new file mode 100644
index 00000000000..76409820f1d
--- /dev/null
+++ b/.github/actions/pretest/action.yml
@@ -0,0 +1,42 @@
+name: 'Setup Tests'
+description: 'Set up pnpm, node w/ pnpm cache, install and audit deps, compile, and restore electron cache'
+inputs:
+ cache-key:
+ description: 'The key to the electron cache'
+ required: true
+ cache-path:
+ description: 'The path to the electron cache'
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - uses: pnpm/action-setup@v2
+ with:
+ version: 6.25.1
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: '14'
+ cache: 'pnpm'
+
+ - name: install dependencies
+ run: pnpm install --frozen-lockfile
+ shell: bash
+
+ - name: Compile
+ run: pnpm compile
+ shell: bash
+
+ - name: Cache Electron
+ uses: actions/cache@v2
+ with:
+ path: ${{ inputs.cache-path }}
+ key: ${{ inputs.cache-key }}
+
+## Usage
+# - name: Setup Tests
+# uses: ./.github/actions/pretest
+# with:
+# cache-path: ~/Library/Caches/electron
+# cache-key: v-11.0.0-macos-electron
\ No newline at end of file
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index dd39e842c2f..b5fded08e8b 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -26,8 +26,14 @@ jobs:
with:
fetch-depth: 0
- - name: Install deps and audit
- uses: ./.github/actions/pnpm
+ - name: Setup Tests
+ uses: ./.github/actions/pretest
+ with:
+ cache-path: ~/.cache/electron
+ cache-key: v-11.0.0-linux-electron
+
+ - name: Lint
+ run: pnpm pretest
- name: Determine if Dockerfiles changed
id: changed-files
@@ -37,15 +43,6 @@ jobs:
if: ${{ github.event.inputs.build-docker-locally == 'true' }} || contains(steps.changed-files.outputs.all_changed_files, 'Dockerfile') || contains(steps.changed-files.outputs.all_changed_files, 'docker')
run: pnpm docker-images
- - name: Compile and Lint
- run: pnpm pretest
-
- - name: Cache ~/.cache/electron
- uses: actions/cache@v2
- with:
- path: ~/.cache/electron
- key: v-11.0.0-electron
-
- name: Run tests in docker image
run: pnpm test-linux
env:
@@ -54,60 +51,64 @@ jobs:
test-mac:
runs-on: macos-latest
- strategy:
- matrix:
- testFiles:
- - masTest,dmgTest,protonTest
steps:
- name: Checkout code repository
uses: actions/checkout@v2
- - name: Install deps and audit
- uses: ./.github/actions/pnpm
+ - name: Setup Tests
+ uses: ./.github/actions/pretest
+ with:
+ cache-path: ~/Library/Caches/electron
+ cache-key: v-11.0.0-macos-electron
- - name: Compile
- run: pnpm compile
-
- - name: Cache ~/Library/Caches/electron
- uses: actions/cache@v2
+ - name: Test
+ run: pnpm ci:test
+ env:
+ TEST_FILES: masTest,dmgTest,protonTest
+ FORCE_COLOR: 1
+
+ # Need to separate from other tests because logic is specific to when TOKEN env vars are set
+ test-updater:
+ runs-on: macos-latest
+ steps:
+ - name: Checkout code repository
+ uses: actions/checkout@v2
+
+ - name: Setup Tests
+ uses: ./.github/actions/pretest
with:
- path: ~/Library/Caches/electron
- key: v-11.0.0-mac-electron
+ cache-path: ~/Library/Caches/electron
+ cache-key: v-11.0.0-update-electron
- name: Test
- run: node ./test/out/helpers/runTests.js
+ run: pnpm ci:test
env:
- TEST_FILES: ${{ matrix.testFiles }}
+ TEST_FILES: nsisUpdaterTest
+ KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
+ # BITBUCKET_TOKEN: ${{ secrets.BITBUCKET_TOKEN }}
+ GH_TOKEN: ${{ secrets.GH_TOKEN }}
FORCE_COLOR: 1
test-windows:
- runs-on: windows-2019
+ runs-on: windows-latest
strategy:
matrix:
testFiles:
- - installerTest,appxTest,msiTest,portableTest,assistedInstallerTest
- - oneClickInstallerTest,nsisUpdaterTest,winCodeSignTest,winPackagerTest,webInstallerTest
+ - installerTest,appxTest,msiTest,portableTest,assistedInstallerTest,protonTest
+ - oneClickInstallerTest,winCodeSignTest,winPackagerTest,webInstallerTest
steps:
- name: Checkout code repository
uses: actions/checkout@v2
- - name: Install deps and audit
- uses: ./.github/actions/pnpm
-
- - name: Compile
- run: pnpm compile
-
- - name: Cache ~\AppData\Local\Cache\electron
- uses: actions/cache@v2
+ - name: Setup Tests
+ uses: ./.github/actions/pretest
with:
- path: ~\AppData\Local\Cache\electron
- key: v-11.0.0-windows-electron
+ cache-path: ~\AppData\Local\Cache\electron
+ cache-key: v-11.0.0-windows-electron
- name: Test
- run: node ./test/out/helpers/runTests.js
+ run: pnpm ci:test
env:
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
- KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
- GH_TOKEN: ${{ secrets.GH_TOKEN }}
TEST_FILES: ${{ matrix.testFiles }}
FORCE_COLOR: 1
diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile
index 8e0b20b00a6..4f84550fae5 100644
--- a/docker/base/Dockerfile
+++ b/docker/base/Dockerfile
@@ -13,7 +13,7 @@ RUN apt-get -qq update && apt-get -qq dist-upgrade && \
apt-get -qq install --no-install-recommends \
qtbase5-dev build-essential autoconf libssl-dev gcc-multilib g++-multilib \
lzip rpm python libcurl4 git git-lfs ssh unzip libarchive-tools \
- libxtst6 libsecret-1-dev libopenjp2-tools \
+ libxtst6 libsecret-1-dev libopenjp2-tools liblzo2-2 \
&& \
# git-lfs
git lfs install && \
diff --git a/docs/api/electron-builder.md b/docs/api/electron-builder.md
index 0128cf42c2a..eccf30c32ae 100644
--- a/docs/api/electron-builder.md
+++ b/docs/api/electron-builder.md
@@ -1364,7 +1364,7 @@ return path.join(target.outDir, __${target.name}-${getArtifactArchName(arc
Kind: class of electron-publish
Properties
-providerName
String
+providerName
“github” | “s3” | “spaces” | “generic” | “custom” | “snapStore” | “keygen” | “bitbucket”
Methods
options |
-PublishConfiguration | String | GithubOptions | S3Options | SpacesOptions | GenericServerOptions | BintrayOptions | module:builder-util-runtime/out/publishOptions.CustomPublishOptions | module:builder-util-runtime/out/publishOptions.KeygenOptions | SnapStoreOptions | module:builder-util-runtime/out/publishOptions.BitbucketOptions | String |
+PublishConfiguration | String | GithubOptions | S3Options | SpacesOptions | GenericServerOptions | module:builder-util-runtime/out/publishOptions.CustomPublishOptions | module:builder-util-runtime/out/publishOptions.KeygenOptions | SnapStoreOptions | module:builder-util-runtime/out/publishOptions.BitbucketOptions | String |
If you want to override configuration in the app-update.yml . |
diff --git a/docs/configuration/snap.md b/docs/configuration/snap.md
index 07dfca59b58..cb194be4a2b 100644
--- a/docs/configuration/snap.md
+++ b/docs/configuration/snap.md
@@ -57,6 +57,9 @@ The top-level [snap](configuration.md#Configuration-snap) key contains set of op
title
String | “undefined” - An optional title for the snap, may contain uppercase letters and spaces. Defaults to productName
. See snap format documentation.
+
+compression
= lzo
“xz” | “lzo” | “undefined” - Sets the compression type for the snap. Can be xz or lzo. Defaults to lzo when not specified.
+
Inherited from CommonLinuxOptions
:
diff --git a/docs/generated/LinuxTargetSpecificOptions.md b/docs/generated/LinuxTargetSpecificOptions.md
index c0050afc559..b600edfbb18 100644
--- a/docs/generated/LinuxTargetSpecificOptions.md
+++ b/docs/generated/LinuxTargetSpecificOptions.md
@@ -3,7 +3,7 @@
depends
Array<String> | “undefined” - Package dependencies.
-
-
compression
= xz
“gz” | “bzip2” | “xz” | “undefined” - The compression type.
+compression
= xz
“gz” | “bzip2” | “xz” | “lzo” | “undefined” - The compression type.
-
icon
String
diff --git a/docs/generated/PlatformSpecificBuildOptions.md b/docs/generated/PlatformSpecificBuildOptions.md
index 3203d69b195..02c81210cba 100644
--- a/docs/generated/PlatformSpecificBuildOptions.md
+++ b/docs/generated/PlatformSpecificBuildOptions.md
@@ -46,7 +46,7 @@
-
icon
String | “undefined” - 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.
-Not supported on Linux, file issue if need (default icon will be x-office-document
).
+Not supported on Linux, file issue if need (default icon will be x-office-document
). Not supported on MSI.
-
role
= Editor
String - macOS-only The app’s role with respect to the type. The value can be Editor
, Viewer
, Shell
, or None
. Corresponds to CFBundleTypeRole
.
diff --git a/package.json b/package.json
index 7f70444e0a2..7b9abf451d3 100644
--- a/package.json
+++ b/package.json
@@ -14,11 +14,11 @@
"lint": "eslint packages --ext .ts",
"lint-staged": "lint-staged",
"lint-deps": "node ./test/out/helpers/checkDeps.js",
- "pretest": "pnpm compile && pnpm lint && pnpm lint-deps",
+ "pretest": "pnpm lint && pnpm lint-deps",
"prettier": "prettier 'packages/**/*.{ts, js}' 'test/src/**/*.ts' --write",
"///": "Please see https://github.com/electron-userland/electron-builder/blob/master/CONTRIBUTING.md#run-test-using-cli how to run particular test instead full (and very slow) run",
"test": "node ./test/out/helpers/runTests.js skipArtifactPublisher",
- "test-all": "pnpm pretest && node ./test/out/helpers/runTests.js",
+ "test-all": "pnpm compile && pnpm pretest && pnpm ci:test",
"test-linux": "docker run --rm -e UPDATE_SNAPSHOT=${UPDATE_SNAPSHOT:-false} -e TEST_FILES=\"${TEST_FILES:-HoistedNodeModuleTest}\" -v $(pwd):/project -v $(pwd)-node-modules:/project/node_modules -v $HOME/Library/Caches/electron:/root/.cache/electron -v $HOME/Library/Caches/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine-mono /bin/bash -c \"pnpm install && node ./test/out/helpers/runTests.js\"",
"test-update": "UPDATE_SNAPSHOT=true pnpm test-all",
"docker-images": "docker/build.sh",
@@ -26,6 +26,7 @@
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
"generate-changeset": "pnpm changeset",
"generate-docs": "pnpm compile && pnpm jsdoc && pnpm jsdoc2md2html",
+ "ci:test": "node ./test/out/helpers/runTests.js",
"ci:version": "pnpm changelog && changeset version && node scripts/update-package-version-export.js && pnpm run generate-docs && git add .",
"ci:publish": "pnpm compile && pnpm publish -r && changeset tag",
"schema": "typescript-json-schema packages/app-builder-lib/tsconfig-scheme.json Configuration --out packages/app-builder-lib/scheme.json --noExtraProps --useTypeOfKeyword --strictNullChecks --required && node ./scripts/fix-schema.js",
diff --git a/packages/app-builder-lib/scheme.json b/packages/app-builder-lib/scheme.json
index e0698864005..0e6dc99cdfb 100644
--- a/packages/app-builder-lib/scheme.json
+++ b/packages/app-builder-lib/scheme.json
@@ -78,9 +78,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -108,9 +105,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -240,9 +234,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -270,9 +261,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -330,11 +318,6 @@
"AsarOptions": {
"additionalProperties": false,
"properties": {
- "externalAllowed": {
- "default": false,
- "description": "Allows external asar files.",
- "type": "boolean"
- },
"ordering": {
"type": [
"null",
@@ -349,101 +332,6 @@
},
"type": "object"
},
- "BintrayOptions": {
- "additionalProperties": false,
- "description": "[Bintray](https://bintray.com/) options. Requires an API key. An API key can be obtained from the user [profile](https://bintray.com/profile/edit) page (\"Edit Your Profile\" -> API Key).\nDefine `BT_TOKEN` environment variable.",
- "properties": {
- "component": {
- "description": "The Bintray component (Debian only).",
- "type": [
- "null",
- "string"
- ]
- },
- "distribution": {
- "default": "stable",
- "description": "The Bintray distribution (Debian only).",
- "type": [
- "null",
- "string"
- ]
- },
- "owner": {
- "description": "The owner.",
- "type": [
- "null",
- "string"
- ]
- },
- "package": {
- "description": "The Bintray package name.",
- "type": [
- "null",
- "string"
- ]
- },
- "provider": {
- "description": "The provider. Must be `bintray`.",
- "enum": [
- "bintray"
- ],
- "type": "string"
- },
- "publishAutoUpdate": {
- "default": true,
- "description": "Whether to publish auto update info files.\n\nAuto update relies only on the first provider in the list (you can specify several publishers).\nThus, probably, there`s no need to upload the metadata files for the other configured providers. But by default will be uploaded.",
- "type": "boolean"
- },
- "publisherName": {
- "anyOf": [
- {
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ]
- },
- "repo": {
- "default": "generic",
- "description": "The Bintray repository name.",
- "type": [
- "null",
- "string"
- ]
- },
- "requestHeaders": {
- "$ref": "#/definitions/OutgoingHttpHeaders",
- "description": "Any custom request headers"
- },
- "token": {
- "type": [
- "null",
- "string"
- ]
- },
- "updaterCacheDirName": {
- "type": [
- "null",
- "string"
- ]
- },
- "user": {
- "description": "The Bintray user account. Used in cases where the owner is an organization.",
- "type": [
- "null",
- "string"
- ]
- }
- },
- "required": [
- "provider"
- ],
- "type": "object"
- },
"BitbucketOptions": {
"additionalProperties": false,
"description": "Bitbucket options.\nhttps://bitbucket.org/\nDefine `BITBUCKET_TOKEN` environment variable.\n\nFor converting an app password to a usable token, you can utilize this\n```typescript\nconvertAppPassword(owner: string, token: string) {\nconst base64encodedData = Buffer.from(`${owner}:${token.trim()}`).toString(\"base64\")\nreturn `Basic ${base64encodedData}`\n}\n```",
@@ -634,6 +522,7 @@
"enum": [
"bzip2",
"gz",
+ "lzo",
"xz"
],
"type": "string"
@@ -755,9 +644,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -785,9 +671,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -951,9 +834,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -981,9 +861,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -1127,7 +1004,7 @@
},
"FileAssociation": {
"additionalProperties": false,
- "description": "File associations.\n\nmacOS (corresponds to [CFBundleDocumentTypes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685)) and NSIS only.\n\nOn Windows works only if [nsis.perMachine](https://electron.build/configuration/configuration#NsisOptions-perMachine) is set to `true`.",
+ "description": "File associations.\n\nmacOS (corresponds to [CFBundleDocumentTypes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685)), NSIS, and MSI only.\n\nOn Windows (NSIS) works only if [nsis.perMachine](https://electron.build/configuration/configuration#NsisOptions-perMachine) is set to `true`.",
"properties": {
"description": {
"description": "*windows-only.* The description.",
@@ -1151,7 +1028,7 @@
"description": "The extension (minus the leading period). e.g. `png`."
},
"icon": {
- "description": "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.\n\nNot supported on Linux, file issue if need (default icon will be `x-office-document`).",
+ "description": "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.\n\nNot supported on Linux, file issue if need (default icon will be `x-office-document`). Not supported on MSI.",
"type": [
"null",
"string"
@@ -1336,9 +1213,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -1366,9 +1240,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2002,9 +1873,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2032,9 +1900,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2146,6 +2011,7 @@
"enum": [
"bzip2",
"gz",
+ "lzo",
"xz"
],
"type": "string"
@@ -2260,9 +2126,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2290,9 +2153,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2760,9 +2620,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -2790,9 +2647,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3371,9 +3225,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3401,9 +3252,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3625,10 +3473,6 @@
"description": "Whether to create start menu shortcut.",
"type": "boolean"
},
- "iconId": {
- "description": "The [shortcut iconId](https://wixtoolset.org/documentation/manual/v4/reference/wxs/shortcut/). Optional, by default generated using app file name.",
- "type": "string"
- },
"menuCategory": {
"default": false,
"description": "Whether to create submenu for start menu shortcut and program files directory. If `true`, company name will be used. Or string value.",
@@ -3661,9 +3505,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3691,9 +3532,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3955,9 +3793,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -3985,9 +3820,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4285,9 +4117,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4315,9 +4144,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4612,9 +4438,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4642,9 +4465,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4752,9 +4572,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -4782,9 +4599,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -5139,6 +4953,22 @@
"string"
]
},
+ "compression": {
+ "anyOf": [
+ {
+ "enum": [
+ "lzo",
+ "xz"
+ ],
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": "lzo",
+ "description": "Sets the compression type for the snap. Can be xz or lzo. Defaults to lzo when not specified."
+ },
"confinement": {
"anyOf": [
{
@@ -5281,9 +5111,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -5311,9 +5138,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -5609,9 +5433,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -5639,9 +5460,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -6012,9 +5830,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -6042,9 +5857,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -6917,9 +6729,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
@@ -6947,9 +6756,6 @@
{
"$ref": "#/definitions/GenericServerOptions"
},
- {
- "$ref": "#/definitions/BintrayOptions"
- },
{
"$ref": "#/definitions/CustomPublishOptions"
},
diff --git a/packages/app-builder-lib/src/asar/asar.ts b/packages/app-builder-lib/src/asar/asar.ts
index a52d38ced36..70724cb35a8 100644
--- a/packages/app-builder-lib/src/asar/asar.ts
+++ b/packages/app-builder-lib/src/asar/asar.ts
@@ -2,6 +2,20 @@ import { createFromBuffer } from "chromium-pickle-js"
import { close, open, read, readFile, Stats } from "fs-extra"
import * as path from "path"
+/** @internal */
+export interface ReadAsarHeader {
+ readonly header: string
+ readonly size: number
+}
+
+/** @internal */
+export interface NodeIntegrity {
+ algorithm: "SHA256"
+ hash: string
+ blockSize: number
+ blocks: Array
+}
+
/** @internal */
export class Node {
// we don't use Map because later it will be stringified
@@ -16,6 +30,8 @@ export class Node {
executable?: boolean
link?: string
+
+ integrity?: NodeIntegrity
}
/** @internal */
@@ -66,13 +82,16 @@ export class AsarFilesystem {
return result
}
- addFileNode(file: string, dirNode: Node, size: number, unpacked: boolean, stat: Stats): Node {
+ addFileNode(file: string, dirNode: Node, size: number, unpacked: boolean, stat: Stats, integrity?: NodeIntegrity): Node {
if (size > 4294967295) {
throw new Error(`${file}: file size cannot be larger than 4.2GB`)
}
const node = new Node()
node.size = size
+ if (integrity) {
+ node.integrity = integrity
+ }
if (unpacked) {
node.unpacked = true
} else {
@@ -114,7 +133,7 @@ export class AsarFilesystem {
}
}
-export async function readAsar(archive: string): Promise {
+export async function readAsarHeader(archive: string): Promise {
const fd = await open(archive, "r")
let size: number
let headerBuf
@@ -135,7 +154,11 @@ export async function readAsar(archive: string): Promise {
}
const headerPickle = createFromBuffer(headerBuf)
- const header = headerPickle.createIterator().readString()
+ return { header: headerPickle.createIterator().readString(), size }
+}
+
+export async function readAsar(archive: string): Promise {
+ const { header, size } = await readAsarHeader(archive)
return new AsarFilesystem(archive, JSON.parse(header), size)
}
diff --git a/packages/app-builder-lib/src/asar/asarUtil.ts b/packages/app-builder-lib/src/asar/asarUtil.ts
index 7f698b4e4b8..c3102ecd7c3 100644
--- a/packages/app-builder-lib/src/asar/asarUtil.ts
+++ b/packages/app-builder-lib/src/asar/asarUtil.ts
@@ -8,6 +8,7 @@ import { Packager } from "../packager"
import { PlatformPackager } from "../platformPackager"
import { getDestinationPath, ResolvedFileSet } from "../util/appFileCopier"
import { AsarFilesystem, Node } from "./asar"
+import { hashFile, hashFileContents } from "./integrity"
import { detectUnpackedDirs } from "./unpackDetector"
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -112,9 +113,10 @@ export class AsarPackager {
}
const dirNode = currentDirNode!
- const newData = transformedFiles == null ? null : transformedFiles.get(i)
+ const newData = transformedFiles == null ? undefined : transformedFiles.get(i)
const isUnpacked = dirNode.unpacked || (this.unpackPattern != null && this.unpackPattern(file, stat))
- this.fs.addFileNode(file, dirNode, newData == null ? stat.size : Buffer.byteLength(newData), isUnpacked, stat)
+ const integrity = newData === undefined ? await hashFile(file) : hashFileContents(newData)
+ this.fs.addFileNode(file, dirNode, newData == undefined ? stat.size : Buffer.byteLength(newData), isUnpacked, stat, integrity)
if (isUnpacked) {
if (!dirNode.unpacked && !dirToCreateForUnpackedFiles.has(fileParent)) {
dirToCreateForUnpackedFiles.add(fileParent)
diff --git a/packages/app-builder-lib/src/asar/integrity.ts b/packages/app-builder-lib/src/asar/integrity.ts
index 53344799162..67e78b4d137 100644
--- a/packages/app-builder-lib/src/asar/integrity.ts
+++ b/packages/app-builder-lib/src/asar/integrity.ts
@@ -3,43 +3,107 @@ import { createHash } from "crypto"
import { createReadStream } from "fs"
import { readdir } from "fs/promises"
import * as path from "path"
+import { readAsarHeader, NodeIntegrity } from "./asar"
export interface AsarIntegrityOptions {
- /**
- * Allows external asar files.
- *
- * @default false
- */
- readonly externalAllowed?: boolean
+ readonly resourcesPath: string
+ readonly resourcesRelativePath: string
}
-export interface AsarIntegrity extends AsarIntegrityOptions {
- checksums: { [key: string]: string }
+export interface HeaderHash {
+ algorithm: "SHA256"
+ hash: string
}
-export async function computeData(resourcesPath: string, options?: AsarIntegrityOptions | null): Promise {
+export interface AsarIntegrity {
+ [key: string]: HeaderHash
+}
+
+export async function computeData({ resourcesPath, resourcesRelativePath }: AsarIntegrityOptions): Promise {
// sort to produce constant result
const names = (await readdir(resourcesPath)).filter(it => it.endsWith(".asar")).sort()
- const checksums = await BluebirdPromise.map(names, it => hashFile(path.join(resourcesPath, it)))
+ const checksums = await BluebirdPromise.map(names, it => hashHeader(path.join(resourcesPath, it)))
- const result: { [key: string]: string } = {}
+ const result: AsarIntegrity = {}
for (let i = 0; i < names.length; i++) {
- result[names[i]] = checksums[i]
+ result[path.join(resourcesRelativePath, names[i])] = checksums[i]
+ }
+ return result
+}
+
+async function hashHeader(file: string): Promise {
+ const hash = createHash("sha256")
+ const { header } = await readAsarHeader(file)
+ hash.update(header)
+ return {
+ algorithm: "SHA256",
+ hash: hash.digest("hex"),
}
- return { checksums: result, ...options }
}
-function hashFile(file: string, algorithm = "sha512", encoding: "hex" | "base64" | "latin1" = "base64") {
- return new Promise((resolve, reject) => {
- const hash = createHash(algorithm)
- hash.on("error", reject).setEncoding(encoding)
+export function hashFile(file: string, blockSize = 4 * 1024 * 1024): Promise {
+ return new Promise((resolve, reject) => {
+ const hash = createHash("sha256")
+
+ const blocks = new Array()
+
+ let blockBytes = 0
+ let blockHash = createHash("sha256")
+
+ function updateBlockHash(chunk: Buffer) {
+ let off = 0
+ while (off < chunk.length) {
+ const toHash = Math.min(blockSize - blockBytes, chunk.length - off)
+ blockHash.update(chunk.slice(off, off + toHash))
+ off += toHash
+ blockBytes += toHash
+
+ if (blockBytes === blockSize) {
+ blocks.push(blockHash.digest("hex"))
+ blockHash = createHash("sha256")
+ blockBytes = 0
+ }
+ }
+ }
createReadStream(file)
+ .on("data", it => {
+ // Note that `it` is a Buffer anyway so this cast is a no-op
+ updateBlockHash(Buffer.from(it))
+ hash.update(it)
+ })
.on("error", reject)
.on("end", () => {
- hash.end()
- resolve(hash.read() as string)
+ if (blockBytes !== 0) {
+ blocks.push(blockHash.digest("hex"))
+ }
+ resolve({
+ algorithm: "SHA256",
+ hash: hash.digest("hex"),
+ blockSize,
+ blocks,
+ })
})
- .pipe(hash, { end: false })
})
}
+
+export function hashFileContents(contents: Buffer | string, blockSize = 4 * 1024 * 1024): NodeIntegrity {
+ const buffer = Buffer.from(contents)
+ const hash = createHash("sha256")
+ hash.update(buffer)
+
+ const blocks = new Array()
+
+ for (let off = 0; off < buffer.length; off += blockSize) {
+ const blockHash = createHash("sha256")
+ blockHash.update(buffer.slice(off, off + blockSize))
+ blocks.push(blockHash.digest("hex"))
+ }
+
+ return {
+ algorithm: "SHA256",
+ hash: hash.digest("hex"),
+ blockSize,
+ blocks,
+ }
+}
diff --git a/packages/app-builder-lib/src/electron/electronMac.ts b/packages/app-builder-lib/src/electron/electronMac.ts
index ead3aa6b3c2..04f2023826f 100644
--- a/packages/app-builder-lib/src/electron/electronMac.ts
+++ b/packages/app-builder-lib/src/electron/electronMac.ts
@@ -214,7 +214,7 @@ export async function createMacApp(packager: MacPackager, appOutDir: string, asa
}
if (asarIntegrity != null) {
- appPlist.AsarIntegrity = JSON.stringify(asarIntegrity)
+ appPlist.ElectronAsarIntegrity = asarIntegrity
}
const plistDataToWrite: any = {
diff --git a/packages/app-builder-lib/src/options/FileAssociation.ts b/packages/app-builder-lib/src/options/FileAssociation.ts
index 82eb6f37532..4be4e9c62be 100644
--- a/packages/app-builder-lib/src/options/FileAssociation.ts
+++ b/packages/app-builder-lib/src/options/FileAssociation.ts
@@ -1,9 +1,9 @@
/**
* File associations.
*
- * macOS (corresponds to [CFBundleDocumentTypes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685)) and NSIS only.
+ * macOS (corresponds to [CFBundleDocumentTypes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685)), NSIS, and MSI only.
*
- * On Windows works only if [nsis.perMachine](https://electron.build/configuration/configuration#NsisOptions-perMachine) is set to `true`.
+ * On Windows (NSIS) works only if [nsis.perMachine](https://electron.build/configuration/configuration#NsisOptions-perMachine) is set to `true`.
*/
export interface FileAssociation {
/**
@@ -29,7 +29,7 @@ export interface FileAssociation {
/**
* 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.
*
- * Not supported on Linux, file issue if need (default icon will be `x-office-document`).
+ * Not supported on Linux, file issue if need (default icon will be `x-office-document`). Not supported on MSI.
*/
readonly icon?: string | null
diff --git a/packages/app-builder-lib/src/options/MsiOptions.ts b/packages/app-builder-lib/src/options/MsiOptions.ts
index 4132ebdb6c1..28d6da3d9d5 100644
--- a/packages/app-builder-lib/src/options/MsiOptions.ts
+++ b/packages/app-builder-lib/src/options/MsiOptions.ts
@@ -23,9 +23,4 @@ export interface MsiOptions extends CommonWindowsInstallerConfiguration, TargetS
* Any additional arguments to be passed to the WiX installer compiler, such as `["-ext", "WixUtilExtension"]`
*/
readonly additionalWixArgs?: Array | null
-
- /**
- * The [shortcut iconId](https://wixtoolset.org/documentation/manual/v4/reference/wxs/shortcut/). Optional, by default generated using app file name.
- */
- readonly iconId?: string
}
diff --git a/packages/app-builder-lib/src/options/PlatformSpecificBuildOptions.ts b/packages/app-builder-lib/src/options/PlatformSpecificBuildOptions.ts
index 1871786c99a..9a95b2a3ffc 100644
--- a/packages/app-builder-lib/src/options/PlatformSpecificBuildOptions.ts
+++ b/packages/app-builder-lib/src/options/PlatformSpecificBuildOptions.ts
@@ -1,4 +1,3 @@
-import { AsarIntegrityOptions } from "../asar/integrity"
import { CompressionLevel, Publish, TargetConfiguration, TargetSpecificOptions } from "../core"
import { FileAssociation } from "./FileAssociation"
@@ -17,7 +16,7 @@ export interface FileSet {
filter?: Array | string
}
-export interface AsarOptions extends AsarIntegrityOptions {
+export interface AsarOptions {
/**
* Whether to automatically unpack executables files.
* @default true
diff --git a/packages/app-builder-lib/src/options/SnapOptions.ts b/packages/app-builder-lib/src/options/SnapOptions.ts
index 66b217b6c36..e81f0b59308 100644
--- a/packages/app-builder-lib/src/options/SnapOptions.ts
+++ b/packages/app-builder-lib/src/options/SnapOptions.ts
@@ -125,6 +125,12 @@ export interface SnapOptions extends CommonLinuxOptions, TargetSpecificOptions {
* An optional title for the snap, may contain uppercase letters and spaces. Defaults to `productName`. See [snap format documentation](https://snapcraft.io/docs/snap-format).
*/
readonly title?: string | null
+
+ /**
+ * Sets the compression type for the snap. Can be xz or lzo. Defaults to lzo when not specified.
+ * @default lzo
+ */
+ readonly compression?: "xz" | "lzo" | null
}
export interface PlugDescriptor {
diff --git a/packages/app-builder-lib/src/options/linuxOptions.ts b/packages/app-builder-lib/src/options/linuxOptions.ts
index 8b84e140c70..8bb1214ad1d 100644
--- a/packages/app-builder-lib/src/options/linuxOptions.ts
+++ b/packages/app-builder-lib/src/options/linuxOptions.ts
@@ -77,7 +77,7 @@ export interface LinuxTargetSpecificOptions extends CommonLinuxOptions, TargetSp
* The compression type.
* @default xz
*/
- readonly compression?: "gz" | "bzip2" | "xz" | null
+ readonly compression?: "gz" | "bzip2" | "xz" | "lzo" | null
readonly icon?: string
diff --git a/packages/app-builder-lib/src/platformPackager.ts b/packages/app-builder-lib/src/platformPackager.ts
index 911397bed4d..ab4ebb7c5f6 100644
--- a/packages/app-builder-lib/src/platformPackager.ts
+++ b/packages/app-builder-lib/src/platformPackager.ts
@@ -280,10 +280,12 @@ export abstract class PlatformPackager
}
if (framework.beforeCopyExtraFiles != null) {
+ const resourcesRelativePath = this.platform === Platform.MAC ? "Resources" : isElectronBased(framework) ? "resources" : ""
+
await framework.beforeCopyExtraFiles({
packager: this,
appOutDir,
- asarIntegrity: asarOptions == null || disableAsarIntegrity ? null : await computeData(resourcesPath, asarOptions.externalAllowed ? { externalAllowed: true } : null),
+ asarIntegrity: asarOptions == null || disableAsarIntegrity ? null : await computeData({ resourcesPath, resourcesRelativePath }),
platformName,
})
}
diff --git a/packages/app-builder-lib/src/publish/BintrayPublisher.ts b/packages/app-builder-lib/src/publish/BintrayPublisher.ts
deleted file mode 100644
index 2a97989ba15..00000000000
--- a/packages/app-builder-lib/src/publish/BintrayPublisher.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { Arch, InvalidConfigurationError, isEmptyOrSpaces, isTokenCharValid, log, toLinuxArchString } from "builder-util"
-import { BintrayOptions, configureRequestOptions, HttpError, HttpExecutor } from "builder-util-runtime"
-import { BintrayClient, Version } from "builder-util-runtime/out/bintray"
-import { httpExecutor } from "builder-util/out/nodeHttpExecutor"
-import { ClientRequest, RequestOptions } from "http"
-import { Lazy } from "lazy-val"
-import { HttpPublisher, PublishContext, PublishOptions } from "electron-publish"
-
-export class BintrayPublisher extends HttpPublisher {
- private readonly _versionPromise = new Lazy(() => this.init())
-
- private readonly client: BintrayClient
-
- readonly providerName = "Bintray"
-
- constructor(context: PublishContext, info: BintrayOptions, private readonly version: string, private readonly options: PublishOptions = {}) {
- super(context)
-
- let token = info.token
- if (isEmptyOrSpaces(token)) {
- token = process.env.BT_TOKEN
- if (isEmptyOrSpaces(token)) {
- throw new InvalidConfigurationError(
- `Bintray token is not set, neither programmatically, nor using env "BT_TOKEN" (see https://www.electron.build/configuration/publish#bintrayoptions)`
- )
- }
-
- token = token.trim()
-
- if (!isTokenCharValid(token)) {
- throw new InvalidConfigurationError(`Bintray token (${JSON.stringify(token)}) contains invalid characters, please check env "BT_TOKEN"`)
- }
- }
-
- this.client = new BintrayClient(info, httpExecutor, this.context.cancellationToken, token)
- }
-
- private async init(): Promise {
- try {
- return await this.client.getVersion(this.version)
- } catch (e) {
- if (e instanceof HttpError && e.statusCode === 404) {
- if (this.options.publish !== "onTagOrDraft") {
- log.info({ version: this.version }, "version doesn't exist, creating one")
- return await this.client.createVersion(this.version)
- } else {
- log.warn({ reason: "version doesn't exist", version: this.version }, "skipped publishing")
- }
- }
-
- throw e
- }
- }
-
- protected async doUpload(fileName: string, arch: Arch, dataLength: number, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void) {
- const version = await this._versionPromise.value
- if (version == null) {
- log.warn({ file: fileName, reason: "version doesn't exist and is not created", version: this.version }, "skipped publishing")
- return
- }
-
- const options: RequestOptions = {
- hostname: "api.bintray.com",
- path: `/content/${this.client.owner}/${this.client.repo}/${this.client.packageName}/${encodeURI(`${version.name}/${fileName}`)}`,
- method: "PUT",
- headers: {
- "Content-Length": dataLength,
- "X-Bintray-Override": "1",
- "X-Bintray-Publish": "1",
- "X-Bintray-Debian-Architecture": toLinuxArchString(arch, "deb"),
- },
- }
-
- if (this.client.distribution != null) {
- options.headers!["X-Bintray-Debian-Distribution"] = this.client.distribution
- }
-
- if (this.client.component != null) {
- options.headers!["X-Bintray-Debian-Component"] = this.client.component
- }
-
- return HttpExecutor.retryOnServerError(() => {
- return httpExecutor.doApiRequest(configureRequestOptions(options, this.client.auth), this.context.cancellationToken, requestProcessor)
- })
- }
-
- //noinspection JSUnusedGlobalSymbols
- async deleteRelease(isForce = false): Promise {
- if (!isForce && !this._versionPromise.hasValue) {
- return
- }
-
- const version = await this._versionPromise.value
- if (version != null) {
- await this.client.deleteVersion(version.name)
- }
- }
-
- toString() {
- return `Bintray (user: ${this.client.user || this.client.owner}, owner: ${this.client.owner}, package: ${this.client.packageName}, repository: ${this.client.repo}, version: ${
- this.version
- })`
- }
-}
diff --git a/packages/app-builder-lib/src/publish/PublishManager.ts b/packages/app-builder-lib/src/publish/PublishManager.ts
index 7a95f4a99b5..20a59a7b543 100644
--- a/packages/app-builder-lib/src/publish/PublishManager.ts
+++ b/packages/app-builder-lib/src/publish/PublishManager.ts
@@ -1,7 +1,6 @@
import BluebirdPromise from "bluebird-lst"
import { Arch, asArray, AsyncTaskManager, InvalidConfigurationError, isEmptyOrSpaces, isPullRequest, log, safeStringifyJson, serializeToYaml } from "builder-util"
import {
- BintrayOptions,
CancellationToken,
GenericServerOptions,
getS3LikeProviderBaseUrl,
@@ -11,10 +10,10 @@ import {
SnapStoreOptions,
PublishConfiguration,
PublishProvider,
+ BitbucketOptions,
} from "builder-util-runtime"
import _debug from "debug"
import { getCiTag, PublishContext, Publisher, PublishOptions, UploadTask } from "electron-publish"
-import { BintrayPublisher } from "./BintrayPublisher"
import { GitHubPublisher } from "electron-publish/out/gitHubPublisher"
import { MultiProgress } from "electron-publish/out/multiProgress"
import S3Publisher from "./s3/s3Publisher"
@@ -163,7 +162,7 @@ export class PublishManager implements PublishContext {
}
const providerName = publisher.providerName
- if (this.publishOptions.publish === "onTagOrDraft" && getCiTag() == null && !(providerName === "GitHub" || providerName === "Bintray")) {
+ if (this.publishOptions.publish === "onTagOrDraft" && getCiTag() == null && providerName !== "bitbucket" && providerName !== "github") {
log.info({ file: event.file, reason: "current build is not for a git tag", publishPolicy: "onTagOrDraft" }, `not published to ${providerName}`)
return
}
@@ -298,9 +297,6 @@ export function createPublisher(context: PublishContext, version: string, publis
case "github":
return new GitHubPublisher(context, publishConfig as GithubOptions, version, options)
- case "bintray":
- return new BintrayPublisher(context, publishConfig as BintrayOptions, version, options)
-
case "keygen":
return new KeygenPublisher(context, publishConfig as KeygenOptions, version)
@@ -322,9 +318,6 @@ function requireProviderClass(provider: string, packager: Packager): any | null
case "github":
return GitHubPublisher
- case "bintray":
- return BintrayPublisher
-
case "generic":
return null
@@ -430,12 +423,14 @@ async function resolvePublishConfigurations(
let serviceName: PublishProvider | null = null
if (!isEmptyOrSpaces(process.env.GH_TOKEN) || !isEmptyOrSpaces(process.env.GITHUB_TOKEN)) {
serviceName = "github"
- } else if (!isEmptyOrSpaces(process.env.BT_TOKEN)) {
- serviceName = "bintray"
} else if (!isEmptyOrSpaces(process.env.KEYGEN_TOKEN)) {
serviceName = "keygen"
} else if (!isEmptyOrSpaces(process.env.BITBUCKET_TOKEN)) {
serviceName = "bitbucket"
+ } else if (!isEmptyOrSpaces(process.env.BT_TOKEN)) {
+ throw new Error(
+ "Bintray has been sunset and is no longer supported by electron-builder. Ref: https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/"
+ )
}
if (serviceName != null) {
@@ -485,7 +480,7 @@ async function getResolvedPublishConfig(
options: PublishConfiguration,
arch: Arch | null,
errorIfCannot: boolean
-): Promise {
+): Promise {
options = { ...options }
expandPublishConfig(options, platformPackager, packager, arch)
@@ -524,12 +519,12 @@ async function getResolvedPublishConfig(
}
const isGithub = provider === "github"
- if (!isGithub && provider !== "bintray") {
+ if (!isGithub && provider !== "bitbucket") {
return options
}
- let owner = isGithub ? (options as GithubOptions).owner : (options as BintrayOptions).owner
- let project = isGithub ? (options as GithubOptions).repo : (options as BintrayOptions).package
+ let owner = isGithub ? (options as GithubOptions).owner : (options as BitbucketOptions).owner
+ let project = isGithub ? (options as GithubOptions).repo : (options as BitbucketOptions).slug
if (isGithub && owner == null && project != null) {
const index = project.indexOf("/")
@@ -578,6 +573,6 @@ async function getResolvedPublishConfig(
return { owner, repo: project, ...options } as GithubOptions
} else {
//tslint:disable-next-line:no-object-literal-type-assertion
- return { owner, package: project, ...options } as BintrayOptions
+ return { owner, slug: project, ...options } as BitbucketOptions
}
}
diff --git a/packages/app-builder-lib/src/publish/s3/s3Publisher.ts b/packages/app-builder-lib/src/publish/s3/s3Publisher.ts
index 30c5e78839a..c751a3877fb 100644
--- a/packages/app-builder-lib/src/publish/s3/s3Publisher.ts
+++ b/packages/app-builder-lib/src/publish/s3/s3Publisher.ts
@@ -4,7 +4,7 @@ import { PublishContext } from "electron-publish"
import { BaseS3Publisher } from "./BaseS3Publisher"
export default class S3Publisher extends BaseS3Publisher {
- readonly providerName = "S3"
+ readonly providerName = "s3"
constructor(context: PublishContext, private readonly info: S3Options) {
super(context, info)
diff --git a/packages/app-builder-lib/src/publish/s3/spacesPublisher.ts b/packages/app-builder-lib/src/publish/s3/spacesPublisher.ts
index d30380e7c20..192f90360c6 100644
--- a/packages/app-builder-lib/src/publish/s3/spacesPublisher.ts
+++ b/packages/app-builder-lib/src/publish/s3/spacesPublisher.ts
@@ -4,7 +4,7 @@ import { PublishContext } from "electron-publish"
import { BaseS3Publisher } from "./BaseS3Publisher"
export default class SpacesPublisher extends BaseS3Publisher {
- readonly providerName = "Spaces"
+ readonly providerName = "spaces"
constructor(context: PublishContext, private readonly info: SpacesOptions) {
super(context, info)
diff --git a/packages/app-builder-lib/src/publish/updateInfoBuilder.ts b/packages/app-builder-lib/src/publish/updateInfoBuilder.ts
index 4297627e059..cf76837b250 100644
--- a/packages/app-builder-lib/src/publish/updateInfoBuilder.ts
+++ b/packages/app-builder-lib/src/publish/updateInfoBuilder.ts
@@ -113,10 +113,8 @@ export async function createUpdateInfoTasks(event: ArtifactCreated, _publishConf
const tasks: Array = []
const electronUpdaterCompatibility = packager.platformSpecificBuildOptions.electronUpdaterCompatibility || packager.config.electronUpdaterCompatibility || ">=2.15"
for (const publishConfiguration of publishConfigs) {
- const isBintray = publishConfiguration.provider === "bintray"
let dir = outDir
- // Bintray uses different variant of channel file info, better to generate it to a separate dir by always
- if (isBintray || (publishConfigs.length > 1 && publishConfiguration !== publishConfigs[0])) {
+ if (publishConfigs.length > 1 && publishConfiguration !== publishConfigs[0]) {
dir = path.join(outDir, publishConfiguration.provider)
}
@@ -149,7 +147,7 @@ export async function createUpdateInfoTasks(event: ArtifactCreated, _publishConf
await writeOldMacInfo(publishConfiguration, outDir, dir, channel, createdFiles, version, packager)
}
- const updateInfoFile = path.join(dir, (isBintray ? `${version}_` : "") + getUpdateInfoFileName(channel, packager, event.arch))
+ const updateInfoFile = path.join(dir, getUpdateInfoFileName(channel, packager, event.arch))
if (createdFiles.has(updateInfoFile)) {
continue
}
diff --git a/packages/app-builder-lib/src/targets/MsiTarget.ts b/packages/app-builder-lib/src/targets/MsiTarget.ts
index 8624d95e13d..ea7c1de4ac6 100644
--- a/packages/app-builder-lib/src/targets/MsiTarget.ts
+++ b/packages/app-builder-lib/src/targets/MsiTarget.ts
@@ -1,5 +1,5 @@
import BluebirdPromise from "bluebird-lst"
-import { Arch, log, deepAssign } from "builder-util"
+import { Arch, asArray, log, deepAssign } from "builder-util"
import { UUID } from "builder-util-runtime"
import { getBinFromUrl } from "../binDownload"
import { walk } from "builder-util/out/fs"
@@ -11,6 +11,7 @@ import * as path from "path"
import { MsiOptions } from "../"
import { Target } from "../core"
import { DesktopShortcutCreationPolicy, FinalCommonWindowsInstallerOptions, getEffectiveOptions } from "../options/CommonWindowsInstallerConfiguration"
+import { normalizeExt } from "../platformPackager"
import { getTemplatePath } from "../util/pathManager"
import { VmManager } from "../vm/vm"
import { WineVmManager } from "../vm/WineVm"
@@ -20,8 +21,6 @@ import { createStageDir, getWindowsInstallationDirName } from "./targetUtil"
const ELECTRON_BUILDER_UPGRADE_CODE_NS_UUID = UUID.parse("d752fe43-5d44-44d5-9fc9-6dd1bf19d5cc")
const ROOT_DIR_ID = "APPLICATIONFOLDER"
-const ASSISTED_UI_FILE_NAME = "WixUI_Assisted.wxs"
-
const projectTemplate = new Lazy<(data: any) => string>(async () => {
const template = (await readFile(path.join(getTemplatePath("msi"), "template.xml"), "utf8"))
.replace(/{{/g, "<%")
@@ -40,6 +39,22 @@ export default class MsiTarget extends Target {
super("msi")
}
+ /**
+ * A product-specific string that can be used in an [MSI Identifier](https://docs.microsoft.com/en-us/windows/win32/msi/identifier).
+ */
+ private get productMsiIdPrefix() {
+ const sanitizedId = this.packager.appInfo.productFilename.replace(/[^\w.]/g, "").replace(/^[^A-Za-z_]+/, "")
+ return sanitizedId.length > 0 ? sanitizedId : "App" + this.upgradeCode.replace(/-/g, "")
+ }
+
+ private get iconId() {
+ return `${this.productMsiIdPrefix}Icon.exe`
+ }
+
+ private get upgradeCode(): string {
+ return (this.options.upgradeCode || UUID.v5(this.packager.appInfo.id, ELECTRON_BUILDER_UPGRADE_CODE_NS_UUID)).toUpperCase()
+ }
+
async build(appOutDir: string, arch: Arch) {
const packager = this.packager
const artifactName = packager.expandArtifactBeautyNamePattern(this.options, "msi", arch)
@@ -55,20 +70,9 @@ export default class MsiTarget extends Target {
const commonOptions = getEffectiveOptions(this.options, this.packager)
- if (commonOptions.isAssisted) {
- // F*** *** *** *** *** *** *** *** *** *** *** *** *** WiX *** *** *** *** *** *** *** *** ***
- // cannot understand how to set MSIINSTALLPERUSER on radio box change. In any case installed per user.
- log.warn(`MSI DOESN'T SUPPORT assisted installer. Please use NSIS instead.`)
- }
-
const projectFile = stageDir.getTempFile("project.wxs")
const objectFiles = ["project.wixobj"]
- const uiFile = commonOptions.isAssisted ? stageDir.getTempFile(ASSISTED_UI_FILE_NAME) : null
await writeFile(projectFile, await this.writeManifest(appOutDir, arch, commonOptions))
- if (uiFile !== null) {
- await writeFile(uiFile, await readFile(path.join(getTemplatePath("msi"), ASSISTED_UI_FILE_NAME), "utf8"))
- objectFiles.push(ASSISTED_UI_FILE_NAME.replace(".wxs", ".wixobj"))
- }
await packager.info.callMsiProjectCreated(projectFile)
@@ -78,9 +82,6 @@ export default class MsiTarget extends Target {
// noinspection SpellCheckingInspection
const candleArgs = ["-arch", arch === Arch.ia32 ? "x86" : arch === Arch.arm64 ? "arm64" : "x64", `-dappDir=${vm.toVmFile(appOutDir)}`].concat(this.getCommonWixArgs())
candleArgs.push("project.wxs")
- if (uiFile !== null) {
- candleArgs.push(ASSISTED_UI_FILE_NAME)
- }
await vm.exec(vm.toVmFile(path.join(vendorPath, "candle.exe")), candleArgs, {
cwd: stageDir.dir,
})
@@ -156,23 +157,22 @@ export default class MsiTarget extends Target {
const compression = this.packager.compression
const options = this.options
const iconPath = await this.packager.getIconPath()
- const iconId = `${appInfo.productFilename}Icon.exe`.replace(/\s/g, "")
return (await projectTemplate.value)({
...commonOptions,
isCreateDesktopShortcut: commonOptions.isCreateDesktopShortcut !== DesktopShortcutCreationPolicy.NEVER,
isRunAfterFinish: options.runAfterFinish !== false,
iconPath: iconPath == null ? null : this.vm.toVmFile(iconPath),
- iconId: iconId,
+ iconId: this.iconId,
compressionLevel: compression === "store" ? "none" : "high",
version: appInfo.getVersionInWeirdWindowsForm(),
productName: appInfo.productName,
- upgradeCode: (options.upgradeCode || UUID.v5(appInfo.id, ELECTRON_BUILDER_UPGRADE_CODE_NS_UUID)).toUpperCase(),
+ upgradeCode: this.upgradeCode,
manufacturer: companyName || appInfo.productName,
appDescription: appInfo.description,
// https://stackoverflow.com/questions/1929038/compilation-error-ice80-the-64bitcomponent-uses-32bitdirectory
programFilesId: arch === Arch.x64 ? "ProgramFiles64Folder" : "ProgramFilesFolder",
// wix in the name because special wix format can be used in the name
- installationDirectoryWixName: getWindowsInstallationDirName(appInfo, commonOptions.isPerMachine === true),
+ installationDirectoryWixName: getWindowsInstallationDirName(appInfo, commonOptions.isAssisted || commonOptions.isPerMachine === true),
dirs,
files,
})
@@ -223,9 +223,8 @@ export default class MsiTarget extends Target {
if (isMainExecutable && (isCreateDesktopShortcut || commonOptions.isCreateStartMenuShortcut)) {
result += `>\n`
const shortcutName = commonOptions.shortcutName
- const iconId = `${appInfo.productFilename}Icon.exe`.replace(/\s/g, "")
if (isCreateDesktopShortcut) {
- result += `${fileSpace} \n`
+ result += `${fileSpace} \n`
}
const hasMenuCategory = commonOptions.menuCategory != null
@@ -234,7 +233,7 @@ export default class MsiTarget extends Target {
if (hasMenuCategory) {
dirs.push(``)
}
- result += `${fileSpace}