Skip to content

OleksandrKucherenko/e-bash

Repository files navigation

Ask DeepWiki zread codecov Run in Smithery

Enhanced BASH Scripts

Usage

Installation into your project with helper script:

install/upgrade to the latest version

# install into current folder/workspace
curl -sSL https://git.new/e-bash | bash -s --

# OR: install into global folder (~/.e-bash)
curl -sSL https://git.new/e-bash | bash -s -- --global install

Alternatives:

# OR: install latest version
wget -qO- https://git.new/e-bash | bash -s -- install
# OR: install latest version (httpie)
http -b https://git.new/e-bash | bash -s -- install

Homebrew (macOS/Linux)

brew install artfulbits-se/tap/e-bash

Direct Download (Manual User)

# install specific version
curl -sSL https://git.new/e-bash | bash -s -- install v1.0.0
# OR: install specific version
wget -qO- https://git.new/e-bash | bash -s -- install v1.0.0
# OR: install specific version (httpie)
http -b https://git.new/e-bash | bash -s -- install v1.0.0

More details

Using e-bash in Your Scripts

To use e-bash utilities in your bash scripts, add this bootstrap snippet at the top:

Version A: Documented/Readable (with error handling)

# -----------------------------------------------------------------------------
# e-bash bootstrap: auto-establish E_BASH and PATH
# -----------------------------------------------------------------------------
[ -z "$E_BASH" ] && readonly E_BASH="$(
  # 1. Use existing E_BASH if set
  # 2. Find .scripts relative to this script
  # 3. Fallback to ~/.e-bash/.scripts
  if [ -n "${E_BASH+x}" ]; then
    echo "$E_BASH"
  elif [ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../.scripts/_colors.sh" ]; then
    echo "$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.scripts && pwd)"
  elif [ -d "$HOME/.e-bash/.scripts" ]; then
    echo "$HOME/.e-bash/.scripts"
  else
    echo "" >&2
    echo "Error: Cannot find e-bash library. Please install e-bash first." >&2
    echo "  Visit: https://github.com/OleksandrKucherenko/e-bash" >&2
    echo "" >&2
    exit 1
  fi
)"

# Add gnubin to PATH (Linux/macOS compatibility)
if [[ -d "$E_BASH/../bin/gnubin" ]]; then
  PATH="$E_BASH/../bin/gnubin:$PATH"
  # Source _gnu.sh to create symlinks if needed
  [[ -f "$E_BASH/_gnu.sh" ]] && source "$E_BASH/_gnu.sh"
fi

# Now source required modules
source "$E_BASH/_colors.sh"
source "$E_BASH/_logger.sh"
# ... add more modules as needed
# -----------------------------------------------------------------------------

Version B: Compact 2-LOC (248 characters, good balance)

# 2-LOC bootstrap: E_BASH discovery + gnubin PATH
[ -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"
# Source modules (examples)
source "$E_BASH/_colors.sh"; source "$E_BASH/_logger.sh"

Version C: Ultra-Optimized (used in e-bash scripts)

# Bootstrap: 1) E_BASH discovery (only if not set), 2) gnubin setup (always)
[ "$E_BASH" ] || { _src=${BASH_SOURCE:-$0}; E_BASH=$(cd "${_src%/*}/../.scripts" 2>&- && pwd || echo ~/.e-bash/.scripts); readonly E_BASH; }
. "$E_BASH/_gnu.sh"; PATH="$(cd "$E_BASH/../bin/gnubin" 2>&- && pwd):$PATH"
# Source modules (examples)
source "$E_BASH/_colors.sh"; source "$E_BASH/_logger.sh"

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.

Then source the modules you need:

See .scripts/ directory for all available modules.

Manual installation

git remote add -f e-bash https://github.com/OleksandrKucherenko/e-bash.git  
git checkout -b e-bash-temp e-bash/master  
git subtree split -P .scripts -b e-bash-scripts  
git checkout master # or main - depends on your main branch in repo
git subtree merge --prefix .scripts e-bash-scripts --squash

Upgrade .scripts to the latest version:

git fetch e-bash master  
git checkout e-bash-temp && git reset --hard e-bash/master  
git subtree split -P .scripts -b e-bash-scripts  
git checkout <your-main-branch>  
git subtree pull --prefix .scripts e-bash-scripts --squash

refs:

Library Overview

Colors

source ".scripts/_colors.sh"

echo -e "${cl_red}Hello World${cl_reset}"

API Reference

Script Dependencies

Bootstrap

source ".scripts/_dependencies.sh"

dependency bash "5.*.*" "brew install bash"
dependency direnv "2.*.*" "curl -sfL https://direnv.net/install.sh | bash"
dependency shellspec "0.28.*" "brew install shellspec"
optional kcov "42" "brew install kcov"
dependency shellcheck "0.9.*" "curl -sS https://webi.sh/shellcheck | sh"
dependency shfmt "3.*.*" "curl -sS https://webi.sh/shfmt | sh"
dependency watchman "2023.07.*.*" "brew install watchman"

# different return codes for success and failure
dependency watchman "2023.07.*.*" "brew install watchman" && echo "OK!" || echo "FAIL!"

# optional always return success
optional watchman "2023.07.*.*" "brew install watchman" && echo "OK!" || echo "never happens!"

# Allow of HEAD or stable versions of the watchman tool
wHead=$(dependency watchman "HEAD-[a-f0-9]{1,8}" "brew install watchman")
wStab=$(dependency watchman "2024.*.*.*" "brew install watchman")
echo "$wHead" | grep 'Error' &>/dev/null && echo "$wStab" || echo "$wHead"

API Reference

Logger

Quick Start Guide | API Reference

Requirements:

  • zero dependencies, pure BASH (optional: _colors.sh)
  • prefix for all logger messages
  • work in pipe mode (forward logs to the named pipe)
    • write logs to pipe; single line or multiple lines in '|' pipe mode
    • read logs from the named pipe and output to the console (or file).
    • redirect logs to file/stream/pipe/tty
  • support prefix for each log message
  • listen to DEBUG environment variable for enabling/disabling logs
    • enable/disable log by tag name or tag name prefix (support wildcards)
  • execute command with logging the command and it parameters first, ref: echo-eval - use DRYRUN functionality for that
source ".scripts/_logger.sh"
logger common "$@" # declare echo:Common and printf:Common functions, tag: common
logger debug "$@" # declare echo:Debug and printf:Debug functions, tag: debug

echo:Common "Hello World" # output "Hello World" only if tag common is enabled

export DEBUG=*          # enable logger output for all tags
export DEBUG=common     # enable logger output for common tag only
export DEBUG=*,-common  # enable logger output for all tags except common

# advanced functions
config:logger:Common "$@" # re-configure logger enable/disable for common tag

# echo in pipe mode
find . -type d -max-depth 1 | log:Common

# echo in output redirect
find . -type d -max-depth 1 >log:Common

# more samples of usage are in `demos/demo.logs.sh` file

Complete demo: Logger Demo

Arguments Parsing

Requirements:

  • zero dependencies, pure BASH
  • support short and long arguments
  • support default values
  • support required arguments
  • support aliases for arguments
  • support destination variables for argument
  • compose help documentation from arguments definition
# pattern: "{argument_index},-{short},--{alias}={output_variable}:{default_initialize_value}:{reserved_args_quantity}"
# example: "-h,--help=args_help:true:0", on --help or -h set $args_help variable to true, expect no arguments;
# example: "$1,--id=args_id::1", expect first unnamed argument to be assigned to $args_id variable; can be also provided as --id=123
export ARGS_DEFINITION="-h,--help -v,--version=:1.0.0"
export ARGS_DEFINITION+=" --debug=DEBUG:*"

# will automatically parse script arguments with definition from $ARGS_DEFINITION global variable
source "$E_BASH/_arguments.sh"

# check variables that are extracted
echo "Is --help: $help"
echo "Is --version: $version"
echo "Is --debug: $DEBUG"

# advanced run. parse provided arguments with definition from $ARGS_DEFINITION global variable
parse:arguments "$@"

More details: Arguments Parsing, Demo script, API Reference.

Common(s) Functions And Inputs

Complete Documentation | API Reference

source ".scripts/_commons.sh"

# Find git repository root (handles regular repos, worktrees, submodules)
repo_root=$(git:root)
repo_type=$(git:root "." "type")  # regular, worktree, or submodule

# Find configuration file hierarchy (similar to c12/cosmiconfig)
configs=$(config:hierarchy ".eslintrc" "." "git" ",.js,.json,.yaml")
# Returns files in order: root → current (for proper config merging)

# XDG-compliant config discovery
configs=$(config:hierarchy:xdg "nvim" "init.vim" "." "home")
# Searches: project configs → ~/.config/nvim → /etc/xdg/nvim → /etc/nvim

# Extract parameter from global env variable OR from secret file (file content)
env:variable:or:secret:file "new_value" \
  "GITLAB_CI_INTEGRATION_TEST" \
  ".secrets/gitlab_ci_integration_test" \
  "{user friendly message}"

echo "Extracted: ${new_value}"

UI: Selector

Selector

source ".scripts/_commons.sh"

# Select value from short list of choices
declare -A -g connections && connections=(["d"]="production" ["s"]="cors-proxy:staging" ["p"]="cors-proxy:local")
echo -n "Select connection type: " && tput civis # hide cursor
selected=$(input:selector "connections") && echo "${cl_blue}${selected}${cl_reset}"

API Reference

UI: Ask for Password

Ask for Password

source ".scripts/_commons.sh"

# Usage:
echo -n "Enter password: "
password=$(input:readpwd) && echo "" && echo "Password: $password"

API Reference

Dry-Run Wrapper System

Safe command execution with three-mode operation: normal, dry-run (preview), and undo/rollback. Provides automatic logging, exit status tracking, and flexible per-command configuration.

source "$E_BASH/_dryrun.sh"

# Create wrappers for commands
dry-run git docker kubectl

# Normal, safe operation, does not mutate anything
run:git status

# Normal mode - execute commands, if no DRY_RUN set
dry:git pull origin main
dry:docker build -t app .

# Normal mode - Register rollback command, executed only when UNDO_RUN is set
rollback:kubectl delete deployment app

# Dry-run mode - preview operation in terminal without executing it
DRY_RUN=true dry:git pull origin main

# Undo mode - execute rollbacks, only when UNDO_RUN=true
UNDO_RUN=true rollback:git reset --hard

function rollback_fn() {
  echo "Cleaning up..."
  echo "Possible execution of multiple commands..."
}

# Rollback via special function
rollback:func rollback_fn

Three Execution Modes:

Mode DRY_RUN UNDO_RUN Normal Commands Rollback Commands
Normal false false Execute Dry-run (safe)
Dry-run true false Dry-run Dry-run
Undo false true Dry-run Execute

Features:

  • ✅ Color-coded logging with exit status (execute:, dry run:, undoing:)
  • ✅ Command-specific overrides (DRY_RUN_GIT=false, pattern: DRY_RUN_*)
  • ✅ Silent mode support (SILENT_GIT=true, pattern: SILENT_*)
  • ✅ Function-based rollbacks (rollback:func cleanup_fn)
  • ✅ Variable precedence: command-specific → global → default

More details: Dry-Run Wrapper System, Demo script, API Reference.

Hooks

Add extension points to any bash script with minimal changes. Hook implementations live in external files - your script just declares and triggers them.

Before (your existing script):

#!/bin/bash
echo "Starting deployment..."
deploy_application
echo "Done."

After (several lines added):

#!/bin/bash
+ export HOOKS_DIR=".hooks"  # Default is 'ci-cd'
+ source "$E_BASH/_hooks.sh"
+ hooks:declare begin deploy end

+ hooks:do begin
echo "Starting deployment..."
deploy_application
+ hooks:do deploy "$VERSION"
echo "Done."
+ hooks:do end

Now add any functionality via external scripts in .hooks/ folder following pattern in naming {hook_name}-{purpose}.sh:

hook_name is one of the declared hooks: begin, deploy, end; purpose - is your user-friendly name for the hook.

Hooks are executed in alphabetical order, and you can declare multiple hooks for the same hook point/name.

.hooks/begin-otel-trace.sh - OpenTelemetry tracing:

#!/bin/bash
export TRACE_ID=$(openssl rand -hex 16)
curl -s "${OTEL_ENDPOINT}/v1/traces" -d "{...span data...}" &>/dev/null

.hooks/deploy-slack-notify.sh - Slack notifications:

#!/bin/bash
curl -s "$SLACK_WEBHOOK" -d "{\"text\":\"Deploying $1\"}"

.hooks/end-metrics.sh - Metrics export:

#!/bin/bash
echo "deployment.duration=$SECONDS" | nc -u metrics.local 8125

Custom hooks directory per script:

HOOKS_DIR=".hooks/$(basename "$0" .sh)"  # .hooks/my-script/
source "$E_BASH/_hooks.sh"

or maybe Hooks Documentation or API Reference;

Demos: Intro, Multiple Hooks, Nested Hooks, Hooks with Logs, CI Hooks Demo, CI Hooks with Middlewar

Semver - Semantic Versioning

Requirements:

  • parse version code, according to semver specification
  • compare version code
  • verify version constraints
  • compose version code from array of segments
source ".scripts/_semver.sh"

# verify that version is passing the constraints expression
semver:constraints "1.0.0-alpha" ">1.0.0-beta || <1.0.0" && echo "$? - OK!" || echo "$? - FAIL!" # expected OK

# more specific cases
semver:constraints:simple "1.0.0-beta.10 != 1.0.0-beta.2" && echo "OK!" || echo "$? - FAIL!"

# parse and recompose version code
semver:parse "2.0.0-rc.1+build.123" "V" \
  && for i in "${!V[@]}"; do echo "$i: ${V[$i]}"; done \
  && semver:recompose "V"

# test version code
echo "1" | grep -E "${SEMVER_LINE}" --color=always --ignore-case || echo "OK!"

API Reference

Git Semantic/Conventional commits

Set of git helpers are implemented.

# compute semantic version from git history
bin/git.semantic-version.sh

Semantic Version History:

Commit Message Tag Version Change Diff
cb10f67 imported version-up.sh script - 0.0.1 → 0.0.1 +0.0.0
c75cdab added several demos (#4) - 0.0.1 → 0.0.1 +0.0.0
32b1951 Update README.md v1.0.0 0.0.1 → 1.0.0 =1.0.0
3e6d934 small patch (#5) v1.0.1-alpha.1 1.0.0 → 1.0.1-alpha.1 =1.0.1-alpha.1
d08724e Self update functionality (#6) - 1.0.1-alpha.1 → 1.0.1-alpha.1 +0.0.0
dffc346 fix: kcov docker image use (#9) - 1.0.1-alpha.1 → 1.0.2-alpha.1 +0.0.1
b982126 wip: log to file and stderr - 1.0.2-alpha.1 → 1.0.2-alpha.1 +0.0.0
8649d55 Document args (#10) v1.1.0 1.0.2-alpha.1 → 1.1.0 =1.1.0
21ba265 Update README.md - 1.1.0 → 1.1.0 +0.0.0
b00a1d0 fix: installation script global and local installation scenarios (#16) - 1.1.0 → 1.1.1 +0.0.1
82a5c35 wip: updated dependencies - 1.1.1 → 1.1.1 +0.0.0
60fcd80 wip: code review of another PR (#18) - 1.1.1 → 1.1.1 +0.0.0
b0901ab test: add comprehensive test coverage for version-up v2 (TDD approach) (#19) - 1.1.1 → 1.1.2 +0.0.1
13f8feb feat: add git semantic version calculator script (#20) - 1.1.2 → 1.2.0 +0.1.0
2b4b34f Fix/coverage unknown status (#21) - 1.2.0 → 1.2.0 +0.0.0
7cc5870 ci: Add CI cache for Homebrew installations (#25) - 1.2.0 → 1.2.1 +0.0.1
fbdee5c feat: add mise tool support to e-bash install script (#23) - 1.2.1 → 1.3.0 +0.1.0
b64d60b feat: add trap management module with multiple handler support - 1.3.0 → 1.4.0 +0.1.0
1e2e98f fix: resolve CI test failures for trap module - 1.4.0 → 1.4.1 +0.0.1
e854d5d fix: trap:push without arguments now correctly snapshots all signals - 1.4.1 → 1.4.2 +0.0.1
c031c50 fix: resolve CI test failures for trap module - 1.4.2 → 1.4.3 +0.0.1
cd3eb44 fix: make logger mocks produce output for test assertions - 1.4.3 → 1.4.4 +0.0.1
6af95a2 fix: redirect mock logger output to stderr matching e-bash convention - 1.4.4 → 1.4.5 +0.0.1
63797bf fix: resolve ShellSpec syntax errors in trap tests - 1.4.5 → 1.4.6 +0.0.1
244b15b fix: redirect stderr to /dev/null for trap setup commands in tests - 1.4.6 → 1.4.7 +0.0.1
Summary:
  Total commits processed: 55
  Version changes:
    Major (breaking): 0
    Minor (features): 3
    Patch (fixes):    11
    Tag   (assigned): 3
    None  (ignored):  38

Final Version: 1.4.7

Git Verify Commits Messages - Conventional Commits

bin/git.verify-all-commits.sh
❯ bin/git.verify-all-commits.sh
 🔍 Gathering commit history...
 🔍 Checking 56 commits for Conventional Commit compliance...

Progress: 0.........10.........20.........30.........40.........50.....

 ❌ 34 commit(s) failed:

 🔴 Commit: bf2da247, Author: Oleksandr, Date: 2025-11-08
    Message: "Fix and optimize Codecov configuration (#22)"

 🔴 Commit: 2b4b34f9, Author: Oleksandr, Date: 2025-11-08
    Message: "Fix/coverage unknown status (#21)"

  // ...TRIMMED...

 🔴 Commit: cb10f677, Author: Oleksandr Kucherenko, Date: 2023-10-03
    Message: "imported version-up.sh script"

 💡 Conventional Commit format: type(scope): description
    Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
    Use ! for breaking changes: feat!: breaking change
    Reference: https://www.conventionalcommits.org/

Git Logs

# show last 10 commits messages
bin/git.log.sh 25

Git Logs Preview

Git Files Changes

Display all changed files from N last commits, in PLAIN or TREE view.

Script also uses links integration into terminal, on click should be open vscode.

# show changed files in 1 last commit, and show it as a tree
bin/git.files.sh 1 --tree

Tree View

Self-Update

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.

Main Usage Pattern: The recommended approach is to invoke self-update when your script exits, ensuring the library stays current for the next execution:

# Using e-bash traps module (recommended)
source ".scripts/_self-update.sh"
source ".scripts/_traps.sh"

function on_exit_update() {
  self-update '^1.0.0'
}
trap:on on_exit_update EXIT

# Or using built-in trap (simpler, but less flexible)
trap "self-update '^1.0.0'" EXIT

How It Works:

  1. Maintains a local git repository at ~/.e-bash/ with multiple version worktrees
  2. Creates symbolic links from your project's .scripts/ files to version-specific files
  3. Performs file-by-file updates with automatic backup creation
  4. Verifies updates using SHA1 hash comparison
  5. Supports rollback to previous versions or backup files

Requirements:

  • detect a new version of the script
  • download multiple versions into folder and do a symbolic link to a specific version
  • download from GIT repo (git clone)
    • keep MASTER as default, extract version tags as sub-folders
  • download from GIT repo release URL (tar/zip archive)
    • extract archive to a version sub-folder
  • rollback to previous version (or specified one)
    • rollback to latest backup file (if exists)
  • partial update of the scripts, different versions of scripts from different version sub-folders
    • developer can bind file to a specific version by calling function self-update:version:bind
  • verify SHA1 hash of the scripts
    • compute file SHA1 hash and store it in *.sha1 file
  • understand version expressions
    • latest - latest stable version (no pre-release tags)
    • * or next - any highest version tag (INCLUDING: alpha, beta, rc etc)
    • branch:{any_branch} - update from any branch name
    • tag:{any_tag} - update to specific tag
    • >, <, >=, <=, ~, !=, || - comparison syntax
    • 1.0.0 or =1.0.0 - exact version
    • ~1.0.0 - version in range >= 1.0.x, patch releases allowed
    • ^1.0.0 - version in range >= 1.x.x, minor & patch releases allowed
    • >1.0.0 <=1.5.0 - version in range > 1.0.0 && <= 1.5.0
    • >1.0.0 <1.1.0 || >1.5.0 - version in range (> 1.0.0 < 1.1.0) || (> 1.5.0)

refs:

source ".scripts/_self-update.sh"

# check for version update in range >= 1.0.x, stable versions
# try to update itself from https://github.com/OleksandrKucherenko/e-bash.git repository
self-update "~1.0.0"                          # patch releases allowed
self-update "^1.0.0"                          # minor releases allowed
self-update "> 1.0.0 <= 1.5.0"                # stay in range

# update specific file to latest version tag
self-update "latest" ".scripts/_colors.sh"    # latest stable
self-update "*" ".scripts/_colors.sh"         # any highest version tag

# update specific file to MASTER version (can be used any branch name)
self-update "branch:master" ".scripts/_colors.sh"
self-update "tag:v1.0.0" ".scripts/_colors.sh"

# bind file to a specific version
self-update:version:bind "v1.0.0" ".scripts/_colors.sh"

# TBD

# INTEGRATION EXAMPLE

# do self-update on script exit
trap "self-update '^1.0.0'" EXIT

# OR:
function __exit() {
  # TODO: add more cleanup logic here
  self-update '^1.0.0'
}
trap "__exit" EXIT

API Reference

Documentation Generator: e-docs

Automated documentation generator that extracts documentation from ## comments in your bash scripts and generates GitHub-flavored Markdown.

# Generate docs for all scripts in .scripts/
bin/e-docs.sh

# Generate docs for a specific file
bin/e-docs.sh .scripts/_logger.sh

User Documentation

Troubleshooting

# rollback with use of backup file(s)
source ".scripts/_self-update.sh" && self-update:rollback:backup "${full_path_to_file}"

# rollback to specific version
source ".scripts/_self-update.sh" && self-update:rollback:version "v1.0.0" "${full_path_to_file}"

Profile BASH script execution

Profiler

# print timestamp for each line of executed script
PS4='+ $(gdate "+%s.%N ($LINENO) ")' bash -x bin/version-up.v2.sh

# save trace to file
PS4='+ $(echo -n "$EPOCHREALTIME [$LINENO]: ")' bash -x bin/version-up.v2.sh 2>trace.log

# process output to more user-friendly format: `execution_time | line_number | line_content`
PS4='+ $(echo -n "$EPOCHREALTIME [$LINENO]: ")' bash -x bin/version-up.v2.sh 2>trace.log 1>/dev/null && cat trace.log | bin/profiler/tracing.sh

# profile script execution and print summary
bin/profiler/profile.sh bin/version-up.v2.sh

Colors support in my terminal

Terminal Colors

# print all colors for easier selection
demos/demo.colors.sh

Emoji support in my terminal

Run 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.

demos/demo.emojis.sh

Emoji Support In Terminal

References

Contributing

Conventions and folder structure: docs/public/conventions.md

Roadmap

  • High-level scripts should be in their own bin OR demos
  • Git helpers
  • GitLab's helper scripts (work with branches, forks, submodules)
  • Globals module (declarative way of defining script dependencies to global environment variables)
  • Logs monitoring documentation (different streams/files/tty for different information: info, debug, telemetry, dependencies)
  • Copyright headers composing/parsing (extract from the file, update, insert)

Local Dev Environment - Requirements

Note: alternative Unit Test Frameworks, Bats - BATS core

brew install direnv
brew install shellcheck
brew install shfmt
brew install shellspec
brew install kcov

TDD - Test Driven Development, run tests on file change

# make tool required Python in hidden dependencies
# ref1: https://docs.astral.sh/uv/guides/install-python/
# ref2: https://github.com/astral-sh/uv
uv python install 
# alternative: pyenv install 3.13.2 && pyenv global 3.13.2

# run all unit tests on file change
watchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run "shellspec"

# run failed only unit tests on file change
watchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run "shellspec --quick"

# run failed only unit tests on file change without coverage
watchman-make -p 'spec/*_spec.sh' '.scripts/*.sh' --run "shellspec --quick --no-kcov --"

# Multiple Jobs (parallel execution)
# shellspec -j 8  
# 40.34s user 10.62s system 38% cpu 2:12.97 total
# shellspec -j 4  
# 40.35s user 10.28s system 40% cpu 2:03.77 total

License

MIT