feat: unify on single repo-root compose file for dev and prod
Some checks failed
/ test (push) Has been cancelled
/ build (push) Has been cancelled
/ validate (push) Has been cancelled

This commit is contained in:
Jonas Miederer 2026-09-07 19:46:17 +00:00
parent 9acdbaa453
commit af993bd43f
18 changed files with 253 additions and 229 deletions

View file

@ -1,48 +0,0 @@
FROM mcr.microsoft.com/devcontainers/base:debian
ARG FORGEJO_USERNAME
ENV FORGEJO_USERNAME=${FORGEJO_USERNAME}
# SSH (missing from base image)
RUN apt-get update \
&& apt-get install -y --no-install-recommends openssh-client \
&& rm -rf /var/lib/apt/lists/*
# Node.js 22 + npm (npm tree copied; bin symlinks recreated - COPY dereferences them)
COPY --from=node:22-slim /usr/local/bin/node /usr/local/bin/
COPY --from=node:22-slim /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
# uv (from distroless)
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# Python 3.12 (system-shared location; default /root is unreadable for vscode)
ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python
RUN uv python install 3.12 \
&& ln -s /opt/uv/python/cpython-3.12-linux-x86_64-gnu/bin/python3.12 /usr/local/bin/python3.12 \
&& ln -s python3.12 /usr/local/bin/python3 \
&& ln -s python3 /usr/local/bin/python
# Trust the workspace bind mount (source on Windows FS -> uid mismatch)
RUN git config --system --add safe.directory /workspaces/kirche-einfach
# Pre-create named-volume targets (volumes inherit image content on first mount).
# The backend venv is created as `vscode` so VS Code's Python extension finds an
# interpreter immediately on first attach, before postCreateCommand runs `uv sync`.
USER vscode
RUN mkdir -p /workspaces/kirche-einfach/backend \
&& uv venv /workspaces/kirche-einfach/backend/.venv \
&& mkdir -p /workspaces/kirche-einfach/frontend/node_modules
USER root
# opencode (as non-root user)
USER vscode
RUN curl -fsSL https://opencode.ai/install | bash
USER root
RUN ln -s /home/vscode/.opencode/bin/opencode /usr/local/bin/opencode
# Fix ownership (home + volume targets so vscode user can write)
RUN chown -R vscode:vscode /home/vscode /workspaces
CMD ["sleep", "infinity"]

View file

@ -1,19 +0,0 @@
# Stateless Langflow flow runtime (LFX). Serves versioned flow JSON files
# deployed by our backend via POST /flows/upload/ (persisted in /flows).
# LFX stays on the internal compose network; our FastAPI is the only
# public surface and proxies run/stream calls.
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
# lfx = flow runtime; requests = convenience for custom-component code.
RUN pip install lfx requests
RUN mkdir -p /flows
VOLUME /flows
EXPOSE 6004
# Serve the /flows dir (uploaded flows persist there via --flow-dir).
# Flags verified against `lfx serve --help` (lfx 1.12.0).
CMD ["lfx", "serve", "--host", "0.0.0.0", "--port", "6004", "--flow-dir", "/flows", "/flows"]

View file

@ -1,17 +1,22 @@
{
"name": "kirche-einfach",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"runServices": ["db", "langflow", "lfx"],
"mounts": [
"source=${localEnv:USERPROFILE}/.ssh,target=/mnt/host-ssh,type=bind,readonly"
],
"dockerComposeFile": ["../docker-compose.yml"],
"service": "api",
"runServices": ["api", "frontend", "langflow", "lfx"],
"workspaceFolder": "/srv/kirche-einfach",
"remoteUser": "vscode",
"containerEnv": {
"APP_ENV": "dev",
"FORGEJO_USERNAME": "jonas"
},
"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"
],
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"overrideCommand": false,
"workspaceFolder": "/workspaces/kirche-einfach",
"postCreateCommand": "sudo -E bash .devcontainer/setup.sh",
"postStartCommand": "bash .devcontainer/postStart.sh",
"customizations": {
@ -24,7 +29,7 @@
"esbenp.prettier-vscode"
],
"settings": {
"python.defaultInterpreterPath": "/workspaces/kirche-einfach/backend/.venv/bin/python"
"python.defaultInterpreterPath": "/srv/kirche-einfach/backend/.venv/bin/python"
}
}
}

View file

@ -1,92 +0,0 @@
# Compose stack for the kirche-einfach devcontainer.
# VS Code attaches to the `app` service (see devcontainer.json); all values
# come from the repo-root `.env` via `env_file` (no ${} interpolation needed).
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
FORGEJO_USERNAME: jonas
command: sleep infinity
volumes:
- ..:/workspaces/kirche-einfach:cached
- kirche-einfach-venv:/workspaces/kirche-einfach/backend/.venv
- kirche-einfach-node-modules:/workspaces/kirche-einfach/frontend/node_modules
env_file:
- ../.env
ports:
- "8000:8000" # FastAPI
- "3000:3000" # Vite
langflow:
image: langflowai/langflow:latest
env_file:
- ../.env
environment:
# Static values only; secrets/URLs come from ../.env via env_file
# (compose interpolates ${} from .devcontainer/.env, not the repo-root .env).
LANGFLOW_CONFIG_DIR: /app/langflow
LANGFLOW_HOST: 0.0.0.0
PORT: "7860"
LANGFLOW_AUTO_LOGIN: "false"
LANGFLOW_API_KEY_SOURCE: env
# SQLite file inside the langflow-data volume (absolute path required
# by Langflow); overrides any postgres URL in ../.env.
LANGFLOW_DATABASE_URL: sqlite:////app/langflow/langflow.db
volumes:
- langflow-data:/app/langflow
ports:
- "7860:7860"
healthcheck:
test:
[
"CMD-SHELL",
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:7860/health', timeout=5)\"",
]
interval: 10s
timeout: 5s
retries: 20
start_period: 60s
lfx:
build:
context: .
dockerfile: Dockerfile.lfx
env_file:
- ../.env
volumes:
- lfx-data:/flows
ports:
- "6004:6004"
healthcheck:
test:
[
"CMD-SHELL",
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:6004/health', timeout=5)\"",
]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
# Optional nightly auto-updates of :latest images. Not part of the default
# dev startup (no container updates needed while iterating).
watchtower:
image: nickfedor/watchtower:latest
profiles: ["updates"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_SCHEDULE: "0 0 4 * * *"
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_ROLLING_RESTART: "true"
labels:
com.centurylinklabs.watchtower.enable: "true"
volumes:
kirche-einfach-venv:
kirche-einfach-node-modules:
langflow-data:
lfx-data:

View file

@ -1,22 +1,30 @@
#!/bin/bash
# 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
REPO=/srv/kirche-einfach
echo "=== Setting up kirche-einfach ==="
# Backend
# Backend deps into the venv volume (setup runs via sudo; handed to vscode below).
echo "--- Installing Python dependencies ---"
cd /workspaces/kirche-einfach/backend
cd "$REPO/backend"
uv sync
# Frontend
echo "--- Installing Node dependencies ---"
cd /workspaces/kirche-einfach/frontend
npm install
# 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"
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 ---"
if [ ! -f /workspaces/kirche-einfach/.env ]; then
echo "ERROR: /workspaces/kirche-einfach/.env is missing. Copy .env.example to .env and fill in secrets." >&2
if [ ! -f "$REPO/.env" ]; then
echo "ERROR: $REPO/.env is missing. Copy .env.example to .env and fill in secrets." >&2
exit 1
fi
python3 - <<'EOF'
@ -45,19 +53,17 @@ if pending:
sys.exit(f"ERROR: compose services not ready: {sorted(pending)}")
EOF
# Migrate the SQLite schema and reconcile backend/flows into the DB + LFX.
echo "--- Migrating database ---"
cd /workspaces/kirche-einfach/backend
uv run alembic upgrade head
# Reconcile backend/flows into the DB + LFX (schema is migrated by the api
# entrypoint on every start).
echo "--- Syncing canonical flows ---"
cd "$REPO/backend"
uv run python scripts/sync_flows.py --sync || echo "WARN: flow sync failed (check backend/flows and LFX)"
echo ""
echo "=== Setup complete ==="
echo "Forgejo username: $FORGEJO_USERNAME"
# Hand dirs back to vscode (this script runs via sudo; uv/npm create caches as root)
chown -R vscode:vscode /workspaces/kirche-einfach/backend/.venv \
/workspaces/kirche-einfach/frontend/node_modules \
# Hand dirs back to vscode (this script runs via sudo; uv creates caches as root)
chown -R vscode:vscode "$REPO/backend/.venv" \
"$REPO/frontend/node_modules" \
/home/vscode/.cache 2>/dev/null || true