Signal-Desktop/ts/updater/linux.ts

111 lines
2.8 KiB
TypeScript

// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { version as osVersion } from 'node:os';
import { watch } from 'node:fs';
import { join } from 'node:path';
import { app, ipcMain } from 'electron';
import { extractFile } from '@electron/asar';
import z from 'zod';
import { markShouldQuit } from '../../app/window_state';
import type { LoggerType } from '../types/Logging';
import { DialogType } from '../types/Dialogs';
import * as Errors from '../types/errors';
import type { UpdaterOptionsType } from './common';
const MIN_UBUNTU_VERSION = '22.04';
const PackageSchema = z.object({
version: z.string(),
});
export function getUbuntuVersion(): string | undefined {
if (process.platform !== 'linux') {
return undefined;
}
const match = osVersion().match(/^#\d+~([\d.]+)-Ubuntu\s/);
if (!match) {
return undefined;
}
return match[1];
}
export function isLinuxVersionSupported(logger?: LoggerType): boolean {
const ubuntu = getUbuntuVersion();
if (ubuntu !== undefined && ubuntu < MIN_UBUNTU_VERSION) {
logger?.warn(
`updater/isLinuxVersionSupported: unsupported Ubuntu version ${ubuntu}`
);
return false;
}
return true;
}
export function initLinux({ logger, getMainWindow }: UpdaterOptionsType): void {
if (!app.isPackaged) {
throw new Error('Linux updates are not supported in development');
}
if (!isLinuxVersionSupported(logger)) {
getMainWindow()?.webContents.send(
'show-update-dialog',
DialogType.UnsupportedOS
);
}
ipcMain.handle('start-update', () => {
logger?.info('updater/linux: restarting');
markShouldQuit();
app.relaunch();
app.quit();
});
// In `postinst` script we run `touch .signal-postinst`.
// See our patch for app-builder-lib.
//
// /opt/Signal/resources/app.asar
const asarPath = join(__dirname, '..', '..');
if (!asarPath.endsWith('.asar')) {
throw new Error('updater/linux: not running from ASAR');
}
// /opt/Signal/.signal-postinst
const postinstFile = join(asarPath, '..', '..', '.signal-postinst');
watch(postinstFile, event => {
if (event !== 'change') {
return;
}
let version: string;
try {
const file = extractFile(asarPath, 'package.json').toString();
({ version } = PackageSchema.parse(JSON.parse(file)));
} catch (error) {
logger?.error(
'updater/linux: failed to parse updated asar',
Errors.toLogFormat(error)
);
return;
}
if (version === app.getVersion()) {
logger?.info('updater/linux: ignoring asar update, no version change');
return;
}
logger?.info(`updater/linux: asar updated to version=${version}`);
getMainWindow()?.webContents.send(
'show-update-dialog',
DialogType.AutoUpdate,
{
version,
}
);
}).unref();
}