Skip to content

Commit 424f428

Browse files
bradymillerclaude
andauthored
fix(openemr-cmd): bind primary .git into worktree containers (#696)
## Summary - In a linked worktree, `.git` is a text pointer to an absolute host path inside the primary repo's `.git/worktrees/<slug>`. That path does not exist inside the worktree's container, so anything that shells out to git fails — npm husky postinstall, lint-staged, prek, composer scripts that read commit metadata. - Bind-mount the primary repo's `.git/` at the same absolute host path inside the worktree's openemr container so the existing pointer resolves transparently. RW because git writes refs, indexes, packed objects, and the worktree-specific state under `.git/worktrees/<slug>/`. - Restructure the auto-generated override so the `openemr` service block is always present (previously only emitted for `easy-redis`), with the redis-only `php.ini` volume appended into that same block to avoid duplicate YAML keys. Bumps `openemr-cmd` VERSION to 1.0.30. After this change, existing worktrees pick up the new mount via `openemr-cmd worktree regen <branch>` followed by container recreation (`worktree down --keep-volumes <branch>` + `worktree up <branch>`). ## Test plan - [ ] Generated `easy` override: single `openemr` service block with the `.git` mount; `mysql` and `couchdb` blocks unchanged - [ ] Generated `easy-redis` override: single `openemr` block contains both `.git` and `php.ini` volumes (no duplicate keys); redis-master / replicas / sentinels emit unchanged - [ ] Generated `easy-light` override: `openemr` block has only `.git`; no couchdb / redis blocks - [ ] All three parse as valid YAML (`python3 -c "import yaml; yaml.safe_load(...)"`) - [ ] `openemr-cmd worktree regen <branch>` rewrites an existing worktree's override correctly - [ ] After regen + recreate, `openemr-cmd worktree exec <branch> shell` then `git status` inside the container resolves the repo - [ ] `npm install` postinstall hooks (husky) succeed inside a worktree container - [ ] Existing symlink / path-traversal guards still fire for the prior mounts (lib_root, env_root, php.ini) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7b2c353 commit 424f428

1 file changed

Lines changed: 43 additions & 8 deletions

File tree

utilities/openemr-cmd/openemr-cmd

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ set -euo pipefail
1818
#################################################################################################
1919

2020
# Increment the version when modify script
21-
VERSION="1.0.29"
21+
VERSION="1.0.30"
2222

2323
# ============================================================================
2424
# WORKTREE STATE MANAGEMENT
@@ -242,6 +242,23 @@ wt_write_override() {
242242
fi
243243
fi
244244

245+
# Resolve the primary repo's .git directory. The worktree's own .git is a
246+
# text file containing 'gitdir: <primary>/.git/worktrees/<wt-dir-basename>',
247+
# where <wt-dir-basename> is the basename of the worktree directory (in
248+
# this script's naming, "openemr-wt-<slug>") -- not the branch slug alone.
249+
# Without exposing that absolute path inside the container, any tool that
250+
# shells out to git (npm postinstall hooks like husky, lint-staged, prek,
251+
# composer scripts that read commit metadata, etc.) sees a broken pointer
252+
# and fails. We bind-mount the primary's .git at the same absolute host
253+
# path inside the container so the existing pointer resolves transparently.
254+
local primary_root_real primary_git_real
255+
primary_root_real=$(realpath -e "${OPENEMR_ROOT}") \
256+
|| wt_die "Cannot resolve OPENEMR_ROOT: '${OPENEMR_ROOT}'"
257+
[[ -L "${primary_root_real}/.git" ]] && wt_die "Refusing: '${primary_root_real}/.git' is a symlink"
258+
primary_git_real=$(realpath -e "${primary_root_real}/.git") \
259+
|| wt_die "Cannot resolve primary .git: '${primary_root_real}/.git'"
260+
[[ -d "${primary_git_real}" ]] || wt_die "Primary .git is not a directory: '${primary_git_real}'"
261+
245262
local override_file="${dir}/${compose_subdir}/docker-compose.override.yml"
246263

247264
# Header + core volumes (all envs)
@@ -282,11 +299,34 @@ OVEOF
282299

283300
# Core services (all envs).
284301
# Ports are handled via WT_* variables with defaults in the base compose.
285-
# Override only needs: library bind-mount absolute paths (relative paths in
286-
# base compose break when compose file is symlinked into a worktree directory).
302+
# Override needs:
303+
# 1. The primary repo's .git directory bind-mounted at the same absolute
304+
# host path inside the container, so the worktree's .git pointer file
305+
# resolves and host-installed git tooling (husky, lint-staged, prek,
306+
# etc.) sees a valid repo. Mounted RW because git writes refs,
307+
# indexes, packed objects, and the worktree-specific state in
308+
# <primary>/.git/worktrees/<slug>/.
309+
# 2. Library bind-mount absolute paths (relative paths in the base
310+
# compose break when the compose file is loaded from a worktree
311+
# checkout that does not match the base compose's own directory).
287312
cat >> "${override_file}" <<OVEOF
288313
289314
services:
315+
openemr:
316+
volumes:
317+
- "${primary_git_real}:${primary_git_real}:rw"
318+
OVEOF
319+
320+
# easy-redis: php.ini is mounted directly into the openemr container.
321+
# Append to the openemr service block emitted just above.
322+
if [[ "${env}" == "easy-redis" ]]; then
323+
cat >> "${override_file}" <<OVEOF
324+
- "${env_root}/php.ini:/usr/local/etc/php/php.ini:ro"
325+
OVEOF
326+
fi
327+
328+
cat >> "${override_file}" <<OVEOF
329+
290330
mysql:
291331
volumes:
292332
- "${lib_root}/sql-ssl-certs-keys/easy/ca.pem:/etc/ssl/ca.pem:ro"
@@ -310,14 +350,9 @@ OVEOF
310350
# Redis-specific overrides.
311351
# The base compose uses hardcoded container_name which bypasses Docker project
312352
# namespacing — override with branch-scoped names so multiple worktrees coexist.
313-
# Also patch relative ./php.ini path to absolute.
314353
if [[ "${env}" == "easy-redis" ]]; then
315354
cat >> "${override_file}" <<OVEOF
316355
317-
openemr:
318-
volumes:
319-
- "${env_root}/php.ini:/usr/local/etc/php/php.ini:ro"
320-
321356
redis-master:
322357
container_name: openemr-${slug}-redis-master
323358

0 commit comments

Comments
 (0)