fix(docker): widen chown prune to the entire hermes-agent path
PR #2470 introduces a `:ro` mount for the `hermes-agent-src` named volume on the WebUI side of `docker-compose.{two,three}-container.yml`. The WebUI's docker_init.bash unconditionally runs `chown_home_hermeswebui` which walks `/home/hermeswebui` with `find -exec chown -h {} +`, pruning only `/home/hermeswebui/.hermes/hermes-agent/.git/objects` (the narrow #2237 fix for macOS bind mounts). With the new `:ro` mount, every other file inside the hermes-agent subtree is also on a read-only filesystem. `chown` returns `EROFS`, `find -exec ... +` propagates the non-zero exit, and the wrapping `chown_home_hermeswebui || error_exit "..."` under `set -e` kills the container before the WebUI server can run. Verified locally: $ /usr/bin/find /tmp/ftest -exec false {} + $ echo $? 1 So `find` does propagate `-exec` command failures, which the existing `|| error_exit` then catches. The WebUI never writes to the agent source — `uv pip install /home/hermeswebui/.hermes/hermes-agent` is a pure read. So aligning ownership inside the agent subtree was always a nicety, not a requirement. Widen the prune to skip the entire `/home/hermeswebui/.hermes/hermes-agent` path. This also subsumes the original #2237 case (the `.git/objects` packs are inside the now-pruned subtree) without needing a separate carve-out. Test updates: - Renamed `test_home_chown_skips_hermes_agent_git_objects` → `test_home_chown_skips_hermes_agent_subtree`, and pinned the broader prune target (`-path ".../hermes-agent" -prune`). - Added `test_home_chown_helper_documents_readonly_mount_compat` so a future maintainer narrowing the prune back to `.git/objects` (and re-introducing the EROFS failure mode) trips a regression. Verified: - `tests/test_issue2237_docker_chown_git_objects.py` 4/4 pass. - `tests/test_docker_docs_and_readonly.py` 9/9 pass. - Full suite: 5738 passed, 0 failed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5cc8b6c654
commit
2d66263a6c
2 files changed
+40
-4
No files matched your search
+10
-1
@@ -185,8 +185,17 @@ chown_home_hermeswebui() {
|
||||
# macOS Docker bind mounts can expose hermes-agent git object packs as
|
||||
# read-only host files. The runtime only needs to read those existing objects;
|
||||
# requiring chown on them makes startup fail before WebUI can run (#2237).
|
||||
#
|
||||
# Multi-container compose (#2470) additionally mounts the entire
|
||||
# hermes-agent-src volume read-only on the WebUI side because the WebUI only
|
||||
# reads it for `uv pip install`. On a :ro mount, chown returns EROFS for any
|
||||
# file inside the subtree, which would propagate to `set -e` and kill startup
|
||||
# before the WebUI can run. Either way, the WebUI never writes to the agent
|
||||
# source — prune the entire hermes-agent path from the chown walk so a
|
||||
# read-only or partially-read-only mount doesn't break the rest of the home
|
||||
# ownership alignment.
|
||||
find /home/hermeswebui \
|
||||
-path "/home/hermeswebui/.hermes/hermes-agent/.git/objects" -prune \
|
||||
-path "/home/hermeswebui/.hermes/hermes-agent" -prune \
|
||||
-o -exec chown -h "${WANTED_UID}:${WANTED_GID}" {} +
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,40 @@ REPO = Path(__file__).resolve().parents[1]
|
||||
INIT_SCRIPT = (REPO / "docker_init.bash").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_home_chown_skips_hermes_agent_git_objects():
|
||||
def test_home_chown_skips_hermes_agent_subtree():
|
||||
"""The chown walk must skip the entire hermes-agent mount, not just
|
||||
.git/objects. The original #2237 issue was macOS bind mounts exposing
|
||||
read-only `.git/objects` packs; the post-v0.51.83 multi-container compose
|
||||
setup additionally mounts the whole hermes-agent source tree :ro on the
|
||||
WebUI side (#2470). Either failure mode breaks the chown walk under
|
||||
`set -e`; pruning the parent path covers both."""
|
||||
assert "chown_home_hermeswebui()" in INIT_SCRIPT
|
||||
assert "/home/hermeswebui/.hermes/hermes-agent/.git/objects" in INIT_SCRIPT
|
||||
assert "-prune" in INIT_SCRIPT
|
||||
# The prune target should be the whole hermes-agent subtree, not just
|
||||
# the inner `.git/objects` directory. The old narrower prune was
|
||||
# insufficient once the entire mount became :ro.
|
||||
assert "-path \"/home/hermeswebui/.hermes/hermes-agent\" -prune" in INIT_SCRIPT, (
|
||||
"chown walk must prune the entire hermes-agent path (not just "
|
||||
".git/objects) so a :ro multi-container mount doesn't EROFS-fail "
|
||||
"the chown."
|
||||
)
|
||||
assert 'chown -h "${WANTED_UID}:${WANTED_GID}"' in INIT_SCRIPT
|
||||
|
||||
|
||||
def test_home_chown_helper_documents_readonly_mount_compat():
|
||||
"""The prune comment must reference the :ro multi-container scenario so
|
||||
future maintainers don't narrow it back to just .git/objects (which would
|
||||
re-introduce the EROFS-on-startup failure for the multi-container setup)."""
|
||||
chown_fn_start = INIT_SCRIPT.index("chown_home_hermeswebui()")
|
||||
chown_fn_end = INIT_SCRIPT.index("\n}\n", chown_fn_start)
|
||||
fn_block = INIT_SCRIPT[chown_fn_start:chown_fn_end]
|
||||
|
||||
assert "read-only" in fn_block.lower() or "ro" in fn_block.lower(), (
|
||||
"chown_home_hermeswebui must document why the entire hermes-agent "
|
||||
"path is pruned (the :ro mount made the previous narrower prune "
|
||||
"insufficient)."
|
||||
)
|
||||
|
||||
|
||||
def test_root_init_uses_git_object_safe_chown_helper():
|
||||
root_start = INIT_SCRIPT.index('if [ "A${whoami}" == "Aroot" ]; then')
|
||||
root_restart = INIT_SCRIPT.index("exec su", root_start)
|
||||
|
||||
Reference in new issue
Block a user