Skip to content

Commit a27d3c7

Browse files
authored
Merge pull request #102 from skulidropek/fix/ci-main-lint
fix(lib): restore main CI lint compliance
2 parents f1e7721 + 14b1761 commit a27d3c7

File tree

10 files changed

+274
-235
lines changed

10 files changed

+274
-235
lines changed

packages/lib/src/core/templates-entrypoint/claude.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import type { TemplateConfig } from "../domain.js"
22

33
const claudeAuthRootContainerPath = (sshUser: string): string => `/home/${sshUser}/.docker-git/.orch/auth/claude`
4-
const claudeHomeContainerPath = (sshUser: string): string => `/home/${sshUser}/.claude`
54

6-
const renderClaudeAuthConfig = (config: TemplateConfig): string =>
7-
String
8-
.raw`# Claude Code: expose CLAUDE_CONFIG_DIR for SSH sessions (OAuth cache lives under ~/.docker-git/.orch/auth/claude)
5+
const claudeAuthConfigTemplate = String
6+
.raw`# Claude Code: expose CLAUDE_CONFIG_DIR for SSH sessions (OAuth cache lives under ~/.docker-git/.orch/auth/claude)
97
CLAUDE_LABEL_RAW="$CLAUDE_AUTH_LABEL"
108
if [[ -z "$CLAUDE_LABEL_RAW" ]]; then
119
CLAUDE_LABEL_RAW="default"
@@ -18,7 +16,7 @@ if [[ -z "$CLAUDE_LABEL_NORM" ]]; then
1816
CLAUDE_LABEL_NORM="default"
1917
fi
2018
21-
CLAUDE_AUTH_ROOT="${claudeAuthRootContainerPath(config.sshUser)}"
19+
CLAUDE_AUTH_ROOT="__CLAUDE_AUTH_ROOT__"
2220
CLAUDE_CONFIG_DIR="$CLAUDE_AUTH_ROOT/$CLAUDE_LABEL_NORM"
2321
2422
# Backward compatibility: if default auth is stored directly under claude root, reuse it.
@@ -33,8 +31,8 @@ fi
3331
export CLAUDE_CONFIG_DIR
3432
3533
mkdir -p "$CLAUDE_CONFIG_DIR" || true
36-
CLAUDE_HOME_DIR="${claudeHomeContainerPath(config.sshUser)}"
37-
CLAUDE_HOME_JSON="/home/${config.sshUser}/.claude.json"
34+
CLAUDE_HOME_DIR="__CLAUDE_HOME_DIR__"
35+
CLAUDE_HOME_JSON="__CLAUDE_HOME_JSON__"
3836
mkdir -p "$CLAUDE_HOME_DIR" || true
3937
4038
docker_git_link_claude_file() {
@@ -86,6 +84,12 @@ docker_git_refresh_claude_oauth_token() {
8684
8785
docker_git_refresh_claude_oauth_token`
8886

87+
const renderClaudeAuthConfig = (config: TemplateConfig): string =>
88+
claudeAuthConfigTemplate
89+
.replaceAll("__CLAUDE_AUTH_ROOT__", claudeAuthRootContainerPath(config.sshUser))
90+
.replaceAll("__CLAUDE_HOME_DIR__", `/home/${config.sshUser}/.claude`)
91+
.replaceAll("__CLAUDE_HOME_JSON__", `/home/${config.sshUser}/.claude.json`)
92+
8993
const renderClaudeCliInstall = (): string =>
9094
String.raw`# Claude Code: ensure CLI command exists (non-blocking startup self-heal)
9195
docker_git_ensure_claude_cli() {
@@ -186,7 +190,8 @@ NODE
186190
docker_git_sync_claude_playwright_mcp
187191
chown 1000:1000 "$CLAUDE_SETTINGS_FILE" 2>/dev/null || true`
188192

189-
const entrypointClaudeGlobalPromptTemplate = String.raw`# Claude Code: managed global memory (CLAUDE.md is auto-loaded by Claude Code)
193+
const entrypointClaudeGlobalPromptTemplate = String
194+
.raw`# Claude Code: managed global memory (CLAUDE.md is auto-loaded by Claude Code)
190195
CLAUDE_GLOBAL_PROMPT_FILE="/home/__SSH_USER__/.claude/CLAUDE.md"
191196
CLAUDE_AUTO_SYSTEM_PROMPT="${"$"}{CLAUDE_AUTO_SYSTEM_PROMPT:-1}"
192197
CLAUDE_WORKSPACE_CONTEXT="Контекст workspace: repository"

packages/lib/src/shell/docker-auth.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,18 @@ export type DockerAuthSpec = {
2020
readonly interactive: boolean
2121
}
2222

23-
const resolveDefaultDockerUser = (): string | null => {
24-
const getUid = (process as { readonly getuid?: () => number }).getuid
25-
const getGid = (process as { readonly getgid?: () => number }).getgid
23+
export const resolveDefaultDockerUser = (): string | null => {
24+
const getUid = Reflect.get(process, "getuid")
25+
const getGid = Reflect.get(process, "getgid")
2626
if (typeof getUid !== "function" || typeof getGid !== "function") {
2727
return null
2828
}
29-
return `${getUid()}:${getGid()}`
29+
const uid = getUid.call(process)
30+
const gid = getGid.call(process)
31+
if (typeof uid !== "number" || typeof gid !== "number") {
32+
return null
33+
}
34+
return `${uid}:${gid}`
3035
}
3136

3237
const appendEnvArgs = (base: Array<string>, env: string | ReadonlyArray<string>) => {

packages/lib/src/usecases/actions/prepare-files.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,10 @@ export const migrateProjectOrchLayout = (
172172
globalConfig: CreateCommand["config"],
173173
resolveRootPath: (value: string) => string
174174
): Effect.Effect<void, PlatformError, FileSystem.FileSystem | Path.Path> =>
175-
migrateLegacyOrchLayout(
176-
baseDir,
177-
globalConfig.envGlobalPath,
178-
globalConfig.envProjectPath,
179-
globalConfig.codexAuthPath,
180-
resolveRootPath(".docker-git/.orch/auth/gh"),
181-
resolveRootPath(".docker-git/.orch/auth/claude")
182-
)
175+
migrateLegacyOrchLayout(baseDir, {
176+
envGlobalPath: globalConfig.envGlobalPath,
177+
envProjectPath: globalConfig.envProjectPath,
178+
codexAuthPath: globalConfig.codexAuthPath,
179+
ghAuthPath: resolveRootPath(".docker-git/.orch/auth/gh"),
180+
claudeAuthPath: resolveRootPath(".docker-git/.orch/auth/claude")
181+
})

packages/lib/src/usecases/auth-claude-oauth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as Fiber from "effect/Fiber"
66
import type * as Scope from "effect/Scope"
77
import * as Stream from "effect/Stream"
88

9+
import { resolveDefaultDockerUser } from "../shell/docker-auth.js"
910
import { AuthError, CommandFailedError } from "../shell/errors.js"
1011

1112
const oauthTokenEnvKey = "DOCKER_GIT_CLAUDE_OAUTH_TOKEN"
@@ -142,10 +143,9 @@ const buildDockerSetupTokenSpec = (
142143

143144
const buildDockerSetupTokenArgs = (spec: DockerSetupTokenSpec): ReadonlyArray<string> => {
144145
const base: Array<string> = ["run", "--rm", "-i", "-t", "-v", `${spec.hostPath}:${spec.containerPath}`]
145-
const getUid = (process as { readonly getuid?: () => number }).getuid
146-
const getGid = (process as { readonly getgid?: () => number }).getgid
147-
if (typeof getUid === "function" && typeof getGid === "function") {
148-
base.push("--user", `${getUid()}:${getGid()}`)
146+
const dockerUser = resolveDefaultDockerUser()
147+
if (dockerUser !== null) {
148+
base.push("--user", dockerUser)
149149
}
150150
for (const entry of spec.env) {
151151
const trimmed = entry.trim()

packages/lib/src/usecases/auth-claude.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,13 @@ const buildClaudeAuthEnv = (
102102
const ensureClaudeOrchLayout = (
103103
cwd: string
104104
): Effect.Effect<void, PlatformError, FileSystem.FileSystem | Path.Path> =>
105-
migrateLegacyOrchLayout(
106-
cwd,
107-
defaultTemplateConfig.envGlobalPath,
108-
defaultTemplateConfig.envProjectPath,
109-
defaultTemplateConfig.codexAuthPath,
110-
".docker-git/.orch/auth/gh",
111-
".docker-git/.orch/auth/claude"
112-
)
105+
migrateLegacyOrchLayout(cwd, {
106+
envGlobalPath: defaultTemplateConfig.envGlobalPath,
107+
envProjectPath: defaultTemplateConfig.envProjectPath,
108+
codexAuthPath: defaultTemplateConfig.codexAuthPath,
109+
ghAuthPath: ".docker-git/.orch/auth/gh",
110+
claudeAuthPath: ".docker-git/.orch/auth/claude"
111+
})
113112

114113
const renderClaudeDockerfile = (): string =>
115114
String.raw`FROM ubuntu:24.04

packages/lib/src/usecases/auth-codex.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,13 @@ const ensureCodexOrchLayout = (
3131
cwd: string,
3232
codexAuthPath: string
3333
): Effect.Effect<void, PlatformError, FileSystem.FileSystem | Path.Path> =>
34-
migrateLegacyOrchLayout(
35-
cwd,
36-
defaultTemplateConfig.envGlobalPath,
37-
defaultTemplateConfig.envProjectPath,
34+
migrateLegacyOrchLayout(cwd, {
35+
envGlobalPath: defaultTemplateConfig.envGlobalPath,
36+
envProjectPath: defaultTemplateConfig.envProjectPath,
3837
codexAuthPath,
39-
".docker-git/.orch/auth/gh",
40-
".docker-git/.orch/auth/claude"
41-
)
38+
ghAuthPath: ".docker-git/.orch/auth/gh",
39+
claudeAuthPath: ".docker-git/.orch/auth/claude"
40+
})
4241

4342
const renderCodexDockerfile = (): string =>
4443
String.raw`FROM ubuntu:24.04

packages/lib/src/usecases/auth-github.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,13 @@ const ensureGithubOrchLayout = (
3838
cwd: string,
3939
envGlobalPath: string
4040
): Effect.Effect<void, PlatformError, FileSystem.FileSystem | Path.Path> =>
41-
migrateLegacyOrchLayout(
42-
cwd,
41+
migrateLegacyOrchLayout(cwd, {
4342
envGlobalPath,
44-
defaultTemplateConfig.envProjectPath,
45-
defaultTemplateConfig.codexAuthPath,
46-
ghAuthRoot,
47-
".docker-git/.orch/auth/claude"
48-
)
43+
envProjectPath: defaultTemplateConfig.envProjectPath,
44+
codexAuthPath: defaultTemplateConfig.codexAuthPath,
45+
ghAuthPath: ghAuthRoot,
46+
claudeAuthPath: ".docker-git/.orch/auth/claude"
47+
})
4948

5049
const normalizeGithubLabel = (value: string | null): string => {
5150
const trimmed = value?.trim() ?? ""
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import type { PlatformError } from "@effect/platform/Error"
2+
import * as ParseResult from "@effect/schema/ParseResult"
3+
import * as Schema from "@effect/schema/Schema"
4+
import { Effect, Either } from "effect"
5+
6+
type CopyDecision = "skip" | "copy"
7+
type JsonPrimitive = boolean | number | string | null
8+
type JsonValue = JsonPrimitive | JsonRecord | ReadonlyArray<JsonValue>
9+
type JsonRecord = Readonly<{ [key: string]: JsonValue }>
10+
11+
const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(() =>
12+
Schema.Union(
13+
Schema.Null,
14+
Schema.Boolean,
15+
Schema.String,
16+
Schema.JsonNumber,
17+
Schema.Array(JsonValueSchema),
18+
Schema.Record({ key: Schema.String, value: JsonValueSchema })
19+
)
20+
)
21+
22+
const JsonRecordSchema: Schema.Schema<JsonRecord> = Schema.Record({
23+
key: Schema.String,
24+
value: JsonValueSchema
25+
})
26+
27+
const JsonRecordFromStringSchema = Schema.parseJson(JsonRecordSchema)
28+
const defaultEnvContents = "# docker-git env\n# KEY=value\n"
29+
const codexConfigMarker = "# docker-git codex config"
30+
31+
// CHANGE: enable web search tool in default Codex config (top-level)
32+
// WHY: avoid deprecated legacy flags and keep config minimal
33+
// QUOTE(ТЗ): "да убери легаси"
34+
// REF: user-request-2026-02-05-remove-legacy-web-search
35+
// SOURCE: n/a
36+
// FORMAT THEOREM: ∀c: config(c) -> web_search(c)="live"
37+
// PURITY: CORE
38+
// EFFECT: n/a
39+
// INVARIANT: default config stays deterministic
40+
// COMPLEXITY: O(1)
41+
export const defaultCodexConfig = [
42+
"# docker-git codex config",
43+
"model = \"gpt-5.3-codex\"",
44+
"model_reasoning_effort = \"xhigh\"",
45+
"personality = \"pragmatic\"",
46+
"",
47+
"approval_policy = \"never\"",
48+
"sandbox_mode = \"danger-full-access\"",
49+
"web_search = \"live\"",
50+
"",
51+
"[features]",
52+
"shell_snapshot = true",
53+
"multi_agent = true",
54+
"apps = true",
55+
"shell_tool = true"
56+
].join("\n")
57+
58+
export const resolvePathFromBase = (
59+
path: {
60+
readonly isAbsolute: (targetPath: string) => boolean
61+
readonly resolve: (...parts: ReadonlyArray<string>) => string
62+
},
63+
baseDir: string,
64+
targetPath: string
65+
): string => path.isAbsolute(targetPath) ? targetPath : path.resolve(baseDir, targetPath)
66+
67+
const isPermissionDeniedSystemError = (error: PlatformError): boolean =>
68+
error._tag === "SystemError" && error.reason === "PermissionDenied"
69+
70+
export const skipCodexConfigPermissionDenied = (
71+
configPath: string,
72+
error: PlatformError
73+
): Effect.Effect<void, PlatformError> =>
74+
isPermissionDeniedSystemError(error)
75+
? Effect.logWarning(
76+
`Skipped Codex config sync at ${configPath}: permission denied (${error.description ?? "no details"}).`
77+
)
78+
: Effect.fail(error)
79+
80+
const normalizeConfigText = (text: string): string =>
81+
text
82+
.replaceAll("\r\n", "\n")
83+
.trim()
84+
85+
export const shouldRewriteDockerGitCodexConfig = (existing: string): boolean => {
86+
const normalized = normalizeConfigText(existing)
87+
if (normalized.length === 0) {
88+
return true
89+
}
90+
if (!normalized.startsWith(codexConfigMarker)) {
91+
return false
92+
}
93+
return normalized !== normalizeConfigText(defaultCodexConfig)
94+
}
95+
96+
export const shouldCopyEnv = (sourceText: string, targetText: string): CopyDecision => {
97+
if (sourceText.trim().length === 0) {
98+
return "skip"
99+
}
100+
if (targetText.trim().length === 0) {
101+
return "copy"
102+
}
103+
if (targetText.trim() === defaultEnvContents.trim() && sourceText.trim() !== defaultEnvContents.trim()) {
104+
return "copy"
105+
}
106+
return "skip"
107+
}
108+
109+
export const parseJsonRecord = (text: string): Effect.Effect<JsonRecord | null> =>
110+
Either.match(ParseResult.decodeUnknownEither(JsonRecordFromStringSchema)(text), {
111+
onLeft: () => Effect.succeed(null),
112+
onRight: (record) => Effect.succeed(record)
113+
})
114+
115+
export const hasClaudeOauthAccount = (record: JsonRecord | null): boolean =>
116+
record !== null && typeof record["oauthAccount"] === "object" && record["oauthAccount"] !== null
117+
118+
export const hasClaudeCredentials = (record: JsonRecord | null): boolean =>
119+
record !== null && typeof record["claudeAiOauth"] === "object" && record["claudeAiOauth"] !== null
120+
121+
export const isGithubTokenKey = (key: string): boolean =>
122+
key === "GITHUB_TOKEN" || key === "GH_TOKEN" || key.startsWith("GITHUB_TOKEN__")
123+
124+
export type AuthPaths = {
125+
readonly envGlobalPath: string
126+
readonly envProjectPath: string
127+
readonly codexAuthPath: string
128+
}
129+
130+
export type AuthSyncSpec = {
131+
readonly sourceBase: string
132+
readonly targetBase: string
133+
readonly source: AuthPaths
134+
readonly target: AuthPaths
135+
}
136+
137+
export type LegacyOrchPaths = AuthPaths & {
138+
readonly ghAuthPath: string
139+
readonly claudeAuthPath: string
140+
}

0 commit comments

Comments
 (0)