Skip to content

Commit

Permalink
feat(maker): add support for Windows Store (AppX) packages
Browse files Browse the repository at this point in the history
This PR adds a new make type for Windows, "appx", which generates Windows Store packages

ISSUES CLOSED: #27
  • Loading branch information
anaisbetts authored and malept committed Jan 2, 2017
1 parent ac7a20b commit 74a1216
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -123,6 +123,7 @@ config object:
|-------------|---------------------|-------------|----------------------|----------|--------------|
| `zip` | All | Zips your packaged application | None | Yes | `zip` on Darwin/Linux |
| `squirrel` | Windows | Generates an installer and `.nupkg` files for Squirrel.Windows | [`electronWinstallerConfig`](https://github.com/electron/windows-installer#usage) | Yes | |
| `appx` | Windows | Generates a Windows Store package | [`windowsStoreConfig`](https://github.com/felixrieseberg/electron-windows-store#programmatic-usage) | No | |
| `dmg` | Darwin | Generates a DMG file | [`electronInstallerDMG`](https://github.com/mongodb-js/electron-installer-dmg#api) | No | |
| `deb` | Linux | Generates a Debian package | [`electronInstallerDebian`](https://github.com/unindented/electron-installer-debian#options) | Yes | [`fakeroot` and `dpkg`](https://github.com/unindented/electron-installer-debian#requirements) |
| `rpm` | Linux | Generates an RPM package | [`electronInstallerRedhat`](https://github.com/unindented/electron-installer-redhat#options) | Yes | [`rpm`](https://github.com/unindented/electron-installer-redhatn#requirements) |
Expand Down
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -67,6 +67,7 @@
"electron-installer-dmg": "^0.1.2",
"electron-packager": "^8.4.0",
"electron-sudo": "malept/electron-sudo#fix-linux-sudo-detection",
"electron-windows-store": "^0.6.3",
"electron-winstaller": "^2.5.0",
"fs-promise": "^1.0.0",
"github": "^7.2.0",
Expand All @@ -83,6 +84,7 @@
"resolve-package": "^1.0.1",
"semver": "^5.3.0",
"sudo-prompt": "^6.2.1",
"spawn-rx": "^2.0.7",
"username": "^2.2.2",
"yarn-or-npm": "^2.0.2",
"zip-folder": "^1.0.0"
Expand Down
Binary file added res/default.pvk
Binary file not shown.
1 change: 1 addition & 0 deletions src/init/init-npm.js
Expand Up @@ -21,6 +21,7 @@ export default async (dir, lintStyle) => {
const packageJSON = await readPackageJSON(path.resolve(__dirname, '../../tmpl'));
packageJSON.productName = packageJSON.name = path.basename(dir).toLowerCase();
packageJSON.config.forge.electronWinstallerConfig.name = packageJSON.name.replace(/-/g, '_');
packageJSON.config.forge.windowsStoreConfig.name = packageJSON.productName.replace(/-/g, '');
packageJSON.author = await username();

switch (lintStyle) {
Expand Down
88 changes: 88 additions & 0 deletions src/makers/win32/appx.js
@@ -0,0 +1,88 @@
import windowsStore from 'electron-windows-store';
import fs from 'fs';
import path from 'path';
import { spawnPromise, findActualExecutable } from 'spawn-rx';

import { ensureDirectory } from '../../util/ensure-output';

// NB: This is not a typo, we require AppXs to be built on 64-bit
// but if we're running in a 32-bit node.js process, we're going to
// be Wow64 redirected
const windowsSdkPath = process.arch === 'x64' ?
'C:\\Program Files (x86)\\Windows Kits\\10\\bin\\x64' :
'C:\\Program Files\\Windows Kits\\10\\bin\\x64';

function findSdkTool(exe) {
let sdkTool = path.join(windowsSdkPath, exe);
if (!fs.existsSync(sdkTool)) {
sdkTool = findActualExecutable(exe, []).cmd;
}

if (!fs.existsSync(sdkTool)) {
throw new Error(`Can't find ${exe} in PATH, you probably need to install the Windows SDK`);
}

return sdkTool;
}

function spawnSdkTool(exe, params) {
return spawnPromise(findSdkTool(exe), params);
}

export async function createDefaultCertificate(publisherName, outPath) {
const defaultPvk = path.resolve(__dirname, '..', '..', '..', 'res', 'default.pvk');
const targetCert = path.join(outPath, 'default.cer');
const targetPfx = path.join(outPath, 'default.pfx');

await spawnSdkTool(
'makecert.exe',
['-r', '-h', '0', '-n', `CN=${publisherName}`, '-eku', '1.3.6.1.5.5.7.3.3', '-pe', '-sv', defaultPvk, targetCert]);

await spawnSdkTool('pvk2pfx.exe', ['-pvk', defaultPvk, '-spc', targetCert, '-pfx', targetPfx]);

return targetPfx;
}

export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { // eslint-disable-line
const outPath = path.resolve(dir, `../make/appx/${targetArch}`);
await ensureDirectory(outPath);

const opts = Object.assign({}, forgeConfig.windowsStoreConfig, {

This comment has been minimized.

Copy link
@jacobq

jacobq Mar 7, 2017

Contributor

Why is the forgeConfig overridden by package.json? For example, I would like to be able to set opts.publisher separately from the author field in my package.json configuration.

This comment has been minimized.

Copy link
@malept

malept Mar 7, 2017

Member

See #121 - it's currently blocked by failing tests which I can't fix.

inputDirectory: dir,
outputDirectory: outPath,
publisher: packageJSON.author,
flatten: false,
deploy: false,
packageVersion: `${packageJSON.version}.0`,
packageName: appName.replace(/-/g, ''),
packageDisplayName: appName,
packageDescription: packageJSON.description || appName,
packageExecutable: `app\\${appName}.exe`,
windowsKit: path.dirname(findSdkTool('makeappx.exe')),
});

if (!opts.devCert) {
opts.devCert = await createDefaultCertificate(opts.publisher, outPath);
}

if (!opts.publisher.match(/^CN=/)) {

This comment has been minimized.

Copy link
@jacobq

jacobq Mar 6, 2017

Contributor

This assumes the author field in package.json is a string (or similar object having .match function), but this is not always the case. For example, it can be a plain object with string properties name, email, and url. See https://docs.npmjs.com/files/package.json#people-fields-author-contributors

This comment has been minimized.

Copy link
@malept

malept Mar 6, 2017

Member

@jacobq Thanks for catching this bug! Do you mind filing a PR to fix it?

This comment has been minimized.

Copy link
@jacobq

jacobq Mar 6, 2017

Contributor

Sure, I'll try. I'm currently using the isleofcode fork of this project for ember-electron

This comment has been minimized.

Copy link
@malept

malept Mar 6, 2017

Member

Filed #160 so this bug isn't lost.

opts.publisher = `CN=${opts.publisher}`;
}

if (opts.packageVersion.match(/-/)) {
if (opts.makeVersionWinStoreCompatible) {
const noBeta = opts.packageVersion.replace(/-.*/, '');
opts.packageVersion = `${noBeta}.0`;
} else {
const err = "Windows Store version numbers don't support semver beta tags. To" +
'automatically fix this, set makeVersionWinStoreCompatible to true or ' +
'explicitly set packageVersion to a version of the format X.Y.Z.A';

throw new Error(err);
}
}

delete opts.makeVersionWinStoreCompatible;

await windowsStore(opts);
};
5 changes: 3 additions & 2 deletions test/fixture/dummy_app/package.json
Expand Up @@ -13,14 +13,15 @@
"config": {
"forge": {
"make_targets": {
"win32": ["squirrel"],
"win32": ["squirrel", "appx"],
"darwin": ["zip"],
"linux": ["deb", "rpm"]
},
"electronPackagerConfig": {},
"electronInstallerRedhat": {},
"electronInstallerDebian": {},
"electronWinstallerConfig": { "windows": "magic" }
"electronWinstallerConfig": { "windows": "magic" },
"windowsStoreConfig": { "packageName": "test" }
}
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion test/fixture/dummy_js_conf/forge.config.js
@@ -1,6 +1,6 @@
module.exports = {
make_targets: {
win32: ['squirrel'],
win32: ['squirrel', 'appx'],
darwin: ['zip'],
linux: ['deb', 'rpm'],
mas: ['zip'],
Expand Down
3 changes: 2 additions & 1 deletion test/util_spec.js
Expand Up @@ -18,7 +18,7 @@ describe('resolve-dir', () => {

const defaults = {
make_targets: {
win32: ['squirrel'],
win32: ['squirrel', 'appx'],
darwin: ['zip'],
linux: ['deb', 'rpm'],
mas: ['zip'],
Expand All @@ -34,6 +34,7 @@ describe('forge-config', () => {
it('should resolve the object in package.json with defaults if one exists', async () => {
expect(await findConfig(path.resolve(__dirname, 'fixture/dummy_app'))).to.be.deep.equal(Object.assign({}, defaults, {
electronWinstallerConfig: { windows: 'magic' },
windowsStoreConfig: { packageName: 'test' },
}));
});

Expand Down
6 changes: 4 additions & 2 deletions tmpl/package.json
Expand Up @@ -24,8 +24,10 @@
"electronInstallerDebian": {},
"electronInstallerRedhat": {},
"github_repository": {
"owner": "",
"name": ""
"owner": ""
},
"windowsStoreConfig": {
"packageName": ""
}
}
}
Expand Down

0 comments on commit 74a1216

Please sign in to comment.