Charlie/project-charlie: Build image / config (push) Successful in 2s
Charlie/project-charlie: Deploy runners / config (push) Successful in 0s
Charlie/project-charlie: Deploy runners / deploy-morgott (push) Skipped
Charlie/project-charlie: Deploy runners / deploy-melina (push) Skipped
Charlie/project-charlie: Deploy stack / config (push) Successful in 1s
Charlie/project-charlie: Deploy stack / deploy (push) Skipped
Charlie/project-charlie: Build image / build (push) Successful in 5m57s
The entrypoint now mirrors and locks the repo checkout read-only (root-owned) as root, renders the sherlock config into the copilot user's home, then drops the service to the jarvis user. The jarvis binary carries CAP_SETUID/SETGID so the non-root service can spawn the agent as the separate copilot user. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5749c447-6ecd-46bd-946d-21b4d101d084
91 lines
4.1 KiB
Docker
91 lines
4.1 KiB
Docker
# ---------- Builder ----------
|
|
FROM golang:1.26-bookworm AS builder
|
|
|
|
WORKDIR /src
|
|
|
|
# Cache modules first.
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
# Build the static (cgo-free, pure-Go sqlite) binary.
|
|
COPY . .
|
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/jarvis ./cmd/jarvis
|
|
|
|
# Install the sherlock credential broker + its MCP servers so the headless
|
|
# Copilot agent gets live read-only access to Grafana, Gitea, and hosts.
|
|
# Pinned to a released tag; the module is public so no auth is needed.
|
|
# GOPROXY=direct + GOSUMDB=off because it is served from Charlie's Gitea,
|
|
# not the public proxy/checksum database.
|
|
ARG SHERLOCK_VERSION=v0.1.11
|
|
RUN GOBIN=/out GOFLAGS=-trimpath GOPROXY=direct GOSUMDB=off CGO_ENABLED=0 \
|
|
go install \
|
|
gitea.alexandru.macocian.me/amacocian/sherlock/cmd/sherlock@${SHERLOCK_VERSION} \
|
|
gitea.alexandru.macocian.me/amacocian/sherlock/cmd/grafana-mcp@${SHERLOCK_VERSION} \
|
|
gitea.alexandru.macocian.me/amacocian/sherlock/cmd/gitea-mcp@${SHERLOCK_VERSION} \
|
|
gitea.alexandru.macocian.me/amacocian/sherlock/cmd/gssh-mcp@${SHERLOCK_VERSION}
|
|
|
|
# ---------- Runtime ----------
|
|
# node base image so the GitHub Copilot CLI (a Node package) runs headless.
|
|
FROM node:22-bookworm-slim AS runtime
|
|
|
|
# Runtime deps: git (clone Charlie repos for context), ca-certs, jq (parse
|
|
# the Gitea API in the entrypoint), tini (PID 1 signal handling), gosu (drop
|
|
# privileges to the jarvis user after fixing the bind-mount ownership),
|
|
# gettext-base (envsubst, to render the sherlock config from env), libcap2-bin
|
|
# (setcap: grant the jarvis binary CAP_SETUID/SETGID so it can drop the agent
|
|
# to a separate unprivileged user without the whole service running as root).
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git ca-certificates jq tini curl gosu gettext-base libcap2-bin \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install the GitHub Copilot CLI globally (provides the `copilot` binary).
|
|
RUN npm install -g @github/copilot && npm cache clean --force
|
|
|
|
# Two unprivileged users:
|
|
# jarvis (10001) runs the service and owns /data (the SQLite DB).
|
|
# copilot (10002) runs the --yolo agent + MCP subprocess tree, confined to
|
|
# its own writable home; the mirrored repos are mounted read-only.
|
|
# /context holds the cloned repos (locked read-only at startup by the
|
|
# entrypoint); /data holds the SQLite DB.
|
|
RUN useradd --create-home --uid 10001 jarvis \
|
|
&& useradd --create-home --uid 10002 copilot \
|
|
&& mkdir -p /context /data \
|
|
&& chown -R jarvis:jarvis /context /data
|
|
|
|
COPY --from=builder /out/jarvis /usr/local/bin/jarvis
|
|
# Let the (non-root) jarvis service drop the agent subprocess to the copilot
|
|
# user via setuid/setgid, while the service itself keeps running as jarvis.
|
|
RUN setcap cap_setuid,cap_setgid+ep /usr/local/bin/jarvis
|
|
# The mirrored repos are checked out root-owned and read-only; allow any user
|
|
# (the copilot agent) to run read-only git commands inside them.
|
|
RUN git config --system --add safe.directory '*'
|
|
# sherlock CLI + MCP servers on PATH so `sherlock copilot` can spawn them.
|
|
COPY --from=builder /out/sherlock /out/grafana-mcp /out/gitea-mcp /out/gssh-mcp /usr/local/bin/
|
|
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
|
|
# The container starts as root so the entrypoint can chown the /data bind
|
|
# mount (which Docker creates root-owned on the host), then drops to the
|
|
# unprivileged jarvis user via gosu before running the service.
|
|
WORKDIR /context
|
|
|
|
ENV HOME="/home/jarvis" \
|
|
JARVIS_LISTEN=":8080" \
|
|
JARVIS_DB_PATH="/data/jarvis.db" \
|
|
JARVIS_WORKDIR="/context" \
|
|
JARVIS_AGENT_WORKDIR="/context/Charlie/project-charlie" \
|
|
JARVIS_ADD_DIRS="/context" \
|
|
JARVIS_COPILOT_USER="copilot" \
|
|
GITEA_URL="https://gitea.alexandru.macocian.me" \
|
|
GITEA_ORGS="Charlie" \
|
|
GRAFANA_URL="https://grafana.alexandru.macocian.me" \
|
|
GSSH_URL="https://terminal.alexandru.macocian.me" \
|
|
SHERLOCK_ISSUER="https://id.alexandru.macocian.me/application/o/sherlock-cli/" \
|
|
SHERLOCK_KEYRING="memory" \
|
|
SHERLOCK_CONFIG="/home/copilot/.config/sherlock/config.toml"
|
|
|
|
EXPOSE 8080
|
|
VOLUME ["/data"]
|
|
|
|
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|