Skip to content

Commit

Permalink
feat(pkg): build pkg that doesn't require admin install
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Jun 17, 2017
1 parent 86af4cd commit a3a23f2
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 14 deletions.
13 changes: 11 additions & 2 deletions docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,17 @@ Configuration Options
* <a name="DmgOptions-publish"></a>`publish` String | [GithubOptions](Publishing-Artifacts#GithubOptions) | [S3Options](Publishing-Artifacts#S3Options) | [GenericServerOptions](Publishing-Artifacts#GenericServerOptions) | [BintrayOptions](Publishing-Artifacts#BintrayOptions) | Array
* <a name="Config-pkg"></a>`pkg`<a name="PkgOptions"></a> - macOS product archive options.
* <a name="PkgOptions-scripts"></a>`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).
* <a name="PkgOptions-installLocation"></a>`installLocation` = `/Applications` String - The install location.
* <a name="PkgOptions-identity"></a>`identity` String
* <a name="PkgOptions-installLocation"></a>`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.
* <a name="PkgOptions-allowAnywhere"></a>`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).
* <a name="PkgOptions-allowCurrentUserHome"></a>`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).
* <a name="PkgOptions-allowRootDirectory"></a>`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).
* <a name="PkgOptions-identity"></a>`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.
* <a name="PkgOptions-artifactName"></a>`artifactName` String - The [artifact file name pattern](https://github.com/electron-userland/electron-builder/wiki/Options#artifact-file-name-pattern).
* <a name="PkgOptions-publish"></a>`publish` String | [GithubOptions](Publishing-Artifacts#GithubOptions) | [S3Options](Publishing-Artifacts#S3Options) | [GenericServerOptions](Publishing-Artifacts#GenericServerOptions) | [BintrayOptions](Publishing-Artifacts#BintrayOptions) | Array
* <a name="Config-win"></a>`win`<a name="WinBuildOptions"></a> - Windows options.
Expand Down
3 changes: 0 additions & 3 deletions packages/electron-builder/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,6 @@ export interface Config extends PlatformSpecificBuildOptions {
*/
readonly dmg?: DmgOptions | null

/**
* macOS product archive options.
*/
readonly pkg?: PkgOptions | null

/**
Expand Down
35 changes: 34 additions & 1 deletion packages/electron-builder/src/options/macOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -94,11 +97,41 @@ export interface PkgOptions extends TargetSpecificOptions {
readonly productbuild?: Array<string> | 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
}

Expand Down
25 changes: 18 additions & 7 deletions packages/electron-builder/src/targets/pkg.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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) {
Expand All @@ -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("</installer-gui-script>")
distInfo = distInfo.substring(0, insertIndex) + ` <domains enable_anywhere="${options.allowAnywhere}" enable_currentUserHome="${options.allowCurrentUserHome}" enable_localSystem="${options.allowRootDirectory}" />\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
Expand All @@ -44,15 +55,15 @@ 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(...<any>it))

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`)
}
Expand Down
19 changes: 18 additions & 1 deletion test/out/mac/__snapshots__/macArchiveTest.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -399,7 +411,7 @@ Array [
]
`;

exports[`pkg scripts 3`] = `
exports[`pkg scripts 4`] = `
Object {
"choice": Array [
Object {
Expand All @@ -425,6 +437,11 @@ Object {
},
},
},
"domains": Object {
"enable_anywhere": "true",
"enable_currentUserHome": "true",
"enable_localSystem": "true",
},
"minSpecVersion": "2",
"options": Object {
"customize": "never",
Expand Down
12 changes: 12 additions & 0 deletions test/src/mac/macArchiveTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}, {
Expand Down

0 comments on commit a3a23f2

Please sign in to comment.