#!/usr/bin/env bash # core/hook.sh # # Lifecycle hook runner. # # Hooks are shell scripts sourced in lexicographic order from: # dxkit/hooks/// # # Timing: # pre — runs before the command's main logic # post — runs after the command's main logic # # Naming convention: # NN-description.sh (e.g. 10-permissions.sh, 20-update-cron.sh) # Numeric prefix controls execution order. # # Hooks are sourced (not executed) so they have full access to all # loaded modules, functions, and environment variables. # # Usage: # hook::run init pre # hook::run init post # hook::run build post # # Example structure: # dxkit/hooks/ # init/ # pre/ # 10-something.sh # post/ # 10-permissions.sh # 20-update-cron.sh # build/ # post/ # 10-something.sh # ============================================ # Runner # ============================================ function hook::run() { local command="$1" local timing="$2" local hook_dir hook_dir="$(ctx::dxkit)/hooks/${command}/${timing}" [[ -d "$hook_dir" ]] || return 0 local hooks hooks="$(hook::_list "$hook_dir")" [[ -z "$hooks" ]] && return 0 log::info "Running ${timing}-${command} hooks..." local hook while IFS= read -r hook; do [[ -f "$hook" ]] || continue hook::_run_one "$hook" done <<< "$hooks" } # ============================================ # Run a single hook # ============================================ function hook::_run_one() { local hook="$1" local name name="$(basename "$hook")" log::info " → ${name}" # shellcheck disable=SC1090 source "$hook" || { log::error "Hook failed: ${name}" return 1 } } # ============================================ # List hooks in a directory sorted by name # ============================================ function hook::_list() { local dir="$1" find "$dir" -maxdepth 1 -name "*.sh" -type f | sort } # ============================================ # Predicates # ============================================ function hook::has() { local command="$1" local timing="$2" local hook_dir hook_dir="$(ctx::dxkit)/hooks/${command}/${timing}" [[ -d "$hook_dir" ]] && [[ -n "$(hook::_list "$hook_dir")" ]] } # ============================================ # Scaffold hook directory and example file # ============================================ function hook::scaffold() { local command="$1" local timing="${2:-post}" local hook_dir hook_dir="$(ctx::dxkit)/hooks/${command}/${timing}" mkdir -p "$hook_dir" local example="${hook_dir}/10-example.sh" [[ -f "$example" ]] && return 0 cat > "$example" <