Skip to content
Open
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
19 changes: 19 additions & 0 deletions .changeset/fix-claude-auth-login-probe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@prover-coder-ai/docker-git": patch
---

Fix `docker-git auth claude login` failing after a successful OAuth login.

After `claude setup-token` created and persisted the OAuth token, the login
command ran a verification probe (`claude -p ping`) and treated any non-zero
exit as a hard failure, exiting with code 1 even though the token was already
saved. A transient probe failure (network hiccup, rate limit, or token
propagation delay) would therefore discard an otherwise successful login.

The probe failure is now reported as a warning instead of an error, mirroring
`docker-git auth claude status`. The token is kept, and the user is advised to
re-check connectivity later with `docker-git auth claude status`.

Controller startup now also rejects `DOCKER_GIT_CONTROLLER_GPU=all` when
`docker-compose.gpu.yml` exists as a directory instead of a regular file,
matching the extra compose overlay invariant before invoking Docker Compose.
30 changes: 29 additions & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,32 @@ runs:
run: npm install -g node-gyp
- name: Install dependencies
shell: bash
run: bun install --frozen-lockfile
run: |
run_bun_install() {
local timeout_seconds=$((20 * 60))
bun install --frozen-lockfile &
local install_pid="$!"
(
sleep "$timeout_seconds"
echo "bun install exceeded 20 minutes; terminating" >&2
kill "$install_pid" 2>/dev/null || true
) &
local timeout_pid="$!"
local status=0
wait "$install_pid" || status="$?"
kill "$timeout_pid" 2>/dev/null || true
wait "$timeout_pid" 2>/dev/null || true
return "$status"
}

for attempt in 1 2 3; do
if run_bun_install; then
exit 0
fi
if [[ "$attempt" == "3" ]]; then
echo "bun install failed after retries" >&2
exit 1
fi
echo "bun install attempt ${attempt} failed; retrying..." >&2
sleep $((attempt * 2))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
done
21 changes: 21 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,27 @@ jobs:
- name: Login context notice
run: bash scripts/e2e/login-context.sh

e2e-auth-claude-login:
name: E2E (Claude auth login)
runs-on: ubuntu-latest
timeout-minutes: 40
env:
DOCKER_GIT_CONTROLLER_BUILD_SKILLER: "0"
DOCKER_GIT_E2E_REUSE_WORKSPACE_INSTALL: "1"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false
submodules: true
- name: Install dependencies
uses: ./.github/actions/setup
- name: Free Docker disk
uses: ./.github/actions/free-docker-disk
- name: Docker info
run: docker version && docker compose version
- name: Claude auth login warning path
run: bash scripts/e2e/auth-claude-login.sh

