From cb53800a200ff49e7a9ab99c49d9fc3ba4338e68 Mon Sep 17 00:00:00 2001 From: Manuele Sarfatti Date: Sun, 11 May 2025 13:37:25 +0200 Subject: [PATCH] Properly source modules --- Makefile | 30 +++- build/beddu.sh | 320 -------------------------------------- demo/demo.sh | 2 + src/00.utils/_symbols.sh | 6 +- src/00.utils/movements.sh | 6 +- src/01.core/pen.sh | 7 +- src/01.core/run.sh | 6 +- src/02.ui/check.sh | 16 +- src/02.ui/repen.sh | 13 +- src/02.ui/spin.sh | 29 ++-- src/02.ui/throw.sh | 16 +- src/02.ui/warn.sh | 16 +- src/03.prompt/ask.sh | 16 +- src/03.prompt/choose.sh | 12 +- src/03.prompt/confirm.sh | 16 +- 15 files changed, 114 insertions(+), 397 deletions(-) delete mode 100755 build/beddu.sh diff --git a/Makefile b/Makefile index 65a5126..4933bc2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Beddu build Makefile -OUT_DIR = build +OUT_DIR = dist OUTPUT = $(OUT_DIR)/beddu.sh SRC_DIR = src DEMO_DIR = demo @@ -14,7 +14,7 @@ get_dir_files = $(wildcard $(1)*.sh) # Build ALL_SRC_FILES by including files from each subdirectory in order ALL_SRC_FILES = $(foreach dir,$(SUBDIRS),$(call get_dir_files,$(dir))) -.PHONY: all clean demo build +.PHONY: all clean demo build release all: $(OUTPUT) @@ -25,6 +25,26 @@ build: demo: build @./$(DEMO_DIR)/demo.sh +release: + @if [ -z "$(filter-out $@,$(MAKECMDGOALS))" ]; then \ + echo "Error: Please specify a version number (e.g. make release v0.0.5)"; \ + exit 1; \ + fi + @VERSION="$(filter-out $@,$(MAKECMDGOALS))"; \ + if ! git diff-index --quiet HEAD --; then \ + echo "Error: Git working directory is not clean. Please commit or stash your changes first."; \ + exit 1; \ + fi; \ + $(MAKE) build; \ + sed -i '' "s/# Version: .*/# Version: $$VERSION/" $(OUTPUT); \ + git add $(OUTPUT); \ + git commit -m "Release $$VERSION"; \ + git tag -a "$$VERSION" -m "Release $$VERSION" + @echo "\nRelease complete: \033[32m$$VERSION\033[0m" + +%: + @: + $(OUTPUT): $(ALL_SRC_FILES) @mkdir -p $(OUT_DIR) @echo '#!/usr/bin/env bash' > $(OUTPUT) @@ -33,12 +53,12 @@ $(OUTPUT): $(ALL_SRC_FILES) @echo '# beddu.sh - A lightweight bash framework for interactive scripts and pretty output' >> $(OUTPUT) @echo '# https://github.com/mjsarfatti/beddu' >> $(OUTPUT) @echo '#' >> $(OUTPUT) - @echo '# Version: $(shell git describe --tags)' >> $(OUTPUT) + @echo '# Version: $(shell git describe --tags --dirty)' >> $(OUTPUT) @echo '# Generated on: $(shell date)' >> $(OUTPUT) - @# Process each file, stripping comments and empty lines + @# Process each file, stripping comments, empty lines, and source lines @for file in $(ALL_SRC_FILES); do \ echo "" >> $(OUTPUT); \ - grep -v '^\s*#' "$$file" | sed '/^[[:space:]]*$$/d' | sed 's/#[a-zA-Z0-9 ]*$$//' >> $(OUTPUT); \ + grep -v '^\s*#\|^source \|^SCRIPT_DIR=\|^readonly BEDDU_.*_LOADED\|^\[\[ \$$BEDDU_.*_LOADED \]\]' "$$file" | sed '/^[[:space:]]*$$/d' | sed 's/#[a-zA-Z0-9 ]*$$//' >> $(OUTPUT); \ done @chmod +x $(OUTPUT) @echo "\nBuild complete: \033[32m$(OUTPUT)\033[0m" diff --git a/build/beddu.sh b/build/beddu.sh deleted file mode 100755 index 8b53b36..0000000 --- a/build/beddu.sh +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=all -# -# beddu.sh - A lightweight bash framework for interactive scripts and pretty output -# https://github.com/mjsarfatti/beddu -# -# Version: v0.0.4 -# Generated on: Sun May 11 01:44:23 CEST 2025 - -readonly _q='?' -readonly _a='❯' -readonly _o='◌' -readonly _O='●' -readonly _mark='✓' -readonly _warn='!' -readonly _cross='✗' -readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt -export _q _a _o _O _mark _warn _cross _spinner - -up() { - printf "\033[A" -} -down() { - printf "\033[B" -} -bol() { - printf "\r" -} -eol() { - printf "\033[999C" -} -cl() { - printf "\033[2K" -} -upclear() { - up; bol; cl -} -line() { - printf "\n" -} -show_cursor() { - printf "\033[?25h" -} -hide_cursor() { - printf "\033[?25l" -} -export -f up down bol eol cl line show_cursor hide_cursor - -pen() { - local new_line="\n" - local text="${*: -1}" - local args=("${@:1:$#-1}") - local format_code="" - local reset_code="\033[0m" - for arg in "${args[@]}"; do - arg=${arg,,} - case "$arg" in - -n) new_line="" ;; - bold) format_code+="\033[1m" ;; - italic) format_code+="\033[3m" ;; - underline) format_code+="\033[4m" ;; - black) format_code+="\033[30m" ;; - red) format_code+="\033[31m" ;; - green) format_code+="\033[32m" ;; - yellow) format_code+="\033[33m" ;; - blue) format_code+="\033[34m" ;; - purple) format_code+="\033[35m" ;; - cyan) format_code+="\033[36m" ;; - white) format_code+="\033[37m" ;; - grey | gray) format_code+="\033[90m" ;; - [0-9]*) - if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 0 ] && [ "$arg" -le 255 ]; then - format_code+="\033[38;5;${arg}m" - fi - ;; - *) ;; - esac - done - printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}" -} -export -f pen - -run() { - local outvar_name errvar_name - local -n outvar errvar # Declare namerefs (will be assigned below if needed) - local cmd - while [[ $# -gt 0 ]]; do - case "$1" in - --out) - outvar_name="$2" - shift 2 - ;; - --err) - errvar_name="$2" - shift 2 - ;; - *) - cmd=("$@") - break - ;; - esac - done - [[ -n "${outvar_name}" ]] && local -n outvar="${outvar_name}" - [[ -n "${errvar_name}" ]] && local -n errvar="${errvar_name}" - local stdout_file stderr_file - stdout_file=$(mktemp) - stderr_file=$(mktemp) - "${cmd[@]}" >"${stdout_file}" 2>"${stderr_file}" - local exit_code=$? - [[ -n "${outvar_name}" ]] && outvar="$(<"$stdout_file")" - [[ -n "${errvar_name}" ]] && errvar="$(<"$stderr_file")" - rm -f "${stdout_file}" "${stderr_file}" - return $exit_code -} -export -f run - -check() { - if spinning; then - spop - upclear - fi - pen -n green "${_mark:-✓} " - pen "$@" -} -export -f check - -repen() { - upclear - pen "$@" -} -export -f repen - -trap spop EXIT INT TERM -_spinner_frame_duration=0.1 -_spinner_pid="" -spin() { - local message=("$@") - _spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" - if spinning; then - spop --keep-cursor-hidden - fi - ( - hide_cursor - trap "show_cursor; exit 0" USR1 - pen -n cyan "${_spinner:0:1} " - pen "${message[@]}" - while true; do - for ((i = 0; i < ${#_spinner}; i++)); do - frame="${_spinner:$i:1}" - up - bol - pen -n cyan "${frame} " - pen "${message[@]}" - sleep $_spinner_frame_duration - done - done - ) & - _spinner_pid=$! -} -spop() { - local keep_cursor_hidden=false - [[ "$1" == "--keep-cursor-hidden" ]] && keep_cursor_hidden=true - if spinning; then - kill -USR1 "${_spinner_pid}" 2>/dev/null - sleep $_spinner_frame_duration - if ps -p "${_spinner_pid}" >/dev/null 2>&1; then - kill "${_spinner_pid}" 2>/dev/null - if [[ "$keep_cursor_hidden" == false ]]; then - show_cursor - fi - fi - _spinner_pid="" - fi -} -spinning() { - [[ -n "${_spinner_pid}" ]] -} -export -f spin spop spinning - -throw() { - if spinning; then - spop - upclear - fi - pen -n red "${_cross:-✗} " - pen "$@" -} -export -f throw - -warn() { - if spinning; then - spop - upclear - fi - pen -n yellow bold italic "${_warn:-!} " - pen italic "$@" -} -export -f warn - -ask() { - local -n outvar="$1" - local prompt - local answer - prompt=$( - pen -n blue "${_q:-?} " - pen "${2}" - pen -n blue "${_a:-❯} " - ) - show_cursor - while true; do - read -r -p "$prompt" answer - case "$answer" in - "") - echo - warn "Please type your answer." - ;; - *) break ;; - esac - done - outvar="$answer" -} -export -f ask - -choose() { - local -n outvar="$1" - local prompt - local options=("${@:3}") - local current=0 - local count=${#options[@]} - prompt=$( - pen -n blue "${_q:-?} " - pen -n "${2}" - pen gray "[↑↓]" - ) - hide_cursor - trap 'show_cursor; return' INT TERM - pen "$prompt" - while true; do - local index=0 - for item in "${options[@]}"; do - if ((index == current)); then - pen -n blue "${_O:-●} " - pen "${item}" - else - pen gray "${_o:-◌} ${item}" - fi - ((index++)) - done - read -s -r -n1 key - if [[ $key == $'\e' ]]; then - read -s -r -n2 -t 0.0001 escape - key+="$escape" - fi - case "$key" in - $'\e[A' | 'k') - ((current--)) - [[ $current -lt 0 ]] && current=$((count - 1)) - ;; - $'\e[B' | 'j') - ((current++)) - [[ $current -ge "$count" ]] && current=0 - ;; - '') - break - ;; - esac - echo -en "\e[${count}A\e[J" - done - outvar="${options[$current]}" -} - -confirm() { - local default="y" - local hint="[Y/n]" - local prompt - local response - while [[ $# -gt 0 ]]; do - case "$1" in - --default-no) - default="n" - hint="[y/N]" - shift - ;; - --default-yes) - shift - ;; - *) break ;; - esac - done - prompt=$( - pen -n blue "${_q:-?} " - pen -n "$1" - pen gray " $hint" - pen -n blue "${_a:-❯} " - ) - show_cursor - while true; do - read -r -p "$prompt" response - response="${response:-$default}" - case "$response" in - [Yy] | [Yy][Ee][Ss]) - upclear - pen -n blue "${_a:-❯} " - pen "yes" - return 0 - ;; - [Nn] | [Nn][Oo]) - upclear - pen -n blue "${_a:-❯} " - pen "no" - return 1 - ;; - *) - echo - warn "Please answer yes or no." - ;; - esac - done -} -export -f confirm diff --git a/demo/demo.sh b/demo/demo.sh index c50bc2f..e4c9f7f 100755 --- a/demo/demo.sh +++ b/demo/demo.sh @@ -9,6 +9,8 @@ demo() { _violet=99 _pink=219 + pen "\n>> $_q <<\n" + line pen $_violet "╔═════════════════════════════════════════════╗" pen $_violet "║ ║" diff --git a/src/00.utils/_symbols.sh b/src/00.utils/_symbols.sh index cafbae5..68fa233 100644 --- a/src/00.utils/_symbols.sh +++ b/src/00.utils/_symbols.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash # @private +[[ $BEDDU_SYMBOLS_LOADED ]] && return +readonly BEDDU_SYMBOLS_LOADED=true + readonly _q='?' readonly _a='❯' readonly _o='◌' @@ -9,5 +12,4 @@ readonly _mark='✓' readonly _warn='!' readonly _cross='✗' readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt - -export _q _a _o _O _mark _warn _cross _spinner +readonly _spinner_frame_duration=0.1 diff --git a/src/00.utils/movements.sh b/src/00.utils/movements.sh index e759b68..3e9fc37 100644 --- a/src/00.utils/movements.sh +++ b/src/00.utils/movements.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash # movements.sh - Cursor helper functions +[[ $BEDDU_MOVEMENTS_LOADED ]] && return +readonly BEDDU_MOVEMENTS_LOADED=true + # Move cursor up one line up() { printf "\033[A" @@ -45,6 +48,3 @@ show_cursor() { hide_cursor() { printf "\033[?25l" } - -# Export the functions so they are available to subshells -export -f up down bol eol cl line show_cursor hide_cursor diff --git a/src/01.core/pen.sh b/src/01.core/pen.sh index 1d60547..046f78e 100644 --- a/src/01.core/pen.sh +++ b/src/01.core/pen.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # pen.sh - Print pretty text -# @depends on: -# - _symbols.sh +[[ $BEDDU_PEN_LOADED ]] && return +readonly BEDDU_PEN_LOADED=true # Print text with ANSI color codes and text formatting # @@ -57,6 +57,3 @@ pen() { printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}" } - -# Export the pen function so it's available to subshells -export -f pen diff --git a/src/01.core/run.sh b/src/01.core/run.sh index b4e9c30..bf2b7f1 100644 --- a/src/01.core/run.sh +++ b/src/01.core/run.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash # run.sh - Execute commands with output/error capture +[[ $BEDDU_RUN_LOADED ]] && return +readonly BEDDU_RUN_LOADED=true + # Execute a command with stdout and stderr capture capabilities # # Usage: @@ -54,6 +57,3 @@ run() { rm -f "${stdout_file}" "${stderr_file}" return $exit_code } - -# Export the run function so it's available to subshells -export -f run diff --git a/src/02.ui/check.sh b/src/02.ui/check.sh index d676a28..663de33 100644 --- a/src/02.ui/check.sh +++ b/src/02.ui/check.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # check.sh - Print a success message -# @depends on: -# - pen.sh -# - movements.sh -# - _symbols.sh +[[ $BEDDU_CHECK_LOADED ]] && return +readonly BEDDU_CHECK_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" +source "$SCRIPT_DIR/spin.sh" # Print a checkmark with a message, and stop and replace the # spinner if it's running (relies on the spinner being the last @@ -31,6 +36,3 @@ check() { pen -n green "${_mark:-✓} " pen "$@" } - -# Export the check function so it can be used in other scripts -export -f check diff --git a/src/02.ui/repen.sh b/src/02.ui/repen.sh index b0afa73..90208aa 100644 --- a/src/02.ui/repen.sh +++ b/src/02.ui/repen.sh @@ -1,9 +1,13 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # repen.sh - Overwrite the previous line with new text -# @depends on: -# - pen.sh -# - movements.sh +[[ $BEDDU_REPEN_LOADED ]] && return +readonly BEDDU_REPEN_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" # Move up one line, move to the beginning, clear the line, and print the text. # @@ -20,6 +24,3 @@ repen() { upclear pen "$@" } - -# Export the repen function so it can be used in other scripts -export -f repen diff --git a/src/02.ui/spin.sh b/src/02.ui/spin.sh index 5fd8384..00c263a 100644 --- a/src/02.ui/spin.sh +++ b/src/02.ui/spin.sh @@ -1,17 +1,21 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # spin.sh - Print a spinner with a message -# @depends on: -# - pen.sh -# - movements.sh -# - _symbols.sh +[[ $BEDDU_SPIN_LOADED ]] && return +readonly BEDDU_SPIN_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" # Make sure the cursor is shown and the spinner stopped if the script exits abnormally trap spop EXIT INT TERM # Module state variables -_spinner_frame_duration=0.1 _spinner_pid="" +_frame_duration="${_spinner_frame_duration:-0.1}" # Print a message with a spinner at the beginning # @@ -30,7 +34,7 @@ _spinner_pid="" # check "Dependancies installed." spin() { local message=("$@") - _spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" + local spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" # If there is already a spinner running, stop it if spinning; then @@ -45,17 +49,17 @@ spin() { trap "show_cursor; exit 0" USR1 # Print the first frame of the spinner - pen -n cyan "${_spinner:0:1} " + pen -n cyan "${spinner:0:1} " pen "${message[@]}" while true; do - for ((i = 0; i < ${#_spinner}; i++)); do - frame="${_spinner:$i:1}" + for ((i = 0; i < ${#spinner}; i++)); do + frame="${spinner:$i:1}" up bol pen -n cyan "${frame} " pen "${message[@]}" - sleep $_spinner_frame_duration + sleep "$_frame_duration" done done ) & @@ -73,7 +77,7 @@ spop() { kill -USR1 "${_spinner_pid}" 2>/dev/null # Wait briefly for cleanup - sleep $_spinner_frame_duration + sleep "$_frame_duration" # Ensure it's really gone if ps -p "${_spinner_pid}" >/dev/null 2>&1; then @@ -92,6 +96,3 @@ spop() { spinning() { [[ -n "${_spinner_pid}" ]] } - -# Export the functions so they are available to subshells -export -f spin spop spinning diff --git a/src/02.ui/throw.sh b/src/02.ui/throw.sh index 44af460..44355fa 100644 --- a/src/02.ui/throw.sh +++ b/src/02.ui/throw.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # throw.sh - Print an throw message -# @depends on: -# - pen.sh -# - movements.sh -# - _symbols.sh +[[ $BEDDU_THROW_LOADED ]] && return +readonly BEDDU_THROW_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" +source "$SCRIPT_DIR/spin.sh" # Print an throwmark with a message, and stop and replace the # spinner if it's running (relies on the spinner being the last @@ -31,6 +36,3 @@ throw() { pen -n red "${_cross:-✗} " pen "$@" } - -# Export the throw function so it can be used in other scripts -export -f throw diff --git a/src/02.ui/warn.sh b/src/02.ui/warn.sh index 91e4b46..35a07bb 100644 --- a/src/02.ui/warn.sh +++ b/src/02.ui/warn.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # warn.sh - Print a warning message -# @depends on: -# - pen.sh -# - movements.sh -# - _symbols.sh +[[ $BEDDU_WARN_LOADED ]] && return +readonly BEDDU_WARN_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" +source "$SCRIPT_DIR/spin.sh" # Print a "!" with a message, and stop and replace the # spinner if it's running (relies on the spinner being @@ -31,6 +36,3 @@ warn() { pen -n yellow bold italic "${_warn:-!} " pen italic "$@" } - -# Export the warn function so it can be used in other scripts -export -f warn diff --git a/src/03.prompt/ask.sh b/src/03.prompt/ask.sh index b8c4a47..a7c87cc 100644 --- a/src/03.prompt/ask.sh +++ b/src/03.prompt/ask.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # ask.sh - Get free text input from the user -# @depends on: -# - pen.sh -# - _symbols.sh -# - cursor.sh +[[ $BEDDU_ASK_LOADED ]] && return +readonly BEDDU_ASK_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" +source "$SCRIPT_DIR/../02.ui/warn.sh" # Ask a question and get a free text answer from the user # @@ -42,6 +47,3 @@ ask() { # shellcheck disable=SC2034 outvar="$answer" } - -# Export the ask function so it's available to subshells -export -f ask diff --git a/src/03.prompt/choose.sh b/src/03.prompt/choose.sh index 9799f8e..9e4e2ad 100644 --- a/src/03.prompt/choose.sh +++ b/src/03.prompt/choose.sh @@ -1,10 +1,14 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # choose.sh - Choose from a menu of options -# @depends on: -# - pen.sh -# - _symbols.sh -# - cursor.sh +[[ $BEDDU_CHOOSE_LOADED ]] && return +readonly BEDDU_CHOOSE_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" # Print an interactive menu of options and return the selected option # diff --git a/src/03.prompt/confirm.sh b/src/03.prompt/confirm.sh index b8b366e..c175f94 100644 --- a/src/03.prompt/confirm.sh +++ b/src/03.prompt/confirm.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # confirm.sh - Read a yes/no confirmation from the user -# @depends on: -# - pen.sh -# - _symbols.sh -# - movements.sh +[[ $BEDDU_CONFIRM_LOADED ]] && return +readonly BEDDU_CONFIRM_LOADED=true + +SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../00.utils/_symbols.sh" +source "$SCRIPT_DIR/../00.utils/movements.sh" +source "$SCRIPT_DIR/../01.core/pen.sh" +source "$SCRIPT_DIR/../02.ui/warn.sh" # Ask a question and get a yes/no answer from the user # @@ -74,6 +79,3 @@ confirm() { esac done } - -# Export the confirm function so it's available to subshells -export -f confirm