Skip to content

fix(shell): resolve function/alias shadowing for zsh and bash#98

Merged
sassman merged 14 commits into
mainfrom
fix/nix-native-alias
Apr 17, 2026
Merged

fix(shell): resolve function/alias shadowing for zsh and bash#98
sassman merged 14 commits into
mainfrom
fix/nix-native-alias

Conversation

@sassman

@sassman sassman commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #97 — native aliases were silently ignored when a same-named shell function existed, and vice-versa.

  • Root cause: in zsh/bash, aliases beat functions at lookup time. If gs() exists when alias gs='git status' is defined, both coexist but only the alias is reachable — and if a function was defined after the alias, the alias would be unreachable instead. Neither side should silently win.
  • Fix: scan the shell's existing functions (typeset +f / declare -F) and aliases (alias) at am init time, then emit cleanup only for confirmed conflicts:
    • unset -f <name> before a native alias when a same-named function exists
    • unalias <name> before a function when a same-named alias exists
  • Recursion guard: scans run zsh -i -c '…' / bash -i -c '…', which sources startup files and would trigger am hook again. A new _AM_DETECTING_ALIASES env var causes am to exit immediately (no-op eval) when set during those subprocesses.
  • env_vars module: centralises all _AM_* env var string constants.
  • Zsh and Bash now own their own rendering structs with the conditional cleanup logic; NixShell stays as the shared fallback for brush.

Test plan

  • Unit tests for parse_zsh_function_names, parse_zsh_alias_names, parse_bash_function_names, parse_bash_alias_names
  • Unit tests for conditional unset -f / unalias emission in Zsh and Bash
  • Integration test: am hook produces no output when _AM_DETECTING_ALIASES is set (recursion guard)
  • Snapshot tests updated for new output format
  • cargo clippy --locked --all-targets -- -D warnings clean

sassman added 12 commits April 16, 2026 13:33
…r templates

The previous approach wrapped every alias in a shell function, which caused
infinite recursion for same-name aliases like `ls = "ls -la"` (zsh/bash
detect self-reference in native aliases but not in functions).

- simple aliases → `alias name="command"` (safe, idiomatic)
- parameterized aliases with {{N}}/{{@}} → shell function (required)
- unalias → `unalias ... 2>/dev/null; unset -f ... 2>/dev/null` to handle
  both old function-style and new native aliases during migration

Mirrors the fine-grained approach fish already uses. Closes #97.

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- native aliases silently ignored when a same-named function exists in the shell (aliases beat functions in zsh/bash) — fix by scanning `typeset +f` / `declare -F` and emitting `unset -f` only for confirmed conflicts
- functions silently ignored when a same-named alias exists — fix by scanning `alias` output and emitting `unalias` only for confirmed conflicts
- both scans set `_AM_DETECTING_ALIASES` to prevent recursive `am` invocations from the shell startup files sourced during the scan
- adds `env_vars` module to centralise all shell-env-var string constants
- zsh and bash each own their rendering logic (Zsh/Bash structs); NixShell remains the shared fallback for brush

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Clippy `collapsible_match` lint fired in CI (--all-features) for two
patterns in am-tui that were pre-existing but not caught locally without
the feature flag.

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- seed ~/.zshrc with `la='ls -lAh'` on non-Windows runners
- ensures the zsh alias scan integration test passes in CI
  where runners have a bare shell config

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- add apt-get install zsh step for Linux runners
- zsh is pre-installed on macOS but absent on ubuntu-latest

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- extract zsh binary/env tests into tests/e2e.rs, all marked #[ignore]
- keep pure parser unit test in zsh_alias_scan.rs
- run e2e suite explicitly in CI: cargo test --test e2e -- --ignored
- add apt-get update before zsh install on Linux runners

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
@codecov-commenter

codecov-commenter commented Apr 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.27848% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.19%. Comparing base (cb6bb08) to head (fba49cb).

