From c2125f063b1d6cdf91ea063008f8f5872db52a06 Mon Sep 17 00:00:00 2001 From: Jonas Miederer Date: Fri, 11 Sep 2026 17:18:59 +0200 Subject: [PATCH] fix: devcontainer build and setup on fresh clones --- .devcontainer/devcontainer.json | 8 ++++++-- .devcontainer/setup.sh | 27 ++++++++++++++++++++------- backend/.dockerignore | 9 +++++++++ backend/Dockerfile | 5 +++-- backend/entrypoint.sh | 6 ++++++ docker-compose.yml | 24 ++++++++++++++++++++---- frontend/.dockerignore | 6 ++++++ 7 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 backend/.dockerignore create mode 100644 frontend/.dockerignore diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c97848c..360eee8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,12 +11,16 @@ }, "mounts": [ "source=${localEnv:USERPROFILE}/.ssh,target=/mnt/host-ssh,type=bind,readonly", - "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind", "source=kirche-opencode-share,target=/home/vscode/.local/share/opencode,type=volume", "source=kirche-opencode-config,target=/home/vscode/.config/opencode,type=volume" ], "features": { - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} + // moby:false — python:3.12-slim is Debian trixie now, whose repos have + // no moby-cli (feature default). false installs the upstream Docker CE + // CLI instead, which supports trixie. + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "moby": false + } }, "overrideCommand": false, "postCreateCommand": "sudo -E bash .devcontainer/setup.sh", diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index a92a38d..dda5724 100644 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -2,10 +2,19 @@ # Dev-only setup (runs via postCreateCommand in the attached `api` container). # Production needs none of this: images carry their dependencies and the # api entrypoint migrates on every start. -set -e +set -eo pipefail REPO=/srv/kirche-einfach +# Run a command as vscode with the current environment intact. +# (`su vscode -c` resets the environment, which used to drop +# UV_PROJECT_ENVIRONMENT/UV_PYTHON_INSTALL_DIR plus every compose-provided +# variable — uv then created a project-local backend/.venv in the bind-mount +# instead of using /opt/venv, and sync_flows.py ran without its settings.) +as_vscode() { + sudo -u vscode -E bash -c "$*" +} + # Venv lives outside the bind-mount (see backend/Dockerfile). export UV_PYTHON_INSTALL_DIR=/opt/python UV_PROJECT_ENVIRONMENT=/opt/venv @@ -33,22 +42,26 @@ chown -R vscode:vscode "$REPO/backend/data" \ # the editor; uv resolves at setup time, no lockfile is committed). echo "--- Installing Python dependencies ---" cd "$REPO/backend" -su vscode -c "uv sync" +as_vscode "uv sync" # opencode CLI (dev tooling; intentionally not baked into the image). if [ ! -x /home/vscode/.opencode/bin/opencode ]; then echo "--- Installing opencode ---" - su vscode -c "curl -fsSL https://opencode.ai/install | bash" + as_vscode "curl -fsSL https://opencode.ai/install | bash" ln -sf /home/vscode/.opencode/bin/opencode /usr/local/bin/opencode fi # Compose services (langflow, lfx) must be up; fail loudly otherwise. # (api/frontend are starting alongside this script.) -echo "--- Waiting for compose services ---" +# Fresh clones have no `.env` (gitignored) and compose `env_file` is +# optional, so provision one from the example instead of failing setup. +# Fill in real secrets afterwards; empty SMTP_* means the API logs mail. if [ ! -f "$REPO/.env" ]; then - echo "ERROR: $REPO/.env is missing. Copy .env.example to .env and fill in secrets." >&2 - exit 1 + echo "WARN: $REPO/.env missing — copying .env.example (fill in secrets)." + cp "$REPO/.env.example" "$REPO/.env" + chown vscode:vscode "$REPO/.env" 2>/dev/null || true fi +echo "--- Waiting for compose services ---" python3 - <<'EOF' import socket import sys @@ -79,7 +92,7 @@ EOF # entrypoint on every start). echo "--- Syncing canonical flows ---" cd "$REPO/backend" -su vscode -c "uv run python scripts/sync_flows.py --sync" || echo "WARN: flow sync failed (check backend/flows and LFX)" +as_vscode "uv run python scripts/sync_flows.py --sync" || echo "WARN: flow sync failed (check backend/flows and LFX)" echo "" echo "=== Setup complete ===" diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..b519d04 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,9 @@ +# Keep the build context lean: only pyproject/alembic/entrypoint plus +# app/migrations/scripts are COPYed. Local venvs, databases and caches must +# never enter the context (slow builds, root-owned file leaks). +.venv/ +data/ +__pycache__/ +*.py[cod] +.pytest_cache/ +uv.lock diff --git a/backend/Dockerfile b/backend/Dockerfile index 06d304e..d31f76b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -16,9 +16,10 @@ ENV PATH="/opt/venv/bin:$PATH" COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv # Foundational, inert in prod: non-root user for the editor, sudo for setup, -# openssh-client for the devcontainer post-start hook. +# openssh-client for the devcontainer post-start hook, curl for the opencode +# installer in setup.sh, git for the VS Code source-control view. RUN apt-get update \ - && apt-get install -y --no-install-recommends sudo openssh-client \ + && apt-get install -y --no-install-recommends sudo openssh-client curl git ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && useradd -m -s /bin/bash vscode \ && echo "vscode ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/vscode \ diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index f329967..a47bf93 100644 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -16,6 +16,12 @@ fi /opt/venv/bin/alembic upgrade head +# Migrate ran as root: hand the (possibly just created) DB files back to +# vscode so editor-run commands stay writable. +if id vscode >/dev/null 2>&1; then + chown -R vscode:vscode data 2>/dev/null || true +fi + if [ "${APP_ENV:-prod}" = "dev" ]; then exec /opt/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload else diff --git a/docker-compose.yml b/docker-compose.yml index 6a6313f..5ca297a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,11 @@ services: context: ./backend dockerfile: Dockerfile env_file: - - .env + # Optional: fresh clones and CI checkouts have no `.env` (gitignored); + # containers must still create so `config`/`build` work everywhere. + # Runtime secrets come from `.env` when present (see setup.sh). + - path: .env + required: false volumes: # No venv volume by design: the venv lives at /opt/venv (image-local, # see backend/Dockerfile). A named volume here used to shadow the image @@ -43,7 +47,11 @@ services: context: ./frontend dockerfile: Dockerfile env_file: - - .env + # Optional: fresh clones and CI checkouts have no `.env` (gitignored); + # containers must still create so `config`/`build` work everywhere. + # Runtime secrets come from `.env` when present (see setup.sh). + - path: .env + required: false environment: # Dev default (HMR); prod .env sets `preview` (build + serve bundle). FRONTEND_MODE: ${FRONTEND_MODE:-dev} @@ -65,7 +73,11 @@ services: # .env.example) together with Dockerfile.lfx + backend `lfx~=` pin. image: langflowai/langflow:${LANGFLOW_VERSION:-1.12.0} env_file: - - .env + # Optional: fresh clones and CI checkouts have no `.env` (gitignored); + # containers must still create so `config`/`build` work everywhere. + # Runtime secrets come from `.env` when present (see setup.sh). + - path: .env + required: false environment: # Static values only; secrets/URLs come from .env via env_file. LANGFLOW_CONFIG_DIR: /app/langflow @@ -104,7 +116,11 @@ services: context: . dockerfile: Dockerfile.lfx env_file: - - .env + # Optional: fresh clones and CI checkouts have no `.env` (gitignored); + # containers must still create so `config`/`build` work everywhere. + # Runtime secrets come from `.env` when present (see setup.sh). + - path: .env + required: false volumes: - lfx-data:/flows ports: diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..7ff60fe --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,6 @@ +# Never bake host build output into the Linux image: the host checkout runs +# on Windows, so a copied node_modules would shadow the image install with +# wrong-platform binaries and diverge from package-lock.json. +node_modules/ +dist/ +*.tsbuildinfo