mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
* docs: design Linux frame-copy packaging * docs: plan Linux frame-copy packaging * feat(packaging): define Linux frame-copy profiles * fix(packaging): reject inherited profile names * feat(embedded-mpv): validate staged Linux runtime * fix(embedded-mpv): require Linux source packages * fix(embedded-mpv): harden Linux runtime staging * feat(embedded-mpv): build LGPL Linux runtime * fix(embedded-mpv): pin Linux runtime inputs * feat(embedded-mpv): build relocatable Linux helper * fix(embedded-mpv): require bundled Linux runtime * fix(embedded-mpv): make Linux runtime portable * feat(packaging): ship Linux frame-copy artifacts * fix(embedded-mpv): verify Linux helper linkage * fix(packaging): enforce Linux frame-copy isolation * fix(embedded-mpv): pin Linux display data * docs(embedded-mpv): document Linux frame-copy packaging * feat(embedded-mpv): probe Linux frame-copy runtime * test(embedded-mpv): smoke packaged Linux frame-copy * docs(embedded-mpv): clarify Linux system runtime baseline * fix(embedded-mpv): harden Linux runtime capability gate * ci: verify Linux frame-copy packages * test(embedded-mpv): harden packaged Linux smoke * test(embedded-mpv): preserve packaged GL mode * test(packaging): harden Linux package probes * fix(embedded-mpv): enable private Snap shared memory * fix(embedded-mpv): sanitize Linux helper environment * fix(packaging): enforce private Snap memory semantics * fix(packaging): reject ambiguous Snap memory metadata * fix(embedded-mpv): prioritize trusted Snap GL * fix(packaging): reject advanced Snap YAML semantics * fix(packaging): reject arbitrary Snap YAML aliases * feat(packaging): ship Linux runtime license notices * docs(embedded-mpv): document Linux runtime distribution * fix(packaging): parse Snap trailing comments safely * fix(release): gate Snap publish on public source release * fix(packaging): strip VCS metadata from source bundle * docs(packaging): clarify Linux source release gate * test(embedded-mpv): smoke missing bundled libmpv * style(embedded-mpv): format final validation inputs * fix(e2e): satisfy fixture index signature typing * fix(ci): declare fontconfig gperf generator * fix(embedded-mpv): hash runtime cache identities * fix(packaging): harden Linux frame-copy delivery * fix(packaging): tighten runtime delivery gates * fix(ci): decouple Linux runtime matrix * fix(packaging): harden Linux frame-copy delivery * fix(packaging): validate Linux frame-copy runtimes * fix(packaging): scope Snap Electron library checks * feat(packaging): ship Linux frame-copy runtimes * fix(packaging): improve Linux runtime smoke diagnostics * fix(packaging): expose bounded helper probe details * test(packaging): trace Snap EGL probe failures * fix(packaging): prefer core22 ABI in Snap helper * fix(packaging): bound helper probe capture * fix(packaging): harden Linux frame-copy releases * fix(packaging): canonicalize libplacebo submodule identity * fix(packaging): make source archive inspection portable * fix(packaging): harden Snap release verification
579 lines
19 KiB
JavaScript
579 lines
19 KiB
JavaScript
'use strict';
|
|
|
|
const crypto = require('node:crypto');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const { isDeepStrictEqual } = require('node:util');
|
|
const {
|
|
validateLinuxRuntimeManifest,
|
|
} = require('../embedded-mpv/linux-runtime-manifest.cjs');
|
|
const {
|
|
validateLinuxSourceArchiveBinding,
|
|
} = require('../embedded-mpv/linux-source-archive-contract.cjs');
|
|
const {
|
|
NOTICE_MANIFEST,
|
|
THIRD_PARTY_NOTICES,
|
|
validateLinuxRuntimeNotices,
|
|
} = require('../embedded-mpv/generate-linux-runtime-notices.cjs');
|
|
const {
|
|
LINUX_SYSTEM_PACKAGE_DEPENDENCIES,
|
|
resolveLinuxFrameCopyProfile,
|
|
validateLinuxProfileTargets,
|
|
} = require('./linux-frame-copy-profile.cjs');
|
|
|
|
const FRAME_COPY_HELPER = 'iptvnator_mpv_helper';
|
|
const WINDOWS_FRAME_COPY_HELPER = 'iptvnator_mpv_helper.exe';
|
|
const FRAME_COPY_READER = 'embedded_mpv_frame_reader.node';
|
|
const EMBEDDED_MPV_ADDON = 'embedded_mpv.node';
|
|
const RUNTIME_MANIFEST = 'embedded-mpv-runtime.json';
|
|
const UNAVAILABLE_MARKER = 'embedded-mpv-unavailable.txt';
|
|
const LICENSES_DIRECTORY = 'licenses';
|
|
const VERSIONED_LIBMPV_PATTERN = /^libmpv\.so\.\d+(?:\.\d+)*$/;
|
|
const EXPECTED_ARTIFACTS = Object.freeze({
|
|
addon: EMBEDDED_MPV_ADDON,
|
|
frameReader: FRAME_COPY_READER,
|
|
helper: FRAME_COPY_HELPER,
|
|
});
|
|
const EXPECTED_PROCESS_ISOLATION = Object.freeze({
|
|
addonLoadsLibmpv: false,
|
|
helperLinksLibmpv: true,
|
|
helperRunpath: Object.freeze(['$ORIGIN/lib']),
|
|
});
|
|
|
|
function isObject(value) {
|
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
}
|
|
|
|
function readJsonFile(filePath, label) {
|
|
let contents;
|
|
try {
|
|
contents = fs.readFileSync(filePath, 'utf8');
|
|
} catch (error) {
|
|
throw new Error(
|
|
`Unable to read ${label} at ${filePath}: ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`
|
|
);
|
|
}
|
|
try {
|
|
return JSON.parse(contents);
|
|
} catch (error) {
|
|
throw new Error(
|
|
`Invalid JSON in ${label} at ${filePath}: ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`
|
|
);
|
|
}
|
|
}
|
|
|
|
function assertRegularReadableFile(filePath, label) {
|
|
let stat;
|
|
try {
|
|
stat = fs.lstatSync(filePath);
|
|
} catch {
|
|
throw new Error(`Missing ${label}: ${filePath}`);
|
|
}
|
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
throw new Error(`${label} must be a regular file: ${filePath}`);
|
|
}
|
|
try {
|
|
fs.accessSync(filePath, fs.constants.R_OK);
|
|
} catch {
|
|
throw new Error(`${label} must be readable: ${filePath}`);
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
function sameJson(left, right) {
|
|
return isDeepStrictEqual(left, right);
|
|
}
|
|
|
|
function normalizeTargetNames(targetNames) {
|
|
if (!Array.isArray(targetNames) || targetNames.length === 0) {
|
|
throw new Error(
|
|
'Linux frame-copy packaging requires at least one target.'
|
|
);
|
|
}
|
|
const normalized = [];
|
|
for (const targetName of targetNames) {
|
|
const name = String(targetName ?? '')
|
|
.trim()
|
|
.toLowerCase();
|
|
if (!name) {
|
|
throw new Error(
|
|
'Linux frame-copy target names must be non-empty strings.'
|
|
);
|
|
}
|
|
if (normalized.includes(name)) {
|
|
throw new Error(`Linux frame-copy target "${name}" is duplicated.`);
|
|
}
|
|
normalized.push(name);
|
|
}
|
|
return normalized.sort();
|
|
}
|
|
|
|
function validateLinuxFrameCopyBuildManifest(manifest) {
|
|
const errors = [];
|
|
if (!isObject(manifest)) {
|
|
return ['Linux frame-copy build manifest must be an object.'];
|
|
}
|
|
for (const [field, expected] of [
|
|
['schemaVersion', 1],
|
|
['origin', 'linux-frame-copy-build'],
|
|
['platform', 'linux'],
|
|
['arch', 'x64'],
|
|
['buildInputMode', 'bundled-runtime'],
|
|
['sourceRuntimeValidated', true],
|
|
]) {
|
|
if (manifest[field] !== expected) {
|
|
errors.push(
|
|
`Linux frame-copy build manifest ${field} must equal ${JSON.stringify(
|
|
expected
|
|
)}.`
|
|
);
|
|
}
|
|
}
|
|
if (!sameJson(manifest.allowedPackageRuntimeModes, ['system', 'bundled'])) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest allowedPackageRuntimeModes must contain exactly system and bundled.'
|
|
);
|
|
}
|
|
if (
|
|
!sameJson(manifest.packageRuntimeAvailability, {
|
|
system: true,
|
|
bundled: true,
|
|
})
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest packageRuntimeAvailability must mark exactly system and bundled as available.'
|
|
);
|
|
}
|
|
if (!sameJson(manifest.artifacts, EXPECTED_ARTIFACTS)) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest artifacts must name the addon, frame reader, and helper exactly.'
|
|
);
|
|
}
|
|
if (!sameJson(manifest.processIsolation, EXPECTED_PROCESS_ISOLATION)) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest processIsolation contract is invalid.'
|
|
);
|
|
}
|
|
if (manifest.nativeViewFallback !== 'process-isolated mpv --wid') {
|
|
errors.push(
|
|
'Linux frame-copy build manifest nativeViewFallback contract is invalid.'
|
|
);
|
|
}
|
|
if (
|
|
typeof manifest.generatedAt !== 'string' ||
|
|
Number.isNaN(Date.parse(manifest.generatedAt))
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest generatedAt must be a valid timestamp.'
|
|
);
|
|
}
|
|
if (
|
|
typeof manifest.libmpvSoname !== 'string' ||
|
|
!VERSIONED_LIBMPV_PATTERN.test(manifest.libmpvSoname)
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest libmpvSoname must be a versioned libmpv SONAME.'
|
|
);
|
|
}
|
|
const sourceLinkerAlias =
|
|
manifest.sourceRuntime?.runtimeDependencyClosure?.entries?.find(
|
|
(entry) => entry?.name === 'libmpv.so'
|
|
);
|
|
if (
|
|
typeof manifest.libmpvSoname === 'string' &&
|
|
sourceLinkerAlias?.soname !== manifest.libmpvSoname
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest libmpvSoname must be derived from the validated source runtime libmpv.so SONAME.'
|
|
);
|
|
}
|
|
const sourceErrors = validateLinuxRuntimeManifest(manifest.sourceRuntime);
|
|
errors.push(
|
|
...sourceErrors.map(
|
|
(error) => `Invalid bundled source runtime: ${error}`
|
|
)
|
|
);
|
|
errors.push(
|
|
...validateLinuxSourceArchiveBinding(manifest.sourceArchive).map(
|
|
(error) => `Invalid Linux source archive binding: ${error}`
|
|
)
|
|
);
|
|
if (
|
|
Array.isArray(manifest.runtimeFiles) &&
|
|
Array.isArray(manifest.sourceRuntime?.runtimeFiles) &&
|
|
!sameJson(manifest.runtimeFiles, manifest.sourceRuntime.runtimeFiles)
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest runtimeFiles must exactly match sourceRuntime.runtimeFiles.'
|
|
);
|
|
}
|
|
const expectedTotal = Array.isArray(manifest.runtimeFiles)
|
|
? manifest.runtimeFiles.reduce(
|
|
(total, runtimeFile) =>
|
|
total +
|
|
(isObject(runtimeFile) &&
|
|
Number.isInteger(runtimeFile.size) &&
|
|
runtimeFile.size > 0
|
|
? runtimeFile.size
|
|
: 0),
|
|
0
|
|
)
|
|
: 0;
|
|
if (manifest.runtimeTotalBytes !== expectedTotal) {
|
|
errors.push(
|
|
`Linux frame-copy build manifest runtimeTotalBytes must equal ${expectedTotal}.`
|
|
);
|
|
}
|
|
if (
|
|
Array.isArray(manifest.runtimeFiles) &&
|
|
typeof manifest.libmpvSoname === 'string' &&
|
|
!manifest.runtimeFiles.some(
|
|
(runtimeFile) => runtimeFile.name === manifest.libmpvSoname
|
|
)
|
|
) {
|
|
errors.push(
|
|
'Linux frame-copy build manifest runtimeFiles must include libmpvSoname.'
|
|
);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
function verifyBundledRuntimeFiles(nativeDir, manifest) {
|
|
const libDir = path.join(nativeDir, 'lib');
|
|
let libDirStat;
|
|
try {
|
|
libDirStat = fs.lstatSync(libDir);
|
|
} catch {
|
|
throw new Error(`Missing bundled runtime directory: ${libDir}`);
|
|
}
|
|
if (!libDirStat.isDirectory() || libDirStat.isSymbolicLink()) {
|
|
throw new Error(
|
|
`Bundled runtime directory must be a regular directory: ${libDir}`
|
|
);
|
|
}
|
|
|
|
const declaredFiles = new Map(
|
|
manifest.runtimeFiles.map((runtimeFile) => [
|
|
runtimeFile.name,
|
|
runtimeFile,
|
|
])
|
|
);
|
|
for (const entry of fs.readdirSync(libDir, { withFileTypes: true })) {
|
|
if (!declaredFiles.has(entry.name)) {
|
|
throw new Error(
|
|
`Found undeclared bundled runtime file ${entry.name} in ${libDir}.`
|
|
);
|
|
}
|
|
}
|
|
|
|
for (const runtimeFile of manifest.runtimeFiles) {
|
|
const runtimePath = path.join(libDir, runtimeFile.name);
|
|
let stat;
|
|
try {
|
|
stat = fs.lstatSync(runtimePath);
|
|
} catch {
|
|
throw new Error(`Missing bundled runtime file: ${runtimePath}`);
|
|
}
|
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
throw new Error(
|
|
`Bundled runtime file must be a materialized regular file: ${runtimePath}`
|
|
);
|
|
}
|
|
try {
|
|
fs.accessSync(runtimePath, fs.constants.R_OK);
|
|
} catch {
|
|
throw new Error(
|
|
`Bundled runtime file must be readable: ${runtimePath}`
|
|
);
|
|
}
|
|
const contents = fs.readFileSync(runtimePath);
|
|
if (contents.length !== runtimeFile.size) {
|
|
throw new Error(
|
|
`Bundled runtime file size mismatch for ${runtimeFile.name}: expected ${runtimeFile.size}, received ${contents.length}.`
|
|
);
|
|
}
|
|
const actualSha256 = crypto
|
|
.createHash('sha256')
|
|
.update(contents)
|
|
.digest('hex');
|
|
if (actualSha256 !== runtimeFile.sha256) {
|
|
throw new Error(
|
|
`Bundled runtime file SHA-256 mismatch for ${runtimeFile.name}: expected ${runtimeFile.sha256}, received ${actualSha256}.`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function writeManifest(nativeDir, manifest) {
|
|
const manifestPath = path.join(nativeDir, RUNTIME_MANIFEST);
|
|
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, {
|
|
mode: 0o644,
|
|
});
|
|
return manifest;
|
|
}
|
|
|
|
function removeLinuxRuntimeNotices(nativeDir) {
|
|
for (const relativePath of [
|
|
NOTICE_MANIFEST,
|
|
THIRD_PARTY_NOTICES,
|
|
LICENSES_DIRECTORY,
|
|
'notices',
|
|
]) {
|
|
fs.rmSync(path.join(nativeDir, relativePath), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
function prepareBundledLinuxRuntimeNotices(
|
|
nativeDir,
|
|
sourceRuntime,
|
|
noticeSourceDir
|
|
) {
|
|
if (noticeSourceDir) {
|
|
const sourceErrors = validateLinuxRuntimeNotices(
|
|
noticeSourceDir,
|
|
sourceRuntime
|
|
);
|
|
if (sourceErrors.length > 0) {
|
|
throw new Error(
|
|
[
|
|
`Invalid Linux runtime notice source at ${noticeSourceDir}.`,
|
|
...sourceErrors.map((error) => `- ${error}`),
|
|
].join('\n')
|
|
);
|
|
}
|
|
removeLinuxRuntimeNotices(nativeDir);
|
|
for (const relativePath of [
|
|
NOTICE_MANIFEST,
|
|
THIRD_PARTY_NOTICES,
|
|
LICENSES_DIRECTORY,
|
|
]) {
|
|
fs.cpSync(
|
|
path.join(noticeSourceDir, relativePath),
|
|
path.join(nativeDir, relativePath),
|
|
{
|
|
recursive: true,
|
|
force: true,
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
const packagedErrors = validateLinuxRuntimeNotices(
|
|
nativeDir,
|
|
sourceRuntime,
|
|
{ allowUnrelatedFiles: true }
|
|
);
|
|
if (packagedErrors.length > 0) {
|
|
throw new Error(
|
|
[
|
|
`Invalid packaged Linux runtime notices at ${nativeDir}.`,
|
|
...packagedErrors.map((error) => `- ${error}`),
|
|
].join('\n')
|
|
);
|
|
}
|
|
}
|
|
|
|
function createPackagedManifest(buildManifest, profile, targetNames) {
|
|
const bundled = profile.runtimeMode === 'bundled';
|
|
const runtimeFiles = bundled
|
|
? buildManifest.runtimeFiles.map((runtimeFile) => ({ ...runtimeFile }))
|
|
: [];
|
|
return {
|
|
schemaVersion: 1,
|
|
origin: profile.manifestOrigin,
|
|
generatedAt: buildManifest.generatedAt,
|
|
platform: 'linux',
|
|
arch: 'x64',
|
|
profile: profile.name,
|
|
runtimeMode: profile.runtimeMode,
|
|
targets: [...new Set(targetNames)].sort(),
|
|
artifacts: {
|
|
addon: {
|
|
name: EMBEDDED_MPV_ADDON,
|
|
regularFile: true,
|
|
readable: true,
|
|
},
|
|
frameReader: {
|
|
name: FRAME_COPY_READER,
|
|
regularFile: true,
|
|
readable: true,
|
|
},
|
|
helper: {
|
|
name: FRAME_COPY_HELPER,
|
|
regularFile: true,
|
|
readable: true,
|
|
executable: true,
|
|
},
|
|
},
|
|
processIsolation: {
|
|
addonLoadsLibmpv: false,
|
|
readerLoadsLibmpv: false,
|
|
electronLoadsLibmpv: false,
|
|
helperLinksLibmpv: true,
|
|
helperRunpath: ['$ORIGIN/lib'],
|
|
},
|
|
nativeViewFallback: buildManifest.nativeViewFallback,
|
|
libmpvSoname: buildManifest.libmpvSoname,
|
|
packageDependencies:
|
|
profile.runtimeMode === 'system'
|
|
? { ...LINUX_SYSTEM_PACKAGE_DEPENDENCIES }
|
|
: {},
|
|
runtimeFiles,
|
|
runtimeTotalBytes: runtimeFiles.reduce(
|
|
(total, runtimeFile) => total + runtimeFile.size,
|
|
0
|
|
),
|
|
...(bundled
|
|
? {
|
|
runtimeDependencyClosure: {
|
|
entries:
|
|
buildManifest.sourceRuntime.runtimeDependencyClosure.entries.map(
|
|
(entry) => ({
|
|
name: entry.name,
|
|
soname: entry.soname ?? null,
|
|
needed: [...entry.needed],
|
|
rpath: [...entry.rpath],
|
|
runpath: [...entry.runpath],
|
|
})
|
|
),
|
|
externalDependencies: [
|
|
...buildManifest.sourceRuntime
|
|
.runtimeDependencyClosure.externalDependencies,
|
|
],
|
|
},
|
|
externalSystemLibraries:
|
|
buildManifest.sourceRuntime.externalSystemLibraries.map(
|
|
(entry) => ({ ...entry })
|
|
),
|
|
sourceArchive: structuredClone(buildManifest.sourceArchive),
|
|
sourceRuntime: structuredClone(buildManifest.sourceRuntime),
|
|
}
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
function prepareNativeViewOnlyLinuxArtifacts(nativeDir) {
|
|
for (const fileName of [
|
|
FRAME_COPY_HELPER,
|
|
WINDOWS_FRAME_COPY_HELPER,
|
|
FRAME_COPY_READER,
|
|
UNAVAILABLE_MARKER,
|
|
]) {
|
|
fs.rmSync(path.join(nativeDir, fileName), { force: true });
|
|
}
|
|
fs.rmSync(path.join(nativeDir, 'lib'), { recursive: true, force: true });
|
|
removeLinuxRuntimeNotices(nativeDir);
|
|
|
|
return writeManifest(nativeDir, {
|
|
schemaVersion: 1,
|
|
origin: 'external-mpv-process',
|
|
platform: 'linux',
|
|
arch: 'x64',
|
|
runtimeMode: 'native-view-only',
|
|
frameCopyAvailable: false,
|
|
artifacts: {
|
|
addon: EMBEDDED_MPV_ADDON,
|
|
},
|
|
nativeViewFallback: 'process-isolated mpv --wid',
|
|
});
|
|
}
|
|
|
|
function prepareLinuxFrameCopyArtifacts(nativeDir, options = {}) {
|
|
if (!options.profile) {
|
|
return prepareNativeViewOnlyLinuxArtifacts(nativeDir);
|
|
}
|
|
const profile = resolveLinuxFrameCopyProfile(options.profile);
|
|
const targetNames = normalizeTargetNames(options.targetNames);
|
|
const targetErrors = validateLinuxProfileTargets(profile.name, targetNames);
|
|
if (targetErrors.length > 0) {
|
|
throw new Error(targetErrors.join('\n'));
|
|
}
|
|
|
|
const addonPath = path.join(nativeDir, EMBEDDED_MPV_ADDON);
|
|
const readerPath = path.join(nativeDir, FRAME_COPY_READER);
|
|
const helperPath = path.join(nativeDir, FRAME_COPY_HELPER);
|
|
const manifestPath = path.join(nativeDir, RUNTIME_MANIFEST);
|
|
assertRegularReadableFile(addonPath, 'embedded MPV addon');
|
|
assertRegularReadableFile(readerPath, 'embedded MPV frame reader');
|
|
assertRegularReadableFile(helperPath, 'embedded MPV frame-copy helper');
|
|
assertRegularReadableFile(manifestPath, 'embedded MPV runtime manifest');
|
|
|
|
const buildManifest = readJsonFile(
|
|
manifestPath,
|
|
'embedded MPV runtime manifest'
|
|
);
|
|
const manifestErrors = validateLinuxFrameCopyBuildManifest(buildManifest);
|
|
if (manifestErrors.length > 0) {
|
|
throw new Error(
|
|
['Invalid Linux frame-copy build manifest.', ...manifestErrors]
|
|
.map((error) => `- ${error}`)
|
|
.join('\n')
|
|
);
|
|
}
|
|
verifyBundledRuntimeFiles(nativeDir, buildManifest);
|
|
|
|
fs.chmodSync(helperPath, 0o755);
|
|
fs.chmodSync(readerPath, 0o644);
|
|
fs.rmSync(path.join(nativeDir, WINDOWS_FRAME_COPY_HELPER), { force: true });
|
|
fs.rmSync(path.join(nativeDir, UNAVAILABLE_MARKER), { force: true });
|
|
if (profile.runtimeMode === 'system') {
|
|
fs.rmSync(path.join(nativeDir, 'lib'), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
removeLinuxRuntimeNotices(nativeDir);
|
|
} else {
|
|
prepareBundledLinuxRuntimeNotices(
|
|
nativeDir,
|
|
buildManifest.sourceRuntime,
|
|
options.noticeSourceDir
|
|
);
|
|
}
|
|
|
|
return writeManifest(
|
|
nativeDir,
|
|
createPackagedManifest(buildManifest, profile, targetNames)
|
|
);
|
|
}
|
|
|
|
function preparePackagedFrameCopyArtifacts(nativeDir, platform, options = {}) {
|
|
if (platform === 'linux') {
|
|
return prepareLinuxFrameCopyArtifacts(nativeDir, options);
|
|
}
|
|
|
|
const helperPath = path.join(
|
|
nativeDir,
|
|
platform === 'win32' ? WINDOWS_FRAME_COPY_HELPER : FRAME_COPY_HELPER
|
|
);
|
|
|
|
if (platform !== 'win32' && fs.existsSync(helperPath)) {
|
|
// Asset copying drops POSIX modes; restore spawn permission.
|
|
fs.chmodSync(helperPath, 0o755);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function removeStaleFrameCopyArtifacts(nativeDir) {
|
|
for (const fileName of [
|
|
FRAME_COPY_HELPER,
|
|
WINDOWS_FRAME_COPY_HELPER,
|
|
FRAME_COPY_READER,
|
|
]) {
|
|
fs.rmSync(path.join(nativeDir, fileName), { force: true });
|
|
}
|
|
removeLinuxRuntimeNotices(nativeDir);
|
|
}
|
|
|
|
module.exports = {
|
|
preparePackagedFrameCopyArtifacts,
|
|
removeStaleFrameCopyArtifacts,
|
|
validateLinuxFrameCopyBuildManifest,
|
|
};
|