diff --git a/Makefile b/Makefile index 8913070..c346475 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ bats: shellcheck: @echo Running shellcheck @shellcheck --version || true - @shellcheck -s bash yadm bootstrap test/*.bash + @shellcheck -s bash yadm bootstrap test/*.bash completion/yadm.bash_completion @cd test; \ for bats_file in *bats; do \ sed 's/^@test.*{/function test() {/' "$$bats_file" > "/tmp/$$bats_file.bash"; \ diff --git a/completion/README.md b/completion/README.md new file mode 100644 index 0000000..86d5960 --- /dev/null +++ b/completion/README.md @@ -0,0 +1,19 @@ +# Prerequisites + +**yadm** completion only works if Git completions are also enabled. + +# Installation + +## Homebrew + +If using `homebrew` to install **yadm**, completions should automatically be handled if you also install `brew install bash-completion`. This might require you to include the main completion script in your own bashrc file like this: + +``` +[ -f /usr/local/etc/bash_completion ] && source /usr/local/etc/bash_completion +``` + +## Manual installation +Copy the completion script locally, and add this to you bashrc: +``` +[ -f /full/path/to/yadm.bash_completion ] && source /full/path/to/yadm.bash_completion +``` diff --git a/completion/yadm.bash_completion b/completion/yadm.bash_completion new file mode 100644 index 0000000..e538cbc --- /dev/null +++ b/completion/yadm.bash_completion @@ -0,0 +1,85 @@ +# test if git completion is missing, but loader exists, attempt to load +if ! declare -F _git > /dev/null && declare -F _completion_loader > /dev/null; then + _completion_loader git +fi + +# only operate if git completion is present +if declare -F _git > /dev/null; then + + _yadm() { + + local current=${COMP_WORDS[COMP_CWORD]} + local penultimate=${COMP_WORDS[COMP_CWORD-1]} + local antepenultimate=${COMP_WORDS[COMP_CWORD-2]} + + local GIT_DIR + # shellcheck disable=SC2034 + GIT_DIR="$(yadm introspect repo 2>/dev/null)" + + case "$penultimate" in + bootstrap) + COMPREPLY=() + return 0 + ;; + config) + COMPREPLY=( $(compgen -W "$(yadm introspect configs 2>/dev/null)") ) + return 0 + ;; + decrypt) + COMPREPLY=( $(compgen -W "-l" -- "$current") ) + return 0 + ;; + init) + COMPREPLY=( $(compgen -W "-f -w" -- "$current") ) + return 0 + ;; + introspect) + COMPREPLY=( $(compgen -W "commands configs repo switches" -- "$current") ) + return 0 + ;; + help) + COMPREPLY=() # no specific help yet + return 0 + ;; + list) + COMPREPLY=( $(compgen -W "-a" -- "$current") ) + return 0 + ;; + esac + + case "$antepenultimate" in + clone) + COMPREPLY=( $(compgen -W "-f -w --bootstrap --no-bootstrap" -- "$current") ) + return 0 + ;; + esac + + # this condition is so files are completed properly for --yadm-xxx options + if [[ ! "$penultimate" =~ ^- ]]; then + # TODO: somehow solve the problem with [--yadm-xxx option] being + # incompatible with what git expects, namely [--arg=option] + _git + fi + if [[ "$current" =~ ^- ]]; then + local matching + matching=$(compgen -W "$(yadm introspect switches 2>/dev/null)" -- "$current") + __gitcompappend "$matching" + fi + + if [ "$COMP_CWORD" == 1 ] || [[ "$antepenultimate" =~ ^- ]] ; then + local matching + matching=$(compgen -W "$(yadm introspect commands 2>/dev/null)" -- "$current") + __gitcompappend "$matching" + fi + + # remove duplicates found in COMPREPLY (a native bash way could be better) + if [ -n "${COMPREPLY[*]}" ]; then + COMPREPLY=($(echo "${COMPREPLY[@]}" | sort -u)) + fi + + } + + complete -o bashdefault -o default -F _yadm yadm 2>/dev/null \ + || complete -o default -F _yadm yadm + +fi diff --git a/test/115_accept_introspect.bats b/test/115_accept_introspect.bats new file mode 100644 index 0000000..c283f6d --- /dev/null +++ b/test/115_accept_introspect.bats @@ -0,0 +1,99 @@ +load common +load_fixtures +status=;output=; #; populated by bats run() + +function count_introspect() { + local category="$1" + local expected_status="$2" + local expected_words="$3" + local expected_regex="$4" + + run "${T_YADM_Y[@]}" introspect "$category" + local output_words + output_words=$(wc -w <<< "$output") + + if [ "$status" -ne "$expected_status" ]; then + echo "ERROR: Unexpected exit code (expected $expected_status, got $status)" + return 1; + fi + + if [ "$output_words" -ne "$expected_words" ]; then + echo "ERROR: Unexpected number of output words (expected $expected_words, got $output_words)" + return 1; + fi + + if [ -n "$expected_regex" ]; then + if [[ ! "$output" =~ $expected_regex ]]; then + echo "OUTPUT:$output" + echo "ERROR: Output does not match regex: $expected_regex" + return 1; + fi + fi + +} + +@test "Command 'introspect' (no category)" { + echo " + When 'introspect' command is provided, + And no category is provided + Produce no output + Exit with 0 + " + + count_introspect "" 0 0 +} + +@test "Command 'introspect' (invalid category)" { + echo " + When 'introspect' command is provided, + And an invalid category is provided + Produce no output + Exit with 0 + " + + count_introspect "invalid_cat" 0 0 +} + +@test "Command 'introspect' (commands)" { + echo " + When 'introspect' command is provided, + And category 'commands' is provided + Produce command list + Exit with 0 + " + + count_introspect "commands" 0 15 'version' +} + +@test "Command 'introspect' (configs)" { + echo " + When 'introspect' command is provided, + And category 'configs' is provided + Produce switch list + Exit with 0 + " + + count_introspect "configs" 0 11 'yadm\.auto-alt' +} + +@test "Command 'introspect' (repo)" { + echo " + When 'introspect' command is provided, + And category 'repo' is provided + Output repo + Exit with 0 + " + + count_introspect "repo" 0 1 "$T_DIR_REPO" +} + +@test "Command 'introspect' (switches)" { + echo " + When 'introspect' command is provided, + And category 'switches' is provided + Produce switch list + Exit with 0 + " + + count_introspect "switches" 0 7 '--yadm-dir' +} diff --git a/yadm b/yadm index 366845e..bb25d27 100755 --- a/yadm +++ b/yadm @@ -57,7 +57,7 @@ function main() { #; parse command line arguments local retval=0 - internal_commands="^(alt|bootstrap|clean|clone|config|decrypt|encrypt|enter|help|init|list|perms|version)$" + internal_commands="^(alt|bootstrap|clean|clone|config|decrypt|encrypt|enter|help|init|introspect|list|perms|version)$" if [ -z "$*" ] ; then #; no argumnts will result in help() help @@ -338,21 +338,13 @@ function config() { if [ -z "$*" ] ; then #; with no parameters, provide some helpful documentation + echo "yadm supports the following configurations:" + echo + for supported_config in $(introspect_configs); do + echo " ${supported_config}" + done + echo cat << EOF -yadm supports the following configurations: - - local.class - local.hostname - local.os - local.user - yadm.auto-alt - yadm.auto-perms - yadm.git-program - yadm.gpg-perms - yadm.gpg-program - yadm.gpg-recipient - yadm.ssh-perms - Please read the CONFIGURATION section in the man page for more details about configurations, and how to adjust them. @@ -558,6 +550,66 @@ function init() { } +function introspect() { + case "$1" in + commands|configs|repo|switches) + "introspect_$1" + ;; + esac +} + +function introspect_commands() { + cat <<-EOF +alt +bootstrap +clean +clone +config +decrypt +encrypt +enter +gitconfig +help +init +introspect +list +perms +version +EOF +} + +function introspect_configs() { + cat << EOF +local.class +local.hostname +local.os +local.user +yadm.auto-alt +yadm.auto-perms +yadm.git-program +yadm.gpg-perms +yadm.gpg-program +yadm.gpg-recipient +yadm.ssh-perms +EOF +} + +function introspect_repo() { + echo "$YADM_REPO" +} + +function introspect_switches() { + cat <<-EOF +--yadm-archive +--yadm-bootstrap +--yadm-config +--yadm-dir +--yadm-encrypt +--yadm-repo +-Y +EOF +} + function list() { require_repo diff --git a/yadm.1 b/yadm.1 index d5c5e31..4ee349e 100644 --- a/yadm.1 +++ b/yadm.1 @@ -49,6 +49,9 @@ list .BR yadm " alt .BR yadm " perms + +.BR yadm " introspect +.I category .SH DESCRIPTION .B yadm is a tool for managing a collection of files across multiple computers, @@ -242,6 +245,17 @@ Print a list of files managed by option will cause all managed files to be listed. Otherwise, the list will only include files from the current directory or below. .TP +.BI introspect " category +Report internal +.B yadm +data. Supported categories are +.IR commands , +.IR configs , +.IR repo, +and +.IR switches . +The purpose of introspection is to support command line completion. +.TP .B perms Update permissions as described in the PERMISSIONS section. It is usually unnecessary to run this command, as