Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ docker-git apply-all
docker-git apply-all --active
```

- `apply` применяет конфиг к одному проекту. `--no-up` только обновляет файлы без `docker compose up`. В текущем API-only host mode команда ещё недоступна.
- `apply` применяет конфиг к одному проекту. `--no-up` только обновляет файлы без `docker compose up`.
- `apply-all` применяет конфиг ко всем проектам. `--active` только к запущенным контейнерам.


Expand Down
13 changes: 12 additions & 1 deletion docker-compose.api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ services:
container_name: ${DOCKER_GIT_API_CONTAINER_NAME:-docker-git-api}
environment:
DOCKER_GIT_API_PORT: ${DOCKER_GIT_API_PORT:-3334}
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-isolated}
DOCKER_HOST: ${DOCKER_GIT_CONTROLLER_DOCKER_HOST:-unix:///var/run/docker.sock}
DOCKER_GIT_DOCKERD_TCP_HOST: ${DOCKER_GIT_DOCKERD_TCP_HOST:-tcp://0.0.0.0:2375}
DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE: ${DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE:-host}
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-tcp://host.docker.internal:2375}
DOCKER_GIT_PROJECT_SSH_BIND_HOST: ${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-0.0.0.0}
DOCKER_GIT_PROJECTS_ROOT: ${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
DOCKER_GIT_PROJECTS_ROOT_VOLUME: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN: ${DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN:-}
Expand All @@ -25,10 +31,15 @@ services:
- 8.8.4.4
- 1.1.1.1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- docker_git_projects:${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
- docker_git_docker_data:/var/lib/docker
privileged: true
cgroup: host
init: true
restart: unless-stopped

volumes:
docker_git_projects:
name: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
docker_git_docker_data:
name: ${DOCKER_GIT_DOCKER_DATA_VOLUME:-docker-git-docker-data}
13 changes: 12 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ services:
environment:
DOCKER_GIT_API_PORT: ${DOCKER_GIT_API_PORT:-3334}
DOCKER_GIT_CONTROLLER_REV: ${DOCKER_GIT_CONTROLLER_REV:-unknown}
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-isolated}
DOCKER_HOST: ${DOCKER_GIT_CONTROLLER_DOCKER_HOST:-unix:///var/run/docker.sock}
DOCKER_GIT_DOCKERD_TCP_HOST: ${DOCKER_GIT_DOCKERD_TCP_HOST:-tcp://0.0.0.0:2375}
DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE: ${DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE:-host}
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-tcp://host.docker.internal:2375}
DOCKER_GIT_PROJECT_SSH_BIND_HOST: ${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-0.0.0.0}
DOCKER_GIT_PROJECTS_ROOT: ${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
DOCKER_GIT_PROJECTS_ROOT_VOLUME: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN: ${DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN:-}
Expand All @@ -27,10 +33,15 @@ services:
- 8.8.4.4
- 1.1.1.1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- docker_git_projects:${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
- docker_git_docker_data:/var/lib/docker
privileged: true
cgroup: host
init: true
restart: unless-stopped

volumes:
docker_git_projects:
name: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
docker_git_docker_data:
name: ${DOCKER_GIT_DOCKER_DATA_VOLUME:-docker-git-docker-data}
4 changes: 3 additions & 1 deletion packages/api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ RUN bun run --cwd packages/lib build
RUN bun run --cwd packages/api build

ENV DOCKER_GIT_API_PORT=3334
ENV DOCKER_GIT_DOCKER_RUNTIME=isolated
ENV DOCKER_HOST=unix:///var/run/docker.sock
EXPOSE 3334

CMD ["bun", "packages/api/dist/src/main.js"]
CMD ["bash", "packages/api/scripts/start-controller.sh"]
9 changes: 8 additions & 1 deletion packages/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ HTTP API for docker-git orchestration (projects, agents, logs/events, federation
This is now the intended controller plane:
- the API runs inside `docker-git-api`
- `.docker-git` state lives in the Docker volume `docker-git-projects`
- the API talks to Docker through `/var/run/docker.sock`
- the API starts an isolated Docker daemon inside the controller by default
- child project containers no longer depend on host bind mounts for bootstrap auth/env
- the host `/var/run/docker.sock` is not mounted into the controller or project containers

## UI wrapper

Expand Down Expand Up @@ -41,6 +42,12 @@ Optional env:

- `DOCKER_GIT_API_BIND_HOST` (default: `127.0.0.1`)
- `DOCKER_GIT_API_PORT` (default: `3334`)
- `DOCKER_GIT_DOCKER_RUNTIME` (default: `isolated`; starts a managed Docker daemon in `docker-git-api`)
- `DOCKER_GIT_CONTROLLER_DOCKER_HOST` (default: `unix:///var/run/docker.sock`; socket path inside the controller)
- `DOCKER_GIT_DOCKERD_TCP_HOST` (default: `tcp://0.0.0.0:2375`; reachable only inside Docker networks unless explicitly published)
- `DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE` (default: `host`; keeps nested project containers compatible with cgroup v2 DinD)
- `DOCKER_GIT_PROJECT_DOCKER_HOST` (default: `tcp://host.docker.internal:2375`; lets project containers use the isolated daemon)
- `DOCKER_GIT_PROJECT_SSH_BIND_HOST` (default: `0.0.0.0` in controller mode; project SSH binds inside the isolated controller runtime)
- `DOCKER_GIT_PROJECTS_ROOT` (container path, default: `/home/dev/.docker-git`)
- `DOCKER_GIT_PROJECTS_ROOT_VOLUME` (Docker volume name for controller state, default: `docker-git-projects`)
- `DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN` (optional public ActivityPub origin)
Expand Down
68 changes: 68 additions & 0 deletions packages/api/scripts/start-controller.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -euo pipefail

