mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
fix(packaging): bound helper probe capture
This commit is contained in:
1 parent
43f4388665
commit
a651dd69a4
11 files changed
+79
-28
No files matched your search
@@ -282,13 +282,14 @@ Key files:
|
||||
keeps top-level reason `helper-probe-failed`; `helperReason` is present only
|
||||
for an exact protocol-v1 line carrying a fixed allowlisted reason, and its
|
||||
optional `helperDetail` must be 1–1024 printable ASCII characters. Invalid
|
||||
detail suppresses both helper fields. With `IPTVNATOR_TRACE_PLAYER=1`, a
|
||||
non-empty helper stderr capture is emitted separately as one JSON-escaped
|
||||
stderr line whose `stderr` field is limited to 16,384 characters and whose
|
||||
`truncated` field is always explicit; trace-write failure cannot change the
|
||||
capability result. Installed-Snap CI enables Mesa EGL/GL diagnostics through
|
||||
this bounded channel. Any loader failure remains a stable native-view
|
||||
fallback, never a flag-enabled success.
|
||||
detail suppresses both helper fields. Every probe uses an explicit 16 MiB
|
||||
per-stream child-capture ceiling independent of tracing. With
|
||||
`IPTVNATOR_TRACE_PLAYER=1`, a non-empty helper stderr capture is emitted
|
||||
separately as one JSON-escaped stderr line whose `stderr` field is limited
|
||||
to 16,384 characters and whose `truncated` field is always explicit;
|
||||
trace-write failure cannot change the capability result. Installed-Snap CI
|
||||
enables Mesa EGL/GL diagnostics through this bounded channel. Any loader
|
||||
failure remains a stable native-view fallback, never a flag-enabled success.
|
||||
- In the exact packaged Flatpak `/app` context, reconstruct only Freedesktop
|
||||
Platform 24.08's immutable `__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS`; its GL
|
||||
extension loader path comes from the sandbox cache. Flatpak CI must invoke
|
||||
|
||||
@@ -673,9 +673,10 @@ engine` (restart required) or
|
||||
helper exit keeps top-level reason `helper-probe-failed`; `helperReason` is
|
||||
present only for an exact protocol-v1 line carrying a fixed allowlisted
|
||||
reason, and its optional `helperDetail` must be 1–1024 printable ASCII
|
||||
characters. Invalid detail suppresses both helper fields. With
|
||||
`IPTVNATOR_TRACE_PLAYER=1`, non-empty helper stderr is emitted separately as
|
||||
one JSON-escaped stderr line with a 16,384-character `stderr` limit and an
|
||||
characters. Invalid detail suppresses both helper fields. Every probe uses
|
||||
an explicit 16 MiB per-stream child-capture ceiling independent of tracing.
|
||||
With `IPTVNATOR_TRACE_PLAYER=1`, non-empty helper stderr is emitted separately
|
||||
as one JSON-escaped stderr line with a 16,384-character `stderr` limit and an
|
||||
explicit `truncated` field; trace-write failure cannot change availability.
|
||||
Installed-Snap CI enables Mesa EGL/GL diagnostics through this bounded
|
||||
channel. The exact packaged Flatpak `/app` context reconstructs only
|
||||
|
||||
@@ -12,7 +12,8 @@ export const FRAME_COPY_ADDON_NAME = 'embedded_mpv.node';
|
||||
export const FRAME_COPY_READER_NAME = 'embedded_mpv_frame_reader.node';
|
||||
export const FRAME_COPY_HELPER_NAME = 'iptvnator_mpv_helper';
|
||||
export const RUNTIME_PROBE_PROTOCOL = 1;
|
||||
export const { RUNTIME_PROBE_TIMEOUT_MS } = runtimeProbeContract;
|
||||
export const { RUNTIME_PROBE_MAX_BUFFER_BYTES, RUNTIME_PROBE_TIMEOUT_MS } =
|
||||
runtimeProbeContract;
|
||||
export const VERSIONED_LIBMPV_PATTERN = /^libmpv\.so\.\d+(?:\.\d+)*$/;
|
||||
export const SAFE_RUNTIME_NAME_PATTERN = /^[A-Za-z0-9_+.-]+$/;
|
||||
export const SHARED_LIBRARY_PATTERN = /\.so(?:\.\d+)*$/;
|
||||
|
||||
+30
-3
@@ -90,6 +90,7 @@ describe('embedded-mpv frame-copy runtime probe orchestration', () => {
|
||||
timeout: 3000,
|
||||
killSignal: 'SIGKILL',
|
||||
windowsHide: true,
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
env: {
|
||||
PATH: '/usr/bin',
|
||||
LIBGL_ALWAYS_SOFTWARE: '1',
|
||||
@@ -171,6 +172,11 @@ describe('embedded-mpv frame-copy runtime probe orchestration', () => {
|
||||
});
|
||||
|
||||
const virtualSnapRoot = '/snap/iptvnator/42';
|
||||
const linuxTriplet = 'x86_64-linux-gnu';
|
||||
const snapLibraries = (...relativePaths: string[]): string[] =>
|
||||
relativePaths.map((relativePath) =>
|
||||
path.join(virtualSnapRoot, relativePath)
|
||||
);
|
||||
const virtualNativeDir = path.join(
|
||||
virtualSnapRoot,
|
||||
'resources',
|
||||
@@ -231,6 +237,8 @@ describe('embedded-mpv frame-copy runtime probe orchestration', () => {
|
||||
virtualSnapRoot,
|
||||
'gnome-platform'
|
||||
),
|
||||
SNAP_LIBRARY_PATH:
|
||||
'/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl/nvidia',
|
||||
},
|
||||
fileSystem: virtualFileSystem,
|
||||
});
|
||||
@@ -248,9 +256,28 @@ describe('embedded-mpv frame-copy runtime probe orchestration', () => {
|
||||
expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
SNAP: virtualSnapRoot,
|
||||
LD_LIBRARY_PATH: expect.stringContaining(
|
||||
path.join(virtualNativeDir, 'lib')
|
||||
),
|
||||
LD_LIBRARY_PATH: [
|
||||
path.join(virtualNativeDir, 'lib'),
|
||||
'/var/lib/snapd/lib/gl',
|
||||
'/var/lib/snapd/lib/gl/nvidia',
|
||||
...snapLibraries(
|
||||
`graphics/usr/lib/${linuxTriplet}`,
|
||||
`graphics/usr/lib/${linuxTriplet}/vdpau`
|
||||
),
|
||||
'/usr/lib/x86_64-linux-gnu',
|
||||
...snapLibraries(
|
||||
`gnome-platform/lib/${linuxTriplet}`,
|
||||
`gnome-platform/usr/lib/${linuxTriplet}`,
|
||||
`gnome-platform/usr/lib/${linuxTriplet}/mesa`,
|
||||
`gnome-platform/usr/lib/${linuxTriplet}/mesa-egl`,
|
||||
`gnome-platform/usr/lib/${linuxTriplet}/dri`,
|
||||
`gnome-platform/usr/lib/${linuxTriplet}/pulseaudio`,
|
||||
'lib',
|
||||
'usr/lib',
|
||||
`lib/${linuxTriplet}`,
|
||||
`usr/lib/${linuxTriplet}`
|
||||
),
|
||||
].join(':'),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as nodeFileSystem from 'fs';
|
||||
import path from 'path';
|
||||
import {
|
||||
RUNTIME_MANIFEST_NAME,
|
||||
RUNTIME_PROBE_MAX_BUFFER_BYTES,
|
||||
RUNTIME_PROBE_PROTOCOL,
|
||||
RUNTIME_PROBE_TIMEOUT_MS,
|
||||
} from './contracts';
|
||||
@@ -180,6 +181,7 @@ function runHelperProbe(
|
||||
timeout: RUNTIME_PROBE_TIMEOUT_MS,
|
||||
killSignal: 'SIGKILL',
|
||||
windowsHide: true,
|
||||
maxBuffer: RUNTIME_PROBE_MAX_BUFFER_BYTES,
|
||||
env: launch.env,
|
||||
});
|
||||
} catch {
|
||||
|
||||
@@ -282,12 +282,15 @@ allowlisted helper reason as `helperReason`. An optional `helperDetail` is
|
||||
copied only from the same exact line when it contains 1–1024 printable ASCII
|
||||
characters; an invalid detail rejects both helper fields. Malformed,
|
||||
multi-line, wrong-protocol, or unknown failure output never reaches either
|
||||
field. When `IPTVNATOR_TRACE_PLAYER=1`, the probe also emits non-empty captured
|
||||
helper stderr separately as one JSON line. JSON escaping keeps embedded
|
||||
newlines on that single line, the `stderr` field is limited to the first 16,384
|
||||
characters, and the `truncated` boolean is always present. A missing flag,
|
||||
empty capture, or trace-writer failure produces no trace and never changes the
|
||||
cached availability result or the application diagnostic's stdout protocol.
|
||||
field. Every probe uses the same explicit 16 MiB per-stream child-capture
|
||||
ceiling, independent of tracing, so verbose diagnostics do not fall back to
|
||||
Node's smaller implicit buffer. When `IPTVNATOR_TRACE_PLAYER=1`, the probe also
|
||||
emits non-empty captured helper stderr separately as one JSON line. JSON
|
||||
escaping keeps embedded newlines on that single line, the `stderr` field is
|
||||
limited to the first 16,384 characters, and the `truncated` boolean is always
|
||||
present. A missing flag, empty capture, or trace-writer failure produces no
|
||||
trace and never changes the cached availability result or the application
|
||||
diagnostic's stdout protocol.
|
||||
|
||||
The startup probe and every playback helper session use the same sanitized
|
||||
loader environment selected by the validated manifest's cached `runtimeMode`.
|
||||
|
||||
@@ -239,12 +239,14 @@ application diagnostic retains `helper-probe-failed` as the top-level reason
|
||||
for nonzero helper exits and adds `helperReason` only when the helper emitted
|
||||
one exact protocol-v1 line with a fixed allowlisted reason. Its optional
|
||||
`helperDetail` is restricted to 1–1024 printable ASCII characters; invalid
|
||||
detail suppresses both helper fields. With `IPTVNATOR_TRACE_PLAYER=1`, non-empty
|
||||
captured helper stderr is written separately as one JSON-escaped stderr line:
|
||||
its `stderr` field contains at most the first 16,384 characters and its
|
||||
`truncated` boolean is always explicit. Empty captures, disabled tracing, and
|
||||
trace-writer failures do not emit a record or alter availability. The
|
||||
installed-Snap probe therefore tests the private shared-memory confinement
|
||||
detail suppresses both helper fields. Every probe has the same explicit 16 MiB
|
||||
per-stream child-capture ceiling, regardless of tracing. With
|
||||
`IPTVNATOR_TRACE_PLAYER=1`, non-empty captured helper stderr is written
|
||||
separately as one JSON-escaped stderr line: its `stderr` field contains at most
|
||||
the first 16,384 characters and its `truncated` boolean is always explicit.
|
||||
Empty captures, disabled tracing, and trace-writer failures do not emit a
|
||||
record or alter availability. The installed-Snap probe therefore tests the
|
||||
private shared-memory confinement
|
||||
needed by playback rather than only loader and graphics startup.
|
||||
Packaging CI invokes the same gate through
|
||||
`snap run iptvnator --embedded-mpv-runtime-probe`. This packaging-only
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const RUNTIME_PROBE_CONTRACT = Object.freeze({
|
||||
RUNTIME_PROBE_MAX_BUFFER_BYTES: 16 * 1024 * 1024,
|
||||
RUNTIME_PROBE_TIMEOUT_MS: 3000,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
declare const RUNTIME_PROBE_CONTRACT: Readonly<{
|
||||
readonly RUNTIME_PROBE_MAX_BUFFER_BYTES: 16777216;
|
||||
readonly RUNTIME_PROBE_TIMEOUT_MS: 3000;
|
||||
}>;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ const {
|
||||
resolveLinuxFrameCopyProfile,
|
||||
} = require('./linux-frame-copy-profile.cjs');
|
||||
const {
|
||||
RUNTIME_PROBE_MAX_BUFFER_BYTES,
|
||||
RUNTIME_PROBE_TIMEOUT_MS,
|
||||
} = require('../embedded-mpv/runtime-probe-contract.cjs');
|
||||
|
||||
@@ -71,6 +72,7 @@ function defaultRunCommand(command, args, options = {}) {
|
||||
stdio: 'pipe',
|
||||
timeout: options.timeout,
|
||||
killSignal: options.killSignal,
|
||||
maxBuffer: options.maxBuffer,
|
||||
windowsHide: true,
|
||||
});
|
||||
}
|
||||
@@ -1611,6 +1613,7 @@ export function verifyExtractedLinuxFrameCopyRuntime({
|
||||
encoding: 'utf8',
|
||||
env: probeEnvironment,
|
||||
killSignal: 'SIGKILL',
|
||||
maxBuffer: RUNTIME_PROBE_MAX_BUFFER_BYTES,
|
||||
timeout: RUNTIME_PROBE_TIMEOUT_MS,
|
||||
windowsHide: true,
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ const DEB_SYSTEM_PACKAGE_DEPENDENCIES = [
|
||||
'libgbm1',
|
||||
];
|
||||
|
||||
test('uses the shared frozen 3000 ms runtime probe timeout contract', async () => {
|
||||
test('uses the shared frozen runtime probe resource contract', async () => {
|
||||
assert.equal(
|
||||
fs.existsSync(runtimeProbeContractUrl),
|
||||
true,
|
||||
@@ -108,10 +108,18 @@ test('uses the shared frozen 3000 ms runtime probe timeout contract', async () =
|
||||
);
|
||||
assert.equal(Object.isFrozen(runtimeProbeContract), true);
|
||||
assert.equal(runtimeProbeContract.RUNTIME_PROBE_TIMEOUT_MS, 3000);
|
||||
assert.equal(
|
||||
runtimeProbeContract.RUNTIME_PROBE_MAX_BUFFER_BYTES,
|
||||
16 * 1024 * 1024
|
||||
);
|
||||
assert.match(
|
||||
fs.readFileSync(runtimeProbeContractTypesUrl, 'utf8'),
|
||||
/readonly RUNTIME_PROBE_TIMEOUT_MS: 3000/
|
||||
);
|
||||
assert.match(
|
||||
fs.readFileSync(runtimeProbeContractTypesUrl, 'utf8'),
|
||||
/readonly RUNTIME_PROBE_MAX_BUFFER_BYTES: 16777216/
|
||||
);
|
||||
|
||||
const verifierSource = fs.readFileSync(verifierUrl, 'utf8');
|
||||
assert.match(
|
||||
@@ -133,7 +141,7 @@ test('uses the shared frozen 3000 ms runtime probe timeout contract', async () =
|
||||
);
|
||||
assert.match(
|
||||
backendContractsSource,
|
||||
/export const \{ RUNTIME_PROBE_TIMEOUT_MS \} = runtimeProbeContract/
|
||||
/export const \{\s*RUNTIME_PROBE_MAX_BUFFER_BYTES,\s*RUNTIME_PROBE_TIMEOUT_MS,?\s*\}\s*=\s*runtimeProbeContract/
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
backendContractsSource,
|
||||
@@ -784,6 +792,7 @@ test('validates an x64 system payload and executes one bounded helper probe', ()
|
||||
encoding: 'utf8',
|
||||
env: { PATH: '/usr/bin' },
|
||||
killSignal: 'SIGKILL',
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
timeout: 3000,
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user