Set Up Continuous Deployment on Electron Using Squirrel

If you are a web developer, you would be amazed by the possibilities that a desktop application offers.
Just give a look at the applications listed on electron website to have a quick glance of the infinite opportunities offered by such a technology.
Few key features:

  • Access to the filesystem (see Atom)
  • Access to the webcam and mike (see Dischord)
  • Access to the command line interface within your app (see Hyper)

The problem is: How can you keep your speed and ease of reaching your users when you develop a desktop application ?

Meet, Squirrel:

Squirrel-Logo

This little open-source framework aims to simplify installers for desktop softwares.
When correctly set up, it enables your application to watch for new releases deployed on a server and to automatically update itself from the downloaded files.

Electron auto-updater gives you an API to easily plug Squirrel to your application.

This sounds great, but when I recently tried to implement this feature for a Windows application, I had a hard time to understand how every pieces fit together.
I will give you a quick glance of what I learned doing this, and explain how the update loop of a Squirrel application works.
The framework also works on Mac but the server implementation is slightly different:
To use Squirrel for Mac with Electron, check this article which helped me a lot when implementing the feature.

Prepare your application to watch Squirrel

Ok to have a common base of code, let’s say we will implement this feature on the Electron Quick Start and use it as an example.

This is a real minimal electron application and we will use only two files in it: Main.js and package.json.
Git clone the repository and here we go.

First thing to make your application listen to your Squirrel server, you’ll need to use the electron.auto-updater API.

Add this script which is going to make your app watch for server updates.

const electron = require('electron');
const squirrelUrl = "http://localhost:3333";

const startAutoUpdater = (squirrelUrl) => {
  // The Squirrel application will watch the provided URL
  electron.autoUpdater.setFeedURL(`${squirrelUrl}/win64/`);

  // Display a success message on successful update
  electron.autoUpdater.addListener("update-downloaded", (event, releaseNotes, releaseName) => {
    electron.dialog.showMessageBox({"message": `The release ${releaseName} has been downloaded`});
  });

  // Display an error message on update error
  electron.autoUpdater.addListener("error", (error) => {
    electron.dialog.showMessageBox({"message": "Auto updater error: " + error});
  });

  // tell squirrel to check for updates
  electron.autoUpdater.checkForUpdates();
}

app.on('ready', function (){
  // Add this condition to avoid error when running your application locally
  if (process.env.NODE_ENV !== "dev") startAutoUpdater(squirrelUrl)
});

Great, now your application will listen to the provided feedUrl. But as it is not wrapped yet into the Squirrel framework, you will have an error thrown when using it in dev mode.

To avoid this inconvenience, use the following command as your npm start in package.json:

NODE_ENV=dev electron .

When launched for the first time, Squirrel will need to restart or it will throw an error.
To handle this, add the following to your Main.js:

const handleSquirrelEvent = () => {
  if (process.argv.length === 1) {
    return false;
  }

  const squirrelEvent = process.argv[1];
  switch (squirrelEvent) {
    case '--squirrel-install':
    case '--squirrel-updated':
    case '--squirrel-uninstall':
      setTimeout(app.quit, 1000);
      return true;

    case '--squirrel-obsolete':
      app.quit();
      return true;
  }
}

if (handleSquirrelEvent()) {
  // squirrel event handled and app will exit in 1000ms, so don't do anything else
  return;
}

This script will read the option of the squirrel event when launching your application, giving you the ability to execute scripts at specific moments of the installation.
In this case, it will restart the application when installing it, updating it or uninstalling it.
You can as well do thing like add an shortcut icon on desktop when installing the application and remove it when uninstalling (check this documentation).

Your app is now ready to be packed :)

Let’s release our app!

Okay you have your wonderful app ready to be released!
We now need to package it, using for example the electron-packager.

Install the package :

npm install electron-packager --save-dev

And run this command to package your release :

./node_modules/.bin/electron-packager . MyAwesomeApp --platform=win32 --arch=x64 --out=release/package

Inside release/package/MyAwesomeApp-win32-x64 folder, you now have a MyAwesomeApp.exe file that you can run on Windows! Here is your first release of your wonderful app.

Now wrap it with Squirrel

We will now have to create a Windows installer for it that includes Squirrel.
The Electron team released lately the electron-winstaller package that does the job pretty well.

Install the package with:

npm install electron-winstaller --save-dev

Then create a build.js script like this one:

var electronInstaller = require('electron-winstaller');

resultPromise = electronInstaller.createWindowsInstaller({
    appDirectory: './release/MyAwesomeApp-win32-x64',
    outputDirectory: './release/installer',
    authors: 'Me',
    exe: 'MyAwesomeApp.exe'
  });

