Skip to content

fix: re-assert Hermit env after shell startup files - #631

Merged
alecthomas merged 1 commit into
masterfrom
zsh-path-reassert-order
Oct 21, 2026
Merged

alecthomas merged 1 commit into
masterfrom
zsh-path-reassert-order

Conversation

@alecthomas

Copy link
Copy Markdown
Collaborator

Hermit can activate an environment before the user's own startup files run, and those files can then override it. For example, RVM prepending to PATH shadows Hermit's tools. This happens in three ways:

  • zsh hooks may be installed in /etc/zshrc, which runs before ~/.zshrc.
  • Fish hooks live in conf.d/hermit.fish, which fish runs before later conf.d snippets and before config.fish.
  • A child bash inherits Hermit's variables but not its shell functions, so nothing re-applies the environment after its .bashrc runs. The child also has no deactivate-hermit.

zsh already had a one-shot first-prompt hook (#594), but it rebuilt the front of PATH from only HERMIT_PREPEND_PATH and bin. That moved bin ahead of package PATH entries and the link farm. So a freshly activated shell ended up with a different PATH order from one re-activated by update_hermit_env. The hook also ignored every variable other than PATH.

The approach:

  • A new hermit env --reassert-from-ops=OPS reverts HERMIT_ENV_OPS against the current environment and then applies it again. It has none of the activation side effects, such as triggers, channel update checks or link farm creation. Because the ops are idempotent, this restores every Hermit-managed variable in activation order. Reverting first means values set by startup files become what deactivation restores.
  • zsh (reassert_hermit_env in activate.tmpl.sh) and fish (activate.tmpl.fish) run it once at the first prompt. Their deactivation removes the hook.
  • Bash's hooks add restore_hermit_env ahead of change_hermit_env in PROMPT_COMMAND. It fires only when an environment is inherited but deactivate-hermit is missing. It then reverts the inherited ops and re-sources activate-hermit. Bash hooks already activate at the prompt, after startup files, so bash needs no separate first-prompt step.

Tradeoff: each new zsh and fish shell now starts one Hermit process at its first prompt. The old zsh hook was pure shell.

Known gap: a child fish shell has the same inherited-functions problem as bash did. This PR does not fix it.

Tests:

  • ZshReassertsPackageEnvironmentAtFirstPrompt and TestFishReassertsEnvironmentAtFirstPrompt check that a package PATH entry and a non-PATH variable are re-asserted after simulated startup changes. They check the full Hermit PATH prefix order, and that deactivation restores the startup values.
  • BashChildShellRestoresEnvironmentAtPrompt covers the bash child shell under both bash and zsh parents.
  • Each new test failed before its fix.

🤖 Generated with Claude Code

Hermit can activate before the user's startup files run: zsh hooks may
live in /etc/zshrc, fish hooks run from conf.d before config.fish, and
a child bash inherits Hermit's variables without its functions. Those
files can then override the environment (eg. RVM prepending to PATH).

zsh already re-asserted PATH at the first prompt, but only for bin and
HERMIT_PREPEND_PATH, so it moved bin ahead of package PATH entries and
the final order differed from a fresh activation. It also ignored every
other variable.

Add `hermit env --reassert-from-ops`, which reverts and then re-applies
HERMIT_ENV_OPS. Reverting first makes values set by startup files the
ones deactivation restores. zsh and fish run it once at the first
prompt. Bash re-activates at the prompt when it inherited an
environment but lacks deactivate-hermit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alecthomas
alecthomas marked this pull request as ready for review October 20, 2026 11:29
@alecthomas
alecthomas merged commit 68ea170 into master Oct 21, 2026
8 checks passed
@alecthomas
alecthomas deleted the zsh-path-reassert-order branch October 21, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant