diff --git a/.markdownlint.json b/.markdownlint.json index a1628ba622a37..495df656c2cd3 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -23,7 +23,5 @@ "br_spaces": 0 }, "single-h1": false, - "no-inline-html": { - "allowed_elements": ["br"] - } + "no-inline-html": false } diff --git a/docs/README.md b/docs/README.md index 261c67c5941ce..af7460e6b528b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -70,9 +70,6 @@ an issue: * [Windows Store](tutorial/windows-store-guide.md) * [Snapcraft](tutorial/snapcraft.md) * [Updates](tutorial/updates.md) - * [Deploying an Update Server](tutorial/updates.md#deploying-an-update-server) - * [Implementing Updates in Your App](tutorial/updates.md#implementing-updates-in-your-app) - * [Applying Updates](tutorial/updates.md#applying-updates) * [Getting Support](tutorial/support.md) ## Detailed Tutorials diff --git a/docs/fiddles/tutorial-first-app/index.html b/docs/fiddles/tutorial-first-app/index.html new file mode 100644 index 0000000000000..3d677b7c97b5b --- /dev/null +++ b/docs/fiddles/tutorial-first-app/index.html @@ -0,0 +1,21 @@ + + + + + + + Hello from Electron renderer! + + +

Hello from Electron renderer!

+

👋

+

+ + + diff --git a/docs/fiddles/tutorial-first-app/main.js b/docs/fiddles/tutorial-first-app/main.js new file mode 100644 index 0000000000000..10d57a0696f0f --- /dev/null +++ b/docs/fiddles/tutorial-first-app/main.js @@ -0,0 +1,26 @@ +const { app, BrowserWindow } = require('electron'); + +const createWindow = () => { + const win = new BrowserWindow({ + width: 800, + height: 600, + }); + + win.loadFile('index.html'); +}; + +app.whenReady().then(() => { + createWindow(); + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); diff --git a/docs/fiddles/tutorial-preload/index.html b/docs/fiddles/tutorial-preload/index.html new file mode 100644 index 0000000000000..3d677b7c97b5b --- /dev/null +++ b/docs/fiddles/tutorial-preload/index.html @@ -0,0 +1,21 @@ + + + + + + + Hello from Electron renderer! + + +

Hello from Electron renderer!

+

👋

+

+ + + diff --git a/docs/fiddles/tutorial-preload/main.js b/docs/fiddles/tutorial-preload/main.js new file mode 100644 index 0000000000000..6b7184900e6dd --- /dev/null +++ b/docs/fiddles/tutorial-preload/main.js @@ -0,0 +1,30 @@ +const { app, BrowserWindow } = require('electron'); +const path = require('path'); + +const createWindow = () => { + const win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + }, + }); + + win.loadFile('index.html'); +}; + +app.whenReady().then(() => { + createWindow(); + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); diff --git a/docs/fiddles/tutorial-preload/preload.js b/docs/fiddles/tutorial-preload/preload.js new file mode 100644 index 0000000000000..e0dbdce1b8b2f --- /dev/null +++ b/docs/fiddles/tutorial-preload/preload.js @@ -0,0 +1,7 @@ +const { contextBridge } = require('electron'); + +contextBridge.exposeInMainWorld('versions', { + node: () => process.versions.node, + chrome: () => process.versions.chrome, + electron: () => process.versions.electron, +}); diff --git a/docs/fiddles/tutorial-preload/renderer.js b/docs/fiddles/tutorial-preload/renderer.js new file mode 100644 index 0000000000000..7585229a91781 --- /dev/null +++ b/docs/fiddles/tutorial-preload/renderer.js @@ -0,0 +1,2 @@ +const information = document.getElementById('info'); +information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`; diff --git a/docs/images/gatekeeper.png b/docs/images/gatekeeper.png index ed4b15ec7e8d2..22567135b7da1 100644 Binary files a/docs/images/gatekeeper.png and b/docs/images/gatekeeper.png differ diff --git a/docs/images/preload-example.png b/docs/images/preload-example.png new file mode 100644 index 0000000000000..9f330b32de9ca Binary files /dev/null and b/docs/images/preload-example.png differ diff --git a/docs/tutorial/application-distribution.md b/docs/tutorial/application-distribution.md index fb5dc918dad91..1b17541f2b3f5 100644 --- a/docs/tutorial/application-distribution.md +++ b/docs/tutorial/application-distribution.md @@ -1,26 +1,26 @@ -# Application Distribution +--- +title: 'Application Packaging' +description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.' +slug: application-distribution +hide_title: false +--- -## Overview - -To distribute your app with Electron, you need to package and rebrand it. -To do this, you can either use specialized tooling or manual approaches. +To distribute your app with Electron, you need to package and rebrand it. To do this, you +can either use specialized tooling or manual approaches. ## With tooling -You can use the following tools to distribute your application: - -* [electron-forge](https://github.com/electron-userland/electron-forge) -* [electron-builder](https://github.com/electron-userland/electron-builder) -* [electron-packager](https://github.com/electron/electron-packager) +There are a couple tools out there that exist to package and distribute your Electron app. +We recommend using [Electron Forge](https://www.electronforge.io). You can check out +its documentation directly, or refer to the [Packaging and Distribution](./tutorial-5-packaging.md) +part of the Electron tutorial. -These tools will take care of all the steps you need to take to end up with a -distributable Electron application, such as bundling your application, -rebranding the executable, and setting the right icons. +## Manual packaging -You can check the example of how to package your app with `electron-forge` in -the [Quick Start guide](quick-start.md#package-and-distribute-your-application). +If you prefer the manual approach, there are 2 ways to distribute your application: -## Manual distribution +- With prebuilt binaries +- With an app source code archive ### With prebuilt binaries @@ -29,21 +29,19 @@ binaries](https://github.com/electron/electron/releases). Next, the folder containing your app should be named `app` and placed in Electron's resources directory as shown in the following examples. -> *NOTE:* the location of Electron's prebuilt binaries is indicated +:::note +The location of Electron's prebuilt binaries is indicated with `electron/` in the examples below. +::: -*On macOS:* - -```plaintext +```plain title='macOS' electron/Electron.app/Contents/Resources/app/ ├── package.json ├── main.js └── index.html ``` -*On Windows and Linux:* - -```plaintext +```plain title='Windows and Linux' electron/resources/app ├── package.json ├── main.js @@ -54,7 +52,7 @@ Then execute `Electron.app` on macOS, `electron` on Linux, or `electron.exe` on Windows, and Electron will start as your app. The `electron` directory will then be your distribution to deliver to users. -### With an app source code archive +### With an app source code archive (asar) Instead of shipping your app by copying all of its source files, you can package your app into an [asar] archive to improve the performance of reading @@ -65,16 +63,12 @@ To use an `asar` archive to replace the `app` folder, you need to rename the archive to `app.asar`, and put it under Electron's resources directory like below, and Electron will then try to read the archive and start from it. -*On macOS:* - -```plaintext +```plain title='macOS' electron/Electron.app/Contents/Resources/ └── app.asar ``` -*On Windows and Linux:* - -```plaintext +```plain title='Windows' electron/resources/ └── app.asar ``` @@ -87,47 +81,44 @@ You can find more details on how to use `asar` in the After bundling your app into Electron, you will want to rebrand Electron before distributing it to users. -#### macOS - -You can rename `Electron.app` to any name you want, and you also have to rename -the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the -following files: +- **Windows:** You can rename `electron.exe` to any name you like, and edit + its icon and other information with tools like [rcedit](https://github.com/electron/rcedit). +- **Linux:** You can rename the `electron` executable to any name you like. +- **macOS:** You can rename `Electron.app` to any name you want, and you also have to rename + the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the + following files: -* `Electron.app/Contents/Info.plist` -* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist` + - `Electron.app/Contents/Info.plist` + - `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist` -You can also rename the helper app to avoid showing `Electron Helper` in the -Activity Monitor, but make sure you have renamed the helper app's executable -file's name. + You can also rename the helper app to avoid showing `Electron Helper` in the + Activity Monitor, but make sure you have renamed the helper app's executable + file's name. -The structure of a renamed app would be like: + The structure of a renamed app would be like: -```plaintext +```plain MyApp.app/Contents ├── Info.plist ├── MacOS/ -│   └── MyApp +│ └── MyApp └── Frameworks/ └── MyApp Helper.app ├── Info.plist └── MacOS/ -    └── MyApp Helper + └── MyApp Helper ``` -#### Windows +:::note -You can rename `electron.exe` to any name you like, and edit its icon and other -information with tools like [rcedit](https://github.com/electron/rcedit). - -#### Linux - -You can rename the `electron` executable to any name you like. - -### Rebranding by rebuilding Electron from source - -It is also possible to rebrand Electron by changing the product name and +it is also possible to rebrand Electron by changing the product name and building it from source. To do this you need to set the build argument corresponding to the product name (`electron_product_name = "YourProductName"`) in the `args.gn` file and rebuild. +Keep in mind this is not recommended as setting up the environment to compile +from source is not trivial and takes significant time. + +::: + [asar]: https://github.com/electron/asar diff --git a/docs/tutorial/code-signing.md b/docs/tutorial/code-signing.md index d591fe617c89b..a035b480cd57e 100644 --- a/docs/tutorial/code-signing.md +++ b/docs/tutorial/code-signing.md @@ -1,14 +1,20 @@ -# Code Signing +--- +title: 'Code Signing' +description: 'Code signing is a security technology that you use to certify that an app was created by you.' +slug: code-signing +hide_title: false +--- Code signing is a security technology that you use to certify that an app was -created by you. +created by you. You should sign your application so it does not trigger any +operating system security checks. -On macOS the system can detect any change to the app, whether the change is +On macOS, the system can detect any change to the app, whether the change is introduced accidentally or by malicious code. On Windows, the system assigns a trust level to your code signing certificate which if you don't have, or if your trust level is low, will cause security -dialogs to appear when users start using your application. Trust level builds +dialogs to appear when users start using your application. Trust level builds over time so it's better to start code signing as early as possible. While it is possible to distribute unsigned apps, it is not recommended. Both @@ -16,20 +22,19 @@ Windows and macOS will, by default, prevent either the download or the execution of unsigned applications. Starting with macOS Catalina (version 10.15), users have to go through multiple manual steps to open unsigned applications. -![macOS Catalina Gatekeeper warning: The app cannot be opened because the -developer cannot be verified](../images/gatekeeper.png) +![macOS Catalina Gatekeeper warning: The app cannot be opened because the developer cannot be verified](../images/gatekeeper.png) As you can see, users get two options: Move the app straight to the trash or cancel running it. You don't want your users to see that dialog. If you are building an Electron app that you intend to package and distribute, -it should be code-signed. +it should be code signed. -# Signing & notarizing macOS builds +## Signing & notarizing macOS builds -Properly preparing macOS applications for release requires two steps: First, the -app needs to be code-signed. Then, the app needs to be uploaded to Apple for a -process called "notarization", where automated systems will further verify that +Properly preparing macOS applications for release requires two steps. First, the +app needs to be code signed. Then, the app needs to be uploaded to Apple for a +process called **notarization**, where automated systems will further verify that your app isn't doing anything to endanger its users. To start the process, ensure that you fulfill the requirements for signing and @@ -42,18 +47,18 @@ notarizing your app: Electron's ecosystem favors configuration and freedom, so there are multiple ways to get your application signed and notarized. -## `electron-forge` +### Using Electron Forge If you're using Electron's favorite build tool, getting your application signed and notarized requires a few additions to your configuration. [Forge](https://electronforge.io) is a collection of the official Electron tools, using [`electron-packager`], [`electron-osx-sign`], and [`electron-notarize`] under the hood. -Let's take a look at an example configuration with all required fields. Not all -of them are required: the tools will be clever enough to automatically find a -suitable `identity`, for instance, but we recommend that you are explicit. +Let's take a look at an example `package.json` configuration with all required fields. Not all of them are +required: the tools will be clever enough to automatically find a suitable `identity`, for instance, +but we recommend that you are explicit. -```json +```json title="package.json" {7} { "name": "my-app", "version": "0.0.1", @@ -69,7 +74,7 @@ suitable `identity`, for instance, but we recommend that you are explicit. }, "osxNotarize": { "appleId": "felix@felix.fun", - "appleIdPassword": "my-apple-id-password", + "appleIdPassword": "my-apple-id-password" } } } @@ -77,11 +82,11 @@ suitable `identity`, for instance, but we recommend that you are explicit. } ``` -The `plist` file referenced here needs the following macOS-specific entitlements +The `entitlements.plist` file referenced here needs the following macOS-specific entitlements to assure the Apple security mechanisms that your app is doing these things without meaning any harm: -```xml +```xml title="entitlements.plist" @@ -104,7 +109,7 @@ file](https://github.com/electron/fiddle/blob/master/forge.config.js). If you plan to access the microphone or camera within your app using Electron's APIs, you'll also need to add the following entitlements: -```xml +```xml title="entitlements.plist" com.apple.security.device.audio-input com.apple.security.device.camera @@ -113,28 +118,26 @@ need to add the following entitlements: If these are not present in your app's entitlements when you invoke, for example: -```js +```js title="main.js" const { systemPreferences } = require('electron') - const microphone = systemPreferences.askForMediaAccess('microphone') ``` Your app may crash. See the Resource Access section in [Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime) for more information and entitlements you may need. -## `electron-builder` +### Using Electron Builder Electron Builder comes with a custom solution for signing your application. You can find [its documentation here](https://www.electron.build/code-signing). -## `electron-packager` +### Using Electron Packager If you're not using an integrated build pipeline like Forge or Builder, you are likely using [`electron-packager`], which includes [`electron-osx-sign`] and [`electron-notarize`]. If you're using Packager's API, you can pass [in configuration that both signs -and notarizes your -application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html). +and notarizes your application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html). ```js const packager = require('electron-packager') @@ -155,11 +158,11 @@ packager({ }) ``` -The `plist` file referenced here needs the following macOS-specific entitlements +The `entitlements.plist` file referenced here needs the following macOS-specific entitlements to assure the Apple security mechanisms that your app is doing these things without meaning any harm: -```xml +```xml title="entitlements.plist" @@ -175,11 +178,11 @@ without meaning any harm: Up until Electron 12, the `com.apple.security.cs.allow-unsigned-executable-memory` entitlement was required as well. However, it should not be used anymore if it can be avoided. -## Mac App Store +### Signing Mac App Store applications See the [Mac App Store Guide]. -# Signing Windows builds +## Signing Windows builds Before signing Windows builds, you must do the following: @@ -190,31 +193,140 @@ Before signing Windows builds, you must do the following: You can get a code signing certificate from a lot of resellers. Prices vary, so it may be worth your time to shop around. Popular resellers include: -* [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm) -* [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing) -* Amongst others, please shop around to find one that suits your needs, Google - is your friend 😄 +- [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm) +- [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing) +- Amongst others, please shop around to find one that suits your needs! 😄 + +:::caution Keep your certificate password private +Your certificate password should be a **secret**. Do not share it publicly or +commit it to your source code. +::: + +### Using Electron Forge + +Once you have a code signing certificate file (`.pfx`), you can sign +[Squirrel.Windows][maker-squirrel] and [MSI][maker-msi] installers in Electron Forge +with the `certificateFile` and `certificatePassword` fields in their respective +configuration objects. -There are a number of tools for signing your packaged app: +For example, if you keep your Forge config in your `package.json` file and are +creating a Squirrel.Windows installer: + +```json {9-15} title='package.json' +{ + "name": "my-app", + "version": "0.0.1", + //... + "config": { + "forge": { + "packagerConfig": {}, + "makers": [ + { + "name": "@electron-forge/maker-squirrel", + "config": { + "certificateFile": "./cert.pfx", + "certificatePassword": "this-is-a-secret" + } + } + ] + } + } + //... +} +``` -* [`electron-winstaller`] will generate an installer for windows and sign it for - you -* [`electron-forge`] can sign installers it generates through the - Squirrel.Windows or MSI targets. -* [`electron-builder`] can sign some of its windows targets +### Using electron-winstaller (Squirrel.Windows) + +[`electron-winstaller`] is a package that can generate Squirrel.Windows installers for your +Electron app. This is the tool used under the hood by Electron Forge's +[Squirrel.Windows Maker][maker-squirrel]. If you're not using Electron Forge and want to use +`electron-winstaller` directly, use the `certificateFile` and `certificatePassword` configuration +options when creating your installer. + +```js {10-11} +const electronInstaller = require('electron-winstaller') +// 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', + certificateFile: './cert.pfx', + certificatePassword: 'this-is-a-secret', + }) + console.log('It worked!') +} catch (e) { + console.log(`No dice: ${e.message}`) +} +``` + +For full configuration options, check out the [`electron-winstaller`] repository! + +### Using electron-wix-msi (WiX MSI) + +[`electron-wix-msi`] is a package that can generate MSI installers for your +Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi]. + +If you're not using Electron Forge and want to use `electron-wix-msi` directly, use the +`certificateFile` and `certificatePassword` configuration options +or pass in parameters directly to [SignTool.exe] with the `signWithParams` option. + +```js {12-13} +import { MSICreator } from 'electron-wix-msi' + +// Step 1: Instantiate the MSICreator +const msiCreator = new MSICreator({ + appDirectory: '/path/to/built/app', + description: 'My amazing Kitten simulator', + exe: 'kittens', + name: 'Kittens', + manufacturer: 'Kitten Technologies', + version: '1.1.2', + outputDirectory: '/path/to/output/folder', + certificateFile: './cert.pfx', + certificatePassword: 'this-is-a-secret', +}) + +// Step 2: Create a .wxs template file +const supportBinaries = await msiCreator.create() + +// 🆕 Step 2a: optionally sign support binaries if you +// sign you binaries as part of of your packaging script +supportBinaries.forEach(async (binary) => { + // Binaries are the new stub executable and optionally + // the Squirrel auto updater. + await signFile(binary) +}) + +// Step 3: Compile the template to a .msi file +await msiCreator.compile() +``` + +For full configuration options, check out the [`electron-wix-msi`] repository! + +### Using Electron Builder + +Electron Builder comes with a custom solution for signing your application. You +can find [its documentation here](https://www.electron.build/code-signing). -## Windows Store +### Signing Windows Store applications See the [Windows Store Guide]. -[Apple Developer Program]: https://developer.apple.com/programs/ +[apple developer program]: https://developer.apple.com/programs/ [`electron-builder`]: https://github.com/electron-userland/electron-builder [`electron-forge`]: https://github.com/electron-userland/electron-forge [`electron-osx-sign`]: https://github.com/electron-userland/electron-osx-sign [`electron-packager`]: https://github.com/electron/electron-packager [`electron-notarize`]: https://github.com/electron/electron-notarize [`electron-winstaller`]: https://github.com/electron/windows-installer -[Xcode]: https://developer.apple.com/xcode +[`electron-wix-msi`]: https://github.com/felixrieseberg/electron-wix-msi +[xcode]: https://developer.apple.com/xcode [signing certificates]: https://github.com/electron/electron-osx-sign/wiki/1.-Getting-Started#certificates -[Mac App Store Guide]: mac-app-store-submission-guide.md -[Windows Store Guide]: windows-store-guide.md +[mac app store guide]: ./mac-app-store-submission-guide.md +[windows store guide]: ./windows-store-guide.md +[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows +[maker-msi]: https://www.electronforge.io/config/makers/wix-msi +[signtool.exe]: https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe diff --git a/docs/tutorial/distribution-overview.md b/docs/tutorial/distribution-overview.md new file mode 100644 index 0000000000000..b7e9bd991b4fd --- /dev/null +++ b/docs/tutorial/distribution-overview.md @@ -0,0 +1,54 @@ +--- +title: 'Distribution Overview' +description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.' +slug: distribution-overview +hide_title: false +--- + +Once your app is ready for production, there are a couple steps you need to take before +you can deliver it to your users. + +## Packaging + +To distribute your app with Electron, you need to package all your resources and assets +into an executable and rebrand it. To do this, you can either use specialized tooling +or do it manually. See the [Application Packaging][application-packaging] tutorial +for more information. + +## Code signing + +Code signing is a security technology that you use to certify that an app was +created by you. You should sign your application so it does not trigger the +security checks of your user's operating system. + +To get started with each operating system's code signing process, please read the +[Code Signing][code-signing] docs. + +## Publishing + +Once your app is packaged and signed, you can freely distribute your app directly +to users by uploading your installers online. + +To reach more users, you can also choose to upload your app to each operating system's +digital distribution platform (i.e. app store). These require another build step aside +from your direct download app. For more information, check out each individual app store guide: + +- [Mac App Store][mac-app] +- [Windows Store][windows-store] +- [Snapcraft (Linux)][snapcraft] + +## Updating + +Electron's auto-updater allows you to deliver application updates to users +without forcing them to manually download new versions of your application. +Check out the [Updating Applications][updates] guide for details on implementing automatic updates +with Electron. + + + +[application-packaging]: ./application-distribution.md +[code-signing]: ./code-signing.md +[mac-app]: ./mac-app-store-submission-guide.md +[windows-store]: ./windows-store-guide.md +[snapcraft]: ./snapcraft.md +[updates]: ./updates.md diff --git a/docs/tutorial/examples.md b/docs/tutorial/examples.md new file mode 100644 index 0000000000000..a2bea3c12f300 --- /dev/null +++ b/docs/tutorial/examples.md @@ -0,0 +1,56 @@ +--- +title: 'Examples Overview' +description: 'A set of examples for common Electron features' +slug: examples +hide_title: false +--- + +# Examples Overview + +In this section, we have collected a set of guides for common features +that you may want to implement in your Electron application. Each guide +contains a practical example in a minimal, self-contained example app. +The easiest way to run these examples is by downloading [Electron Fiddle][fiddle]. + +Once Fiddle is installed, you can press on the "Open in Fiddle" button that you +will find below code samples like the following one: + +```fiddle docs/fiddles/quick-start +window.addEventListener('DOMContentLoaded', () => { + const replaceText = (selector, text) => { + const element = document.getElementById(selector) + if (element) element.innerText = text + } + + for (const type of ['chrome', 'node', 'electron']) { + replaceText(`${type}-version`, process.versions[type]) + } +}) +``` + +If there is still something that you do not know how to do, please take a look at the [API][app] +as there is a chance it might be documented just there (and also open an issue requesting the +guide!). + + + +| Guide | Description | +| :-------------------- | ------------------------------------------------------------------------------------------------------------------- | +| [Message ports] | This guide provides some examples of how you might use MessagePorts in your app to communicate different processes. | +| [Device access] | Learn how to access the device hardware (Bluetooth, USB, Serial). | +| [Keyboard shortcuts] | Configure local and global keyboard shortcuts for your Electron application. | +| [Multithreading] | With Web Workers, it is possible to run JavaScript in OS-level threads | +| [Offscreen rendering] | Offscreen rendering lets you obtain the content of a BrowserWindow in a bitmap, so it can be rendered anywhere. | +| [Spellchecker] | Learn how to use the built-in spellchecker, set languages, etc. | +| [Web embeds] | Discover the different ways to embed third-party web content in your application. | + + + +## How to...? + +You can find the full list of "How to?" in the sidebar. If there is +something that you would like to do that is not documented, please join +our [Discord server][] and let us know! + +[discord server]: https://discord.com/invite/electron +[fiddle]: https://www.electronjs.org/fiddle diff --git a/docs/tutorial/introduction.md b/docs/tutorial/introduction.md index fe26df6498f8f..8091434c01b0f 100644 --- a/docs/tutorial/introduction.md +++ b/docs/tutorial/introduction.md @@ -1,10 +1,11 @@ -# Introduction +--- +title: 'Introduction' +description: 'Welcome to the Electron documentation! If this is your first time developing an Electron app, read through this Getting Started section to get familiar with the basics. Otherwise, feel free to explore our guides and API documentation!' +slug: /latest/ +hide_title: false +--- -Welcome to the Electron documentation! If this is your first time developing -an Electron app, read through this Getting Started section to get familiar with the -basics. Otherwise, feel free to explore our guides and API documentation! - -## What is Electron? +# What is Electron? Electron is a framework for building desktop applications using JavaScript, HTML, and CSS. By embedding [Chromium][chromium] and [Node.js][node] into its @@ -12,20 +13,12 @@ binary, Electron allows you to maintain one JavaScript codebase and create cross-platform apps that work on Windows, macOS, and Linux — no native development experience required. -## Prerequisites - -These docs operate under the assumption that the reader is familiar with both -Node.js and general web development. If you need to get more comfortable with -either of these areas, we recommend the following resources: - -* [Getting started with the Web (MDN)][mdn-guide] -* [Introduction to Node.js][node-guide] +## Getting started -Moreover, you'll have a better time understanding how Electron works if you get -acquainted with Chromium's process model. You can get a brief overview of -Chrome architecture with the [Chrome comic][comic], which was released alongside -Chrome's launch back in 2008. Although it's been over a decade since then, the -core principles introduced in the comic remain helpful to understand Electron. +We recommend you to start with the [tutorial], which guides you through the +process of developing an Electron app and distributing it to users. +The [examples] and [API documentation] are also good places to browse around +and discover new things. ## Running examples with Electron Fiddle @@ -39,21 +32,44 @@ a code block. If you have Fiddle installed, this button will open a `fiddle.electronjs.org` link that will automatically load the example into Fiddle, no copy-pasting required. +```fiddle docs/fiddles/quick-start +``` + +## What is in the docs? + +All the official documentation is available from the sidebar. These +are the different categories and what you can expect on each one: + +- **Tutorial**: An end-to-end guide on how to create and publish your first Electron + application. +- **Processes in Electron**: In-depth reference on Electron processes and how to work with them. +- **Best Practices**: Important checklists to keep in mind when developing an Electron app. +- **How-To Examples**: Quick references to add features to your Electron app. +- **Development**: Miscellaneous development guides. +- **Distribution**: Learn how to distribute your app to end users. +- **Testing and debugging**: How to debug JavaScript, write tests, and other tools used + to create quality Electron applications. +- **Resources**: Useful links to better understand how the Electron project works + and is organized. +- **Contributing to Electron**: Compiling Electron and making contributions can be daunting. + We try to make it easier in this section. + ## Getting help Are you getting stuck anywhere? Here are a few links to places to look: -* If you need help with developing your app, our [community Discord server][discord] -is a great place to get advice from other Electron app developers. -* If you suspect you're running into a bug with the `electron` package, please check -the [GitHub issue tracker][issue-tracker] to see if any existing issues match your -problem. If not, feel free to fill out our bug report template and submit a new issue. +- If you need help with developing your app, our [community Discord server][discord] + is a great place to get advice from other Electron app developers. +- If you suspect you're running into a bug with the `electron` package, please check + the [GitHub issue tracker][issue-tracker] to see if any existing issues match your + problem. If not, feel free to fill out our bug report template and submit a new issue. + + +[api documentation]: ../api/app.md [chromium]: https://www.chromium.org/ -[node]: https://nodejs.org/ -[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web -[node-guide]: https://nodejs.dev/learn -[comic]: https://www.google.com/googlebooks/chrome/ +[discord]: https://discord.com/invite/APGC3k5yaH +[examples]: examples.md [fiddle]: https://electronjs.org/fiddle [issue-tracker]: https://github.com/electron/electron/issues -[discord]: https://discord.gg/electronjs +[node]: https://nodejs.org/ diff --git a/docs/tutorial/process-model.md b/docs/tutorial/process-model.md index 6b7737ec50b84..2cb37099b513b 100644 --- a/docs/tutorial/process-model.md +++ b/docs/tutorial/process-model.md @@ -1,10 +1,17 @@ +--- +title: 'Process Model' +description: 'Electron inherits its multi-process architecture from Chromium, which makes the framework architecturally very similar to a modern web browser. This guide will expand on the concepts applied in the tutorial.' +slug: process-model +hide_title: false +--- + # Process Model Electron inherits its multi-process architecture from Chromium, which makes the framework -architecturally very similar to a modern web browser. In this guide, we'll expound on -the conceptual knowledge of Electron that we applied in the minimal [quick start app][]. +architecturally very similar to a modern web browser. This guide will expand on the +concepts applied in the [Tutorial][tutorial]. -[quick start app]: ./quick-start.md +[tutorial]: ./tutorial-1-prerequisites.md ## Why not a single process? @@ -27,10 +34,10 @@ visualizes this model: ![Chrome's multi-process architecture](../images/chrome-processes.png) Electron applications are structured very similarly. As an app developer, you control -two types of processes: main and renderer. These are analogous to Chrome's own browser -and renderer processes outlined above. +two types of processes: [main](#the-main-process) and [renderer](#the-renderer-process). +These are analogous to Chrome's own browser and renderer processes outlined above. -[Chrome Comic]: https://www.google.com/googlebooks/chrome/ +[chrome comic]: https://www.google.com/googlebooks/chrome/ ## The main process @@ -68,7 +75,7 @@ When a `BrowserWindow` instance is destroyed, its corresponding renderer process terminated as well. [browser-window]: ../api/browser-window.md -[web-embed]: ./web-embeds.md +[web-embed]: ../tutorial/web-embeds.md [web-contents]: ../api/web-contents.md [event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter @@ -90,7 +97,7 @@ app.on('window-all-closed', () => { ``` [app]: ../api/app.md -[quick-start-lifecycle]: ./quick-start.md#manage-your-windows-lifecycle +[quick-start-lifecycle]: ../tutorial/quick-start.md#manage-your-windows-lifecycle ### Native APIs @@ -105,7 +112,7 @@ For a full list of Electron's main process modules, check out our API documentat Each Electron app spawns a separate renderer process for each open `BrowserWindow` (and each web embed). As its name implies, a renderer is responsible for -*rendering* web content. For all intents and purposes, code ran in renderer processes +_rendering_ web content. For all intents and purposes, code ran in renderer processes should behave according to web standards (insofar as Chromium does, at least). Therefore, all user interfaces and app functionality within a single browser @@ -115,18 +122,22 @@ web. Although explaining every web spec is out of scope for this guide, the bare minimum to understand is: -* An HTML file is your entry point for the renderer process. -* UI styling is added through Cascading Style Sheets (CSS). -* Executable JavaScript code can be added through ` + +``` + +After following the above steps, your app should look something like this: + +![Electron app showing This app is using Chrome (v102.0.5005.63), Node.js (v16.14.2), and Electron (v19.0.3)](../images/preload-example.png) + +And the code should look like this: + +```fiddle docs/fiddles/tutorial-preload + +``` + +## Communicating between processes + +As we have mentioned above, Electron's main and renderer process have distinct responsibilities +and are not interchangeable. This means it is not possible to access the Node.js APIs directly +from the renderer process, nor the HTML Document Object Model (DOM) from the main process. + +The solution for this problem is to use Electron's `ipcMain` and `ipcRenderer` modules for +inter-process communication (IPC). To send a message from your web page to the main process, +you can set up a main process handler with `ipcMain.handle` and +then expose a function that calls `ipcRenderer.invoke` to trigger the handler in your preload script. + +To illustrate, we will add a global function to the renderer called `ping()` +that will return a string from the main process. + +First, set up the `invoke` call in your preload script: + +```js {1,7} title="preload.js" +const { contextBridge, ipcRenderer } = require('electron') + +contextBridge.exposeInMainWorld('versions', { + node: () => process.versions.node, + chrome: () => process.versions.chrome, + electron: () => process.versions.electron, + ping: () => ipcRenderer.invoke('ping'), + // we can also expose variables, not just functions +}) +``` + +:::caution IPC security + +Notice how we wrap the `ipcRenderer.invoke('ping')` call in a helper function rather +than expose the `ipcRenderer` module directly via context bridge. You **never** want to +directly expose the entire `ipcRenderer` module via preload. This would give your renderer +the ability to send arbitrary IPC messages to the main process, which becomes a powerful +attack vector for malicious code. + +::: + +Then, set up your `handle` listener in the main process. We do this _before_ +loading the HTML file so that the handler is guaranteed to be ready before +you send out the `invoke` call from the renderer. + +```js {1,11} title="main.js" +const { ipcMain } = require('electron') + +const createWindow = () => { + const win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + }, + }) + ipcMain.handle('ping', () => 'pong') + win.loadFile('index.html') +} +``` + +Once you have the sender and receiver set up, you can now send messages from the renderer +to the main process through the `'ping'` channel you just defined. + +```js title='renderer.js' +const func = async () => { + const response = await window.versions.ping() + console.log(response) // prints out 'pong' +} + +func() +``` + +:::info + +For more in-depth explanations on using the `ipcRenderer` and `ipcMain` modules, +check out the full [Inter-Process Communication][ipc] guide. + +::: + +## Summary + +A preload script contains code that runs before your web page is loaded into the browser +window. It has access to both DOM APIs and Node.js environment, and is often used to +expose privileged APIs to the renderer via the `contextBridge` API. + +Because the main and renderer processes have very different responsibilities, Electron +apps often use the preload script to set up inter-process communication (IPC) interfaces +to pass arbitrary messages between the two kinds of processes. + +In the next part of the tutorial, we will be showing you resources on adding more +functionality to your app, then teaching you distributing your app to users. + + + +[advanced-installation]: ./installation.md +[application debugging]: ./application-debugging.md +[app]: ../api/app.md +[app-ready]: ../api/app.md#event-ready +[app-when-ready]: ../api/app.md#appwhenready +[browser-window]: ../api/browser-window.md +[commonjs]: https://nodejs.org/docs/latest/api/modules.html#modules_modules_commonjs_modules +[compound task]: https://code.visualstudio.com/Docs/editor/tasks#_compound-tasks +[content-script]: https://developer.chrome.com/docs/extensions/mv3/content_scripts/ +[contextbridge]: ../api/context-bridge.md +[context-isolation]: ./context-isolation.md +[`document.getelementbyid`]: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById +[devtools-extension]: ./devtools-extension.md +[dirname]: https://nodejs.org/api/modules.html#modules_dirname +[global]: https://developer.mozilla.org/en-US/docs/Glossary/Global_object +[ipc]: ./ipc.md +[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP +[modules]: ../api/app.md +[node-api]: https://nodejs.org/dist/latest/docs/api/ +[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main +[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts +[path-join]: https://nodejs.org/api/path.html#path_path_join_paths +[process-model]: ./process-model.md +[react]: https://reactjs.org +[sandbox]: ./sandbox.md +[webpack]: https://webpack.js.org + + + +[prerequisites]: tutorial-1-prerequisites.md +[building your first app]: tutorial-2-first-app.md +[preload]: tutorial-3-preload.md +[features]: tutorial-4-adding-features.md +[packaging]: tutorial-5-packaging.md +[updates]: tutorial-6-publishing-updating.md diff --git a/docs/tutorial/tutorial-4-adding-features.md b/docs/tutorial/tutorial-4-adding-features.md new file mode 100644 index 0000000000000..b7c776c1dbd67 --- /dev/null +++ b/docs/tutorial/tutorial-4-adding-features.md @@ -0,0 +1,77 @@ +--- +title: 'Adding Features' +description: 'In this step of the tutorial, we will share some resources you should read to add features to your application' +slug: tutorial-adding-features +hide_title: false +--- + +:::info Follow along the tutorial + +This is **part 4** of the Electron tutorial. + +1. [Prerequisites][prerequisites] +1. [Building your First App][building your first app] +1. [Using Preload Scripts][preload] +1. **[Adding Features][features]** +1. [Packaging Your Application][packaging] +1. [Publishing and Updating][updates] + +::: + +## Adding application complexity + +If you have been following along, you should have a functional Electron application +with a static user interface. From this starting point, you can generally progress +in developing your app in two broad directions: + +1. Adding complexity to your renderer process' web app code +1. Deeper integrations with the operating system and Node.js + +It is important to understand the distinction between these two broad concepts. For the +first point, Electron-specific resources are not necessary. Building a pretty to-do +list in Electron is just pointing your Electron BrowserWindow to a pretty +to-do list web app. Ultimately, you are building your renderer's UI using the same tools +(HTML, CSS, JavaScript) that you would on the web. Therefore, Electron's docs will +not go in-depth on how to use standard web tools. + +On the other hand, Electron also provides a rich set of tools that allow +you to integrate with the desktop environment, from creating tray icons to adding +global shortcuts to displaying native menus. It also gives you all the power of a +Node.js environment in the main process. This set of capabilities separates +Electron applications from running a website in a browser tab, and are the +focus of Electron's documentation. + +## How-to examples + +Electron's documentation has many tutorials to help you with more advanced topics +and deeper operating system integrations. To get started, check out the +[How-To Examples][how-to] doc. + +:::note Let us know if something is missing! + +If you can't find what you are looking for, please let us know on [GitHub] or in +our [Discord server][discord]! + +::: + +## What's next? + +For the rest of the tutorial, we will be shifting away from application code +and giving you a look at how you can get your app from your developer machine +into end users' hands. + + + +[discord]: https://discord.com/invite/APGC3k5yaH +[github]: https://github.com/electron/electronjs.org-new/issues/new +[how to]: ./examples.md +[node-platform]: https://nodejs.org/api/process.html#process_process_platform + + + +[prerequisites]: tutorial-1-prerequisites.md +[building your first app]: tutorial-2-first-app.md +[preload]: tutorial-3-preload.md +[features]: tutorial-4-adding-features.md +[packaging]: tutorial-5-packaging.md +[updates]: tutorial-6-publishing-updating.md diff --git a/docs/tutorial/tutorial-5-packaging.md b/docs/tutorial/tutorial-5-packaging.md new file mode 100644 index 0000000000000..3ab4f15b50d84 --- /dev/null +++ b/docs/tutorial/tutorial-5-packaging.md @@ -0,0 +1,225 @@ +--- +title: 'Packaging Your Application' +description: 'To distribute your app with Electron, you need to package it and create installers.' +slug: tutorial-packaging +hide_title: false +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +:::info Follow along the tutorial + +This is **part 5** of the Electron tutorial. + +1. [Prerequisites][prerequisites] +1. [Building your First App][building your first app] +1. [Using Preload Scripts][preload] +1. [Adding Features][features] +1. **[Packaging Your Application][packaging]** +1. [Publishing and Updating][updates] + +::: + +## Learning goals + +In this part of the tutorial, we'll be going over the basics of packaging and distributing +your app with [Electron Forge]. + +## Using Electron Forge + +Electron does not have any tooling for packaging and distribution bundled into its core +modules. Once you have a working Electron app in dev mode, you need to use +additional tooling to create a packaged app you can distribute to your users (also known +as a **distributable**). Distributables can be either installers (e.g. MSI on Windows) or +portable executable files (e.g. `.app` on macOS). + +Electron Forge is an all-in-one tool that handles the packaging and distribution of Electron +apps. Under the hood, it combines a lot of existing Electron tools (e.g. [`electron-packager`], +[`@electron/osx-sign`], [`electron-winstaller`], etc.) into a single interface so you do not +have to worry about wiring them all together. + +### Importing your project into Forge + +You can install Electron Forge's CLI in your project's `devDependencies` and import your +existing project with a handy conversion script. + +```sh npm2yarn +npm install --save-dev @electron-forge/cli +npx electron-forge import +``` + +Once the conversion script is done, Forge should have added a few scripts +to your `package.json` file. + +```json title='package.json' + //... + "scripts": { + "start": "electron-forge start", + "package": "electron-forge package", + "make": "electron-forge make" + }, + //... +``` + +:::info CLI documentation + +For more information on `make` and other Forge APIs, check out +the [Electron Forge CLI documentation]. + +::: + +You should also notice that your package.json now has a few more packages installed +under your `devDependencies`, and contains an added `config.forge` field with an array +of makers configured. **Makers** are Forge plugins that create distributables from +your source code. You should see multiple makers in the pre-populated configuration, +one for each target platform. + +### Creating a distributable + +To create a distributable, use your project's new `make` script, which runs the +`electron-forge make` command. + +```sh npm2yarn +npm run make +``` + +This `make` command contains two steps: + +1. It will first run `electron-forge package` under the hood, which bundles your app + code together with the Electron binary. The packaged code is generated into a folder. +1. It will then use this packaged app folder to create a separate distributable for each + configured maker. + +After the script runs, you should see an `out` folder containing both the distributable +and a folder containing the packaged application code. + +```plain title='macOS output example' +out/ +├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip +├── ... +└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app +``` + +The distributable in the `out/make` folder should be ready to launch! You have now +created your first bundled Electron application. + +:::tip Distributable formats + +Electron Forge can be configured to create distributables in different OS-specific formats +(e.g. DMG, deb, MSI, etc.). See Forge's [Makers] documentation for all configuration options. + +::: + +:::note Packaging without Electron Forge + +If you want to manually package your code, or if you're just interested understanding the +mechanics behind packaging an Electron app, check out the full [Application Packaging] +documentation. + +::: + +## Important: signing your code + +In order to distribute desktop applications to end users, we _highly recommended_ for you +to **code sign** your Electron app. Code signing is an important part of shipping +desktop applications, and is mandatory for the auto-update step in the final part +of the tutorial. + +Code signing is a security technology that you use to certify that a desktop app was +created by a known source. Windows and macOS have their own OS-specific code signing +systems that will make it difficult for users to download or launch unsigned applications. + +If you already have code signing certificates for Windows and macOS, you can set your +credentials in your Forge configuration. Otherwise, please refer to the full +[Code Signing] documentation to learn how to purchase a certificate and for more information +on the desktop app code signing process. + +On macOS, code signing is done at the app packaging level. On Windows, distributable installers +are signed instead. + + + + +```json title='package.json' {6-18} +{ + //... + "config": { + "forge": { + //... + "packagerConfig": { + "osxSign": { + "identity": "Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)", + "hardened-runtime": true, + "entitlements": "entitlements.plist", + "entitlements-inherit": "entitlements.plist", + "signature-flags": "library" + }, + "osxNotarize": { + "appleId": "felix@felix.fun", + "appleIdPassword": "this-is-a-secret" + } + } + //... + } + } + //... +} +``` + + + + +```json title='package.json' {6-14} +{ + //... + "config": { + "forge": { + //... + "makers": [ + { + "name": "@electron-forge/maker-squirrel", + "config": { + "certificateFile": "./cert.pfx", + "certificatePassword": "this-is-a-secret" + } + } + ] + //... + } + } + //... +} +``` + + + + +## Summary + +Electron applications need to be packaged to be distributed to users. In this tutorial, +you imported your app into Electron Forge and configured it to package your app and +generate installers. + +In order for your application to be trusted by the user's system, you need to digitally +certify that the distributable is authentic and untampered by code signing it. Your app +can be signed through Forge once you configure it to use your code signing certificate +information. + +[`@electron/osx-sign`]: https://github.com/electron/osx-sign +[application packaging]: ./application-distribution.md +[code signing]: ./code-signing.md +[`electron-packager`]: https://github.com/electron/electron-packager +[`electron-winstaller`]: https://github.com/electron/windows-installer +[electron forge]: https://www.electronforge.io +[electron forge cli documentation]: https://www.electronforge.io/cli#commands +[makers]: https://www.electronforge.io/config/makers + + + +[prerequisites]: tutorial-1-prerequisites.md +[building your first app]: tutorial-2-first-app.md +[preload]: tutorial-3-preload.md +[features]: tutorial-4-adding-features.md +[packaging]: tutorial-5-packaging.md +[updates]: tutorial-6-publishing-updating.md diff --git a/docs/tutorial/tutorial-6-publishing-updating.md b/docs/tutorial/tutorial-6-publishing-updating.md new file mode 100644 index 0000000000000..65b89766d88f2 --- /dev/null +++ b/docs/tutorial/tutorial-6-publishing-updating.md @@ -0,0 +1,251 @@ +--- +title: 'Publishing and Updating' +description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module." +slug: tutorial-publishing-updating +hide_title: false +--- + +:::info Follow along the tutorial + +This is **part 6** of the Electron tutorial. + +1. [Prerequisites][prerequisites] +1. [Building your First App][building your first app] +1. [Using Preload Scripts][preload] +1. [Adding Features][features] +1. [Packaging Your Application][packaging] +1. **[Publishing and Updating][updates]** + +::: + +## Learning goals + +If you've been following along, this is the last step of the tutorial! In this part, +you will publish your app to GitHub releases and integrate automatic updates +into your app code. + +## Using update.electronjs.org + +The Electron maintainers provide a free auto-updating service for open-source apps +at https://update.electronjs.org. Its requirements are: + +- Your app runs on macOS or Windows +- Your app has a public GitHub repository +- Builds are published to [GitHub releases] +- Builds are [code signed][code-signed] + +At this point, we'll assume that you have already pushed all your +code to a public GitHub repository. + +:::info Alternative update services + +If you're using an alternate repository host (e.g. GitLab or Bitbucket) or if +you need to keep your code repository private, please refer to our +[step-by-step guide][update-server] on hosting your own Electron update server. + +::: + +## Publishing a GitHub release + +Electron Forge has [Publisher] plugins that can automate the distribution +of your packaged application to various sources. In this tutorial, we will +be using the GitHub Publisher, which will allow us to publish +our code to GitHub releases. + +### Generating a personal access token + +Forge cannot publish to any repository on GitHub without permission. You +need to pass in an authenticated token that gives Forge access to +your GitHub releases. The easiest way to do this is to +[create a new personal access token (PAT)][new-pat] +with the `public_repo` scope, which gives write access to your public repositories. +**Make sure to keep this token a secret.** + +### Setting up the GitHub Publisher + +#### Installing the module + +Forge's [GitHub Publisher] is a plugin that +needs to be installed in your project's `devDependencies`: + +```sh npm2yarn +npm install --save-dev @electron-forge/publisher-github +``` + +#### Configuring the publisher in Forge + +Once you have it installed, you need to set it up in your Forge +configuration. A full list of options is documented in the Forge's +[`PublisherGitHubConfig`] API docs. + +```json title='package.json' {6-16} +{ + //... + "config": { + "forge": { + "publishers": [ + { + "name": "@electron-forge/publisher-github", + "config": { + "repository": { + "owner": "github-user-name", + "name": "github-repo-name" + }, + "prerelease": false, + "draft": true + } + } + ] + } + } + //... +} +``` + +:::tip Drafting releases before publishing + +Notice that you have configured Forge to publish your release as a draft. +This will allow you to see the release with its generated artifacts +without actually publishing it to your end users. You can manually +publish your releases via GitHub after writing release notes and +double-checking that your distributables work. + +::: + +#### Setting up your authentication token + +You also need to make the Publisher aware of your authentication token. +By default, it will use the value stored in the `GITHUB_TOKEN` environment +variable. + +### Running the publish command + +Add Forge's [publish command] to your npm scripts. + +```json {6} title='package.json' + //... + "scripts": { + "start": "electron-forge start", + "package": "electron-forge package", + "make": "electron-forge make", + "publish": "electron-forge publish" + }, + //... +``` + +This command will run your configured makers and publish the output distributables to a new +GitHub release. + +```sh npm2yarn +npm run publish +``` + +By default, this will only publish a single distributable for your host operating system and +architecture. You can publish for different architectures by passing in the `--arch` flag to your +Forge commands. + +The name of this release will correspond to the `version` field in your project's package.json file. + +:::tip Tagging releases + +Optionally, you can also [tag your releases in Git][git-tag] so that your +release is associated with a labeled point in your code history. npm comes +with a handy [`npm version`](https://docs.npmjs.com/cli/v8/commands/npm-version) +command that can handle the version bumping and tagging for you. + +::: + +#### Bonus: Publishing in GitHub Actions + +Publishing locally can be painful, especially because you can only create distributables +for your host operating system (i.e. you can't publish a Window `.exe` file from macOS). + +A solution for this would be to publish your app via automation workflows +such as [GitHub Actions], which can run tasks in the +cloud on Ubuntu, macOS, and Windows. This is the exact approach taken by [Electron Fiddle]. +You can refer to Fiddle's [Build and Release pipeline][fiddle-build] +and [Forge configuration][fiddle-forge-config] +for more details. + +## Instrumenting your updater code + +Now that we have a functional release system via GitHub releases, we now need to tell our +Electron app to download an update whenever a new release is out. Electron apps do this +via the [autoUpdater] module, which reads from an update server feed to check if a new version +is available for download. + +The update.electronjs.org service provides an updater-compatible feed. For example, Electron +Fiddle v0.28.0 will check the endpoint at https://update.electronjs.org/electron/fiddle/darwin/v0.28.0 +to see if a newer GitHub release is available. + +After your release is published to GitHub, the update.electronjs.org service should work +for your application. The only step left is to configure the feed with the autoUpdater module. + +To make this process easier, the Electron team maintains the [`update-electron-app`] module, +which sets up the autoUpdater boilerplate for update.electronjs.org in one function +call — no configuration required. This module will search for the update.electronjs.org +feed that matches your project's package.json `"repository"` field. + +First, install the module as a runtime dependency. + +```sh npm2yarn +npm install update-electron-app +``` + +Then, import the module and call it immediately in the main process. + +```js title='main.js' +require('update-electron-app')() +``` + +And that is all it takes! Once your application is packaged, it will update itself for each new +GitHub release that you publish. + +## Summary + +In this tutorial, we configured Electron Forge's GitHub Publisher to upload your app's +distributables to GitHub releases. Since distributables cannot always be generated +between platforms, we recommend setting up your building and publishing flow +in a Continuous Integration pipeline if you do not have access to machines. + +Electron applications can self-update by pointing the autoUpdater module to an update server feed. +update.electronjs.org is a free update server provided by Electron for open-source applications +published on GitHub releases. Configuring your Electron app to use this service is as easy as +installing and importing the `update-electron-app` module. + +If your application is not eligible for update.electronjs.org, you should instead deploy your +own update server and configure the autoUpdater module yourself. + +:::info 🌟 You're done! + +From here, you have officially completed our tutorial to Electron. Feel free to explore the +rest of our docs and happy developing! If you have questions, please stop by our community +[Discord server]. + +::: + +[autoupdater]: ../api/auto-updater.md +[code-signed]: ./code-signing.md +[discord server]: https://discord.com/invite/APGC3k5yaH +[electron fiddle]: https://electronjs.org/fiddle +[fiddle-build]: https://github.com/electron/fiddle/blob/master/.github/workflows/build.yaml +[fiddle-forge-config]: https://github.com/electron/fiddle/blob/master/forge.config.js +[github actions]: https://github.com/features/actions +[github publisher]: https://www.electronforge.io/config/publishers/github +[github releases]: https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository +[git tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging +[new-pat]: https://github.com/settings/tokens/new +[publish command]: https://www.electronforge.io/cli#publish +[publisher]: https://www.electronforge.io/config/publishers +[`publishergithubconfig`]: https://js.electronforge.io/publisher/github/interfaces/publishergithubconfig +[`update-electron-app`]: https://github.com/electron/update-electron-app +[update-server]: ./updates.md + + + +[prerequisites]: tutorial-1-prerequisites.md +[building your first app]: tutorial-2-first-app.md +[preload]: tutorial-3-preload.md +[features]: tutorial-4-adding-features.md +[packaging]: tutorial-5-packaging.md +[updates]: tutorial-6-publishing-updating.md diff --git a/docs/tutorial/updates.md b/docs/tutorial/updates.md index 83174b3e4b747..530d9658d496d 100644 --- a/docs/tutorial/updates.md +++ b/docs/tutorial/updates.md @@ -1,11 +1,16 @@ -# Updating Applications - -There are several ways to update an Electron application. The easiest and -officially supported one is taking advantage of the built-in +--- +title: 'Updating Applications' +description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module." +slug: updates +hide_title: false +--- + +There are several ways to provide automatic updates to your Electron application. +The easiest and officially supported one is taking advantage of the built-in [Squirrel](https://github.com/Squirrel) framework and Electron's [autoUpdater](../api/auto-updater.md) module. -## Using `update.electronjs.org` +## Using update.electronjs.org The Electron team maintains [update.electronjs.org], a free and open-source webservice that Electron apps can use to self-update. The service is designed @@ -13,72 +18,77 @@ for Electron apps that meet the following criteria: - App runs on macOS or Windows - App has a public GitHub repository -- Builds are published to GitHub Releases -- Builds are code-signed +- Builds are published to [GitHub Releases][gh-releases] +- Builds are [code-signed](./code-signing.md) The easiest way to use this service is by installing [update-electron-app], a Node.js module preconfigured for use with update.electronjs.org. -Install the module: +Install the module using your Node.js package manager of choice: -```sh +```sh npm2yarn npm install update-electron-app ``` -Invoke the updater from your app's main process file: +Then, invoke the updater from your app's main process file: -```js +```js title="main.js" require('update-electron-app')() ``` By default, this module will check for updates at app startup, then every ten -minutes. When an update is found, it will automatically be downloaded in the background. When the download completes, a dialog is displayed allowing the user -to restart the app. +minutes. When an update is found, it will automatically be downloaded in the background. +When the download completes, a dialog is displayed allowing the user to restart the app. If you need to customize your configuration, you can -[pass options to `update-electron-app`][update-electron-app] +[pass options to update-electron-app][update-electron-app] or [use the update service directly][update.electronjs.org]. -## Deploying an Update Server +## Using other update services If you're developing a private Electron application, or if you're not publishing releases to GitHub Releases, it may be necessary to run your own update server. +### Step 1: Deploying an update server + Depending on your needs, you can choose from one of these: - [Hazel][hazel] – Update server for private or open-source apps which can be -deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases] -and leverages the power of GitHub's CDN. + deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases] + and leverages the power of GitHub's CDN. - [Nuts][nuts] – Also uses [GitHub Releases][gh-releases], but caches app -updates on disk and supports private repositories. + updates on disk and supports private repositories. - [electron-release-server][electron-release-server] – Provides a dashboard for -handling releases and does not require releases to originate on GitHub. + handling releases and does not require releases to originate on GitHub. - [Nucleus][nucleus] – A complete update server for Electron apps maintained by -Atlassian. Supports multiple applications and channels; uses a static file store -to minify server cost. + Atlassian. Supports multiple applications and channels; uses a static file store + to minify server cost. + +Once you've deployed your update server, you can instrument your app code to receive and +apply the updates with Electron's [autoUpdater] module. -## Implementing Updates in Your App +### Step 2: Receiving updates in your app -Once you've deployed your update server, continue with importing the required -modules in your code. The following code might vary for different server -software, but it works like described when using -[Hazel][hazel]. +First, import the required modules in your main process code. The following code might +vary for different server software, but it works like described when using [Hazel][hazel]. -**Important:** Please ensure that the code below will only be executed in -your packaged app, and not in development. You can use -[electron-is-dev](https://github.com/sindresorhus/electron-is-dev) to check for -the environment. +:::warning Check your execution environment! -```javascript +Please ensure that the code below will only be executed in your packaged app, and not in development. +You can use the [app.isPackaged](../api/app.md#appispackaged-readonly) API to check the environment. + +::: + +```javascript title='main.js' const { app, autoUpdater, dialog } = require('electron') ``` -Next, construct the URL of the update server and tell +Next, construct the URL of the update server feed and tell [autoUpdater](../api/auto-updater.md) about it: -```javascript +```javascript title='main.js' const server = 'https://your-deployment-url.com' const url = `${server}/update/${process.platform}/${app.getVersion()}` @@ -87,32 +97,32 @@ autoUpdater.setFeedURL({ url }) As the final step, check for updates. The example below will check every minute: -```javascript +```javascript title='main.js' setInterval(() => { autoUpdater.checkForUpdates() }, 60000) ``` -Once your application is [packaged](../tutorial/application-distribution.md), +Once your application is [packaged](./application-distribution.md), it will receive an update for each new [GitHub Release](https://help.github.com/articles/creating-releases/) that you publish. -## Applying Updates +### Step 3: Notifying users when updates are available Now that you've configured the basic update mechanism for your application, you need to ensure that the user will get notified when there's an update. This -can be achieved using the autoUpdater API -[events](../api/auto-updater.md#events): +can be achieved using the [autoUpdater API events](../api/auto-updater.md#events): -```javascript +```javascript title="main.js" autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { const dialogOpts = { type: 'info', buttons: ['Restart', 'Later'], title: 'Application Update', message: process.platform === 'win32' ? releaseNotes : releaseName, - detail: 'A new version has been downloaded. Restart the application to apply the updates.' + detail: + 'A new version has been downloaded. Restart the application to apply the updates.', } dialog.showMessageBox(dialogOpts).then((returnValue) => { @@ -125,16 +135,22 @@ Also make sure that errors are [being handled](../api/auto-updater.md#event-error). Here's an example for logging them to `stderr`: -```javascript -autoUpdater.on('error', message => { +```javascript title="main.js" +autoUpdater.on('error', (message) => { console.error('There was a problem updating the application') console.error(message) }) ``` -## Handling Updates Manually +:::info Handling updates manually + +Because the requests made by autoUpdate aren't under your direct control, you may find situations +that are difficult to handle (such as if the update server is behind authentication). The `url` +field supports the `file://` protocol, which means that with some effort, you can sidestep the +server-communication aspect of the process by loading your update from a local directory. +[Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990). -Because the requests made by Auto Update aren't under your direct control, you may find situations that are difficult to handle (such as if the update server is behind authentication). The `url` field does support files, which means that with some effort, you can sidestep the server-communication aspect of the process. [Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990). +::: [vercel]: https://vercel.com [hazel]: https://github.com/vercel/hazel