From defeccc04d6edc9ff4a9c35e71c35940417b4b24 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Sat, 2 Apr 2016 22:45:01 +0200 Subject: [PATCH] Run shellcheck on bash completion --- .travis.yml | 6 +++++- bash_completions.go | 30 ++++++++++++++++-------------- bash_completions_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a6cb7fd..e11869ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,10 @@ go: - 1.4.2 - 1.5.1 - tip +before_install: + - mkdir -p bin + - curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.3/shellcheck + - chmod +x bin/shellcheck script: - - go test -v ./... + - PATH=$PATH:$PWD/bin go test -v ./... - go build diff --git a/bash_completions.go b/bash_completions.go index fd8060ff..3f33bb0e 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -61,7 +61,7 @@ __contains_word() __handle_reply() { - __debug "${FUNCNAME}" + __debug "${FUNCNAME[0]}" case $cur in -*) if [[ $(type -t compopt) = "builtin" ]]; then @@ -75,7 +75,7 @@ __handle_reply() fi COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) if [[ $(type -t compopt) = "builtin" ]]; then - [[ $COMPREPLY == *= ]] || compopt +o nospace + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace fi # complete after --flag=abc @@ -88,11 +88,13 @@ __handle_reply() flag="${cur%%=*}" __index_of_word "${flag}" "${flags_with_completion[@]}" if [[ ${index} -ge 0 ]]; then - COMPREPLY=() PREFIX="" cur="${cur#*=}" + COMPREPLY=() + PREFIX="" + cur="${cur#*=}" ${flags_completion[${index}]} if [ -n "${ZSH_VERSION}" ]; then # zfs completion needs --flag= prefix - eval COMPREPLY=( "\${COMPREPLY[@]/#/${flag}=}" ) + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" fi fi fi @@ -149,7 +151,7 @@ __handle_subdirs_in_dir_flag() __handle_flag() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" # if a command required a flag, and we found it, unset must_have_one_flag() local flagname=${words[c]} @@ -160,15 +162,15 @@ __handle_flag() flagname=${flagname%%=*} # strip everything after the = flagname="${flagname}=" # but put the = back fi - __debug "${FUNCNAME}: looking for ${flagname}" + __debug "${FUNCNAME[0]}: looking for ${flagname}" if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then must_have_one_flag=() fi # keep flag value with flagname as flaghash - if [ ${flagvalue} ] ; then + if [ -n "${flagvalue}" ] ; then flaghash[${flagname}]=${flagvalue} - elif [ ${words[ $((c+1)) ]} ] ; then + elif [ -n "${words[ $((c+1)) ]}" ] ; then flaghash[${flagname}]=${words[ $((c+1)) ]} else flaghash[${flagname}]="true" # pad "true" for bool flag @@ -189,7 +191,7 @@ __handle_flag() __handle_noun() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then must_have_one_noun=() @@ -203,20 +205,20 @@ __handle_noun() __handle_command() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" local next_command if [[ -n ${last_command} ]]; then next_command="_${last_command}_${words[c]//:/__}" else if [[ $c -eq 0 ]]; then - next_command="_$(basename ${words[c]//:/__})" + next_command="_$(basename "${words[c]//:/__}")" else next_command="_${words[c]//:/__}" fi fi c=$((c+1)) - __debug "${FUNCNAME}: looking for ${next_command}" + __debug "${FUNCNAME[0]}: looking for ${next_command}" declare -F $next_command >/dev/null && $next_command } @@ -226,12 +228,12 @@ __handle_word() __handle_reply return fi - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if [[ "${words[c]}" == -* ]]; then __handle_flag elif __contains_word "${words[c]}" "${commands[@]}"; then __handle_command - elif [[ $c -eq 0 ]] && __contains_word "$(basename ${words[c]})" "${commands[@]}"; then + elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then __handle_command else __handle_noun diff --git a/bash_completions_test.go b/bash_completions_test.go index 14fa7e15..6957f8bd 100644 --- a/bash_completions_test.go +++ b/bash_completions_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "os/exec" "strings" "testing" ) @@ -23,6 +24,26 @@ func check(t *testing.T, found, expected string) { } } +func runShellCheck(s string) error { + excluded := []string{ + "SC2034", // PREFIX appears unused. Verify it or export it. + } + cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ",")) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + stdin, err := cmd.StdinPipe() + if err != nil { + return err + } + go func() { + defer stdin.Close() + stdin.Write([]byte(s)) + }() + + return cmd.Run() +} + // World worst custom function, just keep telling you to enter hello! const ( bashCompletionFunc = `__custom_func() { @@ -107,4 +128,13 @@ func TestBashCompletions(t *testing.T) { check(t, str, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`) checkOmit(t, str, cmdDeprecated.Name()) + + // if available, run shellcheck against the script + if err := exec.Command("which", "shellcheck").Run(); err != nil { + return + } + err := runShellCheck(str) + if err != nil { + t.Fatalf("shellcheck failed: %v", err) + } }