Files with missing lines Patch % Lines
crates/am/src/shell/bash.rs 84.90% 16 Missing ⚠️
crates/am/src/shell/zsh.rs 85.32% 16 Missing ⚠️
crates/am/src/update.rs 0.00% 16 Missing ⚠️
crates/am/src/hook.rs 80.00% 4 Missing ⚠️
crates/am/src/bin/am.rs 0.00% 3 Missing ⚠️
crates/am-tui/src/update/transfer.rs 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #98      +/-   ##
==========================================
+ Coverage   73.99%   74.19%   +0.20%     
==========================================
  Files          45       45              
  Lines       11510    11779     +269     
  Branches    11510    11779     +269     
==========================================
+ Hits         8517     8740     +223     
- Misses       2811     2859      +48     
+ Partials      182      180       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

sassman added 2 commits April 17, 2026 16:47
- collapse match guard onto one line (transfer.rs)
- convert doc comments to inner module doc style (zsh_alias_scan.rs)

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- CI runners have a bare zsh with only 2-3 built-in aliases
- the meaningful assertion is that `la` was sourced from ~/.zshrc
- removed the `keys.len() > 5` guard that assumed a rich plugin env

Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
@sassman sassman merged commit 3b212c4 into main Apr 17, 2026
31 checks passed
@sassman sassman deleted the fix/nix-native-alias branch April 17, 2026 21:26
@github-actions github-actions Bot mentioned this pull request Apr 17, 2026
sassman pushed a commit that referenced this pull request Apr 18, 2026
## 🤖 New release

* `amoxide`: 0.6.1 -> 0.7.0 (⚠ API breaking changes)
* `amoxide-tui`: 0.6.1 -> 0.7.0

### ⚠ `amoxide` breaking changes

```text
--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field ShellContext.external_functions in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/shell/shell.rs:41
  field ShellContext.external_aliases in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/shell/shell.rs:43

--- failure enum_missing: pub enum removed or renamed ---

Description:
A publicly-visible enum cannot be imported by its prior path. A `pub use` may have been removed, or the enum itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_missing.ron

Failed in:
  enum amoxide::shell::Shells, previously in file /tmp/.tmpgcZOTb/amoxide/src/shell/shell.rs:28

--- failure enum_struct_variant_field_added: pub enum struct variant field added ---

Description:
An enum's exhaustive struct variant has a new field, which has to be included when constructing or matching on this variant.
        ref: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_struct_variant_field_added.ron

Failed in:
  field force of variant Commands::Init in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/cli.rs:85

--- failure enum_tuple_variant_field_added: pub enum tuple variant field added ---

Description:
An enum's exhaustive tuple variant has a new field, which has to be included when constructing or matching on this variant.
        ref: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_tuple_variant_field_added.ron

Failed in:
  field 1 of variant Message::InitShell in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/messages.rs:37
  field 1 of variant Message::InitShell in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/messages.rs:37

--- failure struct_missing: pub struct removed or renamed ---

Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/struct_missing.ron

Failed in:
  struct amoxide::shell::Zsh, previously in file /tmp/.tmpgcZOTb/amoxide/src/shell/zsh.rs:5
  struct amoxide::shell::Bash, previously in file /tmp/.tmpgcZOTb/amoxide/src/shell/bash.rs:5

--- failure trait_changed_kind: pub trait changed kind ---

Description:
A public trait was replaced by a struct, enum, or union at the same import path.
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/trait_changed_kind.ron
Failed in:
  trait amoxide::shell::Shell became enum in /tmp/.tmpnR09NE/amoxide-rs/crates/am/src/shell/shell_enum.rs:2
```

<details><summary><i><b>Changelog</b></i></summary><p>

## `amoxide`

<blockquote>

##
[0.7.0](v0.6.1...v0.7.0) -
2026-04-18

### Bug Fixes

- Resolve function/alias shadowing for zsh and bash
([#98](#98))

### Features

- Add --force flag to reinitialise shell aliases
([#101](#101))
</blockquote>

## `amoxide-tui`

<blockquote>

##
[0.7.0](v0.6.1...v0.7.0) -
2026-04-18

### Bug Fixes

- Resolve function/alias shadowing for zsh and bash
([#98](#98))

### Features

- Add --force flag to reinitialise shell aliases
([#101](#101))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

ZSH initialisation fails with global alias name collision

2 participants