Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- multiple identity/recipient support
- written in portable posix shell
- simple to extend
- only ~180 lines of code
- only ~200 lines of code
- pronounced "pah" - as in "papa"


Expand All @@ -30,6 +30,7 @@
[g]it [cmd] - Run git command in the password dir.
[l]ist [cat] - List all entries in a category.
[s]how [name] - Show password for an entry.
[m]ove [src] [name] - Rename a password entry.

env vars:
data directory: export PA_DIR=~/.local/share/pa/passwords
Expand All @@ -52,11 +53,14 @@
$ pa edit test
<opens $EDITOR or vi>

$ pa del test
delete password 'test'? [y/N]: y
$ pa mv test example

$ pa del example
delete password 'example'? [y/N]: y

$ pa git log --oneline
bbe85dc (HEAD -> main) delete 'test'
bbe85dc (HEAD -> main) delete 'example'
c7f76d7 move 'test' to 'example'
b597c04 edit 'test'
cba20cc add 'test'
ef76f7e initial commit
Expand Down Expand Up @@ -91,11 +95,6 @@
probably the default location:
~/.local/share/pa/passwords

> how do i rename a password?

cd ~/.local/share/pa/passwords
mv foo.age bar.age


credits
- pa was originally forked from pash[4] by dylanaraps[5]
Expand Down
39 changes: 24 additions & 15 deletions contrib/pa-completion-new.bash
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
# requires bash-completion >= v2.12.0, preferably >= v2.17.0

_comp_pa_entries() {
pushd "$PA_DIR/passwords" &>/dev/null || return
_comp_compgen_split -l "$(find . -type f -name \*.age | sed 's/..//;s/\.age$//')"
popd &>/dev/null || return
}

_comp_pa_categories() {
_comp_compgen -C "$PA_DIR/passwords" filedir -df
local i
for i in "${!COMPREPLY[@]}"; do
if [[ ${COMPREPLY[i]} == .git/ ]]; then
unset 'COMPREPLY[i]'
break
fi
done
}

_comp_cmd_pa() {
local words cword
_comp_initialize -- "$@" || return

if [[ $cword -eq 1 ]]; then
_comp_compgen_split "add del edit git list show"
_comp_compgen_split "add del edit git list move show"
return
fi

local PA_DIR=${PA_DIR:-$HOME/.local/share/pa}
[[ -d $PA_DIR/passwords ]] || return

case ${words[1]} in
[al]*)
if [[ $cword -eq 2 ]]; then
_comp_compgen -C "$PA_DIR/passwords" filedir -df
local i
for i in "${!COMPREPLY[@]}"; do
if [[ ${COMPREPLY[i]} == .git/ ]]; then
unset 'COMPREPLY[i]'
break
fi
done
fi
;;
[des]*)
[al]*) [[ $cword -eq 2 ]] && _comp_pa_categories ;;
[des]*) [[ $cword -eq 2 ]] && _comp_pa_entries ;;
m*)
if [[ $cword -eq 2 ]]; then
_comp_compgen_split -l "$(${words[0]} l 2>/dev/null)"
_comp_pa_entries
elif [[ $cword -eq 3 ]]; then
_comp_pa_categories
fi
;;
g*)
Expand Down
51 changes: 46 additions & 5 deletions contrib/pa-completion.fish
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
function __fish_complete_pa -d 'Complete pa'
set -l cmd (commandline -opc)
test (count $cmd) -eq 2; and string match -q -r $argv[1] $cmd[2]
function __fish_pa_using_command
set -l cmd (commandline -xpc)
test (count $cmd) -gt 1; and string match -q -r $argv[1] $cmd[2]
end

function __fish_pa_categories
pushd $PA_DIR/passwords >/dev/null
and __fish_complete_directories (commandline -ct) ''
popd >/dev/null
end

function __fish_pa_entries
pushd $PA_DIR/passwords >/dev/null
and path change-extension '' (string sub -s 3 (find . -type f -name \*.age))
popd >/dev/null
end

function __fish_pa_move_complete
set -l cmd_count (count (commandline -xpc))
if test $cmd_count -eq 2
__fish_pa_entries
else if test $cmd_count -eq 3
__fish_pa_categories
end
end

function __fish_pa_git_complete
set -l git_cmd (commandline -xpc) (commandline -ct)
set -e git_cmd[1 2]
pushd $PA_DIR/passwords >/dev/null
and complete -C"git $git_cmd"
popd >/dev/null
end

