* feat(electron): add Linux AppImage releases * ci: cache Linux OpenCode CLI artifacts * fix(ci): await Linux release inventory check * fix(electron): add frameless window controls on Linux desktop Linux AppImages were created without native WM decorations and without in-app controls, leaving users unable to close the window with a mouse. Treat Linux like Windows: frameless BrowserWindow plus the existing WindowsWindowControls header buttons and app-menu entry. macOS keeps hidden title bar with traffic lights unchanged. Shared usesFramelessElectronChrome() helper drives main window, mini chat, header insets, and titlebar controls. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * feat(desktop): add configurable window controls position by OS Add desktopWindowControlsPosition setting (auto/left/right) with OS-aware defaults: Linux left, Windows right. Wire frameless chrome controls in Header, TitlebarLeftControls, and MiniChatLayout, plus a Sessions settings control for Windows and Linux desktop shells. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(desktop): address Linux AppImage release review findings Propagate updater capability errors to the UI, treat missing latest-linux.yml feeds as no-update, stop installed-apps IPC spam on Linux, document FUSE/AppImage limits, add CHANGELOG entry, migrate remaining btriapitsyn URLs, and run Electron Linux unit tests on PRs. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> --------- Co-authored-by: jibanez-staticduo <staticduo@gmail.com> Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
75 lines
2.7 KiB
JavaScript
75 lines
2.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import crypto from 'node:crypto';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import { verifyUpdateManifest } from './verify-update-manifest.mjs';
|
|
|
|
const fixture = (manifestName, artifactName, fields) => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-manifest-test-'));
|
|
const artifactPath = path.join(root, artifactName);
|
|
const manifestPath = path.join(root, manifestName);
|
|
const bytes = Buffer.from(`artifact:${artifactName}`);
|
|
fs.writeFileSync(artifactPath, bytes);
|
|
fs.writeFileSync(manifestPath, [
|
|
'version: 1.15.0',
|
|
'files:',
|
|
...(fields || [
|
|
` - url: ${artifactName}`,
|
|
` sha512: ${crypto.createHash('sha512').update(bytes).digest('base64')}`,
|
|
` size: ${bytes.length}`,
|
|
]),
|
|
`path: ${artifactName}`,
|
|
'releaseDate: 2026-07-10T00:00:00.000Z',
|
|
'',
|
|
].join('\n'));
|
|
return { root, artifactPath, manifestPath };
|
|
};
|
|
|
|
for (const [manifestName, artifactName] of [
|
|
['latest-linux.yml', 'OpenChamber-1.15.0-linux-x86_64.AppImage'],
|
|
['latest-linux-arm64.yml', 'OpenChamber-1.15.0-linux-arm64.AppImage'],
|
|
]) {
|
|
test(`validates architecture-specific ${manifestName}`, () => {
|
|
const value = fixture(manifestName, artifactName);
|
|
try {
|
|
assert.equal(verifyUpdateManifest({ ...value, expectedVersion: '1.15.0' }).name, artifactName);
|
|
} finally {
|
|
fs.rmSync(value.root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
}
|
|
|
|
test('accepts electron-builder field ordering and optional blockMapSize', () => {
|
|
const artifactName = 'OpenChamber-1.15.0-linux-x86_64.AppImage';
|
|
const bytes = Buffer.from(`artifact:${artifactName}`);
|
|
const value = fixture('latest-linux.yml', artifactName, [
|
|
` - sha512: ${crypto.createHash('sha512').update(bytes).digest('base64')}`,
|
|
` size: ${bytes.length}`,
|
|
' blockMapSize: 1234',
|
|
` url: ${artifactName}`,
|
|
]);
|
|
try {
|
|
assert.equal(verifyUpdateManifest({ ...value, expectedVersion: '1.15.0' }).name, artifactName);
|
|
} finally {
|
|
fs.rmSync(value.root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('rejects a manifest that points at the other architecture artifact', () => {
|
|
const value = fixture('latest-linux-arm64.yml', 'OpenChamber-1.15.0-linux-arm64.AppImage');
|
|
try {
|
|
const x64Artifact = path.join(value.root, 'OpenChamber-1.15.0-linux-x86_64.AppImage');
|
|
fs.copyFileSync(value.artifactPath, x64Artifact);
|
|
assert.throws(() => verifyUpdateManifest({
|
|
manifestPath: value.manifestPath,
|
|
artifactPath: x64Artifact,
|
|
expectedVersion: '1.15.0',
|
|
}), /artifact mismatch/);
|
|
} finally {
|
|
fs.rmSync(value.root, { recursive: true, force: true });
|
|
}
|
|
});
|