Extensibility and Lifecycle Hooks System
This module provides a declarative hooks system for script extension points.
- demo: demo.hooks.sh, demo.hooks-logging.sh, demo.hooks-nested.sh, demo.hooks-registration.sh, ci-mode/demo.ci-modes.sh, ci-mode/ci-10-compile.sh, ci-mode/ci-20-compile.sh
- bin: npm.versions.sh (uses hooks for extensibility)
- documentation: docs/public/hooks.md
- tests: spec/hooks_spec.sh
- E_BASH - Path to .scripts directory
- DEBUG - Always includes "error" when this module loads
- HOOKS_DIR - Hooks scripts directory, default: "ci-cd"
- HOOKS_PREFIX - Hook function prefix, default: "hook:"
- HOOKS_EXEC_MODE - Execution mode ("exec" or "source"), default: "exec"
- HOOKS_AUTO_TRAP - Auto-install EXIT trap, default: "true"
- __HOOKS_DEFINED - Associative array: hook name -> existence
- __HOOKS_CONTEXTS - Associative array: hook name -> pipe-separated contexts
- __HOOKS_REGISTERED - Associative array: hook name -> "friendly:func|friendly2:func2"
- __HOOKS_MIDDLEWARE - Associative array: hook name -> middleware function
- __HOOKS_SOURCE_PATTERNS - Array of patterns for forced sourced mode
- __HOOKS_SCRIPT_PATTERNS - Array of patterns for forced exec mode
- __HOOKS_CAPTURE_SEQ - Counter for capture array naming
- __HOOKS_END_TRAP_INSTALLED - Whether EXIT trap for end hook is installed
- __HOOKS_FLOW_ROUTE - Routing directive from middleware
- __HOOKS_FLOW_TERMINATE - Whether to terminate execution
- __HOOKS_FLOW_EXIT_CODE - Exit code directive
- _traps.sh - trap:on for EXIT trap installation
- _commons.sh - to:slug() for creating filesystem-safe slugs
- Function: hook:hook_name() - direct function implementation
- Registered: hooks:register hook_name "friendly" function_name
- Script: HOOKS_DIR/{hook_name}-.sh or {hook_name}_.sh
- {hook_name}-{purpose}.sh - basic script
- {hook_name}{NN}{purpose}.sh - ordered script (recommended)
- Scripts must be executable (+x) Contract Directives (output from hooks to middleware):
- contract:env:NAME=VALUE - Set environment variable
- contract:env:NAME+=VALUE - Append to PATH-like variable
- contract:env:NAME^=VALUE - Prepend to PATH-like variable
- contract:env:NAME-=VALUE - Remove segment from PATH-like variable
- contract:route:/path/to/script.sh - Route to another script
- contract:exit:42 - Exit with code
Bootstrap default hooks and install EXIT trap for end hook
| Name | Type | Default | Description |
|---|
- reads/listen: HOOKS_AUTO_TRAP
- mutate/publish: __HOOKS_DEFINED, __HOOKS_END_TRAP_INSTALLED
- Declares begin/end hooks
- Installs EXIT trap if HOOKS_AUTO_TRAP=true
hooks:bootstrapDeclare available hook names for the script
| Name | Type | Default | Description |
|---|---|---|---|
@ |
string array | variadic | Hook names to declare |
- reads/listen: BASH_SOURCE, __HOOKS_DEFINED, __HOOKS_CONTEXTS
- mutate/publish: __HOOKS_DEFINED, __HOOKS_CONTEXTS
- Registers hook names as available
- Tracks calling context for nested/composed scripts
hooks:declare begin end validate process
hooks:declare custom_hook another_hook- 0 on success, 1 on invalid hook name
Execute a hook and all its implementations
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to execute |
@ |
variadic | required | Additional parameters to pass to implementations |
- reads/listen: __HOOKS_DEFINED, __HOOKS_REGISTERED, HOOKS_DIR, HOOKS_PREFIX, HOOKS_EXEC_MODE, __HOOKS_SOURCE_PATTERNS, __HOOKS_SCRIPT_PATTERNS, __HOOKS_MIDDLEWARE
- mutate/publish: none (calls hook implementations)
- Executes hook:hook_name function if exists
- Executes all registered functions in alphabetical order
- Executes all matching scripts in HOOKS_DIR
- Calls middleware for output processing Execution order:
- Check if hook is defined via hooks:declare
- Execute function hook:{name} if it exists
- Execute registered functions (hooks:register) in alphabetical order
- Find and execute matching scripts in HOOKS_DIR/{hook_name}-.sh or {hook_name}_.sh
- Scripts execute in alphabetical order Script naming patterns:
- {hook_name}-{purpose}.sh
- {hook_name}{NN}{purpose}.sh (recommended for ordered execution) Execution modes (controlled by HOOKS_EXEC_MODE):
- "exec" (default): Scripts execute in subprocess
- "source": Scripts sourced, hook:run function called Logging:
- Enable with DEBUG=hooks or DEBUG=* to see execution flow
hooks:do begin
hooks:do decide param1 param2
result=$(hooks:do decide "question")- Last hook's exit code or 0 if not implemented
Execute a hook with forced exec mode (overrides HOOKS_EXEC_MODE)
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to execute |
@ |
variadic | required | Additional parameters |
- reads/listen: HOOKS_EXEC_MODE
- mutate/publish: HOOKS_EXEC_MODE (temporarily sets to "exec")
hooks:do:script end
hooks:do:script notify url status- Last hook's exit code or 0 if not implemented
Execute a hook with forced sourced mode (overrides HOOKS_EXEC_MODE)
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to execute |
@ |
variadic | required | Additional parameters |
- reads/listen: HOOKS_EXEC_MODE
- mutate/publish: HOOKS_EXEC_MODE (temporarily sets to "source")
hooks:do:source begin
hooks:do:source deploy param1 param2- Last hook's exit code or 0 if not implemented
Determine execution mode for a specific script
| Name | Type | Default | Description |
|---|---|---|---|
script_name |
string | required | Script basename to check |
- reads/listen: HOOKS_EXEC_MODE, __HOOKS_SOURCE_PATTERNS, __HOOKS_SCRIPT_PATTERNS
- mutate/publish: none
- Echoes "source" or "exec"
mode=$(hooks:exec:mode "begin-init.sh")Apply flow directives from middleware (route, exit)
| Name | Type | Default | Description |
|---|
- reads/listen: __HOOKS_FLOW_TERMINATE, __HOOKS_FLOW_ROUTE, __HOOKS_FLOW_EXIT_CODE
- mutate/publish: none (may exit or source route script)
- May exit with code if __HOOKS_FLOW_TERMINATE=true
- May source route script if __HOOKS_FLOW_ROUTE set
hooks:flow:apply # call after hook executionCheck if a hook is defined
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to check |
- reads/listen: __HOOKS_DEFINED
- mutate/publish: none
if hooks:known begin; then echo "begin hook defined"; fi- 0 if hook is defined, 1 otherwise
List all defined hooks and their implementations
| Name | Type | Default | Description |
|---|
- reads/listen: __HOOKS_DEFINED, __HOOKS_REGISTERED, HOOKS_PREFIX, HOOKS_DIR
- mutate/publish: none
hooks:list- 0, prints hooks and implementations to stdout
Register middleware function for a hook
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name for middleware |
middleware_fn |
string | optional | Middleware function name (empty to reset) |
- reads/listen: __HOOKS_MIDDLEWARE
- mutate/publish: __HOOKS_MIDDLEWARE
hooks:middleware begin my_middleware
hooks:middleware begin # reset to default- 0 on success, 1 on invalid parameters or missing function
Register file patterns to always execute as scripts (not sourced)
| Name | Type | Default | Description |
|---|---|---|---|
@ |
string array | variadic | File patterns (wildcards supported) |
- reads/listen: none
- mutate/publish: __HOOKS_SCRIPT_PATTERNS
hooks:pattern:script "end-datadog.sh"
hooks:pattern:script "notify-*.sh"Register file patterns to always execute in sourced mode
| Name | Type | Default | Description |
|---|---|---|---|
@ |
string array | variadic | File patterns (wildcards supported) |
- reads/listen: none
- mutate/publish: __HOOKS_SOURCE_PATTERNS
hooks:pattern:source "begin-*-init.sh"
hooks:pattern:source "env-*.sh" "config-*.sh"Register a function to be executed as part of a hook
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to register for |
friendly_name |
string | required | Sort key for ordering (e.g. "10-backup") |
function_name |
string | required | Function to execute |
- reads/listen: __HOOKS_DEFINED, __HOOKS_REGISTERED
- mutate/publish: __HOOKS_REGISTERED
hooks:register deploy "10-backup" backup_database
hooks:register deploy "20-update" update_code- 0 on success, 1 on invalid parameters or missing function
Reset all hooks module state (for testing)
| Name | Type | Default | Description |
|---|
- reads/listen: none
- mutate/publish: __HOOKS_DEFINED, __HOOKS_CONTEXTS, __HOOKS_REGISTERED, __HOOKS_MIDDLEWARE, __HOOKS_SOURCE_PATTERNS, __HOOKS_SCRIPT_PATTERNS, __HOOKS_CAPTURE_SEQ, __HOOKS_END_TRAP_INSTALLED, HOOKS_DIR, HOOKS_PREFIX, HOOKS_EXEC_MODE, HOOKS_AUTO_TRAP
- Unsets and redeclares all global arrays/variables
- Resets to default values
hooks:reset # typically in test teardownCheck if a hook has any implementation (function or script)
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name to check |
- reads/listen: HOOKS_PREFIX, HOOKS_DIR, __HOOKS_REGISTERED
- mutate/publish: none
if hooks:runnable begin; then echo "has implementation"; fi- 0 if hook has implementation, 1 otherwise
Unregister a function from a hook
| Name | Type | Default | Description |
|---|---|---|---|
hook_name |
string | required | Hook name |
friendly_name |
string | required | Friendly name of registration to remove |
- reads/listen: __HOOKS_REGISTERED
- mutate/publish: __HOOKS_REGISTERED
hooks:unregister deploy "10-backup"
hooks:unregister build "metrics"- 0 on success, 1 on invalid parameters or not found