runtime="${DOCKER_GIT_DOCKER_RUNTIME:-isolated}"
docker_host="${DOCKER_HOST:-unix:///var/run/docker.sock}"
dockerd_pid=""

cleanup() {
if [[ -n "$dockerd_pid" ]] && kill -0 "$dockerd_pid" >/dev/null 2>&1; then
kill "$dockerd_pid" >/dev/null 2>&1 || true
wait "$dockerd_pid" >/dev/null 2>&1 || true
fi
}

trap cleanup EXIT INT TERM

if [[ "$runtime" == "isolated" ]]; then
if [[ "$docker_host" != unix://* ]]; then
echo "DOCKER_GIT_DOCKER_RUNTIME=isolated requires a unix:// DOCKER_HOST for the managed controller daemon." >&2
exit 1
fi

export DOCKER_HOST="$docker_host"

socket_path="${docker_host#unix://}"
data_root="${DOCKER_GIT_DOCKER_DATA_ROOT:-/var/lib/docker}"
log_path="${DOCKER_GIT_DOCKERD_LOG:-/var/log/docker-git/dockerd.log}"
tcp_host="${DOCKER_GIT_DOCKERD_TCP_HOST:-tcp://0.0.0.0:2375}"
default_cgroupns_mode="${DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE:-host}"

mkdir -p "$(dirname "$socket_path")" "$data_root" "$(dirname "$log_path")"

if ! docker info >/dev/null 2>&1; then
# shellcheck disable=SC2086
dockerd \
--host="$docker_host" \
--host="$tcp_host" \
--tls=false \
--data-root="$data_root" \
--exec-opt native.cgroupdriver=cgroupfs \
--default-cgroupns-mode="$default_cgroupns_mode" \
${DOCKER_GIT_DOCKERD_ARGS:-} \
>"$log_path" 2>&1 &
dockerd_pid="$!"

for _ in $(seq 1 90); do
if docker info >/dev/null 2>&1; then
break
fi
if ! kill -0 "$dockerd_pid" >/dev/null 2>&1; then
echo "Managed Docker daemon exited during startup." >&2
tail -n 200 "$log_path" >&2 || true
exit 1
fi
sleep 1
done

if ! docker info >/dev/null 2>&1; then
echo "Managed Docker daemon did not become ready in time." >&2
tail -n 200 "$log_path" >&2 || true
exit 1
fi
fi
fi

bun packages/api/dist/src/main.js &
api_pid="$!"
wait "$api_pid"
1 change: 1 addition & 0 deletions packages/app/src/lib/core/templates-entrypoint/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ rm -f "$AGENT_DONE_PATH" "$AGENT_FAIL_PATH" "$AGENT_PROMPT_FILE"`,
String.raw`# Collect tokens for agent environment (su - dev does not always inherit profile.d)
AGENT_ENV_FILE="/run/docker-git/agent-env.sh"
{
[[ -f /etc/profile.d/docker-host.sh ]] && cat /etc/profile.d/docker-host.sh
[[ -f /etc/profile.d/gh-token.sh ]] && cat /etc/profile.d/gh-token.sh
[[ -f /etc/profile.d/claude-config.sh ]] && cat /etc/profile.d/claude-config.sh
[[ -f /etc/profile.d/gemini-config.sh ]] && cat /etc/profile.d/gemini-config.sh
Expand Down
15 changes: 12 additions & 3 deletions packages/app/src/lib/core/templates-entrypoint/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,25 @@ fi
chown -R 1000:1000 /home/${config.sshUser}/.ssh`

export const renderEntrypointDockerSocket = (config: TemplateConfig): string =>
`# Ensure docker socket access for ${config.sshUser}
if [[ -S /var/run/docker.sock ]]; then
`# Ensure Docker CLI targets only the explicitly configured daemon.
if [[ -n "\${DOCKER_GIT_PROJECT_DOCKER_HOST:-}" && -z "\${DOCKER_HOST:-}" ]]; then
DOCKER_HOST="$DOCKER_GIT_PROJECT_DOCKER_HOST"
export DOCKER_HOST
fi

if [[ -n "\${DOCKER_HOST:-}" ]]; then
printf "export DOCKER_HOST=%q\\n" "$DOCKER_HOST" > /etc/profile.d/docker-host.sh
docker_git_upsert_ssh_env "DOCKER_HOST" "$DOCKER_HOST"
elif [[ -S /var/run/docker.sock ]]; then
DOCKER_SOCK_GID="$(stat -c "%g" /var/run/docker.sock)"
DOCKER_GROUP="$(getent group "$DOCKER_SOCK_GID" | cut -d: -f1 || true)"
if [[ -z "$DOCKER_GROUP" ]]; then
DOCKER_GROUP="docker"
groupadd -g "$DOCKER_SOCK_GID" "$DOCKER_GROUP" || true
fi
usermod -aG "$DOCKER_GROUP" ${config.sshUser} || true
printf "export DOCKER_HOST=unix:///var/run/docker.sock\n" > /etc/profile.d/docker-host.sh
printf "export DOCKER_HOST=unix:///var/run/docker.sock\\n" > /etc/profile.d/docker-host.sh
docker_git_upsert_ssh_env "DOCKER_HOST" "unix:///var/run/docker.sock"
fi`

export const renderEntrypointZshShell = (config: TemplateConfig): string =>
Expand Down
7 changes: 5 additions & 2 deletions packages/app/src/lib/core/templates/docker-compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,20 @@ ${fragments.maybeGithubAuthSkipEnv} # Optional anonymous public GitHub clon
${fragments.maybeGitTokenLabelEnv} # Optional token label selector (maps to GITHUB_TOKEN__<LABEL>/GIT_AUTH_TOKEN__<LABEL>)
${fragments.maybeCodexAuthLabelEnv} # Optional Codex account label selector (maps to CODEX_AUTH_LABEL)
${fragments.maybeClaudeAuthLabelEnv}${fragments.maybeAgentModeEnv}${fragments.maybeAgentAutoEnv} # Optional Claude account label selector (maps to CLAUDE_AUTH_LABEL)
# Optional isolated Docker daemon endpoint injected by the API controller.
DOCKER_GIT_PROJECT_DOCKER_HOST: "\${DOCKER_GIT_PROJECT_DOCKER_HOST:-}"
TARGET_DIR: "${config.targetDir}"
CODEX_HOME: "${config.codexHome}"
${fragments.maybePlaywrightEnv}${fragments.maybeDependsOn} # bootstrap auth/env arrives through docker_git_bootstrap
ports:
- "127.0.0.1:${config.sshPort}:22"
- "\${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-127.0.0.1}:${config.sshPort}:22"
${renderResourceLimits(resourceLimits)} volumes:
- ${config.volumeName}:/home/${config.sshUser}
- ${sharedCacheVolumeKey}:/home/${config.sshUser}/.docker-git/.cache
- ${sharedCodexVolumeKey}:${config.codexHome}-shared
${fragments.maybeBootstrapMounts}
- /var/run/docker.sock:/var/run/docker.sock
extra_hosts:
- "host.docker.internal:host-gateway"
dns:
- 8.8.8.8
- 8.8.4.4
Expand Down
1 change: 1 addition & 0 deletions packages/lib/src/core/templates-entrypoint/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ rm -f "$AGENT_DONE_PATH" "$AGENT_FAIL_PATH" "$AGENT_PROMPT_FILE"`,
String.raw`# Collect tokens for agent environment (su - dev does not always inherit profile.d)
AGENT_ENV_FILE="/run/docker-git/agent-env.sh"
{
[[ -f /etc/profile.d/docker-host.sh ]] && cat /etc/profile.d/docker-host.sh
[[ -f /etc/profile.d/gh-token.sh ]] && cat /etc/profile.d/gh-token.sh
[[ -f /etc/profile.d/claude-config.sh ]] && cat /etc/profile.d/claude-config.sh
[[ -f /etc/profile.d/gemini-config.sh ]] && cat /etc/profile.d/gemini-config.sh
Expand Down
15 changes: 12 additions & 3 deletions packages/lib/src/core/templates-entrypoint/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,25 @@ fi
chown -R 1000:1000 /home/${config.sshUser}/.ssh`

export const renderEntrypointDockerSocket = (config: TemplateConfig): string =>
`# Ensure docker socket access for ${config.sshUser}
if [[ -S /var/run/docker.sock ]]; then
`# Ensure Docker CLI targets only the explicitly configured daemon.
if [[ -n "\${DOCKER_GIT_PROJECT_DOCKER_HOST:-}" && -z "\${DOCKER_HOST:-}" ]]; then
DOCKER_HOST="$DOCKER_GIT_PROJECT_DOCKER_HOST"
export DOCKER_HOST
fi

if [[ -n "\${DOCKER_HOST:-}" ]]; then
printf "export DOCKER_HOST=%q\\n" "$DOCKER_HOST" > /etc/profile.d/docker-host.sh
docker_git_upsert_ssh_env "DOCKER_HOST" "$DOCKER_HOST"
elif [[ -S /var/run/docker.sock ]]; then
DOCKER_SOCK_GID="$(stat -c "%g" /var/run/docker.sock)"
DOCKER_GROUP="$(getent group "$DOCKER_SOCK_GID" | cut -d: -f1 || true)"
if [[ -z "$DOCKER_GROUP" ]]; then
DOCKER_GROUP="docker"
groupadd -g "$DOCKER_SOCK_GID" "$DOCKER_GROUP" || true
fi
usermod -aG "$DOCKER_GROUP" ${config.sshUser} || true
printf "export DOCKER_HOST=unix:///var/run/docker.sock\n" > /etc/profile.d/docker-host.sh
printf "export DOCKER_HOST=unix:///var/run/docker.sock\\n" > /etc/profile.d/docker-host.sh
docker_git_upsert_ssh_env "DOCKER_HOST" "unix:///var/run/docker.sock"
fi`

export const renderEntrypointZshShell = (config: TemplateConfig): string =>
Expand Down
7 changes: 5 additions & 2 deletions packages/lib/src/core/templates/docker-compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,20 @@ ${fragments.maybeGithubAuthSkipEnv} # Optional anonymous public GitHub clon
${fragments.maybeGitTokenLabelEnv} # Optional token label selector (maps to GITHUB_TOKEN__<LABEL>/GIT_AUTH_TOKEN__<LABEL>)
${fragments.maybeCodexAuthLabelEnv} # Optional Codex account label selector (maps to CODEX_AUTH_LABEL)
${fragments.maybeClaudeAuthLabelEnv}${fragments.maybeAgentModeEnv}${fragments.maybeAgentAutoEnv} # Optional Claude account label selector (maps to CLAUDE_AUTH_LABEL)
# Optional isolated Docker daemon endpoint injected by the API controller.
DOCKER_GIT_PROJECT_DOCKER_HOST: "\${DOCKER_GIT_PROJECT_DOCKER_HOST:-}"
TARGET_DIR: "${config.targetDir}"
CODEX_HOME: "${config.codexHome}"
${fragments.maybePlaywrightEnv}${fragments.maybeDependsOn} # bootstrap auth/env arrives through docker_git_bootstrap
ports:
- "127.0.0.1:${config.sshPort}:22"
- "\${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-127.0.0.1}:${config.sshPort}:22"
${renderResourceLimits(resourceLimits)} volumes:
- ${config.volumeName}:/home/${config.sshUser}
- ${sharedCacheVolumeKey}:/home/${config.sshUser}/.docker-git/.cache
- ${sharedCodexVolumeKey}:${config.codexHome}-shared
${fragments.maybeBootstrapMounts}
- /var/run/docker.sock:/var/run/docker.sock
extra_hosts:
- "host.docker.internal:host-gateway"
dns:
- 8.8.8.8
- 8.8.4.4
Expand Down
Loading