fix(packaging): tighten runtime delivery gates

This commit is contained in:
4gray committed 2026-07-18 00:43:33 +02:00
1 parent b6a7b87a61
commit 61ed0e65ea
9 files changed
+104 -26

No files matched your search

+3 -3
View File
@@ -410,7 +410,7 @@ jobs:
shell: bash
env:
IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: ${{ vars.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256 || secrets.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256 || '' }}
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: a26e28fa55c15ac5b6209d8a9f9c3c8cb39649631a53d3c4774beeae559a078c
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: 6014aa0e6d8e98cdba90f5288295a7105d7d14ab0ca906f51465eeb478d5fea0
run: |
set -euo pipefail
@@ -521,8 +521,8 @@ jobs:
env:
IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_URL: ${{ vars.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_URL || secrets.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_URL || '' }}
IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: ${{ vars.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256 || secrets.IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256 || '' }}
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_URL: https://github.com/zhongfly/mpv-winbuild/releases/download/2026-06-14-7d245fd100/mpv-dev-lgpl-x86_64-20260614-git-7d245fd100.7z
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: a26e28fa55c15ac5b6209d8a9f9c3c8cb39649631a53d3c4774beeae559a078c
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_URL: https://github.com/zhongfly/mpv-winbuild/releases/download/2026-07-17-94335ab87a/mpv-dev-lgpl-x86_64-20260717-git-94335ab87a.7z
IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256: 6014aa0e6d8e98cdba90f5288295a7105d7d14ab0ca906f51465eeb478d5fea0
run: |
set -euo pipefail
@@ -55,6 +55,27 @@ describe('embedded-mpv frame-copy package integrity', () => {
},
reason: 'runtime-artifact-invalid',
},
{
label: 'setuid helper',
mutate(fixture: RuntimeFixture) {
chmodSync(fixture.helperPath, 0o4755);
},
reason: 'runtime-artifact-invalid',
},
{
label: 'setgid helper',
mutate(fixture: RuntimeFixture) {
chmodSync(fixture.helperPath, 0o2755);
},
reason: 'runtime-artifact-invalid',
},
{
label: 'sticky helper',
mutate(fixture: RuntimeFixture) {
chmodSync(fixture.helperPath, 0o1755);
},
reason: 'runtime-artifact-invalid',
},
{
label: 'wrong reader mode',
mutate(fixture: RuntimeFixture) {
@@ -44,7 +44,7 @@ function validateRegularArtifact(
if (
stat.isSymbolicLink() ||
!stat.isFile() ||
(expectedMode !== null && (stat.mode & 0o777) !== expectedMode)
(expectedMode !== null && (stat.mode & 0o7777) !== expectedMode)
) {
return validationFailure('runtime-artifact-invalid');
}
+7 -4
View File
@@ -685,10 +685,13 @@ Windows CI uses a checksum-pinned `win32-x64` runtime archive configured
through `IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_URL` and
`IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256`. Non-tag artifact builds have
a pinned `zhongfly/mpv-winbuild` `mpv-dev-lgpl-x86_64` fallback; tagged
releases require explicit repository configuration. The archive helper accepts
normal `lib/` + `bin/` prefixes and common flat archives, preserves the DLL
basename encoded by the import library, and generates minimal build metadata
only when the archive lacks it.
releases require explicit repository configuration. Upstream retains only its
latest 30 daily builds, so the fallback and any repository-variable copy must
be refreshed as one URL/checksum pair before expiry. A long-lived mirror must
publish the matching source/build records and license notices with the binary.
The archive helper accepts normal `lib/` + `bin/` prefixes and common flat
archives, preserves the DLL basename encoded by the import library, and
generates minimal build metadata only when the archive lacks it.
The Linux builder pins FFmpeg `8.1`, mpv `0.41.0`, libplacebo `7.360.1`,
libass `0.17.3`, FreeType `2.13.3`, FriBidi `1.0.16`, HarfBuzz `8.5.0`,
-9
View File
@@ -98,15 +98,6 @@
"artifactName": "${name}-${version}-${os}-${arch}.${ext}",
"icon": "apps/web/src/assets/icons"
},
"deb": {
"fpm": ["--depends=libmpv2"]
},
"rpm": {
"fpm": ["--depends=mpv-libs"]
},
"pacman": {
"fpm": ["--depends=mpv"]
},
"flatpak": {
"branch": "stable",
"runtime": "org.freedesktop.Platform",
+5 -1
View File
@@ -217,7 +217,11 @@ missing-runtime fallback smoke; GitHub Actions never promotes automatically.
Windows CI stages a checksum-pinned x64 LGPL archive. The DLL basename encoded
in its import library is preserved and must be present beside
`iptvnator_mpv_helper.exe`. Tagged releases require explicit repository
configuration; the public fallback is for non-tag artifacts only.
configuration; the public fallback is for non-tag artifacts only. The upstream
keeps only its latest 30 daily builds, so the fallback URL and checksum plus any
matching repository variables must be refreshed as one pair before they age
out. A permanent mirror must publish the corresponding source/build records and
license notices with the binary.
## Local Development
@@ -139,10 +139,9 @@ test('rejects duplicate profile targets even when target count looks complete',
test('preserves unrelated fpm dependencies and normalizes only frame-copy dependencies', () => {
const customized = structuredClone(electronBuilderConfig);
customized.deb.fpm = [
'--depends=unrelated-runtime',
'--depends=libmpv2 >= 2',
];
customized.deb = {
fpm: ['--depends=unrelated-runtime', '--depends=libmpv2 >= 2'],
};
const system = configureLinuxFrameCopyBuild(customized, {
profileName: 'system',
});
@@ -732,6 +732,16 @@ test('Windows CI packages embedded MPV from a staged x64 runtime', () => {
const requireEmbeddedMpvLines = buildAndMakeWorkflow
.split(/\r?\n/)
.filter((line) => line.includes('IPTVNATOR_REQUIRE_EMBEDDED_MPV:'));
const defaultRuntimeUrls = [
...buildAndMakeWorkflow.matchAll(
/IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_URL:\s+(\S+)/g
),
].map((match) => match[1]);
const defaultRuntimeSha256s = [
...buildAndMakeWorkflow.matchAll(
/IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256:\s+([a-f0-9]{64})/g
),
].map((match) => match[1]);
assert.equal(
packageMetadata.scripts?.['embedded-mpv:stage-runtime:windows-archive'],
@@ -754,6 +764,16 @@ test('Windows CI packages embedded MPV from a staged x64 runtime', () => {
buildAndMakeWorkflow,
/IPTVNATOR_DEFAULT_WINDOWS_EMBEDDED_MPV_RUNTIME_URL: https:\/\/github\.com\/zhongfly\/mpv-winbuild\/releases\/download\//
);
assert.deepEqual(
[...new Set(defaultRuntimeUrls)],
[
'https://github.com/zhongfly/mpv-winbuild/releases/download/2026-07-17-94335ab87a/mpv-dev-lgpl-x86_64-20260717-git-94335ab87a.7z',
]
);
assert.deepEqual(
[...new Set(defaultRuntimeSha256s)],
['6014aa0e6d8e98cdba90f5288295a7105d7d14ab0ca906f51465eeb478d5fea0']
);
assert.match(buildAndMakeWorkflow, /refs\/tags\/v\*/);
assert.match(
buildAndMakeWorkflow,
@@ -5,6 +5,8 @@ import { dirname, join } from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
import { configureLinuxFrameCopyBuild } from './configure-linux-frame-copy-build.mjs';
const currentDir = dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const {
@@ -20,6 +22,14 @@ const electronBuilderConfig = JSON.parse(
)
);
function hasSystemFrameCopyDependency(config, format, dependency) {
return (config[format]?.fpm ?? []).some((option) =>
new RegExp(`^--depends(?:=|\\s+)${dependency}(?:$|\\s|[<>=])`).test(
option
)
);
}
test('defines the exact immutable Linux frame-copy profile matrix', () => {
assert.deepEqual(LINUX_FRAME_COPY_PROFILES, {
system: {
@@ -147,7 +157,7 @@ test('rejects a non-array profile target list', () => {
);
});
test('adds only libmpv-specific package dependencies without replacing electron-builder defaults', () => {
test('keeps frame-copy package dependencies out of the base Electron Builder config', () => {
for (const [target, dependency] of Object.entries(
LINUX_SYSTEM_PACKAGE_DEPENDENCIES
)) {
@@ -156,8 +166,38 @@ test('adds only libmpv-specific package dependencies without replacing electron-
undefined,
`${target}.depends must remain unset so electron-builder keeps its defaults`
);
assert.deepEqual(electronBuilderConfig[target]?.fpm, [
`--depends=${dependency}`,
]);
assert.equal(
hasSystemFrameCopyDependency(
electronBuilderConfig,
target,
dependency
),
false
);
}
});
test('keeps frame-copy package dependencies out of non-system passes', () => {
const configs = [
configureLinuxFrameCopyBuild(electronBuilderConfig, {
profileName: 'portable',
}),
configureLinuxFrameCopyBuild(electronBuilderConfig, {
profileName: 'flatpak',
}),
configureLinuxFrameCopyBuild(electronBuilderConfig, {
foreignDeb: true,
}),
];
for (const config of configs) {
for (const [format, dependency] of Object.entries(
LINUX_SYSTEM_PACKAGE_DEPENDENCIES
)) {
assert.equal(
hasSystemFrameCopyDependency(config, format, dependency),
false
);
}
}
});