{
  "markdown": "[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/OleksandrKucherenko/e-bash) [![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat-square&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/OleksandrKucherenko/e-bash) [![codecov](https://codecov.io/gh/OleksandrKucherenko/e-bash/branch/master/graph/badge.svg)](https://codecov.io/gh/OleksandrKucherenko/e-bash) [![Run in Smithery](https://smithery.ai/badge/skills/oleksandrkucherenko)](https://smithery.ai/skills?ns=oleksandrkucherenko&utm_source=github&utm_medium=badge)\n\n# Enhanced BASH Scripts\n\n- [Enhanced BASH Scripts](#enhanced-bash-scripts)\n  - [Usage](#usage)\n    - [Homebrew (macOS/Linux)](#homebrew-macoslinux)\n    - [Direct Download (Manual User)](#direct-download-manual-user)\n    - [Using e-bash in Your Scripts](#using-e-bash-in-your-scripts)\n    - [Manual installation](#manual-installation)\n  - [Library Overview](#library-overview)\n    - [Colors](#colors)\n    - [Script Dependencies](#script-dependencies)\n    - [Logger](#logger)\n    - [Arguments Parsing](#arguments-parsing)\n    - [Common(s) Functions And Inputs](#commons-functions-and-inputs)\n    - [UI: Selector](#ui-selector)\n    - [UI: Ask for Password](#ui-ask-for-password)\n    - [UI: Multi-line Text Editor](#ui-multi-line-text-editor)\n    - [Dry-Run Wrapper System](#dry-run-wrapper-system)\n    - [Hooks](#hooks)\n    - [Semver - Semantic Versioning](#semver---semantic-versioning)\n    - [Git Semantic/Conventional commits](#git-semanticconventional-commits)\n    - [Git Verify Commits Messages - Conventional Commits](#git-verify-commits-messages---conventional-commits)\n    - [Git Logs](#git-logs)\n    - [Git Files Changes](#git-files-changes)\n    - [Self-Update](#self-update)\n    - [Documentation Generator: e-docs](#documentation-generator-e-docs)\n    - [Troubleshooting](#troubleshooting)\n    - [Profile BASH script execution](#profile-bash-script-execution)\n    - [Colors support in my terminal](#colors-support-in-my-terminal)\n    - [Emoji support in my terminal](#emoji-support-in-my-terminal)\n    - [References](#references)\n  - [Contributing](#contributing)\n    - [Roadmap](#roadmap)\n    - [Local Dev Environment - Requirements](#local-dev-environment---requirements)\n    - [TDD - Test Driven Development, run tests on file change](#tdd---test-driven-development-run-tests-on-file-change)\n  - [License](#license)\n\n## Usage\n\nInstallation into your project with helper script:\n\n> install/upgrade to the latest version\n\n```bash\n# install into current folder/workspace\ncurl -sSL https://git.new/e-bash | bash -s --\n\n# OR: install into global folder (~/.e-bash)\ncurl -sSL https://git.new/e-bash | bash -s -- --global install\n```\n\nAlternatives:\n\n```bash\n# OR: install latest version\nwget -qO- https://git.new/e-bash | bash -s -- install\n# OR: install latest version (httpie)\nhttp -b https://git.new/e-bash | bash -s -- install\n```\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew install artfulbits-se/tap/e-bash\n```\n\n### Direct Download (Manual User)\n\n```bash\n# install specific version\ncurl -sSL https://git.new/e-bash | bash -s -- install v1.0.0\n# OR: install specific version\nwget -qO- https://git.new/e-bash | bash -s -- install v1.0.0\n# OR: install specific version (httpie)\nhttp -b https://git.new/e-bash | bash -s -- install v1.0.0\n```\n\n[More details](./docs/public/installation.md)\n\n### Using e-bash in Your Scripts\n\nTo use e-bash utilities in your bash scripts, add this bootstrap snippet at the top:\n\n**Version A: Documented/Readable** (with error handling)\n\n```bash\n# -----------------------------------------------------------------------------\n# e-bash bootstrap: auto-establish E_BASH and PATH\n# -----------------------------------------------------------------------------\n[ -z \"$E_BASH\" ] && readonly E_BASH=\"$(\n  # 1. Use existing E_BASH if set\n  # 2. Find .scripts relative to this script\n  # 3. Fallback to ~/.e-bash/.scripts\n  if [ -n \"${E_BASH+x}\" ]; then\n    echo \"$E_BASH\"\n  elif [ -f \"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)/../.scripts/_colors.sh\" ]; then\n    echo \"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && cd ../.scripts && pwd)\"\n  elif [ -d \"$HOME/.e-bash/.scripts\" ]; then\n    echo \"$HOME/.e-bash/.scripts\"\n  else\n    echo \"\" >&2\n    echo \"Error: Cannot find e-bash library. Please install e-bash first.\" >&2\n    echo \"  Visit: https://github.com/OleksandrKucherenko/e-bash\" >&2\n    echo \"\" >&2\n    exit 1\n  fi\n)\"\n\n# Add gnubin to PATH (Linux/macOS compatibility)\nif [[ -d \"$E_BASH/../bin/gnubin\" ]]; then\n  PATH=\"$E_BASH/../bin/gnubin:$PATH\"\n  # Source _gnu.sh to create symlinks if needed\n  [[ -f \"$E_BASH/_gnu.sh\" ]] && source \"$E_BASH/_gnu.sh\"\nfi\n\n# Now source required modules\nsource \"$E_BASH/_colors.sh\"\nsource \"$E_BASH/_logger.sh\"\n# ... add more modules as needed\n# -----------------------------------------------------------------------------\n```\n\n**Version B: Compact 2-LOC** (248 characters, good balance)\n\n```bash\n# 2-LOC bootstrap: E_BASH discovery + gnubin PATH\n[ -z \"$E_BASH\" ] && readonly E_BASH=\"$(cd \"$(dirname \"${BASH_SOURCE[0]:-$0}\")\" && [[ -f ../.scripts/_colors.sh ]] && echo \"$(pwd)/../.scripts\" || echo \"$HOME/.e-bash/.scripts\")\" && source \"${E_BASH}/_gnu.sh\" && PATH=\"${E_BASH}/../bin/gnubin:$PATH\"\n# Source modules (examples)\nsource \"$E_BASH/_colors.sh\"; source \"$E_BASH/_logger.sh\"\n```\n\n**Version C: Ultra-Optimized** (used in e-bash scripts)\n\n```bash\n# Bootstrap: 1) E_BASH discovery (only if not set), 2) gnubin setup (always)\n[ \"$E_BASH\" ] || { _src=${BASH_SOURCE:-$0}; E_BASH=$(cd \"${_src%/*}/../.scripts\" 2>&- && pwd || echo ~/.e-bash/.scripts); readonly E_BASH; }\n. \"$E_BASH/_gnu.sh\"; PATH=\"$(cd \"$E_BASH/../bin/gnubin\" 2>&- && pwd):$PATH\"\n# Source modules (examples)\nsource \"$E_BASH/_colors.sh\"; source \"$E_BASH/_logger.sh\"\n```\n\n**Note:** All versions use the same discovery order: `E_BASH` env var → relative `.scripts/` → `~/.e-bash/.scripts` fallback. Version A has full error handling; Versions B/C are more compact.\n\nThen source the modules you need:\n\n- `source \"$E_BASH/_logger.sh\"` - [Advanced logging with tag-based filtering](docs/public/lib/_logger.md)\n- `source \"$E_BASH/_dependencies.sh\"` - [Dependency management with version constraints](docs/public/lib/_dependencies.md)\n- `source \"$E_BASH/_arguments.sh\"` - [Command-line argument parsing](docs/public/lib/_arguments.md)\n- `source \"$E_BASH/_hooks.sh\"` - [Declarative hooks system](docs/public/lib/_hooks.md)\n- `source \"$E_BASH/_traps.sh\"` - [Enhanced trap management](docs/public/lib/_traps.md)\n- `source \"$E_BASH/_semver.sh\"` - [Semantic versioning support](docs/public/lib/_semver.md)\n- `source \"$E_BASH/_commons.sh\"` - [Common utilities and UI components](docs/public/lib/_commons.md)\n- `source \"$E_BASH/_colors.sh\"` - [Terminal color detection and ANSI definitions](docs/public/lib/_colors.md)\n\nSee `.scripts/` directory for all available modules.\n\n### Manual installation\n\n```bash\ngit remote add -f e-bash https://github.com/OleksandrKucherenko/e-bash.git  \ngit checkout -b e-bash-temp e-bash/master  \ngit subtree split -P .scripts -b e-bash-scripts  \ngit checkout master # or main - depends on your main branch in repo\ngit subtree merge --prefix .scripts e-bash-scripts --squash\n```\n\nUpgrade `.scripts` to the latest version:\n\n```bash\ngit fetch e-bash master  \ngit checkout e-bash-temp && git reset --hard e-bash/master  \ngit subtree split -P .scripts -b e-bash-scripts  \ngit checkout <your-main-branch>  \ngit subtree pull --prefix .scripts e-bash-scripts --squash\n```\n\nrefs:\n\n- [Git Subtree Basics](https://gist.github.com/SKempin/b7857a6ff6bddb05717cc17a44091202)\n- [git-cross](https://github.com/epcim/git-cross)\n- [git-subrepo](https://github.com/ingydotnet/git-subrepo)\n- [github repo for article](https://gist.github.com/icheko/9ff2a0a90ef2b676a5fc8d76f69db1d3), [article itself](https://medium.com/@icheko/use-a-subfolder-from-a-remote-repo-in-another-repo-using-git-subtree-98046f33ca40)\n\n## Library Overview\n\n### Colors\n\n```bash\nsource \".scripts/_colors.sh\"\n\necho -e \"${cl_red}Hello World${cl_reset}\"\n```\n\n[API Reference](docs/public/lib/_colors.md)\n\n### Script Dependencies\n\n![Bootstrap](docs/images/public/bootstrap.direnv.gif)\n\n```bash\nsource \".scripts/_dependencies.sh\"\n\ndependency bash \"5.*.*\" \"brew install bash\"\ndependency direnv \"2.*.*\" \"curl -sfL https://direnv.net/install.sh | bash\"\ndependency shellspec \"0.28.*\" \"brew install shellspec\"\noptional kcov \"42\" \"brew install kcov\"\ndependency shellcheck \"0.9.*\" \"curl -sS https://webi.sh/shellcheck | sh\"\ndependency shfmt \"3.*.*\" \"curl -sS https://webi.sh/shfmt | sh\"\ndependency watchman \"2023.07.*.*\" \"brew install watchman\"\n\n# different return codes for success and failure\ndependency watchman \"2023.07.*.*\" \"brew install watchman\" && echo \"OK!\" || echo \"FAIL!\"\n\n# optional always return success\noptional watchman \"2023.07.*.*\" \"brew install watchman\" && echo \"OK!\" || echo \"never happens!\"\n\n# Allow of HEAD or stable versions of the watchman tool\nwHead=$(dependency watchman \"HEAD-[a-f0-9]{1,8}\" \"brew install watchman\")\nwStab=$(dependency watchman \"2024.*.*.*\" \"brew install watchman\")\necho \"$wHead\" | grep 'Error' &>/dev/null && echo \"$wStab\" || echo \"$wHead\"\n```\n\n[API Reference](docs/public/lib/_dependencies.md)\n\n### Logger\n\n[Quick Start Guide](docs/public/logger.md) | [API Reference](docs/public/lib/_logger.md)\n\nRequirements:\n\n- [x] zero dependencies, pure BASH (optional: _colors.sh)\n- [x] prefix for all logger messages\n- [x] work in pipe mode (forward logs to the named pipe)\n  - [x] write logs to pipe; single line or multiple lines in '|' pipe mode\n  - [x] read logs from the named pipe and output to the console (or file).\n  - [x] redirect logs to file/stream/pipe/tty \n- [x] support prefix for each log message\n- [x] listen to DEBUG environment variable for enabling/disabling logs\n  - [x] enable/disable log by tag name or tag name prefix (support wildcards)\n- [x] execute command with logging the command and it parameters first, ref: [echo-eval](https://bpkg.sh/pkg/echo-eval) - use DRYRUN functionality for that\n  - [x] can be easily self-made, ref: [echo-eval, ee](https://github.com/kj4ezj/echo-eval/blob/main/ee.sh)\n\n```bash\nsource \".scripts/_logger.sh\"\nlogger common \"$@\" # declare echo:Common and printf:Common functions, tag: common\nlogger debug \"$@\" # declare echo:Debug and printf:Debug functions, tag: debug\n\necho:Common \"Hello World\" # output \"Hello World\" only if tag common is enabled\n\nexport DEBUG=*          # enable logger output for all tags\nexport DEBUG=common     # enable logger output for common tag only\nexport DEBUG=*,-common  # enable logger output for all tags except common\n\n# advanced functions\nconfig:logger:Common \"$@\" # re-configure logger enable/disable for common tag\n\n# echo in pipe mode\nfind . -type d -max-depth 1 | log:Common\n\n# echo in output redirect\nfind . -type d -max-depth 1 >log:Common\n\n# more samples of usage are in `demos/demo.logs.sh` file\n```\n\nComplete demo: [Logger Demo](demos/demo.logs.sh)\n\n### Arguments Parsing\n\nRequirements:\n\n- [x] zero dependencies, pure BASH\n- [x] support short and long arguments\n- [x] support default values\n- [x] support required arguments\n- [x] support aliases for arguments\n- [x] support destination variables for argument\n- [x] compose help documentation from arguments definition\n\n```bash\n# pattern: \"{argument_index},-{short},--{alias}={output_variable}:{default_initialize_value}:{reserved_args_quantity}\"\n# example: \"-h,--help=args_help:true:0\", on --help or -h set $args_help variable to true, expect no arguments;\n# example: \"$1,--id=args_id::1\", expect first unnamed argument to be assigned to $args_id variable; can be also provided as --id=123\nexport ARGS_DEFINITION=\"-h,--help -v,--version=:1.0.0\"\nexport ARGS_DEFINITION+=\" --debug=DEBUG:*\"\n\n# will automatically parse script arguments with definition from $ARGS_DEFINITION global variable\nsource \"$E_BASH/_arguments.sh\"\n\n# check variables that are extracted\necho \"Is --help: $help\"\necho \"Is --version: $version\"\necho \"Is --debug: $DEBUG\"\n\n# advanced run. parse provided arguments with definition from $ARGS_DEFINITION global variable\nparse:arguments \"$@\"\n```\n\nMore details: [Arguments Parsing](docs/public/arguments.md), [Demo script](demos/demo.args.sh), [API Reference](docs/public/lib/_arguments.md).\n\n### Common(s) Functions And Inputs\n\n[Complete Documentation](docs/public/commons.md) | [API Reference](docs/public/lib/_commons.md)\n\n```bash\nsource \".scripts/_commons.sh\"\n\n# Find git repository root (handles regular repos, worktrees, submodules)\nrepo_root=$(git:root)\nrepo_type=$(git:root \".\" \"type\")  # regular, worktree, or submodule\n\n# Find configuration file hierarchy (similar to c12/cosmiconfig)\nconfigs=$(config:hierarchy \".eslintrc\" \".\" \"git\" \",.js,.json,.yaml\")\n# Returns files in order: root → current (for proper config merging)\n\n# XDG-compliant config discovery\nconfigs=$(config:hierarchy:xdg \"nvim\" \"init.vim\" \".\" \"home\")\n# Searches: project configs → ~/.config/nvim → /etc/xdg/nvim → /etc/nvim\n\n# Extract parameter from global env variable OR from secret file (file content)\nenv:variable:or:secret:file \"new_value\" \\\n  \"GITLAB_CI_INTEGRATION_TEST\" \\\n  \".secrets/gitlab_ci_integration_test\" \\\n  \"{user friendly message}\"\n\necho \"Extracted: ${new_value}\"\n```\n\n### UI: Selector\n\n![Selector](docs/images/public/ui.selector.gif)\n\n```bash\nsource \".scripts/_commons.sh\"\n\n# Select value from short list of choices\ndeclare -A -g connections && connections=([\"d\"]=\"production\" [\"s\"]=\"cors-proxy:staging\" [\"p\"]=\"cors-proxy:local\")\necho -n \"Select connection type: \" && tput civis # hide cursor\nselected=$(input:selector \"connections\") && echo \"${cl_blue}${selected}${cl_reset}\"\n```\n\n[API Reference](docs/public/lib/_tui.md#inputselector)\n\n### UI: Ask for Password\n\n![Ask for Password](docs/images/public/ui.ask-for-password.gif)\n\n```bash\nsource \".scripts/_commons.sh\"\n\n# Usage:\necho -n \"Enter password: \"\npassword=$(input:readpwd) && echo \"\" && echo \"Password: $password\"\n```\n\n[API Reference](docs/public/lib/_tui.md#inputreadpwd)\n\n### UI: Multi-line Text Editor\n\n```bash\nsource \".scripts/_commons.sh\"\n\n# Open a multi-line text editor (Ctrl+D to save, Esc to cancel)\ntext=$(input:multi-line -w 60 -h 10)\n\n# Full-screen editor with alternative buffer (preserves scroll history)\ntext=$(input:multi-line --alt-buffer)\n\n# Stream mode - inline editor at cursor position (5 lines, full terminal width)\n# Handles bottom-of-terminal by scrolling up; repositions cursor on exit\ntext=$(input:multi-line -m stream)\n\n# Stream mode with custom height\ntext=$(input:multi-line -m stream -h 10)\n\n# Custom keybinding: Ctrl+S to save (use _input:capture-key to discover tokens)\nML_KEY_SAVE=\"ctrl-s\" text=$(input:multi-line -x 5 -y 2 -w 80 -h 20)\n\n# Ctrl+E opens readline for current line (full word movement, history)\n\n# Key capture diagnostic: see hex bytes and tokens for any keypress\n_input:capture-key\n```\n\nControls: Arrow keys, Page Up/Down, Home/End to navigate, Enter for newline,\nBackspace to delete, Ctrl+W delete word, Ctrl+U delete line, Ctrl+V paste,\nCtrl+E readline edit, Tab inserts 2 spaces. Status bar shows position and [+] modified indicator.\nSupports bracketed paste detection and box mode clamping to terminal boundaries.\n\n[API Reference](docs/public/lib/_tui.md#inputmulti-line), [Demo script](demos/demo.multi-line.sh), [Key Capture Demo](demos/demo.capture-key.sh)\n\n### Dry-Run Wrapper System\n\nSafe command execution with three-mode operation: normal, dry-run (preview), and undo/rollback. Provides automatic logging, exit status tracking, and flexible per-command configuration.\n\n```bash\nsource \"$E_BASH/_dryrun.sh\"\n\n# Create wrappers for commands\ndry-run git docker kubectl\n\n# Normal, safe operation, does not mutate anything\nrun:git status\n\n# Normal mode - execute commands, if no DRY_RUN set\ndry:git pull origin main\ndry:docker build -t app .\n\n# Normal mode - Register rollback command, executed only when UNDO_RUN is set\nrollback:kubectl delete deployment app\n\n# Dry-run mode - preview operation in terminal without executing it\nDRY_RUN=true dry:git pull origin main\n\n# Undo mode - execute rollbacks, only when UNDO_RUN=true\nUNDO_RUN=true rollback:git reset --hard\n\nfunction rollback_fn() {\n  echo \"Cleaning up...\"\n  echo \"Possible execution of multiple commands...\"\n}\n\n# Rollback via special function\nrollback:func rollback_fn\n```\n\n**Three Execution Modes:**\n\n| Mode    | DRY_RUN | UNDO_RUN | Normal Commands | Rollback Commands |\n| ------- | ------- | -------- | --------------- | ----------------- |\n| Normal  | false   | false    | Execute         | Dry-run (safe)    |\n| Dry-run | true    | false    | Dry-run         | Dry-run           |\n| Undo    | false   | true     | Dry-run         | **Execute**       |\n\n**Features:**\n\n- ✅ Color-coded logging with exit status (`execute:`, `dry run:`, `undoing:`)\n- ✅ Command-specific overrides (`DRY_RUN_GIT=false`, pattern: `DRY_RUN_*`)\n- ✅ Silent mode support (`SILENT_GIT=true`, pattern: `SILENT_*`)\n- ✅ Function-based rollbacks (`rollback:func cleanup_fn`)\n- ✅ Variable precedence: command-specific → global → default\n\nMore details: [Dry-Run Wrapper System](docs/public/dryrun-wrapper.md), [Demo script](demos/demo.dryrun-modes.sh), [API Reference](docs/public/lib/_dryrun.md).\n\n### Hooks\n\nAdd extension points to any bash script with minimal changes. Hook implementations live in external files - your script just declares and triggers them.\n\n**Before** (your existing script):\n\n```bash\n#!/bin/bash\necho \"Starting deployment...\"\ndeploy_application\necho \"Done.\"\n```\n\n**After** (several lines added):\n\n```diff\n#!/bin/bash\n+ export HOOKS_DIR=\".hooks\"  # Default is 'ci-cd'\n+ source \"$E_BASH/_hooks.sh\"\n+ hooks:declare begin deploy end\n\n+ hooks:do begin\necho \"Starting deployment...\"\ndeploy_application\n+ hooks:do deploy \"$VERSION\"\necho \"Done.\"\n+ hooks:do end\n```\n\nNow add any functionality via external scripts in `.hooks/` folder following pattern in naming `{hook_name}-{purpose}.sh`:\n\nhook_name is one of the declared hooks: `begin`, `deploy`, `end`; purpose - is your user-friendly name for the hook.\n\nHooks are executed in alphabetical order, and you can declare multiple hooks for the same hook point/name.\n\n**.hooks/begin-otel-trace.sh** - OpenTelemetry tracing:\n\n```bash\n#!/bin/bash\nexport TRACE_ID=$(openssl rand -hex 16)\ncurl -s \"${OTEL_ENDPOINT}/v1/traces\" -d \"{...span data...}\" &>/dev/null\n```\n\n**.hooks/deploy-slack-notify.sh** - Slack notifications:\n\n```bash\n#!/bin/bash\ncurl -s \"$SLACK_WEBHOOK\" -d \"{\\\"text\\\":\\\"Deploying $1\\\"}\"\n```\n\n**.hooks/end-metrics.sh** - Metrics export:\n\n```bash\n#!/bin/bash\necho \"deployment.duration=$SECONDS\" | nc -u metrics.local 8125\n```\n\n**Custom hooks directory per script:**\n\n```bash\nHOOKS_DIR=\".hooks/$(basename \"$0\" .sh)\"  # .hooks/my-script/\nsource \"$E_BASH/_hooks.sh\"\n```\n\nor maybe [Hooks Documentation](docs/public/hooks.md) or [API Reference](docs/public/lib/_hooks.md);\n\nDemos: [Intro](demos/demo.hooks.sh), [Multiple Hooks](demos/demo.hooks-registration.sh), [Nested Hooks](demos/demo.hooks-nested.sh), [Hooks with Logs](demos/demo.hooks-logging.sh), [CI Hooks Demo](./demos/ci-mode/demo.ci-modes.sh), [CI Hooks with Middlewar](./demos/ci-mode/demo.ci-modes-middleware.sh)\n\n### Semver - Semantic Versioning\n\nRequirements:\n\n- [x] parse version code, according to semver specification\n- [x] compare version code\n- [x] verify version constraints\n- [x] compose version code from array of segments\n\n```bash\nsource \".scripts/_semver.sh\"\n\n# verify that version is passing the constraints expression\nsemver:constraints \"1.0.0-alpha\" \">1.0.0-beta || <1.0.0\" && echo \"$? - OK!\" || echo \"$? - FAIL!\" # expected OK\n\n# more specific cases\nsemver:constraints:simple \"1.0.0-beta.10 != 1.0.0-beta.2\" && echo \"OK!\" || echo \"$? - FAIL!\"\n\n# parse and recompose version code\nsemver:parse \"2.0.0-rc.1+build.123\" \"V\" \\\n  && for i in \"${!V[@]}\"; do echo \"$i: ${V[$i]}\"; done \\\n  && semver:recompose \"V\"\n\n# test version code\necho \"1\" | grep -E \"${SEMVER_LINE}\" --color=always --ignore-case || echo \"OK!\"\n```\n\n[API Reference](docs/public/lib/_semver.md)\n\n### Git Semantic/Conventional commits\n\nSet of git helpers are implemented.\n\n```bash\n# compute semantic version from git history\nbin/git.semantic-version.sh\n```\n\nSemantic Version History:\n\n| Commit  | Message                                                                      | Tag            | Version Change                | Diff           |\n| ------- | ---------------------------------------------------------------------------- | -------------- | ----------------------------- | -------------- |\n| cb10f67 | imported version-up.sh script                                                | -              | 0.0.1 → 0.0.1                 | +0.0.0         |\n| c75cdab | added several demos (#4)                                                     | -              | 0.0.1 → 0.0.1                 | +0.0.0         |\n| 32b1951 | Update README.md                                                             | v1.0.0         | 0.0.1 → 1.0.0                 | =1.0.0         |\n| 3e6d934 | small patch (#5)                                                             | v1.0.1-alpha.1 | 1.0.0 → 1.0.1-alpha.1         | =1.0.1-alpha.1 |\n| d08724e | Self update functionality (#6)                                               | -              | 1.0.1-alpha.1 → 1.0.1-alpha.1 | +0.0.0         |\n| dffc346 | fix: kcov docker image use (#9)                                              | -              | 1.0.1-alpha.1 → 1.0.2-alpha.1 | +0.0.1         |\n| b982126 | wip: log to file and stderr                                                  | -              | 1.0.2-alpha.1 → 1.0.2-alpha.1 | +0.0.0         |\n| 8649d55 | Document args (#10)                                                          | v1.1.0         | 1.0.2-alpha.1 → 1.1.0         | =1.1.0         |\n| 21ba265 | Update README.md                                                             | -              | 1.1.0 → 1.1.0                 | +0.0.0         |\n| b00a1d0 | fix: installation script global and local installation scenarios (#16)       | -              | 1.1.0 → 1.1.1                 | +0.0.1         |\n| 82a5c35 | wip: updated dependencies                                                    | -              | 1.1.1 → 1.1.1                 | +0.0.0         |\n| 60fcd80 | wip: code review of another PR (#18)                                         | -              | 1.1.1 → 1.1.1                 | +0.0.0         |\n| b0901ab | test: add comprehensive test coverage for version-up v2 (TDD approach) (#19) | -              | 1.1.1 → 1.1.2                 | +0.0.1         |\n| 13f8feb | feat: add git semantic version calculator script (#20)                       | -              | 1.1.2 → 1.2.0                 | +0.1.0         |\n| 2b4b34f | Fix/coverage unknown status (#21)                                            | -              | 1.2.0 → 1.2.0                 | +0.0.0         |\n| 7cc5870 | ci: Add CI cache for Homebrew installations (#25)                            | -              | 1.2.0 → 1.2.1                 | +0.0.1         |\n| fbdee5c | feat: add mise tool support to e-bash install script (#23)                   | -              | 1.2.1 → 1.3.0                 | +0.1.0         |\n| b64d60b | feat: add trap management module with multiple handler support               | -              | 1.3.0 → 1.4.0                 | +0.1.0         |\n| 1e2e98f | fix: resolve CI test failures for trap module                                | -              | 1.4.0 → 1.4.1                 | +0.0.1         |\n| e854d5d | fix: trap:push without arguments now correctly snapshots all signals         | -              | 1.4.1 → 1.4.2                 | +0.0.1         |\n| c031c50 | fix: resolve CI test failures for trap module                                | -              | 1.4.2 → 1.4.3                 | +0.0.1         |\n| cd3eb44 | fix: make logger mocks produce output for test assertions                    | -              | 1.4.3 → 1.4.4                 | +0.0.1         |\n| 6af95a2 | fix: redirect mock logger output to stderr matching e-bash convention        | -              | 1.4.4 → 1.4.5                 | +0.0.1         |\n| 63797bf | fix: resolve ShellSpec syntax errors in trap tests                           | -              | 1.4.5 → 1.4.6                 | +0.0.1         |\n| 244b15b | fix: redirect stderr to /dev/null for trap setup commands in tests           | -              | 1.4.6 → 1.4.7                 | +0.0.1         |\n\n```text\nSummary:\n  Total commits processed: 55\n  Version changes:\n    Major (breaking): 0\n    Minor (features): 3\n    Patch (fixes):    11\n    Tag   (assigned): 3\n    None  (ignored):  38\n\nFinal Version: 1.4.7\n```\n\n### Git Verify Commits Messages - Conventional Commits\n\n```bash\nbin/git.verify-all-commits.sh\n```\n\n```text\n❯ bin/git.verify-all-commits.sh\n 🔍 Gathering commit history...\n 🔍 Checking 56 commits for Conventional Commit compliance...\n\nProgress: 0.........10.........20.........30.........40.........50.....\n\n ❌ 34 commit(s) failed:\n\n 🔴 Commit: bf2da247, Author: Oleksandr, Date: 2025-11-08\n    Message: \"Fix and optimize Codecov configuration (#22)\"\n\n 🔴 Commit: 2b4b34f9, Author: Oleksandr, Date: 2025-11-08\n    Message: \"Fix/coverage unknown status (#21)\"\n\n  // ...TRIMMED...\n\n 🔴 Commit: cb10f677, Author: Oleksandr Kucherenko, Date: 2023-10-03\n    Message: \"imported version-up.sh script\"\n\n 💡 Conventional Commit format: type(scope): description\n    Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert\n    Use ! for breaking changes: feat!: breaking change\n    Reference: https://www.conventionalcommits.org/\n```\n\n### Git Logs\n\n```bash\n# show last 10 commits messages\nbin/git.log.sh 25\n```\n\n![Git Logs Preview](docs/images/public/git-logs-last-25-messages.jpg)\n\n### Git Files Changes\n\nDisplay all changed files from N last commits, in PLAIN or TREE view.\n\nScript also uses links integration into terminal, on click should be open vscode.\n\n```bash\n# show changed files in 1 last commit, and show it as a tree\nbin/git.files.sh 1 --tree\n```\n\n![Tree View](docs/images/public/git-changed-files-tree.jpg)\n\n### Self-Update\n\n**Purpose:** The self-update functionality allows any project that uses the e-bash scripts library to automatically detect source updates and update library files file-by-file. This is designed specifically for BASH scripts built on top of the e-bash library.\n\n**Main Usage Pattern:** The recommended approach is to invoke `self-update` when your script exits, ensuring the library stays current for the next execution:\n\n```bash\n# Using e-bash traps module (recommended)\nsource \".scripts/_self-update.sh\"\nsource \".scripts/_traps.sh\"\n\nfunction on_exit_update() {\n  self-update '^1.0.0'\n}\ntrap:on on_exit_update EXIT\n\n# Or using built-in trap (simpler, but less flexible)\ntrap \"self-update '^1.0.0'\" EXIT\n```\n\n**How It Works:**\n\n1. Maintains a local git repository at `~/.e-bash/` with multiple version worktrees\n2. Creates symbolic links from your project's `.scripts/` files to version-specific files\n3. Performs file-by-file updates with automatic backup creation\n4. Verifies updates using SHA1 hash comparison\n5. Supports rollback to previous versions or backup files\n\nRequirements:\n\n- [x] detect a new version of the script\n- [x] download multiple versions into folder and do a symbolic link to a specific version\n- [x] download from GIT repo (git clone)\n  - [x] keep MASTER as default, extract version tags as sub-folders\n- [ ] download from GIT repo release URL (tar/zip archive)\n  - [ ] extract archive to a version sub-folder\n- [x] rollback to previous version (or specified one)\n  - [x] rollback to latest backup file (if exists)\n- [x] partial update of the scripts, different versions of scripts from different version sub-folders\n  - [x] developer can bind file to a specific version by calling function `self-update:version:bind`\n- [x] verify SHA1 hash of the scripts\n  - [x] compute file SHA1 hash and store it in \\*.sha1 file\n- [x] understand version expressions\n  - [x] `latest` - latest stable version (no pre-release tags)\n  - [x] `*` or `next` - any highest version tag (INCLUDING: alpha, beta, rc etc)\n  - [x] `branch:{any_branch}` - update from any branch name\n  - [x] `tag:{any_tag}` - update to specific tag\n  - [x] `>`, `<`, `>=`, `<=`, `~`, `!=`, `||` - comparison syntax\n  - [x] `1.0.0` or `=1.0.0` - exact version\n  - [x] `~1.0.0` - version in range >= 1.0.x, patch releases allowed\n  - [x] `^1.0.0` - version in range >= 1.x.x, minor & patch releases allowed\n  - [x] `>1.0.0 <=1.5.0` - version in range `> 1.0.0 && <= 1.5.0`\n  - [x] `>1.0.0 <1.1.0 || >1.5.0` - version in range `(> 1.0.0 < 1.1.0) || (> 1.5.0)`\n\nrefs:\n\n- [yarn dependency version](https://classic.yarnpkg.com/lang/en/docs/dependency-versions/)\n- [semver tools](https://github.com/fsaintjacques/semver-tool)\n- [semver](https://github.com/Masterminds/semver)\n- [concurrency in bash](https://stackoverflow.com/questions/356100/how-to-wait-in-bash-for-several-subprocesses-to-finish-and-return-exit-code-0)\n\n\n```bash\nsource \".scripts/_self-update.sh\"\n\n# check for version update in range >= 1.0.x, stable versions\n# try to update itself from https://github.com/OleksandrKucherenko/e-bash.git repository\nself-update \"~1.0.0\"                          # patch releases allowed\nself-update \"^1.0.0\"                          # minor releases allowed\nself-update \"> 1.0.0 <= 1.5.0\"                # stay in range\n\n# update specific file to latest version tag\nself-update \"latest\" \".scripts/_colors.sh\"    # latest stable\nself-update \"*\" \".scripts/_colors.sh\"         # any highest version tag\n\n# update specific file to MASTER version (can be used any branch name)\nself-update \"branch:master\" \".scripts/_colors.sh\"\nself-update \"tag:v1.0.0\" \".scripts/_colors.sh\"\n\n# bind file to a specific version\nself-update:version:bind \"v1.0.0\" \".scripts/_colors.sh\"\n\n# TBD\n\n# INTEGRATION EXAMPLE\n\n# do self-update on script exit\ntrap \"self-update '^1.0.0'\" EXIT\n\n# OR:\nfunction __exit() {\n  # TODO: add more cleanup logic here\n  self-update '^1.0.0'\n}\ntrap \"__exit\" EXIT\n```\n\n[API Reference](docs/public/lib/_self-update.md)\n\n### Documentation Generator: e-docs\n\nAutomated documentation generator that extracts documentation from `##` comments in your bash scripts and generates GitHub-flavored Markdown.\n\n```bash\n# Generate docs for all scripts in .scripts/\nbin/e-docs.sh\n\n# Generate docs for a specific file\nbin/e-docs.sh .scripts/_logger.sh\n```\n\n[User Documentation](docs/public/e-docs.md)\n\n### Troubleshooting\n\n```bash\n# rollback with use of backup file(s)\nsource \".scripts/_self-update.sh\" && self-update:rollback:backup \"${full_path_to_file}\"\n\n# rollback to specific version\nsource \".scripts/_self-update.sh\" && self-update:rollback:version \"v1.0.0\" \"${full_path_to_file}\"\n```\n\n### Profile BASH script execution\n\n![Profiler](docs/images/public/profiler.version-up.gif)\n\n```bash\n# print timestamp for each line of executed script\nPS4='+ $(gdate \"+%s.%N ($LINENO) \")' bash -x bin/version-up.v2.sh\n\n# save trace to file\nPS4='+ $(echo -n \"$EPOCHREALTIME [$LINENO]: \")' bash -x bin/version-up.v2.sh 2>trace.log\n\n# process output to more user-friendly format: `execution_time | line_number | line_content`\nPS4='+ $(echo -n \"$EPOCHREALTIME [$LINENO]: \")' bash -x bin/version-up.v2.sh 2>trace.log 1>/dev/null && cat trace.log | bin/profiler/tracing.sh\n\n# profile script execution and print summary\nbin/profiler/profile.sh bin/version-up.v2.sh\n```\n\n- ref1: https://itecnote.com/tecnote/r-performance-profiling-tools-for-shell-scripts/\n- ref2: https://www.thegeekstuff.com/2008/09/bash-shell-take-control-of-ps1-ps2-ps3-ps4-and-prompt_command/\n\n### Colors support in my terminal\n\n![Terminal Colors](docs/images/public/terminal.colors.gif)\n\n```bash\n# print all colors for easier selection\ndemos/demo.colors.sh\n```\n\n### Emoji support in my terminal\n\nRun this command if you want to see how your terminal setup support emojis. Usually Windows WSL2 teminals has very limited support enabled by default, so you should be carefull when selecting emojis for your scripts.\n\n```bash\ndemos/demo.emojis.sh\n```\n\n![Emoji Support In Terminal](./docs/images/public/demo-emoji-support.gif)\n\n### References\n\n- PV - https://manpages.ubuntu.com/manpages/focal/man1/pv.1.html\n- https://catern.com/posts/pipes.html\n- https://stackoverflow.com/questions/238073/how-to-add-a-progress-bar-to-a-shell-script\n- [bash-core](https://github.com/bash-bastion/bash-core/blob/main/pkg/src/util/util.sh#L17-L38), trap enhancement\n- [bash-bastion](https://github.com/bash-bastion) BASH helpers\n- https://github.com/dylanaraps/writing-a-tui-in-bash\n- [bash-toml](https://github.com/bash-bastion/bash-toml) TOML Support (also INI files support!)\n- [Pure Bash Bible](https://github.com/dylanaraps/pure-bash-bible)\n\n## Contributing\n\nConventions and folder structure: [docs/public/conventions.md](docs/public/conventions.md)\n\n### Roadmap\n\n- [x] High-level scripts should be in their own `bin` OR `demos`\n- [x] Git helpers\n- [ ] GitLab's helper scripts (work with branches, forks, submodules)\n- [ ] Globals module (declarative way of defining script dependencies to global environment variables)\n- [x] Logs monitoring documentation (different streams/files/tty for different information: info, debug, telemetry, dependencies)\n- [x] Copyright headers composing/parsing (extract from the file, update, insert)\n\n### Local Dev Environment - Requirements\n\n- [DirEnv](https://github.com/direnv/direnv)\n- [ShellFormat](https://github.com/mvdan/sh)\n- [ShellCheck](https://github.com/koalaman/shellcheck)\n- [KCov](https://github.com/SimonKagstrom/kcov)\n- [ShellSpec](https://github.com/shellspec/shellspec)\n\n> Note: alternative Unit Test Frameworks, Bats - [BATS core](https://github.com/bats-core/bats-core)\n\n```bash\nbrew install direnv\nbrew install shellcheck\nbrew install shfmt\nbrew install shellspec\nbrew install kcov\n```\n\n### TDD - Test Driven Development, run tests on file change\n\n```bash\n# make tool required Python in hidden dependencies\n# ref1: https://docs.astral.sh/uv/guides/install-python/\n# ref2: https://github.com/astral-sh/uv\nuv python install \n# alternative: pyenv install 3.13.2 && pyenv global 3.13.2\n\n# run all unit tests on file change\nwatchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run \"shellspec\"\n\n# run failed only unit tests on file change\nwatchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run \"shellspec --quick\"\n\n# run failed only unit tests on file change without coverage\nwatchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run \"shellspec --quick --no-kcov --\"\n\n# Multiple Jobs (parallel execution)\n# shellspec -j 8  \n# 40.34s user 10.62s system 38% cpu 2:12.97 total\n# shellspec -j 4  \n# 40.35s user 10.28s system 40% cpu 2:03.77 total\n```\n\n## License\n\n[MIT](./LICENSE)\n",
  "bytes": 36081,
  "sha": "d5d74c0974f484c17cbcf86dbe50b73792e8f465a729061d0d4434e491ee6895",
  "repo_slug": "oleksandrkucherenko/e-bash",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/skl_oleksandrkucherenko_e_bash_testing_peste_dafd8245/readme"
}