e2e-runtime-volumes-ssh:
name: E2E (Runtime volumes + SSH)
runs-on: ubuntu-latest
Expand Down
19 changes: 17 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"workspaces": [
"packages/api",
"packages/app",
"packages/auth-oauth",
"packages/container",
"packages/docker-git-session-sync",
"packages/lib",
Expand All @@ -15,13 +16,13 @@
],
"scripts": {
"setup:pre-commit-hook": "bun scripts/setup-pre-commit-hook.js",
"build": "bun run --filter @prover-coder-ai/docker-git-session-sync build && bun run --filter @prover-coder-ai/docker-git-terminal build && bun run --filter @prover-coder-ai/docker-git build",
"build": "bun run --filter @prover-coder-ai/docker-git-auth-oauth build && bun run --filter @prover-coder-ai/docker-git-session-sync build && bun run --filter @prover-coder-ai/docker-git-terminal build && bun run --filter @prover-coder-ai/docker-git build",
"api:build": "bun run --filter @effect-template/api build",
"api:start": "bun run --filter @effect-template/api start",
"api:dev": "bun run --filter @effect-template/api dev",
"api:test": "bun run --filter @effect-template/api test",
"api:typecheck": "bun run --filter @effect-template/api typecheck",
"check": "bun run --filter @prover-coder-ai/docker-git-session-sync check && bun run --filter @prover-coder-ai/docker-git-terminal check && bun run --filter @prover-coder-ai/docker-git-openapi check && bun run --filter @prover-coder-ai/docker-git check && bun run --filter @effect-template/lib typecheck",
"check": "bun run --filter @prover-coder-ai/docker-git-auth-oauth check && bun run --filter @prover-coder-ai/docker-git-session-sync check && bun run --filter @prover-coder-ai/docker-git-terminal check && bun run --filter @prover-coder-ai/docker-git-openapi check && bun run --filter @prover-coder-ai/docker-git check && bun run --filter @effect-template/lib typecheck",
"check:dist-deps-prune": "bun node_modules/@prover-coder-ai/dist-deps-prune/dist/main.js scan --package ./packages/app/package.json --prune-dev true --silent",
"changeset": "changeset",
"changeset-publish": "bun -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish",
Expand Down Expand Up @@ -52,8 +53,8 @@
"lint:effect": "bun run --filter @prover-coder-ai/docker-git-session-sync lint:effect && bun run --filter @prover-coder-ai/docker-git-terminal lint:effect && bun run --filter @prover-coder-ai/docker-git lint:effect && bun run --filter @prover-coder-ai/docker-git-container lint:effect && bun run --filter @effect-template/lib lint:effect && bun run --filter @effect-template/api lint:effect",
"effect:skill:init": "git submodule update --init --checkout third_party/effect-ts-skills",
"effect:skill:check": "bun run effect:skill:init && bash .codex/skills/effect-ts-guide/scripts/run-effect-ts-check.sh packages/app/src/web/api-create-project.ts packages/app/src/web/api-database.ts packages/app/src/web/api-http.ts packages/app/src/web/api-prompts.ts packages/app/src/web/api-skills.ts packages/app/src/web/api-tasks.ts packages/openapi/src --profile strict",
"test": "bun run --filter @prover-coder-ai/docker-git-session-sync test && bun run --filter @prover-coder-ai/docker-git-terminal test && bun run --filter @prover-coder-ai/docker-git test && bun run --filter @effect-template/lib test",
"typecheck": "bun run --filter @prover-coder-ai/docker-git-session-sync typecheck && bun run --filter @prover-coder-ai/docker-git-terminal typecheck && bun run --filter @prover-coder-ai/docker-git-openapi typecheck && bun run --filter @prover-coder-ai/docker-git typecheck && bun run --filter @effect-template/lib typecheck",
"test": "bun run --filter @prover-coder-ai/docker-git-auth-oauth test && bun run --filter @prover-coder-ai/docker-git-session-sync test && bun run --filter @prover-coder-ai/docker-git-terminal test && bun run --filter @prover-coder-ai/docker-git test && bun run --filter @effect-template/lib test",
"typecheck": "bun run --filter @prover-coder-ai/docker-git-auth-oauth typecheck && bun run --filter @prover-coder-ai/docker-git-session-sync typecheck && bun run --filter @prover-coder-ai/docker-git-terminal typecheck && bun run --filter @prover-coder-ai/docker-git-openapi typecheck && bun run --filter @prover-coder-ai/docker-git typecheck && bun run --filter @effect-template/lib typecheck",
"start": "bun run --cwd packages/app build:docker-git && bun ./packages/app/dist/src/docker-git/main.js"
},
"devDependencies": {
Expand Down
6 changes: 5 additions & 1 deletion packages/api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ RUN set -eu; \
FROM controller-base AS workspace-deps

COPY package.json bun.lock bunfig.toml tsconfig.base.json tsconfig.json ./
RUN mkdir -p packages/api packages/app packages/container packages/docker-git-session-sync packages/lib packages/openapi packages/terminal
RUN mkdir -p packages/api packages/app packages/auth-oauth packages/container packages/docker-git-session-sync packages/lib packages/openapi packages/terminal
COPY packages/api/package.json ./packages/api/package.json
COPY packages/app/package.json ./packages/app/package.json
COPY packages/auth-oauth/package.json ./packages/auth-oauth/package.json
COPY packages/container/package.json ./packages/container/package.json
COPY packages/docker-git-session-sync/package.json ./packages/docker-git-session-sync/package.json
COPY packages/lib/package.json ./packages/lib/package.json
Expand All @@ -92,6 +93,7 @@ RUN set -eu; \
--silent \
--filter @effect-template/api \
--filter @effect-template/lib \
--filter @prover-coder-ai/docker-git-auth-oauth \
--filter @prover-coder-ai/docker-git-container \
--filter @prover-coder-ai/docker-git-terminal \
--filter @prover-coder-ai/docker-git-session-sync; then \
Expand All @@ -109,12 +111,14 @@ FROM workspace-deps AS workspace-static
COPY patches ./patches
COPY scripts ./scripts
COPY packages/container ./packages/container
COPY packages/auth-oauth ./packages/auth-oauth
COPY packages/docker-git-session-sync ./packages/docker-git-session-sync
COPY packages/lib ./packages/lib
COPY packages/terminal ./packages/terminal

RUN bun run --cwd packages/docker-git-session-sync build
RUN bun run --cwd packages/terminal build
RUN bun run --cwd packages/auth-oauth build
RUN bun run --cwd packages/container build
RUN bun run --cwd packages/lib build

Expand Down
9 changes: 9 additions & 0 deletions packages/app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# @prover-coder-ai/docker-git

## 1.3.14

### Patch Changes

- chore: automated version bump

- Updated dependencies []:
- @prover-coder-ai/docker-git-session-sync@1.0.70

## 1.3.13

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prover-coder-ai/docker-git",
"version": "1.3.13",
"version": "1.3.14",
"description": "docker-git Bun and Gridland CLI plus browser frontend",
"main": "dist/src/docker-git/main.js",
"bin": {
Expand Down
137 changes: 137 additions & 0 deletions packages/app/src/docker-git/controller-compose-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import type { PlatformError } from "@effect/platform/Error"
import * as FileSystem from "@effect/platform/FileSystem"
import * as Path from "@effect/platform/Path"
import { Effect } from "effect"

import { type ControllerBootstrapError, controllerBootstrapError } from "./host-errors.js"

export const controllerGpuModeEnvKey = "DOCKER_GIT_CONTROLLER_GPU"
export const controllerComposeExtraFileEnvKey = "DOCKER_GIT_CONTROLLER_COMPOSE_EXTRA_FILE"

export type ControllerGpuMode = "none" | "all"

export type ControllerComposeFiles = {
readonly composePath: string
readonly extraOverlayPath: string | null
readonly gpuOverlayPath: string | null
readonly runtimeOverlayPath: string | null
}

const mapComposePathError = (error: PlatformError): ControllerBootstrapError =>
controllerBootstrapError(`Failed to resolve docker-compose.yml path.\nDetails: ${String(error)}`)

// CHANGE: add a verified controller compose overlay boundary for E2E/runtime callers
// WHY: temporary compose overrides must be part of the explicit docker compose argument vector
// QUOTE(ТЗ): n/a
// REF: issue-440-review-compose-overlay
// SOURCE: n/a
// FORMAT THEOREM: forall p: env(extra)=p and regular_file(resolve(p)) -> resolve(extra)=Some(resolve(p))
// PURITY: SHELL
// EFFECT: Effect<string | null, ControllerBootstrapError, FileSystem | Path>
// INVARIANT: non-empty extra compose env values either resolve to a regular file or fail before docker compose
// COMPLEXITY: O(1)
export const loadControllerComposeExtraPath = (): Effect.Effect<
string | null,
ControllerBootstrapError,
FileSystem.FileSystem | Path.Path
> =>
Effect.gen(function*(_) {
const raw = process.env[controllerComposeExtraFileEnvKey]?.trim() ?? ""
if (raw.length === 0) {
return null
}

const fs = yield* _(FileSystem.FileSystem)
const path = yield* _(Path.Path)
const extraOverlayPath = path.resolve(raw)
const isExists = yield* _(fs.exists(extraOverlayPath).pipe(Effect.mapError(mapComposePathError)))
if (!isExists) {
return yield* _(
Effect.fail(
controllerBootstrapError(
`${controllerComposeExtraFileEnvKey} points to ${extraOverlayPath}, but it was not found.`
)
)
)
}

const info = yield* _(fs.stat(extraOverlayPath).pipe(Effect.mapError(mapComposePathError)))
return info.type === "File"
? extraOverlayPath
: yield* _(
Effect.fail(
controllerBootstrapError(
`${controllerComposeExtraFileEnvKey} points to ${extraOverlayPath}, but it is not a regular file.`
)
)
)
})

export const composeFilesForMode = (
composePath: string,
gpuOverlayPath: string | null,
runtimeOverlayPath: string | null = null,
extraOverlayPath: string | null = null
): ReadonlyArray<string> => [
"-f",
composePath,
...(runtimeOverlayPath === null ? [] : ["-f", runtimeOverlayPath]),
...(gpuOverlayPath === null ? [] : ["-f", gpuOverlayPath]),
...(extraOverlayPath === null ? [] : ["-f", extraOverlayPath])
]

export const composeFilesToArgs = (composeFiles: ControllerComposeFiles): ReadonlyArray<string> =>
composeFilesForMode(
composeFiles.composePath,
composeFiles.gpuOverlayPath,
composeFiles.runtimeOverlayPath,
composeFiles.extraOverlayPath
)

// CHANGE: require the GPU compose overlay path to be a regular file
// WHY: docker compose accepts file arguments; accepting directories delays the failure past typed bootstrap validation
// QUOTE(ТЗ): "Исправь CI/CD и все правки от Rabbit Coder."
// REF: PR-440-CodeRabbit-f31ac99d
// SOURCE: n/a
// FORMAT THEOREM: forall p: gpu=all and regular_file(resolve(p)) -> resolve(gpu)=Some(resolve(p))
// PURITY: SHELL
// EFFECT: Effect<string, ControllerBootstrapError, FileSystem | Path>
// INVARIANT: GPU compose overlay resolution returns only existing regular files
// COMPLEXITY: O(1)
const requireGpuOverlayPath = (
composePath: string
): Effect.Effect<string, ControllerBootstrapError, FileSystem.FileSystem | Path.Path> =>
Effect.gen(function*(_) {
const fs = yield* _(FileSystem.FileSystem)
const path = yield* _(Path.Path)
const gpuOverlayPath = path.join(path.dirname(composePath), "docker-compose.gpu.yml")
const isExists = yield* _(fs.exists(gpuOverlayPath).pipe(Effect.mapError(mapComposePathError)))
if (!isExists) {
return yield* _(
Effect.fail(
controllerBootstrapError(`${controllerGpuModeEnvKey}=all requires ${gpuOverlayPath}, but it was not found.`)
)
)
}

const info = yield* _(fs.stat(gpuOverlayPath).pipe(Effect.mapError(mapComposePathError)))
return info.type === "File"
? gpuOverlayPath
: yield* _(
Effect.fail(
controllerBootstrapError(
`${controllerGpuModeEnvKey}=all requires ${gpuOverlayPath}, but it is not a regular file.`
)
)
)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export const composeFilesForGpuMode = (
composePath: string,
gpuMode: ControllerGpuMode
): Effect.Effect<ControllerComposeFiles, ControllerBootstrapError, FileSystem.FileSystem | Path.Path> =>
gpuMode === "none"
? Effect.succeed({ composePath, extraOverlayPath: null, gpuOverlayPath: null, runtimeOverlayPath: null })
: requireGpuOverlayPath(composePath).pipe(
Effect.map((gpuOverlayPath) => ({ composePath, extraOverlayPath: null, gpuOverlayPath, runtimeOverlayPath: null }))
)
Loading
Loading