pass-update
pass-update copied to clipboard
Bash completion Ubuntu 20.04
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
I'm experiencing the exact same problem on Debian 11 (bullseye). With the exact same workaround of source
ing it in .bashrc.
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.
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.
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.
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
.
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?
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)
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.
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.