fix: fix o2switch deployment build and yt-dlp Python compatibility
Turbopack requires native SWC bindings unavailable on o2switch's old glibc, so force webpack for production builds. NODE_ENV=production on o2switch's shell also caused npm to skip devDependencies needed at build time (Tailwind, PostCSS), so deploy now installs with --include=dev. yt-dlp's standalone PyInstaller binary self-extracts to noexec /tmp and fails to mmap its bundled shared libs there, and o2switch's system python3 (3.6) is too old for the plain zipapp. Revert to the zipapp and invoke it explicitly through a configurable PYTHON_BIN, set to o2switch's newer Python 3.11 in .env.prod. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,47 +1,23 @@
|
||||
const mockExistsSync = jest.fn()
|
||||
jest.mock('fs', () => ({ existsSync: (...args: unknown[]) => mockExistsSync(...args) }))
|
||||
jest.mock('../../../config/app.config', () => ({ config: { BIN_DIR: '/bin', PYTHON_BIN: 'python3.11' } }))
|
||||
|
||||
import { buildYtdlpCommand } from '../ytdlp'
|
||||
|
||||
function setPlatform(platform: NodeJS.Platform) {
|
||||
Object.defineProperty(process, 'platform', { value: platform })
|
||||
}
|
||||
|
||||
describe('buildYtdlpCommand', () => {
|
||||
const originalPlatform = process.platform
|
||||
|
||||
afterEach(() => {
|
||||
setPlatform(originalPlatform)
|
||||
mockExistsSync.mockReset()
|
||||
})
|
||||
|
||||
it('runs the bundled binary directly on Linux', () => {
|
||||
setPlatform('linux')
|
||||
it('runs the bundled zipapp through the configured Python interpreter', () => {
|
||||
mockExistsSync.mockReturnValue(true)
|
||||
const { command, args } = buildYtdlpCommand(['-J', 'https://x.test'])
|
||||
expect(command).toContain('yt-dlp')
|
||||
expect(args).toEqual(['-J', 'https://x.test'])
|
||||
})
|
||||
|
||||
it('runs the bundled binary through python on Windows', () => {
|
||||
setPlatform('win32')
|
||||
mockExistsSync.mockReturnValue(true)
|
||||
const { command, args } = buildYtdlpCommand(['-J', 'https://x.test'])
|
||||
expect(command).toBe('python')
|
||||
expect(command).toBe('python3.11')
|
||||
expect(args[0]).toContain('yt-dlp')
|
||||
expect(args.slice(1)).toEqual(['-J', 'https://x.test'])
|
||||
})
|
||||
|
||||
it('runs a system-wide yt-dlp directly on Windows when no bundled binary exists', () => {
|
||||
setPlatform('win32')
|
||||
mockExistsSync.mockReturnValue(false)
|
||||
const { command, args } = buildYtdlpCommand(['-J', 'https://x.test'])
|
||||
expect(command).toBe('yt-dlp')
|
||||
expect(args).toEqual(['-J', 'https://x.test'])
|
||||
})
|
||||
|
||||
it('runs a system-wide yt-dlp directly on Linux when no bundled binary exists', () => {
|
||||
setPlatform('linux')
|
||||
it('runs a system-wide yt-dlp directly when no bundled binary exists', () => {
|
||||
mockExistsSync.mockReturnValue(false)
|
||||
const { command, args } = buildYtdlpCommand(['-J', 'https://x.test'])
|
||||
expect(command).toBe('yt-dlp')
|
||||
|
||||
+10
-7
@@ -22,17 +22,20 @@ export function resolveYtdlpBin(): string {
|
||||
|
||||
export type YtdlpCommand = { command: string; args: string[] }
|
||||
|
||||
// The bundled bin/yt-dlp is a Python zipapp relying on a `#!/usr/bin/env python3`
|
||||
// shebang: Linux (o2switch prod) execs it natively, but Windows' spawn() has no
|
||||
// shebang support and fails with ENOENT, so it must be run through `python` there.
|
||||
// A system-wide `yt-dlp` (PATH fallback) already has a proper platform launcher
|
||||
// on both OSes and is always run directly.
|
||||
// bin/yt-dlp is a pure-Python zipapp, not a standalone binary: it has no bundled
|
||||
// interpreter or native libs to extract/mmap, so it works on hosts with a
|
||||
// noexec /tmp (unlike yt-dlp's PyInstaller-built binaries). It relies on a
|
||||
// `#!/usr/bin/env python3` shebang, which spawn() can't honor on Windows and
|
||||
// which resolves to whatever `python3` happens to be on PATH elsewhere (on
|
||||
// o2switch that's an unsupported 3.6) — so it's always invoked explicitly
|
||||
// through PYTHON_BIN. A system-wide `yt-dlp` (PATH fallback) already has its
|
||||
// own proper launcher and is run directly.
|
||||
export function buildYtdlpCommand(args: string[]): YtdlpCommand {
|
||||
const bin = resolveYtdlpBin()
|
||||
const isBundledScript = bin !== 'yt-dlp'
|
||||
|
||||
if (process.platform === 'win32' && isBundledScript) {
|
||||
return { command: 'python', args: [bin, ...args] }
|
||||
if (isBundledScript) {
|
||||
return { command: config.PYTHON_BIN, args: [bin, ...args] }
|
||||
}
|
||||
|
||||
return { command: bin, args }
|
||||
|
||||
Reference in New Issue
Block a user