# Electron Installer [![CircleCI](https://circleci.com/gh/electron/windows-installer.svg?style=shield)](https://circleci.com/gh/electron/windows-installer) [![NPM package](https://img.shields.io/npm/v/electron-winstaller)](https://npm.im/electron-winstaller) NPM module that builds Windows installers for [Electron](https://github.com/electron/electron) apps using [Squirrel](https://github.com/Squirrel/Squirrel.Windows). ## Installing ```sh npm install --save-dev electron-winstaller ``` ## Usage Require the package: ```javascript const electronInstaller = require('electron-winstaller'); ``` Then do a build like so.. ```javascript // NB: Use this syntax within an async function, Node does not have support for // top-level await as of Node 12. try { await electronInstaller.createWindowsInstaller({ appDirectory: '/tmp/build/my-app-64', outputDirectory: '/tmp/build/installer64', authors: 'My App Inc.', exe: 'myapp.exe' }); console.log('It worked!'); } catch (e) { console.log(`No dice: ${e.message}`); } ``` After running you will have an `.nupkg`, a `RELEASES` file, and a `.exe` installer file in the `outputDirectory` folder for each multi task target given under the config entry. There are several configuration settings supported: | Config Name | Required | Description | | --------------------- | -------- | ----------- | | `appDirectory` | Yes | The folder path of your Electron app | | `outputDirectory` | No | The folder path to create the `.exe` installer in. Defaults to the `installer` folder at the project root. | | `loadingGif` | No | The local path to a `.gif` file to display during install. | | `authors` | Yes | The authors value for the nuget package metadata. Defaults to the `author` field from your app's package.json file when unspecified. | | `owners` | No | The owners value for the nuget package metadata. Defaults to the `authors` field when unspecified. | | `exe` | No | The name of your app's main `.exe` file. This uses the `name` field in your app's package.json file with an added `.exe` extension when unspecified. | | `description` | No | The description value for the nuget package metadata. Defaults to the `description` field from your app's package.json file when unspecified. | | `version` | No | The version value for the nuget package metadata. Defaults to the `version` field from your app's package.json file when unspecified. | | `title` | No | The title value for the nuget package metadata. Defaults to the `productName` field and then the `name` field from your app's package.json file when unspecified. | | `name` | No | Windows Application Model ID (appId). Defaults to the `name` field in your app's package.json file. | | `certificateFile` | No | The path to an Authenticode Code Signing Certificate | | `certificatePassword` | No | The password to decrypt the certificate given in `certificateFile` | | `signWithParams` | No | Params to pass to signtool. Overrides `certificateFile` and `certificatePassword`. | | `iconUrl` | No | A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Atom icon. | | `setupIcon` | No | The ICO file to use as the icon for the generated Setup.exe | | `skipUpdateIcon` | No | Disables setting the icon of `Update.exe`. This can solve installation errors with the following message: "This application could not be started", when the setup is built on a non-Windows system. | | `setupExe` | No | The name to use for the generated Setup.exe file | | `setupMsi` | No | The name to use for the generated Setup.msi file | | `noMsi` | No | Should Squirrel.Windows create an MSI installer? | | `noDelta` | No | Should Squirrel.Windows delta packages? (disable only if necessary, they are a Good Thing) | | `remoteReleases` | No | A URL to your existing updates. If given, these will be downloaded to create delta updates | | `remoteToken` | No | Authentication token for remote updates | | `frameworkVersion` | No | Set the required .NET framework version, e.g. `net461` | | `windowsSign` | No | Use [@electron/windows-sign][@electron/windows-sign] for advanced codesigning. See [documentation](#advanced-codesigning-with-electronwindows-sign) for details. | ## Sign your installer or else bad things will happen For development / internal use, creating installers without a signature is okay, but for a production app you need to sign your application. Internet Explorer's SmartScreen filter will block your app from being downloaded, and many anti-virus vendors will consider your app as malware unless you obtain a valid cert. Any certificate valid for "Authenticode Code Signing" will work here, but if you get the right kind of code certificate, you can also opt-in to [Windows Error Reporting](http://en.wikipedia.org/wiki/Windows_Error_Reporting). [This MSDN page](http://msdn.microsoft.com/en-us/library/windows/hardware/hh801887.aspx) has the latest links on where to get a WER-compatible certificate. The "Standard Code Signing" certificate is sufficient for this purpose. ## Handling Squirrel Events Squirrel will spawn your app with command line flags on first run, updates, and uninstalls. it is **very** important that your app handle these events as _early_ as possible, and quit **immediately** after handling them. Squirrel will give your app a short amount of time (~15sec) to apply these operations and quit. The [electron-squirrel-startup](https://github.com/mongodb-js/electron-squirrel-startup) module will handle the most common events for you, such as managing desktop shortcuts. Add the following to the top of your `main.js` and you're good to go: ```javascript if (require('electron-squirrel-startup')) return; ``` You should handle these events in your app's `main` entry point with something such as: ```javascript const app = require('app'); // this should be placed at top of main.js to handle setup events quickly if (handleSquirrelEvent()) { // squirrel event handled and app will exit in 1000ms, so don't do anything else return; } function handleSquirrelEvent() { if (process.argv.length === 1) { return false; } const ChildProcess = require('child_process'); const path = require('path'); const appFolder = path.resolve(process.execPath, '..'); const rootAtomFolder = path.resolve(appFolder, '..'); const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe')); const exeName = path.basename(process.execPath); const spawn = function(command, args) { let spawnedProcess, error; try { spawnedProcess = ChildProcess.spawn(command, args, {detached: true}); } catch (error) {} return spawnedProcess; }; const spawnUpdate = function(args) { return spawn(updateDotExe, args); }; const squirrelEvent = process.argv[1]; switch (squirrelEvent) { case '--squirrel-install': case '--squirrel-updated': // Optionally do things such as: // - Add your .exe to the PATH // - Write to the registry for things like file associations and // explorer context menus // Install desktop and start menu shortcuts spawnUpdate(['--createShortcut', exeName]); setTimeout(app.quit, 1000); return true; case '--squirrel-uninstall': // Undo anything you did in the --squirrel-install and // --squirrel-updated handlers // Remove desktop and start menu shortcuts spawnUpdate(['--removeShortcut', exeName]); setTimeout(app.quit, 1000); return true; case '--squirrel-obsolete': // This is called on the outgoing version of your app before // we update to the new version - it's the opposite of // --squirrel-updated app.quit(); return true; } }; ``` Notice that the first time the installer launches your app, your app will see a `--squirrel-firstrun` flag. This allows you to do things like showing up a splash screen or presenting a settings UI. Another thing to be aware of is that, since the app is spawned by squirrel and squirrel acquires a file lock during installation, you won't be able to successfully check for app updates till a few seconds later when squirrel releases the lock. ## Advanced codesigning with [@electron/windows-sign][@electron/windows-sign] This package supports two different ways to codesign your application and the installer: 1) Modern: By passing a `windowsSign` option, which will be passed to [@electron/windows-sign]. This method allows full customization of the code-signing process - and supports more complicated scenarios like cloud-hosted EV certificates, custom sign pipelines, and per-file overrides. It also supports all existing "simple" codesigning scenarios, including just passing a certificate file and password. Please see https://github.com/@electron/windows-sign for all possible configuration options. When passing `windowsSign`, do not pass any other available parameters at the top level (like `certificateFile`, `certificatePassword`, or `signWithParams`). 2) Legacy: By passing the top-level settings (`certificateFile`, `certificatePassword`, and `signWithParams`). For simple codesigning scenarios, there's no reason not to use this method - it'll work just as fine as the modern method. ## Debugging this package You can get debug messages from this package by running with the environment variable `DEBUG=electron-windows-installer:main` e.g. ```shell DEBUG=electron-windows-installer:main node tasks/electron-winstaller.js ``` [@electron/windows-sign]: https://github.com/electron/windows-sign/