mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(packaging): restore Snap desktop runtime (#1406)
* fix(packaging): restore Snap desktop runtime * docs(packaging): publish Snap launch repair note * fix(packaging): declare Node 22.12 floor * docs(architecture): update SQLite pin rationale * fix(tooling): align Node engine floor * fix(tooling): constrain supported Node releases * docs(architecture): correct node-abi consumer
This commit is contained in:
1 parent
2a7d7c315e
commit
728df1a68c
11 files changed
+404
-660
No files matched your search
@@ -0,0 +1,6 @@
|
||||
---
|
||||
type: fix
|
||||
area: packaging
|
||||
---
|
||||
|
||||
Snap packages now launch correctly with the desktop runtime scripts required by their generated startup command.
|
||||
@@ -496,7 +496,10 @@ Key files:
|
||||
`$SNAP/graphics/drirc.d`. The provider is external shared content, not part
|
||||
of IPTVnator's package size, source archive, or notices. Installed-Snap CI
|
||||
must prove controlled unavailable exit after disconnect, then reconnect and
|
||||
prove success. The helper links `libGL.so.1` rather than `libOpenGL.so.0`.
|
||||
prove success. Static artifact verification requires regular
|
||||
`desktop-init.sh`, `desktop-common.sh`, and `desktop-gnome-specific.sh`
|
||||
files at the Snap root, with `desktop-init.sh` executable. The helper links
|
||||
`libGL.so.1` rather than `libOpenGL.so.0`.
|
||||
- The probe and playback helper share one sanitized loader environment:
|
||||
ambient audit, preload, library, graphics-driver, and shell-startup overrides
|
||||
are removed; the validated private closure wins; trusted Snap GL,
|
||||
|
||||
@@ -974,8 +974,11 @@ engine` (restart required) or
|
||||
default provider. Its only provider-data layouts bind `/usr/share/libdrm`
|
||||
from `$SNAP/graphics/libdrm` and symlink `/usr/share/drirc.d` to
|
||||
`$SNAP/graphics/drirc.d`. Installed-Snap CI requires controlled unavailable
|
||||
status after disconnect, then reconnects and requires success. The helper
|
||||
links `libGL.so.1`, and probe/playback share a sanitized loader environment
|
||||
status after disconnect, then reconnects and requires success. Static
|
||||
artifact verification requires regular `desktop-init.sh`,
|
||||
`desktop-common.sh`, and `desktop-gnome-specific.sh` files at the Snap root,
|
||||
with `desktop-init.sh` executable. The helper links `libGL.so.1`, and
|
||||
probe/playback share a sanitized loader environment
|
||||
in which ambient audit, preload, library, graphics-driver, and shell-startup
|
||||
overrides are removed; the validated private closure plus trusted host GL,
|
||||
graphics-content, core22 base x64, and exact GNOME-platform roots have
|
||||
|
||||
@@ -266,7 +266,7 @@ needed.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Node.js with pnpm (via Corepack)
|
||||
- Node.js 22.13–22.x or 24 and newer with pnpm (via Corepack)
|
||||
|
||||
1. Clone this repository and install project dependencies:
|
||||
|
||||
|
||||
@@ -365,7 +365,11 @@ missing, non-directory, symlinked, non-empty, or incorrectly permissioned
|
||||
target. It also requires exactly the canonical provider-data layouts:
|
||||
`/usr/share/libdrm` binds from `$SNAP/graphics/libdrm`, and
|
||||
`/usr/share/drirc.d` symlinks to `$SNAP/graphics/drirc.d`. No additional or
|
||||
duplicate layout entry is accepted.
|
||||
duplicate layout entry is accepted. Static extraction verification also
|
||||
requires regular `desktop-init.sh`, `desktop-common.sh`, and
|
||||
`desktop-gnome-specific.sh` files at the Snap root, with `desktop-init.sh`
|
||||
executable, because Electron Builder's generated `command.sh` invokes that
|
||||
runtime before the application binary.
|
||||
|
||||
Private shared memory gives the app a confined, snap-specific POSIX shm
|
||||
namespace rather than global cross-snap access. The packaging-only
|
||||
|
||||
@@ -305,18 +305,25 @@ Window decorations on Linux (shadows, corners):
|
||||
|
||||
Toolchain notes for the Electron 41 upgrade:
|
||||
|
||||
1. `better-sqlite3` is pinned to exactly `12.9.0` — the last release that
|
||||
ships prebuilt binaries for BOTH Node 20 (ABI 115, used by Jest) and
|
||||
Electron 41 (ABI 145, used at runtime). `12.10.0` dropped the Node 20
|
||||
prebuilds, which forces a from-source build that fails on machines
|
||||
without a C++ toolchain.
|
||||
1. `better-sqlite3` remains pinned to exactly `12.9.0` so native dependency
|
||||
updates happen deliberately with database-worker, packaging, and Electron
|
||||
E2E validation. The former Node 20 prebuild constraint no longer applies
|
||||
now that the supported development floor is Node 22.13; revisit this pin
|
||||
in a dedicated native-dependency update.
|
||||
2. The pnpm override `node-abi@3.85.0 -> 3.92.0` is required so
|
||||
`@electron/rebuild` (via `electron-builder install-app-deps`) can map
|
||||
Electron 41 to its ABI.
|
||||
3. Local development needs **Node >= 22.12**, declared in `engines`.
|
||||
`electron-builder` 26.15.3 pulls `@electron/rebuild` 4, which sets that
|
||||
floor, and the root `postinstall` runs `install-app-deps` on every
|
||||
`pnpm install`. CI already runs Node 22.
|
||||
`better-sqlite3`'s `prebuild-install` can map Electron 41 to ABI 145 when
|
||||
`electron-builder install-app-deps` rebuilds native modules. The 3.85
|
||||
registry stops at Electron 40, while 3.92 includes Electron 41;
|
||||
`@electron/rebuild` itself now resolves `node-abi` 4.x.
|
||||
3. Local development supports **Node 22.13–22.x or Node >= 24**, declared in
|
||||
`engines` as `^22.13.0 || >=24.0.0`.
|
||||
The direct `@faker-js/faker` dependency and current lint tooling require
|
||||
that floor. `electron-builder` 26.15.7 also pulls `@electron/rebuild` 4,
|
||||
which requires Node 22.12 or newer, and the root `postinstall` runs
|
||||
`install-app-deps` on every `pnpm install`. The 26.15.7 minimum also
|
||||
carries the v26 backport that fully extracts the Snap template's `.tar.7z`
|
||||
payload; 26.15.0–26.15.6 can
|
||||
produce a Snap that is missing `desktop-init.sh`. CI already runs Node 22.
|
||||
|
||||
Known caveats:
|
||||
|
||||
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"name": "iptvnator",
|
||||
"version": "0.23.0",
|
||||
"engines": {
|
||||
"node": "^22.13.0 || >=24.0.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"description": "IPTV player application.",
|
||||
"homepage": "https://github.com/4gray/iptvnator",
|
||||
@@ -189,7 +192,7 @@
|
||||
"cors": "2.8.6",
|
||||
"drizzle-kit": "0.31.10",
|
||||
"electron": "^41.10.3",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-builder": "^26.15.7",
|
||||
"electron-playwright-helpers": "1.8.2",
|
||||
"esbuild": "0.28.1",
|
||||
"eslint": "^9.8.0",
|
||||
|
||||
Generated
+284
-643
File diff suppressed because it is too large.
Load diff
@@ -1235,6 +1235,15 @@ test('hashes the final source archive bytes and reads the exact packaged Snap bi
|
||||
const graphicsRoot = path.join(snapSourceRoot, 'graphics');
|
||||
fs.mkdirSync(graphicsRoot, { mode: 0o755 });
|
||||
fs.chmodSync(graphicsRoot, 0o755);
|
||||
for (const script of [
|
||||
'desktop-init.sh',
|
||||
'desktop-common.sh',
|
||||
'desktop-gnome-specific.sh',
|
||||
]) {
|
||||
const desktopScriptPath = path.join(snapSourceRoot, script);
|
||||
fs.writeFileSync(desktopScriptPath, '#!/bin/sh\n');
|
||||
fs.chmodSync(desktopScriptPath, 0o755);
|
||||
}
|
||||
const asarSourceRoot = path.join(temporaryRoot, 'asar-source');
|
||||
const appAsarPath = path.join(appRoot, 'resources', 'app.asar');
|
||||
fs.mkdirSync(asarSourceRoot);
|
||||
|
||||
@@ -34,6 +34,11 @@ const {
|
||||
const scriptPath = fileURLToPath(import.meta.url);
|
||||
const LIBMPV_DEPENDENCY_PATTERN = /^libmpv\.so(?:\.|$)/;
|
||||
const SNAP_METADATA_MAX_BYTES = 256 * 1024;
|
||||
const SNAP_DESKTOP_RUNTIME_SCRIPTS = [
|
||||
'desktop-init.sh',
|
||||
'desktop-common.sh',
|
||||
'desktop-gnome-specific.sh',
|
||||
];
|
||||
// Keep this set identical to the packaged helper sanitizer in
|
||||
// embedded-mpv-frame-copy-runtime/helper-environment.ts. Feature/debug
|
||||
// selectors such as LIBGL_ALWAYS_SOFTWARE remain intentionally available.
|
||||
@@ -715,6 +720,35 @@ export function validateExtractedSnapMetadata(extractionRoot) {
|
||||
];
|
||||
}
|
||||
const errors = [];
|
||||
for (const script of SNAP_DESKTOP_RUNTIME_SCRIPTS) {
|
||||
const desktopScriptPath = path.join(extractionRoot, script);
|
||||
let desktopScriptStat;
|
||||
try {
|
||||
desktopScriptStat = fs.lstatSync(desktopScriptPath);
|
||||
} catch {
|
||||
errors.push(
|
||||
`Missing required desktop runtime script: ${desktopScriptPath}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!desktopScriptStat.isFile() ||
|
||||
desktopScriptStat.isSymbolicLink()
|
||||
) {
|
||||
errors.push(
|
||||
`Extracted Snap desktop runtime script must be a regular file: ${desktopScriptPath}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
script === 'desktop-init.sh' &&
|
||||
(desktopScriptStat.mode & 0o111) === 0
|
||||
) {
|
||||
errors.push(
|
||||
`Extracted Snap desktop-init.sh must be executable: ${desktopScriptPath}`
|
||||
);
|
||||
}
|
||||
}
|
||||
const graphicsMountPath = path.join(extractionRoot, 'graphics');
|
||||
let graphicsMountStat;
|
||||
try {
|
||||
|
||||
@@ -96,6 +96,19 @@ const DEB_SYSTEM_PACKAGE_DEPENDENCIES = [
|
||||
'libgl1',
|
||||
'libgbm1',
|
||||
];
|
||||
const SNAP_DESKTOP_RUNTIME_SCRIPTS = [
|
||||
'desktop-init.sh',
|
||||
'desktop-common.sh',
|
||||
'desktop-gnome-specific.sh',
|
||||
];
|
||||
|
||||
function writeSnapDesktopRuntimeScripts(root) {
|
||||
for (const script of SNAP_DESKTOP_RUNTIME_SCRIPTS) {
|
||||
const scriptPath = path.join(root, script);
|
||||
fs.writeFileSync(scriptPath, '#!/bin/sh\n');
|
||||
fs.chmodSync(scriptPath, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
test('uses the shared frozen runtime probe resource contract', async () => {
|
||||
assert.equal(
|
||||
@@ -1239,6 +1252,7 @@ test('requires exact Snap graphics layouts and plugs used by the app', () => {
|
||||
const snapYamlPath = path.join(root, 'meta', 'snap.yaml');
|
||||
fs.mkdirSync(path.dirname(snapYamlPath), { recursive: true });
|
||||
fs.mkdirSync(path.join(root, 'graphics'));
|
||||
writeSnapDesktopRuntimeScripts(root);
|
||||
|
||||
const validSnapYaml = [
|
||||
'name: iptvnator',
|
||||
@@ -1275,6 +1289,24 @@ test('requires exact Snap graphics layouts and plugs used by the app', () => {
|
||||
fs.writeFileSync(snapYamlPath, validSnapYaml);
|
||||
assert.deepEqual(validateExtractedSnapMetadata(root), []);
|
||||
|
||||
for (const script of SNAP_DESKTOP_RUNTIME_SCRIPTS) {
|
||||
const scriptPath = path.join(root, script);
|
||||
fs.rmSync(scriptPath);
|
||||
assert.match(
|
||||
validateExtractedSnapMetadata(root).join('\n'),
|
||||
new RegExp(`required desktop runtime script.*${script}`, 'i')
|
||||
);
|
||||
fs.writeFileSync(scriptPath, '#!/bin/sh\n');
|
||||
fs.chmodSync(scriptPath, 0o755);
|
||||
}
|
||||
|
||||
fs.chmodSync(path.join(root, 'desktop-init.sh'), 0o644);
|
||||
assert.match(
|
||||
validateExtractedSnapMetadata(root).join('\n'),
|
||||
/desktop-init\.sh.*executable/i
|
||||
);
|
||||
fs.chmodSync(path.join(root, 'desktop-init.sh'), 0o755);
|
||||
|
||||
fs.truncateSync(snapYamlPath, 256 * 1024 + 1);
|
||||
assert.match(
|
||||
validateExtractedSnapMetadata(root).join('\n'),
|
||||
@@ -1675,6 +1707,7 @@ for (const [kind, mutate, expected] of [
|
||||
const snapYamlPath = path.join(root, 'meta', 'snap.yaml');
|
||||
fs.mkdirSync(path.dirname(snapYamlPath), { recursive: true });
|
||||
fs.mkdirSync(path.join(root, 'graphics'));
|
||||
writeSnapDesktopRuntimeScripts(root);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(snapYamlPath, SNAP_METADATA_WITH_LITERAL_HASHES);
|
||||
@@ -1702,6 +1735,7 @@ test('artifact verification enforces Snap metadata for x64 and ARM payloads', ()
|
||||
fs.writeFileSync(artifactPath, 'fixture');
|
||||
fs.mkdirSync(path.dirname(snapYamlPath), { recursive: true });
|
||||
fs.mkdirSync(path.join(fixture.root, 'graphics'));
|
||||
writeSnapDesktopRuntimeScripts(fixture.root);
|
||||
fs.writeFileSync(
|
||||
snapYamlPath,
|
||||
[
|
||||
|
||||
Reference in new issue
Block a user