# syntax=docker/dockerfile:1.6 FROM ubuntu:22.04 # ----- system deps ------------------------------------------------------- ENV DEBIAN_FRONTEND=noninteractive \ TZ=Europe/Amsterdam \ LANG=C.UTF-8 \ LC_ALL=C.UTF-8 RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl git tini gosu jq xz-utils \ build-essential python3 \ tzdata logrotate \ gnupg \ && ln -fs /usr/share/zoneinfo/$TZ /etc/localtime \ && dpkg-reconfigure --frontend=noninteractive tzdata \ && rm -rf /var/lib/apt/lists/* # ----- gh CLI ------------------------------------------------------------ # Required for auto_pr (`gh pr create`) and authenticates via the GH_TOKEN # env-var that is also used by the git credential helper for HTTPS. RUN install -m 0755 -d /etc/apt/keyrings \ && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ | gpg --dearmor -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \ && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/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/* # ----- node 22 LTS ------------------------------------------------------- # Voor zowel Claude Code (de native installer heeft geen node nodig, maar # scrum4me-mcp draait op tsx) als de health-server. 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@9 tsx@4 \ && npm cache clean --force # ----- claude code via native installer --------------------------------- # Zet PATH zodat het user-binary gevonden wordt; native installer plaatst # in $HOME/.local/bin standaard. We installeren als root om in /usr/local # te belanden, of fallback naar /opt. ARG CLAUDE_CODE_VERSION=latest RUN curl -fsSL https://claude.ai/install.sh | bash -s ${CLAUDE_CODE_VERSION} \ && cp /root/.local/bin/claude /usr/local/bin/claude \ && chmod +x /usr/local/bin/claude \ && claude --version # ----- scrum4me-mcp ------------------------------------------------------ # Clone zonder submodules — de Prisma-schema zit al gecommit in het repo. # De vendor/scrum4me submodule is alleen nodig om het schema te updaten, # niet om te builden. Pin via build-arg; default = main. ARG MCP_GIT_REPO=https://github.com/madhura68/scrum4me-mcp.git ARG MCP_GIT_REF=main # Cache-bust voor de clone-laag: hetzelfde MCP_GIT_REF kan tussen rebuilds # een ander commit aanwijzen (bv. main na een merge). Geef als build-arg # `--build-arg MCP_CACHE_BUST=$(date +%s)` mee om deze laag te invalidaten. ARG MCP_CACHE_BUST=1 RUN echo "cache-bust=${MCP_CACHE_BUST}" \ && git clone --branch ${MCP_GIT_REF} --depth 1 \ ${MCP_GIT_REPO} /opt/scrum4me-mcp \ && cd /opt/scrum4me-mcp \ && npm ci --omit=dev --omit=optional || npm install --omit=dev \ && npx prisma generate # ----- non-root user ----------------------------------------------------- # UID 1000 zodat bind-mounted /share/Agent/* schrijfrechten matchen met de # admin op QNAP. Pas aan via build-arg als je een andere UID gebruikt. ARG AGENT_UID=1000 ARG AGENT_GID=1000 RUN set -eux; \ if getent group "${AGENT_GID}" >/dev/null; then \ groupmod -n agent "$(getent group "${AGENT_GID}" | cut -d: -f1)"; \ else \ groupadd -g "${AGENT_GID}" agent; \ fi; \ if id -u "${AGENT_UID}" >/dev/null 2>&1; then \ usermod -l agent -d /home/agent -m -g "${AGENT_GID}" -s /bin/bash "$(id -nu "${AGENT_UID}")"; \ else \ useradd -u "${AGENT_UID}" -g "${AGENT_GID}" -m -s /bin/bash agent; \ fi; \ mkdir -p /var/cache/repos /var/cache/npm /var/log/agent /var/run/agent; \ mkdir -p /home/agent/Projects /home/agent/.scrum4me-agent-worktrees; \ chown -R agent:agent /var/cache /var/log/agent /var/run/agent /home/agent # ----- runner files ------------------------------------------------------ WORKDIR /opt/agent COPY --chown=agent:agent bin/ ./bin/ COPY --chown=agent:agent etc/ ./etc/ COPY --chown=agent:agent CLAUDE.md ./ COPY --chown=agent:agent mcp-config.json ./ RUN chmod +x ./bin/*.sh # ----- usage-capture hook ------------------------------------------------ # Claude Code start met cwd=/opt/agent en HOME=/home/agent. Zonder dit # kopieerblok ziet Claude Code geen .claude/settings.json en fireert de # PostToolUse-hook van scrum4me-mcp niet — token-tellers op claude_jobs # blijven dan NULL. Plaats de hook-config in de user-scope settings zodat # 'm activeert ongeacht de cwd waarin de agent draait. RUN install -d -o agent -g agent /home/agent/.claude \ && install -m 0644 -o agent -g agent \ /opt/scrum4me-mcp/.claude/settings.json \ /home/agent/.claude/settings.json # ----- runtime config ---------------------------------------------------- ENV PATH=/opt/agent/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ HOME=/home/agent \ NPM_CONFIG_CACHE=/var/cache/npm \ PNPM_HOME=/var/cache/pnpm \ AGENT_STATE_DIR=/var/run/agent \ AGENT_LOG_DIR=/var/log/agent \ AGENT_REPO_CACHE=/var/cache/repos \ AGENT_JOB_ROOT=/tmp \ AGENT_HEALTH_PORT=8080 \ SCRUM4ME_MCP_DIR=/opt/scrum4me-mcp \ NODE_PATH=/opt/scrum4me-mcp/node_modules EXPOSE 8080 # tini als PID 1 → correcte signal handling, geen zombies ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["/opt/agent/bin/entrypoint.sh"]