mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
The publish-snap verifier had never run against a real release and encoded three stale expectations that the tag build's own validators do not share: - it required the app under usr/lib/iptvnator inside the snap, while Electron Builder's snap target ships the app at the snap root (/iptvnator.bin, /resources/**) — the layout the packaged smoke tests exercise; - it validated the source archive's runtime manifest with the raw source-build validator, but the archive carries the STAGED manifest (origin "vendored-lgpl" + sourceBuildOrigin) written by stage-runtime.mjs; the staged envelope is now checked explicitly and the remaining fields still go through the shared validator via an origin projection; - it deep-equaled the snap's bundled sourceRuntime against the archive manifest, but the snap bundles the builder view (no staging envelope); the binding now projects the envelope away first. Verified end-to-end in a Linux container against the real v0.23.0 release assets: release-snap-assets.cjs verify now passes and emits the sealed snapshot receipt. Regression tests cover the legacy usr/lib layout and staged-envelope mismatches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
102 lines
2.7 KiB
JavaScript
102 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import {
|
|
collectEmbeddedMpvNativeArchiveEntries,
|
|
listAsarPackageEntries,
|
|
} from './asar-dependency-closure.mjs';
|
|
import { validateExtractedSnapMetadata } from './verify-linux-frame-copy-runtime.mjs';
|
|
|
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
const SNAP_RELEASE_BOUNDARY_SCHEMA_VERSION = 1;
|
|
|
|
export function validateExtractedSnapReleaseBoundary(
|
|
extractionRoot,
|
|
{ asarListPackage = listAsarPackageEntries } = {}
|
|
) {
|
|
const errors = [...validateExtractedSnapMetadata(extractionRoot)];
|
|
const asarPath = path.join(extractionRoot, 'resources', 'app.asar');
|
|
let asarStat;
|
|
try {
|
|
asarStat = fs.lstatSync(asarPath);
|
|
} catch {
|
|
errors.push(
|
|
`Public-release Snap must contain its canonical app.asar: ${asarPath}`
|
|
);
|
|
return errors;
|
|
}
|
|
if (
|
|
!asarStat.isFile() ||
|
|
asarStat.isSymbolicLink() ||
|
|
asarStat.size === 0
|
|
) {
|
|
errors.push(
|
|
`Public-release Snap app.asar must be a non-empty regular file: ${asarPath}`
|
|
);
|
|
return errors;
|
|
}
|
|
|
|
let nativeEntries;
|
|
try {
|
|
nativeEntries = collectEmbeddedMpvNativeArchiveEntries(
|
|
asarListPackage(asarPath)
|
|
);
|
|
} catch (error) {
|
|
errors.push(
|
|
`Unable to inspect public-release Snap app.asar at ${asarPath}: ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`
|
|
);
|
|
return errors;
|
|
}
|
|
if (nativeEntries.length > 0) {
|
|
errors.push(
|
|
`Public-release Snap app.asar must not contain embedded MPV native payloads: ${nativeEntries.join(
|
|
', '
|
|
)}`
|
|
);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
function main() {
|
|
const args = process.argv.slice(2);
|
|
if (args.length !== 1 || args[0].length === 0) {
|
|
throw new Error(
|
|
'Usage: validate-snap-release-boundary.mjs <extracted-snap-root>'
|
|
);
|
|
}
|
|
const errors = validateExtractedSnapReleaseBoundary(path.resolve(args[0]));
|
|
process.stdout.write(
|
|
`${JSON.stringify({
|
|
schemaVersion: SNAP_RELEASE_BOUNDARY_SCHEMA_VERSION,
|
|
errors,
|
|
})}\n`
|
|
);
|
|
}
|
|
|
|
function isMainModule() {
|
|
if (!process.argv[1]) {
|
|
return false;
|
|
}
|
|
try {
|
|
return fs.realpathSync(process.argv[1]) === fs.realpathSync(scriptPath);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (isMainModule()) {
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
process.stderr.write(
|
|
`${error instanceof Error ? error.message : String(error)}\n`
|
|
);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|