Spaces:
Sleeping
Sleeping
| # The Space image: the Gradio app plus a headless Bambu Studio, so the | |
| # "Slice with Bambu Studio" box actually produces a printer-ready .gcode.3mf | |
| # instead of falling back to an unsliced plate. | |
| # | |
| # Build context is the assembled Space payload (app.py, chess2d/, | |
| # requirements.txt) -- see scripts/deploy_space.py. To build it locally: | |
| # | |
| # python scripts/deploy_space.py --space-id local/build --dry-run | |
| # docker build -t chess2d build/space | |
| # docker run --rm -p 7860:7860 chess2d | |
| # | |
| # Ubuntu 24.04 on purpose: Bambu Studio publishes its Linux build as an | |
| # ubuntu-24.04 AppImage, and an older base (python:*-slim is Debian 12) has too | |
| # old a glibc to run it. | |
| FROM ubuntu:24.04 | |
| ENV DEBIAN_FRONTEND=noninteractive | |
| # Two dependency sets in one layer: Python for the app, and the GTK/GL/media | |
| # libraries the AppImage links against. xvfb is not optional -- Bambu Studio is | |
| # a GUI program whose command line still wants a display to start on. | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| ca-certificates \ | |
| curl \ | |
| python3 \ | |
| python3-venv \ | |
| xvfb \ | |
| libgtk-3-0 \ | |
| libwebkit2gtk-4.1-0 \ | |
| libsoup-3.0-0 \ | |
| libgl1 \ | |
| libglu1-mesa \ | |
| libegl1 \ | |
| libgstreamer1.0-0 \ | |
| libgstreamer-plugins-base1.0-0 \ | |
| libsecret-1-0 \ | |
| libnotify4 \ | |
| libdbus-1-3 \ | |
| libcurl4 \ | |
| libssl3 \ | |
| libx11-6 \ | |
| libxrandr2 \ | |
| libxinerama1 \ | |
| libxcursor1 \ | |
| libxi6 \ | |
| libfontconfig1 \ | |
| libfreetype6 \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Bambu Studio, extracted rather than mounted: --appimage-extract unpacks | |
| # without FUSE, which containers do not have. | |
| # | |
| # Left empty, the release feed is queried for the newest Ubuntu AppImage. That | |
| # query is an unauthenticated GitHub API call (60/hour per IP) and it makes the | |
| # image non-reproducible, so pin an asset for anything you care about: | |
| # | |
| # docker build --build-arg BAMBU_STUDIO_URL=https://.../Bambu_Studio_ubuntu-24.04_vX.AppImage . | |
| # | |
| # The last step records where the release put its profile tree -- it has moved | |
| # between versions -- so $BAMBU_PROFILES below is a fact, not a guess. | |
| ARG BAMBU_STUDIO_URL="" | |
| RUN set -eux; \ | |
| url="${BAMBU_STUDIO_URL}"; \ | |
| if [ -z "$url" ]; then \ | |
| url="$(curl -fsSL https://api.github.com/repos/bambulab/BambuStudio/releases/latest \ | |
| | grep -o 'https://[^\"]*ubuntu[^\"]*\.AppImage' | head -n 1)"; \ | |
| fi; \ | |
| test -n "$url" || { echo "could not resolve a Bambu Studio AppImage URL"; exit 1; }; \ | |
| echo "Installing Bambu Studio from $url"; \ | |
| curl -fL --retry 3 -o /tmp/BambuStudio.AppImage "$url"; \ | |
| chmod +x /tmp/BambuStudio.AppImage; \ | |
| cd /opt && /tmp/BambuStudio.AppImage --appimage-extract >/dev/null; \ | |
| mv /opt/squashfs-root /opt/bambustudio; \ | |
| rm /tmp/BambuStudio.AppImage; \ | |
| profiles="$(find /opt/bambustudio -type d -name profiles -print -quit)"; \ | |
| test -n "$profiles" || { echo "no profiles/ directory in the AppImage"; exit 1; }; \ | |
| ln -sfn "$profiles" /opt/bambustudio-profiles; \ | |
| echo "profiles: $profiles" | |
| # A wrapper so the app finds one stable path: it supplies the virtual display | |
| # the GUI binary insists on, and -a picks a free server number per call. | |
| RUN printf '%s\n' \ | |
| '#!/bin/sh' \ | |
| 'exec xvfb-run -a --server-args="-screen 0 1280x1024x24" /opt/bambustudio/AppRun "$@"' \ | |
| > /usr/local/bin/bambu-studio \ | |
| && chmod +x /usr/local/bin/bambu-studio | |
| # chess2d's own discovery hooks (see src/chess2d/bambu.py): without these it | |
| # would search $PATH and the usual install locations and find nothing usable. | |
| ENV BAMBU_STUDIO=/usr/local/bin/bambu-studio \ | |
| BAMBU_PROFILES=/opt/bambustudio-profiles | |
| # A smoke test with the build log as its output: if the AppImage cannot start at | |
| # all (a missing library, the wrong glibc) this prints the failure where a human | |
| # will see it. The exit code is not trusted -- Bambu Studio's CLI has returned | |
| # non-zero for --help across releases -- so it does not gate the build; the | |
| # `image` CI job slices a real plate for that. | |
| RUN set -eux; \ | |
| test -x /opt/bambustudio/AppRun; \ | |
| bambu-studio --help > /tmp/bambu-help.txt 2>&1 || true; \ | |
| head -n 20 /tmp/bambu-help.txt; \ | |
| rm -f /tmp/bambu-help.txt | |
| # Hugging Face Spaces run the container as UID 1000; matching it here keeps the | |
| # app's temp files and Bambu Studio's config directory writable. Ubuntu 24.04 | |
| # ships its own UID 1000 (`ubuntu`), which has to go first or useradd fails. | |
| RUN userdel -r ubuntu 2>/dev/null || true; \ | |
| useradd -m -u 1000 user | |
| # LANG matters: a bare container locale is POSIX, which makes Python's default | |
| # text encoding ASCII, and the app reads UTF-8 SVGs. PYTHONUTF8 covers anything | |
| # that reaches for the locale anyway. | |
| ENV HOME=/home/user \ | |
| PATH=/opt/venv/bin:/usr/local/bin:/usr/bin:/bin \ | |
| PYTHONUNBUFFERED=1 \ | |
| PYTHONUTF8=1 \ | |
| LANG=C.UTF-8 \ | |
| LC_ALL=C.UTF-8 \ | |
| GRADIO_SERVER_NAME=0.0.0.0 \ | |
| GRADIO_SERVER_PORT=7860 | |
| # Ubuntu's Python is externally managed, so the app gets its own venv. | |
| RUN python3 -m venv /opt/venv && chown -R user:user /opt/venv | |
| USER user | |
| WORKDIR /home/user/app | |
| COPY --chown=user requirements.txt ./ | |
| RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt | |
| COPY --chown=user . . | |
| EXPOSE 7860 | |
| # Liveness only. A missing slicer deliberately does *not* fail this: the app | |
| # still serves generic 3MFs, and restarting the container would not conjure | |
| # Bambu Studio back. The build log and the `image` CI job cover that instead. | |
| HEALTHCHECK --interval=60s --timeout=15s --start-period=60s --retries=3 \ | |
| CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:7860/', timeout=10)" | |
| CMD ["python", "app.py"] | |