resultPromise.then(() => console.log("It worked!"), (e) => console.log(`No dice: ${e.message}`));

This file will tell Squirrel all it needs to know to create you an installer:

  • Where your app release is located
  • Where to put the new release
  • Where the entrypoint of your app is

Execute this script with node:

node ./build.js

Go and check in release/installer, you now have a ready to use Squirrel server!

It should look like this:

installer
├─ RELEASES
├─ MyAwesomeApp-0.0.1-full.nupkg
└─ Setup.exe

Distribute your application

The only thing you need now is to create a file server to serve this folder on internet. You can for example serve it with php:

php -s localhost:3333

Very simply, you can now distribute your application to your users with the Setup.exe file.
Go to http://localhost:3333/Setup.exe, this will download the Setup.exe file which will install MyAwesomeApp wrapped with Squirrel on your computer.

Run the Setup.exe file, and the application should be installed in C:\Users\Me\AppData\MyAwesomeApp\.
To run it, launch the MyAwesome.exe file.
You can as well create a shortcut on your desktop for later use.

Time to build a new release

Let’s now try to build a new version of our app and to release it!

First things first, let’s create a new feature:

alert('OMG such new feature!!');

Now bump the version from package.json.
This is compulsory if you want to create a new package, otherwise the previous one will be overwritten:

npm version patch

The 0.0.2 version of our app is ready!
Redo the process to build a new package.
To simplify this, we can write npm commands:

"scripts": {
  "build:package": "electron-packager . MyAwesomeApp --platform=win32 --arch=x64 --out=release/package",
  "build:winstaller": "node ./build.js",
  "build": "npm run build:package && npm run build:winstaller"
}

Run then:

npm run build

Check out the releases/installer, a new package appeared!
Your Squirrel server should now looks like this:

installer
├─ RELEASES
├─ MyAwesomeApp-0.0.1-full.nupkg
├─ MyAwesomeApp-0.0.2-diff.nupkg
├─ MyAwesomeApp-0.0.2-full.nupkg
└─ Setup.exe

When the magic happens

Open now your application: you can use the shortcut that you have created earlier or go to C:\Users\Me\AppData\MyAwesomeApp\.
Wait for around 20sec, and your app should reload and you will see your new wonderful feature appears!

Delivering package

What happened?

Let’s give a look at how the Squirrel app has been installed: the location should be C:\Users\Me\AppData\MyAwesomeApp\, where Me is your Windows username.
The application is bundled as follows:

MyAwesomeApp
│
├─ app-0.0.1                // This contains the packaged electron application version 0.0.1
│  ├─ MyAwesomeApp.exe
|  ├─ squirrel.exe
|  ...
│  └─ resources
│     ├─ app.asar           // This contains the source code of electron application version 0.0.1
│     └─ electron.asar
│
├─ packages                 // This contains the packages downloaded from Squirrel server
│  ├─ RELEASES
│  ├─ MyAwesomeApp-0.0.1-full.nupkg  
│  ...
│
├─ MyAwesomeApp.exe            // This will launch Update.exe and then the latest app installed
├─ SquirrelSetup.log
└─ Update.exe               // This is the Squirrel program used to Update application

So what’s happening when you click on the shortcut?

  • The entry point is /MyAwesomeApp.exe.
  • This will launch the latest local version of the application (here 0.0.1).
  • The entry point of the application is main.js.
  • It contains the startAutoUpdater function we added earlier which configures the squirrel updater through the electron.autoupdater API.
  • This will call /Update.exe (which is the main Squirrel program) to check for new releases.
  • Update.exe checks at the feedUrl set and download the remote RELEASES file.
  • Then, it compares this downloaded file to the local /packages/RELEASES file.
  • If there is a new version, it downloads it and unpack it into an app-0.0.2 folder.
  • Then the ‘update-downloaded’ event is triggered and the alert appears in MyAwesomeApp.
  • When launching again the MyAwesomeApp, the previous version is cleansed and the application is updated.

The easy part

To set up continuous deployment, deploy a new release on your server and Squirrel will do the rest!

You now know pretty everything about the Squirrel.Windows framework!
To dive deeper into the possibilities that it offers, open a terminal on Windows and run ./Update.exe into your project folder to display available documentation.

Hope this helps, don’t hesitate to give feedbacks!


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us

  • Erni Augusto

    Thanks a lot! Nice! … It did help me! But I am having some issues, the build is working, but when I try to start the app, gives me an error “Auto updater error: Error: Can not find Squirrel”.
    And also, when I try to install the app, even thou I try to install as administrator, gives me the following: “Please re-run this installer as a normal user instead of Run as Administrator”, if I try to install as normal user, it does ask to run as administrator.
    Could you help me with this issue?
    Once again, thanks a lot!!!