# crafting-table v0.1 — polyglot dev/build/audit container
#
# Step 1 of 10: monolith image with every toolchain in the spec.
# Spec: Sulkta-Coop/openclaw-workspace/memory/spec-crafting-table.md
#
# Toolchain version pins (bump these to upgrade):
#   NODE_VERSION    22.11.0 (LTS)
#   GO_VERSION      1.22.10
#   RUST_CHANNEL    stable
#   BUN_VERSION     latest (rolling — pinned at install only)
#   DOTNET_VERSION  8.0
#   SWIFT_VERSION   5.9.2
#   KOTLIN_VERSION  1.9.25
#   GRADLE_VERSION  8.10
#   JDK             17 (default) + 21 (alongside, via JAVA_HOME_21)
#
# Image runs as non-root user `crafter` (uid 1000) with passwordless sudo.
# Persistent caches mounted at /caches/{cargo,maven,gradle,npm,pip}.
# Workspace at /workspace. Per-job state at /data.

FROM debian:bookworm-slim

ENV DEBIAN_FRONTEND=noninteractive \
    LANG=C.UTF-8 \
    LC_ALL=C.UTF-8

# ---------- Toolchain version pins ----------
ENV NODE_VERSION=22.11.0 \
    GO_VERSION=1.22.10 \
    DOTNET_CHANNEL=8.0 \
    SWIFT_VERSION=5.9.2 \
    SWIFT_PLATFORM=ubuntu22.04 \
    KOTLIN_VERSION=1.9.25 \
    GRADLE_VERSION=8.10

