From b6f8156d052fcb937b5a87bf2f2548390c950f88 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 12:47:12 +0000 Subject: [PATCH 1/3] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/ProverCoderAI/docker-git/issues/164 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..6e16ba2 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-03-22T12:47:12.050Z for PR creation at branch issue-164-56f6399aa833 for issue https://github.com/ProverCoderAI/docker-git/issues/164 \ No newline at end of file From 6d84a9d4838de4c9757f743df830114254f83014 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 12:54:48 +0000 Subject: [PATCH 2/3] feat(core): add apply-all command to bulk-update all docker-git projects - Add ApplyAllCommand domain type for the new CLI command - Create projects-apply-all.ts use case that iterates all discovered docker-git projects and runs docker compose up with port check - Register apply-all and update-all aliases in CLI parser - Wire command handler in program dispatcher - Update usage text with apply-all description - Add parser test for apply-all and update-all aliases - Individual project failures (port conflicts, env errors, missing configs) are logged as warnings without aborting the batch Fixes ProverCoderAI/docker-git#164 Co-Authored-By: Claude Opus 4.6 --- packages/app/src/docker-git/cli/parser.ts | 3 + packages/app/src/docker-git/cli/usage.ts | 2 + packages/app/src/docker-git/program.ts | 4 +- packages/app/tests/docker-git/parser.test.ts | 6 ++ packages/lib/src/core/domain.ts | 13 ++++ .../lib/src/usecases/projects-apply-all.ts | 64 +++++++++++++++++++ packages/lib/src/usecases/projects.ts | 1 + 7 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 packages/lib/src/usecases/projects-apply-all.ts diff --git a/packages/app/src/docker-git/cli/parser.ts b/packages/app/src/docker-git/cli/parser.ts index 7892f66..ade0d3c 100644 --- a/packages/app/src/docker-git/cli/parser.ts +++ b/packages/app/src/docker-git/cli/parser.ts @@ -22,6 +22,7 @@ const helpCommand: Command = { _tag: "Help", message: usageText } const menuCommand: Command = { _tag: "Menu" } const statusCommand: Command = { _tag: "Status" } const downAllCommand: Command = { _tag: "DownAll" } +const applyAllCommand: Command = { _tag: "ApplyAll" } const parseCreate = (args: ReadonlyArray): Either.Either => Either.flatMap(parseRawOptions(args), (raw) => buildCreateCommand(raw)) @@ -75,6 +76,8 @@ export const parseArgs = (args: ReadonlyArray): Either.Either Either.right(menuCommand)) ) .pipe( + Match.when("apply-all", () => Either.right(applyAllCommand)), + Match.when("update-all", () => Either.right(applyAllCommand)), Match.when("auth", () => parseAuth(rest)), Match.when("open", () => parseAttach(rest)), Match.when("apply", () => parseApply(rest)), diff --git a/packages/app/src/docker-git/cli/usage.ts b/packages/app/src/docker-git/cli/usage.ts index db3c53f..8debd26 100644 --- a/packages/app/src/docker-git/cli/usage.ts +++ b/packages/app/src/docker-git/cli/usage.ts @@ -19,6 +19,7 @@ docker-git session-gists backup [] [options] docker-git session-gists view docker-git session-gists download [options] docker-git ps +docker-git apply-all docker-git down-all docker-git auth [options] docker-git state [options] @@ -36,6 +37,7 @@ Commands: sessions List/kill/log container terminal processes session-gists Manage AI session backups via a private session repository (backup/list/view/download) ps, status Show docker compose status for all docker-git projects + apply-all Apply docker-git config and refresh all containers (docker compose up) down-all Stop all docker-git containers (docker compose down) auth Manage GitHub/Codex/Claude Code auth for docker-git state Manage docker-git state directory via git (sync across machines) diff --git a/packages/app/src/docker-git/program.ts b/packages/app/src/docker-git/program.ts index 67f5a6a..4369ed1 100644 --- a/packages/app/src/docker-git/program.ts +++ b/packages/app/src/docker-git/program.ts @@ -19,7 +19,7 @@ import { import type { AppError } from "@effect-template/lib/usecases/errors" import { renderError } from "@effect-template/lib/usecases/errors" import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright" -import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects" +import { applyAllDockerGitProjects, downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects" import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap" import { sessionGistBackup, @@ -80,6 +80,7 @@ type NonBaseCommand = Exclude< | { readonly _tag: "Create" } | { readonly _tag: "Status" } | { readonly _tag: "DownAll" } + | { readonly _tag: "ApplyAll" } | { readonly _tag: "Menu" } > @@ -141,6 +142,7 @@ export const program = pipe( Match.when({ _tag: "Create" }, (create) => createProject(create)), Match.when({ _tag: "Status" }, () => listProjectStatus), Match.when({ _tag: "DownAll" }, () => downAllDockerGitProjects), + Match.when({ _tag: "ApplyAll" }, () => applyAllDockerGitProjects), Match.when({ _tag: "Menu" }, () => runMenu), Match.orElse((cmd) => handleNonBaseCommand(cmd)) ) diff --git a/packages/app/tests/docker-git/parser.test.ts b/packages/app/tests/docker-git/parser.test.ts index 47d807b..419855c 100644 --- a/packages/app/tests/docker-git/parser.test.ts +++ b/packages/app/tests/docker-git/parser.test.ts @@ -265,6 +265,12 @@ describe("parseArgs", () => { expect(command.enableMcpPlaywright).toBe(true) })) + it.effect("parses apply-all and update-all commands", () => + Effect.sync(() => { + expect(parseOrThrow(["apply-all"])._tag).toBe("ApplyAll") + expect(parseOrThrow(["update-all"])._tag).toBe("ApplyAll") + })) + it.effect("parses down-all command", () => Effect.sync(() => { const command = parseOrThrow(["down-all"]) diff --git a/packages/lib/src/core/domain.ts b/packages/lib/src/core/domain.ts index 0c9f908..0767735 100644 --- a/packages/lib/src/core/domain.ts +++ b/packages/lib/src/core/domain.ts @@ -144,6 +144,18 @@ export interface ApplyCommand { readonly enableMcpPlaywright?: boolean | undefined } +// CHANGE: add apply-all command to apply docker-git config to every known project +// WHY: allow bulk-updating all containers in one command instead of running apply for each project manually +// QUOTE(ТЗ): "Сделать команду которая сама на все контейнеры применит новые настройки" +// REF: issue-164 +// PURITY: CORE +// EFFECT: n/a +// INVARIANT: applies to all discovered projects; individual failures do not abort the batch +// COMPLEXITY: O(1) +export interface ApplyAllCommand { + readonly _tag: "ApplyAll" +} + export interface HelpCommand { readonly _tag: "Help" readonly message: string @@ -322,6 +334,7 @@ export type Command = | ScrapCommand | McpPlaywrightUpCommand | ApplyCommand + | ApplyAllCommand | HelpCommand | StatusCommand | DownAllCommand diff --git a/packages/lib/src/usecases/projects-apply-all.ts b/packages/lib/src/usecases/projects-apply-all.ts new file mode 100644 index 0000000..ed4628c --- /dev/null +++ b/packages/lib/src/usecases/projects-apply-all.ts @@ -0,0 +1,64 @@ +import type { CommandExecutor } from "@effect/platform/CommandExecutor" +import type { PlatformError } from "@effect/platform/Error" +import type { FileSystem } from "@effect/platform/FileSystem" +import type { Path } from "@effect/platform/Path" +import { Effect, pipe } from "effect" + +import { ensureDockerDaemonAccess } from "../shell/docker.js" +import type { DockerAccessError, DockerCommandError } from "../shell/errors.js" +import { renderError } from "./errors.js" +import { forEachProjectStatus, loadProjectIndex, renderProjectStatusHeader } from "./projects-core.js" +import { runDockerComposeUpWithPortCheck } from "./projects-up.js" + +// CHANGE: provide an "apply all" helper for docker-git managed projects +// WHY: allow applying updated docker-git config to every known project in one command +// QUOTE(ТЗ): "Сделать команду которая сама на все контейнеры применит новые настройки" +// REF: issue-164 +// SOURCE: n/a +// FORMAT THEOREM: ∀p ∈ Projects: applyAll(p) → updated(p) ∨ warned(p) +// PURITY: SHELL +// EFFECT: Effect +// INVARIANT: continues applying to other projects when one docker compose up fails with DockerCommandError +// COMPLEXITY: O(n) where n = |projects| +export const applyAllDockerGitProjects: Effect.Effect< + void, + PlatformError | DockerAccessError, + FileSystem | Path | CommandExecutor +> = pipe( + ensureDockerDaemonAccess(process.cwd()), + Effect.zipRight(loadProjectIndex()), + Effect.flatMap((index) => + index === null + ? Effect.void + : forEachProjectStatus(index.configPaths, (status) => + pipe( + Effect.log(renderProjectStatusHeader(status)), + Effect.zipRight( + runDockerComposeUpWithPortCheck(status.projectDir).pipe( + Effect.catchTag("DockerCommandError", (error: DockerCommandError) => + Effect.logWarning( + `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.` + )), + Effect.catchTag("ConfigNotFoundError", (error) => + Effect.logWarning( + `Skipping ${status.projectDir}: ${renderError(error)}` + )), + Effect.catchTag("ConfigDecodeError", (error) => + Effect.logWarning( + `Skipping ${status.projectDir}: ${renderError(error)}` + )), + Effect.catchTag("PortProbeError", (error) => + Effect.logWarning( + `Skipping ${status.projectDir}: ${renderError(error)}` + )), + Effect.catchTag("FileExistsError", (error) => + Effect.logWarning( + `Skipping ${status.projectDir}: ${renderError(error)}` + )), + Effect.asVoid + ) + ) + )) + ), + Effect.asVoid +) diff --git a/packages/lib/src/usecases/projects.ts b/packages/lib/src/usecases/projects.ts index 0bad9f4..3f53ce4 100644 --- a/packages/lib/src/usecases/projects.ts +++ b/packages/lib/src/usecases/projects.ts @@ -7,6 +7,7 @@ export { type ProjectLoadError, type ProjectStatus } from "./projects-core.js" +export { applyAllDockerGitProjects } from "./projects-apply-all.js" export { deleteDockerGitProject } from "./projects-delete.js" export { downAllDockerGitProjects } from "./projects-down.js" export { listProjectItems, listProjects, listProjectSummaries, listRunningProjectItems } from "./projects-list.js" From e1f9b4b8950ba692fdcc7e5389b62870dd464a97 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 12:59:14 +0000 Subject: [PATCH 3/3] Revert "Initial commit with task details" This reverts commit b6f8156d052fcb937b5a87bf2f2548390c950f88. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 6e16ba2..0000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-03-22T12:47:12.050Z for PR creation at branch issue-164-56f6399aa833 for issue https://github.com/ProverCoderAI/docker-git/issues/164 \ No newline at end of file