-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve plugin protocol #120
Comments
Part of #120. Now scripts that may be exported as plugins must reside in a project's `/bin` directory, not the root directory. Some implications of this: The top-level `./go` scripts in plugins will not appear in the parent's available commands. This way a plugin can have its own `./go` script for development. Applications intended to function both standalone and as a plugin are possible. There may be separate `./go` scripts and script directories for the standalone application interface and for the development interface (e.g. `./dev` with its own scripts dir, which could possibly even be `bin/dev`). Applications that want to export plugins, but not the entire application, must extract a new project repository that is imported into the original application. In addition to freeing potential clients from the original application's dependencies, it's a forcing function to ensure the original application's team keeps the plugin interface up-to-date. This change also ensures `_GO_PLUGINS_DIR` is always set. This will have implications in a future commit such that for scripts running as a plugin, this will be the plugin dir for the top-most script. A bunch of the changes in this commit are to the `tests/commands/helpers.bats` functions and how they're called from the `commands/` tests.
Part of #120, and similar to #122, searching the local `_GO_SCRIPT_DIR` before plugin dirs seems like a more natural and scalable model. `_GO_CORE_DIR/libexec` for builtins still comes first. The idea behind this model is that when reading a script within the `./go` script framework, it should be clear that: - core framework scripts are the same everywhere; they can't be shadowed by scripts with the same name - project-local scripts take precedence before any plugin scripts The implications of this last point are that: - a script that is installed as a plugin will find its own scripts before those that share the same name with other installed plugins, avoiding collisions and possibly obscure bugs - the parent project can determine the precedence of plugin scripts sharing the same name, as the plugins can be installed using directory names that force a particular order (e.g. '00-', '01-', '02-' prefixes). - as an alternative to the approach in the preceding point, the parent project can potentially resolve conflicts by installing its own scripts that then delegate to a specific plugin script And to this last point, I've filed #127 to facilitate help text discovery.
This is a step towards #120, in which plugin command scripts will now have their own scoped `_GO_ROOTDIR`, `_GO_SCRIPTS_DIR`, `_GO_SEARCH_PATHS`, and `_GO_PLUGINS_PATHS`. This will encourage more composable plugins, some which may even operate as standalone applications that can be incorporated wholesale into other applications. Notice that the search path precedence is: - _GO_INJECT_SEARCH_PATH - _GO_CORE_DIR/libexec - _GO_SCRIPTS_DIR - _GO_SCRIPTS_DIR/plugins/*/bin - any inherited _GO_PLUGINS_PATHS from the parent environment The implication of the last two points is that command scripts installed as plugins will first search for plugins in their own `_GO_SCRIPTS_DIR`, then through the `_GO_PLUGINS_PATHS` inherited from its parent script. These `_GO_PLUGINS_PATHS` search paths effectively build a command path search that recurses up to the top-level `_GO_PLUGINS_DIR`.
Part of #120. Plugin command scripts now have their own scoped `_GO_ROOTDIR`, `_GO_SCRIPTS_DIR`, `_GO_SEARCH_PATHS`, and `_GO_PLUGINS_PATHS`. This will encourage more composable plugins, some which may even operate as standalone applications that can be incorporated wholesale into other applications. This lays the groundwork for: - sharing a single copy of a plugin between all plugins that may depend on it - installing specific versions of a plugin within plugins themselves to resolve version incompatibilities with other installed versions of a plugin - circular dependency support, though such dependencies are strongly discouraged
Part of #120. This implements semantics similar to npm's `node_modules` by providing a parent directory-recursive plugin search. After implementing #130 and coming back to my original `@go.find_plugin_item_path` that I'd had in another branch, I realized the parent-recursive search could be abstracted into `@go.search_plugins`. Ironically, it was implementing the algorithm first in `_@go.set_search_paths` than enabled me to more thoroughly test the extracted function, since plugin scope was already established and tested. Talk about circular dependencies! As with the earlier commits building up #120, the implication is that rather than having all plugins comprising an application always checkout their own plugins in their own subtree, eventually they can be installed in their parent's plugin directory. This nesting of plugins can be arbitrarily deep, ending at the top-level `_GO_PLUGINS_DIR`. This will help break cyclical dependencies (though they should still be avoided) and save disk space, and paves the way for semantic versioning and `npm`-like functionality (but in pure Bash, modulo `git` or `curl`, et. al, via `./go get`).
Part of #120, built upon `@go.search_plugins`. This implements semantics similar to npm's `node_modules` for library modules. Since Bash's flat namespace creates the potential for function redefinitions, a warning is emitted when a module collision occurs. The hope is that this will encourage good compatibility practices via semantic versioning, ultimately reducing the need for nested plugin module checkouts due to version incompatibilities. Since command scripts should define their own functions using a project-specific namespace that prevents them from being treated as exported units when sourced by a parent script (especially those exported as plugins), command script namespace collisions should be less of a concern. Also, the `_@go.set_use_caller` wrapper function is necessary due to the same Bash <4.3 bug from commit 2b8f09f. Accessing `BASH_SOURCE` and `FUNCNAME` through at least two levels of indirection apparently resolves the issue; this is why `@go.print_stack_trace` always worked. Confirmed the breakage from the previous commit under Bash 3.2.57 and 4.2.25 is resolved by the `_@go.set_use_caller` wrapper.
#126, #128, #130, #135, and #136 have effectively implemented the new protocol, which is very similar to npm's Next steps would be to document the protocol and write a tool to automatically install plugins. But the semantics of the protocol are themselves done, so this issue is closed and I'll file new ones. |
This concludes the refactoring of `_GO_USE_MODULES` to provide correct `@go` command script scope for plugin modules. It is germane to #120. Whereas #136 implemented correctly-scoped lookup of other plugin modules, this allows any module, plugin or otherwise, to invoke its own command scripts or plugin command scripts via `@go`.
Now that I'm trying to write a separate plugin, I'm hitting some rough edges. Basically, I'd like plugins to potentially be standalone programs, but that collides with some presumptions the core framework makes regarding command and module lookup paths. The solution here may overlap with #118.
The text was updated successfully, but these errors were encountered: