feat(devenv): MCP dev hot-reload with image-based approach (#145)

Add mcp-build.sh that compiles the MCP binary and packages it as an
OCI image layer on top of the base rootfs, imported directly into
containerd. Air triggers rebuild on code changes, cleaning stale
containers automatically.

Consolidate dev-only files (Dockerfiles, entrypoint, config, build
script) into devenv/ to separate dev tooling from production artifacts.
Skip image pull when already imported to speed up dev startup.
This commit is contained in:
BBQ
2026-03-02 14:59:48 +08:00
committed by GitHub
parent f9f968f13f
commit 04bce702b7
12 changed files with 131 additions and 26 deletions
-60
View File
@@ -1,60 +0,0 @@
# syntax=docker/dockerfile:1
FROM golang:1.25-alpine
WORKDIR /workspace
RUN go install github.com/air-verse/air@latest
RUN apk add --no-cache \
bash \
ca-certificates \
cni-plugins \
containerd \
containerd-ctr \
git \
iptables \
make \
tzdata \
wget \
&& mkdir -p /opt/cni/bin \
&& (cp -a /usr/lib/cni/. /opt/cni/bin/ 2>/dev/null || true) \
&& (cp -a /usr/libexec/cni/. /opt/cni/bin/ 2>/dev/null || true) \
&& mkdir -p /etc/cni/net.d /var/lib/cni /run/containerd /var/lib/containerd /opt/memoh/data
RUN printf '%s\n' \
'{' \
' "cniVersion": "1.0.0",' \
' "name": "memoh-cni",' \
' "plugins": [' \
' {' \
' "type": "bridge",' \
' "bridge": "cni0",' \
' "isGateway": true,' \
' "ipMasq": true,' \
' "hairpinMode": true,' \
' "ipam": {' \
' "type": "host-local",' \
' "ranges": [[' \
' { "subnet": "10.88.0.0/16" }' \
' ]],' \
' "routes": [' \
' { "dst": "0.0.0.0/0" }' \
' ]' \
' }' \
' },' \
' {' \
' "type": "portmap",' \
' "capabilities": { "portMappings": true }' \
' }' \
' ]' \
'}' > /etc/cni/net.d/10-memoh.conflist
COPY docker/server-dev-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
VOLUME ["/var/lib/containerd", "/opt/memoh/data"]
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
-3
View File
@@ -1,3 +0,0 @@
FROM node:25-alpine
RUN npm install -g pnpm@10
WORKDIR /workspace
-45
View File
@@ -1,45 +0,0 @@
#!/bin/sh
set -e
# Setup cgroup v2 delegation for nested containerd.
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
mkdir -p /sys/fs/cgroup/init
while read -r pid; do
echo "$pid" > /sys/fs/cgroup/init/cgroup.procs 2>/dev/null || true
done < /sys/fs/cgroup/cgroup.procs
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \
> /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null || true
fi
mkdir -p /run/containerd
containerd &
CONTAINERD_PID=$!
echo "Waiting for containerd..."
for i in $(seq 1 30); do
if ctr version >/dev/null 2>&1; then
break
fi
sleep 1
done
if ! ctr version >/dev/null 2>&1; then
echo "ERROR: containerd not responsive after 30s"
exit 1
fi
echo "containerd is ready, starting server command..."
trap 'kill ${SERVER_PID:-0} 2>/dev/null || true; kill ${CONTAINERD_PID:-0} 2>/dev/null || true; wait' TERM INT
"$@" &
SERVER_PID=$!
wait $SERVER_PID
EXIT_CODE=$?
kill $CONTAINERD_PID 2>/dev/null || true
wait $CONTAINERD_PID 2>/dev/null || true
exit $EXIT_CODE