diff --git a/.bash_path_resolver b/.bash_path_resolver index ccfe7f4..dadb644 100644 --- a/.bash_path_resolver +++ b/.bash_path_resolver @@ -1,6 +1,10 @@ #!/usr/bin/env bash # vim: ft=bash +function is-debug() { + [[ $((DEBUG + BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) -gt 0 ]] +} + function bashmatic.path.resolver() { export PATH="/usr/local/bin:/usr/bin:/bin:/sbin:${PATH}" @@ -32,10 +36,10 @@ function bashmatic.path.resolver() { [[ -n "${BASHMATIC_HOME}" && -d "${BASHMATIC_HOME}" && -f "${BASHMATIC_HOME}/init.sh" ]] || { if [[ "${SHELL_COMMAND}" =~ zsh ]]; then - ((BASHMATIC_DEBUG)) && printf "${BASHMATIC_PREFIX} Detected zsh version ${ZSH_VERSION}, source=$0:A\n" + is-debug && printf "${BASHMATIC_PREFIX} Detected zsh version ${ZSH_VERSION}, source=$0:A\n" BASHMATIC_HOME="$(/usr/bin/dirname "$0:A")" elif [[ "${SHELL_COMMAND}" =~ bash ]]; then - ((BASHMATIC_DEBUG)) && printf "${BASHMATIC_PREFIX} Detected bash version ${BASH_VERSION}, source=${BASH_SOURCE[0]}\n" + is-debug && printf "${BASHMATIC_PREFIX} Detected bash version ${BASH_VERSION}, source=${BASH_SOURCE[0]}\n" BASHMATIC_HOME="$(cd -P -- "$(/usr/bin/dirname -- "${BASH_SOURCE[0]}")" && printf '%s\n' "$(pwd -P)")" else printf "${BASHMATIC_PREFIX} WARNING: Detected an unsupported shell type: ${SHELL_COMMAND}, continue.\n" >&2 diff --git a/.bash_profile b/.bash_profile index 5ab65bc..9536d6a 100644 --- a/.bash_profile +++ b/.bash_profile @@ -1,5 +1,10 @@ #!/usr/bin/env bash # vim: ft=bash + +function is-debug() { + [[ $((DEBUG + BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) -gt 0 ]] +} + [[ -z ${BASHMATIC_HOME} ]] && { [[ -d ~/.bashmatic ]] && export BASHMATIC_HOME="${HOME}/.bashmatic" } @@ -21,9 +26,9 @@ if [[ ! -f "${BASHMATIC_HOME}/.bash_safe_source" ]]; then if [[ -s "${file}" ]]; then source "${file}" local code=$? - ((BASHMATIC_DEBUG)) && printf "[debug] loading file: [${bldgrn}%40.40s${clr}]" + is-debug && printf "[debug] loading file: [${bldgrn}%40.40s${clr}]" if ((code)); then - if ((BASHMATIC_DEBUG)); then + if is-debug; then printf " (exit code: ${code} [ ๐Ÿ’ฃ ])\n" else echo "WARNING: sourced file ${file} returned non-zero code ${code}" >&2 diff --git a/.bash_safe_source b/.bash_safe_source index 760e52a..58166a2 100644 --- a/.bash_safe_source +++ b/.bash_safe_source @@ -5,26 +5,33 @@ set +e -((DEBUG + __debug + debug)) && BASHMATIC_DEBUG=1 +function is-debug() { + [[ $((DEBUG + BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) -gt 0 ]] +} + +is-debug && BASHMATIC_DEBUG=1 +is-debug || unset BASHMATIC_DEBUG function source_if_exists() { local errors=0 + local files="${#@}" for file in "$@"; do if [[ -s "${file}" ]]; then - ((BASHMATIC_DEBUG)) && printf "[debug] โ€”โ€”> ${bldgrn}%-60.60s${clr}" "${file}" + is-debug && printf "[debug] โ€”โ€”> ${bldgrn}%-60.60s${clr}" "${file}" source "${file}" local code=$? if ((code)); then - if ((BASHMATIC_DEBUG)); then + if is-debug; then printf " (exit code: ${code} [ ๐Ÿ’ฃ ])\n" code=$((code + 1)) + errors=$((errors + 1)) else code=$((code + 1)) echo "WARNING: sourced file ${file} returned non-zero code ${code}" >&2 fi else - if ((BASHMATIC_DEBUG)); then + if is-debug; then printf " (${bldgrn}[ โœ”๏ธŽ ]${clr})\n" fi fi @@ -32,6 +39,9 @@ function source_if_exists() { echo "WARNING: sourced file ${file} does not exist" >&2 fi done + + is-debug && echo "source_if_exists: attempted to source ${files} files, ${errors} returned errors." + return ${code} } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d96a2d..b2aaf44 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,4 +24,4 @@ jobs: env: TERM: xterm-256color MIN_WIDTH: 80 - run: bin/specs -t -c + run: bin/specs diff --git a/.gitignore b/.gitignore index 8548e56..f4fd031 100644 --- a/.gitignore +++ b/.gitignore @@ -3,13 +3,11 @@ .bats-* .bundle .bundle/ -.developer-warned .envrc.local .fonts/ .idea/ .last-rebase .ruby-version -/Gemfile.lock /test/tmp/ /test/version_tmp/ /tmp/ @@ -30,3 +28,5 @@ doc/USAGE.adoc .history .vscode/*.log .ruby-version +.last-loaded +.developer-warned diff --git a/.version b/.version index 37c2961..4a36342 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.7.2 +3.0.0 diff --git a/Dockerfile b/Dockerfile index 1f69e67..0ec3ae7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,21 +17,15 @@ # $ encrypt word # -FROM ubuntu:20.10 +FROM ruby:3.1.2-slim RUN apt-get update -y && \ apt-get install -yqq \ build-essential \ - git \ - ruby \ - python3-pip - + git ENV TERM=xterm-256color \ BASHMATIC_HOME=/app/bashmatic \ - LC_ALL=en_US.UTF-8 \ - LANG=en_US.UTF-8 \ - LANGUAGE=en_US.UTF-8 \ USER=root \ HOME=/root \ DEBIAN_FRONTEND=noninteractive \ @@ -54,6 +48,9 @@ RUN apt-get update -y && apt-get install -yqq \ rbenv \ sudo +RUN apt-get install -yqq \ + python3-pip + ENV SHELL_INIT="${HOME}/.bashrc" RUN set -e && \ @@ -85,4 +82,8 @@ RUN rm -f ~/.zshrc && \ RUN sed -i'' -E 's/robbyrussell/agnoster/g' ${HOME}/.zshrc RUN echo system > .ruby-version +ENV LANG=en_US.UTF-8 \ + LANGUAGE=en_US.UTF-8 + ENTRYPOINT /bin/bash -l + diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..2d4010b --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,186 @@ +GIT + remote: https://github.com/asciidoctor/asciidoctor-pdf.git + revision: 5fe7d4b9dd817451dfa9a4ccb2afa04b47ef8472 + specs: + asciidoctor-pdf (2.0.0.beta.1) + asciidoctor (~> 2.0) + concurrent-ruby (~> 1.1) + matrix (~> 0.4) + prawn (~> 2.4.0) + prawn-icon (~> 3.0.0) + prawn-svg (~> 0.32.0) + prawn-table (~> 0.2.0) + prawn-templates (~> 0.1.0) + treetop (~> 1.6.0) + +GIT + remote: https://github.com/prawnpdf/prawn-table.git + revision: 38b5bdb5dd95237646675c968091706f57a7a641 + specs: + prawn-table (0.2.3) + prawn (>= 1.3.0, < 3.0.0) + +GIT + remote: https://github.com/prawnpdf/ttfunk.git + revision: 9f9df2b5fb02f5398f360b6a1bcc63b4f5063e07 + specs: + ttfunk (1.7.0) + +GEM + remote: https://rubygems.org/ + specs: + Ascii85 (1.1.0) + activesupport (7.0.2.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + afm (0.2.2) + asciidoctor (2.0.17) + async (2.0.2) + console (~> 1.10) + io-event (~> 1.0.0) + timers (~> 4.1) + async-http (0.56.6) + async (>= 1.25) + async-io (>= 1.28) + async-pool (>= 0.2) + protocol-http (~> 0.22.0) + protocol-http1 (~> 0.14.0) + protocol-http2 (~> 0.14.0) + traces (~> 0.4.0) + async-http-faraday (0.11.0) + async-http (~> 0.42) + faraday + async-io (1.33.0) + async + async-pool (0.3.10) + async (>= 1.25) + cmdparse (3.0.7) + coderay (1.1.3) + concurrent-ruby (1.1.10) + console (1.15.0) + fiber-local + css_parser (1.11.0) + addressable + faraday (1.10.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-http-cache (2.2.0) + faraday (>= 0.8) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.3) + multipart-post (>= 1.2, < 3) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + fiber-local (1.0.0) + geom2d (0.3.1) + github_changelog_generator (1.16.4) + activesupport + async (>= 1.25.0) + async-http-faraday + faraday-http-cache + multi_json + octokit (~> 4.6) + rainbow (>= 2.2.1) + rake (>= 10.0) + hashery (2.1.2) + hexapdf (0.22.0) + cmdparse (~> 3.0, >= 3.0.3) + geom2d (~> 0.3) + i18n (1.10.0) + concurrent-ruby (~> 1.0) + io-event (1.0.9) + matrix (0.4.2) + minitest (5.15.0) + multi_json (1.15.0) + multipart-post (2.1.1) + octokit (4.22.0) + faraday (>= 0.9) + sawyer (~> 0.8.0, >= 0.5.3) + pdf-core (0.9.0) + pdf-reader (2.9.2) + Ascii85 (~> 1.0) + afm (~> 0.2.1) + hashery (~> 2.0) + ruby-rc4 + ttfunk + polyglot (0.3.5) + prawn (2.4.0) + pdf-core (~> 0.9.0) + ttfunk (~> 1.7) + prawn-icon (3.0.0) + prawn (>= 1.1.0, < 3.0.0) + prawn-svg (0.32.0) + css_parser (~> 1.6) + prawn (>= 0.11.1, < 3) + rexml (~> 3.2) + prawn-templates (0.1.2) + pdf-reader (~> 2.0) + prawn (~> 2.2) + protocol-hpack (1.4.2) + protocol-http (0.22.6) + protocol-http1 (0.14.4) + protocol-http (~> 0.22) + protocol-http2 (0.14.2) + protocol-hpack (~> 1.4) + protocol-http (~> 0.18) + public_suffix (4.0.7) + pygments.rb (2.3.0) + rainbow (3.1.1) + rake (13.0.6) + rexml (3.2.5) + rghost (0.9.7) + rouge (3.28.0) + ruby-rc4 (0.1.5) + ruby2_keywords (0.0.5) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + specific_install (0.3.7) + text-hyphen (1.4.1) + timers (4.3.3) + traces (0.4.1) + treetop (1.6.11) + polyglot (~> 0.3) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + +PLATFORMS + x86_64-darwin-21 + +DEPENDENCIES + asciidoctor-pdf! + coderay + github_changelog_generator + hexapdf + matrix + prawn + prawn-table! + pygments.rb + rexml + rghost + rouge + specific_install + text-hyphen + ttfunk! + +BUNDLED WITH + 2.3.8 diff --git a/Makefile b/Makefile index f798f66..1ce68ee 100755 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ BASHMATIC_HOME := $(shell dirname $(MAKEFILE_PATH)) BASHMATIC_VERSION := $(shell cat .version) BASHMATIC_TAG := "v$(BASHMATIC_VERSION)" BASHMATIC_RELEASE := "Release for Tag $(BASHMATIC_TAG)" +BASHMATIC_GEMFILE :="$(BASHMATIC_HOME)/Gemfile" +BASHMATIC_GEMS :="$(HOME)/.bundle/gems/bashmatic" help: ## Prints help message auto-generated from the comments. @@ -73,6 +75,11 @@ shell-files: ## Lists every single checked in SHELL file in this repo file-stats-git: ## Print all files known to `git ls-files` command @git ls-files | xargs files + +bundle-install: ## Install all gems from the Gemfile + @mkdir -p $(BASHMATIC_GEMS) + bundle config set --local path "$(BASHMATIC_GEMS)" + time bundle install -j 12 --quiet --gemfile="$(BASHMATIC_GEMFILE)" #โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” update: update-changelog update-functions update-usage update-readme fonts-clean git-add ## Runs all of the updates, add locally modiofied files to git. @@ -128,11 +135,11 @@ setup: ## Run the comprehensive development setup on this machine -g postgres \ -g ruby -test: ## Run fully automated test suite based on Bats +test: ## Run fully automated test suite based on Bats in paralle @$(BASHMATIC_HOME)/bin/specs -test-parallel: ## Run the fully auto-g mated test suite - @$(BASHMATIC_HOME)/bin/specs -p +test-sequential: ## Run the fully automated test suite sequentially + @$(BASHMATIC_HOME)/bin/specs -P test-install-quiet: @bash -c "cd $(BASHMATIC_HOME); source bin/bashmatic-install; bashmatic-install -q" diff --git a/README.adoc b/README.adoc index eaa59ed..2eecd55 100644 --- a/README.adoc +++ b/README.adoc @@ -1,7 +1,7 @@ [separator=โ€”] = Bashmaticยฎ โ€” BASH-based DSL helpers for humans, sysadmins, and fun. // vim: ft=asciidoc -:author: Version v2.7.2 +:author: Version v3.0.0 :doctype: book :source-highlighter: rouge :rouge-style: base16.monokai @@ -35,15 +35,16 @@ icon:arrow-circle-down[3x, color="purple"] [source,bash] ---- +# An example of a DSL-like function function bashmatic.auto-update() { - local dir - dir=${1:-${BASHMATIC_HOME}} + local dir="${1:-"${BASHMATIC_HOME"}}" is.a-directory "${dir}" && { file.exists-and-newer-than "${dir}/.last-update" 30 && return 0 - - ( cd ${BASHMATIC_HOME} && \ + ( + cd ${BASHMATIC_HOME} && \ git.is-it-time-to-update && \ - git.sync-remote ) + git.sync-remote + ) } } @@ -57,16 +58,16 @@ Final note, - once Bashmatic is installed and loaded by your shell init files, y _Bashmaticยฎ_ offers a huge range of ever-growing helper functions for running commands, auto-retrying, repeatable, runtime-measuring execution framework with the key function `run`. There are helpers for every occasion, from drawing boxes, lines, headers, to showing progress bars, getting user input, installing packages, and much more. -NOTE: A good portion of the helpers within *_Bashmaticยฎ_ are written for OS-X, although many useful functions will also work under linux.* Our entire test suite runs on Ubuntu. There is an effort underway to convert Homebrew-specifc functions to OS-neutral helpers such as `package.install` that would work equally well on linux. +NOTE: A good portion of the helpers within *_Bashmaticยฎ_ are written for OS-X, although many useful functions will also work under linux.* Our entire test suite runs on Ubuntu. There is an effort underway to convert Homebrew-specific functions to OS-neutral helpers such as `package.install` that would work equally well on linux. Start exploring _Bashmaticยฎ_ below with our examples section. When you are ready, the complete entire set of pubic functions (nearly 500 of those) can be found in the https://github.com/kigster/bashmatic/blob/master/doc/FUNCTIONS.adoc[functions index page]. And, finally, don't worry, *_Bashmaticยฎ_* is totally open source and free to use and extend. We just like the way it looks with a little *ยฎ* :) -[CAUTION] +[TIP] ==== -You can also download the **https://github.com/kigster/bashmatic/blob/master/README.pdf[PDF version of this document]** which is better for print. +We suggest that you learn about Bashmatic from the **https://github.com/kigster/bashmatic/blob/master/README.pdf[PDF version of this document]** which is much better for print. * We recently began providing function documentation using a fork of `shdoc` utility. You can find the auto-generated documentation in the https://github.com/kigster/bashmatic/blob/master/doc/USAGE.md[USAGE] file, or it's https://github.com/kigster/bashmatic/blob/master/doc/USAGE.pdf[PDF] version. @@ -81,7 +82,7 @@ You can also download the **https://github.com/kigster/bashmatic/blob/master/REA * BASH version 3 (partial compatibility, some functions are disabled) * ZSH โ€“ as of recent update, Bashmatic is almost 90% compatible with ZSH. -**Not Supported** +**Not Currently Supported** * FISH (although you could use Bashmatic via `bin/bashmatic` script helper, or its executables) @@ -90,6 +91,7 @@ You can also download the **https://github.com/kigster/bashmatic/blob/master/REA This project was born out of a simple realization made by several very senior and highly experienced engineers, that: * It is often easier to use BASH for writing things like universal *installers*, a.k.a. *setup scripts*, *uploaders*, wrappers for all sorts of functionality, such as *NPM*, *rbenv*, installing gems, rubies, using AWS, deploying code, etc. + * BASH function's return values lend themselves nicely to a compact DSL (https://en.wikipedia.org/wiki/Domain-specific_language[domain specific language]) where multiple functions can be chained by logical AND `&&` and OR `||` to provide a very compact execution logic. Most importantly, we think that this logic is *extremely easy to read and understand.* Despite the above points, it is also generally accepted that: @@ -106,26 +108,45 @@ Perhaps the easiest way to install _Bashmaticยฎ_ is using `curl` as shown below. First, make sure that you have Curl installed, run `which curl` to see. Then copy/paste this command into your Terminal. +=== 1. Automated Install + +icon:arrow-down[3x, color="yellow"] + [source,bash] ---- -# -q stands for "quiet"; use -v for "verbose", or -h for help. bash -c "$(curl -fsSL https://bashmatic.re1.re); bashmatic-install -q" ---- -TIP: The URL _https://bashmatic.re1.re_ redirects to the HEAD of the https://raw.githubusercontent.com/kigster/bashmatic/master/bin/bashmatic-install[`bin/bashmatic-install`] script in the Github Bashmatic Repo. +icon:arrow-up[3x, color="yellow"] + +Where: + +* -q stands for "quiet"; +* -v for "verbose" -Here is a small variation: +TIP: The URL _https://bashmatic.re1.re_ redirects to the HEAD of the https://raw.githubusercontent.com/kigster/bashmatic/master/bin/bashmatic-install[`bin/bashmatic-install`] script in the Github Bashmatic Repo. We use this URL so that we retain the ability to redirect the installation to a different script in the future, if need be. + +=== 2. Automated Install, More Explicit + +If you prefer to be able to examine the script before executing code piped straight off the Internet, I don't blame you. You are cautious and smart. + +For folks like you, here is a slightly more secure way of doing the same thing: [source,bash] ---- -export install="/tmp/install" -curl -fsSL https://bashmatic.re1.re > "${install}" -chmod 755 "${install} -${install} [ --help | --quiet | --verbose | .... ] +export script="/tmp/install" +curl -fsSL https://bashmatic.re1.re > /tmp/install +chmod 755 /tmp/install + +# At this point you can examine /tmp/install +/tmp/install --help +/tmp/install --verbose --debug # install with extra info ---- This method allows you to examine the `/tmp/install` script before running it. +Below are some of the explanations + ==== Installing a Particular Version or a Branch You can install a branch or a tag of Bashmatic by passing `-b / --git-branch ` flag. @@ -148,15 +169,17 @@ For instance, here we are installing Bashmatic into a non-default destination, w [source,bash] ---- bash -c "$(curl -fsSL https://bashmatic.re1.re); \ - bashmatic-install -d -v -f -b v2.4.1 -H ~/workspace/bashmatic" + bashmatic-install -d -v -f -b v2.4.1 -H ~/workspace/bashmatic" ---- + ==== -If you have your SSH keys installed both locally, and the public key was configured with your account on Github, you might want to install Bashmatic using `git@github.com:kigster/bashmatic` origin, instead of the default `https://github.com/kigster/bashmatic': +If you have your SSH keys installed both locally, and the public key was configured with your account on Github, you might want to install Bashmatic using `git@github.com:kigster/bashmatic` origin, instead of the default `https://github.com/kigster/bashmatic`: + Here is the complete list of options accepted by the installer: -image::doc/img/bashmatic-install.png[Installer,width=99%,align=left] +image::doc/img/bashmatic-install.png[Installer,width=99%,align=left,border=5,margin=10] === Understanding what the Installer Does @@ -191,8 +214,21 @@ $ grep bashmatic ~/.bashrc [ATTENTION] ==== -icon:check-circle[fw, color="green"] Pros of loading at login:: Instant access to 800+ convenience functions Bashmtic offers and helpers. Bashmatic will auto-update whenever its loaded from the master branch. -icon:times-circle[fw, color="red"] Cons of loading at login:: About 600ms delay at login, and a potential security attack vector (eg, if someone hacks the repo). +icon:check-circle[fw, color="green"] Pros of loading at login:: Instant access to 800+ convenience functions Bashmaticยฉ offers and helpers. Bashmatic will auto-update whenever its loaded from the master branch. + +icon:times-circle[fw, color="red"] Cons of loading at login:: About __134ms__ delay at login, and a potential security attack vector (eg, if someone hacks the repo). + +TIP: We recently dramatically improved the loading time of the entirety of Bashmaticยฉ functions. Previously it took nearly 900ms, almost a full second to load 854 functions. Today it's no more than 180ms: + +[source,bash] +---- +โฏ time source init.sh + +real 0m0.134s +user 0m0.078s +sys 0m0.074s +---- + ==== If the above command shows the output you see above, when you grep your `bashrc` or `zshrc`, then all Bashmatic Functions will be loaded into your shell. This could be very convenient, for instance, @@ -295,11 +331,11 @@ To discover the breadth of available functions, type the following command to se ---- # List all functions using 4-column mode; print top 5 lines. โฏ bashmatic functions 4 | head -5 -7z.a db.psql.connect.db-set hl.yellow-on-gray run.inspect-variables -7z.install db.psql.connect.db-set hr run.inspect-variables- -7z.unzip db.psql.connect.just-d hr.colored run.inspect.set-skip-f -7z.x db.psql.connect.table- http.servers run.on-error.ask-is-en -7z.zip db.psql.connect.table- https.servers run.print-command +7z.a db.psql.connect.db-set hl.yellow-on-gray run.inspect-variables +7z.install db.psql.connect.db-set hr run.inspect-variables- +7z.unzip db.psql.connect.just-d hr.colored run.inspect.set-skip-f +7z.x db.psql.connect.table- http.servers run.on-error.ask-is-en +7z.zip db.psql.connect.table- https.servers run.print-command # or, to get the count of all functions, use 1 column output: $ bashmatic functions 1 | wc -l @@ -443,7 +479,7 @@ Run `make docker-build` to create an docker image `bashmatic:latest`. Run `make docker-run-bash` (or `...-zsh` or `...-fish`) to start a container with your favorite shell, and then validate if your functions work as expected. -image::doc/img/docker-bash.png[Docker Build,width=80%,align=center] +image::doc/img/docker-bash.png[Docker Build,width=100%,align=center] Note how this dropped me straight into the Linux environment prompt with Bashmatic already installed. @@ -473,7 +509,7 @@ gem.install "sym" && brew.install.package "curl" && \ Results in this detailed and, let's be honest, _gorgeous_ ASCII output: -image::doc/img/bashmatic-example.png[example,width=90%,border=2] +image::doc/img/bashmatic-example.png[example,width=100%,border=2] Tell me you are not at all excited to start writing complex installation flows in BASH right away? @@ -491,7 +527,7 @@ We provided an example script in link:examples/k8s-installer.sh[`examples/k8s-in Here is the output of running this script: -image::doc/img/k8installer.png[K8 Minicube Installer,width=80%,align=center] +image::doc/img/k8installer.png[K8 Minicube Installer,width=100%,align=center] Why do we think this type of installer is pretty awesome, compared to a silent but deadly shell script that "Jim-in-the-corner" wrote and now nobody understands? @@ -516,7 +552,7 @@ NOTE: the script relies on Homebrew behind the scenes, and therefore would not w It's located in https://github.com/kigster/bashmatic/blob/master/bin/dev-setup[`bin/dev-setup`] and has many CLI flags: -image::doc/img/dev-setup.png[Developer Setup,width=80%,align=center] +image::doc/img/dev-setup.png[Developer Setup,width=100%,align=center] In the example below we'll use `dev-setup` script to install the following: @@ -570,7 +606,7 @@ Just like with the regular `top` you can see the "top" resource-consuming proces Here is the pixelated screenshot of `dbtop` running against two live databases: -image::doc/img/dbtop.png[DBTop Example,width=80%,align=center,link="https://github.com/kigster/bashmatic/blob/master/FUNCTIONS.adoc#db-top"] +image::doc/img/dbtop.png[DBTop Example,width=100%,align=center,link="https://github.com/kigster/bashmatic/blob/master/FUNCTIONS.adoc#db-top"] In order for this to work, you must first define database connection parameters in a YAML file located at the following PATH: `~/.db/database.yml`. @@ -625,7 +661,7 @@ You can configure the following settings for `db top`: If you run `db` without any arguments, or with `-h` you will see the following: -image::doc/img/db.png[db usage,border=2,width=80%,align=center] +image::doc/img/db.png[db usage,border=2,width=100%,align=center] As you might notice, there is an ever-growing list of "actions" โ€” the sub-commands to the `db` script. @@ -633,11 +669,11 @@ As you might notice, there is an ever-growing list of "actions" โ€” the sub-comm You can view the full list by passing `--commands` flag: -image::doc/img/db-commands.png[db usage,border=2,width=80%,align=center] +image::doc/img/db-commands.png[db usage,border=2,width=100%,align=center] Altgernatively, here is the `--examples` view: -image::doc/img/db-examples.png[db examples,border=2,width=80%,align=center] +image::doc/img/db-examples.png[db examples,border=2,width=100%,align=center] ==== Sub-Command `db connections` @@ -650,7 +686,7 @@ db connections db --connections ---- -image::doc/img/db-connections.png[db usage,border=2,width=80%,align=center] +image::doc/img/db-connections.png[db usage,border=2,width=100%,align=center] ==== Sub-Command `db pga` (eg. `pg_activity`) @@ -658,7 +694,7 @@ For instance, a recent addition is the ability to invoke https://github.com/dali You can invoke `db pga ` where the connection is taken from the database connection definitions shown above. This is what `pg-activity` looks like in action: -image::doc/img/db-pga.png[pg_activity,border=2,width=80%,align=center] +image::doc/img/db-pga.png[pg_activity,border=2,width=100%,align=center] ==== Other Sub-Commands @@ -708,7 +744,7 @@ The script is meant to be run against one database, and perform a table-level op Below is the screenshot of the help screen from this script: -image::doc/img/bashmatic-tablet.png[Tablet Script in Action,border=2,width=80%,align=center] +image::doc/img/bashmatic-tablet.png[Tablet Script in Action,border=2,width=100%,align=center] == Usage @@ -859,18 +895,18 @@ These collectively offer the following functions: ---- $ bashmatic.functions-from 'run*' -run run.set-next -run.config.detail-is-enabled run.set-next.list -run.config.verbose-is-enabled run.ui.ask -run.inspect run.ui.ask-user-value -run.inspect-variable run.ui.get-user-value -run.inspect-variables run.ui.press-any-key -run.inspect-variables-that-are run.ui.retry-command -run.inspect.set-skip-false-or-blank run.variables-ending-with -run.on-error.ask-is-enabled run.variables-starting-with -run.print-variable run.with.minimum-duration -run.print-variables run.with.ruby-bundle -run.set-all run.with.ruby-bundle-and-output +run run.set-next +run.config.detail-is-enabled run.set-next.list +run.config.verbose-is-enabled run.ui.ask +run.inspect run.ui.ask-user-value +run.inspect-variable run.ui.get-user-value +run.inspect-variables run.ui.press-any-key +run.inspect-variables-that-are run.ui.retry-command +run.inspect.set-skip-false-or-blank run.variables-ending-with +run.on-error.ask-is-enabled run.variables-starting-with +run.print-variable run.with.minimum-duration +run.print-variables run.with.ruby-bundle +run.set-all run.with.ruby-bundle-and-output run.set-all.list ---- @@ -916,37 +952,37 @@ Here is the list of functions in this module: [source,bash] ---- $ bashmatic.functions-from output 3 -abort error: left-prefix -ascii-clean h.black ok -box.blue-in-green h.blue okay -box.blue-in-yellow h.green output.color.off -box.green-in-cyan h.red output.color.on -box.green-in-green h.yellow output.is-pipe -box.green-in-magenta h1 output.is-redirect -box.green-in-yellow h1.blue output.is-ssh -box.magenta-in-blue h1.green output.is-terminal -box.magenta-in-green h1.purple output.is-tty -box.red-in-magenta h1.red puts -box.red-in-red h1.yellow reset-color -box.red-in-yellow h2 reset-color: -box.yellow-in-blue h2.green screen-width -box.yellow-in-red h3 screen.height -box.yellow-in-yellow hdr screen.width -br hl.blue shutdown -center hl.desc stderr -columnize hl.green stdout -command-spacer hl.orange success -cursor.at.x hl.subtle test-group -cursor.at.y hl.white-on-orange ui.closer.kind-of-ok -cursor.down hl.white-on-salmon ui.closer.kind-of-ok: -cursor.left hl.yellow ui.closer.not-ok -cursor.rewind hl.yellow-on-gray ui.closer.not-ok: -cursor.right hr ui.closer.ok: -cursor.up hr.colored warn -debug inf warning -duration info warning: -err info: -error left +abort error: left-prefix +ascii-clean h.black ok +box.blue-in-green h.blue okay +box.blue-in-yellow h.green output.color.off +box.green-in-cyan h.red output.color.on +box.green-in-green h.yellow output.is-pipe +box.green-in-magenta h1 output.is-redirect +box.green-in-yellow h1.blue output.is-ssh +box.magenta-in-blue h1.green output.is-terminal +box.magenta-in-green h1.purple output.is-tty +box.red-in-magenta h1.red puts +box.red-in-red h1.yellow reset-color +box.red-in-yellow h2 reset-color: +box.yellow-in-blue h2.green screen-width +box.yellow-in-red h3 screen.height +box.yellow-in-yellow hdr screen.width +br hl.blue shutdown +center hl.desc stderr +columnize hl.green stdout +command-spacer hl.orange success +cursor.at.x hl.subtle test-group +cursor.at.y hl.white-on-orange ui.closer.kind-of-ok +cursor.down hl.white-on-salmon ui.closer.kind-of-ok: +cursor.left hl.yellow ui.closer.not-ok +cursor.rewind hl.yellow-on-gray ui.closer.not-ok: +cursor.right hr ui.closer.ok: +cursor.up hr.colored warn +debug inf warning +duration info warning: +err info: +error left ---- Note that some function names end with `:` โ€“ this indicates that the function outputs a new-line in the end. These functions typically exist together with their non-`:`-terminated counter-parts. If you use one, eg, `inf`, you are then supposed to finish the line by providing an additional output call, most commonly it will be one of `ok:`, `ui.closer.not-ok:` and `ui.closer.kind-of-ok:`. @@ -1041,7 +1077,7 @@ success "installed Sym version $(gem.version sym)" When you run the above script, you shyould seee the following output: -image::doc/img/bashmatic-example.png[example,align=center,width=80%] +image::doc/img/bashmatic-example.png[example,align=center,width=100%] ==== Shortening URLs and Github Access @@ -1160,7 +1196,7 @@ util.call-if-function util.shell-init-files shasum.sha-only util.shell-name shasum.sha-only-stdin util.ver-to-i util.functions-starting-with util.whats-installed -util.generate-password ` watch.ls-al +util.generate-password watch.ls-al ---- For example, version helpers can be very handy in automated version detection, sorting and identifying the latest or the oldest versions: @@ -1183,18 +1219,18 @@ Additional Ruby helpers abound: ---- $ bashmatic.functions-from ruby -bundle.gems-with-c-extensions ruby.install-ruby-with-deps -interrupted ruby.install-upgrade-bundler -ruby.bundler-version ruby.installed-gems -ruby.compiled-with ruby.kigs-gems -ruby.default-gems ruby.linked-libs -ruby.full-version ruby.numeric-version -ruby.gemfile-lock-version ruby.rbenv -ruby.gems ruby.rubygems-update -ruby.gems.install ruby.stop -ruby.gems.uninstall ruby.top-versions -ruby.init ruby.top-versions-as-yaml -ruby.install ruby.validate-version +bundle.gems-with-c-extensions ruby.install-ruby-with-deps +interrupted ruby.install-upgrade-bundler +ruby.bundler-version ruby.installed-gems +ruby.compiled-with ruby.kigs-gems +ruby.default-gems ruby.linked-libs +ruby.full-version ruby.numeric-version +ruby.gemfile-lock-version ruby.rbenv +ruby.gems ruby.rubygems-update +ruby.gems.install ruby.stop +ruby.gems.uninstall ruby.top-versions +ruby.init ruby.top-versions-as-yaml +ruby.install ruby.validate-version ruby.install-ruby ---- @@ -1280,7 +1316,7 @@ video.convert.compress These commands auto-install ffmpeg and other utilities, and then use best in class compression. For instance, here is 80% compressed video file: -image::doc/img/video-squeeze.png[Video Squeeze, width="70%",align="center"] +image::doc/img/video-squeeze.png[Video Squeeze, width="100%",align="center"] ==== Additional Helpers @@ -1402,60 +1438,46 @@ We use Bats framework for testing, however we provided a convenient wrapper `bin The script can be run: -1. Without any arguments to run all tests in the `test` folder, or +1. Without any arguments to run all tests in the `test` folder in parallel by default 2. You can pass one or more existing test file paths as arguments, eg `bin/specs test/time_test.bats` 3. Finally, you can pass an abbreviated test file name โ€” eg "time" will resolve to `test/time_test.bats` The script accepts a bunch of CLI arguments and flags shown below: -image::doc/img/specs.png[example,align=center,width=60%] - - -==== Run Tests Sequentially using the `Makefile` - -Alternatively, you can run the entire test suite via the Makefile, using one of two targets: - -[source,bash] ----- -# Sequential -make test - -# Parallel -make test-parallel ----- - - -==== Running Specs in Parallel with `bin/spec -p` - -One of the very useful flags to `bin/specs` script is the `-p/--parallel`. +image::doc/img/specs-parallel.png[example,align=center,width=100%] -If you invoke it with this flag, the script will install **GNU Parallel** utility, which is in itself worth reading about. We refer you to the following set of https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1[YouTube Introductory Videos] on taking advantage of GNU Parallel projects and it's executable. +==== Running Specs Sequentially with `bin/spec -P` -Below is the screenshot of the tests running with the parallel flag. The script automatically detects that my machine has 16 CPU cores and uses this as a parallization factor. +By the default, `bin/spec` runs tests in parallel, and takes about 20 seconds. -image::doc/img/specs-parallel.png[example,align=center,width=60%] +If you pass the `-P/--no-parallel` flag, it will run sequentially and take about twice as long. +Below is the screenshot of the tests running in the parallel mode. The script automatically detects that my machine has 16 CPU cores and uses this as a parallization factor. +image::doc/img/specs.png[example,align=center,width=100%] ==== Run Tests Parallel using the `Makefile` -Note that you can run all tests in less than 15 seconds by using GNU parallel. Just run the following make target, and it will install any dependencies. +Note that you can run all tests in parallel using the following make target: [source,bash] ----- -make test-parallel ----- - - +make test While not every single function is tested (far from it), we do try to add tests to the critical ones. Please see https://github.com/kigster/bashmatic/blob/master/test/array_test.bats[existing tests] for the examples. +==== Run Tests Sequentially using the `Makefile` + +Alternatively, you can run the entire test suite via the Makefile, using one of two targets: + +[source,bash] +make test-sequential + -## Copyright & License +== Copyright & License -NOTE: ยฉ 2016-2021 Konstantin Gredeskoul + +NOTE: ยฉ 2016-2022 Konstantin Gredeskoul + This project is distributed under the **MIT License.** diff --git a/README.pdf b/README.pdf index 7054ac4..7d52eb1 100644 Binary files a/README.pdf and b/README.pdf differ diff --git a/bin/bashmatic-install b/bin/bashmatic-install index ce51554..d9289c8 100755 --- a/bin/bashmatic-install +++ b/bin/bashmatic-install @@ -323,7 +323,8 @@ function .install.os-requirements() { # Main Body #โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” -export BASHMATIC_DEBUG=0 +unset BASHMATIC_DEBUG + export bootstrap__skip_git_check=0 export bootstrap__skip_install=0 export bootstrap__verbose=0 @@ -339,7 +340,7 @@ export bootstrap__git_method="https" export bootstrap__git_branch="master" function is-debug() { - ((BASHMATIC_DEBUG)) + ((BASHMASTIC_DEBUG)) } function is-verbose() { @@ -448,6 +449,7 @@ function .install.parse-opts() { ;; -d | --debug) shift + export DEBUG=1 export BASHMATIC_DEBUG=1 export BASHMATIC_PATH_DEBUG=1 ;; @@ -652,12 +654,10 @@ set +e # source bin/bashmatic-install init if [[ ${current_shell} =~ "bash" ]] && [[ -n ${BASH_VERSION} && "$0" != "${BASH_SOURCE[${last_index}]}" ]]; then echo >/dev/null -elif [[ ${current_shell} =~ "zsh" ]] && [[ -n ${ZSH_EVAL_CONEXT} && ${ZSH_EVAL_CONTEXT} =~ :file$ ]] - bashmatic-install "$@" -then +elif [[ ${current_shell} =~ "zsh" ]] && [[ -n ${ZSH_EVAL_CONEXT} && ${ZSH_EVAL_CONTEXT} =~ :file$ ]]; then echo >/dev/null else - bashmatic-install "$@" + .install.bashmatic-install "$@" fi diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index eee1ada..e13ec23 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [v3.0.0](https://github.com/kigster/bashmatic/tree/v3.0.0) (2022-05-03) + +[Full Changelog](https://github.com/kigster/bashmatic/compare/v2.7.2...v3.0.0) + ## [v2.7.2](https://github.com/kigster/bashmatic/tree/v2.7.2) (2022-05-03) [Full Changelog](https://github.com/kigster/bashmatic/compare/v2.7.0...v2.7.2) @@ -10,6 +14,7 @@ **Merged pull requests:** +- Adding documentaion to is.sh; verison 2.7.2 [\#98](https://github.com/kigster/bashmatic/pull/98) ([kigster](https://github.com/kigster)) - Various tweaks for the installation [\#97](https://github.com/kigster/bashmatic/pull/97) ([kigster](https://github.com/kigster)) - Fixes to video functions [\#96](https://github.com/kigster/bashmatic/pull/96) ([kigster](https://github.com/kigster)) - Various new helpers and improvements [\#94](https://github.com/kigster/bashmatic/pull/94) ([kigster](https://github.com/kigster)) diff --git a/doc/FUNCTIONS.adoc b/doc/FUNCTIONS.adoc index fa09f32..e1927bb 100644 --- a/doc/FUNCTIONS.adoc +++ b/doc/FUNCTIONS.adoc @@ -1229,6 +1229,32 @@ bashmatic.current-os () ---- +==== `bashmatic.debug-off` + +[source,bash] +---- +bashmatic.debug-off () +{ + unset DEBUG + unset BASHMATIC_DEBUG + unset BASHMATIC_PATH_DEBUG +} + +---- + +==== `bashmatic.debug-on` + +[source,bash] +---- +bashmatic.debug-on () +{ + export DEBUG=1 + export BASHMATIC_DEBUG=1 + export BASHMATIC_PATH_DEBUG=1 +} + +---- + ==== `bashmatic.functions` [source,bash] @@ -1319,7 +1345,7 @@ bashmatic.load-at-login () ---- bashmatic.reload () { - bashmatic.set-is-not-loaded + __bashmatic.set-is-not-loaded source "${BASHMATIC_HOME}/.envrc.no-debug" source "${BASHMATIC_INIT}" } @@ -1332,13 +1358,28 @@ bashmatic.reload () ---- bashmatic.reload-debug () { - bashmatic.set-is-not-loaded + __bashmatic.set-is-not-loaded source "${BASHMATIC_HOME}/.envrc.debug" source "${BASHMATIC_INIT}" } ---- +==== `bashmatic.reset.cache` + +[source,bash] +---- +bashmatic.reset.cache () +{ + unset load_cache + bashmatic.bash.version-four-or-later && { + declare -g -A load_cache=() + } + rm -f "${__bashmatic_library_last_sourced}" +} + +---- + ==== `bashmatic.setup` [source,bash] @@ -1351,10 +1392,9 @@ bashmatic.setup () .err "Unable to file BashMatic's library source folder โ€” ${BASHMATIC_LIBDIR}" return 1 fi - bashmatic.source time.sh output.sh output-utils.sh output-repeat-char.sh - bashmatic.source is.sh output-boxes.sh user.sh + declare -a preload_modules=(time.sh output.sh output-utils.sh output-repeat-char.sh output-boxes.sh is.sh user.sh util.sh git.sh file.sh color.sh brew.sh) + bashmatic.source "${preload_modules[@]}" bashmatic.shell-check || return 1 - bashmatic.source util.sh git.sh file.sh color.sh brew.sh bashmatic.source-dir "${BASHMATIC_LIBDIR}" output.unconstrain-screen-width [[ -d ${BASHMATIC_HOME}/.git ]] && bashmatic.auto-update 1>&2 2> /dev/null @@ -1388,16 +1428,57 @@ bashmatic.source () { local __path="${BASHMATIC_LIBDIR}" local file + local total=0 + local files=0 + local last_loaded_at=0 + [[ -f ${__bashmatic_library_last_sourced} ]] && last_loaded_at=$(cat "${__bashmatic_library_last_sourced}") for file in "${@}" do + local t1=$(millis) [[ "${file}" =~ "/" ]] || file="${__path}/${file}" + bashmatic.bash.version-four-or-later && { + local cached_at=${load_cache[${file}]} + cached_at=${cached_at:-0} + local modified_at="$(file.last-modified-millis "${file}")" + [[ ${modified_at} -le ${cached_at} && ${modified_at} -le ${last_loaded_at} ]] && { + is-debug && printf -- "${bldred} (cached) ${txtgrn} โ–ถ๏ธŽ %s${clr}\n" "${file/\/*\//}" + continue + } + } [[ -s "${file}" ]] || { .err "Can't source file ${file} โ€” fils is invalid." return 1 } - [[ -n ${SOURCE_DEBUG} || ${DEBUG} -eq 1 ]] && printf "${txtred}[source] ${bldylw}${file}${clr}...\n" 1>&2 - source "${file}" + if [[ -n ${SOURCE_DEBUG} || ${DEBUG} -eq 1 ]]; then + is-debug && printf -- " ${txtylw} โ–ถ๏ธŽ %s${clr}" "${file/\/*\//}" + source "${file}" > /dev/null + is-debug && { + cursor.rewind -120 + local code=$? + local t2=$(millis) + local duration=$(( t2 - t1 )) + total=$(( total + duration )) + files=$(( files + 1 )) + } + bashmatic.bash.version-four-or-later && { + ((code)) || load_cache[${file}]=${t1} + } + is-debug && { + local color=${txtblu} + [[ ${duration} -gt 20 ]] && color="${bldred}" + printf "${color}${duration}ms [%3d]" "${code}" + printf "\n" + unset t1 + unset t2 + } + else + source "${file}" + fi done + bashmatic.bash.version-four-or-later && { + [[ ${#load_cache[@]} -gt 0 ]] && millis > "${__bashmatic_library_last_sourced}" + } + is-debug && printf "${files} sourced in, taking ${total}ms total.\n" return 0 } @@ -1419,12 +1500,15 @@ bashmatic.source-dir () .err "No files were returned from files.map in " "\n ${bldylw}${folder}" return 1 fi + local -a sources=() for file in "${files[@]}" do local n="$(basename "${file}")" [[ ${n:0:1} == . ]] && continue - bashmatic.source "${file}" && loaded=true + sources+=("${file}") done + bashmatic.source "${sources[@]}" + loaded=true unset files ${loaded} || { .err "Unable to find BashMatic library folder with files:" "${BASHMATIC_LIBDIR}" @@ -4224,6 +4308,17 @@ file.last-modified-date () ---- +==== `file.last-modified-millis` + +[source,bash] +---- +file.last-modified-millis () +{ + echo -n "$(/usr/bin/stat -f %m "$1")000" +} + +---- + ==== `file.last-modified-year` [source,bash] @@ -5355,7 +5450,7 @@ git.sync () cd "${dir}" > /dev/null return 1 } - if ((BASHMATIC_DEBUG)); then + if is-debug; then git.update-repo-if-needed else git.update-repo-if-needed 1>&2 > /dev/null @@ -6325,6 +6420,10 @@ node.install.pin.version () ---- nvm.activate () { + is.an-existing-file .nvmrc && { + export node_version=$(cat .nvmrc | tr -d 'v') + info "Detected node version ${node_version}" + } nvm.load } @@ -12686,7 +12785,7 @@ usage-widget () } ((cache_wipe)) && rm -f "$(.usage-cache-file)" local -a args=("$@") - ((BASHMATIC_DEBUG)) && { + is-debug && { h1 "Got total of ${#args[@]} arguments." } local -a details diff --git a/doc/USAGE.pdf b/doc/USAGE.pdf index f870372..cfa105f 100644 Binary files a/doc/USAGE.pdf and b/doc/USAGE.pdf differ diff --git a/doc/img/bashmatic-install.png b/doc/img/bashmatic-install.png index e6521cd..f59e68c 100644 Binary files a/doc/img/bashmatic-install.png and b/doc/img/bashmatic-install.png differ diff --git a/doc/img/specs-parallel.png b/doc/img/specs-parallel.png index 2f2dbf1..562abfe 100644 Binary files a/doc/img/specs-parallel.png and b/doc/img/specs-parallel.png differ diff --git a/doc/img/specs.png b/doc/img/specs.png index 20ff7c9..3819b6a 100644 Binary files a/doc/img/specs.png and b/doc/img/specs.png differ diff --git a/init.sh b/init.sh index 3b6150f..460059e 100644 --- a/init.sh +++ b/init.sh @@ -1,50 +1,45 @@ -#/usr/bin/env bash +#!/usr/bin/env bash # vim: ft=bash -# file: lib/init.bash +# +# file: lib/init.sh # # @description # This is primary loading file to access ALL or almost all of the Bashmatic functions. # At the time of this writing, this encompasses 826 funcitions, and takes 756ms on my # machine without the caching enabled. # -# The scripts that rely on Bashmatic, will typically have the following three lines at the top: -# [[ -z ${BASHMATIC_HOME} ]] && export BASHMATIC_HOME="${HOME}/.bashmatic" -# [[ -d ${BASHMATIC_HOME} ]] || bash -c "$(curl -fsSL https://bashmatic.re1.re); bashmatic-install " -# [[ -d ${BASHMATIC_HOME} ]] || { -# echo "Can't find Bashmatic, even after attempting an installation." -# echo "Please install Bashmatic with the following command line:" -# echo 'bash -c "$(curl -fsSL https://bashmatic.re1.re); bashmatic-install"' -# exit 1 -# } -# The meaning of this installation procedure is described in detail in the Bashmatic README: -# https://github.com/kigster/bashmatic#4-installing-bashmatic +# @see https://github.com/kigster/bashmatic#4-installing-bashmatic set +ex -export SCRIPT_SOURCE="$(cd "$(/usr/bin/dirname "${BASH_SOURCE[0]:-"${(%):-%x}"}")" || exit 1; pwd -P)" +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +# Initialization and Setup +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” + +export SCRIPT_SOURCE="$(cd "$(/usr/bin/dirname "${BASH_SOURCE[0]:-"${(%):-%x}"}")" || exit 1; pwd -P)" export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/sbin" -export BASHMATIC_HOME=${BASHMATIC_HOME:-${SCRIPT_SOURCE}} -export BASHMATIC_MAIN=${BASHMATIC_HOME}/init.sh +export BASHMATIC_HOME="${BASHMATIC_HOME:-${SCRIPT_SOURCE}}" +export BASHMATIC_MAIN="${BASHMATIC_HOME}/init.sh" if ! declare -f -F source_if_exists >/dev/null; then source "${BASHMATIC_HOME}/.bash_safe_source" fi -function bashmatic.home.valid() { - [[ -n $BASHMATIC_HOME && -d ${BASHMATIC_HOME} && -s ${BASHMATIC_HOME}/init.sh ]] +function __bashmatic.home.is-valid() { + [[ -n ${BASHMATIC_HOME} && -d ${BASHMATIC_HOME} && -s ${BASHMATIC_HOME}/init.sh ]] } -export BASHMATIC_PREFIX="[${bldpur}bashmaticยฎ ${bldylw}${italic}${BASHMATIC_VERSION}]${clr} " +export BASHMATIC_PREFIX="${txtred}๎‚ฒ${txtwht}${bakred} BASHMATICโ„ข ${txtblk}${bakylw} ยฎ 2015-2022 Konstantin Gredeskoul ${txtwht}${bakblu} Version ${BASHMATIC_VERSION}${clr}${txtblu}๎‚ฐ" # resolve BASHMATIC_HOME if necessary -bashmatic.home.valid || { +__bashmatic.home.is-valid || { log.inf "Resolving BASHMATIC_HOME as the current one is invalid: ${BASHMATIC_HOME}" if [[ "${SHELL_COMMAND}" =~ zsh ]]; then - ((BASHMATIC_DEBUG)) && \ + is-debug && \ printf "${BASHMATIC_PREFIX} Detected zsh version ${ZSH_VERSION}, source=$0:A\n" export BASHMATIC_HOME="$(/usr/bin/dirname "$0:A")" elif [[ "${SHELL_COMMAND}" =~ bash ]]; then - ((BASHMATIC_DEBUG)) && \ + is-debug && \ printf "${BASHMATIC_PREFIX} Detected bash version ${BASH_VERSION}, source=${BASH_SOURCE[0]}\n" export BASHMATIC_HOME="$(cd -P -- "$(/usr/bin/dirname -- "${BASH_SOURCE[0]}")" && printf '%s\n' "$(pwd -P)")" else @@ -55,9 +50,9 @@ bashmatic.home.valid || { log.inf "Resolved BASHMATIC_HOME to [${BASHMATIC_HOME}]" } -bashmatic.home.valid || { +__bashmatic.home.is-valid || { log.err "ERROR: Can't determine BASHMATIC installation path." - .bashmatic.print-path-config + __bashmatic.print-path-config return 1 } @@ -71,27 +66,31 @@ export BASHMATIC_OS="$($BASHMATIC_UNAME -s | /usr/bin/tr '[:upper:]' '[:lower:]' # grab our shell command export SHELL_COMMAND="$(/bin/ps -p $$ -o args | ${GREP_CMD} -v -E 'ARGS|COMMAND' | /usr/bin/cut -d ' ' -f 1 | sed -E 's/-//g')" +function is-debug() { + [[ $((DEBUG + BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) -gt 0 ]] +} + function log.err() { - ((BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) || return 0 + is-debug || return 0 printf "${blderr}[ERROR] --> ${bldylw}$*${clr}\n" } function log.inf() { - ((BASHMATIC_DEBUG + BASHMATIC_PATH_DEBUG)) || return 0 + is-debug || return 0 printf "${bldblu}[INFO] --> ${bldgrn}$*${clr}\n" } -function .bashmatic.print-path-config { - ((BASHMATIC_PATH_DEBUG)) || return 0 +function __bashmatic.print-path-config() { + printf "${BASHMATIC_PREFIX}\n" + is-debug || return 0 echo "BASHMATIC_HOME[${BASHMATIC_HOME}]" echo "BASHMATIC_MAIN[${BASHMATIC_MAIN}]" command -v pstree >/dev/null && $(command -v pstree) -p $$ -w } - # @description -function bashmatic.dealias() { +function __bashmatic.dealias() { for cmd in printf echo grep tr ps kill ; do unalias ${cmd} 2>/dev/null >/dev/null || true; done if [[ -f "${BASHMATIC_HOME}/.bash_safe_source" ]] ; then @@ -100,44 +99,32 @@ function bashmatic.dealias() { fi } -function .bashmatic.load-time() { - [[ -n $(type millis 2>/dev/null) ]] && return 0 - [[ -f ${BASHMATIC_HOME}/lib/time.sh ]] && source "${BASHMATIC_HOME}/lib/time.sh" +function __bashmatic.load-time() { + [[ -n $(type millis 2>/dev/null) ]] || source "${BASHMATIC_HOME}/lib/time.sh" export __bashmatic_start_time="$(millis)" } -function source-if-exists() { - [[ -n $(type source_if_exists 2>/dev/null) ]] || source "${BASHMATIC_HOME}/.bash_safe_source" - source_if_exists "$@" -} - -# Set initial state to 0 -# This can not be exported, because then subshells don't initialize correctly -export __bashmatic_load_state=${__bashmatic_load_state:=0} -function bashmatic.is-loaded() { - [[ $SHELL =~ bash ]] && ((__bashmatic_load_state)) - return 0 -} - -function bashmatic.set-is-loaded() { - export __bashmatic_load_state=1 +function __bashmatic.init.darwin() { + local -a required_binares=(gdate gsed) + for binary in "${required_binares[@]}"; do + command -v "${binary}" >/dev/null || brew install "${binary}" + done } -function bashmatic.set-is-not-loaded() { - export __bashmatic_load_state=0 +function __bashmatic.init.linux() { + return 0 } -function bashmatic.init-core() { - log.inf calling .bashhmatic.pre-init - bashmatic.dealias +function __bashmatic.init-core() { + __bashmatic.dealias # DEFINE CORE VARIABLES export BASHMATIC_URL="https://github.com/kigster/bashmatic" export BASHMATIC_OS="${BASHMATIC_OS}" # shellcheck disable=2046 - export BASHMATIC_TEMP="/tmp/${USER}/.bashmatic" + export BASHMATIC_TEMP="/tmp/${USER}/__bashmatic" [[ -d ${BASHMATIC_TEMP} ]] || mkdir -p "${BASHMATIC_TEMP}" if [[ -f ${BASHMATIC_HOME}/init.sh ]]; then @@ -147,37 +134,13 @@ function bashmatic.init-core() { return 1 fi - [[ -n ${BASHMATIC_DEBUG} ]] && { - [[ -f "${BASHMATIC_HOME}/lib/time.sh" ]] && source "${BASHMATIC_HOME}/lib/time.sh" - export __bashmatic_start_time=$(millis) + is-debug && { + printf "${BASHMATIC_PREFIX}\n" + __bashmatic.load-time } - # If defined BASHMATIC_AUTOLOAD_FILES, we source these files together with BASHMATIC - for _init in ${BASHMATIC_AUTOLOAD_FILES}; do - [[ -s "${PWD}/${_init}" ]] && { - [[ -n ${BASHMATIC_DEBUG} ]] && - printf "${BASHMATIC_PREFIX} sourcing in <โ€”โ€” [${bldblu}${PWD}/${_init}${clr}]" - source "${PWD}/${_init}" - } - done - - # Load BASHMATIC library - ((BASHMATIC_DEBUG)) && { - printf "${BASHMATIC_PREFIX} evaluating all shell files under ${bldylw}${BASHMATIC_HOME}/lib...${clr}\n" - } - - # for f in $(find "${BASHMATIC_HOME}/lib" -name '[a-z]*.sh' -type f -print); do - # ((BASHMATIC_DEBUG)) && { - # printf "${BASHMATIC_PREFIX} loading ${bldylw}%-50s${clr}\n" "$f" - # } - # source $f - # done - # - # for f in $(find "${BASHMATIC_HOME}/lib" -name '[a-z]*.sh' -type f -print); do - # source $f - # done - - eval "$(/bin/cat "${BASHMATIC_HOME}"/lib/*.sh)" + local init_func="__bashmatic.init.${BASHMATIC_OS}" + [[ -n $(type "${init_func}" 2>/dev/null) ]] && ${init_func} # shellcheck disable=SC2155 [[ ${PATH} =~ ${BASHMATIC_HOME}/bin ]] || export PATH=${PATH}:${BASHMATIC_HOME}/bin @@ -190,129 +153,54 @@ function bashmatic.init-core() { # Future CLI flags, but for now just vars export LibGit__QuietUpdate=${LibGit__QuietUpdate:-1} export LibGit__ForceUpdate=${LibGit__ForceUpdate:-0} -} - -function .bashmatic.init.darwin() { - local -a required_binares - required_binares=(brew gdate gsed) - local some_missing=0 - for binary in "${required_binares[@]}"; do - command -v "${binary}" >/dev/null && continue - some_missing=$((some_mising + 1)) - done - - if [[ ${some_missing} -gt 0 ]]; then - set +e - source "${BASHMATIC_HOME}/bin/bashmatic-install" >/dev/null - fi - # shellcheck disable=SC2155 - [[ ${PATH} =~ ${BASHMATIC_HOME}/bin ]] || export PATH=${PATH}:${BASHMATIC_HOME}/bin - unalias grep 2>/dev/null || true - export GrepCommand="$(command -v grep) -E " - export True=1 - export False=0 - export LoadedShown=${LoadedShown:-1} + # LOAD ALL BASHMATIC SCRIPTS AT ONCE + # This is the fastest method that only takes about 110ms + eval "$(/bin/cat "${BASHMATIC_HOME}"/lib/*.sh)" - # Future CLI flags, but for now just vars - export LibGit__QuietUpdate=${LibGit__QuietUpdate:-1} - export LibGit__ForceUpdate=${LibGit__ForceUpdate:-0} + is-debug && { + local __bashmatic_end_time=$(millis) + h.yellow "Bashmatic library took $((__bashmatic_end_time - __bashmatic_start_time)) milliseconds to load." + } } -function .bashmatic.init.darwin() { - local -a required_binares - required_binares=(brew gdate gsed) - local some_missing=0 - for binary in "${required_binares[@]}"; do - command -v "${binary}" >/dev/null && continue - some_missing=$((some_mising + 1)) - done - - if [[ ${some_missing} -gt 0 ]]; then - set +e - # eval "$(curl -fsSL https://bashmatic.re1.re)" >/dev/null - source "${BASHMATIC_HOME}/bin/bashmatic-install" >/dev/null - local func=".install.darwin-requirements" - [[ -z $(type -f ${func}) ]] || eval "${func}" - fi -} -function .bashmatic.init.linux() { - return 0 -} +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +# Argument Parsing in case they loaded us with , eg. . +# source init.sh reload +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” -function bashmatic.init.paths() { - declare -a paths=() - for p in $(path.dirs "${PATH}"); do - paths+=("$p") +function __bashmatic.parse-arguments() { + for file in "$@"; do + [[ $0 =~ $file ]] && { + log.inf "skipping the first file ${file}" + continue + } + local env_file="${BASHMATIC_HOME}/.envrc.${file}" + if [[ -f $env_file ]]; then + log.inf "sourcing env file ${env_file}" + source "$env_file" + fi + if [[ "$file" =~ (reload|force|refresh) ]]; then + log.inf "setting to is-not-loaded" + fi done - - ((BASHMATIC_PATH_DEBUG)) && { - h3bg "The current \$PATH components are:" - for p in "${paths[@]}"; do - printf " โ€ข [$(printf "%40.40s\n" "${p}")]\n" - done - } - return 0 } +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +# Public functions +#โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” -function bashmatic.init() { - for file in "$@"; do - [[ $0 =~ $file ]] && { - log.inf "skipping the first file ${file}" - continue - } - local env_file="${BASHMATIC_HOME}/.envrc.${file}" - if [[ -f $env_file ]]; then - log.inf "sourcing env file ${env_file}" - source "$env_file" - fi - if [[ "$file" =~ (reload|force|refresh) ]]; then - log.inf "setting to is-not-loaded" - bashmatic.set-is-not-loaded - fi - done - - log.inf "calling bashhmatic.init-core" - bashmatic.init-core - log.inf "calling bashhmatic.is-loaded" - bashmatic.is-loaded || { - log.inf "calling bashhmatic.init" - bashmatic.init "$@" - } - - local init_func=".bashmatic.init.${BASHMATIC_OS}" - - [[ -n $(type "${init_func}" 2>/dev/null) ]] && ${init_func} - - local setup_script="${BASHMATIC_LIBDIR}/bashmatic.sh" - - if [[ -s "${setup_script}" ]]; then - source "${setup_script}" - bashmatic.setup - local code=$? - ((code)) && printf "${BASHMATIC_PREFIX} Function ${bldred}bashmatic.setup${clr} returned exit code [${code}]" - else - log.err " โ›”๏ธ Bashmatic appears to be broken:" - log.err " File not found: ${setup_script}" - - .bashmatic.print-path-config - return 1 - fi - - if [[ -n ${BASHMATIC_DEBUG} ]]; then - local __bashmatic_end_time=$(millis) - ((BASHMATIC_DEBUG)) && notice "Bashmatic library took $((__bashmatic_end_time - __bashmatic_start_time)) milliseconds to load." - fi - - unset __bashmatic_end_time - unset __bashmatic_start_time - - bashmatic.set-is-loaded +function source-if-exists() { + [[ -n $(type source_if_exists 2>/dev/null) ]] || source "${BASHMATIC_HOME}/.bash_safe_source" + source_if_exists "$@" +} - export BASHMATIC_CACHE_INIT=1 +function bashmatic.load() { + __bashmatic.parse-arguments "$@" + __bashmatic.init-core + return 0 } -bashmatic.init "$@" +bashmatic.load "$@" diff --git a/lib/bashmatic.sh b/lib/bashmatic.sh index f20847a..dd33774 100644 --- a/lib/bashmatic.sh +++ b/lib/bashmatic.sh @@ -6,42 +6,57 @@ # True if .envrc.local file is present. We take it as a sign # you may be developing bashmatic. -__bashmatic_warning_notification=${HOME}/.bashmatic/.developer-warned +export __bashmatic_warning_notification=${BASHMATIC_HOME}/.developer-warned +export __bashmatic_library_last_sourced=${BASHMATIC_HOME}/.last-loaded -bashmatic.cd-into() { +[[ -n $(type millis 2>/dev/null) ]] || source "${BASHMATIC_HOME}"/lib/time.sh +[[ -n $(type file.last-modified-millis 2>/dev/null) ]] || source "${BASHMATIC_HOME}"/lib/file.sh + +function bashmatic.cd-into() { [[ -d ${BASHMATIC_HOME} ]] || return 1 cd "${BASHMATIC_HOME}" || exit 1 } -bashmatic.current-os() { +function bashmatic.current-os() { export AppCurrentOS="$(uname -s | tr '[:upper:]' '[:lower:]')" printf "%s" "${AppCurrentOS}" } - # @descripion True if .envrc.local file is present. We take it as a sign # you may be developing bashmatic. -bashmatic.is-developer() { +function bashmatic.is-developer() { [[ ${BASHMATIC_DEVELOPER} -eq 1 || -f ${BASHMATIC_HOME}/.envrc.local ]] } -bashmatic.reload() { - bashmatic.set-is-not-loaded +function bashmatic.debug-on() { + export DEBUG=1 + export BASHMATIC_DEBUG=1 + export BASHMATIC_PATH_DEBUG=1 +} + +function bashmatic.debug-off() { + unset DEBUG + unset BASHMATIC_DEBUG + unset BASHMATIC_PATH_DEBUG +} + +function bashmatic.reload() { + __bashmatic.set-is-not-loaded source "${BASHMATIC_HOME}/.envrc.no-debug" source "${BASHMATIC_INIT}" } -bashmatic.reload-debug() { - bashmatic.set-is-not-loaded +function bashmatic.reload-debug() { + __bashmatic.set-is-not-loaded source "${BASHMATIC_HOME}/.envrc.debug" source "${BASHMATIC_INIT}" } -bashmatic.version() { +function bashmatic.version() { cat "$(dirname "${BASHMATIC_INIT}")/.version" } -bashmatic.load-at-login() { +function bashmatic.load-at-login() { local file="${1}" [[ -z ${file} ]] && file="$(user.login-shell-init-file)" @@ -56,7 +71,7 @@ bashmatic.load-at-login() { } } -bashmatic.functions-from() { +function bashmatic.functions-from() { local pattern="${1}" [[ -n ${pattern} ]] && shift @@ -85,15 +100,15 @@ bashmatic.functions-from() { } # pass number of columns to print, default is 2 -bashmatic.functions() { +function bashmatic.functions() { bashmatic.functions-from '*.sh' "$@" } -bashmatic.functions.output() { +function bashmatic.functions.output() { bashmatic.functions-from 'output.sh' "$@" } -bashmatic.functions.runtime() { +function bashmatic.functions.runtime() { bashmatic.functions-from 'run*.sh' "$@" } @@ -110,22 +125,91 @@ function bashmatic.bash.exit-unless-version-four-or-later() { bashmatic.bash.version-four-or-later || { error "Sorry, this functionality requires BASH version 4 or later." exit 1 >/dev/null + } +} + +function __rnd() { + echo -n $(( (1009 * RANDOM) % 44311 + (917 * RANDOM) % 34411 )) +} + +bashmatic.bash.version-four-or-later && { + [[ ${#load_cache[@]} -gt 0 ]] || { + declare -g -A load_cache=() } } +function bashmatic.reset.cache() { + unset load_cache + bashmatic.bash.version-four-or-later && { + declare -g -A load_cache=() + } + rm -f "${__bashmatic_library_last_sourced}" +} -bashmatic.source() { +function bashmatic.source() { local __path="${BASHMATIC_LIBDIR}" local file + local total=0 + local files=0 + + local last_loaded_at=0 + [[ -f ${__bashmatic_library_last_sourced} ]] && last_loaded_at=$(cat "${__bashmatic_library_last_sourced}") + for file in "${@}"; do + local t1=$(millis) + [[ "${file}" =~ "/" ]] || file="${__path}/${file}" + + bashmatic.bash.version-four-or-later && { + local cached_at=${load_cache[${file}]} + cached_at=${cached_at:-0} + local modified_at="$(file.last-modified-millis "${file}")" + [[ ${modified_at} -le ${cached_at} && ${modified_at} -le ${last_loaded_at} ]] && { + is-debug && printf -- "${bldred} (cached) ${txtgrn} โ–ถ๏ธŽ %s${clr}\n" "${file/\/*\//}" + continue + } + } + [[ -s "${file}" ]] || { .err "Can't source file ${file} โ€” fils is invalid." return 1 } - [[ -n ${SOURCE_DEBUG} || ${DEBUG} -eq 1 ]] && printf "${txtred}[source] ${bldylw}${file}${clr}...\n" >&2 - source "${file}" + + if [[ -n ${SOURCE_DEBUG} || ${DEBUG} -eq 1 ]]; then + is-debug && printf -- " ${txtylw} โ–ถ๏ธŽ %s${clr}" "${file/\/*\//}" + source "${file}" >/dev/null + is-debug && { + cursor.rewind -120 + local code=$? + local t2=$(millis) + local duration=$(( t2 - t1 )) + total=$(( total + duration )) + files=$(( files + 1 )) + } + + bashmatic.bash.version-four-or-later && { + ((code)) || load_cache[${file}]=${t1} + } + + is-debug && { + local color=${txtblu} + [[ ${duration} -gt 20 ]] && color="${bldred}" + printf "${color}${duration}ms [%3d]" "${code}" + printf "\n" + unset t1 + unset t2 + } + else + source "${file}" + fi done + + bashmatic.bash.version-four-or-later && { + # save the current timestamp into the cache marker + [[ ${#load_cache[@]} -gt 0 ]] && millis > "${__bashmatic_library_last_sourced}" + } + + is-debug && printf "${files} sourced in, taking ${total}ms total.\n" return 0 } @@ -135,7 +219,7 @@ bashmatic.source() { printf "${bldred} ERROR:\n${txtred} $*%s\n" "" >&2 } -bashmatic.source-dir() { +function bashmatic.source-dir() { local folder="${1}" local loaded=false local file @@ -149,13 +233,17 @@ bashmatic.source-dir() { return 1 fi + local -a sources=() for file in "${files[@]}"; do local n="$(basename "${file}")" [[ ${n:0:1} == . ]] && continue - bashmatic.source "${file}" && loaded=true + sources+=("${file}") done + bashmatic.source "${sources[@]}" + loaded=true + unset files ${loaded} || { @@ -181,7 +269,7 @@ function bashmatic.shell-check() { fi } -bashmatic.setup() { +function bashmatic.setup() { [[ -z ${BashMatic__Downloader} && -n $(command -v curl) ]] && export BashMatic__Downloader="curl -fsSL --connect-timeout 5 " @@ -193,10 +281,23 @@ bashmatic.setup() { return 1 fi - bashmatic.source time.sh output.sh output-utils.sh output-repeat-char.sh - bashmatic.source is.sh output-boxes.sh user.sh + declare -a preload_modules=( + time.sh + output.sh + output-utils.sh + output-repeat-char.sh + output-boxes.sh + is.sh + user.sh + util.sh + git.sh + file.sh + color.sh + brew.sh + ) + + bashmatic.source "${preload_modules[@]}" bashmatic.shell-check || return 1 - bashmatic.source util.sh git.sh file.sh color.sh brew.sh bashmatic.source-dir "${BASHMATIC_LIBDIR}" output.unconstrain-screen-width diff --git a/lib/file.sh b/lib/file.sh index 5deeab3..8c9fcd0 100644 --- a/lib/file.sh +++ b/lib/file.sh @@ -1,7 +1,7 @@ #!/usr/local/bin/env bash # vim: ft=bash -source "${BASHMATIC_LIBDIR}/file-helpers.sh" +source "${BASHMATIC_HOME}/lib/file-helpers.sh" # @description Creates a temporary file and returns it as STDOUT # shellcheck disable=SC2120 @@ -189,6 +189,10 @@ file.last-modified-year() { stat -f "%Sm" -t "%Y" "$1" } +file.last-modified-millis() { + echo -n "$(/usr/bin/stat -f %m "$1")000" +} + # Return one field of stat -s call on a given file. file.stat() { local file="$1" diff --git a/lib/ftrace.sh b/lib/ftrace.sh index aa5f032..d809043 100644 --- a/lib/ftrace.sh +++ b/lib/ftrace.sh @@ -29,7 +29,6 @@ # } export __LibTrace__StackLevel=0 - ftrace-on() { export TraceON=true } diff --git a/lib/git.sh b/lib/git.sh index 9b759f7..69fb83d 100644 --- a/lib/git.sh +++ b/lib/git.sh @@ -71,7 +71,7 @@ function git.sync() { return 1 } - if ((BASHMATIC_DEBUG)); then + if is-debug; then git.update-repo-if-needed else git.update-repo-if-needed >&2 1>/dev/null diff --git a/lib/nvm.sh b/lib/nvm.sh index a18a977..3f3acda 100644 --- a/lib/nvm.sh +++ b/lib/nvm.sh @@ -89,12 +89,11 @@ function node.install.pin.version() { } function nvm.activate() { + is.an-existing-file .nvmrc && { + export node_version=$(cat .nvmrc | tr -d 'v') + info "Detected node version ${node_version}" + } nvm.load } -declare node_version - -is.an-existing-file .nvmrc && { - export node_version=$(cat .nvmrc | tr -d 'v') -} diff --git a/lib/usage.sh b/lib/usage.sh index 9f42c43..1299f7e 100644 --- a/lib/usage.sh +++ b/lib/usage.sh @@ -230,7 +230,7 @@ function usage-widget() { ((cache_wipe)) && rm -f "$(.usage-cache-file)" local -a args=("$@") - ((BASHMATIC_DEBUG)) && { + is-debug && { h1 "Got total of ${#args[@]} arguments." } diff --git a/test/git_test.bats b/test/git_test.bats index f0a8a75..ba57cc5 100644 --- a/test/git_test.bats +++ b/test/git_test.bats @@ -15,17 +15,25 @@ source lib/util.sh set -e +export git_skip=skip +if [[ -n $(git tag -l 2>/dev/null) ]]; then + export git_skip= +fi + @test "git.repo.latest-local-tag regex" { + ${git_skip} tag=$(git.repo.latest-local-tag) [[ ${tag} =~ ^v[0-9]+.[0-9]+.[0-9]+$ ]] } @test "git.repo.next-local-tag regex" { + ${git_skip} ntag=$(git.repo.next-local-tag) [[ ${ntag} =~ ^v[0-9]+.[0-9]+.[0-9]+$ ]] } @test "git.repo.next-local-tag increment" { + ${git_skip} otag=$(git.repo.latest-local-tag) ntag=$(git.repo.next-local-tag) over=$(util.ver-to-i ${otag}) @@ -56,6 +64,7 @@ set -e @test "git.repo-is-clean() when clean" { + ${git_skip} if [[ -n $CI ]]; then git.config.kigster 2>/dev/null pwd=$(pwd) diff --git a/test/helpers/test-lib.sh b/test/helpers/test-lib.sh index b501764..8259754 100755 --- a/test/helpers/test-lib.sh +++ b/test/helpers/test-lib.sh @@ -312,7 +312,7 @@ export flag_file_count_failed=0 export flag_keep_going_on_error=0 export flag_bats_args="-p" export flag_bats_reinstall=0 -export flag_parallel_tests=0 +export flag_parallel_tests=1 function specs.parse-opts() { trap 'printf "\n\n\n${bldred}Ctrl-C detected, aborting tests.${clr}\n\n"; exit 1' SIGINT @@ -333,10 +333,9 @@ function specs.parse-opts() { shift export flag_bats_reinstall=1 ;; - -p | --parallel) + -P | --no-parallel) shift - export flag_parallel_tests=1 - export flag_bats_args="-p" + export flag_parallel_tests=0 ;; -t | --taps) shift @@ -409,8 +408,7 @@ function specs.usage() { hr echo printf "${bldylw}OPTIONS\n${txtpur}" - printf " -p | --parallel Runs all tests in parallel using ${bldylw}parallel${txtpur} dependency.\n" - printf " This may speed up your test suite by 2-3x\n\n" + printf " -P | --no-parallel Runs all tests sequentially instead of ${bldylw}parallel${txtpur}.\n" printf " -i | --install METHOD Install Bats using the provided methjod.\n" printf " Supported methods: ${bldylw}${Bashmatic__BatsInstallMethods}${txtpur}\n\n" printf " -r | --reinstall Reinstall Bats framework before running\n"