FROM python:3.10-slim # 创建用户(HF Spaces 要求 UID=1000) RUN useradd -m -u 1000 user USER root # 安装系统依赖(Blender + 完整的无头渲染支持) RUN apt-get update && apt-get install -y \ blender \ xvfb \ xorg \ libgl1 \ libglib2.0-0 \ libsm6 \ libxrender1 \ libxext6 \ libxi6 \ libxkbcommon-x11-0 \ libgomp1 \ libxxf86vm1 \ libxfixes3 \ wget \ unzip \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # 下载完整 LDraw 库(官方 + 非官方零件) RUN echo "📦 Downloading complete LDraw library..." && \ # 官方零件库 (~18MB → ~70MB) wget -q --retry-connrefused --waitretry=5 --tries=3 \ https://library.ldraw.org/library/updates/complete.zip \ -O /tmp/ldraw.zip && \ echo "📂 Extracting official LDraw library..." && \ unzip -q /tmp/ldraw.zip -d /home/user/ && \ rm /tmp/ldraw.zip && \ # 非官方零件库 (~12MB → ~50MB) echo "📦 Downloading unofficial LDraw parts..." && \ wget -q --retry-connrefused --waitretry=5 --tries=3 \ https://library.ldraw.org/library/unofficial/ldrawunf.zip \ -O /tmp/ldrawunf.zip && \ mkdir -p /home/user/ldraw/unofficial && \ echo "📂 Extracting unofficial parts..." && \ unzip -q /tmp/ldrawunf.zip -d /home/user/ldraw/unofficial/ && \ rm /tmp/ldrawunf.zip && \ chown -R user:user /home/user/ldraw && \ echo "✅ Complete LDraw library installed" # 验证 LDraw 库完整性(增强版 - 详细输出) RUN test -d /home/user/ldraw/parts && \ test -d /home/user/ldraw/unofficial/parts && \ echo "📊 Official parts: $(find /home/user/ldraw/parts -name '*.dat' | wc -l)" && \ echo "📊 Unofficial parts: $(find /home/user/ldraw/unofficial/parts -name '*.dat' | wc -l)" && \ echo "📦 Sample official parts:" && \ ls /home/user/ldraw/parts/*.dat 2>/dev/null | head -10 | xargs -n 1 basename && \ echo "📦 Sample unofficial parts:" && \ ls /home/user/ldraw/unofficial/parts/*.dat 2>/dev/null | head -10 | xargs -n 1 basename && \ echo "🔍 Checking critical parts from GPT outputs..." && \ for part in 4081.dat 60470.dat 2431.dat 92583.dat 92593.dat 18892.dat 15068.dat 2412.dat 61678.dat 3666.dat; do \ if find /home/user/ldraw -name "$part" | grep -q .; then \ echo " ✓ Found $part"; \ else \ echo " ✗ Missing $part (will check if it's a subpart)"; \ fi; \ done && \ echo "✓ LDraw installation verified" || \ (echo "✗ LDraw installation failed" && exit 1) # 以 root 身份预创建 X11 socket 目录(解决 Xvfb 权限问题) RUN mkdir -p /tmp/.X11-unix && \ chmod 1777 /tmp/.X11-unix && \ chown root:root /tmp/.X11-unix && \ echo "✓ X11 socket directory created" # 切换到普通用户 USER user ENV HOME=/home/user \ PATH=/home/user/.local/bin:$PATH \ LDRAW_LIBRARY_PATH=/home/user/ldraw \ HF_HOME=/home/user/.cache/huggingface \ HF_HUB_CACHE=/home/user/.cache/huggingface/hub \ TRANSFORMERS_CACHE=/home/user/.cache/huggingface/transformers \ HF_HUB_DISABLE_XET=1 \ DISPLAY=:99 \ LIBGL_ALWAYS_SOFTWARE=1 WORKDIR $HOME/app # 复制项目文件 COPY --chown=user . $HOME/app # 验证 ImportLDraw 插件,若不存在则下载 RUN if [ ! -d "$HOME/app/code/ImportLDraw" ] || [ ! -f "$HOME/app/code/ImportLDraw/__init__.py" ]; then \ echo "⚠️ ImportLDraw not found in repo, downloading..."; \ wget -q https://github.com/TobyLobster/ImportLDraw/archive/refs/tags/v1.2.1.zip -O /tmp/importldraw.zip && \ unzip -q /tmp/importldraw.zip -d /tmp/ && \ mv /tmp/ImportLDraw-1.2.1 $HOME/app/code/ImportLDraw && \ rm /tmp/importldraw.zip && \ echo "✅ ImportLDraw downloaded successfully"; \ else \ echo "✅ ImportLDraw found in repo"; \ fi # 验证插件已正确部署 RUN ls -la $HOME/app/code/ImportLDraw/__init__.py || \ (echo "❌ ImportLDraw deployment failed" && exit 1) # 安装 Python 依赖 RUN pip install --no-cache-dir --upgrade pip && \ pip install --no-cache-dir -r requirements.txt # 注意:模型通过 README.md 的 preload_from_hub 自动下载(HF Spaces 官方推荐方式) # 下载到 ~/.cache/huggingface/hub/ 并在运行时通过 HF_HUB_CACHE 环境变量访问 # 创建启动脚本(先启动 Xvfb,再运行应用) RUN echo '#!/bin/bash\n\ echo "🖥️ 启动虚拟显示服务器 Xvfb..."\n\ Xvfb :99 -screen 0 1024x768x24 -ac +extension GLX +extension RENDER +render -noreset -nolisten tcp -nolisten unix &\n\ sleep 2\n\ echo "✅ Xvfb 已启动 (DISPLAY=:99, 无 Unix socket)"\n\ echo "🚀 启动应用..."\n\ exec python code/demo.py' > /home/user/start.sh && \ chmod +x /home/user/start.sh # 暴露端口(Gradio 默认) EXPOSE 7860 # 启动命令(使用启动脚本) CMD ["/home/user/start.sh"]