Skip to content

Commit 09ca1dd

Browse files
committed
fix(shell): prepare host-backed controller builds
- Default controller runtime to host Docker with socket mount - Initialize the pinned Skiller submodule before controller revision/build - Include Skiller inputs in controller revision hashing
1 parent b555367 commit 09ca1dd

7 files changed

Lines changed: 113 additions & 18 deletions

File tree

docker-compose.api.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ services:
1111
environment:
1212
DOCKER_GIT_API_PORT: ${DOCKER_GIT_API_PORT:-3334}
1313
DOCKER_GIT_CONTROLLER_REV: ${DOCKER_GIT_CONTROLLER_REV:-unknown}
14-
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-isolated}
14+
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-host}
1515
DOCKER_HOST: ${DOCKER_GIT_CONTROLLER_DOCKER_HOST:-unix:///var/run/docker.sock}
1616
DOCKER_GIT_DOCKERD_TCP_HOST: ${DOCKER_GIT_DOCKERD_TCP_HOST:-tcp://0.0.0.0:2375}
1717
DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE: ${DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE:-host}
18-
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-tcp://host.docker.internal:2375}
18+
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-}
1919
DOCKER_GIT_PROJECT_SSH_BIND_HOST: ${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-0.0.0.0}
2020
DOCKER_GIT_PROJECTS_ROOT: ${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
2121
DOCKER_GIT_PROJECTS_ROOT_VOLUME: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
@@ -36,6 +36,7 @@ services:
3636
volumes:
3737
- docker_git_projects:${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
3838
- docker_git_docker_data:/var/lib/docker
39+
- /var/run/docker.sock:/var/run/docker.sock
3940
privileged: true
4041
cgroup: host
4142
init: true

docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ services:
1111
environment:
1212
DOCKER_GIT_API_PORT: ${DOCKER_GIT_API_PORT:-3334}
1313
DOCKER_GIT_CONTROLLER_REV: ${DOCKER_GIT_CONTROLLER_REV:-unknown}
14-
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-isolated}
14+
DOCKER_GIT_DOCKER_RUNTIME: ${DOCKER_GIT_DOCKER_RUNTIME:-host}
1515
DOCKER_HOST: ${DOCKER_GIT_CONTROLLER_DOCKER_HOST:-unix:///var/run/docker.sock}
1616
DOCKER_GIT_DOCKERD_TCP_HOST: ${DOCKER_GIT_DOCKERD_TCP_HOST:-tcp://0.0.0.0:2375}
1717
DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE: ${DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE:-host}
18-
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-tcp://host.docker.internal:2375}
18+
DOCKER_GIT_PROJECT_DOCKER_HOST: ${DOCKER_GIT_PROJECT_DOCKER_HOST:-}
1919
DOCKER_GIT_PROJECT_SSH_BIND_HOST: ${DOCKER_GIT_PROJECT_SSH_BIND_HOST:-0.0.0.0}
2020
DOCKER_GIT_PROJECTS_ROOT: ${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
2121
DOCKER_GIT_PROJECTS_ROOT_VOLUME: ${DOCKER_GIT_PROJECTS_ROOT_VOLUME:-docker-git-projects}
@@ -36,6 +36,7 @@ services:
3636
volumes:
3737
- docker_git_projects:${DOCKER_GIT_PROJECTS_ROOT:-/home/dev/.docker-git}
3838
- docker_git_docker_data:/var/lib/docker
39+
- /var/run/docker.sock:/var/run/docker.sock
3940
privileged: true
4041
cgroup: host
4142
init: true

packages/api/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ LABEL io.prover-coder-ai.docker-git.controller-build-skiller=$DOCKER_GIT_CONTROL
150150
ENV DOCKER_GIT_CONTROLLER_REV=$DOCKER_GIT_CONTROLLER_REV
151151
ENV DOCKER_GIT_CONTROLLER_BUILD_SKILLER=$DOCKER_GIT_CONTROLLER_BUILD_SKILLER
152152
ENV DOCKER_GIT_API_PORT=3334
153-
ENV DOCKER_GIT_DOCKER_RUNTIME=isolated
153+
ENV DOCKER_GIT_DOCKER_RUNTIME=host
154154
ENV DOCKER_HOST=unix:///var/run/docker.sock
155155
EXPOSE 3334
156156

packages/api/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ HTTP API for docker-git orchestration (projects, agents, logs/events, federation
55
This is now the intended controller plane:
66
- the API runs inside `docker-git-api`
77
- `.docker-git` state lives in the Docker volume `docker-git-projects`
8-
- the API starts an isolated Docker daemon inside the controller by default
9-
- child project containers no longer depend on host bind mounts for bootstrap auth/env
10-
- the host `/var/run/docker.sock` is not mounted into the controller or project containers
8+
- the API uses the host Docker daemon by default via `/var/run/docker.sock`
9+
- child project containers use host-backed Docker unless an explicit
10+
`DOCKER_GIT_PROJECT_DOCKER_HOST` is provided
1111

1212
## Runtime contract: host-Docker-backed
1313

@@ -61,12 +61,12 @@ Optional env:
6161

6262
- `DOCKER_GIT_API_BIND_HOST` (default: `127.0.0.1`)
6363
- `DOCKER_GIT_API_PORT` (default: `3334`)
64-
- `DOCKER_GIT_DOCKER_RUNTIME` (default: `isolated`; starts a managed Docker daemon in `docker-git-api`)
64+
- `DOCKER_GIT_DOCKER_RUNTIME` (default: `host`; set to `isolated` to use an embedded controller daemon)
6565
- `DOCKER_GIT_CONTROLLER_DOCKER_HOST` (default: `unix:///var/run/docker.sock`; socket path inside the controller)
6666
- `DOCKER_GIT_DOCKERD_TCP_HOST` (default: `tcp://0.0.0.0:2375`; reachable only inside Docker networks unless explicitly published)
6767
- `DOCKER_GIT_DOCKERD_DEFAULT_CGROUPNS_MODE` (default: `host`; keeps nested project containers compatible with cgroup v2 DinD)
68-
- `DOCKER_GIT_PROJECT_DOCKER_HOST` (default: `tcp://host.docker.internal:2375`; lets project containers use the isolated daemon)
69-
- `DOCKER_GIT_PROJECT_SSH_BIND_HOST` (default: `0.0.0.0` in controller mode; project SSH binds inside the isolated controller runtime)
68+
- `DOCKER_GIT_PROJECT_DOCKER_HOST` (default: empty; unset uses host socket in project containers when mounted)
69+
- `DOCKER_GIT_PROJECT_SSH_BIND_HOST` (default: `0.0.0.0`)
7070
- `DOCKER_GIT_PROJECTS_ROOT` (container path, default: `/home/dev/.docker-git`)
7171
- `DOCKER_GIT_PROJECTS_ROOT_VOLUME` (Docker volume name for controller state, default: `docker-git-projects`)
7272
- `DOCKER_GIT_FEDERATION_PUBLIC_ORIGIN` (optional public ActivityPub origin)

packages/api/scripts/start-controller.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
runtime="${DOCKER_GIT_DOCKER_RUNTIME:-isolated}"
4+
runtime="${DOCKER_GIT_DOCKER_RUNTIME:-host}"
55
docker_host="${DOCKER_HOST:-unix:///var/run/docker.sock}"
66
dockerd_pid=""
77

packages/app/src/docker-git/controller-compose.ts

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import type * as CommandExecutor from "@effect/platform/CommandExecutor"
12
import type { PlatformError } from "@effect/platform/Error"
23
import * as FileSystem from "@effect/platform/FileSystem"
34
import * as Path from "@effect/platform/Path"
45
import { Effect } from "effect"
56

67
import { computeLocalControllerRevision, controllerRevisionEnvKey } from "./controller-revision.js"
8+
import { runCommandWithCapturedOutput } from "./frontend-lib/shell/command-runner.js"
79
import { findExistingUpwards } from "./frontend-lib/usecases/path-helpers.js"
810
import type { ControllerBootstrapError } from "./host-errors.js"
911

@@ -18,6 +20,9 @@ export type ControllerComposeFiles = {
1820
readonly gpuOverlayPath: string | null
1921
}
2022

23+
const skillerSubmodulePath = "third_party/skiller-desktop-skills-manager"
24+
const skillerPackagePath = `${skillerSubmodulePath}/package.json`
25+
2126
const controllerBootstrapError = (message: string): ControllerBootstrapError => ({
2227
_tag: "ControllerBootstrapError",
2328
message
@@ -85,6 +90,85 @@ const mapComposePathError = (error: PlatformError): ControllerBootstrapError =>
8590
const mapControllerRevisionError = (error: PlatformError): ControllerBootstrapError =>
8691
controllerBootstrapError(`Failed to compute docker-git controller revision.\nDetails: ${String(error)}`)
8792

93+
const skillerSubmoduleCommand = [
94+
"submodule",
95+
"update",
96+
"--init",
97+
"--checkout",
98+
skillerSubmodulePath
99+
]
100+
101+
const formatSkillerSubmoduleFailure = (rootDir: string, exitCode: number, output: string): ControllerBootstrapError =>
102+
controllerBootstrapError(
103+
[
104+
"Failed to initialize Skiller submodule before building docker-git controller.",
105+
`Command: git ${skillerSubmoduleCommand.join(" ")}`,
106+
`Working directory: ${rootDir}`,
107+
`Exit code: ${exitCode}`,
108+
output.trim().length > 0 ? `Output:\n${output.trim()}` : "Output: n/a"
109+
].join("\n")
110+
)
111+
112+
const runSkillerSubmoduleInit = (
113+
rootDir: string
114+
): Effect.Effect<void, ControllerBootstrapError, CommandExecutor.CommandExecutor> =>
115+
runCommandWithCapturedOutput(
116+
{
117+
cwd: rootDir,
118+
command: "git",
119+
args: skillerSubmoduleCommand
120+
},
121+
[0],
122+
(exitCode, output) => formatSkillerSubmoduleFailure(rootDir, exitCode, output)
123+
).pipe(
124+
Effect.mapError((error): ControllerBootstrapError =>
125+
error._tag === "ControllerBootstrapError"
126+
? error
127+
: controllerBootstrapError(
128+
`Failed to initialize Skiller submodule before building docker-git controller.\nDetails: ${String(error)}`
129+
)
130+
)
131+
)
132+
133+
// CHANGE: initialize the pinned Skiller submodule before controller Docker builds
134+
// WHY: the API image copies `third_party`, so an empty submodule makes the patch/build step fail
135+
// QUOTE(ТЗ): "исправь проблему"
136+
// REF: user-message-2026-05-24-controller-skiller-submodule
137+
// SOURCE: n/a
138+
// FORMAT THEOREM: forall root: missing(root/skillerPackagePath) -> init(root) -> exists(root/skillerPackagePath) or typed error
139+
// PURITY: SHELL
140+
// EFFECT: Effect<void, ControllerBootstrapError, FileSystem | Path | CommandExecutor>
141+
// INVARIANT: controller revision and Docker build context are computed only after Skiller source exists
142+
// COMPLEXITY: O(1) filesystem probes plus O(git submodule update)
143+
export const ensureSkillerSubmoduleInitialized = (
144+
rootDir: string
145+
): Effect.Effect<void, ControllerBootstrapError, FileSystem.FileSystem | Path.Path | CommandExecutor.CommandExecutor> =>
146+
Effect.gen(function*(_) {
147+
const fs = yield* _(FileSystem.FileSystem)
148+
const path = yield* _(Path.Path)
149+
const packagePath = path.join(rootDir, skillerPackagePath)
150+
const existsBeforeInit = yield* _(fs.exists(packagePath).pipe(Effect.mapError(mapComposePathError)))
151+
if (existsBeforeInit) {
152+
return
153+
}
154+
155+
yield* _(Effect.log("Initializing Skiller submodule for docker-git controller build."))
156+
yield* _(runSkillerSubmoduleInit(rootDir))
157+
158+
const existsAfterInit = yield* _(fs.exists(packagePath).pipe(Effect.mapError(mapComposePathError)))
159+
if (existsAfterInit) {
160+
return
161+
}
162+
163+
return yield* _(
164+
Effect.fail(
165+
controllerBootstrapError(
166+
`Skiller submodule initialization completed but ${packagePath} was not found.`
167+
)
168+
)
169+
)
170+
})
171+
88172
export const composeFilesForMode = (
89173
composePath: string,
90174
gpuOverlayPath: string | null
@@ -126,13 +210,13 @@ type ComposePathAndGpuMode = {
126210
readonly buildSkillerMode: ControllerBuildSkillerMode
127211
}
128212

129-
const withComposePathAndGpuMode = <A>(
213+
const withComposePathAndGpuMode = <A, R>(
130214
effect: (input: ComposePathAndGpuMode) => Effect.Effect<
131215
A,
132216
ControllerBootstrapError,
133-
FileSystem.FileSystem | Path.Path
217+
R
134218
>
135-
): Effect.Effect<A, ControllerBootstrapError, FileSystem.FileSystem | Path.Path> =>
219+
): Effect.Effect<A, ControllerBootstrapError, FileSystem.FileSystem | Path.Path | R> =>
136220
composeFilePath().pipe(
137221
Effect.mapError(mapComposePathError),
138222
Effect.flatMap((composePath) =>
@@ -170,8 +254,16 @@ const persistControllerRevision = (revision: string): Effect.Effect<void> =>
170254
export const prepareControllerRevision = (): Effect.Effect<
171255
string,
172256
ControllerBootstrapError,
173-
FileSystem.FileSystem | Path.Path
257+
FileSystem.FileSystem | Path.Path | CommandExecutor.CommandExecutor
174258
> =>
175259
withComposePathAndGpuMode(({ buildSkillerMode, composePath, gpuMode }) =>
176-
computeControllerRevision(composePath, gpuMode, buildSkillerMode)
177-
).pipe(Effect.tap((revision) => persistControllerRevision(revision)))
260+
Effect.gen(function*(_) {
261+
const path = yield* _(Path.Path)
262+
if (buildSkillerMode === "1") {
263+
yield* _(ensureSkillerSubmoduleInitialized(path.dirname(composePath)))
264+
}
265+
return yield* _(computeControllerRevision(composePath, gpuMode, buildSkillerMode))
266+
})
267+
).pipe(
268+
Effect.tap((revision) => persistControllerRevision(revision))
269+
)

packages/app/src/docker-git/controller-revision.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const controllerRevisionInputs: ReadonlyArray<string> = [
99
"docker-compose.yml",
1010
"docker-compose.api.yml",
1111
"docker-compose.gpu.yml",
12+
".gitmodules",
1213
"package.json",
1314
"bun.lock",
1415
"bunfig.toml",

0 commit comments

Comments
 (0)