From 067d017ea63940c373e7b1347bfe7a63c2d2c210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Tue, 16 Jun 2026 19:52:55 +0200 Subject: [PATCH] feat(stacks): add --static flag to completion command Bakes the current stack list into the generated completion script instead of using runtime directory discovery. Useful for remote hosts where the stacks dir path differs from the local repo. Co-Authored-By: Claude Sonnet 4.6 --- stacks.sh | 88 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/stacks.sh b/stacks.sh index a39f5c5..92732db 100755 --- a/stacks.sh +++ b/stacks.sh @@ -726,6 +726,7 @@ REDIS # ─── Shell Completion ───────────────────────────────────────────────────────── _completion_bash() { + local stacks_init="$1" # pre-rendered stacks array initialisation block cat << 'EOF' # stacks.sh bash completion — source this file or place in /etc/bash_completion.d/ _stacks_complete() { @@ -736,19 +737,10 @@ _stacks_complete() { prev="${COMP_WORDS[COMP_CWORD-1]}" } - # Locate the stacks dir from the invoked script path local script="${COMP_WORDS[0]}" - local stacks_dir - stacks_dir="$(cd "$(dirname "$(realpath "$script" 2>/dev/null || echo "$script")")" 2>/dev/null && pwd)" - - local -a stacks=() - if [[ -d "$stacks_dir" ]]; then - for d in "$stacks_dir"/*/; do - local n; n="$(basename "$d")" - [[ "$n" == _* ]] && continue - [[ -f "$d/compose.yml" ]] && stacks+=("$n") - done - fi +EOF + echo "$stacks_init" + cat << 'EOF' local commands="ls ps up down restart pull logs exec run config images top stats compose update backup new completion version help" local update_subs="install uninstall run status logs next" @@ -758,7 +750,7 @@ _stacks_complete() { case "${COMP_WORDS[1]}" in update) COMPREPLY=( $(compgen -W "$update_subs" -- "$cur") ); return ;; backup) COMPREPLY=( $(compgen -W "$backup_subs" -- "$cur") ); return ;; - completion) COMPREPLY=( $(compgen -W "bash zsh --install" -- "$cur") ); return ;; + completion) COMPREPLY=( $(compgen -W "bash zsh --install --static" -- "$cur") ); return ;; new) case "$prev" in --db) COMPREPLY=( $(compgen -W "postgres mysql mariadb" -- "$cur") ); return ;; @@ -803,22 +795,15 @@ EOF } _completion_zsh() { + local stacks_init="$1" # pre-rendered stacks array initialisation block cat << 'EOF' #compdef stacks.sh stacks _stacks() { local script="${words[1]}" - local stacks_dir - stacks_dir="$(cd "$(dirname "$(realpath "$script" 2>/dev/null || echo "$script")")" 2>/dev/null && pwd)" - - local -a stacks=() - if [[ -d "$stacks_dir" ]]; then - for d in "$stacks_dir"/*/; do - local n="${d:t}" - [[ "$n" == _* ]] && continue - [[ -f "${d}compose.yml" ]] && stacks+=("$n") - done - fi +EOF + echo "$stacks_init" + cat << 'EOF' local -a commands=( 'ls:List all stacks with live status' @@ -916,6 +901,7 @@ _stacks() { completion) _arguments \ '--install[Install the completion file]' \ + '--static[Bake current stack list into completion script]' \ '1:shell:(bash zsh)' ;; esac @@ -928,13 +914,14 @@ EOF } cmd_completion() { - local shell="" install=false + local shell="" install=false static=false for arg in "$@"; do case "$arg" in bash|zsh) shell="$arg" ;; --install) install=true ;; - *) die "Unknown argument: $arg (bash|zsh [--install])" ;; + --static) static=true ;; + *) die "Unknown argument: $arg (bash|zsh [--install] [--static])" ;; esac done @@ -944,10 +931,52 @@ cmd_completion() { warn "No shell specified, defaulting to: $shell" fi + # Build the stacks array initialisation block — either dynamic (runtime discovery) + # or static (current list baked in at generation time). + local bash_stacks_init zsh_stacks_init + + if $static; then + local -a cur_stacks=() + mapfile -t cur_stacks < <(list_all_stacks) + [[ ${#cur_stacks[@]} -eq 0 ]] && die "No stacks found to bake into completion" + local generated_at + generated_at="$(date '+%Y-%m-%d')" + bash_stacks_init=" # Stacks baked in at generation time (${generated_at}) — rerun to update + local -a stacks=(${cur_stacks[*]})" + zsh_stacks_init=" # Stacks baked in at generation time (${generated_at}) — rerun to update + local -a stacks=(${cur_stacks[*]})" + info "Baking ${#cur_stacks[@]} stacks: ${cur_stacks[*]}" + else + bash_stacks_init=' # Dynamically discover stacks from the script location at completion time + local stacks_dir + stacks_dir="$(cd "$(dirname "$(realpath "$script" 2>/dev/null || echo "$script")")" 2>/dev/null && pwd)" + + local -a stacks=() + if [[ -d "$stacks_dir" ]]; then + for d in "$stacks_dir"/*/; do + local n; n="$(basename "$d")" + [[ "$n" == _* ]] && continue + [[ -f "$d/compose.yml" ]] && stacks+=("$n") + done + fi' + zsh_stacks_init=' # Dynamically discover stacks from the script location at completion time + local stacks_dir + stacks_dir="$(cd "$(dirname "$(realpath "$script" 2>/dev/null || echo "$script")")" 2>/dev/null && pwd)" + + local -a stacks=() + if [[ -d "$stacks_dir" ]]; then + for d in "$stacks_dir"/*/; do + local n="${d:t}" + [[ "$n" == _* ]] && continue + [[ -f "${d}compose.yml" ]] && stacks+=("$n") + done + fi' + fi + local content case "$shell" in - bash) content="$(_completion_bash)" ;; - zsh) content="$(_completion_zsh)" ;; + bash) content="$(_completion_bash "$bash_stacks_init")" ;; + zsh) content="$(_completion_zsh "$zsh_stacks_init")" ;; *) die "Unsupported shell: $shell (bash|zsh)" ;; esac @@ -1042,7 +1071,8 @@ ${BOLD}UTILITIES${RESET} --db postgres|mysql|mariadb Add a database service --redis Add a Redis service --no-traefik Expose port instead of Traefik labels - ${CYAN}completion${RESET} bash|zsh [--install] Generate/install shell completion + ${CYAN}completion${RESET} bash|zsh [--install] [--static] Generate/install shell completion + --static Bake current stack list in (no runtime discovery) ${CYAN}version${RESET} Print version ${CYAN}help${RESET} Show this help