Skip to content

Commit 8e79847

Browse files
committed
fix(app): satisfy lint after controller bootstrap refactor
1 parent b7c949e commit 8e79847

File tree

5 files changed

+213
-132
lines changed

5 files changed

+213
-132
lines changed

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import { Effect } from "effect"
77
import { runCommandCapture, runCommandExitCode } from "@lib/shell/command-runner"
88

99
import { type DockerNetworkIps, parseDockerNetworkIps, uniqueStrings } from "./controller-reachability.js"
10-
import { computeLocalControllerRevision, controllerRevisionEnvKey, parseControllerRevisionEnvOutput } from "./controller-revision.js"
10+
import {
11+
computeLocalControllerRevision,
12+
controllerRevisionEnvKey,
13+
parseControllerRevisionEnvOutput
14+
} from "./controller-revision.js"
1115
import type { ControllerBootstrapError } from "./host-errors.js"
1216

1317
export type ControllerRuntime =
@@ -182,24 +186,31 @@ export const controllerExists = (): Effect.Effect<boolean, ControllerBootstrapEr
182186
Effect.map((exitCode) => exitCode === 0)
183187
)
184188

185-
export const inspectControllerRevision = (): Effect.Effect<string | null, ControllerBootstrapError, ControllerRuntime> =>
189+
export const inspectControllerRevision = (): Effect.Effect<
190+
string | null,
191+
ControllerBootstrapError,
192+
ControllerRuntime
193+
> =>
186194
controllerExists().pipe(
187195
Effect.flatMap((exists) =>
188196
exists
189197
? runDockerCapture(
190-
["inspect", "-f", inspectEnvTemplate, controllerContainerName],
191-
`Failed to inspect env for ${controllerContainerName}`
192-
).pipe(
193-
Effect.map(parseControllerRevisionEnvOutput),
194-
Effect.orElseSucceed((): string | null => null)
195-
)
196-
: Effect.succeed<string | null>(null))
198+
["inspect", "-f", inspectEnvTemplate, controllerContainerName],
199+
`Failed to inspect env for ${controllerContainerName}`
200+
).pipe(
201+
Effect.map((output) => parseControllerRevisionEnvOutput(output)),
202+
Effect.orElseSucceed((): string | null => null)
203+
)
204+
: Effect.succeed<string | null>(null)
205+
)
197206
)
198207

199208
export const prepareLocalControllerRevision = (): Effect.Effect<string, ControllerBootstrapError, ControllerRuntime> =>
200209
Effect.gen(function*(_) {
201210
const composePath = yield* _(composeFilePath().pipe(Effect.mapError(mapComposePathError)))
202-
const revision = yield* _(computeLocalControllerRevision(composePath).pipe(Effect.mapError(mapControllerRevisionError)))
211+
const revision = yield* _(
212+
computeLocalControllerRevision(composePath).pipe(Effect.mapError(mapControllerRevisionError))
213+
)
203214
yield* _(
204215
Effect.sync(() => {
205216
process.env[controllerRevisionEnvKey] = revision
@@ -241,7 +252,7 @@ const connectControllerToNetworkBestEffort = (
241252

242253
return runDockerExitCodeCommand(["network", "connect", trimmed, controllerContainerName]).pipe(
243254
Effect.asVoid,
244-
Effect.orElseSucceed(() => undefined)
255+
Effect.orElseSucceed(() => {})
245256
)
246257
}
247258

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

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { createHash } from "node:crypto"
2-
31
import type { PlatformError } from "@effect/platform/Error"
42
import * as FileSystem from "@effect/platform/FileSystem"
53
import * as Path from "@effect/platform/Path"
@@ -23,59 +21,71 @@ const controllerRevisionInputs: ReadonlyArray<string> = [
2321
const skippedDirectoryNames = new Set([".git", "node_modules", "dist", "dist-test", ".turbo"])
2422
const skippedFileNames = new Set([".DS_Store"])
2523

26-
const hashMissingPath = (hash: ReturnType<typeof createHash>, relativePath: string): void => {
27-
hash.update(`missing:${relativePath}\n`)
24+
const appendChunk = (chunks: Array<string>, value: string): void => {
25+
chunks.push(value)
26+
}
27+
28+
const hashMissingPath = (chunks: Array<string>, relativePath: string): void => {
29+
appendChunk(chunks, `missing:${relativePath}\n`)
2830
}
2931

30-
const hashDirectoryMarker = (hash: ReturnType<typeof createHash>, relativePath: string): void => {
31-
hash.update(`dir:${relativePath}\n`)
32+
const hashDirectoryMarker = (chunks: Array<string>, relativePath: string): void => {
33+
appendChunk(chunks, `dir:${relativePath}\n`)
3234
}
3335

3436
const hashFileContents = (
35-
hash: ReturnType<typeof createHash>,
37+
chunks: Array<string>,
3638
relativePath: string,
3739
contents: string
3840
): void => {
39-
hash.update(`file:${relativePath}\n`)
40-
hash.update(contents)
41-
hash.update("\n")
41+
appendChunk(chunks, `file:${relativePath}\n`)
42+
appendChunk(chunks, contents)
43+
appendChunk(chunks, "\n")
4244
}
4345

46+
const bytesToHex = (bytes: Uint8Array): string =>
47+
Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")
48+
49+
const digestRevision = (chunks: ReadonlyArray<string>): Effect.Effect<string> =>
50+
Effect.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(chunks.join("")))).pipe(
51+
Effect.map((buffer) => bytesToHex(new Uint8Array(buffer)).slice(0, 16))
52+
)
53+
4454
const hashTree = (
4555
fs: FileSystem.FileSystem,
4656
path: Path.Path,
4757
rootDir: string,
4858
relativePath: string,
49-
hash: ReturnType<typeof createHash>
59+
chunks: Array<string>
5060
): Effect.Effect<void, PlatformError> =>
5161
Effect.gen(function*(_) {
5262
const absolutePath = path.join(rootDir, relativePath)
5363
const exists = yield* _(fs.exists(absolutePath))
5464
if (!exists) {
55-
hashMissingPath(hash, relativePath)
65+
hashMissingPath(chunks, relativePath)
5666
return
5767
}
5868

5969
const info = yield* _(fs.stat(absolutePath))
6070
if (info.type === "Directory") {
61-
hashDirectoryMarker(hash, relativePath)
62-
const entries = (yield* _(fs.readDirectory(absolutePath))).sort((left, right) => left.localeCompare(right))
71+
hashDirectoryMarker(chunks, relativePath)
72+
const entries = (yield* _(fs.readDirectory(absolutePath))).toSorted((left, right) => left.localeCompare(right))
6373
for (const entry of entries) {
6474
if (skippedDirectoryNames.has(entry) || skippedFileNames.has(entry)) {
6575
continue
6676
}
67-
yield* _(hashTree(fs, path, rootDir, path.join(relativePath, entry), hash))
77+
yield* _(hashTree(fs, path, rootDir, path.join(relativePath, entry), chunks))
6878
}
6979
return
7080
}
7181

7282
if (info.type === "File") {
7383
const contents = yield* _(fs.readFileString(absolutePath))
74-
hashFileContents(hash, relativePath, contents)
84+
hashFileContents(chunks, relativePath, contents)
7585
return
7686
}
7787

78-
hash.update(`other:${relativePath}:${info.type}\n`)
88+
appendChunk(chunks, `other:${relativePath}:${info.type}\n`)
7989
})
8090

8191
export const parseControllerRevisionEnvOutput = (output: string): string | null => {
@@ -114,11 +124,11 @@ export const computeLocalControllerRevision = (
114124
const fs = yield* _(FileSystem.FileSystem)
115125
const path = yield* _(Path.Path)
116126
const repoRoot = path.dirname(composePath)
117-
const hash = createHash("sha256")
127+
const chunks: Array<string> = []
118128

119129
for (const relativePath of controllerRevisionInputs) {
120-
yield* _(hashTree(fs, path, repoRoot, relativePath, hash))
130+
yield* _(hashTree(fs, path, repoRoot, relativePath, chunks))
121131
}
122132

123-
return hash.digest("hex").slice(0, 16)
133+
return yield* _(digestRevision(chunks))
124134
})

0 commit comments

Comments
 (0)