Skip to content

Commit eb98254

Browse files
konardclaude
andcommitted
fix(lint): resolve pre-existing lint errors blocking CI
- Replace Effect.catchAll with explicit Effect.catchTags in docker.ts - Replace `as const` casts with `satisfies` in create-project.ts - Replace spread in Array.push with for-of loop - Use pipe-style Effect.map to avoid unicorn/no-array-callback-reference - Fix test lint: extract hardcoded IPs, remove generator-without-yield, extract nested ternary, fix TS2554 argument count Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92cac6f commit eb98254

8 files changed

Lines changed: 125 additions & 50 deletions

File tree

packages/app/src/lib/shell/command-runner.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,14 @@ export const runCommandWithCapturedOutput = <E>(
134134
[
135135
collectStreamText(process.stdout),
136136
collectStreamText(process.stderr),
137-
Effect.map(process.exitCode, (value) => Number(value))
137+
process.exitCode.pipe(Effect.map(Number))
138138
],
139139
{ concurrency: "unbounded" }
140140
)
141141
)
142142
yield* _(
143143
ensureExitCode(exitCode, okExitCodes, (numericExitCode) =>
144-
onFailure(numericExitCode, combineCommandOutput(stdout, stderr))
145-
)
144+
onFailure(numericExitCode, combineCommandOutput(stdout, stderr)))
146145
)
147146
})
148147
)

packages/app/src/lib/shell/docker.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { ExitCode } from "@effect/platform/CommandExecutor"
55
import type { PlatformError } from "@effect/platform/Error"
66
import { Duration, Effect, pipe, Schedule } from "effect"
77

8-
import { runCommandCapture, runCommandExitCode, runCommandWithCapturedOutput, runCommandWithExitCodes } from "./command-runner.js"
8+
import {
9+
runCommandCapture,
10+
runCommandExitCode,
11+
runCommandWithCapturedOutput,
12+
runCommandWithExitCodes
13+
} from "./command-runner.js"
914
import { composeSpec, resolveDockerComposeEnv } from "./docker-compose-env.js"
1015
import { parseInspectNetworkEntry } from "./docker-inspect-parse.js"
1116
import { CommandFailedError, DockerCommandError } from "./errors.js"
@@ -345,7 +350,7 @@ export const runDockerInspectContainerRuntimeInfo = (
345350
(exitCode) => new DockerCommandError({ exitCode })
346351
),
347352
Effect.flatMap((output) => {
348-
const [status, projectWorkingDir, composeService] = output.trim().replaceAll("\\t", "\t").split("\t")
353+
const [status, projectWorkingDir, composeService] = output.trim().replaceAll(String.raw`\t`, "\t").split("\t")
349354
if ((status?.trim() ?? "") !== "running") {
350355
return Effect.succeed(null)
351356
}
@@ -359,7 +364,11 @@ export const runDockerInspectContainerRuntimeInfo = (
359364
}))
360365
)
361366
}),
362-
Effect.catchAll(() => Effect.succeed(null))
367+
Effect.catchTags({
368+
DockerCommandError: () => Effect.succeed(null),
369+
SystemError: () => Effect.succeed(null),
370+
BadArgument: () => Effect.succeed(null)
371+
})
363372
)
364373

365374
// CHANGE: inspect the container IP address on the default `bridge` network

