Skip to content

feat: bench completions#1707

Merged
netchampfaris merged 7 commits into
frappe:developfrom
netchampfaris:feat/shell-completion
Apr 23, 2026
Merged

feat: bench completions#1707
netchampfaris merged 7 commits into
frappe:developfrom
netchampfaris:feat/shell-completion

Conversation

@netchampfaris
Copy link
Copy Markdown
Contributor

@netchampfaris netchampfaris commented Apr 1, 2026

Adds tab completion for bash and zsh via a single bench completions command, and removes the legacy completion.sh.

Usage

Run interactively from your frappe-bench directory:

bench completions

Or non-interactively with flags:

bench completions --zsh
bench completions --bash

Must be run from inside a frappe-bench directory — the command detects the bench root from the current working directory to include installed apps' commands in the generated script.

Deleted completion.sh

The old completion.sh (added in 2018) had several problems:

completion.sh bench completions
Tab keypress cost Full Python startup (~1s) Pure bash case lookup (~1ms)
Click 8 compatible No (_BENCH_COMPLETE=complete was removed) Yes
Works outside bench root No (hardcoded ../env/bin/python) Yes
cd side effect during completion Yes No
External tools ls, cut, xargs None
Install UX Source manually bench completions

The core difference: completion.sh did live Python introspection on every keypress. bench completions is a code generator — it bakes the full command tree into static bash case functions once. Runtime completion is pure bash, zero Python.

How it works

  • Walks the bench click command tree in-process to collect all bench-native commands and flags.
  • If run from inside a bench, spawns a single subprocess using the bench's own Python to load Frappe once and introspect the full click group via get_app_groups(), emitting all command metadata as JSON. Generation takes ~1-2s regardless of how many commands are installed.
  • Falls back to a parallel BFS of --help subprocesses for older Frappe versions that don't expose get_app_groups().
  • The generated script is pure static bash case functions — tab completion at runtime is zero-Python.

@netchampfaris netchampfaris force-pushed the feat/shell-completion branch from 377bce2 to 1b8015f Compare April 1, 2026 18:31
@netchampfaris netchampfaris changed the title feat: add generated shell completions feat: bench completions Apr 11, 2026
netchampfaris and others added 7 commits April 23, 2026 20:32
- Collapse `bench completion bash/zsh/install` into one `bench completions`
  command; interactive by default, non-interactive when any flag is passed
- Replace per-command --help subprocess calls with a single frappe Python
  process that introspects the click group via get_app_groups(), reducing
  generation time from ~40s (sequential) to ~1-2s
- Extract the frappe introspection script to frappe_spec_collector.py so it
  can be linted, syntax-highlighted, and run directly for debugging
- Fall back to parallel BFS of --help subprocesses for older frappe versions
- Suppress progress output in non-interactive mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
completion.sh used Click's _BENCH_COMPLETE=complete mechanism (removed in
Click 8), spawned Python on every keypress, used `cd` as a side effect during
completion, and required hardcoded paths that only worked from the bench root.
bench completions supersedes it entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… passed

Writing to ~/.zshrc without asking is too aggressive. The completion script
file itself is safe to write silently, but dotfile modification should always
prompt so users know what changed. --yes/-y opts out for scripts and dotfile
managers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…exity

Sonar flagged _collect_frappe_tree at complexity 20 (limit 15). Extracting
the BFS fallback into _collect_frappe_tree_bfs brings both functions well
under the threshold with no logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hardcoded paths under /tmp are publicly writable and can be exploited via
symlink attacks. TemporaryDirectory creates a private directory with
restricted permissions and cleans it up automatically after the test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
requests 2.32.5 → 2.33.0  (CVE-2026-25645)
uv 0.9.30      → 0.11.6   (GHSA-pjjw-68hj-v9mw)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@netchampfaris netchampfaris force-pushed the feat/shell-completion branch from cf76a20 to 96c094d Compare April 23, 2026 15:02
@sonarqubecloud
Copy link
Copy Markdown

@netchampfaris netchampfaris merged commit 08ccbf6 into frappe:develop Apr 23, 2026
8 of 9 checks passed
@netchampfaris netchampfaris deleted the feat/shell-completion branch April 23, 2026 15:44
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