From 498cdfc2bcfba631f444d3820bc1fbf8f20add4a Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Mon, 28 Jan 2019 21:18:44 +0100 Subject: [PATCH 01/13] Adds tests for multiple scipt dirs --- tests/core/run-command-script.bats | 19 +++++++++++++++++++ tests/core/set-scripts-dir.bats | 25 ++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/tests/core/run-command-script.bats b/tests/core/run-command-script.bats index d477219..18bd4fb 100644 --- a/tests/core/run-command-script.bats +++ b/tests/core/run-command-script.bats @@ -23,6 +23,25 @@ teardown() { assert_success 'Can use @go.printf' } +@test "$SUITE: run bash script by sourcing with multiple script dirs" { + echo '#!/bin/bash' >"$TEST_GO_SCRIPT" + printf ". '$_GO_CORE_DIR/go-core.bash' " >>"$TEST_GO_SCRIPT" + printf "'$TEST_GO_SCRIPTS_RELATIVE_DIR' " >>"$TEST_GO_SCRIPT" + echo "'$TEST_GO_SCRIPTS_RELATIVE_DIR/../scripts-2'" >>"$TEST_GO_SCRIPT" + echo '@go "$@"' >>"$TEST_GO_SCRIPT" + + mkdir -p "$TEST_GO_SCRIPTS_DIR/../scripts-2" + TEST_COMMAND_SCRIPT_PATH="$TEST_GO_SCRIPTS_DIR/../scripts-2/test-command" + + @go.create_test_command_script "../scripts-2/test-command" + + echo '#!/bin/bash' >"$TEST_COMMAND_SCRIPT_PATH" + echo '@go.printf "%s" "$*"' >>"$TEST_COMMAND_SCRIPT_PATH" + + run "$TEST_GO_SCRIPT" test-command Can use '@go.printf' + assert_success 'Can use @go.printf' +} + @test "$SUITE: run sh script by sourcing" { echo '#!/bin/sh' >"$TEST_COMMAND_SCRIPT_PATH" echo '@go.printf "%s" "$*"' >>"$TEST_COMMAND_SCRIPT_PATH" diff --git a/tests/core/set-scripts-dir.bats b/tests/core/set-scripts-dir.bats index 6d7bf07..d670dbe 100644 --- a/tests/core/set-scripts-dir.bats +++ b/tests/core/set-scripts-dir.bats @@ -15,18 +15,29 @@ teardown() { assert_success } -@test "$SUITE: produce an error if more than one dir specified when sourced" { +@test "$SUITE: multiple scripts dir successfully set" { + echo "#! /usr/bin/env bash" >"$TEST_GO_SCRIPT" + printf ". '$_GO_ROOTDIR/go-core.bash' " >>"$TEST_GO_SCRIPT" + printf "'$TEST_GO_SCRIPTS_RELATIVE_DIR' " >>"$TEST_GO_SCRIPT" + printf "'scripts-2' " >>"$TEST_GO_SCRIPT" + + mkdir -p "$TEST_GO_SCRIPTS_DIR/../scripts-2" + + run "$TEST_GO_SCRIPT" + assert_success +} + +@test "$SUITE: produce an error if no dir specified when sourced" { # Overwrite the entire script to force the multiple dir error. echo "#! /usr/bin/env bash" >"$TEST_GO_SCRIPT" - echo ". '$_GO_ROOTDIR/go-core.bash' " \ - "'$TEST_GO_SCRIPTS_RELATIVE_DIR' 'test/scripts'" >>"$TEST_GO_SCRIPT" + echo ". '$_GO_ROOTDIR/go-core.bash' " >>"$TEST_GO_SCRIPT" run "$TEST_GO_SCRIPT" assert_failure \ - 'ERROR: there should be exactly one command script dir specified' + 'ERROR: no command script dir specified' } -@test "$SUITE: produce an error if the script dir does not exist" { +@test "$SUITE: produce an error if a script dir does not exist" { local expected='ERROR: command script directory ' expected+="$TEST_GO_SCRIPTS_DIR does not exist" @@ -35,14 +46,14 @@ teardown() { assert_failure "$expected" } -@test "$SUITE: produce an error if the script dir isn't a directory" { +@test "$SUITE: produce an error if a script dir isn't a directory" { rm -rf "$TEST_GO_SCRIPTS_DIR" printf '' >"$TEST_GO_SCRIPTS_DIR" run "$TEST_GO_SCRIPT" assert_failure "ERROR: $TEST_GO_SCRIPTS_DIR is not a directory" } -@test "$SUITE: produce an error if the script dir can't be read or accessed" { +@test "$SUITE: produce an error if a script dir can't be read or accessed" { skip_if_cannot_trigger_file_permission_failure local expected="ERROR: you do not have permission to access the " From 92eb68dbcc32c63aa31d50e6e6d0712f060f03b7 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Mon, 28 Jan 2019 21:19:43 +0100 Subject: [PATCH 02/13] Supports multiple scripts dir --- go-core.bash | 78 ++++++++++++++++++++--------------- lib/internal/set-search-paths | 11 +++-- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/go-core.bash b/go-core.bash index 61cf511..05fc895 100755 --- a/go-core.bash +++ b/go-core.bash @@ -110,8 +110,8 @@ declare _GO_IMPORTED_MODULE_FILES=() # Used in the plugin module namespace collision warning message. declare _GO_IMPORTED_MODULE_CALLERS=() -# Path to the project's script directory -declare _GO_SCRIPTS_DIR= +# Paths to the project's script directories +declare _GO_SCRIPTS_DIR=() # Directory containing Bats tests, relative to `_GO_ROOTDIR` declare -r -x _GO_TEST_DIR="${_GO_TEST_DIR:-tests}" @@ -139,7 +139,7 @@ declare -x _GO_CMD_ARGV= # # If a command script is running as a plugin, this value will be the plugins # directory of the top-level `./go` script. -declare _GO_PLUGINS_DIR= +declare _GO_PLUGINS_DIR=() # Directories containing executable plugin scripts. declare _GO_PLUGINS_PATHS=() @@ -265,16 +265,19 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # Returns: # Zero if `search_func` ever returns zero, nonzero otherwise @go.search_plugins() { - local __gsp_plugins_dir="$_GO_SCRIPTS_DIR/plugins" - - while true; do - if "$1" "$__gsp_plugins_dir"; then - return - elif [[ "$__gsp_plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then - return 1 - fi - __gsp_plugins_dir="${__gsp_plugins_dir%/plugins/*}/plugins" + local __gsp_plugins_dir + + for __gsp_plugins_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do + while true; do + if "$1" "$__gsp_plugins_dir"; then + return + elif [[ "$__gsp_plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then + break + fi + __gsp_plugins_dir="${__gsp_plugins_dir%/plugins/*}/plugins" + done done + return 1 } # Main driver of ./go script functionality. @@ -332,11 +335,15 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" return 1 fi - if [[ "${__go_cmd_path#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/bin/ ]]; then - _@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}" - else - _@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}" - fi + local script_dir + for script_dir in "${_GO_SCRIPTS_DIR}"; do + if [[ "${__go_cmd_path#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/bin/ ]]; then + _@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}" + return + fi + done + + _@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}" } _@go.source_builtin() { @@ -397,23 +404,28 @@ _@go.run_command_script() { } _@go.set_scripts_dir() { - local scripts_dir="$_GO_ROOTDIR/$1" - - if [[ "$#" -ne '1' ]]; then - echo "ERROR: there should be exactly one command script dir specified" >&2 - return 1 - elif [[ ! -e "$scripts_dir" ]]; then - echo "ERROR: command script directory $scripts_dir does not exist" >&2 - return 1 - elif [[ ! -d "$scripts_dir" ]]; then - echo "ERROR: $scripts_dir is not a directory" >&2 - return 1 - elif [[ ! -r "$scripts_dir" || ! -x "$scripts_dir" ]]; then - echo "ERROR: you do not have permission to access the $scripts_dir" \ - "directory" >&2 + if [[ "$#" -eq '0' ]]; then + echo "ERROR: no command script dir specified" >&2 return 1 fi - _GO_SCRIPTS_DIR="$scripts_dir" + + local scripts_dir + while [[ "$#" -gt 0 ]]; do + scripts_dir="$_GO_ROOTDIR/$1" + if [[ ! -e "$scripts_dir" ]]; then + echo "ERROR: command script directory $scripts_dir does not exist" >&2 + return 1 + elif [[ ! -d "$scripts_dir" ]]; then + echo "ERROR: $scripts_dir is not a directory" >&2 + return 1 + elif [[ ! -r "$scripts_dir" || ! -x "$scripts_dir" ]]; then + echo "ERROR: you do not have permission to access the $scripts_dir" \ + "directory" >&2 + return 1 + fi + shift + done + _GO_SCRIPTS_DIR+=("$scripts_dir") } if ! _@go.set_scripts_dir "$@"; then @@ -428,4 +440,4 @@ elif [[ -z "$COLUMNS" ]]; then fi export COLUMNS="${COLUMNS:-80}" fi -_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins" +_GO_PLUGINS_DIR=("${_GO_SCRIPTS_DIR[@]/%//plugins}") diff --git a/lib/internal/set-search-paths b/lib/internal/set-search-paths index b540b39..96c49a5 100644 --- a/lib/internal/set-search-paths +++ b/lib/internal/set-search-paths @@ -3,13 +3,16 @@ _@go.set_search_paths_add_plugin_paths() { local plugin_paths=("$1"/*/bin) local plugin_path + local script_dir if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then # Ensure a plugin's _GO_SCRIPTS_DIR isn't duplicated in _GO_PLUGINS_PATHS. for plugin_path in "${plugin_paths[@]}"; do - if [[ "$plugin_path" != "$_GO_SCRIPTS_DIR" ]]; then - _GO_PLUGINS_PATHS+=("$plugin_path") - fi + for script_dir in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "$plugin_path" != "$script_dir" ]]; then + _GO_PLUGINS_PATHS+=("$plugin_path") + fi + done done fi return 1 @@ -21,7 +24,7 @@ _@go.set_search_paths() { if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then _GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH") fi - _GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "$_GO_SCRIPTS_DIR") + _GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "${_GO_SCRIPTS_DIR[@]}") # A plugin's own local plugin paths will appear before inherited ones. If # there is a version incompatibility issue with other installed plugins, this From 434e845f3758f3f25ff23eb9dfb385261eea0b21 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Mon, 28 Jan 2019 21:43:10 +0100 Subject: [PATCH 03/13] Fixes tests --- tests/vars.bats | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/vars.bats b/tests/vars.bats index 9eedb0c..b250461 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -50,11 +50,11 @@ quotify_expected() { 'declare -- _GO_INJECT_MODULE_PATH=""' 'declare -- _GO_INJECT_SEARCH_PATH=""' "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -- _GO_PLUGINS_DIR=\"$TEST_GO_PLUGINS_DIR\"" + "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR\""\) 'declare -a _GO_PLUGINS_PATHS=()' "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -- _GO_SCRIPTS_DIR=\"$TEST_GO_SCRIPTS_DIR\"" + "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR\""\) "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") @@ -124,11 +124,11 @@ quotify_expected() { "declare -x _GO_INJECT_MODULE_PATH=\"$TEST_GO_ROOTDIR/lib\"" "declare -x _GO_INJECT_SEARCH_PATH=\"$TEST_GO_ROOTDIR/bin\"" "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -- _GO_PLUGINS_DIR=\"$TEST_GO_PLUGINS_DIR\"" + "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR\""\) "declare -a _GO_PLUGINS_PATHS=(${plugins_paths[*]})" "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -- _GO_SCRIPTS_DIR=\"$TEST_GO_SCRIPTS_DIR\"" + "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR\""\) "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") From 87fe3ecf2930b3160cd450b25b05229431f84c27 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Mon, 28 Jan 2019 21:59:05 +0100 Subject: [PATCH 04/13] Updates README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 62cc4bd..2a808ab 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,8 @@ where: file within your project's copy of the framework (adjusted to reflect where your copy of `go-script-bash` actually resides) - `scripts` is the path to the directory holding your project's command scripts - relative to the project root (it can be any name you like) + relative to the project root (it can be any name you like). You can specify + multiple paths, separated by a space, according to your project's structure. #### Directory structure @@ -338,9 +339,10 @@ The following variables are set by the framework based on the above example * `_GO_SCRIPTS_DIR`: `$_GO_ROOTDIR/scripts` * `_GO_PLUGINS_DIR`: `/absolute/path/to/project-root/plugins` -For plugins, `_GO_ROOTDIR` and `_GO_SCRIPTS_DIR` will be scoped to the root -directory of the plugin installation; the other variables will remain the same. -See `./go help plugins` for more details. +`_GO_SCRIPTS_DIR` and `_GO_PLUGINS_DIR` are arrays of file paths to support +flexible project structures. For plugins, `_GO_ROOTDIR` and `_GO_SCRIPTS_DIR` +will be scoped to the root directory of the plugin installation; the other +variables will remain the same. See `./go help plugins` for more details. #### Command scripts From 1359995dd1a9afe2eb7f247daed1cf25fac0ad8f Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Tue, 29 Jan 2019 01:11:45 +0100 Subject: [PATCH 05/13] Fixes --- go-core.bash | 10 +++++++--- tests/core/run-command-script.bats | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/go-core.bash b/go-core.bash index 05fc895..e3547c5 100755 --- a/go-core.bash +++ b/go-core.bash @@ -266,16 +266,20 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # Zero if `search_func` ever returns zero, nonzero otherwise @go.search_plugins() { local __gsp_plugins_dir + local plugin_dir + local -i i=0 - for __gsp_plugins_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do + for plugin_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do + __gsp_plugins_dir="$plugin_dir" while true; do if "$1" "$__gsp_plugins_dir"; then return - elif [[ "$__gsp_plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then + elif [[ "$__gsp_plugins_dir" == "${_GO_PLUGINS_DIR[i]}" ]]; then break fi __gsp_plugins_dir="${__gsp_plugins_dir%/plugins/*}/plugins" done + i=i+1 done return 1 } @@ -424,8 +428,8 @@ _@go.set_scripts_dir() { return 1 fi shift + _GO_SCRIPTS_DIR+=("$scripts_dir") done - _GO_SCRIPTS_DIR+=("$scripts_dir") } if ! _@go.set_scripts_dir "$@"; then diff --git a/tests/core/run-command-script.bats b/tests/core/run-command-script.bats index 18bd4fb..9b5bc4e 100644 --- a/tests/core/run-command-script.bats +++ b/tests/core/run-command-script.bats @@ -26,8 +26,8 @@ teardown() { @test "$SUITE: run bash script by sourcing with multiple script dirs" { echo '#!/bin/bash' >"$TEST_GO_SCRIPT" printf ". '$_GO_CORE_DIR/go-core.bash' " >>"$TEST_GO_SCRIPT" - printf "'$TEST_GO_SCRIPTS_RELATIVE_DIR' " >>"$TEST_GO_SCRIPT" - echo "'$TEST_GO_SCRIPTS_RELATIVE_DIR/../scripts-2'" >>"$TEST_GO_SCRIPT" + printf "scripts-2 " >>"$TEST_GO_SCRIPT" + echo "'$TEST_GO_SCRIPTS_RELATIVE_DIR'" >>"$TEST_GO_SCRIPT" echo '@go "$@"' >>"$TEST_GO_SCRIPT" mkdir -p "$TEST_GO_SCRIPTS_DIR/../scripts-2" From 26bcf9c5edf5e5185f79562348f0139112acd48a Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Tue, 29 Jan 2019 17:18:58 +0100 Subject: [PATCH 06/13] Allows to load modules from multiple sources --- lib/internal/use | 28 +++++++++++++++++++------- tests/modules/use.bats | 45 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/lib/internal/use b/lib/internal/use index a5ee0a9..e3e6d3c 100644 --- a/lib/internal/use +++ b/lib/internal/use @@ -90,19 +90,33 @@ _@go.find_module() { return fi fi + __go_module_file="$_GO_CORE_DIR/lib/$__go_module_name" if [[ ! -f "$__go_module_file" ]]; then - __go_module_file="$_GO_SCRIPTS_DIR/lib/$__go_module_name" + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + __go_module_file="$scripts_dir/lib/$__go_module_name" + + if [[ -f "$__go_module_file" ]]; then + return + fi + done + + __go_module_file="$_GO_ROOTDIR/lib/$__go_module_name" if [[ ! -f "$__go_module_file" ]]; then - __go_module_file="$_GO_ROOTDIR/lib/$__go_module_name" - if [[ ! -f "$__go_module_file" ]]; then - # Convert / to plugins//lib/ - __go_module_file="${__go_module_name/\///lib/}" - @go.search_plugins '_@go.find_plugin_module' - fi + local plugins_dir + for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + __go_module_file="$plugins_dir/lib/$__go_module_name" + + if [[ ! -f "$__go_module_file" ]]; then + # Convert / to plugins//lib/ + __go_module_file="${__go_module_name/\///lib/}" + @go.search_plugins '_@go.find_plugin_module' + fi + done fi fi } diff --git a/tests/modules/use.bats b/tests/modules/use.bats index 395512d..27a382b 100644 --- a/tests/modules/use.bats +++ b/tests/modules/use.bats @@ -6,18 +6,24 @@ load "$_GO_CORE_DIR/lib/testing/stubbing" BUILTIN_MODULE_FILE="$_GO_CORE_DIR/lib/builtin-test" PLUGIN_MODULE_FILE="$TEST_GO_PLUGINS_DIR/test-plugin/lib/plugin-test" +PLUGIN_MODULE_FILE_2="$TEST_GO_SCRIPTS_DIR/../scripts-2/plugins/test-plugin-2/lib/plugin-test-2" EXPORT_MODULE_FILE="$TEST_GO_ROOTDIR/lib/export-test" INTERNAL_MODULE_FILE="$TEST_GO_SCRIPTS_DIR/lib/internal-test" +INTERNAL_MODULE_FILE_2="$TEST_GO_SCRIPTS_DIR/../scripts-2/lib/internal-test-2" TEST_MODULES=( "$BUILTIN_MODULE_FILE" "$PLUGIN_MODULE_FILE" + "$PLUGIN_MODULE_FILE_2" "$EXPORT_MODULE_FILE" - "$INTERNAL_MODULE_FILE") + "$INTERNAL_MODULE_FILE" + "$INTERNAL_MODULE_FILE_2") IMPORTS=( 'test-plugin/plugin-test' + 'test-plugin-2/plugin-test-2' 'internal-test' + 'internal-test-2' 'builtin-test' 'export-test') @@ -25,15 +31,23 @@ CALLER="$TEST_GO_SCRIPT:3 main" EXPECTED=( 'plugin-test loaded' + 'plugin-test-2 loaded' 'internal-test loaded' + 'internal-test-2 loaded' 'builtin-test loaded' 'export-test loaded' 'module: test-plugin/plugin-test' "source: $PLUGIN_MODULE_FILE" "caller: $CALLER" + 'module: test-plugin-2/plugin-test-2' + "source: $PLUGIN_MODULE_FILE_2" + "caller: $CALLER" 'module: internal-test' "source: $INTERNAL_MODULE_FILE" "caller: $CALLER" + 'module: internal-test-2' + "source: $INTERNAL_MODULE_FILE_2" + "caller: $CALLER" 'module: builtin-test' "source: $BUILTIN_MODULE_FILE" "caller: $CALLER" @@ -65,7 +79,13 @@ do_setup() { fi done - @go.create_test_go_script \ + # Repeating the body of '@go.create_test_go_script' to include two script + # directories + set "$DISABLE_BATS_SHELL_OPTIONS" + create_bats_test_script 'go' \ + ". '$_GO_CORE_DIR/go-core.bash' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR/../scripts-2'" \ ". \"\$_GO_USE_MODULES\" $*" \ 'for ((i=0; i != ${#_GO_IMPORTED_MODULES[@]}; ++i)); do' \ " printf -- 'module: %s\nsource: %s\ncaller: %s\n' \\" \ @@ -74,6 +94,11 @@ do_setup() { " \"\${_GO_IMPORTED_MODULE_CALLERS[\$i]}\"" \ 'done' + if [[ ! -d "$TEST_GO_SCRIPTS_DIR" ]]; then + mkdir "$TEST_GO_SCRIPTS_DIR" + fi + restore_bats_shell_options "$?" + local module for module in "${TEST_MODULES[@]}"; do mkdir -p "${module%/*}" @@ -164,13 +189,15 @@ do_setup() { @test "$SUITE: error if module contains errors" { # These correspond to the 'internal-test' module. - local module="${IMPORTS[1]}" - local module_file="${TEST_MODULES[3]}" + local module="${IMPORTS[2]}" + local module_file="${TEST_MODULES[4]}" echo "This is a totally broken module." > "$module_file" run "$TEST_GO_SCRIPT" "${IMPORTS[@]}" - local expected=("${IMPORTS[0]##*/} loaded" + local expected=( + "${IMPORTS[0]##*/} loaded" + "${IMPORTS[1]##*/} loaded" "$module_file: line 1: This: command not found" "ERROR: Failed to import $module module from $module_file at:" "$GO_USE_MODULES_STACK_ITEM" @@ -180,15 +207,17 @@ do_setup() { @test "$SUITE: error if module returns an error" { # These correspond to the 'internal-test' module. - local module="${IMPORTS[1]}" - local module_file="${TEST_MODULES[3]}" + local module="${IMPORTS[2]}" + local module_file="${TEST_MODULES[4]}" local error_message='These violent delights have violent ends...' echo "echo '$error_message' >&2" > "$module_file" echo "return 1" >> "$module_file" run "$TEST_GO_SCRIPT" "${IMPORTS[@]}" - local expected=("${IMPORTS[0]##*/} loaded" + local expected=( + "${IMPORTS[0]##*/} loaded" + "${IMPORTS[1]##*/} loaded" "$error_message" "ERROR: Failed to import $module module from $module_file at:" "$GO_USE_MODULES_STACK_ITEM" From 8a36c50e1ac5e60fc704d7243c2a607bc93eeae7 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Wed, 30 Jan 2019 16:41:11 +0100 Subject: [PATCH 07/13] Updates rest of commands/libs --- go-core.bash | 21 ++-- lib/internal/complete | 6 +- lib/internal/set-search-paths | 12 +- lib/internal/use | 14 ++- lib/log | 22 +++- lib/testing/environment | 10 +- libexec/help | 2 +- libexec/modules | 36 +++++- libexec/new | 124 +++++++++++++++---- tests/complete.bats | 4 +- tests/core/columns.bats | 6 +- tests/core/plugin-scope-execution.bats | 1 + tests/core/plugin-scope-variable-values.bats | 5 +- tests/core/search-plugins.bats | 5 +- tests/help.bats | 2 +- tests/log/setup-project.bats | 9 +- tests/new.bats | 107 ++++++++++++++-- tests/path/init-constants.bats | 16 ++- tests/testing/stack-trace.bats | 4 +- tests/vars.bats | 20 +-- 20 files changed, 338 insertions(+), 88 deletions(-) diff --git a/go-core.bash b/go-core.bash index e3547c5..a1584ae 100755 --- a/go-core.bash +++ b/go-core.bash @@ -266,20 +266,23 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # Zero if `search_func` ever returns zero, nonzero otherwise @go.search_plugins() { local __gsp_plugins_dir - local plugin_dir - local -i i=0 + local scripts_dir + local plugins_dir - for plugin_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do - __gsp_plugins_dir="$plugin_dir" + for scripts_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do + __gsp_plugins_dir="$scripts_dir" while true; do if "$1" "$__gsp_plugins_dir"; then return - elif [[ "$__gsp_plugins_dir" == "${_GO_PLUGINS_DIR[i]}" ]]; then - break + else + for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + if [[ "$__gsp_plugins_dir" == "$plugins_dir" ]]; then + break 2 + fi + done fi __gsp_plugins_dir="${__gsp_plugins_dir%/plugins/*}/plugins" done - i=i+1 done return 1 } @@ -340,8 +343,8 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" fi local script_dir - for script_dir in "${_GO_SCRIPTS_DIR}"; do - if [[ "${__go_cmd_path#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/bin/ ]]; then + for script_dir in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "${__go_cmd_path#$script_dir}" =~ /plugins/[^/]+/bin/ ]]; then _@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}" return fi diff --git a/lib/internal/complete b/lib/internal/complete index a187dd3..b7c0d04 100644 --- a/lib/internal/complete +++ b/lib/internal/complete @@ -6,7 +6,11 @@ _@go.complete_top_level_commands() { _@go.source_builtin 'commands' else printf 'help\n' - _@go.source_builtin 'commands' "$_GO_SCRIPTS_DIR" + + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + _@go.source_builtin 'commands' "$scripts_dir" + done fi } diff --git a/lib/internal/set-search-paths b/lib/internal/set-search-paths index 96c49a5..26bced3 100644 --- a/lib/internal/set-search-paths +++ b/lib/internal/set-search-paths @@ -3,16 +3,20 @@ _@go.set_search_paths_add_plugin_paths() { local plugin_paths=("$1"/*/bin) local plugin_path - local script_dir if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then # Ensure a plugin's _GO_SCRIPTS_DIR isn't duplicated in _GO_PLUGINS_PATHS. for plugin_path in "${plugin_paths[@]}"; do - for script_dir in "${_GO_SCRIPTS_DIR[@]}"; do - if [[ "$plugin_path" != "$script_dir" ]]; then - _GO_PLUGINS_PATHS+=("$plugin_path") + local included_path + local found='false' + for included_path in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "$plugin_path" == "$included_path" ]]; then + found='true' fi done + if [[ "$found" == 'false' ]]; then + _GO_PLUGINS_PATHS+=("$plugin_path") + fi done fi return 1 diff --git a/lib/internal/use b/lib/internal/use index e3e6d3c..39c180a 100644 --- a/lib/internal/use +++ b/lib/internal/use @@ -161,11 +161,15 @@ _@go.use_modules() { # functions and variables get redefined. Keeping a flat module name # namespace allows us to detect such potential collisions and issue a # warning below. - if [[ "$__go_module_file" =~ ^$_GO_PLUGINS_DIR/ ]]; then - __go_module_name="${__go_module_file##*/plugins/}" - __go_module_name="${__go_module_name/\/bin\///}" - __go_module_name="${__go_module_name/\/lib\///}" - fi + local plugins_dir + for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + if [[ "$__go_module_file" =~ ^$plugins_dir/ ]]; then + __go_module_name="${__go_module_file##*/plugins/}" + __go_module_name="${__go_module_name/\/bin\///}" + __go_module_name="${__go_module_name/\/lib\///}" + break + fi + done module_index=0 for loaded_module in "${_GO_IMPORTED_MODULES[@]}"; do diff --git a/lib/log b/lib/log index 793a9a0..6721b17 100644 --- a/lib/log +++ b/lib/log @@ -561,20 +561,34 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' # $1: The path of setup script relative to the project scripts directory # ...: Any arguments to pass through to the 'setup' script @go.setup_project() { - local setup_script="$_GO_SCRIPTS_DIR/$1" + local setup_script local setup_status - shift @go.log START Project setup in "$_GO_ROOTDIR" ((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS)) - if [[ ! -f "$setup_script" ]]; then - @go.log FATAL "Create $setup_script before invoking $FUNCNAME." + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + setup_script="$scripts_dir/$1" + if [[ -f "$setup_script" ]]; then + break + fi + setup_script='' + done + + if [[ -z "$setup_script" ]]; then + local scripts_dirs + IFS=* eval 'scripts_dirs="${_GO_SCRIPTS_DIR[*]}"' + scripts_dirs="${scripts_dirs//\*/ or }" + + @go.log FATAL "Before invoking $FUNCNAME, create '$1' in $scripts_dirs." elif [[ ! -x "$setup_script" ]]; then @go.log FATAL "$setup_script is not executable." fi ((--__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS)) + shift + @go.log RUN "${setup_script#$_GO_ROOTDIR/}" "$@" _@go.run_command_script "$setup_script" "$@" setup_status="$?" diff --git a/lib/testing/environment b/lib/testing/environment index 250b0ad..a810fbd 100644 --- a/lib/testing/environment +++ b/lib/testing/environment @@ -33,8 +33,11 @@ unset -v _GO_MAX_FILE_DESCRIPTORS "${!_GO_LOG@}" "${!__GO_LOG@}" readonly TEST_GO_ROOTDIR="$BATS_TEST_ROOTDIR" readonly TEST_GO_SCRIPT="$TEST_GO_ROOTDIR/go" readonly TEST_GO_SCRIPTS_RELATIVE_DIR="scripts" +readonly TEST_GO_SCRIPTS_RELATIVE_DIR_2="scripts-2" readonly TEST_GO_SCRIPTS_DIR="$TEST_GO_ROOTDIR/$TEST_GO_SCRIPTS_RELATIVE_DIR" +readonly TEST_GO_SCRIPTS_DIR_2="$TEST_GO_ROOTDIR/$TEST_GO_SCRIPTS_RELATIVE_DIR_2" readonly TEST_GO_PLUGINS_DIR="$TEST_GO_SCRIPTS_DIR/plugins" +readonly TEST_GO_PLUGINS_DIR_2="$TEST_GO_SCRIPTS_DIR_2/plugins" # Recursively removes `TEST_GO_ROOTDIR` # @@ -56,7 +59,9 @@ readonly TEST_GO_PLUGINS_DIR="$TEST_GO_SCRIPTS_DIR/plugins" @go.create_test_go_script() { set "$DISABLE_BATS_SHELL_OPTIONS" create_bats_test_script 'go' \ - ". '$_GO_CORE_DIR/go-core.bash' '$TEST_GO_SCRIPTS_RELATIVE_DIR'" \ + ". '$_GO_CORE_DIR/go-core.bash' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR_2' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR'" \ "$@" # Most tests should assume this directory is present. Those that don't should @@ -64,6 +69,9 @@ readonly TEST_GO_PLUGINS_DIR="$TEST_GO_SCRIPTS_DIR/plugins" if [[ ! -d "$TEST_GO_SCRIPTS_DIR" ]]; then mkdir "$TEST_GO_SCRIPTS_DIR" fi + if [[ ! -d "$TEST_GO_SCRIPTS_DIR_2" ]]; then + mkdir "$TEST_GO_SCRIPTS_DIR_2" + fi restore_bats_shell_options "$?" } diff --git a/libexec/help b/libexec/help index 7c3d207..f400d44 100755 --- a/libexec/help +++ b/libexec/help @@ -55,7 +55,7 @@ # enforced. _@go.usage() { - local cmd_paths=("$_GO_SCRIPTS_DIR") + local cmd_paths=("${_GO_SCRIPTS_DIR[@]}") local summaries local summaries_status=0 diff --git a/libexec/modules b/libexec/modules index 3afe2af..52de74a 100755 --- a/libexec/modules +++ b/libexec/modules @@ -124,11 +124,18 @@ _@go.modules_summaries() { _@go.modules_produce_listing() { local action="$1" local modules=("${__go_modules[@]#$_GO_CORE_DIR/lib/}") - modules=("${modules[@]#$_GO_SCRIPTS_DIR/lib/}") + + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + modules=("${modules[@]#$scripts_dir/lib/}") + done modules=("${modules[@]#$_GO_ROOTDIR/lib/}") if [[ -n "$_GO_PLUGINS_DIR" ]]; then - modules=("${modules[@]#$_GO_PLUGINS_DIR/}") + local plugins_dir + for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + modules=("${modules[@]#$plugins_dir/}") + done modules=("${modules[@]/lib\//}") fi @@ -162,9 +169,13 @@ _@go.modules_produce_listing() { } _@go.modules_search_plugins() { + local old_count="${#__go_modules[@]}" for plugin in "$1/"${plugin_glob[0]:-*}; do _@go.modules_find_all_in_dir "$plugin" "${plugin_glob[1]:-*}" done + if [[ "${#__go_modules[@]}" -eq "$old_count" ]]; then + return 1 + fi } _@go.modules_search() { @@ -183,7 +194,10 @@ _@go.modules_search() { _@go.modules_find_all_in_dir "$_GO_CORE_DIR" "$glob" __go_core_modules_end="${#__go_modules[@]}" - _@go.modules_find_all_in_dir "$_GO_SCRIPTS_DIR" "$glob" + local script_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + _@go.modules_find_all_in_dir "$scripts_dir" "$glob" + done __go_internal_modules_end="${#__go_modules[@]}" _@go.modules_find_all_in_dir "$_GO_ROOTDIR" "$glob" @@ -266,18 +280,24 @@ _@go.modules_tab_completion_plugins() { # `glob` indicates specific plugin module. if [[ "$glob" =~ / ]]; then + local old_count="${#__go_modules[@]}" plugin="${glob%/*}" _@go.modules_find_all_in_dir "$1/$plugin" "${glob#*/}*" + if [[ "${#__go_modules[@]}" -eq "$old_count" ]]; then + return 1 + fi return fi # Otherwise collect all plugin modules and the names of the plugins to # which they belong. Which set gets returned will be decided at the end. + local has_results='false' for plugin in "$1"/$glob; do _@go.modules_find_all_in_dir "$plugin" if [[ "${#__go_modules[@]}" -eq '0' ]]; then continue fi + has_results='true' __go_modules=("${__go_modules[@]#$1/}") __go_modules=("${__go_modules[@]/\/lib\///}") @@ -290,6 +310,10 @@ _@go.modules_tab_completion_plugins() { __go_modules=() fi done + + if [[ "$has_results" == 'false' ]]; then + return 1 + fi } _@go.modules_tab_completion() { @@ -329,7 +353,11 @@ _@go.modules_tab_completion() { # Don't search other dirs if completing a plugin (word contains a '/'). if [[ ! "$glob" =~ / ]]; then _@go.modules_find_all_in_dir "$_GO_CORE_DIR" "$glob" - _@go.modules_find_all_in_dir "$_GO_SCRIPTS_DIR" "$glob" + local scripts_dir + local plugins_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + _@go.modules_find_all_in_dir "$scripts_dir" "$glob" + done _@go.modules_find_all_in_dir "$_GO_ROOTDIR" "$glob" fi diff --git a/libexec/new b/libexec/new index 276f750..7f293f5 100755 --- a/libexec/new +++ b/libexec/new @@ -7,10 +7,10 @@ # {{go}} {{cmd}} --type [lines...] # # To generate a new command script in `_GO_SCRIPTS_DIR`: -# {{go}} {{cmd}} --command [...] +# {{go}} {{cmd}} --command [--parent ] [...] # # To generate a new internal module in `_GO_SCRIPTS_DIR/lib`: -# {{go}} {{cmd}} --internal +# {{go}} {{cmd}} --internal [--parent ] # # To generate a new public module in `_GO_ROOTDIR/lib`: # {{go}} {{cmd}} --public @@ -25,6 +25,7 @@ # [lines...] Optional list of lines to add to the file # Permissions to set for the new file # Command script name +# Path to command file (one of `_GO_SCRIPTS_DIRS`) # Subcommand script name # Path to module file relative to `_GO_*DIR/lib` # Path to module file relative to `_GO_TEST_DIR` @@ -46,6 +47,8 @@ _@go.new_tab_completions() { local mode="$2" shift 2 + local paths=() + if [[ "$word_index" -eq '0' ]]; then printf -- '--command --internal --public --test --type' return @@ -55,7 +58,10 @@ _@go.new_tab_completions() { case "$mode" in --command) if [[ "$word_index" -eq '0' ]]; then - _@go.source_builtin 'commands' "$_GO_SCRIPTS_DIR" + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + _@go.source_builtin 'commands' "$scripts_dir" + done else . "$_GO_CORE_DIR/lib/internal/complete" _@go.complete_command_path "$word_index" "$@" @@ -63,18 +69,18 @@ _@go.new_tab_completions() { return ;; --internal) - if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_SCRIPTS_DIR/lib"; then - return 1 + if [[ "$word_index" -eq '0' ]]; then + paths+=("${_GO_SCRIPTS_DIR[@]/%//lib}") fi ;; --public) - if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_ROOTDIR/lib"; then - return 1 + if [[ "$word_index" -eq '0' ]]; then + paths+=("$_GO_ROOTDIR/lib") fi ;; --test) - if [[ "$word_index" -ne '0' ]] || ! cd "$_GO_ROOTDIR/$_GO_TEST_DIR"; then - return 1 + if [[ "$word_index" -eq '0' ]]; then + paths+=("$_GO_ROOTDIR/$_GO_TEST_DIR") fi ;; --type) @@ -82,12 +88,26 @@ _@go.new_tab_completions() { return 1 fi shift + @go.compgen -f -- "$1" ;; *) return 1 ;; esac - @go.compgen -f -- "$1" + + if [[ "${#paths[@]}" -eq 0 ]]; then + return 1 + fi + + local path_exists='false' + for path in "${paths[@]}"; do + cd "$path" && path_exists='true' || continue + @go.compgen -f -- "$1" + done + + if [[ "$path_exists" == 'false' ]]; then + return 1 + fi } _@go.new_file() { @@ -161,21 +181,43 @@ _@go.new_command_script() { _@go.new_command_scripts() { local cmd local cmd_path - local parent_dir="$_GO_SCRIPTS_DIR" + local paths=("$@") local new_scripts=() + local parent_dir="${_GO_SCRIPTS_DIR[0]}" local is_last_cmd local i=0 - if [[ "$#" -eq '0' ]]; then + if [[ "$#" -ge 3 && "$2" == '--parent' ]]; then + parent_dir="$3" + cmd_path="$1" + unset paths[1] + unset paths[2] + elif [[ "$#" -eq 2 && "$2" == '--parent' ]]; then + printf "'--parent' expects an argument.\n" >&2 + return 1 + elif [[ "$#" -eq '0' || -z "${paths[0]}" ]]; then printf 'No command script name specified.\n' >&2 return 1 fi - for cmd in "$@"; do + local found='false' + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "$parent_dir" == "$scripts_dir" ]]; then + found='true' + fi + done + + if [[ "$found" == 'false' ]]; then + printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIR').\n" + return 1 + fi + + for cmd in "${paths[@]}"; do cmd_path="$parent_dir/$cmd" parent_dir="$cmd_path.d" - if [[ "$((++i))" -eq "$#" ]]; then + if [[ "$((++i))" -eq "${#paths[@]}" ]]; then is_last_cmd='true' elif [[ -f "$cmd_path" ]]; then continue @@ -193,7 +235,39 @@ _@go.new_command_scripts() { } _@go.new_module() { - local module_path="$1" + local args=("$@") + local parent_dir="${_GO_SCRIPTS_DIR[0]}" + + if [[ "$#" -ge 3 && "$2" == '--parent' ]]; then + parent_dir="$3" + unset args[1] + unset args[2] + elif [[ "$#" -eq 2 && "$2" == '--parent' ]]; then + printf "'--parent' expects an argument.\n" >&2 + return 1 + elif [[ "$#" -ne 1 ]]; then + printf 'Command expects exactly one path.\n' + return 1 + elif [[ "$#" -eq '0' || -z "$1" ]]; then + printf 'No module name specified.\n' >&2 + return 1 + fi + + local found='false' + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "$parent_dir" == "$scripts_dir" || "$parent_dir" == "$_GO_ROOTDIR" ]]; + then + found='true' + fi + done + + if [[ "$found" == 'false' ]]; then + printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIR').\n" + return 1 + fi + + local module_path="$parent_dir/lib/${args[0]}" local module_relpath="${module_path#*/lib/}" local module_type local impl=('#! /usr/bin/env bash' @@ -204,14 +278,16 @@ _@go.new_module() { '# func_name' '# Short description of the func_name function') - case "${module_path%%/lib/*}" in - $_GO_SCRIPTS_DIR) - module_type='internal module' - ;; - $_GO_ROOTDIR) + if [[ "${module_path%%/lib/*}" == "$_GO_ROOTDIR" ]]; then module_type='public module' - ;; - esac + else + local scripts_dir + for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + if [[ "${module_path%%/lib/*}" == "$scripts_dir" ]]; then + module_type='internal module' + fi + done + fi if ! _@go.new_file "$module_type" "$module_path" '644' "${impl[@]}"; then return 1 @@ -274,10 +350,10 @@ _@go.new() { _@go.new_command_scripts "$@" ;; --internal) - _@go.new_module "$_GO_SCRIPTS_DIR/lib/$1" + _@go.new_module "$@" ;; --public) - _@go.new_module "$_GO_ROOTDIR/lib/$1" + _@go.new_module "${1-}" --parent "$_GO_ROOTDIR" "${@: 2}" ;; --test) _@go.new_test "$_GO_ROOTDIR/$_GO_TEST_DIR/$1" diff --git a/tests/complete.bats b/tests/complete.bats index a77c0d6..f022ff7 100644 --- a/tests/complete.bats +++ b/tests/complete.bats @@ -61,9 +61,9 @@ teardown() { touch "${files[@]/#/$TEST_GO_SCRIPTS_DIR/}" run "$TEST_GO_SCRIPT" complete 1 cd - assert_success 'scripts/' + assert_success 'scripts-2/' 'scripts/' run "$TEST_GO_SCRIPT" complete 1 pushd '' - assert_success 'scripts/' + assert_success 'scripts-2/' 'scripts/' local expected=() @go.test_compgen expected -d "$TEST_GO_SCRIPTS_DIR/" diff --git a/tests/core/columns.bats b/tests/core/columns.bats index 76a3375..fd4c1c9 100644 --- a/tests/core/columns.bats +++ b/tests/core/columns.bats @@ -67,8 +67,10 @@ teardown() { @test "$SUITE: default to 80 columns if tput fails or use mode.com on Windows" { local expected_cols='80' - if [[ "$(mode.com con)" =~ Columns:\ +([0-9]+) ]]; then - expected_cols="${BASH_REMATCH[1]}" + if -v mode.com &>/dev/null; then + if [[ "$(mode.com con)" =~ Columns:\ +([0-9]+) ]]; then + expected_cols="${BASH_REMATCH[1]}" + fi fi # One way to cause tput to fail is to set `$TERM` to null. On Travis it's set diff --git a/tests/core/plugin-scope-execution.bats b/tests/core/plugin-scope-execution.bats index 1ab347e..80847a4 100644 --- a/tests/core/plugin-scope-execution.bats +++ b/tests/core/plugin-scope-execution.bats @@ -233,6 +233,7 @@ teardown() { local test_rootdir="$TEST_GO_ROOTDIR/plugins/rootdir" mkdir -p "$test_rootdir" mv "$TEST_GO_SCRIPT" "$test_rootdir" + mkdir -p "$test_rootdir/scripts-2" # We can't use `@go.create_test_command_script` since we can't change the # readonly `TEST_GO_*` variables. diff --git a/tests/core/plugin-scope-variable-values.bats b/tests/core/plugin-scope-variable-values.bats index fd730dd..24de22f 100644 --- a/tests/core/plugin-scope-variable-values.bats +++ b/tests/core/plugin-scope-variable-values.bats @@ -10,7 +10,8 @@ setup() { local print_scope_implementation=( 'printf -- "_GO_ROOTDIR:\n%s\n" "$_GO_ROOTDIR"' - 'printf -- "_GO_SCRIPTS_DIR:\n%s\n" "$_GO_SCRIPTS_DIR"' + 'printf -- "_GO_SCRIPTS_DIR:\n"' + 'printf -- "%s\n" "${_GO_SCRIPTS_DIR[@]}"' 'printf -- "_GO_PLUGINS_PATHS:\n"' 'printf -- "%s\n" "${_GO_PLUGINS_PATHS[@]}"' 'printf -- "_GO_SEARCH_PATHS:\n"' @@ -43,11 +44,13 @@ setup() { EXPECTED_ROOT_SCOPE_VALUES=('_GO_ROOTDIR:' "$TEST_GO_ROOTDIR" '_GO_SCRIPTS_DIR:' + "$TEST_GO_SCRIPTS_DIR_2" "$TEST_GO_SCRIPTS_DIR" '_GO_PLUGINS_PATHS:' "${EXPECTED_ROOT_SCOPE_PLUGINS_PATHS[@]}" '_GO_SEARCH_PATHS:' "$_GO_CORE_DIR/libexec" + "$TEST_GO_SCRIPTS_DIR_2" "$TEST_GO_SCRIPTS_DIR" "${EXPECTED_ROOT_SCOPE_PLUGINS_PATHS[@]}") } diff --git a/tests/core/search-plugins.bats b/tests/core/search-plugins.bats index 3f5aee1..8f3ae26 100644 --- a/tests/core/search-plugins.bats +++ b/tests/core/search-plugins.bats @@ -17,7 +17,7 @@ setup() { ' return "$result"' \ '}' \ 'if [[ -z "$COLLECT_DIRS_SUCCESS_AFTER_NUM_ITERATIONS" ]]; then' \ - ' COLLECT_DIRS_SUCCESS_AFTER_NUM_ITERATIONS=1' \ + ' COLLECT_DIRS_SUCCESS_AFTER_NUM_ITERATIONS=2' \ 'fi' \ '@go "$@"' } @@ -123,8 +123,9 @@ teardown() { local test_scripts_dir="$test_rootdir/plugins" local test_plugins_dir="$test_scripts_dir/plugins" mkdir -p "$test_plugins_dir/foo/bin/plugins/bar/bin/plugins" + mkdir -p "$test_rootdir/plugins-2" - printf '%s\n' "${test_go_script_impl/$TEST_GO_SCRIPTS_RELATIVE_DIR/plugins}" \ + printf '%s\n' "${test_go_script_impl//$TEST_GO_SCRIPTS_RELATIVE_DIR/plugins}" \ >"$test_go_script" chmod 700 "$test_go_script" diff --git a/tests/help.bats b/tests/help.bats index 3092022..40178ec 100644 --- a/tests/help.bats +++ b/tests/help.bats @@ -60,7 +60,7 @@ teardown() { assert_line_equals 0 "Usage: $TEST_GO_SCRIPT [arguments...]" local expected_err="&2 restore_bats_shell_options '1' @@ -288,6 +289,27 @@ assert_command_script_is_executable() { @test "$SUITE: new command script" { run "$TEST_GO_SCRIPT" new --command foo + assert_success "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo" + assert_file_equals "$TEST_GO_SCRIPTS_DIR_2/foo" \ + '#! /usr/bin/env bash' \ + '#' \ + '# Short description of the {{cmd}} command' \ + '' \ + '_foo() {' \ + ' :' \ + '}' \ + '' \ + '_foo "$@"' + assert_command_script_is_executable 'foo' "$TEST_GO_SCRIPTS_DIR_2" + + rm "$TEST_GO_SCRIPTS_DIR_2/foo" + EDITOR= run "$TEST_GO_SCRIPT" new --command foo + assert_success '' + assert_file_matches "$TEST_GO_SCRIPTS_DIR_2/foo" $'\n_foo "\$@"' +} + +@test "$SUITE: new command script in specified scripts dir" { + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/foo" assert_file_equals "$TEST_GO_SCRIPTS_DIR/foo" \ '#! /usr/bin/env bash' \ @@ -302,18 +324,30 @@ assert_command_script_is_executable() { assert_command_script_is_executable 'foo' rm "$TEST_GO_SCRIPTS_DIR/foo" - EDITOR= run "$TEST_GO_SCRIPT" new --command foo + EDITOR= run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success '' assert_file_matches "$TEST_GO_SCRIPTS_DIR/foo" $'\n_foo "\$@"' } @test "$SUITE: new subcommand script" { run "$TEST_GO_SCRIPT" new --command foo + assert_success "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo" + assert_file_matches "$TEST_GO_SCRIPTS_DIR_2/foo" $'\n_foo\(\) \{\n' + assert_command_script_is_executable 'foo' "$TEST_GO_SCRIPTS_DIR_2" + + run "$TEST_GO_SCRIPT" new --command foo bar + assert_success "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo.d/bar" + assert_file_matches "$TEST_GO_SCRIPTS_DIR_2/foo.d/bar" $'\n_bar\(\) \{\n' + assert_command_script_is_executable 'foo.d/bar' "$TEST_GO_SCRIPTS_DIR_2" +} + +@test "$SUITE: new subcommand script in specified scripts dir" { + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/foo" assert_file_matches "$TEST_GO_SCRIPTS_DIR/foo" $'\n_foo\(\) \{\n' assert_command_script_is_executable 'foo' - run "$TEST_GO_SCRIPT" new --command foo bar + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" bar assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/foo.d/bar" assert_file_matches "$TEST_GO_SCRIPTS_DIR/foo.d/bar" $'\n_bar\(\) \{\n' assert_command_script_is_executable 'foo.d/bar' @@ -321,6 +355,28 @@ assert_command_script_is_executable() { @test "$SUITE: new command and subcommand scripts" { run "$TEST_GO_SCRIPT" new --command foo bar baz + assert_success "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo" \ + "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo.d/bar" \ + "EDITING: $TEST_GO_SCRIPTS_DIR_2/foo.d/bar.d/baz" + + assert_file_equals "$TEST_GO_SCRIPTS_DIR_2/foo" \ + '#! /usr/bin/env bash' \ + '#' \ + '# Short description of the {{cmd}} command' \ + '' \ + ". \"\$_GO_USE_MODULES\" 'subcommands'" \ + '' \ + '@go.show_subcommands' + assert_file_matches "$TEST_GO_SCRIPTS_DIR_2/foo.d/bar" '@go.show_subcommands' + assert_file_matches "$TEST_GO_SCRIPTS_DIR_2/foo.d/bar.d/baz" $'\n_baz\(\) \{\n' + + assert_command_script_is_executable 'foo' "$TEST_GO_SCRIPTS_DIR_2" + assert_command_script_is_executable 'foo.d/bar' "$TEST_GO_SCRIPTS_DIR_2" + assert_command_script_is_executable 'foo.d/bar.d/baz' "$TEST_GO_SCRIPTS_DIR_2" +} + +@test "$SUITE: new command and subcommand scripts in specified scripts dir" { + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" bar baz assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/foo" \ "EDITING: $TEST_GO_SCRIPTS_DIR/foo.d/bar" \ "EDITING: $TEST_GO_SCRIPTS_DIR/foo.d/bar.d/baz" @@ -346,19 +402,30 @@ assert_command_script_is_executable() { assert_failure 'No command script name specified.' } +@test "$SUITE: --command fails if no argument specified with '--parent'" { + run "$TEST_GO_SCRIPT" new --command foo --parent + assert_failure "'--parent' expects an argument." +} + +@test "$SUITE: --command fails if invalid dir specified with '--parent'" { + path='does-not-exist' + run "$TEST_GO_SCRIPT" new --command foo --parent "$path" + assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIR')." +} + @test "$SUITE: --command fails if script already exists" { - run "$TEST_GO_SCRIPT" new --command foo bar baz + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" bar baz assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/foo" \ "EDITING: $TEST_GO_SCRIPTS_DIR/foo.d/bar" \ "EDITING: $TEST_GO_SCRIPTS_DIR/foo.d/bar.d/baz" - run "$TEST_GO_SCRIPT" new --command foo bar baz + run "$TEST_GO_SCRIPT" new --command foo --parent "$TEST_GO_SCRIPTS_DIR" bar baz local failing_path="$TEST_GO_SCRIPTS_RELATIVE_DIR/foo.d/bar.d/baz" assert_failure "command script file already exists: $failing_path" } @test "$SUITE: --internal creates new internal module" { - run "$TEST_GO_SCRIPT" new --internal foo + run "$TEST_GO_SCRIPT" new --internal foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/lib/foo" assert_file_equals "$TEST_GO_SCRIPTS_DIR/lib/foo" \ '#! /usr/bin/env bash' \ @@ -370,18 +437,40 @@ assert_command_script_is_executable() { '# Short description of the func_name function' rm "$TEST_GO_SCRIPTS_DIR/lib/foo" - EDITOR= run "$TEST_GO_SCRIPT" new --internal foo + EDITOR= run "$TEST_GO_SCRIPT" new --internal foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success '' assert_file_matches "$TEST_GO_SCRIPTS_DIR/lib/foo" \ $'\n# Short description of the foo module\n' } +@test "$SUITE: --internal fails if --parent specified with no argument" { + run "$TEST_GO_SCRIPT" new --internal foo --parent + assert_failure "'--parent' expects an argument." +} + +@test "$SUITE: --internal fails if invalid dir specified with '--parent'" { + path='does-not-exist' + run "$TEST_GO_SCRIPT" new --internal foo --parent "$path" + assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIR')." +} + +@test "$SUITE: --internal fails if extraneous arguments are given" { + run "$TEST_GO_SCRIPT" new --internal foo bar + assert_failure 'Command expects exactly one path.' +} + @test "$SUITE: --internal fails if internal module already exists" { - run "$TEST_GO_SCRIPT" new --internal foo + run "$TEST_GO_SCRIPT" new --internal foo --parent "$TEST_GO_SCRIPTS_DIR" assert_success "EDITING: $TEST_GO_SCRIPTS_DIR/lib/foo" - run "$TEST_GO_SCRIPT" new --internal foo + run "$TEST_GO_SCRIPT" new --internal foo --parent "$TEST_GO_SCRIPTS_DIR" assert_failure \ "internal module file already exists: $TEST_GO_SCRIPTS_RELATIVE_DIR/lib/foo" + + run "$TEST_GO_SCRIPT" new --internal foo + assert_success "EDITING: $TEST_GO_SCRIPTS_DIR_2/lib/foo" + run "$TEST_GO_SCRIPT" new --internal foo + assert_failure \ + "internal module file already exists: $TEST_GO_SCRIPTS_RELATIVE_DIR_2/lib/foo" } @test "$SUITE: --public creates new public module" { diff --git a/tests/path/init-constants.bats b/tests/path/init-constants.bats index 81dd482..b3275ba 100644 --- a/tests/path/init-constants.bats +++ b/tests/path/init-constants.bats @@ -5,9 +5,9 @@ load ../environment setup() { @go.create_test_go_script \ '. "$_GO_CORE_DIR/lib/internal/path"' \ - 'echo "_GO_PLUGINS_DIR: $_GO_PLUGINS_DIR"' \ - 'echo "_GO_PLUGINS_PATHS: ${_GO_PLUGINS_PATHS[@]}"' \ - 'echo "_GO_SEARCH_PATHS: ${_GO_SEARCH_PATHS[@]}"' + 'echo "_GO_PLUGINS_DIR: ${_GO_PLUGINS_DIR[*]}"' \ + 'echo "_GO_PLUGINS_PATHS: ${_GO_PLUGINS_PATHS[*]}"' \ + 'echo "_GO_SEARCH_PATHS: ${_GO_SEARCH_PATHS[*]}"' } teardown() { @@ -18,11 +18,13 @@ teardown() { run "$TEST_GO_SCRIPT" assert_success - local expected_paths=("$_GO_ROOTDIR/libexec" "$TEST_GO_SCRIPTS_DIR") + local expected_paths=("$_GO_ROOTDIR/libexec") + expected_paths+=("$TEST_GO_SCRIPTS_DIR_2 $TEST_GO_SCRIPTS_DIR") # Even if the plugins dir doesn't exist, we still set the value so its # existence can be checked for generically. - assert_line_equals 0 "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR" + assert_line_equals 0 \ + "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" assert_line_equals 1 '_GO_PLUGINS_PATHS: ' assert_line_equals 2 "_GO_SEARCH_PATHS: ${expected_paths[*]}" } @@ -39,10 +41,12 @@ teardown() { local expected_paths=( "$_GO_ROOTDIR/libexec" + "$TEST_GO_SCRIPTS_DIR_2" "$TEST_GO_SCRIPTS_DIR" "${plugin_bindirs[@]}") - assert_line_equals 0 "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR" + assert_line_equals 0 \ + "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" assert_line_equals 1 \ "_GO_PLUGINS_PATHS: ${plugin_bindirs[*]}" assert_line_equals 2 "_GO_SEARCH_PATHS: ${expected_paths[*]}" diff --git a/tests/testing/stack-trace.bats b/tests/testing/stack-trace.bats index 3949525..affda97 100644 --- a/tests/testing/stack-trace.bats +++ b/tests/testing/stack-trace.bats @@ -8,7 +8,9 @@ EXPECTED_TEST_SCRIPT= setup() { test_filter EXPECTED_TEST_GO_SCRIPT=('#! /usr/bin/env bash' - ". '$_GO_CORE_DIR/go-core.bash' '$TEST_GO_SCRIPTS_RELATIVE_DIR'" + ". '$_GO_CORE_DIR/go-core.bash' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR_2' \ + '$TEST_GO_SCRIPTS_RELATIVE_DIR'" '# Comment' '' '# Moar comment' diff --git a/tests/vars.bats b/tests/vars.bats index b250461..7e3f202 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -29,7 +29,8 @@ quotify_expected() { set "$DISABLE_BATS_SHELL_OPTIONS" local search_paths=("[0]=\"$_GO_CORE_DIR/libexec\"" - "[1]=\"$TEST_GO_SCRIPTS_DIR\"") + "[1]=\"$TEST_GO_SCRIPTS_DIR_2\"" + "[2]=\"$TEST_GO_SCRIPTS_DIR\"") local expected=("declare -x _GO_BATS_COVERAGE_DIR=\"$_GO_BATS_COVERAGE_DIR\"" "declare -x _GO_BATS_DIR=\"$_GO_BATS_DIR\"" @@ -50,11 +51,11 @@ quotify_expected() { 'declare -- _GO_INJECT_MODULE_PATH=""' 'declare -- _GO_INJECT_SEARCH_PATH=""' "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR\""\) + "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\")" 'declare -a _GO_PLUGINS_PATHS=()' "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR\""\) + "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\")" "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") @@ -91,10 +92,11 @@ quotify_expected() { "[2]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\"") local search_paths=("[0]=\"$TEST_GO_ROOTDIR/bin\"" "[1]=\"$_GO_CORE_DIR/libexec\"" - "[2]=\"$TEST_GO_SCRIPTS_DIR\"" - "[3]=\"$TEST_GO_PLUGINS_DIR/plugin0/bin\"" - "[4]=\"$TEST_GO_PLUGINS_DIR/plugin1/bin\"" - "[5]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\"") + "[2]=\"$TEST_GO_SCRIPTS_DIR_2\"" + "[3]=\"$TEST_GO_SCRIPTS_DIR\"" + "[4]=\"$TEST_GO_PLUGINS_DIR/plugin0/bin\"" + "[5]=\"$TEST_GO_PLUGINS_DIR/plugin1/bin\"" + "[6]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\"") # Note that the `format` module imports `strings` and `validation`. local command_script_trace="$TEST_GO_SCRIPTS_DIR/" @@ -124,11 +126,11 @@ quotify_expected() { "declare -x _GO_INJECT_MODULE_PATH=\"$TEST_GO_ROOTDIR/lib\"" "declare -x _GO_INJECT_SEARCH_PATH=\"$TEST_GO_ROOTDIR/bin\"" "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR\""\) + "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\""\) "declare -a _GO_PLUGINS_PATHS=(${plugins_paths[*]})" "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR\""\) + "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\""\) "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") From 25000fb3a5014cf931c83bf1a0bb8ab9636eea2d Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Wed, 30 Jan 2019 16:57:40 +0100 Subject: [PATCH 08/13] Renames _GO_SCRIPTS_DIR to _GO_SCRIPTS_DIRS --- go-core.bash | 16 +++++++------- go-template | 12 +++++------ lib/internal/complete | 2 +- lib/internal/set-search-paths | 6 +++--- lib/internal/use | 12 +++++------ lib/log | 6 +++--- libexec/commands | 6 +++--- libexec/help | 2 +- libexec/modules | 6 +++--- libexec/new | 22 ++++++++++---------- libexec/plugins | 2 +- tests/core/plugin-scope-variable-values.bats | 16 +++++++------- tests/new.bats | 4 ++-- tests/vars.bats | 4 ++-- 14 files changed, 58 insertions(+), 58 deletions(-) diff --git a/go-core.bash b/go-core.bash index a1584ae..0842be1 100755 --- a/go-core.bash +++ b/go-core.bash @@ -111,7 +111,7 @@ declare _GO_IMPORTED_MODULE_FILES=() declare _GO_IMPORTED_MODULE_CALLERS=() # Paths to the project's script directories -declare _GO_SCRIPTS_DIR=() +declare _GO_SCRIPTS_DIRS=() # Directory containing Bats tests, relative to `_GO_ROOTDIR` declare -r -x _GO_TEST_DIR="${_GO_TEST_DIR:-tests}" @@ -231,7 +231,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # Searches through plugin directories using a helper function # -# The search will begin in `_GO_SCRIPTS_DIR/plugins`. As long as `search_func` +# The search will begin in `_GO_SCRIPTS_DIRS/plugins`. As long as `search_func` # returns nonzero, every parent `/plugins/` directory will be searched, up to # and including the top-level `_GO_PLUGINS_DIR`. The search will end either when # `search_func` returns zero, or when all of the plugin paths are exhausted. @@ -269,7 +269,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" local scripts_dir local plugins_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]/%//plugins}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]/%//plugins}"; do __gsp_plugins_dir="$scripts_dir" while true; do if "$1" "$__gsp_plugins_dir"; then @@ -343,7 +343,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" fi local script_dir - for script_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for script_dir in "${_GO_SCRIPTS_DIRS[@]}"; do if [[ "${__go_cmd_path#$script_dir}" =~ /plugins/[^/]+/bin/ ]]; then _@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}" return @@ -360,8 +360,8 @@ _@go.source_builtin() { } _@go.run_plugin_command_script() { - local _GO_SCRIPTS_DIR="${__go_cmd_path%/bin/*}/bin" - local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/*}" + local _GO_SCRIPTS_DIRS="${__go_cmd_path%/bin/*}/bin" + local _GO_ROOTDIR="${_GO_SCRIPTS_DIRS%/*}" local _GO_PLUGINS_PATHS=() local _GO_SEARCH_PATHS=() @@ -431,7 +431,7 @@ _@go.set_scripts_dir() { return 1 fi shift - _GO_SCRIPTS_DIR+=("$scripts_dir") + _GO_SCRIPTS_DIRS+=("$scripts_dir") done } @@ -447,4 +447,4 @@ elif [[ -z "$COLUMNS" ]]; then fi export COLUMNS="${COLUMNS:-80}" fi -_GO_PLUGINS_DIR=("${_GO_SCRIPTS_DIR[@]/%//plugins}") +_GO_PLUGINS_DIR=("${_GO_SCRIPTS_DIRS[@]/%//plugins}") diff --git a/go-template b/go-template index fe51f57..d0ce183 100755 --- a/go-template +++ b/go-template @@ -7,7 +7,7 @@ # # This template automatically checks for the presence of the go-script-bash # sources and downloads the go-script-bash repository contents if necessary -# before dispatching commands. (If you prefer, you can change the logic to +# before dispatching commands. (If you prefer, you can change the logic to # create a shallow or regular clone instead.) This allows users to set up the # framework without taking any extra steps when running the command for the # first time, without the need to commit the framework to your repository. @@ -19,7 +19,7 @@ # Make sure the variables within this script are configured as necessary for # your program. You can add any other initialization or configuration between: # -# . "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" "$GO_SCRIPTS_DIR"` +# . "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" "${GO_SCRIPTS_DIR[@]}"` # `@go "$@"` # Set to 'true' if your script is a standalone program, i.e. not bound to @@ -30,15 +30,15 @@ export _GO_STANDALONE= # The path where your command scripts reside # # For `_GO_STANDALONE` programs and plugins containing command scripts, you may -# wish to set GO_SCRIPTS_DIR to `bin` and have a separate `./go` script to +# wish to set GO_SCRIPTS_DIRS to `bin` and have a separate `./go` script to # manage project tasks that finds its command scripts in `scripts`. -declare GO_SCRIPTS_DIR="${GO_SCRIPTS_DIR:-scripts}" +declare GO_SCRIPTS_DIR=("${GO_SCRIPTS_DIRS[@]-scripts}") # The `GO_SCRIPT_BASH_REPO_URL` tag or branch you wish to use declare GO_SCRIPT_BASH_VERSION="${GO_SCRIPT_BASH_VERSION:-v1.7.0}" # The go-script-bash installation directory within your project -declare GO_SCRIPT_BASH_CORE_DIR="${GO_SCRIPT_BASH_CORE_DIR:-${0%/*}/$GO_SCRIPTS_DIR/go-script-bash}" +declare GO_SCRIPT_BASH_CORE_DIR="${GO_SCRIPT_BASH_CORE_DIR:-${0%/*}/${GO_SCRIPTS_DIR[0]}/go-script-bash}" # The URL of the go-script-bash framework sources declare GO_SCRIPT_BASH_REPO_URL="${GO_SCRIPT_BASH_REPO_URL:-https://github.com/mbland/go-script-bash.git}" @@ -120,6 +120,6 @@ if [[ ! -e "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" ]]; then fi fi -. "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" "$GO_SCRIPTS_DIR" +. "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" "${GO_SCRIPTS_DIR[@]}" # Add any other configuration or initialization steps here. @go "$@" diff --git a/lib/internal/complete b/lib/internal/complete index b7c0d04..dc8220b 100644 --- a/lib/internal/complete +++ b/lib/internal/complete @@ -8,7 +8,7 @@ _@go.complete_top_level_commands() { printf 'help\n' local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do _@go.source_builtin 'commands' "$scripts_dir" done fi diff --git a/lib/internal/set-search-paths b/lib/internal/set-search-paths index 26bced3..df308c9 100644 --- a/lib/internal/set-search-paths +++ b/lib/internal/set-search-paths @@ -5,11 +5,11 @@ _@go.set_search_paths_add_plugin_paths() { local plugin_path if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then - # Ensure a plugin's _GO_SCRIPTS_DIR isn't duplicated in _GO_PLUGINS_PATHS. + # Ensure a plugin's _GO_SCRIPTS_DIRS isn't duplicated in _GO_PLUGINS_PATHS. for plugin_path in "${plugin_paths[@]}"; do local included_path local found='false' - for included_path in "${_GO_SCRIPTS_DIR[@]}"; do + for included_path in "${_GO_SCRIPTS_DIRS[@]}"; do if [[ "$plugin_path" == "$included_path" ]]; then found='true' fi @@ -28,7 +28,7 @@ _@go.set_search_paths() { if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then _GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH") fi - _GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "${_GO_SCRIPTS_DIR[@]}") + _GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "${_GO_SCRIPTS_DIRS[@]}") # A plugin's own local plugin paths will appear before inherited ones. If # there is a version incompatibility issue with other installed plugins, this diff --git a/lib/internal/use b/lib/internal/use index 39c180a..a0a776f 100644 --- a/lib/internal/use +++ b/lib/internal/use @@ -45,9 +45,9 @@ # - `_GO_INJECT_MODULE_PATH` for module stubs injected during testing; # see `lib/testing/stubbing` # - `_GO_CORE_DIR/lib/` for core library modules -# - `_GO_SCRIPTS_DIR/lib` for project-internal modules +# - `_GO_SCRIPTS_DIRS/lib` for project-internal modules # - `_GO_ROOTDIR/lib` for publicly-exported modules (`./go` script plugins) -# - `_GO_SCRIPTS_DIR/plugins/*/lib` for installed plugin modules +# - `_GO_SCRIPTS_DIRS/plugins/*/lib` for installed plugin modules # - Parent plugin dirs up to `_GO_PLUGINS_DIR/*/lib` for plugin modules # installed in parent directories # @@ -95,7 +95,7 @@ _@go.find_module() { if [[ ! -f "$__go_module_file" ]]; then local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do __go_module_file="$scripts_dir/lib/$__go_module_name" if [[ -f "$__go_module_file" ]]; then @@ -122,9 +122,9 @@ _@go.find_module() { } _@go.import_module() { - if [[ "${__go_module_file#$_GO_SCRIPTS_DIR}" =~ /plugins/[^/]+/lib/ ]]; then - local _GO_SCRIPTS_DIR="${__go_module_file%/lib/*}/bin" - local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/bin}" + if [[ "${__go_module_file#$_GO_SCRIPTS_DIRS}" =~ /plugins/[^/]+/lib/ ]]; then + local _GO_SCRIPTS_DIRS="${__go_module_file%/lib/*}/bin" + local _GO_ROOTDIR="${_GO_SCRIPTS_DIRS%/bin}" local _GO_PLUGINS_PATHS=() local _GO_SEARCH_PATHS=() fi diff --git a/lib/log b/lib/log index 6721b17..4823659 100644 --- a/lib/log +++ b/lib/log @@ -549,7 +549,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' # @go "$@" # fi # -# And your `$_GO_SCRIPTS_DIR/setup` script may include (assuming your own `test` +# And your `$_GO_SCRIPTS_DIRS/setup` script may include (assuming your own `test` # script as well): # # @go.critical_section_begin QUIT @@ -568,7 +568,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' ((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS)) local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do setup_script="$scripts_dir/$1" if [[ -f "$setup_script" ]]; then break @@ -578,7 +578,7 @@ readonly __GO_LOG_COMMAND_EXIT_PATTERN='^@go.log_command (exit|fatal):([0-9]+)$' if [[ -z "$setup_script" ]]; then local scripts_dirs - IFS=* eval 'scripts_dirs="${_GO_SCRIPTS_DIR[*]}"' + IFS=* eval 'scripts_dirs="${_GO_SCRIPTS_DIRS[*]}"' scripts_dirs="${scripts_dirs//\*/ or }" @go.log FATAL "Before invoking $FUNCNAME, create '$1' in $scripts_dirs." diff --git a/libexec/commands b/libexec/commands index ad65a5d..f726877 100755 --- a/libexec/commands +++ b/libexec/commands @@ -15,8 +15,8 @@ # - `_GO_INJECT_SEARCH_PATH` for command script stubs injected during testing; # see `lib/testing/stubbing` # - `_GO_CORE_DIR/libexec` for core library command scripts -# - `_GO_SCRIPTS_DIR` for project command scripts -# - `_GO_SCRIPTS_DIR/plugins/*/bin` for installed plugin command scripts +# - `_GO_SCRIPTS_DIRS` for project command scripts +# - `_GO_SCRIPTS_DIRS/plugins/*/bin` for installed plugin command scripts # - Parent plugin dirs up to `_GO_PLUGINS_DIR/*/bin` for plugin command # scripts # @@ -33,7 +33,7 @@ # NOTE: This command will not return the names of shell aliases; use `{{go}} # aliases` for those. # -# While executing a plugin command script, `_GO_ROOTDIR` and `_GO_SCRIPTS_DIR` +# While executing a plugin command script, `_GO_ROOTDIR` and `_GO_SCRIPTS_DIRS` # are scoped to the top-level directory of the plugin. See `{{go}} help plugins` # for more information on plugins and their operating constraints. diff --git a/libexec/help b/libexec/help index f400d44..2892a66 100755 --- a/libexec/help +++ b/libexec/help @@ -55,7 +55,7 @@ # enforced. _@go.usage() { - local cmd_paths=("${_GO_SCRIPTS_DIR[@]}") + local cmd_paths=("${_GO_SCRIPTS_DIRS[@]}") local summaries local summaries_status=0 diff --git a/libexec/modules b/libexec/modules index 52de74a..85a6f2a 100755 --- a/libexec/modules +++ b/libexec/modules @@ -126,7 +126,7 @@ _@go.modules_produce_listing() { local modules=("${__go_modules[@]#$_GO_CORE_DIR/lib/}") local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do modules=("${modules[@]#$scripts_dir/lib/}") done modules=("${modules[@]#$_GO_ROOTDIR/lib/}") @@ -195,7 +195,7 @@ _@go.modules_search() { __go_core_modules_end="${#__go_modules[@]}" local script_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do _@go.modules_find_all_in_dir "$scripts_dir" "$glob" done __go_internal_modules_end="${#__go_modules[@]}" @@ -355,7 +355,7 @@ _@go.modules_tab_completion() { _@go.modules_find_all_in_dir "$_GO_CORE_DIR" "$glob" local scripts_dir local plugins_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do _@go.modules_find_all_in_dir "$scripts_dir" "$glob" done _@go.modules_find_all_in_dir "$_GO_ROOTDIR" "$glob" diff --git a/libexec/new b/libexec/new index 7f293f5..1da2659 100755 --- a/libexec/new +++ b/libexec/new @@ -6,10 +6,10 @@ # To generate an arbitrary text file: # {{go}} {{cmd}} --type [lines...] # -# To generate a new command script in `_GO_SCRIPTS_DIR`: +# To generate a new command script in `_GO_SCRIPTS_DIRS`: # {{go}} {{cmd}} --command [--parent ] [...] # -# To generate a new internal module in `_GO_SCRIPTS_DIR/lib`: +# To generate a new internal module in `_GO_SCRIPTS_DIRS/lib`: # {{go}} {{cmd}} --internal [--parent ] # # To generate a new public module in `_GO_ROOTDIR/lib`: @@ -59,7 +59,7 @@ _@go.new_tab_completions() { --command) if [[ "$word_index" -eq '0' ]]; then local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do _@go.source_builtin 'commands' "$scripts_dir" done else @@ -70,7 +70,7 @@ _@go.new_tab_completions() { ;; --internal) if [[ "$word_index" -eq '0' ]]; then - paths+=("${_GO_SCRIPTS_DIR[@]/%//lib}") + paths+=("${_GO_SCRIPTS_DIRS[@]/%//lib}") fi ;; --public) @@ -183,7 +183,7 @@ _@go.new_command_scripts() { local cmd_path local paths=("$@") local new_scripts=() - local parent_dir="${_GO_SCRIPTS_DIR[0]}" + local parent_dir="${_GO_SCRIPTS_DIRS[0]}" local is_last_cmd local i=0 @@ -202,14 +202,14 @@ _@go.new_command_scripts() { local found='false' local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do if [[ "$parent_dir" == "$scripts_dir" ]]; then found='true' fi done if [[ "$found" == 'false' ]]; then - printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIR').\n" + printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIRS').\n" return 1 fi @@ -236,7 +236,7 @@ _@go.new_command_scripts() { _@go.new_module() { local args=("$@") - local parent_dir="${_GO_SCRIPTS_DIR[0]}" + local parent_dir="${_GO_SCRIPTS_DIRS[0]}" if [[ "$#" -ge 3 && "$2" == '--parent' ]]; then parent_dir="$3" @@ -255,7 +255,7 @@ _@go.new_module() { local found='false' local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do if [[ "$parent_dir" == "$scripts_dir" || "$parent_dir" == "$_GO_ROOTDIR" ]]; then found='true' @@ -263,7 +263,7 @@ _@go.new_module() { done if [[ "$found" == 'false' ]]; then - printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIR').\n" + printf "'$parent_dir' is not valid (not included in '_GO_SCRIPTS_DIRS').\n" return 1 fi @@ -282,7 +282,7 @@ _@go.new_module() { module_type='public module' else local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIR[@]}"; do + for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do if [[ "${module_path%%/lib/*}" == "$scripts_dir" ]]; then module_type='internal module' fi diff --git a/libexec/plugins b/libexec/plugins index fe09450..3d42085 100755 --- a/libexec/plugins +++ b/libexec/plugins @@ -17,7 +17,7 @@ # scripts, a top-level `lib/` directory for its exported library modules, or # both. # -# `_GO_ROOTDIR` and `_GO_SCRIPTS_DIR` are scoped to each installed plugin's +# `_GO_ROOTDIR` and `_GO_SCRIPTS_DIRS` are scoped to each installed plugin's # top-level directory during both command script execution and library module # importation. This enables a plugin's code to refer to its own relative paths # and to give its own command scripts and library modules priority in these diff --git a/tests/core/plugin-scope-variable-values.bats b/tests/core/plugin-scope-variable-values.bats index 24de22f..223ba08 100644 --- a/tests/core/plugin-scope-variable-values.bats +++ b/tests/core/plugin-scope-variable-values.bats @@ -10,8 +10,8 @@ setup() { local print_scope_implementation=( 'printf -- "_GO_ROOTDIR:\n%s\n" "$_GO_ROOTDIR"' - 'printf -- "_GO_SCRIPTS_DIR:\n"' - 'printf -- "%s\n" "${_GO_SCRIPTS_DIR[@]}"' + 'printf -- "_GO_SCRIPTS_DIRS:\n"' + 'printf -- "%s\n" "${_GO_SCRIPTS_DIRS[@]}"' 'printf -- "_GO_PLUGINS_PATHS:\n"' 'printf -- "%s\n" "${_GO_PLUGINS_PATHS[@]}"' 'printf -- "_GO_SEARCH_PATHS:\n"' @@ -43,7 +43,7 @@ setup() { EXPECTED_ROOT_SCOPE_VALUES=('_GO_ROOTDIR:' "$TEST_GO_ROOTDIR" - '_GO_SCRIPTS_DIR:' + '_GO_SCRIPTS_DIRS:' "$TEST_GO_SCRIPTS_DIR_2" "$TEST_GO_SCRIPTS_DIR" '_GO_PLUGINS_PATHS:' @@ -108,7 +108,7 @@ __assert_scope_values_equal_impl() { assert_scope_values_equal 'FIRST PLUGIN' \ '_GO_ROOTDIR:' \ "$TEST_GO_PLUGINS_DIR/first" \ - '_GO_SCRIPTS_DIR:' \ + '_GO_SCRIPTS_DIRS:' \ "$TEST_GO_PLUGINS_DIR/first/bin" \ '_GO_PLUGINS_PATHS:' \ "$TEST_GO_PLUGINS_DIR/second/bin" \ @@ -134,7 +134,7 @@ __assert_scope_values_equal_impl() { assert_scope_values_equal 'THIRD PLUGIN' \ '_GO_ROOTDIR:' \ "$TEST_GO_PLUGINS_DIR/second/bin/plugins/third" \ - '_GO_SCRIPTS_DIR:' \ + '_GO_SCRIPTS_DIRS:' \ "$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \ '_GO_PLUGINS_PATHS:' \ "$TEST_GO_PLUGINS_DIR/first/bin" \ @@ -151,7 +151,7 @@ __assert_scope_values_equal_impl() { assert_scope_values_equal 'SECOND PLUGIN' \ '_GO_ROOTDIR:' \ "$TEST_GO_PLUGINS_DIR/second" \ - '_GO_SCRIPTS_DIR:' \ + '_GO_SCRIPTS_DIRS:' \ "$TEST_GO_PLUGINS_DIR/second/bin" \ '_GO_PLUGINS_PATHS:' \ "$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \ @@ -174,7 +174,7 @@ __assert_scope_values_equal_impl() { assert_scope_values_equal 'FIRST PLUGIN' \ '_GO_ROOTDIR:' \ "$TEST_GO_PLUGINS_DIR/first" \ - '_GO_SCRIPTS_DIR:' \ + '_GO_SCRIPTS_DIRS:' \ "$TEST_GO_PLUGINS_DIR/first/bin" \ '_GO_PLUGINS_PATHS:' \ "$TEST_GO_PLUGINS_DIR/second/bin" \ @@ -187,7 +187,7 @@ __assert_scope_values_equal_impl() { assert_scope_values_equal 'SECOND PLUGIN' \ '_GO_ROOTDIR:' \ "$TEST_GO_PLUGINS_DIR/second" \ - '_GO_SCRIPTS_DIR:' \ + '_GO_SCRIPTS_DIRS:' \ "$TEST_GO_PLUGINS_DIR/second/bin" \ '_GO_PLUGINS_PATHS:' \ "$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \ diff --git a/tests/new.bats b/tests/new.bats index 1d47f27..40f3541 100644 --- a/tests/new.bats +++ b/tests/new.bats @@ -410,7 +410,7 @@ assert_command_script_is_executable() { @test "$SUITE: --command fails if invalid dir specified with '--parent'" { path='does-not-exist' run "$TEST_GO_SCRIPT" new --command foo --parent "$path" - assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIR')." + assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIRS')." } @test "$SUITE: --command fails if script already exists" { @@ -451,7 +451,7 @@ assert_command_script_is_executable() { @test "$SUITE: --internal fails if invalid dir specified with '--parent'" { path='does-not-exist' run "$TEST_GO_SCRIPT" new --internal foo --parent "$path" - assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIR')." + assert_failure "'$path' is not valid (not included in '_GO_SCRIPTS_DIRS')." } @test "$SUITE: --internal fails if extraneous arguments are given" { diff --git a/tests/vars.bats b/tests/vars.bats index 7e3f202..6d77e56 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -55,7 +55,7 @@ quotify_expected() { 'declare -a _GO_PLUGINS_PATHS=()' "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\")" + "declare -a _GO_SCRIPTS_DIRS=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\")" "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") @@ -130,7 +130,7 @@ quotify_expected() { "declare -a _GO_PLUGINS_PATHS=(${plugins_paths[*]})" "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" - "declare -a _GO_SCRIPTS_DIR=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\""\) + "declare -a _GO_SCRIPTS_DIRS=([0]=\"$TEST_GO_SCRIPTS_DIR_2\" [1]=\"$TEST_GO_SCRIPTS_DIR\""\) "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") From a069db12b1003bd4703b39112e4a841d9f8f5627 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Wed, 30 Jan 2019 17:32:19 +0100 Subject: [PATCH 09/13] Final --- go-core.bash | 10 +++++----- lib/internal/use | 6 +++--- lib/testing/environment | 9 ++++++++- libexec/commands | 2 +- libexec/modules | 4 ++-- tests/core/run-command-script.bats | 19 ------------------- tests/core/set-scripts-dir.bats | 15 +-------------- tests/modules/use.bats | 17 +++-------------- tests/path/init-constants.bats | 6 +++--- tests/vars.bats | 4 ++-- 10 files changed, 28 insertions(+), 64 deletions(-) diff --git a/go-core.bash b/go-core.bash index 0842be1..1c517c3 100755 --- a/go-core.bash +++ b/go-core.bash @@ -135,11 +135,11 @@ declare -x _GO_CMD_NAME= # string with the arguments delimited by the ASCII Unit Separator ($'\x1f'). declare -x _GO_CMD_ARGV= -# The top-level directory in which plugins are installed. +# The top-level directories in which plugins are installed. # # If a command script is running as a plugin, this value will be the plugins # directory of the top-level `./go` script. -declare _GO_PLUGINS_DIR=() +declare _GO_PLUGINS_DIRS=() # Directories containing executable plugin scripts. declare _GO_PLUGINS_PATHS=() @@ -233,7 +233,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" # # The search will begin in `_GO_SCRIPTS_DIRS/plugins`. As long as `search_func` # returns nonzero, every parent `/plugins/` directory will be searched, up to -# and including the top-level `_GO_PLUGINS_DIR`. The search will end either when +# and including the top-level `_GO_PLUGINS_DIRS`. The search will end either when # `search_func` returns zero, or when all of the plugin paths are exhausted. # # The helper function, `search_func`, will receive the current plugin directory @@ -275,7 +275,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" if "$1" "$__gsp_plugins_dir"; then return else - for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + for plugins_dir in "${_GO_PLUGINS_DIRS[@]}"; do if [[ "$__gsp_plugins_dir" == "$plugins_dir" ]]; then break 2 fi @@ -447,4 +447,4 @@ elif [[ -z "$COLUMNS" ]]; then fi export COLUMNS="${COLUMNS:-80}" fi -_GO_PLUGINS_DIR=("${_GO_SCRIPTS_DIRS[@]/%//plugins}") +_GO_PLUGINS_DIRS=("${_GO_SCRIPTS_DIRS[@]/%//plugins}") diff --git a/lib/internal/use b/lib/internal/use index a0a776f..21f102d 100644 --- a/lib/internal/use +++ b/lib/internal/use @@ -48,7 +48,7 @@ # - `_GO_SCRIPTS_DIRS/lib` for project-internal modules # - `_GO_ROOTDIR/lib` for publicly-exported modules (`./go` script plugins) # - `_GO_SCRIPTS_DIRS/plugins/*/lib` for installed plugin modules -# - Parent plugin dirs up to `_GO_PLUGINS_DIR/*/lib` for plugin modules +# - Parent plugin dirs up to `_GO_PLUGINS_DIRS/*/lib` for plugin modules # installed in parent directories # # To import a module exported from an installed plugin, you must specify the @@ -108,7 +108,7 @@ _@go.find_module() { if [[ ! -f "$__go_module_file" ]]; then local plugins_dir - for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + for plugins_dir in "${_GO_PLUGINS_DIRS[@]}"; do __go_module_file="$plugins_dir/lib/$__go_module_name" if [[ ! -f "$__go_module_file" ]]; then @@ -162,7 +162,7 @@ _@go.use_modules() { # namespace allows us to detect such potential collisions and issue a # warning below. local plugins_dir - for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + for plugins_dir in "${_GO_PLUGINS_DIRS[@]}"; do if [[ "$__go_module_file" =~ ^$plugins_dir/ ]]; then __go_module_name="${__go_module_file##*/plugins/}" __go_module_name="${__go_module_name/\/bin\///}" diff --git a/lib/testing/environment b/lib/testing/environment index a810fbd..db05ed3 100644 --- a/lib/testing/environment +++ b/lib/testing/environment @@ -38,6 +38,8 @@ readonly TEST_GO_SCRIPTS_DIR="$TEST_GO_ROOTDIR/$TEST_GO_SCRIPTS_RELATIVE_DIR" readonly TEST_GO_SCRIPTS_DIR_2="$TEST_GO_ROOTDIR/$TEST_GO_SCRIPTS_RELATIVE_DIR_2" readonly TEST_GO_PLUGINS_DIR="$TEST_GO_SCRIPTS_DIR/plugins" readonly TEST_GO_PLUGINS_DIR_2="$TEST_GO_SCRIPTS_DIR_2/plugins" +# Variables with the '_2' suffix are used to test that multiple +# _GO_SCRIPTS_DIRS work as expected. # Recursively removes `TEST_GO_ROOTDIR` # @@ -52,7 +54,12 @@ readonly TEST_GO_PLUGINS_DIR_2="$TEST_GO_SCRIPTS_DIR_2/plugins" # Creates an executable `./go` script as `TEST_GO_SCRIPT` # # The `. "$_GO_CORE_DIR/go-core.bash"` line is included, so callers need only -# supply the remaining lines. Also automatically creates `TEST_GO_SCRIPTS_DIR`. +# supply the remaining lines. Two _GO_SCRIPT_DIRS are specified. The first is +# `TEST_GO_SCRIPTS_DIR_2` and the second `TEST_GO_SCRIPTS_DIR`. The latter is +# used as the main command script destination in the tests, while the former is +# specified to test if _GO_SCRIPTS_DIRS are looped properly during execution. +# The function, also automatically creates `TEST_GO_SCRIPTS_DIR` and +# `TEST_GO_SCRIPTS_DIR_2`. # # Arguments: # ...: Lines comprising the `./go` script diff --git a/libexec/commands b/libexec/commands index f726877..ecff8e7 100755 --- a/libexec/commands +++ b/libexec/commands @@ -17,7 +17,7 @@ # - `_GO_CORE_DIR/libexec` for core library command scripts # - `_GO_SCRIPTS_DIRS` for project command scripts # - `_GO_SCRIPTS_DIRS/plugins/*/bin` for installed plugin command scripts -# - Parent plugin dirs up to `_GO_PLUGINS_DIR/*/bin` for plugin command +# - Parent plugin dirs up to `_GO_PLUGINS_DIRS/*/bin` for plugin command # scripts # # If no or is given, lists all top-level builtins, diff --git a/libexec/modules b/libexec/modules index 85a6f2a..a7942d8 100755 --- a/libexec/modules +++ b/libexec/modules @@ -131,9 +131,9 @@ _@go.modules_produce_listing() { done modules=("${modules[@]#$_GO_ROOTDIR/lib/}") - if [[ -n "$_GO_PLUGINS_DIR" ]]; then + if [[ "${#_GO_PLUGINS_DIRS[@]}" -gt 0 ]]; then local plugins_dir - for plugins_dir in "${_GO_PLUGINS_DIR[@]}"; do + for plugins_dir in "${_GO_PLUGINS_DIRS[@]}"; do modules=("${modules[@]#$plugins_dir/}") done modules=("${modules[@]/lib\//}") diff --git a/tests/core/run-command-script.bats b/tests/core/run-command-script.bats index 9b5bc4e..d477219 100644 --- a/tests/core/run-command-script.bats +++ b/tests/core/run-command-script.bats @@ -23,25 +23,6 @@ teardown() { assert_success 'Can use @go.printf' } -@test "$SUITE: run bash script by sourcing with multiple script dirs" { - echo '#!/bin/bash' >"$TEST_GO_SCRIPT" - printf ". '$_GO_CORE_DIR/go-core.bash' " >>"$TEST_GO_SCRIPT" - printf "scripts-2 " >>"$TEST_GO_SCRIPT" - echo "'$TEST_GO_SCRIPTS_RELATIVE_DIR'" >>"$TEST_GO_SCRIPT" - echo '@go "$@"' >>"$TEST_GO_SCRIPT" - - mkdir -p "$TEST_GO_SCRIPTS_DIR/../scripts-2" - TEST_COMMAND_SCRIPT_PATH="$TEST_GO_SCRIPTS_DIR/../scripts-2/test-command" - - @go.create_test_command_script "../scripts-2/test-command" - - echo '#!/bin/bash' >"$TEST_COMMAND_SCRIPT_PATH" - echo '@go.printf "%s" "$*"' >>"$TEST_COMMAND_SCRIPT_PATH" - - run "$TEST_GO_SCRIPT" test-command Can use '@go.printf' - assert_success 'Can use @go.printf' -} - @test "$SUITE: run sh script by sourcing" { echo '#!/bin/sh' >"$TEST_COMMAND_SCRIPT_PATH" echo '@go.printf "%s" "$*"' >>"$TEST_COMMAND_SCRIPT_PATH" diff --git a/tests/core/set-scripts-dir.bats b/tests/core/set-scripts-dir.bats index d670dbe..dd58593 100644 --- a/tests/core/set-scripts-dir.bats +++ b/tests/core/set-scripts-dir.bats @@ -10,25 +10,12 @@ teardown() { @go.remove_test_go_rootdir } -@test "$SUITE: scripts dir successfully set" { - run "$TEST_GO_SCRIPT" - assert_success -} - -@test "$SUITE: multiple scripts dir successfully set" { - echo "#! /usr/bin/env bash" >"$TEST_GO_SCRIPT" - printf ". '$_GO_ROOTDIR/go-core.bash' " >>"$TEST_GO_SCRIPT" - printf "'$TEST_GO_SCRIPTS_RELATIVE_DIR' " >>"$TEST_GO_SCRIPT" - printf "'scripts-2' " >>"$TEST_GO_SCRIPT" - - mkdir -p "$TEST_GO_SCRIPTS_DIR/../scripts-2" - +@test "$SUITE: scripts dirs successfully set" { run "$TEST_GO_SCRIPT" assert_success } @test "$SUITE: produce an error if no dir specified when sourced" { - # Overwrite the entire script to force the multiple dir error. echo "#! /usr/bin/env bash" >"$TEST_GO_SCRIPT" echo ". '$_GO_ROOTDIR/go-core.bash' " >>"$TEST_GO_SCRIPT" diff --git a/tests/modules/use.bats b/tests/modules/use.bats index 27a382b..4c59c32 100644 --- a/tests/modules/use.bats +++ b/tests/modules/use.bats @@ -6,10 +6,10 @@ load "$_GO_CORE_DIR/lib/testing/stubbing" BUILTIN_MODULE_FILE="$_GO_CORE_DIR/lib/builtin-test" PLUGIN_MODULE_FILE="$TEST_GO_PLUGINS_DIR/test-plugin/lib/plugin-test" -PLUGIN_MODULE_FILE_2="$TEST_GO_SCRIPTS_DIR/../scripts-2/plugins/test-plugin-2/lib/plugin-test-2" +PLUGIN_MODULE_FILE_2="$TEST_GO_ROOTDIR/scripts-2/plugins/test-plugin-2/lib/plugin-test-2" EXPORT_MODULE_FILE="$TEST_GO_ROOTDIR/lib/export-test" INTERNAL_MODULE_FILE="$TEST_GO_SCRIPTS_DIR/lib/internal-test" -INTERNAL_MODULE_FILE_2="$TEST_GO_SCRIPTS_DIR/../scripts-2/lib/internal-test-2" +INTERNAL_MODULE_FILE_2="$TEST_GO_ROOTDIR/scripts-2/lib/internal-test-2" TEST_MODULES=( "$BUILTIN_MODULE_FILE" @@ -79,13 +79,7 @@ do_setup() { fi done - # Repeating the body of '@go.create_test_go_script' to include two script - # directories - set "$DISABLE_BATS_SHELL_OPTIONS" - create_bats_test_script 'go' \ - ". '$_GO_CORE_DIR/go-core.bash' \ - '$TEST_GO_SCRIPTS_RELATIVE_DIR' \ - '$TEST_GO_SCRIPTS_RELATIVE_DIR/../scripts-2'" \ + @go.create_test_go_script \ ". \"\$_GO_USE_MODULES\" $*" \ 'for ((i=0; i != ${#_GO_IMPORTED_MODULES[@]}; ++i)); do' \ " printf -- 'module: %s\nsource: %s\ncaller: %s\n' \\" \ @@ -94,11 +88,6 @@ do_setup() { " \"\${_GO_IMPORTED_MODULE_CALLERS[\$i]}\"" \ 'done' - if [[ ! -d "$TEST_GO_SCRIPTS_DIR" ]]; then - mkdir "$TEST_GO_SCRIPTS_DIR" - fi - restore_bats_shell_options "$?" - local module for module in "${TEST_MODULES[@]}"; do mkdir -p "${module%/*}" diff --git a/tests/path/init-constants.bats b/tests/path/init-constants.bats index b3275ba..64c3126 100644 --- a/tests/path/init-constants.bats +++ b/tests/path/init-constants.bats @@ -5,7 +5,7 @@ load ../environment setup() { @go.create_test_go_script \ '. "$_GO_CORE_DIR/lib/internal/path"' \ - 'echo "_GO_PLUGINS_DIR: ${_GO_PLUGINS_DIR[*]}"' \ + 'echo "_GO_PLUGINS_DIRS: ${_GO_PLUGINS_DIRS[*]}"' \ 'echo "_GO_PLUGINS_PATHS: ${_GO_PLUGINS_PATHS[*]}"' \ 'echo "_GO_SEARCH_PATHS: ${_GO_SEARCH_PATHS[*]}"' } @@ -24,7 +24,7 @@ teardown() { # Even if the plugins dir doesn't exist, we still set the value so its # existence can be checked for generically. assert_line_equals 0 \ - "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" + "_GO_PLUGINS_DIRS: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" assert_line_equals 1 '_GO_PLUGINS_PATHS: ' assert_line_equals 2 "_GO_SEARCH_PATHS: ${expected_paths[*]}" } @@ -46,7 +46,7 @@ teardown() { "${plugin_bindirs[@]}") assert_line_equals 0 \ - "_GO_PLUGINS_DIR: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" + "_GO_PLUGINS_DIRS: $TEST_GO_PLUGINS_DIR_2 $TEST_GO_PLUGINS_DIR" assert_line_equals 1 \ "_GO_PLUGINS_PATHS: ${plugin_bindirs[*]}" assert_line_equals 2 "_GO_SEARCH_PATHS: ${expected_paths[*]}" diff --git a/tests/vars.bats b/tests/vars.bats index 6d77e56..1df22c5 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -51,7 +51,7 @@ quotify_expected() { 'declare -- _GO_INJECT_MODULE_PATH=""' 'declare -- _GO_INJECT_SEARCH_PATH=""' "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\")" + "declare -a _GO_PLUGINS_DIRS=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\")" 'declare -a _GO_PLUGINS_PATHS=()' "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" @@ -126,7 +126,7 @@ quotify_expected() { "declare -x _GO_INJECT_MODULE_PATH=\"$TEST_GO_ROOTDIR/lib\"" "declare -x _GO_INJECT_SEARCH_PATH=\"$TEST_GO_ROOTDIR/bin\"" "declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\"" - "declare -a _GO_PLUGINS_DIR=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\""\) + "declare -a _GO_PLUGINS_DIRS=([0]=\"$TEST_GO_PLUGINS_DIR_2\" [1]=\"$TEST_GO_PLUGINS_DIR\""\) "declare -a _GO_PLUGINS_PATHS=(${plugins_paths[*]})" "declare -x _GO_ROOTDIR=\"$TEST_GO_ROOTDIR\"" "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" From 416e6637b1910705431277177cd3db7f94baa121 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Sat, 16 Feb 2019 14:44:31 +0100 Subject: [PATCH 10/13] Removes unneeded loop --- lib/internal/complete | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/complete b/lib/internal/complete index dc8220b..e976d0b 100644 --- a/lib/internal/complete +++ b/lib/internal/complete @@ -7,10 +7,10 @@ _@go.complete_top_level_commands() { else printf 'help\n' - local scripts_dir - for scripts_dir in "${_GO_SCRIPTS_DIRS[@]}"; do - _@go.source_builtin 'commands' "$scripts_dir" - done + local scripts_paths + printf -v scripts_paths '%s:' "${_GO_SCRIPTS_DIRS[@]}" + scripts_paths="${scripts_paths%:}" + _@go.source_builtin 'commands' "$scripts_paths" fi } From f4611822fd644f6db96d797527e13c8d7604579e Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Wed, 2 Oct 2019 14:14:42 +0200 Subject: [PATCH 11/13] Merges commands of the same parent under that parent --- go-core.bash | 2 +- lib/internal/complete | 14 ++++--- lib/internal/path | 70 ++++++++++++++++++------------- lib/internal/set-search-paths | 5 +++ libexec/commands | 2 +- libexec/complete | 4 +- libexec/help | 12 ++++-- libexec/path | 6 +-- tests/path/set-path-and-argv.bats | 46 ++++++++++---------- 9 files changed, 95 insertions(+), 66 deletions(-) diff --git a/go-core.bash b/go-core.bash index 1c517c3..2bf9f6f 100755 --- a/go-core.bash +++ b/go-core.bash @@ -350,7 +350,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" fi done - _@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}" + _@go.run_command_script "${__go_cmd_path[0]}" "${__go_argv[@]}" } _@go.source_builtin() { diff --git a/lib/internal/complete b/lib/internal/complete index e976d0b..1502572 100644 --- a/lib/internal/complete +++ b/lib/internal/complete @@ -39,21 +39,23 @@ _@go.complete_command_path() { elif ! _@go.set_command_path_and_argv "$@"; then return 1 fi - (( __go_complete_word_index -= ($# - ${#__go_argv[@]}) )) + ((__go_complete_word_index -= ($# - ${#__go_argv[@]}))) if [[ "$__go_complete_word_index" -lt '0' ]]; then # This (sub)command itself is the completion target. - echo "${__go_cmd_path##*/}" + echo "${__go_cmd_path[*]##*/}" return 1 elif [[ "$__go_complete_word_index" -eq '0' ]]; then # Complete subcommand scripts. local c local subcommands=() - for c in "${__go_cmd_path}.d"/*; do - if [[ -f "$c" && -x "$c" ]]; then - subcommands+=("${c##*/}") - fi + for path in "${__go_cmd_path[@]}"; do + for c in "$path.d"/*; do + if [[ -f "$c" && -x "$c" ]]; then + subcommands+=("${c##*/}") + fi + done done echo "${subcommands[@]}" fi diff --git a/lib/internal/path b/lib/internal/path index e9a5075..c6e4d7a 100644 --- a/lib/internal/path +++ b/lib/internal/path @@ -25,7 +25,7 @@ _@go.set_command_path_and_argv() { local cmd_args=("$@") local cmd_name="${cmd_args[0]}" - local cmd_path + local cmd_paths=() local try_path unset 'cmd_args[0]' @@ -34,43 +34,57 @@ _@go.set_command_path_and_argv() { try_path="$try_path/$cmd_name" if [[ -f "$try_path" && -x "$try_path" ]]; then - cmd_path="$try_path" - break - elif [[ -e "$try_path" ]]; then - @go.printf "$try_path is not an executable script\n" >&2 - return 1 + cmd_paths+=("$try_path") fi done - if [[ -z "$cmd_path" ]]; then + if [[ "${#cmd_paths[*]}" -eq 0 ]]; then printf "Unknown command: ${cmd_name}\n\n" >&2 _@go.list_available_commands "${_GO_SEARCH_PATHS[@]}" >&2 return 1 fi - local cmd_arg_index=1 - __go_cmd_name=("$cmd_name") - - for arg in "${cmd_args[@]}"; do - # This is most likely to happen during argument completion. - if [[ -z "$arg" ]]; then - break - fi - - try_path="${cmd_path}.d/$arg" - - if [[ ! -e "$try_path" ]]; then - break - elif [[ ! (-f "$try_path" && -x "$try_path") ]]; then - @go.printf "$try_path is not an executable script\n" >&2 - return 1 + local -i cmd_arg_index=1 + cmd_name=("$cmd_name") + + local -i i=0 + local -i longest_path=() + __go_cmd_name=() + for try_path in "${cmd_paths[@]}"; do + for arg in "${cmd_args[@]}"; do + # This is most likely to happen during argument completion. + if [[ -z "$arg" ]]; then + break + fi + + try_path="${cmd_paths[$i]}.d/$arg" + + if [[ ! -e "$try_path" ]]; then + break + elif [[ ! (-f "$try_path" && -x "$try_path") ]]; then + break + fi + + cmd_paths[$i]="$try_path" + + cmd_name+=("$arg") + cmd_arg_index=cmd_arg_index+1 + done + + if [[ "${#cmd_name[@]}" -gt "${#__go_cmd_name[@]}" ]]; then + longest_path="$i" + __go_cmd_name=("${cmd_name[@]}") + __go_argv=("${cmd_args[@]:cmd_arg_index}") fi - __go_cmd_name+=("$arg") - cmd_path="$try_path" - unset "cmd_args[$((cmd_arg_index++))]" + i=i+1 done - __go_cmd_path="$cmd_path" - __go_argv=("${cmd_args[@]}") + # The command that is the most nested one takes precedence. Eg + # `scripts/foobar/aaa/bbb/ccc arg1 arg2` takes precedence over + # `scripts/foobar/aaa bbb ccc arg1 arg2`. + __go_cmd_path=('' "${cmd_paths[@]}") + __go_cmd_path[0]="${cmd_paths[longest_path]}" + unset "__go_cmd_path[longest_path+1]" + __go_cmd_path=("${__go_cmd_path[@]}") } diff --git a/lib/internal/set-search-paths b/lib/internal/set-search-paths index df308c9..57c9c6b 100644 --- a/lib/internal/set-search-paths +++ b/lib/internal/set-search-paths @@ -5,6 +5,11 @@ _@go.set_search_paths_add_plugin_paths() { local plugin_path if [[ "${plugin_paths[0]}" != "$1/*/bin" ]]; then + # If more plugins are located under "$1" (ie there are folder under "$1" + # that have a 'bin' folder inside), then the star expansion will fail and + # `*/bin` will be treated as a string. If that does not happen then there + # are plugins under "$1". + # Ensure a plugin's _GO_SCRIPTS_DIRS isn't duplicated in _GO_PLUGINS_PATHS. for plugin_path in "${plugin_paths[@]}"; do local included_path diff --git a/libexec/commands b/libexec/commands index ecff8e7..1a75f45 100755 --- a/libexec/commands +++ b/libexec/commands @@ -100,7 +100,7 @@ _@go.commands_parse_argv() { if ! _@go.set_command_path_and_argv "$@"; then return 1 fi - __go_commands_search_paths=("${__go_cmd_path}.d") + __go_commands_search_paths=("${__go_cmd_path[@]/%/.d}") fi } diff --git a/libexec/complete b/libexec/complete index 9de742b..73841d9 100755 --- a/libexec/complete +++ b/libexec/complete @@ -56,8 +56,8 @@ _@go.complete_command() { . "$_GO_CORE_DIR/lib/internal/complete" exec 2>/dev/null - if _@go.complete_command_path "$@" && - [[ "$(< "$__go_cmd_path")" =~ $tab_completions_pattern ]]; then + if _@go.complete_command_path "$@" \ + && [[ "$(<"${__go_cmd_path[0]}")" =~ $tab_completions_pattern ]]; then _@go.run_command_script "$__go_cmd_path" --complete \ "$__go_complete_word_index" "${__go_argv[@]}" fi diff --git a/libexec/help b/libexec/help index 2892a66..b75e2f6 100755 --- a/libexec/help +++ b/libexec/help @@ -100,7 +100,9 @@ _@go.help_message_for_command() { return 1 fi - local cmd_name="${__go_cmd_path//.d\// }" + __go_cmd_path="${__go_cmd_path[0]}" + + local cmd_name="${__go_cmd_path[0]//.d\// }" cmd_name="${cmd_name##*/}" . "$_GO_CORE_DIR/lib/internal/command_descriptions" @@ -116,14 +118,16 @@ _@go.help_message_for_command() { local filter_pattern='# [Hh]elp [Ff]ilter['$'\n\r'']' - if [[ "$(< "$__go_cmd_path")" =~ $filter_pattern ]]; then + if [[ "$(<"$__go_cmd_path")" =~ $filter_pattern ]]; then __go_cmd_desc="$(_@go.run_command_script "$__go_cmd_path" --help-filter \ "$__go_cmd_desc")" fi if [[ -d "${__go_cmd_path}.d" ]]; then - __go_cmd_desc+="$(printf '\nSubcommands:\n\n'; \ - _@go.source_builtin 'commands' --summaries "${__go_cmd_path}.d")" + __go_cmd_desc+="$( + printf '\nSubcommands:\n\n' + _@go.source_builtin 'commands' --summaries "${__go_cmd_path}.d" + )" fi @go.printf "$_GO_CMD $cmd_name - $__go_cmd_desc\n" } diff --git a/libexec/path b/libexec/path index 0921ec2..c4c1925 100755 --- a/libexec/path +++ b/libexec/path @@ -1,12 +1,12 @@ #! /bin/bash # -# Prints the path to the script, [alias] or [builtin] +# Prints the path(s) to the script, [alias] or [builtin] # # Usage: # {{go}} {{cmd}} # # Will parse the command path out of a complete argument list and return the -# path to the script relative to {{root}}. Aliases are identifed by a path of +# path(s) to the script relative to {{root}}. Aliases are identifed by a path of # `[alias]`. Builtin command paths are prefixed with `[builtin] `. _@go.path() { @@ -29,7 +29,7 @@ _@go.path() { local __go_cmd_path if _@go.set_command_path_and_argv "$@"; then - echo "${__go_cmd_path#$_GO_ROOTDIR/}" + printf '%s\n' "${__go_cmd_path[@]#$_GO_ROOTDIR/}" else return 1 fi diff --git a/tests/path/set-path-and-argv.bats b/tests/path/set-path-and-argv.bats index f8c9859..cb1499e 100644 --- a/tests/path/set-path-and-argv.bats +++ b/tests/path/set-path-and-argv.bats @@ -73,7 +73,7 @@ teardown() { mkdir "$TEST_GO_SCRIPTS_DIR/foobar" run "$TEST_GO_SCRIPT" 'foobar' assert_failure - assert_line_equals 0 "$TEST_GO_SCRIPTS_DIR/foobar is not an executable script" + assert_line_equals 0 "Unknown command: foobar" } @test "$SUITE: error if top-level command script is not executable" { @@ -81,7 +81,7 @@ teardown() { chmod 600 "$TEST_GO_SCRIPTS_DIR/foobar" run "$TEST_GO_SCRIPT" 'foobar' assert_failure - assert_line_equals 0 "$TEST_GO_SCRIPTS_DIR/foobar is not an executable script" + assert_line_equals 0 "Unknown command: foobar" } @test "$SUITE: find subcommand" { @@ -101,27 +101,31 @@ teardown() { assert_line_equals 2 'ARGV: quux' } -@test "$SUITE: error if subcommand name is a directory" { - local cmd_path="$TEST_GO_SCRIPTS_DIR/foobar" - echo '#!' > "$cmd_path" - chmod 700 "$cmd_path" - mkdir -p "${cmd_path}.d/baz" +@test "$SUITE: merge commands from different script dirs" { + create_bats_test_script scripts/foobar 'echo foobar' + create_bats_test_script scripts/foobar.d/baz 'echo baz' + create_bats_test_script scripts/foobar.d/quux 'echo quux' + create_bats_test_script scripts-2/foobar 'echo foobar' + create_bats_test_script scripts-2/foobar.d/baz 'echo baz2' + create_bats_test_script scripts-2/foobar.d/aaa 'echo aaa' - run "$TEST_GO_SCRIPT" 'foobar' 'baz' 'quux' - assert_failure - assert_line_equals 0 "${cmd_path}.d/baz is not an executable script" -} + create_bats_test_script 'go' \ + ". '$_GO_CORE_DIR/go-core.bash' 'scripts' 'scripts-2'" \ + '@go "$@"' -@test "$SUITE: error if subcommand script is not executable" { - local cmd_path="$TEST_GO_SCRIPTS_DIR/foobar" - echo '#!' > "$cmd_path" - chmod 700 "$cmd_path" + run "$TEST_GO_SCRIPT" 'foobar' + assert_success + assert_output_matches foobar - mkdir "${cmd_path}.d" - touch "${cmd_path}.d/baz" - chmod 600 "${cmd_path}.d/baz" + run "$TEST_GO_SCRIPT" 'foobar' 'baz' + assert_success + assert_output_matches baz - run "$TEST_GO_SCRIPT" 'foobar' 'baz' 'quux' - assert_failure - assert_line_equals 0 "${cmd_path}.d/baz is not an executable script" + run "$TEST_GO_SCRIPT" 'foobar' 'quux' + assert_success + assert_output_matches quux + + run "$TEST_GO_SCRIPT" 'foobar' 'aaa' + assert_success + assert_output_matches aaa } From 9fc5d45592020b67bf68118d219c4af40db49f79 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Wed, 2 Oct 2019 16:29:46 +0200 Subject: [PATCH 12/13] Adds index --- go-core.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/go-core.bash b/go-core.bash index 2bf9f6f..593b6a5 100755 --- a/go-core.bash +++ b/go-core.bash @@ -344,8 +344,8 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH" local script_dir for script_dir in "${_GO_SCRIPTS_DIRS[@]}"; do - if [[ "${__go_cmd_path#$script_dir}" =~ /plugins/[^/]+/bin/ ]]; then - _@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}" + if [[ "${__go_cmd_path[0]#$script_dir}" =~ /plugins/[^/]+/bin/ ]]; then + _@go.run_plugin_command_script "${__go_cmd_path[0]}" "${__go_argv[@]}" return fi done @@ -360,13 +360,13 @@ _@go.source_builtin() { } _@go.run_plugin_command_script() { - local _GO_SCRIPTS_DIRS="${__go_cmd_path%/bin/*}/bin" + local _GO_SCRIPTS_DIRS="${__go_cmd_path[0]%/bin/*}/bin" local _GO_ROOTDIR="${_GO_SCRIPTS_DIRS%/*}" local _GO_PLUGINS_PATHS=() local _GO_SEARCH_PATHS=() _@go.set_search_paths - _@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}" + _@go.run_command_script "${__go_cmd_path[0]}" "${__go_argv[@]}" } _@go.run_command_script() { From 9814499efe41e4683afd47756e0e56c98e68e0b1 Mon Sep 17 00:00:00 2001 From: Nikolaos Kakouros Date: Fri, 4 Oct 2019 23:55:03 +0200 Subject: [PATCH 13/13] Better handles completions for candidates from multiple sources --- lib/internal/path | 76 ++++++++++++++------------------------------- libexec/complete | 2 +- tests/complete.bats | 17 ++++++++++ 3 files changed, 42 insertions(+), 53 deletions(-) diff --git a/lib/internal/path b/lib/internal/path index c6e4d7a..2dbb3fd 100644 --- a/lib/internal/path +++ b/lib/internal/path @@ -23,68 +23,40 @@ _@go.set_command_path_and_argv() { return 1 fi - local cmd_args=("$@") - local cmd_name="${cmd_args[0]}" - local cmd_paths=() + local path_suffix local try_path - unset 'cmd_args[0]' - - for try_path in "${_GO_SEARCH_PATHS[@]}"; do - try_path="$try_path/$cmd_name" - - if [[ -f "$try_path" && -x "$try_path" ]]; then - cmd_paths+=("$try_path") - fi - done - - if [[ "${#cmd_paths[*]}" -eq 0 ]]; then - printf "Unknown command: ${cmd_name}\n\n" >&2 - _@go.list_available_commands "${_GO_SEARCH_PATHS[@]}" >&2 - return 1 - fi - - local -i cmd_arg_index=1 - cmd_name=("$cmd_name") - - local -i i=0 - local -i longest_path=() __go_cmd_name=() - for try_path in "${cmd_paths[@]}"; do - for arg in "${cmd_args[@]}"; do - # This is most likely to happen during argument completion. - if [[ -z "$arg" ]]; then - break + __go_argv=() + __go_cmd_path=() + + for ((i = "$#"; i > 0; i--)); do + for try_path in "${_GO_SEARCH_PATHS[@]}"; do + path_suffix="$(printf '%s.d/' "${@:1:i}")" + path_suffix="${path_suffix%.d/}" + try_path="$try_path/$path_suffix" + + if [[ -f "$try_path" && -x "$try_path" ]]; then + __go_cmd_path+=("$try_path") + if [[ "${#__go_argv[@]}" -eq 0 ]]; then + __go_cmd_name=("${@:1:i}") + __go_argv=("${@:i+1}") + fi fi - - try_path="${cmd_paths[$i]}.d/$arg" - - if [[ ! -e "$try_path" ]]; then - break - elif [[ ! (-f "$try_path" && -x "$try_path") ]]; then - break - fi - - cmd_paths[$i]="$try_path" - - cmd_name+=("$arg") - cmd_arg_index=cmd_arg_index+1 done - if [[ "${#cmd_name[@]}" -gt "${#__go_cmd_name[@]}" ]]; then - longest_path="$i" - __go_cmd_name=("${cmd_name[@]}") - __go_argv=("${cmd_args[@]:cmd_arg_index}") + if [[ "${#__go_cmd_path[@]}" -ne 0 ]]; then + break fi - - i=i+1 done # The command that is the most nested one takes precedence. Eg # `scripts/foobar/aaa/bbb/ccc arg1 arg2` takes precedence over # `scripts/foobar/aaa bbb ccc arg1 arg2`. - __go_cmd_path=('' "${cmd_paths[@]}") - __go_cmd_path[0]="${cmd_paths[longest_path]}" - unset "__go_cmd_path[longest_path+1]" - __go_cmd_path=("${__go_cmd_path[@]}") + + if [[ "${#__go_cmd_name[*]}" -eq 0 ]]; then + printf "Unknown command: $1\n\n" >&2 + _@go.list_available_commands "${_GO_SEARCH_PATHS[@]}" >&2 + return 1 + fi } diff --git a/libexec/complete b/libexec/complete index 73841d9..c707847 100755 --- a/libexec/complete +++ b/libexec/complete @@ -58,7 +58,7 @@ _@go.complete_command() { if _@go.complete_command_path "$@" \ && [[ "$(<"${__go_cmd_path[0]}")" =~ $tab_completions_pattern ]]; then - _@go.run_command_script "$__go_cmd_path" --complete \ + _@go.run_command_script "${__go_cmd_path[0]}" --complete \ "$__go_complete_word_index" "${__go_argv[@]}" fi } diff --git a/tests/complete.bats b/tests/complete.bats index f022ff7..c461403 100644 --- a/tests/complete.bats +++ b/tests/complete.bats @@ -185,6 +185,23 @@ teardown() { assert_success "${expected[@]}" } +@test "$SUITE: get completions only for the longest candidate command" { + create_bats_test_script scripts/foobar + create_bats_test_script scripts/foobar.d/baz 'echo ibaz' + create_bats_test_script scripts-2/foobar + create_bats_test_script scripts-2/foobar.d/baz + create_bats_test_script scripts-2/foobar.d/baz.d/aaa \ + '# Tab completions' 'echo bazi' + + create_bats_test_script 'go' \ + ". '$_GO_CORE_DIR/go-core.bash' 'scripts' 'scripts-2'" \ + '@go "$@"' + + run "$TEST_GO_SCRIPT" complete 2 'foobar' 'baz' '' + assert_success + assert_output_matches aaa +} + @test "$SUITE: -h, -help, and --help invoke help command completion" { run "$TEST_GO_SCRIPT" complete 1 -h 'complet' assert_success 'complete '