mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
feat: 新增 Python 3.14t 自由线程镜像 (#6434)
* fix(resource): select free-threaded extension ABI * chore(deps): require moviepilot-rust 0.2.9 * perf: add free-threaded runtime comparison * feat: add free-threaded runtime profile * chore(deps): require moviepilot-rust 0.3.0 * test: isolate system endpoint import graph * fix(docker): preserve runtime profile during recovery * feat(runtime): expose active GIL state * feat(plugin): log GIL fallback attribution * test: refresh runtime observability dependency baseline * fix(plugin): validate the active uv runtime profile * test(runtime): expand free-threaded benchmark evidence * docs(runtime): define v3t governance gates * test(runtime): separate rust benchmark modes * docs: sync free-threaded architecture baseline * perf: add PostgreSQL driver comparison * docs(runtime): record final free-threaded evidence * fix(runtime): converge dual-profile dependency verification * fix(runtime): scope Python 3.14 warning filter * fix(runtime): match actual oss2 syntax warning * docs(runtime): refresh free-threaded benchmark evidence * test(architecture): refresh runtime dependency baseline * ci: skip unused Trivy Java database * build: exclude local verification artifacts * docs(runtime): refresh PostgreSQL driver benchmarks * docs(runtime): record plugin restore acceptance * docs(runtime): record amd64 candidate acceptance * ci: pin beta image publisher action * test(architecture): merge runtime dependency baseline * feat(runtime): expose Python GIL status * docs(runtime): document Python runtime status fields
This commit is contained in:
+74
-11
@@ -1,5 +1,8 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG MOVIEPILOT_PYTHON_VARIANT="standard"
|
||||
ARG MOVIEPILOT_PYTHON_VERSION="3.14.7"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:0.12.5@sha256:e85be844203885286c60ffad8a858d48afb6c5a5c237ca0e67f12e74b8f174b1 AS uv
|
||||
|
||||
|
||||
@@ -9,7 +12,10 @@ FROM rclone/rclone:1.75.0@sha256:b06aed988cf5967de7c25be5925240983981c757f4ed1ac
|
||||
FROM mwader/static-ffmpeg:8.1.1@sha256:735f84b905e00d5c618b667f0b053f83b1096f5fc404c607e6134bf2275a0e0a AS ffmpeg
|
||||
|
||||
|
||||
FROM python:3.14.7-slim-trixie AS base
|
||||
FROM python:${MOVIEPILOT_PYTHON_VERSION}-slim-trixie AS base
|
||||
|
||||
|
||||
FROM rust:slim-trixie AS rust_toolchain
|
||||
|
||||
|
||||
# 准备外部制品所需的最小工具集
|
||||
@@ -33,6 +39,7 @@ ENV LANG="C.UTF-8" \
|
||||
PUID=0 \
|
||||
PGID=0 \
|
||||
UMASK=000 \
|
||||
PYTHON_THREAD_INHERIT_CONTEXT=0 \
|
||||
VENV_PATH="/opt/venv"
|
||||
|
||||
ENV PATH="${VENV_PATH}/bin:${PATH}"
|
||||
@@ -94,8 +101,8 @@ RUN apt-get update \
|
||||
/var/tmp/*
|
||||
|
||||
|
||||
# 准备 python 环境
|
||||
FROM base AS prepare_venv
|
||||
# 准备 Python 环境的公共输入
|
||||
FROM base AS prepare_venv_common
|
||||
|
||||
# 设置环境变量
|
||||
ENV LANG="C.UTF-8" \
|
||||
@@ -112,15 +119,65 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
jq \
|
||||
wget
|
||||
|
||||
# 按锁文件创建主程序虚拟环境
|
||||
WORKDIR /app
|
||||
COPY --from=uv /uv /usr/local/bin/uv
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN python3 -m venv --without-pip ${VENV_PATH} \
|
||||
|
||||
|
||||
# 准备标准 Python 运行环境
|
||||
FROM prepare_venv_common AS prepare_venv_standard
|
||||
|
||||
RUN mkdir -p /opt/python \
|
||||
&& python3 -m venv --without-pip ${VENV_PATH} \
|
||||
&& UV_PROJECT_ENVIRONMENT=${VENV_PATH} uv sync \
|
||||
--locked \
|
||||
--no-dev \
|
||||
--no-install-project
|
||||
--locked \
|
||||
--no-default-groups \
|
||||
--group runtime-standard \
|
||||
--no-install-project
|
||||
|
||||
|
||||
# free-threaded 原生依赖需要 Rust 和 C 构建工具链。
|
||||
FROM prepare_venv_common AS prepare_venv_free-threaded
|
||||
|
||||
ARG MOVIEPILOT_PYTHON_VERSION
|
||||
|
||||
ENV CARGO_HOME="/usr/local/cargo" \
|
||||
RUSTUP_HOME="/usr/local/rustup" \
|
||||
UV_PYTHON_INSTALL_DIR="/opt/python"
|
||||
|
||||
ENV PATH="${CARGO_HOME}/bin:${VENV_PATH}/bin:${PATH}"
|
||||
|
||||
COPY --from=rust_toolchain /usr/local/cargo /usr/local/cargo
|
||||
COPY --from=rust_toolchain /usr/local/rustup /usr/local/rustup
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& uv python install --no-bin "${MOVIEPILOT_PYTHON_VERSION}t" \
|
||||
&& python_bin="$(uv python find --managed-python "${MOVIEPILOT_PYTHON_VERSION}t")" \
|
||||
&& "${python_bin}" -m venv --without-pip ${VENV_PATH} \
|
||||
&& UV_PROJECT_ENVIRONMENT=${VENV_PATH} uv sync \
|
||||
--locked \
|
||||
--no-default-groups \
|
||||
--group runtime-free-threaded \
|
||||
--no-install-project \
|
||||
--python "${python_bin}"
|
||||
|
||||
|
||||
FROM prepare_venv_standard AS verify_venv_standard
|
||||
|
||||
COPY app/doctor/dependencies.py /tmp/moviepilot-runtime-dependencies.py
|
||||
RUN "${VENV_PATH}/bin/python" /tmp/moviepilot-runtime-dependencies.py --full
|
||||
|
||||
|
||||
FROM prepare_venv_free-threaded AS verify_venv_free-threaded
|
||||
|
||||
COPY app/doctor/dependencies.py /tmp/moviepilot-runtime-dependencies.py
|
||||
RUN "${VENV_PATH}/bin/python" /tmp/moviepilot-runtime-dependencies.py --full
|
||||
|
||||
|
||||
ARG MOVIEPILOT_PYTHON_VARIANT
|
||||
FROM verify_venv_${MOVIEPILOT_PYTHON_VARIANT} AS prepare_venv
|
||||
|
||||
# 准备后端源码
|
||||
FROM base AS prepare_backend
|
||||
@@ -184,10 +241,15 @@ FROM prepare_payload AS prepare_resources
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG MOVIEPILOT_RESOURCES_REF="main"
|
||||
ARG MOVIEPILOT_PYTHON_VARIANT="standard"
|
||||
|
||||
RUN set -eu; \
|
||||
test -n "${MOVIEPILOT_RESOURCES_REF}"; \
|
||||
python_ver="$(python3 -c 'import sys; print(f"cpython-{sys.version_info.major}{sys.version_info.minor}")')"; \
|
||||
case "${MOVIEPILOT_PYTHON_VARIANT}" in \
|
||||
standard) python_ver="$(python3 -c 'import sys; print(f"cpython-{sys.version_info.major}{sys.version_info.minor}")')" ;; \
|
||||
free-threaded) python_ver="cpython-314t" ;; \
|
||||
*) printf 'Unsupported Python variant: %s\n' "${MOVIEPILOT_PYTHON_VARIANT}" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
target_arch="${TARGETARCH:-$(uname -m)}"; \
|
||||
case "${target_arch}" in \
|
||||
arm64|aarch64) suffix="aarch64-linux-gnu" ;; \
|
||||
@@ -230,6 +292,7 @@ COPY --from=rclone /usr/local/bin/rclone /usr/bin/rclone
|
||||
|
||||
# python 环境
|
||||
COPY --from=prepare_venv --chmod=777 ${VENV_PATH} ${VENV_PATH}
|
||||
COPY --from=prepare_venv /opt/python /opt/python
|
||||
COPY --from=uv /uv /usr/local/bin/uv
|
||||
|
||||
# 浏览器运行依赖
|
||||
@@ -252,8 +315,8 @@ COPY --from=prepare_control /bundle/bin/moviepilot /usr/local/bin/moviepilot
|
||||
RUN mkdir -p ${HOME} \
|
||||
&& groupadd -r moviepilot -g 918 \
|
||||
&& useradd -r moviepilot -g moviepilot -d ${HOME} -s /bin/bash -u 918 \
|
||||
&& python_ver=$(python3 -V | awk '{print $2}') \
|
||||
&& echo "/app/" > ${VENV_PATH}/lib/python${python_ver%.*}/site-packages/app.pth \
|
||||
&& site_packages="$(${VENV_PATH}/bin/python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')" \
|
||||
&& echo "/app/" > "${site_packages}/app.pth" \
|
||||
&& echo 'fs.inotify.max_user_watches=5242880' >> /etc/sysctl.conf \
|
||||
&& echo 'fs.inotify.max_user_instances=5242880' >> /etc/sysctl.conf \
|
||||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
||||
|
||||
+10
-17
@@ -337,34 +337,27 @@ function diagnostic_keepalive() {
|
||||
# 插件依赖和主程序共用同一套 venv 时,历史安装记录可能已经污染环境,
|
||||
# 这里优先在真正拉起后端前做一次自愈,避免容器反复起不来。
|
||||
function ensure_backend_runtime_dependencies() {
|
||||
local probe_code="import alembic, cloakbrowser, fastapi, pydantic, pydantic_core, pydantic_settings, sqlalchemy, starlette, uvicorn; from pydantic import BaseModel, Field"
|
||||
local probe_module="app.doctor.dependencies"
|
||||
|
||||
INFO "→ 启动前检查后端核心依赖..."
|
||||
if "${VENV_PATH}/bin/python3" -c "${probe_code}" >/dev/null 2>&1; then
|
||||
if "${VENV_PATH}/bin/python3" -m "${probe_module}" >/dev/null 2>&1; then
|
||||
INFO "→ 后端核心依赖检查通过。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
WARN "→ 检测到后端核心依赖异常,开始尝试恢复主程序依赖..."
|
||||
local -a uv_cmd=(
|
||||
"${UV_BIN}" sync
|
||||
--project /app
|
||||
--locked
|
||||
--no-dev
|
||||
--no-install-project
|
||||
--inexact
|
||||
)
|
||||
if [ -n "${PIP_PROXY}" ]; then
|
||||
uv_cmd+=(--default-index "${PIP_PROXY}")
|
||||
if ! configure_package_route; then
|
||||
ERROR "→ 无法选择可用的主程序依赖源,后端无法启动。"
|
||||
diagnostic_keepalive 1
|
||||
fi
|
||||
|
||||
if ! run_package_command env "UV_PROJECT_ENVIRONMENT=${VENV_PATH}" \
|
||||
"${uv_cmd[@]}" > /dev/stdout 2> /dev/stderr; then
|
||||
PACKAGE_ROUTE_READY="true"
|
||||
INFO "依赖源:${PACKAGE_LOG}"
|
||||
if ! sync_project_dependencies_for "/app" > /dev/stdout 2> /dev/stderr; then
|
||||
ERROR "→ 自动恢复主程序依赖失败,后端无法启动。"
|
||||
diagnostic_keepalive 1
|
||||
fi
|
||||
|
||||
if ! "${VENV_PATH}/bin/python3" -c "${probe_code}" >/dev/null 2>&1; then
|
||||
if ! "${VENV_PATH}/bin/python3" -m "${probe_module}" >/dev/null 2>&1; then
|
||||
ERROR "→ 主程序依赖恢复后仍然异常,后端无法启动。"
|
||||
diagnostic_keepalive 1
|
||||
fi
|
||||
@@ -575,8 +568,8 @@ render_nginx_config
|
||||
|
||||
# 自动更新,控制脚本由 launcher 固化到同一代运行目录,源码替换不会改变本轮执行内容。
|
||||
cd /
|
||||
source "${MP_CONTROL_DIR:-/usr/local/lib/moviepilot/control}/update.sh"
|
||||
if [ "${MOVIEPILOT_BOOTSTRAP_UPDATE_DONE:-0}" != "1" ]; then
|
||||
source "${MP_CONTROL_DIR:-/usr/local/lib/moviepilot/control}/update.sh"
|
||||
if ! recover_pending_update; then
|
||||
ERROR "→ 上一次容器更新未能恢复,容器将保持运行以便执行 moviepilot doctor。"
|
||||
diagnostic_keepalive 1
|
||||
|
||||
+13
-1
@@ -131,6 +131,8 @@ function update_pending_state() {
|
||||
|
||||
function sync_project_dependencies_for() {
|
||||
local project_dir="$1"
|
||||
local runtime_group
|
||||
local runtime_selector
|
||||
local -a uv_cmd=(
|
||||
"${UV_BIN}" sync
|
||||
--project "${project_dir}"
|
||||
@@ -140,6 +142,16 @@ function sync_project_dependencies_for() {
|
||||
--no-install-project
|
||||
--python "${VENV_PATH}/bin/python3"
|
||||
)
|
||||
runtime_group=""
|
||||
if grep -Eq '^runtime-(standard|free-threaded)[[:space:]]*=' "${project_dir}/pyproject.toml"; then
|
||||
runtime_selector="${project_dir}/app/runtime/dependencies.py"
|
||||
[ -f "${runtime_selector}" ] || return 1
|
||||
runtime_group="$("${VENV_PATH}/bin/python3" "${runtime_selector}")" || return 1
|
||||
[ -n "${runtime_group}" ] || return 1
|
||||
fi
|
||||
if [ -n "${runtime_group}" ]; then
|
||||
uv_cmd+=(--no-default-groups --group "${runtime_group}")
|
||||
fi
|
||||
uv_cmd+=("${UV_OPTIONS[@]}")
|
||||
env "${PACKAGE_ENV[@]}" \
|
||||
"UV_PROJECT_ENVIRONMENT=${VENV_PATH}" \
|
||||
@@ -304,7 +316,7 @@ function stage_runtime_payload() {
|
||||
cp -a "${resource_file}" "${stage_resource_dir}/" || return 1
|
||||
done
|
||||
|
||||
python_version="$(python3 -c 'import sys; print(f"cpython-{sys.version_info.major}{sys.version_info.minor}")')" || return 1
|
||||
python_version="$("${VENV_PATH}/bin/python3" -c 'import sys, sysconfig; print(f"cpython-{sys.version_info.major}{sys.version_info.minor}{"t" if sysconfig.get_config_var("Py_GIL_DISABLED") == 1 else ""}")')" || return 1
|
||||
arch="$(uname -m)"
|
||||
if [ "${arch}" = "aarch64" ]; then
|
||||
arch_suffix="aarch64-linux-gnu"
|
||||
|
||||
Reference in New Issue
Block a user