# CONSTANTS MIAOU_INCUS_DIR=${MIAOU_INCUS_DIR:-/opt/miaou-incus} # FUNCTIONS function _incus_container { incus list --format csv --columns n type=CONTAINER } function _incus_running_container { incus list --format csv --columns n type=CONTAINER state=RUNNING } function _incus_stopped_container { incus list --format csv --columns n type=CONTAINER state=STOPPED } function _active_users { echo root miaou-exec "$container" -- awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1}' /etc/passwd } function _miaou_login { local cur="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD - 1]}" local container="${COMP_WORDS[1]}" case $COMP_CWORD in 1) # first argument — containers COMPREPLY=($(compgen -W "$(_incus_running_container)" -- "$cur")) ;; 2) # second argument - options COMPREPLY=($(compgen -W "--user -u" -- "$cur")) ;; 3) if [[ $prev =~ ^(--user|-u) ]]; then # user expected COMPREPLY=($(compgen -W "$(_active_users "$container")" -- "$cur")) fi ;; esac } function _miaou_exec() { local cur="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD - 1]}" local container="${COMP_WORDS[1]}" # find '--' position local dashdash_pos=0 local i for ((i = 0; i < ${#COMP_WORDS[@]}; i++)); do [[ ${COMP_WORDS[$i]} == "--" ]] && dashdash_pos=$i done # complete running container names if ((COMP_CWORD == 1)); then # first argument — containers COMPREPLY=($(compgen -W "$(_incus_running_container)" -- "$cur")) return fi if ((COMP_CWORD == 2)); then # second argument - options COMPREPLY=($(compgen -W "--" -- "$cur")) return fi if ((COMP_CWORD == 3)); then # command or file from inside container # echo -e "\nCOMMAND or FILE from CONTAINER\n" >&2 COMPREPLY_COMMANDS=($( incus exec "$container" -- bash << EOF bind -f /etc/inputrc 2>/dev/null source /etc/bash_completion compgen -c -- "$cur" EOF )) COMPREPLY_FILES=($( incus exec "$container" -- bash << EOF bind -f /etc/inputrc 2>/dev/null source /etc/bash_completion compgen -f -- "$cur" EOF )) COMPREPLY=(${COMPREPLY_COMMANDS[@]} ${COMPREPLY_FILES[@]}) if [[ ${#COMPREPLY_FILES[@]} -gt 0 ]]; then compopt -o filenames compopt -o nospace fi # echo -e "\nCOMPREPLY=( ${COMPREPLY[@]} ) FILES=${#COMPREPLY_FILES[@]}\n" >&2 return fi if ((COMP_CWORD > 3)); then command=${COMP_WORDS[dashdash_pos + 1]} # echo -e "\nCOMMAND:$command from CONTAINER\n" >&2 completion_command=$( incus exec "$container" -- bash << EOF source /etc/bash_completion [[ -f /usr/share/bash-completion/completions/$command ]] && source /usr/share/bash-completion/completions/$command complete -p "$command" 2>/dev/null | grep -oP '(?<=-F )\S+' EOF ) if [[ -n $completion_command ]]; then # Remove first $dashdash_pos items COMP_WORDS=("${COMP_WORDS[@]:dashdash_pos+1}") COMP_CWORD=$((COMP_CWORD - dashdash_pos - 1)) COMP_LINE="${COMP_WORDS[@]}" COMP_POINT=${#COMP_LINE} COMP_POINT=$((COMP_POINT + 1)) # _comp_debug completion_command $completion_command # _comp_debug prev $prev # _comp_debug cur $cur # _comp_debug COMP_WORDS "(${COMP_WORDS[@]})" # _comp_debug COMP_CWORD $COMP_CWORDS COMPREPLY=($( incus exec "$container" -- bash << EOF bind -f /etc/inputrc 2>/dev/null source /etc/bash_completion for i in /etc/bash_completion.d/*; do source \$i; done source /usr/share/bash-completion/bash_completion [[ -f /usr/share/bash-completion/completions/$command ]] && source /usr/share/bash-completion/completions/$command COMP_WORDS=(${COMP_WORDS[@]}) COMP_CWORD=$COMP_CWORD COMP_LINE="$COMP_LINE" COMP_POINT=$COMP_POINT COMP_TYPE=9 COMP_KEY=9 export COMP_WORDS COMP_CWORD COMP_LINE COMP_POINT COMP_TYPE COMP_KEY $completion_command 2>/dev/null echo \${COMPREPLY[@]} EOF )) if [[ $completion_command == '_comp_complete_longopt' ]]; then compopt -o filenames compopt -o nospace fi else # _comp_debug "NO completion_command JUST plain files and dirs" compopt -o filenames compopt -o nospace COMPREPLY=($( incus exec "$container" -- bash << EOF bind -f /etc/inputrc 2>/dev/null source /etc/bash_completion compgen -f "$cur" 2>/dev/null EOF )) fi fi } function _miaou_ls { local cur="${COMP_WORDS[COMP_CWORD]}" if [[ $cur =~ ^- ]]; then # options COMPREPLY=(--vm) elif ((COMP_CWORD == 1)); then # containers COMPREPLY=(--vm $(compgen -W "$(_incus_container)" -- "$cur")) fi } function _comp_debug { echo -e "\nDEBUG: $@" >&2 } function _miaou_start { local cur="${COMP_WORDS[COMP_CWORD]}" # containers... local suggestions=($(compgen -W "$(_incus_stopped_container)" -- "$cur")) local previous_containers=("${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}") # drop first and last items _array_subtract suggestions previous_containers COMPREPLY } function _miaou_stop { local cur="${COMP_WORDS[COMP_CWORD]}" # containers... local suggestions=($(compgen -W "$(_incus_running_container)" -- "$cur")) local previous_containers=("${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}") # drop first and last items _array_subtract suggestions previous_containers COMPREPLY } function _miaou_destroy { local cur suggestions previous_words cur="${COMP_WORDS[COMP_CWORD]}" if [[ $cur =~ ^- ]]; then suggestions=(--yes --force) else _array_contains COMP_WORDS --force && suggestions=($(_incus_container)) || suggestions=($(_incus_stopped_container)) [[ ${#suggestions[@]} == 0 ]] && suggestions=(--force) fi suggestions=($(compgen -W "${suggestions[*]}" -- "$cur")) previous_words=("${COMP_WORDS[@]:1:${#COMP_WORDS[@]}-2}") # drop first and last items _array_subtract suggestions previous_words COMPREPLY } function _miaou_recipe { local cur="${COMP_WORDS[COMP_CWORD]}" # containers... if [[ $COMP_CWORD == 1 ]]; then COMPREPLY=($(compgen -W "$(_incus_running_container)" -- "$cur")) elif [[ $COMP_CWORD == 2 ]]; then COMPREPLY=($(compgen -f -- "$cur")) compopt -o filenames elif [[ $COMP_CWORD == 3 ]]; then COMPREPLY=($(compgen -W "--" -- "$cur")) fi } # MAIN # shellcheck source=/opt/miaou-bash/lib/functions.bash source "$MIAOU_BASH_DIR/lib/functions.bash" complete -F _miaou_login "miaou-login" complete -F _miaou_exec "miaou-exec" complete -F _miaou_ls "miaou-ls" complete -F _miaou_start "miaou-start" complete -F _miaou_stop "miaou-stop" complete -F _miaou_destroy "miaou-destroy" complete -F _miaou_recipe "miaou-recipe"