fix(docker): inject venv PATH into /etc/profile.d for login shells - #33179
fix(docker): inject venv PATH into /etc/profile.d for login shells#33179ugoenyioha wants to merge 4 commits into
Conversation
Introduces MATTERMOST_REPLY_MODE=auto alongside the existing 'thread'
and 'off' modes. When set to 'auto', the adapter checks the channel
type before deciding whether to thread:
- DMs (channel_type 'D'): flat replies (no root_id) — avoids the
'Invalid RootId parameter' 400 error that occurs when the adapter
sends root_id in DM channels where no thread root exists.
- Channels / groups ('O', 'P', 'G'): threaded replies (root_id set
to the user's original post) — keeps conversations organized in
busy channels without cluttering the main timeline.
Implementation:
- New _should_thread(chat_id, reply_to) async method replaces all
inline 'if reply_to and self._reply_mode == "thread"' checks.
- _channel_type_cache dict avoids repeated /channels/{id} API calls
for the same channel within a session.
- get_chat_info() populates the cache on first lookup.
- All four send paths (send, _send_url_as_file, _send_local_file,
send_multiple_images) updated to use _should_thread.
Existing 'thread' and 'off' modes are unchanged.
s6-overlay v3 strips the container's environment before execing the main program (CMD). This causes all Kubernetes-injected env vars (MATTERMOST_TOKEN, MATTERMOST_URL, HERMES_HOME, HOME, API keys) to vanish by the time the hermes gateway process starts. The gateway then fails to detect any messaging platforms because os.getenv() returns empty for every credential. v2026.5.16 used tini as PID 1 (no env stripping), so this was never an issue. The migration to s6-overlay on main introduced the regression. Fix: ENV S6_KEEP_ENV=1 in the Dockerfile, per the s6-overlay docs. This preserves the full container environment for the main program and all cont-init.d scripts.
The terminal tool's init_session() spawns `bash -l -c` (login shell) to capture an environment snapshot. Login shells source /etc/profile which resets PATH to the system default, dropping the venv bin dir. Subsequent terminal commands use the snapshot, so `python3` resolves to /usr/bin/python3 (system Python, no pip, no google-api-python-client) instead of /opt/hermes/.venv/bin/python3. On v2026.5.16 (tini), entrypoint.sh ran `source .venv/bin/activate` in the same process tree, so the activated PATH survived into every child — including login shells. With s6-overlay, /init is a C binary that doesn't carry shell state, and main-wrapper.sh only activates the venv for the hermes process itself. Fix: write /etc/profile.d/hermes-venv.sh so login shells prepend the venv to PATH. Works alongside the existing ENV PATH for non-login shells and docker exec.
|
I found one issue worth noting before merge. PR scope mismatch: The PR body describes only the Docker venv PATH fix (
These Mattermost adapter changes are not mentioned anywhere in the PR body, summary, or test plan. The Dockerfile changes are clean, but the bundled Mattermost feature should either be documented in the PR body or split into a separate PR. Additionally, the Mattermost changes here overlap with PR #33181 (same author) which adds |
|
Thanks for this @ugoenyioha — there are some good ideas here but the PR bundles three independent concerns that need to be split before any can land. Could you break this into separate PRs: 1. 2. 3. Mattermost Closing this one to make the split explicit — once (2) and (3) land as separate PRs, (1) is a no-op (already fixed). Sorry to fragment the work — appreciate the contribution and want each piece to land cleanly. |
|
Split done, @benbarclay — thanks for the clear breakdown.
One correction to that PR while I'm here: its profile.d line omitted No need to reopen this one; happy for it to stay closed now the pieces are separate. |
Summary
The terminal tool's
init_session()spawnsbash -l -c(login shell) to capture an environment snapshot. Login shells source/etc/profilewhich resets PATH to the system default, dropping the venv bin directory. All subsequent terminal commands use this snapshot, sopython3resolves to/usr/bin/python3(system Python — no pip, no google-api-python-client, no installed packages) instead of/opt/hermes/.venv/bin/python3.Root Cause
The Dockerfile sets
ENV PATH="/opt/hermes/.venv/bin:..."which works for:docker execcommandsBut
bash -lresets PATH from/etc/profile, ignoring the Docker ENV. The terminal tool'sBaseEnvironment.init_session()(line 384 intools/environments/base.py) usesbash -lto build the snapshot, so the venv PATH is lost.On v2026.5.16 (tini),
entrypoint.shransource .venv/bin/activatein the same process tree that became PID 1, so the activated PATH was inherited by all children including login shells. With s6-overlay,/initis a C binary that doesn't carry shell state.Fix
Write
/etc/profile.d/hermes-venv.shin the Dockerfile to prepend the venv to PATH for login shells. This is the standard mechanism for injecting PATH entries into login sessions on Debian.Verification
Tested on Talos Kubernetes:
bash -l -c 'which python3'→/usr/bin/python3, terminal tool'spython3calls fail withModuleNotFoundErrorbash -l -c 'which python3'→/opt/hermes/.venv/bin/python3, email fetch via google-api-python-client works