Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/lib/src/core/docker-git-scripts.ts
Original file line number Diff line number Diff line change
@@ -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<string> = [
"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"
]
13 changes: 12 additions & 1 deletion packages/lib/src/core/templates-entrypoint/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions packages/lib/src/core/templates-entrypoint/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions packages/lib/src/core/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
13 changes: 13 additions & 0 deletions packages/lib/src/core/templates/dockerfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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} \
Expand All @@ -241,5 +253,6 @@ export const renderDockerfile = (config: TemplateConfig): string =>
renderDockerfileOpenCode(),
renderDockerfileGitleaks(),
renderDockerfileUsers(config),
renderDockerfileScripts(),
renderDockerfileWorkspace(config)
].join("\n\n")
40 changes: 40 additions & 0 deletions packages/lib/src/shell/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<void, PlatformError, FileSystem | Path>
// 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<void, PlatformError> =>
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(ТЗ): "создавать докер образы"
Expand Down Expand Up @@ -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
})