Build ElectronJS apps for Rasberry Pi

ElectronJS allows you to more or less build a web application that can be distributed as a native application. The code is written in javascript and uses standard HTML and CSS for the presentation layer. The code is packaged with a chromium browser and distributed as an application. Can you really build powerful applications this way? Yes, among the more popular apps built on ElectronJS is Visual Studio Code, Slack, Discord and Skype. There are a lot more examples! From the same codebase, you can build Windows, macOS and Linux applications. This article will focus on building for Raspberry Pi and its armv7l architecture but this setup can also be used to create build pipelines for other architectures as well.

Background

I’m currently developing a client-server application for digital signage where the client app will run on a Raspberry Pi. Development of the server application and the client application is done on windows which lacks the ability to build the .deb packages for the Raspberry Pi armv7l architecture.

Developing on a windows machine for the Raspberry Pi can be challenging. By using ElectronJS I can test the application on windows and then deploy it to a Raspberry Pi without any issue. Since Visual Studio Code, used for the development, runs on both platforms I can easily clone the repo directly onto a Raspberry Pi to test things out. But the Raspberry Pi isn’t that powerful and development runs a lot smoother on my windows laptop.

In the past, the electron-builder had support for building Linux architectures on windows by using a remote build agent. That service has been discontinued probably due to cost since it’s an open-source project. But it can easily be done both locally on your windows machine or in a Google Cloud Build chain as well.

Originally I tried to build the package on windows but ended up with the error:

cannot get, wait error=Get “https://service.electron.build/find-build-agent?no-cache=1gto9fq”: dial tcp 51.15.76.176:443: connectex: No connection could be made because the target machine actively refused it.

Building locally

Since the electron-builder can only build Linux packages while running on Linux we need a Linux base for this. Instead of using a second machine or a VM we can use a docker container. Since the development in ElectronJS is coupled with NodeJS and we have NPM we can use the official node docker container to build upon since it is in turn based on Debian Linux.

So by creating a simple docker container based on node we get the NPM package manager for free and can just install electron-builder in our new docker image. The Dockerfile is only three lines.

FROM node:14
RUN npm install -g electron-builder
ENTRYPOINT ["electron-builder"]

On windows, you can use Docker Desktop and then just run all the commands in PowerShell to build and use the container.

docker build -t electron-builder .
docker run --rm -it --workdir /workspace \
-v C:\users\Administrator\Documents\Projects\screenis\electron_app\:/workspace \
electron-builder --linux deb --armv7l

In the example above we map the ElectronJS project to /workspace in the container and execute.
Supplying the –linux deb parameter will build a Linux deb package. The –armv7l specifies the architecture, in this case, armv7l used by the Raspberry Pi. The output will be in the /dist folder and can then be installed with sudo dpkg -i <package file> on the Raspberry Pi.

Please note that you have to run npm install on your project prior to using electron-builder.

Google Cloud Build

This container can be integrated into your automatic builds on Google Cloud Build or another CD engine that you use. In this example, we do both the npm install step and packaging in the same chain. First we need to build the container image and make it available to the build service.

git clone https://github.com/kallsbo/cloud-builders.git
cd cloud-builders/electron-builder
gcloud builds submit .

Once that is in place we can create a trigger running our build file. I will not go into detail on setting up the build chains or build triggers, I have written about it in the past and it’s well documented in the Google Cloud Build documentation as well. So the package.yaml simply looks like this.

steps:
- name: node
  id: install
  entrypoint: npm
  args: ['install']

- name: gcr.io/$PROJECT_ID/electron-builder
  id: package
  waitFor:
    - install
  args: ['--linux', 'deb', '--armv7l']

artifacts:
  objects:
    location: 'gs://artifacts.$PROJECT_ID.appspot.com/rpi-client-app-deb/'
    paths: ['dist/*.deb']

Besides the two build steps, this also uploads the artifact, in this case the .deb file to a storage bucket. I just used the default artifact bucket containing the build containers for this project.

All the source files and examples can be found on my Github.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: