Skip to content

Commit

Permalink
chore(electron): upgrade Electron to v17
Browse files Browse the repository at this point in the history
In the process, it was easier to switch from the seemingly not really maintained `electron-forge` to using `electron-builder` and `electron` directly. This did introduce one wrinkle: `electron-builder` uses `fpm` to build `.deb` packages, which itself depends on Ruby. `electron-builder` tries to use a self-contained `fpm` and Ruby build, but it doesn't work on ARM yet (electron-userland/electron-builder#5154). I work around this problem by telling `electron-build` (via an environment variable) to use the system version of `fpm`, then ensure that Ruby and the `fpm` gem are installed in `install-dependencies.sh`.
  • Loading branch information
eventualbuddha committed Apr 27, 2022
1 parent 6e7e18d commit 6720727
Show file tree
Hide file tree
Showing 19 changed files with 1,164 additions and 1,114 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ typings/
# Webpack
.webpack/

# Electron-Forge
# Build artifacts
out/
dist/
build/
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ Generic kiosk-mode browser.
1. Install [NodeJS](https://nodejs.org/en/) and [Yarn](https://www.yarnpkg.com/en/).
1. Install native package dependencies with `make install`.
1. Build a Debian/Ubuntu package with `make build`.
1. Copy `out/make/deb/x64/kiosk-browser_*.deb` wherever you want to install it (note wildcard).
1. Install it with `sudo dpkg -i kiosk-browser_*.deb` (note wildcard).
1. Install it with `sudo dpkg -i dist/kiosk-browser_*.deb`.
1. Run with the URL you want to visit as a CLI argument (e.g. `kiosk-browser https://example.com/`) or with an environment variable (e.g. `KIOSK_BROWSER_URL=https://example.com/ kiosk-browser`).

## Kiosk Page API
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
'!src/index.ts',
'!src/preload.ts',
],
modulePathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/out/'],
modulePathIgnorePatterns: ['<rootDir>/build/', '<rootDir>/dist/', '<rootDir>/out/'],
coverageThreshold: {
// TODO: ratchet these up to 100 over time
global: {
Expand Down
38 changes: 18 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
"email": "[email protected]",
"url": "https://voting.works"
},
"main": "dist/src/index.js",
"main": "build/src/index.js",
"scripts": {
"app:dir": "electron-builder --dir",
"app:dist": "USE_SYSTEM_FPM=true electron-builder",
"postinstall": "electron-builder install-app-deps",
"lint": "eslint 'src/**/*.ts'",
"lint:fix": "yarn lint --fix",
"make": "tsc && electron-forge make",
"package": "tsc && electron-forge package",
"publish": "tsc && electron-forge publish",
"start": "tsc && electron-forge start",
"start": "tsc && electron .",
"test": "jest",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch"
Expand All @@ -30,17 +30,6 @@
"*.ts": "eslint --quiet --fix",
"package.json": "sort-package-json"
},
"config": {
"forge": {
"packagerConfig": {},
"makers": [
{
"name": "@electron-forge/maker-deb",
"config": {}
}
]
}
},
"dependencies": {
"@types/luxon": "^1.25.0",
"chalk": "^3.0.0",
Expand All @@ -50,14 +39,12 @@
"multimatch": "^4.0.0",
"rxjs": "^6.5.4",
"tmp-promise": "^3.0.3",
"usb-detection": "votingworks/node-usb-detection",
"usb-detection": "^4.14.1",
"uuid": "^8.3.1",
"xrandr-parse": "^1.0.0",
"zod": "^1.10.2"
},
"devDependencies": {
"@electron-forge/cli": "6.0.0-beta.47",
"@electron-forge/maker-deb": "6.0.0-beta.47",
"@types/debug": "^4.1.5",
"@types/electron-json-storage": "^4.0.0",
"@types/jest": "^24.9.0",
Expand All @@ -67,7 +54,8 @@
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^2.15.0",
"@typescript-eslint/parser": "^2.15.0",
"electron": "7.2.4",
"electron": "17",
"electron-builder": "^23.0.3",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-prettier": "^3.1.2",
Expand All @@ -80,5 +68,15 @@
"ts-jest": "^24.3.0",
"typescript": "^3.7.0"
},
"build": {
"appId": "com.votingworks.kiosk-browser",
"files": [
"build/src/**/*"
],
"linux": {
"target": "deb",
"category": "Utility"
}
},
"productName": "kiosk-browser"
}
9 changes: 8 additions & 1 deletion script/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

set -euo pipefail

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

"${DIR}/verify-dependencies.sh"

# Install dependencies if not already installed.
yarn install

# Build the TypeScript files.
yarn tsc

# Build .deb file.
yarn make
yarn app:dist
23 changes: 13 additions & 10 deletions script/install-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@ set -euo pipefail

SUDO=sudo
OS=$(lsb_release -cs)
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

if [ "$(id --user)" = 0 ]; then
SUDO=
fi

# Install native package build dependencies.
$SUDO apt-get install -y build-essential libudev-dev unzip git python pmount cups-client
$SUDO apt-get install -y \
build-essential \
libudev-dev \
unzip \
git \
python \
pmount \
cups-client \
ruby

if [[ $OS == "bullseye" ]]; then
sudo apt install -y libgtk-3-0 libnotify4 libxss1 libxtst6 xdg-utils libatspi2.0-0 kde-cli-tools trash-cli libglib2.0-bin gvfs-bin cups cups-bsd
$SUDO apt install -y libgtk-3-0 libnotify4 libxss1 libxtst6 xdg-utils libatspi2.0-0 kde-cli-tools trash-cli libglib2.0-bin gvfs-bin cups cups-bsd
fi

if ! command -v node >/dev/null 2>&1; then
echo "node not found. please install it before continuing: https://nodejs.org/en/" >&2
exit 127
fi
$SUDO gem install fpm

if ! command -v yarn >/dev/null 2>&1; then
echo "yarn not found. please install it before continuing: https://www.yarnpkg.com/en/" >&2
exit 127
fi
"${DIR}/verify-dependencies.sh"

# Install kiosk-browser dependencies.
yarn install
18 changes: 18 additions & 0 deletions script/verify-dependencies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -euo pipefail

if ! command -v node >/dev/null 2>&1; then
echo "node not found. please install it before continuing: https://nodejs.org/en/" >&2
exit 127
fi

if ! command -v yarn >/dev/null 2>&1; then
echo "yarn not found. please install it before continuing: https://www.yarnpkg.com/en/" >&2
exit 127
fi

if ! command -v fpm >/dev/null 2>&1; then
echo "fpm not found. please install it before continuing: https://fpm.readthedocs.io/en/latest/" >&2
exit 127
fi
4 changes: 2 additions & 2 deletions src/ipc/file-system-make-directory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test('fails given a non-absolute path', async () => {
})

test('makes a directory non-recursively by default', async () => {
mkdirMock.mockResolvedValueOnce()
mkdirMock.mockResolvedValueOnce(undefined)

await makeDirectory(
[
Expand All @@ -51,7 +51,7 @@ test('makes a directory non-recursively by default', async () => {
})

test('makes a directory recursively if requested', async () => {
mkdirMock.mockResolvedValueOnce()
mkdirMock.mockResolvedValueOnce(undefined)

await makeDirectory(
[
Expand Down
10 changes: 7 additions & 3 deletions src/ipc/file-system-read-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ test('read files with an encoding', async () => {
),
).toEqual('file content as a string')

expect(fs.readFile).toHaveBeenCalledWith('/a/path/to/file.txt', 'utf8')
expect(fs.readFile).toHaveBeenCalledWith('/a/path/to/file.txt', {
encoding: 'utf8',
})
})

test('read files without an encoding', async () => {
Expand All @@ -47,7 +49,9 @@ test('read files without an encoding', async () => {
),
).toEqual(Buffer.of(1, 2, 3))

expect(fs.readFile).toHaveBeenCalledWith('/a/path/to/file.png', undefined)
expect(fs.readFile).toHaveBeenCalledWith('/a/path/to/file.png', {
encoding: undefined,
})
})

test('registers a handler to read files', async () => {
Expand All @@ -72,5 +76,5 @@ test('registers a handler to read files', async () => {
await ipcRenderer.invoke(fileSystemReadFileChannel, '/a/path', 'utf8'),
).toEqual('hello world')

expect(fs.readFile).toHaveBeenCalledWith('/a/path', 'utf8')
expect(fs.readFile).toHaveBeenCalledWith('/a/path', { encoding: 'utf8' })
})
6 changes: 3 additions & 3 deletions src/ipc/file-system-read-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ export async function readFile(
origin: string,
permissions: readonly OriginFilePermission[],
path: string,
encoding?: string,
encoding?: BufferEncoding,
): Promise<Buffer | string> {
if (!isAbsolute(path)) {
debug('aborting request because it is not an absolute path')
throw new Error(`requested path is not absolute: ${path}`)
}

assertHasReadAccess(permissions, origin, path)
return await fs.readFile(path, encoding)
return await fs.readFile(path, { encoding })
}

export default function register(
Expand All @@ -30,7 +30,7 @@ export default function register(
): void {
ipcMain.handle(
channel,
(event: IpcMainInvokeEvent, path: string, encoding?: string) => {
(event: IpcMainInvokeEvent, path: string, encoding?: BufferEncoding) => {
const url = new URL(event.sender.getURL())
return readFile(url.origin, options.originFilePermissions, path, encoding)
},
Expand Down
1 change: 1 addition & 0 deletions src/ipc/get-printer-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export async function getPrinterInfo(
}
return {
name: printer.name,
displayName: printer.displayName,
isDefault: printer.isDefault,
options: printer.options,
description: printer.description,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/getPreferredPrinter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ test('gets the first printer if there is no default printer', () => {
getPreferredPrinter([
{
description: 'printer #1',
displayName: 'printer #1',
options: {},
isDefault: false,
name: 'printer #1',
status: 0,
},
{
description: 'printer #2',
displayName: 'printer #2',
options: {},
isDefault: false,
name: 'printer #2',
status: 0,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/usb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ function deviceKey({

export class USBDetectionManager {
private refCount = 0
private readonly usbDetection: typeof import('usb-detection').default
private readonly usbDetection: typeof import('usb-detection')

/**
* Build a manager for `usbDetection`.
*/
public constructor(usbDetection: typeof import('usb-detection').default) {
public constructor(usbDetection: typeof import('usb-detection')) {
this.usbDetection = usbDetection
}

Expand Down
4 changes: 4 additions & 0 deletions test/fakePrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export function fakeElectronPrinter(
): Electron.PrinterInfo {
return {
description: printer.name ?? 'main printer',
displayName: printer.displayName ?? 'main printer',
options: printer.options ?? {},
isDefault: true,
name: 'main printer',
status: 0,
Expand All @@ -33,6 +35,8 @@ export function fakePrinterInfo(
return {
name: electronPrinter.name,
description: electronPrinter.description,
displayName: electronPrinter.displayName,
options: electronPrinter.options,
isDefault: electronPrinter.isDefault,
state: IppPrinterState.Idle,
connected: true,
Expand Down
8 changes: 4 additions & 4 deletions test/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ export function fakeIpc(

const listeners = new Map<string, IpcMainListener>()

const ipcMain: Partial<IpcMain> = {
const ipcMain = ({
...((testEventEmitter() as unknown) as NodeJS.EventEmitter),
handle: jest.fn(function handle(
channel: string,
listener: IpcMainListener,
): void {
listeners.set(channel, listener)
}),
}
} as unknown) as Partial<IpcMain>

const ipcRenderer: Partial<IpcRenderer> = {
const ipcRenderer = ({
...((testEventEmitter() as unknown) as NodeJS.EventEmitter),
invoke: jest.fn(async function invoke(
channel: string,
Expand All @@ -91,7 +91,7 @@ export function fakeIpc(
),
)
}),
}
} as unknown) as Partial<IpcRenderer>

return {
ipcMain: (ipcMain as unknown) as IpcMain,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": ".",
"outDir": "dist",
"outDir": "build",
"moduleResolution": "node",
"resolveJsonModule": true,
"esModuleInterop": true,
Expand Down
7 changes: 2 additions & 5 deletions types/electron.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
declare namespace Electron {
interface PrinterInfo {
/**
* Add printer options such as `printer-is-accepting-jobs`.
*/
options?: { [key: string]: string }
interface Options {
'device-uri'?: string
}
}
Loading

0 comments on commit 6720727

Please sign in to comment.