complete -c pa -f
complete -c pa -n "__fish_complete_pa ^[des]" -a "(pa l)"
complete -c pa -n "__fish_complete_pa ^l" -a "(pa l | sed 's/[^/]\+\$//' | grep '/\$' | sort -u)"
complete -c pa -n __fish_is_first_token -a add -d 'Add a password entry'
complete -c pa -n __fish_is_first_token -a delete -d 'Delete a password entry'
complete -c pa -n __fish_is_first_token -a edit -d "Edit a password entry with $EDITOR"
complete -c pa -n __fish_is_first_token -a git -d 'Run git command in the password dir'
complete -c pa -n __fish_is_first_token -a list -d 'List all entries in a category'
complete -c pa -n __fish_is_first_token -a move -d 'Rename a password entry'
complete -c pa -n __fish_is_first_token -a show -d 'Show password for an entry'

set -q PA_DIR || set PA_DIR $HOME/.local/share/pa

complete -c pa -n '__fish_pa_using_command ^[al]' -a '(__fish_is_nth_token 2; and __fish_pa_categories)'
complete -c pa -n '__fish_pa_using_command ^[des]' -a '(__fish_is_nth_token 2; and __fish_pa_entries)'
complete -c pa -n '__fish_pa_using_command ^m' -a '(__fish_pa_move_complete)'
complete -c pa -n '__fish_pa_using_command ^g' -a '(__fish_pa_git_complete)'
30 changes: 26 additions & 4 deletions pa
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ pw_list() {
find "./$name" -type f -name \*.age | sed 's/..//;s/\.age$//' | sort
}

pw_move() {
mkdir -p "$(dirname -- "$name")" ||
die "couldn't create category '$(dirname -- "$name")'"

mv -- "$src.age" "$name.age"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smart


if $git_enabled; then
git rm -q "./$src.age"
git_add_and_commit "./$name.age" "move '$src' to '$name'"
fi
}

git_add_and_commit() {
git add "$1" ||
die "couldn't git add $1"
Expand Down Expand Up @@ -187,6 +199,7 @@ usage() {
[g]it [cmd] - Run git command in the password dir.
[l]ist [cat] - List all entries in a category.
[s]how [name] - Show password for an entry.
[m]ove [src] [name] - Rename a password entry.

env vars:
data directory: export PA_DIR=~/.local/share/pa
Expand Down Expand Up @@ -252,24 +265,32 @@ main() {
exit $?
}

glob "$command" 'm*' && { src=$1 && [ "$src" ] ||
die "missing [src] argument"; } && shift

# Combine the rest of positional arguments into
# a name and remove control characters from it
# so that a name can always be safely displayed.
name=$(printf %s "$*" | LC_ALL=C tr -d '[:cntrl:]')

glob "$command" '[ades]*' && [ -z "$name" ] &&
glob "$command" '[adesm]*' && [ -z "$name" ] &&
die "missing [name] argument"

glob "$command" '[ades]*' && { glob "$name" '/*' || glob "$name" '*/'; } &&
glob "$command" '[adesm]*' && { glob "$name" '/*' || glob "$name" '*/' ||
glob "$src" '/*' || glob "$src" '*/'; } &&
die "name can't start or end with '/'"

glob "$command" 'l*' && glob "$name" '/*' &&
die "category can't start with '/'"

glob "$name" '../*' || glob "$name" '*/../*' &&
glob "$name" '../*' || glob "$name" '*/../*' ||
glob "$src" '../*' || glob "$src" '*/../*' &&
die "category went out of bounds"

glob "$command" 'a*' && [ -f "$name.age" ] &&
glob "$command" 'm*' && [ ! -f "$src.age" ] &&
die "password '$src' doesn't exist"

glob "$command" '[am]*' && [ -f "$name.age" ] &&
die "password '$name' already exists"

glob "$command" '[ds]*' && [ ! -f "$name.age" ] &&
Expand Down Expand Up @@ -309,6 +330,7 @@ main() {
e*) pw_edit ;;
l*) pw_list ;;
s*) pw_show ;;
m*) pw_move ;;
*) usage ;;
esac
}
Expand Down
10 changes: 10 additions & 0 deletions test
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ test "$(./pa list)" = "nested/password
test" ||
fail "pa list output should match example"

# pa mv
./pa mv test another/nested password ||
fail "pa mv should rename the test password"

test -f "$PA_DIR/passwords/test.age" &&
fail "pa mv should have removed an old password file"

test -s "$PA_DIR/passwords/another/nested password.age" ||
fail "pa mv should have created a new password file"

# ensure git commits are working
git -C "$PA_DIR/passwords" log | grep -q "add 'nested/password'" ||
fail "git log should have line: add 'nested/password'"
Expand Down