Skip to content

Commit 69312a7

Browse files
authored
Merge pull request #130 from mbland/plugin-scope
Support scoped _GO_* variables for plugins
2 parents 829c98e + d393f38 commit 69312a7

File tree

5 files changed

+410
-12
lines changed

5 files changed

+410
-12
lines changed

go-core.bash

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ cd "${0%/*}" || exit 1
5151
#
5252
# This is directory containing the main ./go script. All functions, commands,
5353
# and scripts are invoked relative to this directory.
54-
declare -r -x _GO_ROOTDIR="$PWD"
54+
declare -x _GO_ROOTDIR="$PWD"
5555

5656
if [[ "${BASH_SOURCE[0]:0:1}" != '/' ]]; then
5757
cd "$__go_orig_dir/${BASH_SOURCE[0]%/*}" || exit 1
@@ -258,7 +258,12 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH"
258258
if ! _@go.set_command_path_and_argv "$cmd" "$@"; then
259259
return 1
260260
fi
261-
_@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}"
261+
262+
if [[ "$__go_cmd_path" =~ ^$_GO_PLUGINS_DIR/ ]]; then
263+
_@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}"
264+
else
265+
_@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}"
266+
fi
262267
}
263268

264269
_@go.source_builtin() {
@@ -267,6 +272,32 @@ _@go.source_builtin() {
267272
. "$_GO_CORE_DIR/libexec/$c"
268273
}
269274

275+
_@go.run_plugin_command_script() {
276+
local _GO_SCRIPTS_DIR="${__go_cmd_path%/*}"
277+
local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/*}"
278+
local _GO_SEARCH_PATHS=()
279+
local _GO_PLUGINS_PATHS=()
280+
local plugins_dir="$_GO_SCRIPTS_DIR/plugins"
281+
local plugin_paths=()
282+
283+
# A plugin's own local plugin paths will appear before inherited ones. If
284+
# there is a version incompatibility issue with other installed plugins, this
285+
# allows a plugin's preferred version to take precedence.
286+
while true; do
287+
plugin_paths=("$plugins_dir"/*/bin)
288+
if [[ "${plugin_paths[0]}" != "$plugins_dir/*/bin" ]]; then
289+
_GO_PLUGINS_PATHS+=("${plugin_paths[@]}")
290+
fi
291+
if [[ "$plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then
292+
break
293+
fi
294+
plugins_dir="${plugins_dir%/plugins/*}/plugins"
295+
done
296+
297+
_@go.set_search_paths
298+
_@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}"
299+
}
300+
270301
_@go.run_command_script() {
271302
local cmd_path="$1"
272303
shift

lib/internal/path

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
#! /bin/bash
22

3-
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
3+
_@go.set_search_paths() {
4+
local plugin_path
5+
46
if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then
57
_GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH")
68
fi
7-
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec")
9+
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "$_GO_SCRIPTS_DIR")
10+
11+
# A plugin's _GO_SCRIPTS_DIR may continue to appear in _GO_PLUGINS_PATHS so
12+
# that it's available to other plugins that depend upon it as a circular
13+
# dependency (though such dependencies are strongly discouraged). However, we
14+
# will ensure its _GO_SCRIPTS_DIR isn't duplicated in _GO_SEARCH_PATHS.
15+
for plugin_path in "${_GO_PLUGINS_PATHS[@]}"; do
16+
if [[ "$plugin_path" != "$_GO_SCRIPTS_DIR" ]]; then
17+
_GO_SEARCH_PATHS+=("$plugin_path")
18+
fi
19+
done
20+
}
21+
22+
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
823
_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins"
24+
_GO_PLUGINS_PATHS=("$_GO_PLUGINS_DIR"/*/bin)
925

10-
if [[ -d "$_GO_PLUGINS_DIR" ]]; then
11-
_GO_PLUGINS_PATHS=("$_GO_PLUGINS_DIR"/*/bin)
12-
if [[ "${_GO_PLUGINS_PATHS[0]}" == "$_GO_PLUGINS_DIR/*/bin" ]]; then
13-
unset '_GO_PLUGINS_PATHS'
14-
fi
26+
if [[ "${_GO_PLUGINS_PATHS[0]}" == "$_GO_PLUGINS_DIR/*/bin" ]]; then
27+
unset '_GO_PLUGINS_PATHS[0]'
1528
fi
16-
_GO_SEARCH_PATHS+=("$_GO_SCRIPTS_DIR" "${_GO_PLUGINS_PATHS[@]}")
29+
_@go.set_search_paths
1730
fi
1831

1932
_@go.list_available_commands() {
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#! /usr/bin/env bats
2+
3+
load ../environment
4+
5+
PRINT_SOURCE='printf -- "%s\n" "$BASH_SOURCE"'
6+
7+
setup() {
8+
test_filter
9+
@go.create_test_go_script '@go "$@"'
10+
}
11+
12+
teardown() {
13+
@go.remove_test_go_rootdir
14+
}
15+
16+
@test "$SUITE: project script takes precedence over plugin" {
17+
@go.create_test_command_script 'plugins/foo/bin/foo' "$PRINT_SOURCE"
18+
@go.create_test_command_script 'foo' "$PRINT_SOURCE"
19+
20+
run "$TEST_GO_SCRIPT" 'foo'
21+
assert_success "$TEST_GO_SCRIPTS_DIR/foo"
22+
}
23+
24+
@test "$SUITE: plugin can't use script from top-level _GO_SCRIPTS_DIR" {
25+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
26+
@go.create_test_command_script 'bar' "$PRINT_SOURCE"
27+
28+
run "$TEST_GO_SCRIPT" 'foo'
29+
assert_failure
30+
assert_line_equals 0 'Unknown command: bar'
31+
}
32+
33+
@test "$SUITE: plugin can use script from top-level _GO_PLUGINS_DIR" {
34+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
35+
@go.create_test_command_script 'plugins/bar/bin/bar' "$PRINT_SOURCE"
36+
37+
run "$TEST_GO_SCRIPT" 'foo'
38+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/bar/bin/bar"
39+
}
40+
41+
@test "$SUITE: plugin can use plugin from own plugin dir" {
42+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
43+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' \
44+
"$PRINT_SOURCE"
45+
46+
run "$TEST_GO_SCRIPT" 'foo'
47+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/plugins/bar/bin/bar"
48+
}
49+
50+
@test "$SUITE: plugin's local _GO_SCRIPTS_DIR scripts take precedence" {
51+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
52+
@go.create_test_command_script 'plugins/foo/bin/bar' "$PRINT_SOURCE"
53+
@go.create_test_command_script 'plugins/bar/bin/bar' "$PRINT_SOURCE"
54+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' \
55+
"$PRINT_SOURCE"
56+
57+
run "$TEST_GO_SCRIPT" 'foo'
58+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/bar"
59+
}
60+
61+
@test "$SUITE: local plugins take precedence over top-level _GO_PLUGINS_DIR" {
62+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
63+
@go.create_test_command_script 'plugins/bar/bin/bar' "$PRINT_SOURCE"
64+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' \
65+
"$PRINT_SOURCE"
66+
67+
run "$TEST_GO_SCRIPT" 'foo'
68+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/plugins/bar/bin/bar"
69+
}
70+
71+
@test "$SUITE: circular dependencies in nested plugin dirs" {
72+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
73+
@go.create_test_command_script 'plugins/foo/bin/baz' "$PRINT_SOURCE"
74+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
75+
76+
run "$TEST_GO_SCRIPT" 'foo'
77+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/baz"
78+
}
79+
80+
@test "$SUITE: circular dependencies in top-level _GO_PLUGINS_DIR" {
81+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
82+
@go.create_test_command_script 'plugins/foo/bin/baz' "$PRINT_SOURCE"
83+
@go.create_test_command_script 'plugins/bar/bin/bar' '@go baz'
84+
85+
run "$TEST_GO_SCRIPT" 'foo'
86+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/baz"
87+
}
88+
89+
@test "$SUITE: nested plugin's _GO_SCRIPTS_DIR precedes plugins" {
90+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
91+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
92+
93+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/baz' \
94+
"$PRINT_SOURCE"
95+
@go.create_test_command_script \
96+
'plugins/foo/bin/plugins/bar/bin/plugins/baz/bin/baz' \
97+
"$PRINT_SOURCE"
98+
@go.create_test_command_script 'plugins/foo/bin/plugins/baz/bin/baz' \
99+
"$PRINT_SOURCE"
100+
@go.create_test_command_script 'plugins/baz/bin/baz' "$PRINT_SOURCE"
101+
102+
run "$TEST_GO_SCRIPT" 'foo'
103+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/plugins/bar/bin/baz"
104+
}
105+
106+
@test "$SUITE: nested plugin's plugins precede parents' plugins" {
107+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
108+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
109+
110+
@go.create_test_command_script \
111+
'plugins/foo/bin/plugins/bar/bin/plugins/baz/bin/baz' \
112+
"$PRINT_SOURCE"
113+
@go.create_test_command_script 'plugins/foo/bin/plugins/baz/bin/baz' \
114+
"$PRINT_SOURCE"
115+
@go.create_test_command_script 'plugins/baz/bin/baz' "$PRINT_SOURCE"
116+
117+
run "$TEST_GO_SCRIPT" 'foo'
118+
assert_success \
119+
"$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/plugins/bar/bin/plugins/baz/bin/baz"
120+
}
121+
122+
@test "$SUITE: nested plugin's sibling precedes top-level _GO_PLUGINS_DIR" {
123+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
124+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
125+
126+
@go.create_test_command_script 'plugins/foo/bin/plugins/baz/bin/baz' \
127+
"$PRINT_SOURCE"
128+
@go.create_test_command_script 'plugins/baz/bin/baz' "$PRINT_SOURCE"
129+
130+
run "$TEST_GO_SCRIPT" 'foo'
131+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/foo/bin/plugins/baz/bin/baz"
132+
}
133+
134+
@test "$SUITE: nested plugin finds top-level _GO_PLUGINS_DIR plugin" {
135+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
136+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
137+
138+
@go.create_test_command_script 'plugins/baz/bin/baz' "$PRINT_SOURCE"
139+
140+
run "$TEST_GO_SCRIPT" 'foo'
141+
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/baz/bin/baz"
142+
}
143+
144+
@test "$SUITE: nested plugins dir doesn't leak to sibling plugin" {
145+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
146+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
147+
@go.create_test_command_script 'plugins/foo/bin/plugins/quux/bin/quux' \
148+
"$PRINT_SOURCE"
149+
@go.create_test_command_script 'plugins/baz/bin/baz' '@go quux'
150+
151+
run "$TEST_GO_SCRIPT" 'foo'
152+
assert_failure
153+
assert_line_equals 0 'Unknown command: quux'
154+
}

0 commit comments

Comments
 (0)