packages/app/src/lib/usecases/actions/create-project.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,24 @@ const resolveDockerIdentityClaims = (
268268
): ReadonlyArray<DockerIdentityClaim> => [
269269
{ namespace: "container", kind: "containerName", name: config.containerName },
270270
...(config.enableMcpPlaywright
271-
? [{ namespace: "container" as const, kind: "browserContainerName" as const, name: `${config.containerName}-browser` }]
271+
? [
272+
{
273+
namespace: "container",
274+
kind: "browserContainerName",
275+
name: `${config.containerName}-browser`
276+
} satisfies DockerIdentityClaim
277+
]
272278
: []),
273279
{ namespace: "composeProject", kind: "serviceName", name: resolveComposeProjectName(config) },
274280
{ namespace: "volume", kind: "volumeName", name: config.volumeName },
275281
...(config.enableMcpPlaywright
276-
? [{ namespace: "volume" as const, kind: "browserVolumeName" as const, name: `${config.volumeName}-browser` }]
282+
? [
283+
{
284+
namespace: "volume",
285+
kind: "browserVolumeName",
286+
name: `${config.volumeName}-browser`
287+
} satisfies DockerIdentityClaim
288+
]
277289
: []),
278290
{ namespace: "volume", kind: "bootstrapVolumeName", name: resolveProjectBootstrapVolumeName(config) }
279291
]
@@ -291,7 +303,15 @@ const deleteConflictingProjectsIfNeeded = (
291303

292304
const candidateClaims = resolveDockerIdentityClaims(config)
293305
const conflicts: Array<DockerIdentityConflict> = []
294-
const conflictingProjects = new Map<string, { readonly projectDir: string; readonly repoUrl: string; readonly containerName: string; readonly serviceName: string }>()
306+
const conflictingProjects = new Map<
307+
string,
308+
{
309+
readonly projectDir: string
310+
readonly repoUrl: string
311+
readonly containerName: string
312+
readonly serviceName: string
313+
}
314+
>()
295315

296316
for (const configPath of index.configPaths) {
297317
const status = yield* _(
@@ -309,8 +329,8 @@ const deleteConflictingProjectsIfNeeded = (
309329
const existingClaims = resolveDockerIdentityClaims(status.config.template)
310330
const sharedClaims = candidateClaims.flatMap((candidate) =>
311331
existingClaims.some(
312-
(existing) => existing.namespace === candidate.namespace && existing.name === candidate.name
313-
)
332+
(existing) => existing.namespace === candidate.namespace && existing.name === candidate.name
333+
)
314334
? [{ conflictingProjectDir: status.projectDir, kind: candidate.kind, name: candidate.name }]
315335
: []
316336
)
@@ -319,7 +339,9 @@ const deleteConflictingProjectsIfNeeded = (
319339
continue
320340
}
321341

322-
conflicts.push(...sharedClaims)
342+
for (const claim of sharedClaims) {
343+
conflicts.push(claim)
344+
}
323345
conflictingProjects.set(status.projectDir, {
324346
projectDir: status.projectDir,
325347
repoUrl: status.config.template.repoUrl,

packages/app/tests/docker-git/create-project-identity-conflict.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import * as FileSystem from "@effect/platform/FileSystem"
2-
import * as Path from "@effect/platform/Path"
31
import { NodeContext } from "@effect/platform-node"
42
import type { PlatformError } from "@effect/platform/Error"
3+
import * as FileSystem from "@effect/platform/FileSystem"
4+
import * as Path from "@effect/platform/Path"
55
import { describe, expect, it } from "@effect/vitest"
66
import { Effect } from "effect"
77
import { beforeEach, vi } from "vitest"
88

9-
import { defaultTemplateConfig, type CreateCommand } from "@lib/core/domain"
9+
import { type CreateCommand, defaultTemplateConfig } from "@lib/core/domain"
1010

1111
import { DockerIdentityConflictError } from "../../src/lib/shell/errors.js"
1212
import { createProject } from "../../src/lib/usecases/actions/create-project.js"
1313
import type { ProjectStatus } from "../../src/lib/usecases/projects-core.js"
1414

1515
const resolveSshPortMock = vi.hoisted(() => vi.fn((config: CreateCommand["config"]) => Effect.succeed(config)))
1616
const buildSshCommandMock = vi.hoisted(() => vi.fn(() => "ssh -p 2222 dev@localhost"))
17-
const getContainerIpIfInsideContainerMock = vi.hoisted(() => vi.fn(() => Effect.succeed(undefined)))
17+
const getContainerIpIfInsideContainerMock = vi.hoisted(() => vi.fn(() => Effect.void))
1818
const loadProjectIndexMock = vi.hoisted(() => vi.fn())
1919
const loadProjectStatusMock = vi.hoisted(() => vi.fn())
2020
const migrateProjectOrchLayoutMock = vi.hoisted(() => vi.fn(() => Effect.void))

packages/app/tests/docker-git/docker-runtime-info.test.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ type RecordedCommand = {
1313
readonly args: ReadonlyArray<string>
1414
}
1515

16+
// eslint-disable-next-line sonarjs/no-hardcoded-ip
17+
const TEST_BRIDGE_IP = "172.17.0.15"
18+
// eslint-disable-next-line sonarjs/no-hardcoded-ip
19+
const TEST_PROJECT_IP = "10.88.0.4"
20+
1621
const encode = (value: string): Uint8Array => new TextEncoder().encode(value)
1722

1823
const isRuntimeInspect = (command: RecordedCommand): boolean =>
@@ -27,24 +32,29 @@ const isIpInspect = (command: RecordedCommand): boolean =>
2732
command.args[1] === "-f" &&
2833
(command.args[2] ?? "").includes("NetworkSettings.Networks")
2934

35+
const resolveStdoutText = (
36+
invocation: RecordedCommand,
37+
outputs: { readonly runtimeOutput: string; readonly ipOutput: string }
38+
): string => {
39+
if (isRuntimeInspect(invocation)) return outputs.runtimeOutput
40+
if (isIpInspect(invocation)) return outputs.ipOutput
41+
return ""
42+
}
43+
3044
const makeFakeExecutor = (outputs: {
3145
readonly runtimeOutput: string
3246
readonly ipOutput: string
3347
}): CommandExecutor.CommandExecutor => {
34-
const start = (command: Command.Command): Effect.Effect<CommandExecutor.Process, never> =>
35-
Effect.gen(function*(_) {
48+
const start = (command: Command.Command): Effect.Effect<CommandExecutor.Process> =>
49+
Effect.sync(() => {
3650
const flattened = Command.flatten(command)
37-
const last = flattened[flattened.length - 1]!
51+
const last = flattened.at(-1)!
3852
const invocation: RecordedCommand = {
3953
command: last.command,
4054
args: last.args
4155
}
4256

43-
const stdoutText = isRuntimeInspect(invocation)
44-
? outputs.runtimeOutput
45-
: isIpInspect(invocation)
46-
? outputs.ipOutput
47-
: ""
57+
const stdoutText = resolveStdoutText(invocation, outputs)
4858

4959
const stdout = stdoutText.length === 0
5060
? Stream.empty
@@ -79,7 +89,7 @@ describe("runDockerInspectContainerRuntimeInfo", () => {
7989
Effect.gen(function*(_) {
8090
const executor = makeFakeExecutor({
8191
runtimeOutput: "running\\t/home/dev/.docker-git/test-owner/repo\\tdg-repo\n",
82-
ipOutput: "bridge=172.17.0.15\nproject=10.88.0.2\n"
92+
ipOutput: `bridge=${TEST_BRIDGE_IP}\nproject=10.88.0.2\n`
8393
})
8494

8595
const runtime = yield* _(
@@ -91,7 +101,7 @@ describe("runDockerInspectContainerRuntimeInfo", () => {
91101
expect(runtime).toEqual({
92102
containerName: "dg-repo",
93103
running: true,
94-
ipAddress: "172.17.0.15",
104+
ipAddress: TEST_BRIDGE_IP,
95105
projectWorkingDir: "/home/dev/.docker-git/test-owner/repo",
96106
composeService: "dg-repo"
97107
})
@@ -101,7 +111,7 @@ describe("runDockerInspectContainerRuntimeInfo", () => {
101111
Effect.gen(function*(_) {
102112
const executor = makeFakeExecutor({
103113
runtimeOutput: "running\t\t\n",
104-
ipOutput: "project=10.88.0.4\n"
114+
ipOutput: `project=${TEST_PROJECT_IP}\n`
105115
})
106116

107117
const runtime = yield* _(
@@ -113,7 +123,7 @@ describe("runDockerInspectContainerRuntimeInfo", () => {
113123
expect(runtime).toEqual({
114124
containerName: "dg-repo",
115125
running: true,
116-
ipAddress: "10.88.0.4",
126+
ipAddress: TEST_PROJECT_IP,
117127
projectWorkingDir: undefined,
118128
composeService: undefined
119129
})

packages/app/tests/docker-git/open-project.test.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,18 @@ import { describe, expect, it } from "@effect/vitest"
33
import { Effect } from "effect"
44

55
import type { ApiProjectDetails } from "../../src/docker-git/api-project-codec.js"
6-
import { openResolvedProjectSshEffect, resolveOpenProjectEffect, selectOpenProject } from "../../src/docker-git/open-project.js"
6+
import {
7+
openResolvedProjectSshEffect,
8+
resolveOpenProjectEffect,
9+
selectOpenProject
10+
} from "../../src/docker-git/open-project.js"
711
import { makeProjectItem } from "./fixtures/project-item.js"
812

13+
// eslint-disable-next-line sonarjs/no-hardcoded-ip
14+
const TEST_CONTAINER_IP = "172.17.0.15"
15+
// eslint-disable-next-line sonarjs/no-hardcoded-ip
16+
const TEST_CONTAINER_IP_ALT = "172.17.0.20"
17+
918
const defaultProject = {
1019
id: "/controller/org/repo",
1120
displayName: "org/repo",
@@ -46,7 +55,7 @@ describe("selectOpenProject", () => {
4655
Effect.gen(function*(_) {
4756
const item = makeProjectItem({
4857
projectDir: "/controller/org/repo/issue-7",
49-
sshCommand: "ssh -p 22 dev@172.17.0.20"
58+
sshCommand: `ssh -p 22 dev@${TEST_CONTAINER_IP_ALT}`
5059
})
5160
const events: Array<string> = []
5261

@@ -70,7 +79,7 @@ describe("selectOpenProject", () => {
7079
)
7180

7281
expect(events).toEqual([
73-
"log:Opening SSH: ssh -p 22 dev@172.17.0.20",
82+
`log:Opening SSH: ssh -p 22 dev@${TEST_CONTAINER_IP_ALT}`,
7483
"connect:/controller/org/repo/issue-7"
7584
])
7685
}))
@@ -117,8 +126,8 @@ describe("selectOpenProject", () => {
117126
})
118127
const preferred = makeProjectItem({
119128
...item,
120-
ipAddress: "172.17.0.15",
121-
sshCommand: "ssh -p 22 dev@172.17.0.15"
129+
ipAddress: TEST_CONTAINER_IP,
130+
sshCommand: `ssh -p 22 dev@${TEST_CONTAINER_IP}`
122131
})
123132
const events: Array<string> = []
124133

@@ -129,7 +138,7 @@ describe("selectOpenProject", () => {
129138
events.push(`log:${message}`)
130139
}),
131140
resolvePreferredItem: () => Effect.succeed(preferred),
132-
probeReady: (selected) => Effect.succeed(selected.ipAddress === "172.17.0.15"),
141+
probeReady: (selected) => Effect.succeed(selected.ipAddress === TEST_CONTAINER_IP),
133142
connect: (selected) =>
134143
Effect.sync(() => {
135144
events.push(`connect:${selected.sshCommand}`)
@@ -142,8 +151,8 @@ describe("selectOpenProject", () => {
142151
)
143152

144153
expect(events).toEqual([
145-
"log:Opening SSH: ssh -p 22 dev@172.17.0.15",
146-
"connect:ssh -p 22 dev@172.17.0.15"
154+
`log:Opening SSH: ssh -p 22 dev@${TEST_CONTAINER_IP}`,
155+
`connect:ssh -p 22 dev@${TEST_CONTAINER_IP}`
147156
])
148157
}))
149158

@@ -156,8 +165,8 @@ describe("selectOpenProject", () => {
156165
})
157166
const preferred = makeProjectItem({
158167
...item,
159-
ipAddress: "172.17.0.20",
160-
sshCommand: "ssh -p 22 dev@172.17.0.20"
168+
ipAddress: TEST_CONTAINER_IP_ALT,
169+
sshCommand: `ssh -p 22 dev@${TEST_CONTAINER_IP_ALT}`
161170
})
162171
const events: Array<string> = []
163172

@@ -168,7 +177,7 @@ describe("selectOpenProject", () => {
168177
events.push(`log:${message}`)
169178
}),
170179
resolvePreferredItem: () => Effect.succeed(preferred),
171-
probeReady: (selected) => Effect.succeed(selected.ipAddress !== "172.17.0.20"),
180+
probeReady: (selected) => Effect.succeed(selected.ipAddress !== TEST_CONTAINER_IP_ALT),
172181
connect: (selected) =>
173182
Effect.sync(() => {
174183
events.push(`connect:${selected.sshCommand}`)
@@ -272,7 +281,7 @@ describe("selectOpenProject", () => {
272281
Effect.succeed({
273282
containerName: "dg-openclaw_autodeployer",
274283
running: true,
275-
ipAddress: "172.17.0.15",
284+
ipAddress: TEST_CONTAINER_IP,
276285
projectWorkingDir: "/controller/telegramgpt/openclaw_autodeployer",
277286
composeService: "dg-openclaw_autodeployer"
278287
})

packages/lib/src/shell/command-runner.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,14 @@ export const runCommandWithCapturedOutput = <E>(
133133
[
134134
collectStreamText(process.stdout),
135135
collectStreamText(process.stderr),
136-
Effect.map(process.exitCode, (value) => Number(value))
136+
process.exitCode.pipe(Effect.map(Number))
137137
],
138138
{ concurrency: "unbounded" }
139139
)
140140
)
141141
yield* _(
142142
ensureExitCode(exitCode, okExitCodes, (numericExitCode) =>
143-
onFailure(numericExitCode, combineCommandOutput(stdout, stderr))
144-
)
143+
onFailure(numericExitCode, combineCommandOutput(stdout, stderr)))
145144
)
146145
})
147146
)

0 commit comments

Comments
 (0)