From f711dae5cfd06f6388ef5556c1c4a271ac496ab9 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 21:04:48 +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/176 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..70e7bc6 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-03-22T21:04:48.844Z for PR creation at branch issue-176-f004daa766ec for issue https://github.com/ProverCoderAI/docker-git/issues/176 \ No newline at end of file From 9264c07b4b8247837c2832794fbfd6c66159d706 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 21:16:12 +0000 Subject: [PATCH 2/3] feat(shell): auto-provision docker-git scripts inside generated containers Embeds docker-git scripts (session-backup, pre-commit/pre-push guards, knowledge splitter) into generated Docker images at /opt/docker-git/scripts/ so git hooks and docker-git modules work inside containers. Changes: - Dockerfile template: COPY scripts/ into /opt/docker-git/scripts/ - Entrypoint: symlink scripts into workspace after clone - Pre-push hook: resolve backup script from embedded or repo-local path - Shell: copy workspace scripts into project build context on create/update - Core: define dockerGitScriptNames constant for script inventory - Gitignore: exclude provisioned scripts/ from project state tracking Fixes ProverCoderAI/docker-git#176 Co-Authored-By: Claude Opus 4.6 --- packages/lib/src/core/docker-git-scripts.ts | 30 ++++++++++++++ .../lib/src/core/templates-entrypoint/git.ts | 13 +++++- .../src/core/templates-entrypoint/tasks.ts | 16 ++++++++ packages/lib/src/core/templates.ts | 3 ++ packages/lib/src/core/templates/dockerfile.ts | 13 ++++++ packages/lib/src/shell/files.ts | 40 +++++++++++++++++++ 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 packages/lib/src/core/docker-git-scripts.ts diff --git a/packages/lib/src/core/docker-git-scripts.ts b/packages/lib/src/core/docker-git-scripts.ts new file mode 100644 index 0000000..4a827c3 --- /dev/null +++ b/packages/lib/src/core/docker-git-scripts.ts @@ -0,0 +1,30 @@ +// CHANGE: define the set of docker-git scripts to embed in generated containers +// WHY: scripts (session-backup, pre-commit guards, knowledge splitter) must be available +// inside containers for git hooks and docker-git module usage +// REF: issue-176 +// SOURCE: n/a +// FORMAT THEOREM: ∀ name ∈ dockerGitScriptNames: name ∈ scripts/ ∧ referenced_by_hooks(name) +// PURITY: CORE (pure constant definition) +// INVARIANT: list is exhaustive for all scripts referenced by generated git hooks +// COMPLEXITY: O(1) + +/** + * Names of docker-git scripts that must be available inside generated containers. + * + * These scripts are referenced by git hooks (pre-push, pre-commit) and session + * backup workflows. They are copied into each project's build context under + * `scripts/` and embedded into the Docker image at `/opt/docker-git/scripts/`. + * + * @pure true + * @invariant ∀ name ∈ result: ∃ file(scripts/{name}) in docker-git workspace + */ +export const dockerGitScriptNames: ReadonlyArray = [ + "session-backup-gist.js", + "session-backup-repo.js", + "session-list-gists.js", + "pre-commit-secret-guard.sh", + "pre-push-knowledge-guard.js", + "split-knowledge-large-files.js", + "repair-knowledge-history.js", + "setup-pre-commit-hook.js" +] diff --git a/packages/lib/src/core/templates-entrypoint/git.ts b/packages/lib/src/core/templates-entrypoint/git.ts index e7d1de3..4e95ea8 100644 --- a/packages/lib/src/core/templates-entrypoint/git.ts +++ b/packages/lib/src/core/templates-entrypoint/git.ts @@ -261,9 +261,20 @@ cat <<'EOF' >> "$PRE_PUSH_HOOK" REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" cd "$REPO_ROOT" +# CHANGE: resolve session-backup script from /opt/docker-git/scripts (embedded) or repo-local fallback +# WHY: docker-git scripts are now embedded in the container image at /opt/docker-git/scripts +# REF: issue-176 if [ "${"${"}DOCKER_GIT_SKIP_SESSION_BACKUP:-}" != "1" ]; then if command -v gh >/dev/null 2>&1; then - node scripts/session-backup-gist.js --verbose || echo "[session-backup] Warning: session backup failed (non-fatal)" + BACKUP_SCRIPT="" + if [ -f /opt/docker-git/scripts/session-backup-gist.js ]; then + BACKUP_SCRIPT="/opt/docker-git/scripts/session-backup-gist.js" + elif [ -f "$REPO_ROOT/scripts/session-backup-gist.js" ]; then + BACKUP_SCRIPT="$REPO_ROOT/scripts/session-backup-gist.js" + fi + if [ -n "$BACKUP_SCRIPT" ]; then + node "$BACKUP_SCRIPT" --verbose || echo "[session-backup] Warning: session backup failed (non-fatal)" + fi fi fi EOF diff --git a/packages/lib/src/core/templates-entrypoint/tasks.ts b/packages/lib/src/core/templates-entrypoint/tasks.ts index 101e1b6..f81f8c9 100644 --- a/packages/lib/src/core/templates-entrypoint/tasks.ts +++ b/packages/lib/src/core/templates-entrypoint/tasks.ts @@ -186,10 +186,26 @@ const renderCloneBody = (config: TemplateConfig): string => renderCloneCacheFinalize(config) ].join("\n") +// CHANGE: provision docker-git scripts into workspace after successful clone +// WHY: git hooks reference scripts/ relative to repo root (e.g. "node scripts/session-backup-gist.js"); +// symlinking embedded /opt/docker-git/scripts makes them available in any cloned repo +// REF: issue-176 +// PURITY: SHELL +// INVARIANT: symlink created only when /opt/docker-git/scripts exists ∧ TARGET_DIR/scripts absent +// COMPLEXITY: O(1) const renderCloneFinalize = (): string => `if [[ "$CLONE_OK" -eq 1 ]]; then echo "[clone] done" touch "$CLONE_DONE_PATH" + + # Provision docker-git scripts into workspace (symlink if not already present) + if [[ -d /opt/docker-git/scripts && -n "$TARGET_DIR" && "$TARGET_DIR" != "/" ]]; then + if [[ ! -e "$TARGET_DIR/scripts" ]]; then + ln -s /opt/docker-git/scripts "$TARGET_DIR/scripts" || true + chown -h 1000:1000 "$TARGET_DIR/scripts" 2>/dev/null || true + echo "[scripts] provisioned docker-git scripts into workspace" + fi + fi else echo "[clone] failed" touch "$CLONE_FAIL_PATH" diff --git a/packages/lib/src/core/templates.ts b/packages/lib/src/core/templates.ts index 356215c..9e037d8 100644 --- a/packages/lib/src/core/templates.ts +++ b/packages/lib/src/core/templates.ts @@ -14,6 +14,9 @@ const renderGitignore = (): string => # NOTE: this directory is intended to be committed to the docker-git state repository. # It intentionally does not ignore .orch/ or auth files; keep the state repo private. +# docker-git scripts (copied from workspace, rebuilt on each project update) +scripts/ + # Volatile Codex artifacts (do not commit) .orch/auth/codex/log/ .orch/auth/codex/tmp/ diff --git a/packages/lib/src/core/templates/dockerfile.ts b/packages/lib/src/core/templates/dockerfile.ts index 9ff9c7e..6401de0 100644 --- a/packages/lib/src/core/templates/dockerfile.ts +++ b/packages/lib/src/core/templates/dockerfile.ts @@ -220,6 +220,18 @@ RUN printf "%s\\n" \ "AllowUsers ${config.sshUser}" \ > /etc/ssh/sshd_config.d/${config.sshUser}.conf` +// CHANGE: add docker-git scripts to Docker image at /opt/docker-git/scripts +// WHY: scripts (session-backup, pre-commit guards, knowledge splitter) must be available +// inside the container for git hooks and docker-git module usage +// REF: issue-176 +// PURITY: CORE (pure template renderer) +// INVARIANT: ∀ script ∈ scripts/: accessible(/opt/docker-git/scripts/{script}) +const renderDockerfileScripts = (): string => + `# docker-git scripts (hooks, session backup, knowledge guards) +COPY scripts/ /opt/docker-git/scripts/ +RUN find /opt/docker-git/scripts -type f -name '*.sh' -exec chmod +x {} + \ + && find /opt/docker-git/scripts -type f -name '*.js' -exec chmod +x {} +` + const renderDockerfileWorkspace = (config: TemplateConfig): string => `# Workspace path (supports root-level dirs like /repo) RUN mkdir -p ${config.targetDir} \ @@ -241,5 +253,6 @@ export const renderDockerfile = (config: TemplateConfig): string => renderDockerfileOpenCode(), renderDockerfileGitleaks(), renderDockerfileUsers(config), + renderDockerfileScripts(), renderDockerfileWorkspace(config) ].join("\n\n") diff --git a/packages/lib/src/shell/files.ts b/packages/lib/src/shell/files.ts index c527eef..fbf34c2 100644 --- a/packages/lib/src/shell/files.ts +++ b/packages/lib/src/shell/files.ts @@ -4,6 +4,7 @@ import type * as Path from "@effect/platform/Path" import { Effect, Match } from "effect" import { type TemplateConfig } from "../core/domain.js" +import { dockerGitScriptNames } from "../core/docker-git-scripts.js" import { resolveComposeResourceLimits, withDefaultResourceLimitIntent } from "../core/resource-limits.js" import { type FileSpec, planFiles } from "../core/templates.js" import { FileExistsError } from "./errors.js" @@ -102,6 +103,40 @@ const failOnExistingFiles = ( return Effect.fail(new FileExistsError({ path: firstPath })) } +// CHANGE: discover and copy docker-git scripts into the project build context +// WHY: scripts must be part of the Docker build context for COPY into the image +// REF: issue-176 +// PURITY: SHELL +// EFFECT: Effect +// INVARIANT: only copies scripts that exist in the workspace; missing scripts are skipped +// COMPLEXITY: O(|dockerGitScriptNames|) +const provisionDockerGitScripts = ( + fs: FileSystem.FileSystem, + path: Path.Path, + baseDir: string +): Effect.Effect => + Effect.gen(function*(_) { + const workspaceRoot = process.cwd() + const sourceScriptsDir = path.join(workspaceRoot, "scripts") + const targetScriptsDir = path.join(baseDir, "scripts") + + const sourceExists = yield* _(fs.exists(sourceScriptsDir)) + if (!sourceExists) { + return + } + + yield* _(fs.makeDirectory(targetScriptsDir, { recursive: true })) + + for (const scriptName of dockerGitScriptNames) { + const sourcePath = path.join(sourceScriptsDir, scriptName) + const targetPath = path.join(targetScriptsDir, scriptName) + const exists = yield* _(fs.exists(sourcePath)) + if (exists) { + yield* _(fs.copyFile(sourcePath, targetPath)) + } + } + }) + // CHANGE: write generated docker-git files to disk // WHY: isolate all filesystem effects in a thin shell // QUOTE(ТЗ): "создавать докер образы" @@ -150,5 +185,10 @@ export const writeProjectFiles = ( } } + // CHANGE: provision docker-git scripts into project build context + // WHY: Dockerfile COPY scripts/ requires scripts to be in the build context + // REF: issue-176 + yield* _(provisionDockerGitScripts(fs, path, baseDir)) + return created }) From e6a7161f6d6e80e96dcb422dfbe0a594292f46a0 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 21:17:46 +0000 Subject: [PATCH 3/3] Revert "Initial commit with task details" This reverts commit f711dae5cfd06f6388ef5556c1c4a271ac496ab9. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 70e7bc6..0000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-03-22T21:04:48.844Z for PR creation at branch issue-176-f004daa766ec for issue https://github.com/ProverCoderAI/docker-git/issues/176 \ No newline at end of file