mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 01:16: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
182 lines
5.4 KiB
JavaScript
182 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
const crypto = require('node:crypto');
|
|
const fs = require('node:fs');
|
|
const { isDeepStrictEqual } = require('node:util');
|
|
|
|
const SOURCE_ARCHIVE_BINDING_SCHEMA_VERSION = 1;
|
|
const SOURCE_ARCHIVE_NAME = 'linux-frame-copy-runtime-sources.tar.xz';
|
|
const SOURCE_ARCHIVE_BINDING_NAME = 'source-archive-binding.json';
|
|
const GIT_COMMIT_PATTERN = /^[a-f0-9]{40,64}$/;
|
|
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
|
|
function validateLinuxSourceArchiveBinding(
|
|
binding,
|
|
{ expectedRepositoryRevision, expectedSha256 } = {}
|
|
) {
|
|
const errors = [];
|
|
if (
|
|
binding === null ||
|
|
typeof binding !== 'object' ||
|
|
Array.isArray(binding)
|
|
) {
|
|
return ['Linux source archive binding must be an object.'];
|
|
}
|
|
if (
|
|
!isDeepStrictEqual(Object.keys(binding).sort(), [
|
|
'name',
|
|
'repositoryRevision',
|
|
'schemaVersion',
|
|
'sha256',
|
|
])
|
|
) {
|
|
errors.push(
|
|
'Linux source archive binding must contain only schemaVersion, name, sha256, and repositoryRevision.'
|
|
);
|
|
}
|
|
if (binding.schemaVersion !== SOURCE_ARCHIVE_BINDING_SCHEMA_VERSION) {
|
|
errors.push(
|
|
`Linux source archive binding schemaVersion must equal ${SOURCE_ARCHIVE_BINDING_SCHEMA_VERSION}.`
|
|
);
|
|
}
|
|
if (binding.name !== SOURCE_ARCHIVE_NAME) {
|
|
errors.push(
|
|
`Linux source archive binding name must equal ${SOURCE_ARCHIVE_NAME}.`
|
|
);
|
|
}
|
|
if (
|
|
typeof binding.sha256 !== 'string' ||
|
|
!SHA256_PATTERN.test(binding.sha256)
|
|
) {
|
|
errors.push(
|
|
'Linux source archive binding sha256 must be a lowercase SHA-256 digest.'
|
|
);
|
|
}
|
|
if (
|
|
typeof binding.repositoryRevision !== 'string' ||
|
|
!GIT_COMMIT_PATTERN.test(binding.repositoryRevision)
|
|
) {
|
|
errors.push(
|
|
'Linux source archive binding repositoryRevision must be a full Git commit.'
|
|
);
|
|
}
|
|
if (
|
|
expectedRepositoryRevision !== undefined &&
|
|
binding.repositoryRevision !== expectedRepositoryRevision
|
|
) {
|
|
errors.push(
|
|
'Linux source archive binding repositoryRevision does not match the expected release commit.'
|
|
);
|
|
}
|
|
if (expectedSha256 !== undefined && binding.sha256 !== expectedSha256) {
|
|
errors.push(
|
|
'Linux source archive binding sha256 does not match the source archive bytes.'
|
|
);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
function sha256File(filePath) {
|
|
const descriptor = fs.openSync(filePath, 'r');
|
|
const hash = crypto.createHash('sha256');
|
|
const buffer = Buffer.alloc(1024 * 1024);
|
|
try {
|
|
let bytesRead;
|
|
do {
|
|
bytesRead = fs.readSync(descriptor, buffer, 0, buffer.length, null);
|
|
if (bytesRead > 0) {
|
|
hash.update(buffer.subarray(0, bytesRead));
|
|
}
|
|
} while (bytesRead > 0);
|
|
} finally {
|
|
fs.closeSync(descriptor);
|
|
}
|
|
return hash.digest('hex');
|
|
}
|
|
|
|
function createLinuxSourceArchiveBinding({ archivePath, repositoryRevision }) {
|
|
const stat = fs.lstatSync(archivePath);
|
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.size === 0) {
|
|
throw new Error(
|
|
'Linux source archive must be a non-empty regular file.'
|
|
);
|
|
}
|
|
const binding = {
|
|
schemaVersion: SOURCE_ARCHIVE_BINDING_SCHEMA_VERSION,
|
|
name: SOURCE_ARCHIVE_NAME,
|
|
sha256: sha256File(archivePath),
|
|
repositoryRevision,
|
|
};
|
|
const errors = validateLinuxSourceArchiveBinding(binding);
|
|
if (errors.length > 0) {
|
|
throw new Error(errors.join('\n'));
|
|
}
|
|
return binding;
|
|
}
|
|
|
|
function parseArguments(argv) {
|
|
const [command, ...tokens] = argv;
|
|
if (tokens.length % 2 !== 0) {
|
|
throw new Error('Linux source archive binding arguments are invalid.');
|
|
}
|
|
const options = {};
|
|
for (let index = 0; index < tokens.length; index += 2) {
|
|
const token = tokens[index];
|
|
const value = tokens[index + 1];
|
|
if (
|
|
!token.startsWith('--') ||
|
|
value === undefined ||
|
|
Object.hasOwn(options, token.slice(2))
|
|
) {
|
|
throw new Error(
|
|
'Linux source archive binding arguments are invalid.'
|
|
);
|
|
}
|
|
options[token.slice(2)] = value;
|
|
}
|
|
return { command, options };
|
|
}
|
|
|
|
function main(argv = process.argv.slice(2)) {
|
|
const { command, options } = parseArguments(argv);
|
|
if (
|
|
command !== 'create' ||
|
|
!options.archive ||
|
|
!options['repository-revision'] ||
|
|
!options.output
|
|
) {
|
|
throw new Error(
|
|
'Usage: linux-source-archive-contract.cjs create --archive <path> --repository-revision <commit> --output <path>'
|
|
);
|
|
}
|
|
const binding = createLinuxSourceArchiveBinding({
|
|
archivePath: options.archive,
|
|
repositoryRevision: options['repository-revision'],
|
|
});
|
|
fs.writeFileSync(options.output, `${JSON.stringify(binding, null, 2)}\n`, {
|
|
mode: 0o644,
|
|
});
|
|
}
|
|
|
|
if (require.main === module) {
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
process.stderr.write(
|
|
`${error instanceof Error ? error.message : String(error)}\n`
|
|
);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
SOURCE_ARCHIVE_BINDING_NAME,
|
|
SOURCE_ARCHIVE_BINDING_SCHEMA_VERSION,
|
|
SOURCE_ARCHIVE_NAME,
|
|
createLinuxSourceArchiveBinding,
|
|
sha256File,
|
|
validateLinuxSourceArchiveBinding,
|
|
};
|