How build Electron app for every plateforms
Olyno
Posted on May 21, 2020
A few days ago, I created an application with ElectronJs. The problem I had when creating it was to make my application available to everyone, regardless of OS and platform.
To do so, I had to be interested in several tools, including Electron Forge, electron-packager and electron-builder.
After several tries with Electron Forge, I realized that it was not stable enough, and that it was not possible to compile for multi-platforms at the moment.
So I went to electron-packager. Even if this tool is very efficient, it's very difficult to customize it, like adding a custom icon to the application.
So I went to electron-builder. Once I understood the documentation, it became very easy to use it.
I also had another problem: to automate the build. Indeed, I code under Windows. It becomes impossible to build the application for Linux and Mac. So I had to use an alternative tool. My choice went to Github and its Github Actions.
Well, let's start the explanations in code form:
Github Action
name: Build <App name>
on:
release:
types:
- published
jobs:
build:
name: Build <App name>
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Setup NodeJs
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install dependencies
run: yarn
- name: Build
run: yarn export
- name: Upload builds to release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.ref }}
files: out/*.*
draft: true
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
- name: Upload Nightly Build
uses: actions/upload-artifact@v2
if: success()
with:
name: <App name>-nightly
path: out/**/*!(.zip)
What I'm doing above is what is called a Github Action. It allows me to automate my tasks. In this one I tell him that at each release he will have to execute certain tasks.
Here I ask him to perform simple tasks:
1) Clone my repository
2) Prepare NodeJs
3) Install the dependencies
4) Export the application
5) Send what was exported to the release that was released
6) (Optional) Create a nightly build.
It is important to know one thing: electron-builder will create unpacked versions of your application. This means that these are folders containing the application available for any platform. If we want to put this version in our release, we have to compress it, which is not done automatically.
To do so, we need a script when we export it
Export script
const pngToIco = require('png-to-ico');
const fs = require('fs-extra');
const ora = require('ora');
const path = require('path');
const zip = require('bestzip');
const args = process.argv;
const plateforms = args.pop().replace(/^-/g, '').split('');
function getValidPlateforms() {
const spinner = ora({
text: 'Searching current platform build...',
spinner: 'line',
color: 'cyan'
}).start();
if (process.platform === 'win32') {
if (plateforms.includes('w')) {
spinner.succeed('Plateform found: ' + process.platform + ' (Only Windows build available)');
return ['w'];
} else {
spinner.fail('Plateform not compatible');
throw new Error('Can\'t compile to Windows: not compatible OS');
}
} else {
spinner.succeed('Plateform found: ' + process.platform + ' (All builds available)');
return plateforms;
}
}
async function zipBuilds() {
const spinner = ora({
text: 'Zip builds...',
spinner: 'line',
color: 'cyan'
}).start();
return fs.readdir('out')
.then(files => {
const statsJobs = [];
for (const file of files) {
const filePath = path.join('out', file);
statsJobs.push(fs.stat(filePath).then(stat => {
return { stat, filePath };
}));
}
return Promise.all(statsJobs);
})
.then(stats => {
const zipJobs = [];
for (const statInfos of stats) {
const { stat, filePath } = statInfos;
if (stat.isDirectory()) {
if (!fs.existsSync(filePath + '.zip')) {
zipJobs.push(
zip({
source: filePath,
destination: filePath + '.zip'
})
)
}
}
}
return Promise.all(zipJobs);
})
.then(() => spinner.succeed('All builds have been zipped with success'));
}
// TODO: Compile to ICNS file for Mac
if (!fs.existsSync('public/images/favicon.ico')) {
pngToIco('public/images/favicon.png')
.then(v => fs.writeFileSync('public/images/favicon.ico', v))
}
const validPlateforms = getValidPlateforms();
const build = require('child_process')
.exec('electron-builder build -' + validPlateforms.join('') + ' -c configs/build.yml');
const spinner = ora({
text: 'Building app...',
spinner: 'line',
color: 'cyan'
}).start();
build.stderr.on('data', data => console.error(data));
build.stdout.on('data', data => {
spinner.text = data;
});
['disconnect', 'exit'].forEach(listener => {
build.on(listener, () => {
spinner.succeed('Build completed');
zipBuilds();
});
});
This code is a little more complicated than the previous one. What it does is pretty straightforward. Apart from having a custom spinner with the ora module, I convert the icon to ico format, which is the windows image format, I check the user's platform to create either a Windows exclusive build or a Mac and Linux build, and finally I zip these builds so I can transfer them to my release.
Note that I did not find an interesting module to convert an image to mac format, it will have to be done from an online site.
Now we're almost done, we still have the configuration file to do. For this, we will create a file "build.yml" where we will put in the following configuration:
Application build configuration
appId: com.<your name>.<your app name in lower case, without spaces>
productName: <your app name>
directories:
output: out
mac:
category: <Category of your app> # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8
target:
- dmg
- mas
icon: public/images/favicon.icns
win:
icon: public/images/favicon.ico
target:
- portable
- squirrel
squirrelWindows:
iconUrl: "https://raw.githubusercontent.com/<your name>/<your app name>/master/favicon.ico"
remoteReleases: true
linux:
target:
- snap
- deb
- rpm
- pacman
icon: favicon.png
synopsis: <What is your app>
category: <Category of your app> # https://specifications.freedesktop.org/menu-spec/latest/apa.html#main-category-registry
I assume that all your files are at the root of your project.
Don't forget to add an access token for your Github Action.
And here we are, we just created our build automation based on Github Actions and a simple export script. Simply execute the export script to build your ElectronJs app.
Posted on May 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.