mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(packaging): preserve Flatpak Electron ELF
This commit is contained in:
1 parent
d09237cbf0
commit
52e248ebf5
5 files changed
+268
-5
No files matched your search
@@ -198,7 +198,9 @@ async function afterPackHook(params) {
|
||||
}
|
||||
);
|
||||
|
||||
await linuxAfterPack(params);
|
||||
await linuxAfterPack(params, {
|
||||
targetNames: linuxPackagingContext?.targetNames,
|
||||
});
|
||||
if (linuxPackagingContext) {
|
||||
const graphicsMountPath = ensureSnapGraphicsContentMount(
|
||||
params.appOutDir,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
const { resolveLinuxLauncherLayout } = require('./linux-launcher-layout.cjs');
|
||||
|
||||
function log(message) {
|
||||
console.log(` - ${message}`);
|
||||
@@ -41,17 +42,33 @@ exec "$SCRIPT_DIR/${executableName}.bin" "\${EXEC_ARGS[@]}" "$@"
|
||||
`;
|
||||
}
|
||||
|
||||
async function afterPackHook(params) {
|
||||
async function afterPackHook(params, { targetNames = params.targets } = {}) {
|
||||
if (params.electronPlatformName !== 'linux') {
|
||||
return;
|
||||
}
|
||||
|
||||
const launcherLayout = resolveLinuxLauncherLayout(
|
||||
targetNames,
|
||||
params.packager.executableName
|
||||
);
|
||||
if (!launcherLayout.wrapperRequired) {
|
||||
log('preserving the Electron ELF for isolated Flatpak packaging');
|
||||
return;
|
||||
}
|
||||
|
||||
log('applying Linux launcher sandbox fix');
|
||||
|
||||
const executable = path.join(params.appOutDir, params.packager.executableName);
|
||||
const executable = path.join(
|
||||
params.appOutDir,
|
||||
params.packager.executableName
|
||||
);
|
||||
const electronBinary = path.join(
|
||||
params.appOutDir,
|
||||
launcherLayout.electronBinaryName
|
||||
);
|
||||
|
||||
try {
|
||||
await fs.rename(executable, `${executable}.bin`);
|
||||
await fs.rename(executable, electronBinary);
|
||||
await fs.writeFile(
|
||||
executable,
|
||||
createLoaderScript({
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const linuxAfterPack = require('./linux-after-pack.cjs');
|
||||
const { createLoaderScript } = linuxAfterPack;
|
||||
|
||||
const electronElf = Buffer.from([
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x49, 0x50, 0x54, 0x56,
|
||||
]);
|
||||
|
||||
async function createAfterPackFixture(targets) {
|
||||
const appOutDir = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), 'iptvnator-linux-after-pack-')
|
||||
);
|
||||
const executablePath = path.join(appOutDir, 'iptvnator');
|
||||
await fs.writeFile(executablePath, electronElf, { mode: 0o755 });
|
||||
|
||||
return {
|
||||
appOutDir,
|
||||
executablePath,
|
||||
params: {
|
||||
appOutDir,
|
||||
electronPlatformName: 'linux',
|
||||
targets,
|
||||
packager: {
|
||||
executableName: 'iptvnator',
|
||||
appInfo: {
|
||||
productName: 'IPTVnator',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolveLinuxLauncherLayout(...args) {
|
||||
return require('./linux-launcher-layout.cjs').resolveLinuxLauncherLayout(
|
||||
...args
|
||||
);
|
||||
}
|
||||
|
||||
async function assertPathMissing(filePath) {
|
||||
await assert.rejects(
|
||||
fs.stat(filePath),
|
||||
(error) => error?.code === 'ENOENT'
|
||||
);
|
||||
}
|
||||
|
||||
test('isolated Flatpak preserves the Electron ELF for Zypak', async (t) => {
|
||||
const fixture = await createAfterPackFixture([{ name: 'flatpak' }]);
|
||||
t.after(() =>
|
||||
fs.rm(fixture.appOutDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
);
|
||||
|
||||
await linuxAfterPack(fixture.params);
|
||||
|
||||
assert.deepEqual(await fs.readFile(fixture.executablePath), electronElf);
|
||||
await assertPathMissing(`${fixture.executablePath}.bin`);
|
||||
});
|
||||
|
||||
test('resolves normalized string and Target-like launcher layouts', () => {
|
||||
assert.deepEqual(resolveLinuxLauncherLayout([' FlatPak ']), {
|
||||
targetNames: ['flatpak'],
|
||||
electronBinaryName: 'iptvnator',
|
||||
wrapperRequired: false,
|
||||
});
|
||||
assert.deepEqual(
|
||||
resolveLinuxLauncherLayout(
|
||||
[' AppImage ', { name: ' DEB ' }],
|
||||
'iptvnator-player'
|
||||
),
|
||||
{
|
||||
targetNames: ['appimage', 'deb'],
|
||||
electronBinaryName: 'iptvnator-player.bin',
|
||||
wrapperRequired: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects missing, empty, and non-array target lists', () => {
|
||||
for (const targets of [undefined, null, 'flatpak', new Map(), {}]) {
|
||||
assert.throws(
|
||||
() => resolveLinuxLauncherLayout(targets),
|
||||
/Linux launcher targets must be an array/
|
||||
);
|
||||
}
|
||||
assert.throws(
|
||||
() => resolveLinuxLauncherLayout([]),
|
||||
/Linux launcher targets must contain at least one target/
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects empty target names and normalized duplicates', () => {
|
||||
for (const target of ['', ' ', {}, { name: '' }, null, 42]) {
|
||||
assert.throws(
|
||||
() => resolveLinuxLauncherLayout([target]),
|
||||
/Linux launcher targets must expose a non-empty name/
|
||||
);
|
||||
}
|
||||
assert.throws(
|
||||
() => resolveLinuxLauncherLayout([{ name: ' DEB ' }, { name: 'deb' }]),
|
||||
/Linux launcher target "deb" is duplicated/
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects Flatpak mixed with another target', () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveLinuxLauncherLayout([
|
||||
{ name: 'flatpak' },
|
||||
{ name: 'AppImage' },
|
||||
]),
|
||||
/Flatpak must be packaged in an isolated Electron Builder pass so Zypak receives the Electron ELF directly/
|
||||
);
|
||||
});
|
||||
|
||||
for (const targetName of ['appimage', 'deb', 'rpm', 'pacman', 'snap']) {
|
||||
test(`${targetName} retains the launcher wrapper and Electron ELF binary`, async (t) => {
|
||||
const fixture = await createAfterPackFixture([{ name: targetName }]);
|
||||
t.after(() =>
|
||||
fs.rm(fixture.appOutDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
);
|
||||
|
||||
await linuxAfterPack(fixture.params);
|
||||
|
||||
assert.equal(
|
||||
await fs.readFile(fixture.executablePath, 'utf8'),
|
||||
createLoaderScript({
|
||||
executableName: 'iptvnator',
|
||||
productName: 'IPTVnator',
|
||||
})
|
||||
);
|
||||
assert.deepEqual(
|
||||
await fs.readFile(`${fixture.executablePath}.bin`),
|
||||
electronElf
|
||||
);
|
||||
assert.equal(
|
||||
(await fs.stat(fixture.executablePath)).mode & 0o777,
|
||||
0o755
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test('mixed Flatpak targets fail before mutating the Electron ELF', async (t) => {
|
||||
const fixture = await createAfterPackFixture([
|
||||
{ name: 'flatpak' },
|
||||
{ name: 'appimage' },
|
||||
]);
|
||||
t.after(() =>
|
||||
fs.rm(fixture.appOutDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
linuxAfterPack(fixture.params),
|
||||
/Flatpak must be packaged in an isolated Electron Builder pass so Zypak receives the Electron ELF directly/
|
||||
);
|
||||
|
||||
assert.deepEqual(await fs.readFile(fixture.executablePath), electronElf);
|
||||
await assertPathMissing(`${fixture.executablePath}.bin`);
|
||||
});
|
||||
|
||||
test('packaging Nx tests register the Linux afterPack regression suite', async () => {
|
||||
const project = JSON.parse(
|
||||
await fs.readFile(new URL('./project.json', import.meta.url), 'utf8')
|
||||
);
|
||||
const testFile =
|
||||
'{workspaceRoot}/tools/packaging/linux-after-pack.test.mjs';
|
||||
|
||||
assert.ok(project.targets.test.inputs.includes(testFile));
|
||||
assert.match(
|
||||
project.targets.test.options.command,
|
||||
/node --test .*tools\/packaging\/linux-after-pack\.test\.mjs/
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
function normalizedTargetName(target) {
|
||||
const value =
|
||||
typeof target === 'string'
|
||||
? target
|
||||
: target && typeof target === 'object'
|
||||
? target.name
|
||||
: null;
|
||||
if (typeof value !== 'string' || value.trim() === '') {
|
||||
throw new Error('Linux launcher targets must expose a non-empty name.');
|
||||
}
|
||||
return value.trim().toLowerCase();
|
||||
}
|
||||
|
||||
function resolveLinuxLauncherLayout(targets, executableName = 'iptvnator') {
|
||||
if (!Array.isArray(targets)) {
|
||||
throw new TypeError('Linux launcher targets must be an array.');
|
||||
}
|
||||
if (targets.length === 0) {
|
||||
throw new Error(
|
||||
'Linux launcher targets must contain at least one target.'
|
||||
);
|
||||
}
|
||||
|
||||
const targetNames = [];
|
||||
for (const target of targets) {
|
||||
const targetName = normalizedTargetName(target);
|
||||
if (targetNames.includes(targetName)) {
|
||||
throw new Error(
|
||||
`Linux launcher target "${targetName}" is duplicated.`
|
||||
);
|
||||
}
|
||||
targetNames.push(targetName);
|
||||
}
|
||||
|
||||
const flatpakSelected = targetNames.includes('flatpak');
|
||||
if (flatpakSelected && targetNames.length !== 1) {
|
||||
throw new Error(
|
||||
'Flatpak must be packaged in an isolated Electron Builder pass so Zypak receives the Electron ELF directly.'
|
||||
);
|
||||
}
|
||||
|
||||
const wrapperRequired = !flatpakSelected;
|
||||
return {
|
||||
targetNames,
|
||||
electronBinaryName: wrapperRequired
|
||||
? `${executableName}.bin`
|
||||
: executableName,
|
||||
wrapperRequired,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
resolveLinuxLauncherLayout,
|
||||
};
|
||||
@@ -24,6 +24,7 @@
|
||||
"{workspaceRoot}/tools/packaging/embedded-mpv-arch.test.mjs",
|
||||
"{workspaceRoot}/tools/packaging/configure-linux-frame-copy-build.mjs",
|
||||
"{workspaceRoot}/tools/packaging/configure-linux-frame-copy-build.test.mjs",
|
||||
"{workspaceRoot}/tools/packaging/linux-after-pack.test.mjs",
|
||||
"{workspaceRoot}/tools/packaging/linux-frame-copy-profile.cjs",
|
||||
"{workspaceRoot}/tools/packaging/linux-frame-copy-profile.test.mjs",
|
||||
"{workspaceRoot}/tools/packaging/prepare-linux-runtime-source-snapshot.cjs",
|
||||
@@ -49,7 +50,7 @@
|
||||
"{workspaceRoot}/tools/embedded-mpv/stage-windows-runtime-archive.mjs"
|
||||
],
|
||||
"options": {
|
||||
"command": "node --test tools/packaging/electron-package-identity.test.mjs tools/packaging/asar-dependency-closure.test.mjs tools/packaging/embedded-mpv-arch.test.mjs tools/packaging/configure-linux-frame-copy-build.test.mjs tools/packaging/linux-frame-copy-profile.test.mjs tools/packaging/prepare-linux-runtime-source-snapshot.test.mjs tools/packaging/publish-snap-workflow.test.mjs tools/packaging/release-snap-assets.test.mjs tools/packaging/verify-linux-frame-copy-runtime.test.mjs tools/embedded-mpv/build-linux-runtime.test.mjs tools/embedded-mpv/generate-linux-runtime-notices.test.mjs tools/embedded-mpv/linux-runtime-manifest.test.mjs",
|
||||
"command": "node --test tools/packaging/electron-package-identity.test.mjs tools/packaging/asar-dependency-closure.test.mjs tools/packaging/embedded-mpv-arch.test.mjs tools/packaging/configure-linux-frame-copy-build.test.mjs tools/packaging/linux-after-pack.test.mjs tools/packaging/linux-frame-copy-profile.test.mjs tools/packaging/prepare-linux-runtime-source-snapshot.test.mjs tools/packaging/publish-snap-workflow.test.mjs tools/packaging/release-snap-assets.test.mjs tools/packaging/verify-linux-frame-copy-runtime.test.mjs tools/embedded-mpv/build-linux-runtime.test.mjs tools/embedded-mpv/generate-linux-runtime-notices.test.mjs tools/embedded-mpv/linux-runtime-manifest.test.mjs",
|
||||
"cwd": "{workspaceRoot}"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user