From a3a23f2f7f6c410ce7d052537e4262e1b0bc1790 Mon Sep 17 00:00:00 2001 From: develar Date: Sat, 17 Jun 2017 20:48:14 +0200 Subject: [PATCH] feat(pkg): build pkg that doesn't require admin install --- docs/Options.md | 13 +++++-- packages/electron-builder/src/metadata.ts | 3 -- .../src/options/macOptions.ts | 35 ++++++++++++++++++- packages/electron-builder/src/targets/pkg.ts | 25 +++++++++---- .../mac/__snapshots__/macArchiveTest.js.snap | 19 +++++++++- test/src/mac/macArchiveTest.ts | 12 +++++++ 6 files changed, 93 insertions(+), 14 deletions(-) diff --git a/docs/Options.md b/docs/Options.md index 893c9c09fe4..98cb3995097 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -220,8 +220,17 @@ Configuration Options * `publish` String | [GithubOptions](Publishing-Artifacts#GithubOptions) | [S3Options](Publishing-Artifacts#S3Options) | [GenericServerOptions](Publishing-Artifacts#GenericServerOptions) | [BintrayOptions](Publishing-Artifacts#BintrayOptions) | Array * `pkg` - macOS product archive options. * `scripts` = `build/pkg-scripts` String - The scripts directory, relative to `build` (build resources directory). The scripts can be in any language so long as the files are marked executable and have the appropriate shebang indicating the path to the interpreter. Scripts are required to be executable (`chmod +x file`). See: [Scripting in installer packages](http://macinstallers.blogspot.de/2012/07/scripting-in-installer-packages.html). - * `installLocation` = `/Applications` String - The install location. - * `identity` String + * `installLocation` = `/Applications` String - The install location. [Do not use it](https://stackoverflow.com/questions/12863944/how-do-you-specify-a-default-install-location-to-home-with-pkgbuild) to create per-user package. Mostly never you will need to change this option. `/Applications` would install it as expected into `/Applications` if the local system domain is chosen, or into `$HOME/Applications` if the home installation is chosen. + * `allowAnywhere` = `true` Boolean - Whether can be installed at the root of any volume, including non-system volumes. Otherwise, it cannot be installed at the root of a volume. + + Corresponds to [enable_anywhere](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * `allowCurrentUserHome` = `true` Boolean - Whether can be installed into the current user’s home directory. A home directory installation is done as the current user (not as root), and it cannot write outside of the home directory. If the product cannot be installed in the user’s home directory and be not completely functional from user’s home directory. + + Corresponds to [enable_currentUserHome](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * `allowRootDirectory` = `true` Boolean - Whether can be installed into the root directory. Should usually be `true` unless the product can be installed only to the user’s home directory. + + Corresponds to [enable_localSystem](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * `identity` String - The name of certificate to use when signing. Consider using environment variables [CSC_LINK or CSC_NAME](https://github.com/electron-userland/electron-builder/wiki/Code-Signing) instead of specifying this option. * `artifactName` String - The [artifact file name pattern](https://github.com/electron-userland/electron-builder/wiki/Options#artifact-file-name-pattern). * `publish` String | [GithubOptions](Publishing-Artifacts#GithubOptions) | [S3Options](Publishing-Artifacts#S3Options) | [GenericServerOptions](Publishing-Artifacts#GenericServerOptions) | [BintrayOptions](Publishing-Artifacts#BintrayOptions) | Array * `win` - Windows options. diff --git a/packages/electron-builder/src/metadata.ts b/packages/electron-builder/src/metadata.ts index 0b5add2d0fd..36741359d0e 100644 --- a/packages/electron-builder/src/metadata.ts +++ b/packages/electron-builder/src/metadata.ts @@ -269,9 +269,6 @@ export interface Config extends PlatformSpecificBuildOptions { */ readonly dmg?: DmgOptions | null - /** - * macOS product archive options. - */ readonly pkg?: PkgOptions | null /** diff --git a/packages/electron-builder/src/options/macOptions.ts b/packages/electron-builder/src/options/macOptions.ts index b2540c9b832..a4511684d6e 100644 --- a/packages/electron-builder/src/options/macOptions.ts +++ b/packages/electron-builder/src/options/macOptions.ts @@ -77,6 +77,9 @@ export interface MacOptions extends PlatformSpecificBuildOptions { readonly requirements?: string | null } +/** + * macOS product archive options. + */ export interface PkgOptions extends TargetSpecificOptions { /** * The scripts directory, relative to `build` (build resources directory). @@ -94,11 +97,41 @@ export interface PkgOptions extends TargetSpecificOptions { readonly productbuild?: Array | null /** - * The install location. + * The install location. [Do not use it](https://stackoverflow.com/questions/12863944/how-do-you-specify-a-default-install-location-to-home-with-pkgbuild) to create per-user package. + * Mostly never you will need to change this option. `/Applications` would install it as expected into `/Applications` if the local system domain is chosen, or into `$HOME/Applications` if the home installation is chosen. * @default /Applications */ readonly installLocation?: string | null + /** + * Whether can be installed at the root of any volume, including non-system volumes. Otherwise, it cannot be installed at the root of a volume. + * + * Corresponds to [enable_anywhere](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * @default true + */ + readonly allowAnywhere?: boolean | null + + /** + * Whether can be installed into the current user’s home directory. + * A home directory installation is done as the current user (not as root), and it cannot write outside of the home directory. + * If the product cannot be installed in the user’s home directory and be not completely functional from user’s home directory. + * + * Corresponds to [enable_currentUserHome](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * @default true + */ + readonly allowCurrentUserHome?: boolean | null + + /** + * Whether can be installed into the root directory. Should usually be `true` unless the product can be installed only to the user’s home directory. + * + * Corresponds to [enable_localSystem](https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW70). + * @default true + */ + readonly allowRootDirectory?: boolean | null + + /** + * The name of certificate to use when signing. Consider using environment variables [CSC_LINK or CSC_NAME](https://github.com/electron-userland/electron-builder/wiki/Code-Signing) instead of specifying this option. + */ readonly identity?: string | null } diff --git a/packages/electron-builder/src/targets/pkg.ts b/packages/electron-builder/src/targets/pkg.ts index 005509d0544..bebf1f7900f 100644 --- a/packages/electron-builder/src/targets/pkg.ts +++ b/packages/electron-builder/src/targets/pkg.ts @@ -1,7 +1,7 @@ import BluebirdPromise from "bluebird-lst" -import { exec, use } from "electron-builder-util" +import { debug, exec, use } from "electron-builder-util" import { statOrNull } from "electron-builder-util/out/fs" -import { unlink } from "fs-extra-p" +import { readFile, unlink, writeFile } from "fs-extra-p" import * as path from "path" import { findIdentity, Identity } from "../codeSign" import { Arch, Target } from "../core" @@ -11,7 +11,11 @@ import { filterCFBundleIdentifier } from "../packager/mac" // http://www.shanekirk.com/2013/10/creating-flat-packages-in-osx/ export class PkgTarget extends Target { - readonly options: PkgOptions = this.packager.config.pkg || Object.create(null) + readonly options: PkgOptions = Object.assign({ + allowAnywhere: true, + allowCurrentUserHome: true, + allowRootDirectory: true, + }, this.packager.config.pkg) private readonly installLocation = this.options.installLocation || "/Applications" constructor(private readonly packager: MacPackager, readonly outDir: string) { @@ -31,11 +35,18 @@ export class PkgTarget extends Target { } const appOutDir = this.outDir - const distInfo = path.join(appOutDir, "distribution.xml") - await exec("productbuild", ["--synthesize", "--component", appPath, this.installLocation, distInfo], { + const distInfoFile = path.join(appOutDir, "distribution.xml") + await exec("productbuild", ["--synthesize", "--component", appPath, this.installLocation, distInfoFile], { cwd: appOutDir, }) + let distInfo = await readFile(distInfoFile, "utf-8") + const insertIndex = distInfo.lastIndexOf("") + distInfo = distInfo.substring(0, insertIndex) + ` \n` + distInfo.substring(insertIndex) + await writeFile(distInfoFile, distInfo) + + debug(distInfo) + // to use --scripts, we must build .app bundle separately using pkgbuild // productbuild --scripts doesn't work (because scripts in this case not added to our package) // https://github.com/electron-userland/electron-osx-sign/issues/96#issuecomment-274986942 @@ -44,7 +55,7 @@ export class PkgTarget extends Target { const outFile = path.join(appOutDir, packager.expandArtifactNamePattern(options, "pkg")) const args = prepareProductBuildArgs(identity, keychainName) - args.push("--distribution", distInfo) + args.push("--distribution", distInfoFile) args.push(outFile) use(options.productbuild, it => args.push(...it)) @@ -52,7 +63,7 @@ export class PkgTarget extends Target { await exec("productbuild", args, { cwd: appOutDir, }) - await BluebirdPromise.all([unlink(innerPackageFile), unlink(distInfo)]) + await BluebirdPromise.all([unlink(innerPackageFile), unlink(distInfoFile)]) packager.dispatchArtifactCreated(outFile, this, arch, `${appInfo.name}-${appInfo.version}.pkg`) } diff --git a/test/out/mac/__snapshots__/macArchiveTest.js.snap b/test/out/mac/__snapshots__/macArchiveTest.js.snap index f130feea044..c9b688b13c9 100644 --- a/test/out/mac/__snapshots__/macArchiveTest.js.snap +++ b/test/out/mac/__snapshots__/macArchiveTest.js.snap @@ -74,6 +74,18 @@ Object { `; exports[`pkg scripts 2`] = ` +Object { + "mac": Array [ + Object { + "arch": 1, + "file": "Test App ßW-1.1.0.pkg", + "safeArtifactName": "TestApp-1.1.0.pkg", + }, + ], +} +`; + +exports[`pkg scripts 3`] = ` Array [ "Test App ßW.app", "Test App ßW.app/Contents", @@ -399,7 +411,7 @@ Array [ ] `; -exports[`pkg scripts 3`] = ` +exports[`pkg scripts 4`] = ` Object { "choice": Array [ Object { @@ -425,6 +437,11 @@ Object { }, }, }, + "domains": Object { + "enable_anywhere": "true", + "enable_currentUserHome": "true", + "enable_localSystem": "true", + }, "minSpecVersion": "2", "options": Object { "customize": "never", diff --git a/test/src/mac/macArchiveTest.ts b/test/src/mac/macArchiveTest.ts index c99fd00c71a..5b1c4818997 100644 --- a/test/src/mac/macArchiveTest.ts +++ b/test/src/mac/macArchiveTest.ts @@ -15,8 +15,20 @@ test("only zip", createMacTargetTest(["zip"])); test("tar.gz", createMacTargetTest(["tar.gz"])) const it = process.env.CSC_KEY_PASSWORD == null ? test.skip : test.ifMac + it("pkg", createMacTargetTest(["pkg"])) +test.ifMac("pkg scripts", app({ + targets: Platform.MAC.createTarget("pkg"), + config: { + pkg: { + installLocation: "" + } + } +}, { + signed: false, +})) + test.ifMac("pkg scripts", app({ targets: Platform.MAC.createTarget("pkg"), }, {