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
15 changes: 15 additions & 0 deletions .changeset/entrypoint-tilde-clone-target.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@prover-coder-ai/docker-git": patch
---

Fix `docker-git clone` leaving the workspace `app` folder empty when `TARGET_DIR`
is a tilde path.

The generated entrypoint runs as `root` (sshd), so `$HOME` resolves to `/root`.
When a `~`/`~/...` `TARGET_DIR` reached the entrypoint (e.g. via the `TARGET_DIR`
env override), it was expanded against `$HOME`, resolving to `/root/app`. Because
the auto-clone runs as `su - <sshUser>`, cloning into the root-owned `/root/app`
failed with "permission denied", so the repository never landed in the prepared
home and the workspace `app` folder stayed empty. The tilde is now expanded
against the unprivileged user's home `/home/<sshUser>`, so the clone always lands
in the dev-owned workspace.
4 changes: 2 additions & 2 deletions packages/container/src/core/templates-entrypoint/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ REPO_REF="\${REPO_REF:-}"
FORK_REPO_URL="\${FORK_REPO_URL:-}"
${renderTargetDirDefault(config)}
if [[ "$TARGET_DIR" == "~" ]]; then
TARGET_DIR="$HOME"
TARGET_DIR="/home/${config.sshUser}"
elif [[ "$TARGET_DIR" == "~/"* ]]; then
TARGET_DIR="$HOME\${TARGET_DIR:1}"
TARGET_DIR="/home/${config.sshUser}\${TARGET_DIR:1}"
fi
Comment on lines 19 to 23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Исправление корректно решает проблему раскрытия тильды, но отсутствует документация согласно coding guidelines.

Техническое изменение верно: когда entrypoint запускается от root (через sshd), переменная $HOME указывает на /root, что приводило к проблеме с правами доступа при git clone. Явное указание /home/${config.sshUser} гарантирует, что пути с тильдой раскрываются в домашнюю директорию SSH пользователя, где у него есть права на запись.

Однако согласно coding guidelines, код должен содержать функциональные комментарии для документирования изменения:

// CHANGE: replace $HOME with explicit /home/${config.sshUser} in tilde expansion
// WHY: entrypoint runs as root (via sshd), so $HOME=/root; expanding ~ against $HOME
//      resolved TARGET_DIR to /root/app, which unprivileged SSH user cannot write to,
//      causing git clone to fail and leaving workspace app folder empty (issue `#413`)
// QUOTE(ТЗ): "Почему-то при docker-git clone не делается git clone в папку app"
// REF: issue-413
// SOURCE: n/a
// FORMAT THEOREM: ∀sshUser: expand("~") = /home/sshUser ∧ expand("~/p") = /home/sshUser/p
// PURITY: CORE (pure string template generation)
// INVARIANT: generated bash expands ~ to dev-owned home, never root's $HOME
// COMPLEXITY: O(1)

As per coding guidelines, all changes must include CHANGE/WHY/QUOTE/REF/SOURCE/FORMAT THEOREM markers for traceability and formal reasoning.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/lib/src/core/templates-entrypoint/base.ts` around lines 19 - 23, The
tilde expansion logic in the if/elif block for TARGET_DIR is missing required
documentation comments according to coding guidelines. Add functional comment
documentation above the TARGET_DIR assignment block that includes all required
markers: CHANGE (describing the replacement of $HOME with explicit
/home/${config.sshUser}), WHY (explaining that entrypoint runs as root via sshd
causing $HOME=/root and git clone failures), QUOTE (referencing the
requirement), REF (linking to issue `#413`), SOURCE, FORMAT THEOREM (defining the
expansion behavior), PURITY, INVARIANT (ensuring bash expands ~ to dev-owned
home, never root's), and COMPLEXITY. Follow the exact format shown in the review
comment with proper documentation structure.

Source: Coding guidelines

CLAUDE_AUTH_LABEL="\${CLAUDE_AUTH_LABEL:-}"
CODEX_AUTH_LABEL="\${CODEX_AUTH_LABEL:-}"
Expand Down
32 changes: 32 additions & 0 deletions packages/container/tests/core/templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,38 @@ describe("renderEntrypoint clone cache", () => {
})
})

describe("renderEntrypoint tilde target dir expansion", () => {
// CHANGE: assert runtime `~`/`~/...` TARGET_DIR overrides resolve to the dev-owned home
// WHY: the entrypoint runs as root (sshd), so `$HOME` is /root; expanding a tilde TARGET_DIR
// against `$HOME` resolved the clone target to /root/app, which `su - dev` cannot write,
// so `git clone` failed and the workspace `app` folder stayed EMPTY (issue #413)
// QUOTE(ТЗ): "Почему-то при docker-git clone не делается git clone в папку app"
// REF: issue-413
// FORMAT THEOREM: expand("~") = /home/<sshUser> ∧ expand("~/p") = /home/<sshUser>/p
it("expands a bare `~` against the dev home, not root's $HOME", () => {
const entrypoint = renderEntrypoint(makeTemplateConfig({ sshUser: "dev" }))

expect(entrypoint).toContain('if [[ "$TARGET_DIR" == "~" ]]; then')
expect(entrypoint).toContain('TARGET_DIR="/home/dev"')
expect(entrypoint).toContain('TARGET_DIR="/home/dev${TARGET_DIR:1}"')
expect(entrypoint).not.toContain('TARGET_DIR="$HOME"')
expect(entrypoint).not.toContain('TARGET_DIR="$HOME${TARGET_DIR:1}"')
})

it("expands the tilde against the configured ssh user for generated configs", () => {
fc.assert(
fc.property(generatedTemplateConfigArbitrary, (config) => {
const entrypoint = renderEntrypoint(config)

expect(entrypoint).toContain(`TARGET_DIR="/home/${config.sshUser}"`)
expect(entrypoint).toContain(`TARGET_DIR="/home/${config.sshUser}\${TARGET_DIR:1}"`)
expect(entrypoint).not.toContain('TARGET_DIR="$HOME"')
expect(entrypoint).not.toContain('TARGET_DIR="$HOME${TARGET_DIR:1}"')
})
)
})
})

describe("renderEntrypointGitHooks", () => {
it("installs pre-push protection checks, plan sync, and a global git post-push runtime", () => {
const hooks = renderEntrypointGitHooks()
Expand Down