Skip to content
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

Closed
mbland opened this issue Jan 19, 2017 · 1 comment
Closed

Improve plugin protocol #120

mbland opened this issue Jan 19, 2017 · 1 comment
Assignees
Milestone

Comments

@mbland
Copy link
Owner

mbland commented Jan 19, 2017

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.

@mbland mbland added this to the v1.4.0 milestone Jan 19, 2017
@mbland mbland self-assigned this Jan 19, 2017
mbland added a commit that referenced this issue Jan 22, 2017
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.
mbland added a commit that referenced this issue Jan 22, 2017
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.
mbland added a commit that referenced this issue Jan 23, 2017
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`.
mbland added a commit that referenced this issue Jan 23, 2017
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
mbland added a commit that referenced this issue Jan 26, 2017
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`).
mbland added a commit that referenced this issue Jan 26, 2017
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.
@mbland
Copy link
Owner Author

mbland commented Jan 27, 2017

#126, #128, #130, #135, and #136 have effectively implemented the new protocol, which is very similar to npm's node_modules schema. Plugins can be nested arbitrarily deep, with command script, module, and plugin lookup paths scoped correctly at every level in the hierarchy.

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.

@mbland mbland closed this as completed Jan 27, 2017
mbland added a commit that referenced this issue Jan 31, 2017
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`.
mbland added a commit that referenced this issue Feb 10, 2017
Preliminary refactoring to use functions introduced as part of #120
before making more substantial changes towards to #150. Note that
`_@go.find_module` replaces `_@go.module_path`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant