Skip to content

Commit d889d7e

Browse files
committed
fix(ci): deduplicate docker shell helpers
1 parent 91333c3 commit d889d7e

5 files changed

Lines changed: 77 additions & 102 deletions

File tree

packages/app/src/docker-git/cli/parser-open.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Either } from "effect"
22

33
import { type OpenCommand, type ParseError } from "@lib/core/domain"
44

5+
import { trimToUndefined } from "../../shared/trimmed-text.js"
56
import { parseRawOptions } from "./parser-options.js"
67

78
type OpenParts = {
@@ -26,11 +27,6 @@ const buildOpenCommand = (parts: OpenParts): OpenCommand => ({
2627
...(parts.projectDir === undefined ? {} : { projectDir: parts.projectDir })
2728
})
2829

29-
const normalizeSelector = (value: string | undefined): string | undefined => {
30-
const trimmed = value?.trim() ?? ""
31-
return trimmed.length > 0 ? trimmed : undefined
32-
}
33-
3430
// CHANGE: parse open as a distinct selector-based command
3531
// WHY: open must resolve existing projects by raw selector without tmux semantics
3632
// QUOTE(ТЗ): "open should parse to a distinct _tag: \"Open\" command"
@@ -46,12 +42,12 @@ export const parseOpen = (args: ReadonlyArray<string>): Either.Either<OpenComman
4642
return Either.flatMap(parseRawOptions(rest), (raw) =>
4743
Either.right(
4844
buildOpenCommand({
49-
...(normalizeSelector(raw.projectDir) === undefined
45+
...(trimToUndefined(raw.projectDir) === undefined
5046
? {}
51-
: { projectDir: normalizeSelector(raw.projectDir) }),
52-
...(normalizeSelector(raw.containerName ?? raw.repoUrl ?? positionalRef) === undefined
47+
: { projectDir: trimToUndefined(raw.projectDir) }),
48+
...(trimToUndefined(raw.containerName ?? raw.repoUrl ?? positionalRef) === undefined
5349
? {}
54-
: { projectRef: normalizeSelector(raw.containerName ?? raw.repoUrl ?? positionalRef) })
50+
: { projectRef: trimToUndefined(raw.containerName ?? raw.repoUrl ?? positionalRef) })
5551
})
5652
))
5753
}

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ import { runCommandCapture, runCommandWithCapturedOutput } from "./command-runne
77
import { composeSpec, resolveDockerComposeEnv } from "./docker-compose-env.js"
88
import { DockerCommandError } from "./errors.js"
99

10+
const buildComposeCommand = (
11+
cwd: string,
12+
args: ReadonlyArray<string>,
13+
env: Record<string, string>
14+
) => ({
15+
...composeSpec(cwd, args),
16+
...(Object.keys(env).length > 0 ? { env } : {})
17+
})
18+
1019
const runCompose = (
1120
cwd: string,
1221
args: ReadonlyArray<string>,
@@ -16,10 +25,7 @@ const runCompose = (
1625
const env = yield* _(resolveDockerComposeEnv(cwd))
1726
yield* _(
1827
runCommandWithCapturedOutput(
19-
{
20-
...composeSpec(cwd, args),
21-
...(Object.keys(env).length > 0 ? { env } : {})
22-
},
28+
buildComposeCommand(cwd, args, env),
2329
okExitCodes,
2430
(exitCode, output) => new DockerCommandError({ exitCode, ...(output.length > 0 ? { details: output } : {}) })
2531
)
@@ -35,10 +41,7 @@ const runComposeCapture = (
3541
const env = yield* _(resolveDockerComposeEnv(cwd))
3642
return yield* _(
3743
runCommandCapture(
38-
{
39-
...composeSpec(cwd, args),
40-
...(Object.keys(env).length > 0 ? { env } : {})
41-
},
44+
buildComposeCommand(cwd, args, env),
4245
okExitCodes,
4346
(exitCode) => new DockerCommandError({ exitCode })
4447
)

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

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,39 @@ import { Effect } from "effect"
66
import { runCommandCapture, runCommandExitCode, runCommandWithExitCodes } from "./command-runner.js"
77
import { DockerCommandError } from "./errors.js"
88

9-
export const runDockerNetworkConnectBridge = (
9+
const runDockerNetworkCommand = (
1010
cwd: string,
11-
containerName: string
11+
args: ReadonlyArray<string>
1212
): Effect.Effect<void, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
13+
runCommandWithExitCodes(
14+
{
15+
cwd,
16+
command: "docker",
17+
args
18+
},
19+
[Number(ExitCode(0))],
20+
(exitCode) => new DockerCommandError({ exitCode })
21+
)
22+
23+
const runDockerNetworkCapture = (
24+
cwd: string,
25+
args: ReadonlyArray<string>
26+
): Effect.Effect<string, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
1327
runCommandCapture(
1428
{
1529
cwd,
1630
command: "docker",
17-
args: ["network", "connect", "bridge", containerName]
31+
args
1832
},
1933
[Number(ExitCode(0))],
2034
(exitCode) => new DockerCommandError({ exitCode })
21-
).pipe(Effect.asVoid)
35+
)
36+
37+
export const runDockerNetworkConnectBridge = (
38+
cwd: string,
39+
containerName: string
40+
): Effect.Effect<void, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
41+
runDockerNetworkCapture(cwd, ["network", "connect", "bridge", containerName]).pipe(Effect.asVoid)
2242

2343
export const runDockerNetworkExists = (
2444
cwd: string,
@@ -34,44 +54,20 @@ export const runDockerNetworkCreateBridge = (
3454
cwd: string,
3555
networkName: string
3656
): Effect.Effect<void, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
37-
runCommandWithExitCodes(
38-
{
39-
cwd,
40-
command: "docker",
41-
args: ["network", "create", "--driver", "bridge", networkName]
42-
},
43-
[Number(ExitCode(0))],
44-
(exitCode) => new DockerCommandError({ exitCode })
45-
)
57+
runDockerNetworkCommand(cwd, ["network", "create", "--driver", "bridge", networkName])
4658

4759
export const runDockerNetworkCreateBridgeWithSubnet = (
4860
cwd: string,
4961
networkName: string,
5062
subnet: string
5163
): Effect.Effect<void, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
52-
runCommandWithExitCodes(
53-
{
54-
cwd,
55-
command: "docker",
56-
args: ["network", "create", "--driver", "bridge", "--subnet", subnet, networkName]
57-
},
58-
[Number(ExitCode(0))],
59-
(exitCode) => new DockerCommandError({ exitCode })
60-
)
64+
runDockerNetworkCommand(cwd, ["network", "create", "--driver", "bridge", "--subnet", subnet, networkName])
6165

6266
export const runDockerNetworkContainerCount = (
6367
cwd: string,
6468
networkName: string
6569
): Effect.Effect<number, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
66-
runCommandCapture(
67-
{
68-
cwd,
69-
command: "docker",
70-
args: ["network", "inspect", "-f", "{{len .Containers}}", networkName]
71-
},
72-
[Number(ExitCode(0))],
73-
(exitCode) => new DockerCommandError({ exitCode })
74-
).pipe(
70+
runDockerNetworkCapture(cwd, ["network", "inspect", "-f", "{{len .Containers}}", networkName]).pipe(
7571
Effect.map((output) => {
7672
const parsed = Number.parseInt(output.trim(), 10)
7773
return Number.isNaN(parsed) ? 0 : parsed
@@ -82,12 +78,4 @@ export const runDockerNetworkRemove = (
8278
cwd: string,
8379
networkName: string
8480
): Effect.Effect<void, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
85-
runCommandWithExitCodes(
86-
{
87-
cwd,
88-
command: "docker",
89-
args: ["network", "rm", networkName]
90-
},
91-
[Number(ExitCode(0))],
92-
(exitCode) => new DockerCommandError({ exitCode })
93-
)
81+
runDockerNetworkCommand(cwd, ["network", "rm", networkName])

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

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type * as CommandExecutor from "@effect/platform/CommandExecutor"
44
import type { PlatformError } from "@effect/platform/Error"
55
import { Effect, pipe } from "effect"
66

7+
import { trimToUndefined } from "../../shared/trimmed-text.js"
78
import { runCommandCapture } from "./command-runner.js"
89
import { parseInspectNetworkEntry } from "./docker-inspect-parse.js"
910
import { CommandFailedError, DockerCommandError } from "./errors.js"
@@ -16,10 +17,20 @@ export type DockerContainerRuntimeInfo = {
1617
readonly composeService?: string | undefined
1718
}
1819

19-
const parseOptionalInspectField = (value: string | undefined): string | undefined => {
20-
const trimmed = value?.trim() ?? ""
21-
return trimmed.length > 0 ? trimmed : undefined
22-
}
20+
const runDockerInspectValue = (
21+
cwd: string,
22+
containerName: string,
23+
format: string
24+
): Effect.Effect<string, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
25+
runCommandCapture(
26+
{
27+
cwd,
28+
command: "docker",
29+
args: ["inspect", "-f", format, containerName]
30+
},
31+
[Number(ExitCode(0))],
32+
(exitCode) => new DockerCommandError({ exitCode })
33+
)
2334

2435
export const runDockerExecExitCode = (
2536
cwd: string,
@@ -42,19 +53,10 @@ export const runDockerInspectContainerIp = (
4253
containerName: string
4354
): Effect.Effect<string, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
4455
pipe(
45-
runCommandCapture(
46-
{
47-
cwd,
48-
command: "docker",
49-
args: [
50-
"inspect",
51-
"-f",
52-
String.raw`{{range $k,$v := .NetworkSettings.Networks}}{{printf "%s=%s\n" $k $v.IPAddress}}{{end}}`,
53-
containerName
54-
]
55-
},
56-
[Number(ExitCode(0))],
57-
(exitCode) => new DockerCommandError({ exitCode })
56+
runDockerInspectValue(
57+
cwd,
58+
containerName,
59+
String.raw`{{range $k,$v := .NetworkSettings.Networks}}{{printf "%s=%s\n" $k $v.IPAddress}}{{end}}`
5860
),
5961
Effect.map((output) => {
6062
const lines = output
@@ -78,19 +80,10 @@ export const runDockerInspectContainerRuntimeInfo = (
7880
containerName: string
7981
): Effect.Effect<DockerContainerRuntimeInfo | null, PlatformError, CommandExecutor.CommandExecutor> =>
8082
pipe(
81-
runCommandCapture(
82-
{
83-
cwd,
84-
command: "docker",
85-
args: [
86-
"inspect",
87-
"-f",
88-
`{{.State.Status}}\t{{with index .Config.Labels "com.docker.compose.project.working_dir"}}{{.}}{{end}}\t{{with index .Config.Labels "com.docker.compose.service"}}{{.}}{{end}}`,
89-
containerName
90-
]
91-
},
92-
[Number(ExitCode(0))],
93-
(exitCode) => new DockerCommandError({ exitCode })
83+
runDockerInspectValue(
84+
cwd,
85+
containerName,
86+
`{{.State.Status}}\t{{with index .Config.Labels "com.docker.compose.project.working_dir"}}{{.}}{{end}}\t{{with index .Config.Labels "com.docker.compose.service"}}{{.}}{{end}}`
9487
),
9588
Effect.flatMap((output) => {
9689
const [status, projectWorkingDir, composeService] = output.trim().replaceAll(String.raw`\t`, "\t").split("\t")
@@ -103,8 +96,8 @@ export const runDockerInspectContainerRuntimeInfo = (
10396
containerName,
10497
running: true,
10598
ipAddress,
106-
projectWorkingDir: parseOptionalInspectField(projectWorkingDir),
107-
composeService: parseOptionalInspectField(composeService)
99+
projectWorkingDir: trimToUndefined(projectWorkingDir),
100+
composeService: trimToUndefined(composeService)
108101
}))
109102
)
110103
}),
@@ -116,19 +109,10 @@ export const runDockerInspectContainerBridgeIp = (
116109
containerName: string
117110
): Effect.Effect<string, DockerCommandError | PlatformError, CommandExecutor.CommandExecutor> =>
118111
pipe(
119-
runCommandCapture(
120-
{
121-
cwd,
122-
command: "docker",
123-
args: [
124-
"inspect",
125-
"-f",
126-
"{{with (index .NetworkSettings.Networks \"bridge\")}}{{.IPAddress}}{{end}}",
127-
containerName
128-
]
129-
},
130-
[Number(ExitCode(0))],
131-
(exitCode) => new DockerCommandError({ exitCode })
112+
runDockerInspectValue(
113+
cwd,
114+
containerName,
115+
"{{with (index .NetworkSettings.Networks \"bridge\")}}{{.IPAddress}}{{end}}"
132116
),
133117
Effect.map((output) => output.trim())
134118
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const trimToUndefined = (value: string | undefined): string | undefined => {
2+
const trimmed = value?.trim() ?? ""
3+
return trimmed.length > 0 ? trimmed : undefined
4+
}

0 commit comments

Comments
 (0)