# ---- Stage 0: QEMU .so + ROM binaries ---- # Two modes: # 1. Local: place files in prebuilt/qemu/ (from build-qemu.sh or manual copy) # 2. CI/CD: downloads from GitHub Release (requires qemu-lcgamboa repo to be public) # The COPY always runs; the RUN only downloads missing files. FROM ubuntu:22.04 AS qemu-provider RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \ && rm -rf /var/lib/apt/lists/* ARG QEMU_RELEASE_URL=https://github.com/davidmonterocrespo24/velxio/releases/download/qemu-prebuilt # Copy the prebuilt directory (may contain .so+ROM files or just the .gitkeep) RUN mkdir -p /qemu COPY prebuilt/qemu/ /qemu/ # Download any missing files from GitHub Release RUN cd /qemu \ && for f in libqemu-xtensa.so libqemu-riscv32.so esp32-v3-rom.bin esp32-v3-rom-app.bin esp32c3-rom.bin; do \ if [ ! -f "$f" ]; then \ echo "Downloading $f from release..." ; \ curl -fSL -o "$f" "${QEMU_RELEASE_URL}/$f" ; \ else \ echo "Using local $f ($(stat -c%s "$f") bytes)" ; \ fi ; \ done \ && ls -lh /qemu/ # ---- Stage 0.5: ESP-IDF toolchain for ESP32 compilation ---- FROM ubuntu:22.04 AS espidf-builder RUN apt-get update && apt-get install -y --no-install-recommends \ git wget flex bison gperf python3 python3-pip python3-venv \ cmake ninja-build ccache libffi-dev libssl-dev \ libusb-1.0-0 ca-certificates \ && rm -rf /var/lib/apt/lists/* # Install ESP-IDF 4.4.7 (matches Arduino ESP32 core 2.0.17 / lcgamboa QEMU ROM) RUN git clone -b v4.4.7 --recursive --depth=1 --shallow-submodules \ https://github.com/espressif/esp-idf.git /opt/esp-idf WORKDIR /opt/esp-idf # Install toolchains for esp32 (Xtensa) and esp32c3 (RISC-V) only RUN ./install.sh esp32,esp32c3 # Clean up large unnecessary files to reduce image size RUN rm -rf .git docs examples \ && find /root/.espressif -name '*.tar.*' -delete 2>/dev/null || true # Install Arduino-as-component for full Arduino API support in ESP-IDF builds RUN git clone --branch 2.0.17 --depth=1 --recursive --shallow-submodules \ https://github.com/espressif/arduino-esp32.git /opt/arduino-esp32 \ && rm -rf /opt/arduino-esp32/.git # ---- Stage 1: Build frontend and wokwi-libs ---- FROM node:20 AS frontend-builder WORKDIR /app # Clone wokwi-libs fresh from upstream to avoid stale submodule pointers. # git is pre-installed in the node:20 Debian image. RUN git clone --depth=1 https://github.com/wokwi/avr8js.git wokwi-libs/avr8js \ && git clone --depth=1 https://github.com/wokwi/rp2040js.git wokwi-libs/rp2040js \ && git clone --depth=1 https://github.com/wokwi/wokwi-elements.git wokwi-libs/wokwi-elements \ && git clone --depth=1 https://github.com/wokwi/wokwi-boards.git wokwi-libs/wokwi-boards # Build avr8js WORKDIR /app/wokwi-libs/avr8js RUN npm install && npm run build --if-present # Build rp2040js (may have no build script on some commits) WORKDIR /app/wokwi-libs/rp2040js RUN npm install && npm run build --if-present # Build wokwi-elements WORKDIR /app/wokwi-libs/wokwi-elements RUN npm install && npm run build --if-present # Build frontend # components-metadata.json is already committed; skip generate:metadata # (it requires wokwi-elements/src which isn't needed at runtime) WORKDIR /app COPY frontend/ frontend/ WORKDIR /app/frontend RUN npm install && npm run build:docker # ---- Stage 2: Final Production Image ---- FROM python:3.12-slim # Install system dependencies, nginx, and QEMU .so runtime libraries RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ nginx \ libglib2.0-0 \ libgcrypt20 \ libslirp0 \ libpixman-1-0 \ libfdt1 \ cmake \ ninja-build \ libusb-1.0-0 \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir packaging # Install arduino-cli into /usr/local/bin directly (avoids touching /bin) RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \ | BINDIR=/usr/local/bin sh # Only install arduino-cli binary here. Core installation (arduino:avr, # rp2040:rp2040) is done at first boot by entrypoint.sh and persisted # in the mounted /root/.arduino15 volume. # ESP32 compilation uses ESP-IDF instead of arduino-cli. WORKDIR /app # Data directory for persistent SQLite database (mounted as a volume at runtime) RUN mkdir -p /app/data # Install Python backend dependencies COPY backend/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy backend application code COPY backend/app/ ./app/ # Setup Nginx configuration COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf # Copy built frontend assets from builder stage COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html # Copy and configure entrypoint script COPY deploy/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh # ── ESP32 emulation: pre-built QEMU .so + ROM binaries ────────────────────── # Downloaded from GitHub Release (public — no access to qemu-lcgamboa needed) # libqemu-xtensa.so → ESP32 / ESP32-S3 (Xtensa LX6/LX7) # libqemu-riscv32.so → ESP32-C3 (RISC-V RV32IMC) # esp32-v3-rom*.bin → boot/app ROM images required by esp32-picsimlab machine # esp32c3-rom.bin → ROM image required by esp32c3-picsimlab machine # NOTE: ROM files must live in the same directory as the .so (worker passes -L # to QEMU pointing at os.path.dirname(lib_path)) RUN mkdir -p /app/lib COPY --from=qemu-provider /qemu/ /app/lib/ # Activate ESP32 emulation # QEMU_ESP32_LIB → Xtensa library (ESP32, ESP32-S3) # QEMU_RISCV32_LIB → RISC-V library (ESP32-C3 and variants) ENV QEMU_ESP32_LIB=/app/lib/libqemu-xtensa.so ENV QEMU_RISCV32_LIB=/app/lib/libqemu-riscv32.so # ── ESP-IDF toolchain for ESP32 compilation ────────────────────────────────── # Copied from espidf-builder stage: IDF framework + cross-compiler toolchains COPY --from=espidf-builder /opt/esp-idf /opt/esp-idf COPY --from=espidf-builder /root/.espressif /root/.espressif COPY --from=espidf-builder /opt/arduino-esp32 /opt/arduino-esp32 ENV IDF_PATH=/opt/esp-idf ENV IDF_TOOLS_PATH=/root/.espressif ENV ARDUINO_ESP32_PATH=/opt/arduino-esp32 # Install ESP-IDF Python dependencies using the final image's Python # The requirements.txt has version constraints required by ESP-IDF 4.4.x RUN grep -v 'esp-windows-curses' /opt/esp-idf/requirements.txt \ | pip install --no-cache-dir -r /dev/stdin EXPOSE 80 CMD ["/app/entrypoint.sh"]