# ============================================================
# 1. System base — apt packages
# ============================================================
RUN apt-get update && apt-get install -y --no-install-recommends \
        curl wget git ca-certificates gnupg lsb-release apt-transport-https \
        build-essential pkg-config make cmake ninja-build \
        jq ripgrep fd-find \
        valgrind clang lld llvm \
        python3 python3-pip python3-venv python3-dev \
        php-cli php-curl php-mbstring php-xml php-zip composer \
        ruby ruby-dev ruby-bundler \
        bash shellcheck bats \
        openjdk-17-jdk-headless \
        sudo unzip xz-utils zstd \
        libcurl4 libxml2 libedit2 libsqlite3-0 libpython3-dev \
        libncurses6 libtinfo6 libgcc-s1 libstdc++6 \
        libssl-dev libffi-dev \
        zlib1g-dev liblzma-dev libbz2-dev \
        locales tzdata \
    && ln -sf /usr/bin/fdfind /usr/local/bin/fd \
    && rm -rf /var/lib/apt/lists/*

# ============================================================
# 2. Node 22 LTS via NodeSource
# ============================================================
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
    && apt-get install -y --no-install-recommends nodejs \
    && rm -rf /var/lib/apt/lists/* \
    && npm install -g pnpm tsx eslint typescript

# ============================================================
# 3. Go (download from go.dev tarball)
# ============================================================
RUN ARCH="$(dpkg --print-architecture)" \
    && case "$ARCH" in \
         amd64) GOARCH=amd64 ;; \
         arm64) GOARCH=arm64 ;; \
         *) echo "Unsupported arch $ARCH" && exit 1 ;; \
       esac \
    && curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz" -o /tmp/go.tgz \
    && tar -C /usr/local -xzf /tmp/go.tgz \
    && rm /tmp/go.tgz
ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH \
    GOPATH=/root/go

# ============================================================
# 4. Microsoft .NET 8 SDK (Microsoft apt repo for bookworm/12)
# ============================================================
RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/microsoft.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/microsoft.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends dotnet-sdk-8.0 \
    && rm -rf /var/lib/apt/lists/*
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
    DOTNET_NOLOGO=1

# ============================================================
# Switch to bash for the bash-ism layers below (${var//pattern} etc).
# Layers above (apt base / Node / Go / .NET) only use POSIX so they cached fine
# under dash; SHELL is set here to invalidate the cache for layer 6+ only.
SHELL ["/bin/bash", "-c"]

# 5. Swift (Ubuntu 22.04 tarball — works on Debian bookworm
#    because bookworm ships the right libicu/libstdc++ baseline)
# ============================================================
RUN ARCH="$(dpkg --print-architecture)" \
    && SWIFT_PLAT="${SWIFT_PLATFORM}" \
    && case "$ARCH" in \
         amd64) SWIFT_TARBALL_NAME="swift-${SWIFT_VERSION}-RELEASE-${SWIFT_PLAT}" ;; \
         arm64) SWIFT_TARBALL_NAME="swift-${SWIFT_VERSION}-RELEASE-${SWIFT_PLAT}-aarch64" ;; \
         *) echo "Unsupported arch $ARCH for Swift" && exit 1 ;; \
       esac \
    && apt-get update \
    && apt-get install -y --no-install-recommends libpython3-dev libxml2-dev \
       libcurl4-openssl-dev libedit-dev libsqlite3-dev libtinfo-dev libncurses-dev \
    && rm -rf /var/lib/apt/lists/* \
    && curl -fsSL "https://download.swift.org/swift-${SWIFT_VERSION}-release/${SWIFT_PLAT//./}/swift-${SWIFT_VERSION}-RELEASE/${SWIFT_TARBALL_NAME}.tar.gz" -o /tmp/swift.tgz \
    && mkdir -p /opt/swift \
    && tar -xzf /tmp/swift.tgz -C /opt/swift --strip-components=1 \
    && rm /tmp/swift.tgz
ENV PATH=/opt/swift/usr/bin:$PATH

# ============================================================
# 6. Kotlin compiler (direct download from GitHub release)
# ============================================================
RUN curl -fsSL "https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip" -o /tmp/kotlin.zip \
    && unzip -q /tmp/kotlin.zip -d /opt \
    && mv /opt/kotlinc /opt/kotlin \
    && rm /tmp/kotlin.zip
ENV PATH=/opt/kotlin/bin:$PATH

# ============================================================
# 7. JDK 21 alongside JDK 17 (Eclipse Temurin via apt)
# ============================================================
RUN mkdir -p /etc/apt/keyrings \
    && curl -fsSL https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /etc/apt/keyrings/adoptium.gpg \
    && echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb bookworm main" > /etc/apt/sources.list.d/adoptium.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends temurin-21-jdk \
    && rm -rf /var/lib/apt/lists/*
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
    JAVA_HOME_17=/usr/lib/jvm/java-17-openjdk-amd64 \
    JAVA_HOME_21=/usr/lib/jvm/temurin-21-jdk-amd64
RUN update-alternatives --set java  ${JAVA_HOME_17}/bin/java  || true \
    && update-alternatives --set javac ${JAVA_HOME_17}/bin/javac || true

# ============================================================
# 8. Maven (apt) + Gradle (direct download — apt's gradle is ancient)
# ============================================================
RUN apt-get update \
    && apt-get install -y --no-install-recommends maven \
    && rm -rf /var/lib/apt/lists/* \
    && curl -fsSL "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" -o /tmp/gradle.zip \
    && unzip -q /tmp/gradle.zip -d /opt \
    && mv /opt/gradle-${GRADLE_VERSION} /opt/gradle \
    && rm /tmp/gradle.zip
ENV PATH=/opt/gradle/bin:$PATH

# ============================================================
# 9. GitHub CLI (gh)
# ============================================================
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
    && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
    && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends gh \
    && rm -rf /var/lib/apt/lists/*

# ============================================================
# 10. yq (Mike Farah, Go binary)
# ============================================================
RUN ARCH="$(dpkg --print-architecture)" \
    && curl -fsSL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${ARCH}" -o /usr/local/bin/yq \
    && chmod +x /usr/local/bin/yq

# ============================================================
# 11. shfmt (Go binary)
# ============================================================
RUN ARCH="$(dpkg --print-architecture)" \
    && curl -fsSL "https://github.com/mvdan/sh/releases/download/v3.10.0/shfmt_v3.10.0_linux_${ARCH}" -o /usr/local/bin/shfmt \
    && chmod +x /usr/local/bin/shfmt

# ============================================================
# 12. Create non-root user `crafter` (uid 1000) with passwordless sudo
#     and prepare workspace / cache / data dirs
# ============================================================
RUN useradd -m -u 1000 -s /bin/bash crafter \
    && echo "crafter ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/crafter \
    && chmod 0440 /etc/sudoers.d/crafter \
    && mkdir -p /workspace /caches/cargo /caches/maven /caches/gradle /caches/npm /caches/pip /caches/bun /data \
    && chown -R crafter:crafter /workspace /caches /data

# ============================================================
# 13. Switch to crafter for user-scoped installs
# ============================================================
USER crafter
WORKDIR /home/crafter

# Cache env (point tools at the persisted cache dirs)
ENV CARGO_HOME=/caches/cargo \
    RUSTUP_HOME=/home/crafter/.rustup \
    GRADLE_USER_HOME=/caches/gradle \
    MAVEN_OPTS="-Dmaven.repo.local=/caches/maven" \
    NPM_CONFIG_CACHE=/caches/npm \
    PIP_CACHE_DIR=/caches/pip \
    BUN_INSTALL=/home/crafter/.bun \
    PIPX_HOME=/home/crafter/.local/pipx \
    PIPX_BIN_DIR=/home/crafter/.local/bin

ENV PATH=/home/crafter/.local/bin:/caches/cargo/bin:/home/crafter/.bun/bin:$PATH

# ============================================================
# 14. Rust (rustup, stable) + cargo-audit + cargo-deny
# ============================================================
RUN curl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal --no-modify-path \
    && /caches/cargo/bin/rustup component add clippy rustfmt
# Install cargo-audit + cargo-deny to /usr/local (root-owned, NOT volume-shadowed
# at runtime). The /caches/cargo/bin/ dir IS volume-shadowed by the host bind
# mount, so cargo install artifacts there disappear inside the live container.
USER root
RUN /home/crafter/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo install \
        --locked --root /usr/local cargo-deny \
    && chmod 755 /usr/local/bin/cargo-deny
# cargo-audit was historically here but its `git2` C-binding dep needs
# libgit2-sys which fails in this base. cargo-deny supersedes it for our
# use: `cargo deny check advisories` does the same RustSec-DB scan that
# cargo-audit does. The rust audit recipe uses cargo-deny accordingly.
USER crafter

# ============================================================
# 15. Bun (curl install)
# ============================================================
RUN curl -fsSL https://bun.sh/install | bash

# ============================================================
# 16. Python user tooling: pipx-managed CLI tools
# ============================================================
RUN python3 -m pip install --user --break-system-packages --no-cache-dir pipx \
    && python3 -m pipx ensurepath \
    && pipx install uv \
    && pipx install ruff \
    && pipx install mypy \
    && pipx install pytest \
    && pipx install pip-audit \
    && pipx install semgrep \
    # mypy needs the third-party stub packages injected into its own pipx
    # venv (mypy-isolated, not the system site-packages). Without these,
    # `mypy --strict` against any project that imports requests/PyYAML/etc.
    # fails with "Library stubs not installed for X" exit 1.
    && pipx inject mypy types-requests types-PyYAML types-setuptools

# ============================================================
# Reset GOPATH to crafter-owned path BEFORE the go install runs as crafter.
# (The /root/go default set in the root-user ENV block fails permission-wise here.)
ENV PATH=/home/crafter/go/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin \
    GOPATH=/home/crafter/go

# 17. Go user tooling: govulncheck + staticcheck
# ============================================================
RUN for i in 1 2 3 4 5; do \
      go install golang.org/x/vuln/cmd/govulncheck@latest \
      && go install honnef.co/go/tools/cmd/staticcheck@latest \
      && break || { echo "go install attempt $i failed, sleeping $((i*10))s"; sleep $((i*10)); }; \
    done; \
    command -v govulncheck && command -v staticcheck || { echo "go install failed after 5 attempts"; exit 1; }

# Make GOPATH bin discoverable for the crafter user
ENV PATH=/home/crafter/go/bin:$PATH \
    GOPATH=/home/crafter/go

# ============================================================
# 18. Ruby user tooling: bundler-audit, rubocop
# ============================================================
RUN gem install --user-install --no-document bundler-audit rubocop || \
    sudo gem install --no-document bundler-audit rubocop
ENV PATH=/home/crafter/.local/share/gem/ruby/3.1.0/bin:$PATH

# ============================================================
# 19. PHP user tooling: phpstan, phpunit (composer global)
# ============================================================
ENV COMPOSER_HOME=/home/crafter/.composer
RUN composer global require --no-interaction phpstan/phpstan phpunit/phpunit
ENV PATH=/home/crafter/.composer/vendor/bin:$PATH

# ============================================================
# 20. Smoke script — bake in
# ============================================================
COPY --chown=crafter:crafter smoke.sh /usr/local/bin/smoke.sh
USER root
RUN chmod +x /usr/local/bin/smoke.sh

# ============================================================
# 21. Application — FastAPI + async runner (wave 1: steps 2+3+4)
# ============================================================
COPY pyproject.toml requirements.txt /app/
RUN pip install --break-system-packages --no-cache-dir -r /app/requirements.txt
COPY --chown=crafter:crafter crafting_table /app/crafting_table
RUN chown -R crafter:crafter /app

# ============================================================
# 22. Final ENV / WORKDIR / CMD
# ============================================================
USER crafter
WORKDIR /workspace

# Final clean PATH — single source of truth that overrides any earlier
# accumulator drift in the layered ENV PATH= statements above. Lists
# every toolchain bin so cargo/rustc, swift, kotlinc, gradle, bun, go +
# govulncheck/staticcheck, ruff/mypy/pytest/uv, phpstan, bundler-audit
# are all reachable from the crafter user shell with no per-recipe prefix.
ENV PATH=/home/crafter/.local/bin:/home/crafter/.composer/vendor/bin:/home/crafter/.local/share/gem/ruby/3.1.0/bin:/home/crafter/.bun/bin:/home/crafter/go/bin:/home/crafter/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin:/caches/cargo/bin:/opt/swift/usr/bin:/opt/kotlin/bin:/opt/gradle/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

ENV PYTHONPATH=/app \
    PYTHONUNBUFFERED=1
CMD ["uvicorn", "crafting_table.server:app", "--host", "0.0.0.0", "--port", "8810"]
