pass-update icon indicating copy to clipboard operation
pass-update copied to clipboard

Bash completion Ubuntu 20.04

Open semseysandor opened this issue 2 years ago • 10 comments

After installing from the apt repository on Ubuntu 20.04, completion for bash was not working.

  • pass update 2.1
  • pass v1.7.3
  • GNU bash, version 5.0.17(1)-release

If I:

  • source /usr/share/bash-completion/completions/pass-update sourced manually the file (or in .bashrc),
  • or moved that to: sudo mv /usr/share/bash-completion/completions/pass-update /etc/bash_completion.d/ then it was working.

In my understanding, completion scripts are loaded by /usr/share/bash-completion/bash_completion only if the completion script has the same name as the invoked command, which is pass in our case. The completion script for pass (/usr/share/bash-completion/completions/pass) will not source the pass-update script. If the completion function (__password_store_extension_complete_update) isn't defined yet it won't be called. However completion scripts in /etc/bash_completion.d/ are sourced by /usr/share/bash-completion/bash_completion automatically.

So it seems on Ubuntu 20.04 /etc/bash_completion.d/ is the good place for this script: https://github.com/roddhjav/pass-update/commit/8721eb067f77ca7cda51313632399b2c86d1dc75#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52

semseysandor avatar Sep 04 '21 10:09 semseysandor

I'm experiencing the exact same problem on Debian 11 (bullseye). With the exact same workaround of sourceing it in .bashrc.

so-rose avatar May 29 '22 11:05 so-rose

i'm working on an official debian package for this which has the patch built in, you can track the progress in #31 or in the Debian bug tracker. alternatively, this git repo "just" needs a new release to ship that patch, so I filed #32 about that.

anarcat avatar Feb 14 '24 19:02 anarcat

Wait, so Ubuntu needs /etc/bash_completion.d/ while Debian is looking for /usr/share/bash-completion/bash_completion/completions ? That does not look right.

roddhjav avatar Feb 14 '24 22:02 roddhjav

uh. now that i reread the original post here, i'm confused as well.

when i built the debian package here, lintian warned about the file in /etc, i assumed that was the issue. i assume the proper location is in /usr, no idea what's up with Ubuntu, but i would assume it's the same.

anarcat avatar Feb 15 '24 00:02 anarcat

I understand the issue now. The problem is not the directory, /usr/share/bash-completion/completions is the future-proof place (/etc/bash_completion.d is deprecated) for completions in Debian and Ubuntu also.

The issue is that completion scripts in /usr/share/bash-completion/completions are dynamically sourced if the completion script name complies with the naming conventions: it should be $cmd, $cmd.bash or _$cmd where $cmd is the invoked command name, which is pass in our case. So the script should be called pass, pass.bash or _pass, but these names are used by upstream pass, not one of it's extensions... /etc/bash_completion.d/ works as all scripts in this dir get sourced statically.

Though pass supports completion functions for extensions - __password_store_extension_complete_update here, but /usr/share/bash-completion/completions/pass-update is never sourced by bash, so the function is not defined and therefore not called by pass.

So this function could be moved to other file which are sourced (update.bash maybe) or source completion/pass-update.bash manually by update.bash.

semseysandor avatar Feb 15 '24 08:02 semseysandor

yeah, so i can confirm that issue as well. the problem is the auto-loading. i wonder how other extensions / software handle this. surely (e.g.) git has some tricks to fix this?

anarcat avatar Feb 27 '24 16:02 anarcat

After a few tests, I can confirm the issue too. My other extensions have the same issue (they all install completion under /usr/share)

In pass-otp, it works because the file is installed in /etc/bash_completion.d (see https://github.com/tadfisher/pass-otp/blob/develop/Makefile)

roddhjav avatar Feb 27 '24 18:02 roddhjav

Hm. I don't know. here the git-annex completion (to give another example) works both with git-annex and git annex, even though the completion file is /usr/share/bash-completion/completions/git-annex.

here's the complete file, in case that matters:

# Use git-annex's built-in bash completion
# This is the same code output by git-annex --bash-completion-script git-annex
# This covers all commands, all options, and will never go out of date!
_git-annex()
{
    local CMDLINE
    local IFS=$'\n'
    CMDLINE=(--bash-completion-index $COMP_CWORD)

    for arg in ${COMP_WORDS[@]}; do
        CMDLINE=(${CMDLINE[@]} --bash-completion-word $arg)
    done

    COMPREPLY=( $(git-annex "${CMDLINE[@]}") )
}

complete -o bashdefault -o default -o filenames -F _git-annex git-annex

# Called by git's bash completion script when completing "git annex"
# Translate to the "git-annex" completion above.
_git_annex() {
    local CMDLINE
    CMDLINE=(--bash-completion-index $(($COMP_CWORD - 1)))

    local seen_git
    local seen_annex
    for arg in ${COMP_WORDS[@]}; do
        if [ "$arg" = git ] && [ -z "$seen_git" ]; then
		seen_git=1
		CMDLINE=(${CMDLINE[@]} --bash-completion-word git-annex)
	elif [ "$arg" = annex ] && [ -z "$seen_annex" ]; then
		seen_annex=1
	else
		CMDLINE=(${CMDLINE[@]} --bash-completion-word $arg)
	fi
    done

    # This is the same as __gitcomp_file_direct in git-completion.bash;
    local IFS=$'\n'
    COMPREPLY=( $(git-annex "${CMDLINE[@]}") )
    compopt -o filenames +o nospace ||
    compgen -f /non-existing-dir/ >/dev/null ||
    true
}

one thing I find interesting is this comment:

# Called by git's bash completion script when completing "git annex"

I think this is something in the git completion mechanism itself that allows sub-commands to define their own completion and auto-load them. And, lo-and-behold, here's this chunk in git's source:

__git_complete_command () {
	local command="$1"
	local completion_func="_git_${command//-/_}"
	if ! __git_have_func $completion_func &&
		__git_have_func _completion_loader
	then
		_completion_loader "git-$command"
	fi
	if __git_have_func $completion_func
	then
		$completion_func
		return 0
	elif __git_support_parseopt_helper "$command"
	then
		__git_complete_common "$command"
		return 0
	else
		return 1
	fi
}

So I think that's the key thing here: the pass completion mechanism needs a hook like this to delegate to extensions if they're available. Reading that source now there's some shim for PASSWORD_STORE_EXTENSION_COMMANDS but not if the extension is globally available.

So I think to fix this properly, one must patch pass directly.

anarcat avatar Feb 27 '24 20:02 anarcat

Interestingly, the pass completion also does not properly delegate the git the way it should be; it only hardcodes a few commands:

			git)
				COMPREPLY+=($(compgen -W "init push pull config log reflog rebase" -- ${cur}))
				;;

... so that's something that could be improved over there as well, but it's getting out of scope here.

anarcat avatar Feb 27 '24 20:02 anarcat