/** * Shell completion support for cele2e. * All completions are static (no DB lookups needed). */ import { BUILD_INFRA_FLAGS, COMMANDS, DOWN_FLAGS, HOST_VERBS, JSON_FLAG_COMMANDS, RUN_FLAGS, UP_PRESETS, } from './command-registry'; import { findAllTestNames } from './module-discovery'; export function getCompletions(words: string[], current: number): string[] { const args = words[0] === 'cele2e' ? words.slice(1) : words; const currentIndex = words[0] === 'cele2e' ? current - 1 : current; if (currentIndex === 0) { return filterSuggestions( COMMANDS.map((c) => c.name), args[0] || '', ); } const command = args[0]; if (command === 'run' && currentIndex >= 1) { const used = new Set(args.slice(1)); const flags = RUN_FLAGS.filter((f) => !used.has(f)); const currentArg = args[currentIndex] || ''; if (currentArg.startsWith('--')) { return filterSuggestions(flags, currentArg); } const testNames = findAllTestNames(process.cwd()); return filterSuggestions([...testNames, ...flags], currentArg); } if (command === 'up' && currentIndex === 1) { return filterSuggestions(UP_PRESETS, args[1] || ''); } if (command === 'down' && currentIndex >= 1) { const used = new Set(args.slice(1)); return filterSuggestions( DOWN_FLAGS.filter((f) => !used.has(f)), args[currentIndex] || '', ); } if (command === 'build-infra' && currentIndex >= 1) { const used = new Set(args.slice(1).filter((a) => a.startsWith('--'))); return filterSuggestions( BUILD_INFRA_FLAGS.filter((f) => !used.has(f)), args[currentIndex] || '', ); } if (command === 'list' && currentIndex >= 1) { const used = new Set(args.slice(1)); return filterSuggestions( ['--paths'].filter((f) => !used.has(f)), args[currentIndex] || '', ); } if (command === 'host' && currentIndex === 1) { return filterSuggestions(HOST_VERBS, args[1] || ''); } if (command === 'host' && args[1] === 'reset' && currentIndex === 2) { return filterSuggestions(['--yes'], args[2] || ''); } if (command === 'completion' && currentIndex === 1) { return filterSuggestions(['zsh', 'bash'], args[1] || ''); } if (JSON_FLAG_COMMANDS.includes(command) && currentIndex >= 1) { const used = new Set(args.slice(1)); return filterSuggestions( ['--json'].filter((f) => !used.has(f)), args[currentIndex] || '', ); } return []; } export function filterSuggestions(options: string[], current: string): string[] { if (!current) return options; return options.filter((opt) => opt.startsWith(current)); } export function generateBashCompletion(): string { return `# cele2e bash completion _cele2e_completion() { local cur prev words cword _init_completion || return local completions completions=$(cele2e --get-completions "\${COMP_WORDS[@]}" "\${COMP_CWORD}") COMPREPLY=( $(compgen -W "$completions" -- "$cur") ) return 0 } complete -F _cele2e_completion cele2e `; } export function generateZshCompletion(): string { return `#compdef cele2e _cele2e() { local curcontext="$curcontext" state line typeset -A opt_args # Capture the original command line — _arguments mutates words/CURRENT # as it consumes arguments, but our dynamic completer needs the originals. # zsh dynamic scoping makes these visible to nested functions. local -a _cele2e_orig_words local _cele2e_orig_current _cele2e_orig_words=( "\${words[@]}" ) _cele2e_orig_current=$CURRENT _arguments -C \\ '1: :_cele2e_commands' \\ '*::arg:->args' case $state in args) case $line[1] in run) _arguments \\ '--all[Run the full regression: every module suite + top-level e2e/tests/]' \\ '--all-modules[Run e2e tests for all modules with e2e/ directories]' \\ '--keep[Keep network running after tests]' \\ '--reuse[Reuse existing network if running]' \\ '--live[Use live (non-simulated) internet]' \\ '--published[Use published .netapp packages]' \\ '--notify[Desktop notification when the run finishes]' \\ '--ci[Plain log output: one line per step, no spinner]' \\ '--no-ci[Force spinner even when a CI env var is set]' \\ '*::test name or module path:_cele2e_run_args' ;; list) _arguments '--paths[Also show each test file path]' ;; up) _arguments '1: :_cele2e_up_presets' ;; down) _arguments \\ '--keep[Stop containers but preserve volumes]' \\ '--all[Also stop competing module containers]' ;; build-infra) _arguments \\ '--save[Save Docker images to tarball after build]' \\ '--skip-modules[Skip .netapp packaging (only rebuild Docker images)]' \\ '--published[Bake management:latest with the published @celilo/cli from real npm]' \\ '*:module directory:_directories' ;; shell) _arguments '1:container name:' ;; scaffold) _arguments '1:test name:' ;; completion) _arguments '1: :_cele2e_completion_shells' ;; status) _arguments '--json[Emit a machine-readable lock snapshot for polling]' ;; release|load|clear-timing) ;; esac ;; esac } _cele2e_commands() { local commands=( 'run:Run e2e tests (pattern filters by test file name)' 'list:List every discoverable Docker e2e test (module + top-level)' 'up:Start interactive test network' 'down:Tear down running network' 'build-infra:Rebuild @celilo/e2e Docker images and standard module .netapps' 'clear-timing:Clear saved timing history for test ETAs' 'shell:Shell into a running container (default: management)' 'status:Show run-lock holder + network status (exit 3 if busy)' 'release:Free a run-lock left by --keep/up (does not tear down)' 'load:Load cached Docker images from tarball' 'scaffold:Generate a new test file from template' 'version:Show cele2e version' 'completion:Generate shell completion script (zsh|bash)' ) _describe 'command' commands } _cele2e_up_presets() { local presets=( '--caddy:Start with caddy machine in DMZ' '--full-stack:Start with caddy, IDP, and DB machines' '--infrastructure:Start infrastructure only (default)' ) _describe 'preset' presets } _cele2e_completion_shells() { local shells=( 'zsh:Generate zsh completion script' 'bash:Generate bash completion script' ) _describe 'shell' shells } # Dynamic completion for 'cele2e run' positional args — calls the CLI to # discover test names from modules under the current directory. Uses the # original (un-stripped) words array captured by _cele2e. _cele2e_run_args() { local -a completions local IFS=$'\\n' completions=( \${(f)"$(cele2e --get-completions "\${_cele2e_orig_words[@]}" "$((_cele2e_orig_current - 1))" 2>/dev/null)"} ) if (( \${#completions[@]} > 0 )); then _describe 'test' completions fi } _cele2e "$@" `; }