fix(setup): tolerate yt-dlp execve ENOENT on slim docker bases
문제: alpine/distroless 처럼 glibc 가 없는 도커 베이스 이미지에서는 PyInstaller 로 빌드된 yt-dlp_linux 의 동적 링커가 없어 execve 가 ENOENT 를 반환. 이전에는 npm run setup 이 통째로 실패해서 Docker 빌드를 차단했음. 수정: - 다운로드는 됐지만 --version 검증이 실패하면 throw 하지 않고 안내만 출력 후 계속 진행. 못 쓰는 바이너리는 unlink 해서 혼동 방지. - SKIP_YT_DLP=1 환경변수로 다운로드 자체를 건너뛸 수 있게 추가. - 도커/PATH 설치 가이드를 warn 으로 같이 노출 (apt/apk/pip 명령). - README 외부 의존 섹션에도 slim base / SKIP_YT_DLP 안내 추가. src/youtube.ts 의 PATH fallback 은 그대로라 시스템에 yt-dlp 가 설치돼 있으면 런타임에 자동으로 그것을 사용합니다. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,7 @@ npm start
|
|||||||
|
|
||||||
## 외부 의존
|
## 외부 의존
|
||||||
|
|
||||||
- `yt-dlp` — YouTube 영상 가져오기. `npm run setup` 이 `./bin/yt-dlp` 로 자동 설치하지만 PATH 에 이미 있어도 됩니다.
|
- `yt-dlp` — YouTube 영상 가져오기. `npm run setup` 이 `./bin/yt-dlp` 로 자동 설치하지만 PATH 에 이미 있어도 됩니다. Alpine / distroless 같이 glibc 가 없는 슬림 도커 베이스에서는 번들 바이너리가 안 도니 `apk add yt-dlp` (또는 `apt-get install yt-dlp`, `pip install yt-dlp`) 로 PATH 에 직접 설치하세요. 도커 빌드에서 아예 받지 않으려면 `SKIP_YT_DLP=1 npm run setup`. (검증 실패해도 setup 은 경고만 출력하고 빌드는 계속 진행됩니다.)
|
||||||
- `ffmpeg` — 영상 트림 저장 (`PATH` 에 설치). 없으면 trim 설정만 저장됩니다. `npm run setup` 이 설치 여부를 검사해 안내 메시지를 출력합니다.
|
- `ffmpeg` — 영상 트림 저장 (`PATH` 에 설치). 없으면 trim 설정만 저장됩니다. `npm run setup` 이 설치 여부를 검사해 안내 메시지를 출력합니다.
|
||||||
|
|
||||||
## 데이터 위치
|
## 데이터 위치
|
||||||
|
|||||||
@@ -73,7 +73,20 @@ function downloadFile(url, dest) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ytDlpInstallHint() {
|
||||||
|
warn('이 환경에서는 번들 yt-dlp 가 실행되지 않습니다 (libc 없는 슬림/도커 베이스 이미지일 가능성).')
|
||||||
|
warn('PATH 에 yt-dlp 를 직접 설치하면 src/youtube.ts 가 우선 그것을 사용합니다:')
|
||||||
|
warn(' Debian/Ubuntu 기반 이미지: apt-get update && apt-get install -y yt-dlp')
|
||||||
|
warn(' Alpine 기반 이미지: apk add --no-cache yt-dlp')
|
||||||
|
warn(' pip 사용: pip install --break-system-packages yt-dlp')
|
||||||
|
warn('YouTube 가져오기 기능이 필요 없다면 무시하거나 SKIP_YT_DLP=1 로 설정해 받지 않을 수 있습니다.')
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureYtDlp() {
|
async function ensureYtDlp() {
|
||||||
|
if (process.env.SKIP_YT_DLP === '1') {
|
||||||
|
log('SKIP_YT_DLP=1 → yt-dlp 다운로드 생략')
|
||||||
|
return
|
||||||
|
}
|
||||||
const target = ytDlpLocalPath()
|
const target = ytDlpLocalPath()
|
||||||
// 이미 있으면 --version 으로 점검
|
// 이미 있으면 --version 으로 점검
|
||||||
try {
|
try {
|
||||||
@@ -90,7 +103,13 @@ async function ensureYtDlp() {
|
|||||||
const asset = ytDlpAssetName()
|
const asset = ytDlpAssetName()
|
||||||
const url = `https://github.com/yt-dlp/yt-dlp/releases/latest/download/${asset}`
|
const url = `https://github.com/yt-dlp/yt-dlp/releases/latest/download/${asset}`
|
||||||
log(`yt-dlp 다운로드 시작: ${url}`)
|
log(`yt-dlp 다운로드 시작: ${url}`)
|
||||||
await downloadFile(url, target)
|
try {
|
||||||
|
await downloadFile(url, target)
|
||||||
|
} catch (err) {
|
||||||
|
warn(`yt-dlp 다운로드 실패: ${err.message || err}`)
|
||||||
|
ytDlpInstallHint()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
await fs.chmod(target, 0o755)
|
await fs.chmod(target, 0o755)
|
||||||
}
|
}
|
||||||
@@ -108,7 +127,12 @@ async function ensureYtDlp() {
|
|||||||
lastErr = r.error || new Error(`exit ${r.status}, stderr: ${String(r.stderr).trim()}`)
|
lastErr = r.error || new Error(`exit ${r.status}, stderr: ${String(r.stderr).trim()}`)
|
||||||
}
|
}
|
||||||
if (!version) {
|
if (!version) {
|
||||||
throw new Error(`yt-dlp 설치는 끝났지만 실행이 실패했습니다 (${target}): ${lastErr?.message || lastErr}`)
|
// 다운로드는 됐는데 실행이 안 되는 환경 (예: glibc 없는 alpine, distroless).
|
||||||
|
// setup 을 통째로 실패시키지 않고 안내만 출력하고 빠진다.
|
||||||
|
warn(`yt-dlp 다운로드는 됐지만 실행 검증에 실패했습니다 (${target}): ${lastErr?.message || lastErr}`)
|
||||||
|
try { await fs.unlink(target) } catch { /* ignore */ }
|
||||||
|
ytDlpInstallHint()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
log(`yt-dlp 설치 완료: ${target} (version ${version})`)
|
log(`yt-dlp 설치 완료: ${target} (version ${version})`)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user