From 61ed0e65ea5790e031e905efced46c8727171ea2 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 18 Jul 2026 00:43:33 +0200 Subject: [PATCH] fix(packaging): tighten runtime delivery gates --- .github/workflows/build-and-make.yaml | 6 +-- .../package-integrity.spec.ts | 21 ++++++++ .../package-validator.ts | 2 +- docs/architecture/embedded-mpv-native.md | 11 +++-- electron-builder.json | 9 ---- tools/embedded-mpv/README.md | 6 ++- .../configure-linux-frame-copy-build.test.mjs | 7 ++- .../electron-package-identity.test.mjs | 20 ++++++++ .../linux-frame-copy-profile.test.mjs | 48 +++++++++++++++++-- 9 files changed, 104 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-and-make.yaml b/.github/workflows/build-and-make.yaml index 424e4c763..ba4661567 100644 --- a/.github/workflows/build-and-make.yaml +++ b/.github/workflows/build-and-make.yaml @@ -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 diff --git a/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-integrity.spec.ts b/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-integrity.spec.ts index a96fdc5ff..4dd40ba8d 100644 --- a/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-integrity.spec.ts +++ b/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-integrity.spec.ts @@ -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) { diff --git a/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-validator.ts b/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-validator.ts index 830b0eb72..145eba544 100644 --- a/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-validator.ts +++ b/apps/electron-backend/src/app/services/embedded-mpv-frame-copy-runtime/package-validator.ts @@ -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'); } diff --git a/docs/architecture/embedded-mpv-native.md b/docs/architecture/embedded-mpv-native.md index a5dea08c4..cee3b08fb 100644 --- a/docs/architecture/embedded-mpv-native.md +++ b/docs/architecture/embedded-mpv-native.md @@ -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`, diff --git a/electron-builder.json b/electron-builder.json index 7d4cb2887..06b36b2c9 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -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", diff --git a/tools/embedded-mpv/README.md b/tools/embedded-mpv/README.md index 8450284e4..26a593dd9 100644 --- a/tools/embedded-mpv/README.md +++ b/tools/embedded-mpv/README.md @@ -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 diff --git a/tools/packaging/configure-linux-frame-copy-build.test.mjs b/tools/packaging/configure-linux-frame-copy-build.test.mjs index 6b9e7164c..a076025b3 100644 --- a/tools/packaging/configure-linux-frame-copy-build.test.mjs +++ b/tools/packaging/configure-linux-frame-copy-build.test.mjs @@ -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', }); diff --git a/tools/packaging/electron-package-identity.test.mjs b/tools/packaging/electron-package-identity.test.mjs index 833b7c844..e7224a1af 100644 --- a/tools/packaging/electron-package-identity.test.mjs +++ b/tools/packaging/electron-package-identity.test.mjs @@ -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, diff --git a/tools/packaging/linux-frame-copy-profile.test.mjs b/tools/packaging/linux-frame-copy-profile.test.mjs index 2cae643f5..7f4c50db9 100644 --- a/tools/packaging/linux-frame-copy-profile.test.mjs +++ b/tools/packaging/linux-frame-copy-profile.test.mjs @@ -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 + ); + } } });