mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
build(embedded-mpv): enhance macOS build process and validation for embedded MPV integration
Entire-Checkpoint: c6e522b4276c
This commit is contained in:
1 parent
37fb7e41ee
commit
6aa4f2521b
6 files changed
+118
-18
No files matched your search
@@ -105,8 +105,30 @@ jobs:
|
||||
- name: Build backend
|
||||
env:
|
||||
IPTVNATOR_EMBEDDED_MPV_ARCH: ${{ matrix.arch || '' }}
|
||||
IPTVNATOR_REQUIRE_EMBEDDED_MPV: ${{ matrix.os == 'macos' && (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'pull_request') && '1' || '0' }}
|
||||
run: pnpm run build:backend
|
||||
|
||||
- name: Verify embedded MPV build output
|
||||
# TEMPORARY PR TEST: remove `|| github.event_name == 'pull_request'`
|
||||
# after the macOS Embedded MPV artifact is built and manually tested.
|
||||
if: matrix.os == 'macos' && (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'pull_request')
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "::group::Native build output"
|
||||
find apps/electron-backend/native/build/Release -maxdepth 3 -type f | sort
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Dist native output"
|
||||
find dist/apps/electron-backend/native -maxdepth 3 -type f | sort
|
||||
echo "::endgroup::"
|
||||
|
||||
test -f apps/electron-backend/native/build/Release/embedded_mpv.node
|
||||
test -f dist/apps/electron-backend/native/embedded_mpv.node
|
||||
test -f dist/apps/electron-backend/native/embedded-mpv-runtime.json
|
||||
test -f dist/apps/electron-backend/native/lib/libmpv.2.dylib
|
||||
|
||||
- name: Validate AppStream metadata
|
||||
if: matrix.os == 'linux'
|
||||
run: appstreamcli validate --pedantic --no-net apps/electron-backend/linux/com.fourgray.iptvnator.metainfo.xml
|
||||
|
||||
@@ -13,6 +13,13 @@ const addonRoot = path.join(workspaceRoot, 'apps', 'electron-backend', 'native')
|
||||
const outputDir = path.join(addonRoot, 'build', 'Release');
|
||||
const outputFile = path.join(outputDir, 'embedded_mpv.node');
|
||||
const outputLibDir = path.join(outputDir, 'lib');
|
||||
const distNativeDir = path.join(
|
||||
workspaceRoot,
|
||||
'dist',
|
||||
'apps',
|
||||
'electron-backend',
|
||||
'native'
|
||||
);
|
||||
const unavailableMarkerFile = path.join(outputDir, 'embedded-mpv-unavailable.txt');
|
||||
const homebrewIncludeDir = '/opt/homebrew/include';
|
||||
const homebrewLibDir = '/opt/homebrew/lib';
|
||||
@@ -30,6 +37,9 @@ const vendoredIncludeDir = path.join(vendoredRuntimeRoot, 'include');
|
||||
const vendoredLibDir = path.join(vendoredRuntimeRoot, 'lib');
|
||||
const homebrewFallbackEnabled =
|
||||
process.env.IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW === '1';
|
||||
const embeddedMpvRequired = ['1', 'true', 'yes', 'on'].includes(
|
||||
(process.env.IPTVNATOR_REQUIRE_EMBEDDED_MPV ?? '').trim().toLowerCase()
|
||||
);
|
||||
|
||||
function log(message) {
|
||||
process.stdout.write(`[embedded-mpv] ${message}\n`);
|
||||
@@ -38,6 +48,11 @@ function log(message) {
|
||||
function cleanOutput() {
|
||||
fs.rmSync(outputFile, { force: true });
|
||||
fs.rmSync(outputLibDir, { recursive: true, force: true });
|
||||
fs.rmSync(path.join(outputDir, '.deps'), { recursive: true, force: true });
|
||||
fs.rmSync(path.join(outputDir, 'obj.target'), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
fs.rmSync(path.join(outputDir, 'embedded-mpv-runtime.json'), {
|
||||
force: true,
|
||||
});
|
||||
@@ -47,6 +62,10 @@ function cleanOutput() {
|
||||
);
|
||||
}
|
||||
|
||||
function cleanDistNativeOutput() {
|
||||
fs.rmSync(distNativeDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function readRuntimeManifest(runtimeRoot) {
|
||||
const manifestPath = path.join(runtimeRoot, 'runtime-manifest.json');
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
@@ -146,9 +165,13 @@ function runNodeGyp(command, env) {
|
||||
|
||||
function main() {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
cleanDistNativeOutput();
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
cleanOutput();
|
||||
if (embeddedMpvRequired) {
|
||||
throw new Error('Embedded MPV is required but this host is not macOS.');
|
||||
}
|
||||
log('Skipping build on non-macOS host.');
|
||||
return;
|
||||
}
|
||||
@@ -156,13 +179,16 @@ function main() {
|
||||
const runtime = resolveRuntime();
|
||||
if (!runtime) {
|
||||
cleanOutput();
|
||||
log(
|
||||
[
|
||||
`Skipping build because no embedded MPV runtime was found for darwin-${targetArch}.`,
|
||||
`Expected vendored runtime at ${vendoredRuntimeRoot}.`,
|
||||
'For local development only, set IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 to use Homebrew libmpv.',
|
||||
].join('\n')
|
||||
);
|
||||
const message = [
|
||||
`Skipping build because no embedded MPV runtime was found for darwin-${targetArch}.`,
|
||||
`Expected vendored runtime at ${vendoredRuntimeRoot}.`,
|
||||
'For local development only, set IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1 to use Homebrew libmpv.',
|
||||
].join('\n');
|
||||
if (embeddedMpvRequired) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
log(message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,15 @@
|
||||
"assets": [
|
||||
"apps/electron-backend/src/assets",
|
||||
{
|
||||
"glob": "{embedded_mpv.node,embedded-mpv-runtime.json,embedded-mpv-unavailable.txt,lib/*}",
|
||||
"glob": "**/*",
|
||||
"input": "apps/electron-backend/native/build/Release",
|
||||
"output": "native"
|
||||
"output": "native",
|
||||
"ignore": [
|
||||
".deps/**",
|
||||
"obj.target/**",
|
||||
"**/.deps/**",
|
||||
"**/obj.target/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -91,9 +97,15 @@
|
||||
"assets": [
|
||||
"apps/electron-backend/src/assets",
|
||||
{
|
||||
"glob": "{embedded_mpv.node,embedded-mpv-runtime.json,embedded-mpv-unavailable.txt,lib/*}",
|
||||
"glob": "**/*",
|
||||
"input": "apps/electron-backend/native/build/Release",
|
||||
"output": "native"
|
||||
"output": "native",
|
||||
"ignore": [
|
||||
".deps/**",
|
||||
"obj.target/**",
|
||||
"**/.deps/**",
|
||||
"**/obj.target/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -120,6 +132,22 @@
|
||||
"frontendProject": "web",
|
||||
"sourcePath": "dist/apps",
|
||||
"outputPath": "dist/packages",
|
||||
"files": [
|
||||
{
|
||||
"from": "electron-backend/native",
|
||||
"to": "electron-backend/native",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"asarUnpack": [
|
||||
"**/better-sqlite3/**",
|
||||
"**/bindings/**",
|
||||
"**/file-uri-to-path/**",
|
||||
"electron-backend/native/**",
|
||||
"**/electron-backend/native/**",
|
||||
"**/*.node",
|
||||
"**/*.dylib"
|
||||
],
|
||||
"prepackageOnly": true
|
||||
}
|
||||
},
|
||||
@@ -130,7 +158,23 @@
|
||||
"name": "electron-backend",
|
||||
"frontendProject": "web",
|
||||
"sourcePath": "dist/apps",
|
||||
"outputPath": "dist/executables"
|
||||
"outputPath": "dist/executables",
|
||||
"files": [
|
||||
{
|
||||
"from": "electron-backend/native",
|
||||
"to": "electron-backend/native",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"asarUnpack": [
|
||||
"**/better-sqlite3/**",
|
||||
"**/bindings/**",
|
||||
"**/file-uri-to-path/**",
|
||||
"electron-backend/native/**",
|
||||
"**/electron-backend/native/**",
|
||||
"**/*.node",
|
||||
"**/*.dylib"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
|
||||
@@ -76,8 +76,7 @@ Current development behavior:
|
||||
- The staged runtime must contain `include/mpv/client.h`, `lib/*.dylib`, and `runtime-manifest.json`.
|
||||
- The compiled `.node` addon is copied into `dist/apps/electron-backend/native/embedded_mpv.node`.
|
||||
- Bundled runtime files are copied into `dist/apps/electron-backend/native/lib/`. Most are `.dylib` files, but some Homebrew-linked runtimes expose non-`.dylib` Mach-O files such as a framework `Python` binary.
|
||||
- `electron-builder` unpacks `.node` files from ASAR.
|
||||
- `electron-builder` also unpacks `.dylib` files from ASAR.
|
||||
- `electron-builder` includes `dist/apps/electron-backend/native/` in the app package and unpacks `electron-backend/native/**` from ASAR so the addon, manifest, dylibs, and non-`.dylib` Mach-O runtime files are filesystem-addressable.
|
||||
|
||||
Current release caveat:
|
||||
|
||||
@@ -115,7 +114,7 @@ node tools/embedded-mpv/stage-macos-runtime.mjs arm64 /path/to/lgpl-prefix
|
||||
node tools/embedded-mpv/stage-macos-runtime.mjs x64 /path/to/lgpl-prefix
|
||||
```
|
||||
|
||||
Tagged macOS release CI builds that prefix from pinned source archives first:
|
||||
Tagged macOS release CI builds that prefix from pinned source archives first. The workflow can temporarily run the same path for macOS PR artifacts while the bundled runtime is being tested:
|
||||
|
||||
```bash
|
||||
pnpm embedded-mpv:build-runtime -- arm64 /tmp/embedded-mpv-prefix
|
||||
@@ -165,7 +164,7 @@ For tagged macOS builds, CI must:
|
||||
- set `IPTVNATOR_EMBEDDED_MPV_ARCH=${arch}` for backend build and packaging
|
||||
- set `IPTVNATOR_REQUIRE_EMBEDDED_MPV=1` for packaging and package-layout verification
|
||||
|
||||
For PR, development, Linux, and Windows packaging, `IPTVNATOR_REQUIRE_EMBEDDED_MPV` stays unset or `0`. In that mode the package validators still reject a present but invalid Embedded MPV runtime, but they do not require the addon to exist. This keeps the native feature in-tree without making non-macOS or non-release builds depend on macOS runtime artifacts.
|
||||
During the temporary macOS PR artifact test, CI also sets `IPTVNATOR_REQUIRE_EMBEDDED_MPV=1` for macOS PR backend build, packaging, and package-layout verification. After the artifact is manually validated, remove the workflow's `pull_request` condition so PR, development, Linux, and Windows packaging leave `IPTVNATOR_REQUIRE_EMBEDDED_MPV` unset or `0`. In that normal mode the package validators still reject a present but invalid Embedded MPV runtime, but they do not require the addon to exist. This keeps the native feature in-tree without making non-macOS or non-release builds depend on macOS runtime artifacts.
|
||||
|
||||
## Release Safety
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
"to": "remote-control-web",
|
||||
"filter": ["**/*"]
|
||||
},
|
||||
{
|
||||
"from": "dist/apps/electron-backend/native",
|
||||
"to": "electron-backend/native",
|
||||
"filter": ["**/*"]
|
||||
},
|
||||
"electron-backend/**/*",
|
||||
"web/**/*",
|
||||
"!**/*.map"
|
||||
@@ -20,6 +25,8 @@
|
||||
"**/better-sqlite3/**",
|
||||
"**/bindings/**",
|
||||
"**/file-uri-to-path/**",
|
||||
"electron-backend/native/**",
|
||||
"**/electron-backend/native/**",
|
||||
"**/*.node",
|
||||
"**/*.dylib"
|
||||
],
|
||||
|
||||
@@ -50,7 +50,7 @@ If the prefix contains `runtime-manifest.json`, the staging script copies its bu
|
||||
|
||||
## Building The CI Runtime
|
||||
|
||||
Tagged macOS release builds build the runtime from pinned source archives before `electron-backend:build`:
|
||||
Tagged macOS release builds build the runtime from pinned source archives before `electron-backend:build`. The workflow can also enable this path temporarily for macOS PR artifact testing:
|
||||
|
||||
```bash
|
||||
pnpm embedded-mpv:build-runtime -- arm64 /tmp/embedded-mpv-prefix
|
||||
@@ -70,6 +70,8 @@ The build manifest records source URLs, downloaded archive SHA-256 values where
|
||||
|
||||
`apps/electron-backend/build-embedded-mpv.js` links the native addon against the staged runtime, copies dylibs into `apps/electron-backend/native/build/Release/lib/`, rewrites Mach-O paths to `@loader_path`, and writes `embedded-mpv-runtime.json`.
|
||||
|
||||
The Electron builder and Nx package/make targets package `dist/apps/electron-backend/native/` into `electron-backend/native/` and unpack that whole native directory from ASAR so the addon, runtime manifest, dylibs, and non-`.dylib` Mach-O runtime files are available as real files.
|
||||
|
||||
During release packaging, `tools/packaging/electron-after-pack.cjs` verifies that the packaged app uses a `vendored-lgpl` runtime and has no `/opt/homebrew` or `/usr/local` dynamic links for embedded MPV.
|
||||
|
||||
Set `IPTVNATOR_REQUIRE_EMBEDDED_MPV=1` when packaging a macOS release artifact that must include Embedded MPV. PR and non-tag development builds leave that variable unset or `0`, so the same in-tree code can package without a staged runtime while Settings keeps Embedded MPV hidden.
|
||||
Set `IPTVNATOR_REQUIRE_EMBEDDED_MPV=1` when packaging a macOS release artifact that must include Embedded MPV. The same variable is temporarily enabled for macOS PR artifacts while the bundled runtime is being tested. After manual artifact validation, remove the workflow's PR condition so PR and non-tag development builds leave the variable unset or `0` and can package without a staged runtime while Settings keeps Embedded MPV hidden.
|
||||
Reference in new issue
Block a user