Skip to content

Commit ecd0c54

Browse files
committed
feat(ci): use static FFmpeg musl package for Alpine builds
- Add musl libc detection in ffmpeg-paths-lib.ts - On musl systems, try @pproenca/webcodecs-ffmpeg-linux-x64-musl first - Update CI to install musl FFmpeg package instead of system ffmpeg-dev - Remove system pkg-config fallback for Alpine (use npm package only) This enables proper static linking on Alpine Linux using the pre-built FFmpeg package with musl libc support.
1 parent 7d38fbe commit ecd0c54

3 files changed

Lines changed: 76 additions & 32 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,16 @@ jobs:
106106
run: |
107107
# Install build tools and Node.js
108108
apk add --no-cache build-base python3 pkgconf git nodejs npm
109-
# Install FFmpeg development files - Alpine uses split packages
110-
# ffmpeg-dev pulls in the core libs, ffmpeg-libavcodec etc for runtime
111-
apk add --no-cache ffmpeg-dev
112-
# Verify pkg-config can find FFmpeg
113-
pkg-config --exists libavcodec libavformat libavutil libswscale libswresample libavfilter
114109
115110
- name: Checkout
116111
uses: actions/checkout@v4
117112

118113
- name: Install dependencies
119114
run: npm install --omit=optional
120115

116+
- name: Install FFmpeg musl package
117+
run: npm install @pproenca/webcodecs-ffmpeg-linux-x64-musl
118+
121119
- name: Build
122120
run: npm run build
123121

gyp/ffmpeg-paths-lib.js

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,45 @@ function filterFrameworkFlags(flags) {
7171
}
7272
return result.join(' ');
7373
}
74-
function tryResolveFromNpmPackage() {
75-
// Build platform-specific package name (e.g., @pproenca/webcodecs-ffmpeg-darwin-arm64)
76-
const pkgName = `@pproenca/webcodecs-ffmpeg-${(0, node_os_1.platform)()}-${(0, node_os_1.arch)()}`;
74+
function isMuslLibc() {
75+
// Check if we're running on musl libc (Alpine Linux, etc.)
76+
if ((0, node_os_1.platform)() !== 'linux') {
77+
return false;
78+
}
7779
try {
78-
// Resolve the pkgconfig export from the platform package
79-
// The package exports "./pkgconfig" pointing to "./lib/pkgconfig/index.js"
80-
const pkgconfigIndex = require.resolve(`${pkgName}/pkgconfig`);
81-
const pkgconfig = (0, node_path_1.dirname)(pkgconfigIndex);
82-
if ((0, node_fs_1.existsSync)(pkgconfig)) {
83-
// The root is two levels up from lib/pkgconfig
84-
const root = (0, node_path_1.dirname)((0, node_path_1.dirname)(pkgconfig));
85-
return { root, pkgconfig };
86-
}
80+
// ldd --version outputs "musl libc" on musl systems
81+
const result = (0, node_child_process_1.execSync)('ldd --version 2>&1 || true', {
82+
encoding: 'utf8',
83+
stdio: ['pipe', 'pipe', 'pipe'],
84+
});
85+
return result.toLowerCase().includes('musl');
8786
}
8887
catch {
89-
// Package not installed - continue to next fallback
88+
return false;
89+
}
90+
}
91+
function tryResolveFromNpmPackage() {
92+
// Build platform-specific package name (e.g., @pproenca/webcodecs-ffmpeg-darwin-arm64)
93+
// On musl systems, try the musl-specific package first
94+
const basePlatform = `${(0, node_os_1.platform)()}-${(0, node_os_1.arch)()}`;
95+
const pkgNames = isMuslLibc()
96+
? [`@pproenca/webcodecs-ffmpeg-${basePlatform}-musl`, `@pproenca/webcodecs-ffmpeg-${basePlatform}`]
97+
: [`@pproenca/webcodecs-ffmpeg-${basePlatform}`];
98+
for (const pkgName of pkgNames) {
99+
try {
100+
// Resolve the pkgconfig export from the platform package
101+
// The package exports "./pkgconfig" pointing to "./lib/pkgconfig/index.js"
102+
const pkgconfigIndex = require.resolve(`${pkgName}/pkgconfig`);
103+
const pkgconfig = (0, node_path_1.dirname)(pkgconfigIndex);
104+
if ((0, node_fs_1.existsSync)(pkgconfig)) {
105+
// The root is two levels up from lib/pkgconfig
106+
const root = (0, node_path_1.dirname)((0, node_path_1.dirname)(pkgconfig));
107+
return { root, pkgconfig };
108+
}
109+
}
110+
catch {
111+
// Package not installed - try next one
112+
}
90113
}
91114
return null;
92115
}

gyp/ffmpeg-paths-lib.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,46 @@ export function filterFrameworkFlags(flags: string): string {
7373
return result.join(' ');
7474
}
7575

76-
function tryResolveFromNpmPackage(): FfmpegRoot | null {
77-
// Build platform-specific package name (e.g., @pproenca/webcodecs-ffmpeg-darwin-arm64)
78-
const pkgName = `@pproenca/webcodecs-ffmpeg-${platform()}-${arch()}`;
79-
76+
function isMuslLibc(): boolean {
77+
// Check if we're running on musl libc (Alpine Linux, etc.)
78+
if (platform() !== 'linux') {
79+
return false;
80+
}
8081
try {
81-
// Resolve the pkgconfig export from the platform package
82-
// The package exports "./pkgconfig" pointing to "./lib/pkgconfig/index.js"
83-
const pkgconfigIndex = require.resolve(`${pkgName}/pkgconfig`);
84-
const pkgconfig = dirname(pkgconfigIndex);
82+
// ldd --version outputs "musl libc" on musl systems
83+
const result = execSync('ldd --version 2>&1 || true', {
84+
encoding: 'utf8',
85+
stdio: ['pipe', 'pipe', 'pipe'],
86+
});
87+
return result.toLowerCase().includes('musl');
88+
} catch {
89+
return false;
90+
}
91+
}
8592

86-
if (existsSync(pkgconfig)) {
87-
// The root is two levels up from lib/pkgconfig
88-
const root = dirname(dirname(pkgconfig));
89-
return {root, pkgconfig};
93+
function tryResolveFromNpmPackage(): FfmpegRoot | null {
94+
// Build platform-specific package name (e.g., @pproenca/webcodecs-ffmpeg-darwin-arm64)
95+
// On musl systems, try the musl-specific package first
96+
const basePlatform = `${platform()}-${arch()}`;
97+
const pkgNames = isMuslLibc()
98+
? [`@pproenca/webcodecs-ffmpeg-${basePlatform}-musl`, `@pproenca/webcodecs-ffmpeg-${basePlatform}`]
99+
: [`@pproenca/webcodecs-ffmpeg-${basePlatform}`];
100+
101+
for (const pkgName of pkgNames) {
102+
try {
103+
// Resolve the pkgconfig export from the platform package
104+
// The package exports "./pkgconfig" pointing to "./lib/pkgconfig/index.js"
105+
const pkgconfigIndex = require.resolve(`${pkgName}/pkgconfig`);
106+
const pkgconfig = dirname(pkgconfigIndex);
107+
108+
if (existsSync(pkgconfig)) {
109+
// The root is two levels up from lib/pkgconfig
110+
const root = dirname(dirname(pkgconfig));
111+
return {root, pkgconfig};
112+
}
113+
} catch {
114+
// Package not installed - try next one
90115
}
91-
} catch {
92-
// Package not installed - continue to next fallback
93116
}
94117
return null;
95118
}

0 commit comments

Comments
 (0)