|
| 1 | +# pluginhook |
| 2 | + |
| 3 | +A simple plugin system for Bash programs that's better than just hook scripts. |
| 4 | + |
| 5 | +## Plugins as a better way |
| 6 | + |
| 7 | +Let's take the core benefits of hook scripts and re-structure it slightly: |
| 8 | + |
| 9 | + 1. Instead of focusing on hook scripts, we focus on plugins -- a directory of hook scripts |
| 10 | + 1. Like hook scripts, plugins are active by being in a certain place. But they can be named anything |
| 11 | + 1. Multiple plugins can handle a hook. Either for fanout event triggering, or for pipeline filtering |
| 12 | + |
| 13 | +So what is a plugin? A directory full of hook scripts. When a hook is triggered, all arguments provided |
| 14 | +in the trigger are given to each hook script. They also receive STDIN in a pipelined fashion. |
| 15 | + |
| 16 | +## Triggering plugin hooks |
| 17 | + |
| 18 | +You use the `pluginhook` command to trigger hooks as if you might call a traditional hook script directly. |
| 19 | +Where before you might have triggered by calling something like: |
| 20 | + |
| 21 | + hooks/post-commit $REV $USER |
| 22 | + |
| 23 | +You'd instead trigger like this: |
| 24 | + |
| 25 | + pluginhook post-commit $REV $USER |
| 26 | + |
| 27 | +The `pluginhook` command simply loops through all plugin directories found in the path defined by the environment variable `PLUGIN_PATH` and passes the same arguments to any hook scripts by that name. This means installing a plugin is as simple as putting it in your `PLUGIN_PATH`. Then any plugin that has the `post-commit` hook script will be run. |
| 28 | + |
| 29 | +## Pipeline filtering with plugins |
| 30 | + |
| 31 | +You don't just get a "broadcast" mechanism for arguments. You also get stream pipelining. If you pipe a stream |
| 32 | +into pluginhook, it will be passed *through* each plugin hook, letting each plugin act as a filtering process. By clearly |
| 33 | +defining how a hook should be used and how it can play well with others, this becomes very powerful infrastructure. |
| 34 | + |
| 35 | +Here is a plugin we'll call `upper` implementing a `text` hook (which would be in `$PLUGIN_PATH/upper/text`): |
| 36 | + |
| 37 | + #!/usr/bin/env python |
| 38 | + import sys |
| 39 | + sys.stdout.write(sys.stdin.read().upper()) |
| 40 | + |
| 41 | +Here is a plugin we'll call `reverse` that also implements a `text` hook (`$PLUGIN_PATH/reverse/text`): |
| 42 | + |
| 43 | + #!/usr/bin/env ruby |
| 44 | + puts STDIN.read.strip.reverse |
| 45 | + |
| 46 | +One plugin uses Python to implement the hook, the other uses Ruby. But it doesn't matter, they work together when you trigger the hook: |
| 47 | + |
| 48 | + $ echo "hello world" | pluginhook text |
| 49 | + DLROW OLLEH |
| 50 | + |
| 51 | +Only plugins that implement a hook are used as filters for that hook, so there's no need to implement pass-through hooks if a |
| 52 | +plugin doesn't care about a hook. |
| 53 | + |
| 54 | +If ordering is important, you can always rename your plugin directory to start with a number, which will define an order of |
| 55 | +execution. A plugin author might care about when it is run, but it's up to the user to take their advice or decide |
| 56 | +to run it in a different position in the order, by simply renaming the plugin script. |
| 57 | + |
| 58 | +## What's wrong with just hook scripts? |
| 59 | + |
| 60 | +Lots of shell-based systems use hook scripts as a means to allow users to extend or customize behavior. Popular examples |
| 61 | +are [Git](https://www.kernel.org/pub/software/scm/git/docs/githooks.html) and [SVN](http://svnbook.red-bean.com/nightly/en/svn.ref.reposhooks.html), but many systems from [libvirt](http://www.libvirt.org/hooks.html) to [NPM](https://npmjs.org/doc/scripts.html) to [OS X](http://superuser.com/questions/295924/how-to-run-a-script-at-login-logout-in-os-x) use this pattern. Shell scripts make |
| 62 | +for a great way to expose hooks because the shell environment is ubiquitous and lets you easily call into |
| 63 | +scripts or programs written in your language of choice. |
| 64 | + |
| 65 | +The standard implementation of hook scripts is to have a shell script with an execute bit in a particular location |
| 66 | +that's named after the hook. The most famous example is the "post-commit" hook of SVN, which if `hooks/post-commit` |
| 67 | +exists in your repository directory with an execute bit, it will trigger this script after each commit. Some systems |
| 68 | +let you register hooks by providing a location instead of using convention. |
| 69 | + |
| 70 | +However, in either case, each hook points to **one** script. The only way for a third-party piece of software to |
| 71 | +"hook in" is to install itself as the one hook script, or have you manually install it by calling it from your |
| 72 | +existing hook script. What's more, if a third-party piece of software wants to use multiple hooks, you have to |
| 73 | +deal with this several times over. Not only is this a hassle, but leads to complex and non-obvious configurations. |
| 74 | + |
| 75 | +## Author |
| 76 | + |
| 77 | +Jeff Lindsay <progrium@gmail.com> |
| 78 | + |
| 79 | +## License |
| 80 | + |
| 81 | +MIT |
0 commit comments