|
| 1 | +import type { CommandExecutor } from "@effect/platform/CommandExecutor" |
| 2 | +import type { PlatformError } from "@effect/platform/Error" |
| 3 | +import type { FileSystem } from "@effect/platform/FileSystem" |
| 4 | +import type { Path } from "@effect/platform/Path" |
| 5 | +import { Effect, pipe } from "effect" |
| 6 | + |
| 7 | +import { ensureDockerDaemonAccess } from "../shell/docker.js" |
| 8 | +import type { DockerAccessError, DockerCommandError } from "../shell/errors.js" |
| 9 | +import { renderError } from "./errors.js" |
| 10 | +import { forEachProjectStatus, loadProjectIndex, renderProjectStatusHeader } from "./projects-core.js" |
| 11 | +import { runDockerComposeUpWithPortCheck } from "./projects-up.js" |
| 12 | + |
| 13 | +// CHANGE: provide an "apply all" helper for docker-git managed projects |
| 14 | +// WHY: allow applying updated docker-git config to every known project in one command |
| 15 | +// QUOTE(ТЗ): "Сделать команду которая сама на все контейнеры применит новые настройки" |
| 16 | +// REF: issue-164 |
| 17 | +// SOURCE: n/a |
| 18 | +// FORMAT THEOREM: ∀p ∈ Projects: applyAll(p) → updated(p) ∨ warned(p) |
| 19 | +// PURITY: SHELL |
| 20 | +// EFFECT: Effect<void, PlatformError | DockerAccessError, FileSystem | Path | CommandExecutor> |
| 21 | +// INVARIANT: continues applying to other projects when one docker compose up fails with DockerCommandError |
| 22 | +// COMPLEXITY: O(n) where n = |projects| |
| 23 | +export const applyAllDockerGitProjects: Effect.Effect< |
| 24 | + void, |
| 25 | + PlatformError | DockerAccessError, |
| 26 | + FileSystem | Path | CommandExecutor |
| 27 | +> = pipe( |
| 28 | + ensureDockerDaemonAccess(process.cwd()), |
| 29 | + Effect.zipRight(loadProjectIndex()), |
| 30 | + Effect.flatMap((index) => |
| 31 | + index === null |
| 32 | + ? Effect.void |
| 33 | + : forEachProjectStatus(index.configPaths, (status) => |
| 34 | + pipe( |
| 35 | + Effect.log(renderProjectStatusHeader(status)), |
| 36 | + Effect.zipRight( |
| 37 | + runDockerComposeUpWithPortCheck(status.projectDir).pipe( |
| 38 | + Effect.catchTag("DockerCommandError", (error: DockerCommandError) => |
| 39 | + Effect.logWarning( |
| 40 | + `apply failed for ${status.projectDir}: ${renderError(error)}. Check the project docker-compose config (e.g. env files for merge conflicts, port conflicts in docker-compose.yml config) and retry.` |
| 41 | + )), |
| 42 | + Effect.catchTag("ConfigNotFoundError", (error) => |
| 43 | + Effect.logWarning( |
| 44 | + `Skipping ${status.projectDir}: ${renderError(error)}` |
| 45 | + )), |
| 46 | + Effect.catchTag("ConfigDecodeError", (error) => |
| 47 | + Effect.logWarning( |
| 48 | + `Skipping ${status.projectDir}: ${renderError(error)}` |
| 49 | + )), |
| 50 | + Effect.catchTag("PortProbeError", (error) => |
| 51 | + Effect.logWarning( |
| 52 | + `Skipping ${status.projectDir}: ${renderError(error)}` |
| 53 | + )), |
| 54 | + Effect.catchTag("FileExistsError", (error) => |
| 55 | + Effect.logWarning( |
| 56 | + `Skipping ${status.projectDir}: ${renderError(error)}` |
| 57 | + )), |
| 58 | + Effect.asVoid |
| 59 | + ) |
| 60 | + ) |
| 61 | + )) |
| 62 | + ), |
| 63 | + Effect.asVoid |
| 64 | +) |
0 commit comments