# ppy: the single shared image every DevPlace container instance runs. # Build once with `make ppy`. Context is devplacepy/services/containers/files. FROM python:3.13-slim-bookworm AS base ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ PIP_DEFAULT_TIMEOUT=120 \ PIP_RETRIES=10 \ DEBIAN_FRONTEND=noninteractive \ PLAYWRIGHT_BROWSERS_PATH=/opt/playwright RUN apt-get update && apt-get install -y --no-install-recommends \ git curl wget vim ack ca-certificates build-essential libpq-dev \ tmux apache2-utils procps htop iftop iotop netcat-openbsd zip unzip \ fakeroot xz-utils pkg-config \ binutils gnupg2 libc6-dev libcurl4-openssl-dev libedit2 libedit-dev \ libncurses-dev libpython3-dev libsqlite3-0 libsqlite3-dev uuid-dev \ libxml2-dev libz3-dev tzdata zlib1g-dev \ && rm -rf /var/lib/apt/lists/* ENV RUSTUP_HOME=/opt/rust/rustup \ CARGO_HOME=/opt/rust/cargo \ CHOOSENIM_HOME=/opt/nim/choosenim \ CHOOSENIM_DIR=/opt/nim/toolchains \ NIMBLE_DIR=/opt/nim/nimble \ SWIFTLY_HOME_DIR=/opt/swift/swiftly \ SWIFTLY_BIN_DIR=/opt/swift/bin \ SWIFT_HOME=/opt/swift/toolchain RUN curl -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path --profile minimal \ && rm -rf "$CARGO_HOME/registry" "$CARGO_HOME/git" \ && "$CARGO_HOME/bin/rustc" --version RUN curl -fsSL https://nim-lang.org/choosenim/init.sh | sh -s -- -y || true; \ rm -rf "$CHOOSENIM_HOME/downloads"; \ "$NIMBLE_DIR/bin/nim" --version | head -1 RUN set -eu; \ curl -fsSL "https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz" -o /tmp/swiftly.tar.gz; \ mkdir -p /tmp/swiftly-unpack; \ tar -xzf /tmp/swiftly.tar.gz -C /tmp/swiftly-unpack; \ /tmp/swiftly-unpack/swiftly init --assume-yes --skip-install --no-modify-profile; \ "$SWIFTLY_BIN_DIR/swiftly" install latest --assume-yes; \ toolchain="$(find /root/.local/share/swiftly/toolchains -mindepth 1 -maxdepth 1 -type d | head -1)"; \ [ -n "$toolchain" ] || { echo "swift toolchain not found after install"; exit 1; }; \ mv "$toolchain" /opt/swift/toolchain; \ rm -rf /tmp/swiftly.tar.gz /tmp/swiftly-unpack /root/.local/share/swiftly \ "$SWIFTLY_HOME_DIR" "$SWIFTLY_BIN_DIR"; \ /opt/swift/toolchain/usr/bin/swift --version FROM base AS deps RUN pip install \ pip setuptools wheel packaging \ requests urllib3 certifi idna charset-normalizer httpx aiohttp \ pydantic typing-extensions python-dotenv aiofiles \ python-dateutil six pytz \ pyyaml toml \ loguru structlog tenacity rich tqdm click typer RUN pip install \ numpy pandas matplotlib plotly \ sqlalchemy psycopg2-binary redis dataset RUN pip install \ flask django fastapi starlette "uvicorn[standard]" gunicorn \ h2 celery apscheduler watchfiles \ websocket-client websockets \ beautifulsoup4 lxml scrapy RUN pip install \ cryptography pycryptodome pillow \ pytest ruff black isort flake8 mypy \ sphinx mkdocs poetry pipenv hatch \ reflex RUN pip install playwright && playwright install --with-deps chromium FROM deps AS runtime ARG CODE_SERVER_VERSION=4.131.0 RUN set -eu; \ case "$(dpkg --print-architecture)" in \ amd64) arch=amd64 ;; \ arm64) arch=arm64 ;; \ *) echo "unsupported architecture: $(dpkg --print-architecture)"; exit 1 ;; \ esac; \ curl -fsSL -o /tmp/code-server.tar.gz \ "https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-${arch}.tar.gz"; \ mkdir -p /usr/local/lib/code-server; \ tar -xzf /tmp/code-server.tar.gz -C /usr/local/lib/code-server --strip-components=1; \ rm -f /tmp/code-server.tar.gz; \ ln -sf /usr/local/lib/code-server/bin/code-server /usr/local/bin/code-server COPY sudo /usr/local/bin/sudo COPY aptroot /usr/local/bin/aptroot COPY pagent /usr/bin/pagent.py COPY bot.py /usr/bin/botje.py COPY d.py /usr/bin/d.py COPY dpc /usr/bin/dpc COPY .vimrc /home/pravda/.vimrc RUN set -eu; \ chmod 0755 /usr/local/bin/sudo /usr/local/bin/aptroot /usr/bin/pagent.py /usr/bin/botje.py /usr/bin/d.py /usr/bin/dpc; \ ln -sf /usr/local/bin/sudo /usr/bin/sudo || true; \ for t in apt apt-get dpkg; do ln -sf /usr/local/bin/aptroot "/usr/local/bin/$t"; done; \ if getent passwd 1000 >/dev/null 2>&1; then \ existing="$(getent passwd 1000 | cut -d: -f1)"; \ if [ "$existing" != "pravda" ]; then userdel "$existing" 2>/dev/null || deluser "$existing" 2>/dev/null || true; fi; \ fi; \ if ! getent group pravda >/dev/null 2>&1; then groupadd --gid 1000 pravda 2>/dev/null || addgroup -g 1000 pravda 2>/dev/null || true; fi; \ if ! id pravda >/dev/null 2>&1; then \ useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash pravda 2>/dev/null \ || adduser -u 1000 -G pravda -D -s /bin/sh pravda 2>/dev/null || true; \ fi; \ mkdir -p /app /home/pravda/.local/bin; \ for d in /app /home/pravda /usr/local/lib /usr/local/bin /usr/local/share /usr/lib/python3 /opt \ /usr/lib /usr/bin /usr/sbin /usr/share /usr/include /etc /var/lib /var/cache /var/log /srv; do \ [ -e "$d" ] && chown -R pravda:pravda "$d" 2>/dev/null || true; \ done USER pravda ENV PATH=/home/pravda/.local/bin:/opt/rust/cargo/bin:/opt/nim/nimble/bin:/opt/swift/toolchain/usr/bin:$PATH \ DEVPLACE_TOOLCHAINS=python,rust,nim,swift WORKDIR /app RUN printf '%s\n' \ 'export RUSTUP_HOME=/opt/rust/rustup' \ 'export CARGO_HOME=/opt/rust/cargo' \ 'export NIMBLE_DIR=/opt/nim/nimble' \ 'export SWIFT_HOME=/opt/swift/toolchain' \ 'export DEVPLACE_TOOLCHAINS=python,rust,nim,swift' \ 'export PATH=/home/pravda/.local/bin:/opt/rust/cargo/bin:/opt/nim/nimble/bin:/opt/swift/toolchain/usr/bin:$PATH' \ > /etc/profile.d/devplace-toolchains.sh \ && chmod 0644 /etc/profile.d/devplace-toolchains.sh RUN set -eu; \ for tool in "python --version" "rustc --version" "cargo --version" \ "nim --version" "nimble --version" "swift --version" \ "code-server --version"; do \ $tool > /tmp/toolcheck 2>&1 || { echo "TOOLCHAIN FAILED: $tool"; cat /tmp/toolcheck; exit 1; }; \ head -1 /tmp/toolcheck; \ done; \ rm -f /tmp/toolcheck; \ for b in /usr/local/bin/sudo /usr/local/bin/aptroot /usr/bin/pagent.py \ /usr/bin/botje.py /usr/bin/d.py /usr/bin/dpc /usr/local/bin/code-server; do \ [ -x "$b" ] || { echo "missing or not executable: $b"; exit 1; }; \ done; \ [ -f /home/pravda/.vimrc ] || { echo "missing /home/pravda/.vimrc"; exit 1; } CMD ["sleep", "infinity"]