From 60778c7d2da9de979b009b371b9cde0c9f7c04e4 Mon Sep 17 00:00:00 2001 From: Peter Urda Date: Sat, 22 Nov 2025 02:58:14 -0800 Subject: [PATCH] urda.bash gets a fresh coat of paint in 2025 Project Seabiscuit --- .github/pull_request_template.md | 11 + .github/workflows/testing.yaml | 8 +- CHANGELOG.md | 36 ++++ LICENSE | 21 ++ Makefile | 23 +- README.md | 8 + VERSION | 1 + bash_aliases | 28 +-- bash_exports | 6 +- bash_history | 1 + bash_linux | 10 + bash_osx | 119 +++++----- bash_secrets | 1 + bashrc | 358 +++++++++++++++++++------------ 14 files changed, 396 insertions(+), 235 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 VERSION create mode 100644 bash_linux diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..8ef8d23 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## Pull Request Checklist + + + +- [ ] Are you the repo owner? `urda.bash` is not accepting outside pull requests at this time. +- [ ] Did you update `URDABASH_VERSION` in `bashrc`? In `VERSION`? +- [ ] Did `make test` pass? +- [ ] Do all files have headers? +- [ ] Did `make copy` get updated (if required)? +- [ ] Did `make diffs` get updated (if required)? +- [ ] Did `make test` get updated (if required)? diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index 823e7d4..aea627f 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -12,10 +12,12 @@ on: jobs: make-test: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + # https://github.com/marketplace/actions/checkout + - name: Checkout ⬇️ + uses: actions/checkout@v5 - - name: urda.bash Testing + - name: urda.bash Testing 🧪 run: make test diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6bd8996 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,36 @@ +# CHANGELOG + +## 1.0.0 + +- In late 2025, `urda.bash` got a fresh coat of paint. +- `bashrc` + - Introduced `URDABASH_*` variables as `readonly`. + - Setup `XDG` environment variables. + - Cleaned up sourcing with `_source_if_exists`. + - Setup `case` against `uname` result for OS imports. + - Cleaned up `pyenv` import, removed `workon` and `deactivate`. Use `.python-version` to "switch `venvs`. + - `_set_ps1` got a full overhaul. Different "lines" are now functions, and are built in a single `prompt` result. + - `PROMPT_COMMAND` is no longer just replaced with our `ps1` command. + - `urda.bash` can now check GitHub for new versions (requires `curl` or `wget`). +- `bash_aliases` + - Moved `colordiff` up to just `alias diff` setup area. + - Removed `/usr/bin/dircolors` section (for now). +- `bash_exports` + - Moved `CLICOLOR` to `bash_osx`. + - Added `GPG` values. +- `bash_linux` + - Joins the `urda.bash` family for any `Linux` needs. +- `bash_osx` + - Added simple "guard" to top of file. + - Updated `env` variables. + - Cleaned up `brew` and related tooling imports. + - Cleaned up `update_brew` function. +- `Makefile` + - Now supports `make version-check` and will be invoked with `make test` as well. +- `VERSION` + - Version file created for remote version checking. +- GitHub + - Added a `pull_request_template` mostly for tracking releases. + - Updated `testing.yaml` to latest `runs-on` and `actions/checkout +- General, Other + - Added a `LICENSE` file to repo, just set to `MIT`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..388dc76 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Peter Urda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile index d819d62..d343616 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,13 @@ DIFF := $(firstword $(shell which colordiff diff)) DIFF_ARGS := -u SHELLCHECK := $(shell which shellcheck) -SHELLCHECK_ARGS := --shell=bash -e SC1090 +SHELLCHECK_ARGS := --shell=bash -e SC1090 -e SC2155 SHELLCHECK_FILES := ./bash* ######################################################################################################################## # Commands ######################################################################################################################## - # `make help` needs to be first so it is ran when a bare `make` is called. .PHONY: help help: # Show this help screen @@ -25,6 +24,7 @@ help: # Show this help screen sort -k1,1 |\ awk 'BEGIN {FS = ":.*?# "}; {printf "\033[1m%-30s\033[0m %s\n", $$1, $$2}' +######################################################################################################################## .PHONY: copy copy: # Copy the local bash configs into your home directory (DESTRUCTIVE). @@ -32,9 +32,9 @@ copy: # Copy the local bash configs into your home directory (DESTRUCTIVE). cp ./bash_aliases ~/.bash_aliases cp ./bash_exports ~/.bash_exports cp ./bash_profile ~/.bash_profile + cp ./bash_linux ~/.bash_linux cp ./bash_osx ~/.bash_osx - .PHONY: diffs diffs: # Run a `diff` against your local shell files against this repo's shell files. @echo "diff ./bashrc ~/.bashrc" @@ -45,10 +45,23 @@ diffs: # Run a `diff` against your local shell files against this repo's shell f @$(DIFF) $(DIFF_ARGS) ./bash_exports ~/.bash_exports || : @echo "diff ./bash_profile ~/.bash_profile" @$(DIFF) $(DIFF_ARGS) ./bash_profile ~/.bash_profile || : + @echo "diff ./bash_linux ~/.bash_linux" + @$(DIFF) $(DIFF_ARGS) ./bash_linux ~/.bash_linux || : @echo "diff ./bash_osx ~/.bash_osx" @$(DIFF) $(DIFF_ARGS) ./bash_osx ~/.bash_osx || : - .PHONY: test -test: # Test and check shell scripts for issues. +test: version-check # Test and check shell scripts for issues. $(SHELLCHECK) $(SHELLCHECK_ARGS) $(SHELLCHECK_FILES) + +.PHONY: version-check +version-check: # Check the reported version and code version. + @file_ver=$$(cat VERSION); \ + var_ver=$$(awk -F\" '/^readonly URDABASH_VERSION=/{print $$2}' bashrc); \ + if [ "$$file_ver" = "$$var_ver" ]; then \ + echo "VERSION matches '$$var_ver'"; \ + exit 0; \ + else \ + echo "VERSION mismatch: VERSION='$$file_ver', bashrc='$$var_ver'"; \ + exit 1; \ + fi diff --git a/README.md b/README.md index f048e91..117dbeb 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,11 @@ After you clone this repo, you can also run a quick `diff` that will compare you ```bash make diffs ``` + +## Running tests + +This will also run a `make version-check`. + +```bash +make test +``` diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/bash_aliases b/bash_aliases index 1e46b56..98d24af 100644 --- a/bash_aliases +++ b/bash_aliases @@ -6,7 +6,11 @@ alias clear='printf "\033c"' # better looking diff -alias diff='diff -u' +if hash colordiff 2>/dev/null; then + alias diff='colordiff -u' +else + alias diff='diff -u' +fi # get a UUID on demand alias get_uuid='python3 -c "import uuid;print(uuid.uuid4())"' @@ -16,25 +20,3 @@ alias ll='ls -hlFs' # When using sudo, use alias expansion (otherwise sudo ignores your aliases) alias sudo='sudo ' - -# Enable colors for certain commands -if [ -x /usr/bin/dircolors ]; then - if [ -r ~/.dircolors ]; then - eval "$(dircolors -b ~/.dircolors)" - else - eval "$(dircolors -b)" - fi - - alias ls='ls --color=auto' - alias dir='dir --color=auto' - alias vdir='vdir --color=auto' - - alias grep='grep --color=auto' - alias fgrep='fgrep --color=auto' - alias egrep='egrep --color=auto' - - # If colordiff is available, use it in place of diff - if hash colordiff 2>/dev/null; then - alias diff='colordiff -u' - fi -fi diff --git a/bash_exports b/bash_exports index 6e27b9d..e628620 100644 --- a/bash_exports +++ b/bash_exports @@ -2,14 +2,14 @@ # shellcheck shell=bash # ~/.bash_exports: General Exports -# General Settings -export CLICOLOR=YES - # Git Settings export GIT_PS1_SHOWDIRTYSTATE=true export GIT_PS1_SHOWSTASHSTATE=true export GIT_PS1_SHOWUNTRACKEDFILES=true +# GPG +export GPG_TTY="$(tty)" + # History Settings export HISTCONTROL=ignoreboth export HISTFILESIZE=2000 diff --git a/bash_history b/bash_history index c6faf66..e570dcf 100644 --- a/bash_history +++ b/bash_history @@ -1,5 +1,6 @@ # -*- bash -*- # shellcheck shell=bash +# ~/.bash_history: Bash command history # (╯°□°)╯︵ ┻━┻ # You serious ? diff --git a/bash_linux b/bash_linux new file mode 100644 index 0000000..a8388cb --- /dev/null +++ b/bash_linux @@ -0,0 +1,10 @@ +# -*- bash -*- +# shellcheck shell=bash +# ~/.bash_linux: Linux specific modifications and tweaks + +[[ $- == *i* ]] || return 0 +[[ "$(uname -s)" == "Linux" ]] || return 0 + +################################################################################ +# Linux Environment Variables +################################################################################ diff --git a/bash_osx b/bash_osx index 2cd1c25..ac6a241 100644 --- a/bash_osx +++ b/bash_osx @@ -1,89 +1,72 @@ # -*- bash -*- # shellcheck shell=bash -# ~/.bash_osx: macOS specific settings +# ~/.bash_osx: macOS specific modifications and tweaks + +[[ $- == *i* ]] || return 0 +[[ "$(uname -s)" == "Darwin" ]] || return 0 ################################################################################ -# macOS Modifications and Tweaks +# macOS Environment Variables ################################################################################ # Silence notice about `zsh`. export BASH_SILENCE_DEPRECATION_WARNING=1 - -# Ignore .DS_Store files in tab complete. See also: -# -# https://apple.stackexchange.com/questions/71398/ -# https://apple.stackexchange.com/a/71402/59583 -# -export FIGNORE=$FIGNORE:DS_Store - -# Ignore .Trash folders -export FIGNORE=$FIGNORE:.Trash - -# Fix Pinentry -# -# https://gpgtools.tenderapp.com/discussions/problems/50502-how-to-avoid-pinentry-pop-up-window-when-using-terminal -# -GPG_TTY="$(tty)" -export GPG_TTY +# Enable CLI Colors +export CLICOLOR=YES +# Tab ignores +export FIGNORE=${FIGNORE:+${FIGNORE}:}.DS_Store:.Trash +# GPG Pin Entry CLI setup export PINENTRY_USER_DATA="USE_CURSES=1" -# Tab complete symlinks. See also: -# -# https://superuser.com/questions/271626/ -# https://superuser.com/questions/155167/ -# https://superuser.com/a/271677/10121 -# -bind 'set mark-symlinked-directories on' - ################################################################################ -# Homebrew Modifications and Tweaks +# Tooling ################################################################################ -# Address "-bash: brew: command not found" on M1 macs -if [[ $(uname -m) == 'arm64' ]]; then - eval "$(/opt/homebrew/bin/brew shellenv)" -fi +# ------------------------------ +# Homebrew +# ------------------------------ +# urda.bash supports Homebrew on Apple Silicon only. +if [ -x /opt/homebrew/bin/brew ]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + + # Hide Homebrew Env Hints + export HOMEBREW_NO_ENV_HINTS=true + + brew_prefix="$(brew --prefix 2>/dev/null)" + nvm_prefix="$(brew --prefix nvm 2>/dev/null)" -# Homebrew's sbin fix -export PATH="/usr/local/sbin:$PATH" + # Custom Homebrew updater function + update_brew() { + bold=$(tput bold 2>/dev/null || printf '') + normal=$(tput sgr0 2>/dev/null || printf '') + brew --version && + echo "${bold} > brew update${normal}" && brew update && + echo "${bold} > brew upgrade${normal}" && brew upgrade && + echo "${bold} > brew autoremove${normal}" && brew autoremove && + echo "${bold} > brew cleanup${normal}" && brew cleanup && + echo "${bold} > brew doctor${normal}" && brew doctor; + } -# Hide Homebrew Env Hints -export HOMEBREW_NO_ENV_HINTS=true + # ------------------------------ + # Homebrew extras + # ------------------------------ -# Attempt to load brew - bash completions -if [ -f "$(brew --prefix)/etc/bash_completion" ]; then + # ------------------------------ + # NVM (via Homebrew) + # ------------------------------ + if [ -n "${nvm_prefix}" ] && [ -f "${nvm_prefix}/nvm.sh" ]; then # shellcheck source=/dev/null - source "$(brew --prefix)/etc/bash_completion" -fi + source "${nvm_prefix}/nvm.sh" + fi -# Configure NVM -if [ -f "$(brew --prefix nvm)/nvm.sh" ]; then + # ------------------------------ + # Bash completions + # ------------------------------ + if [ -n "${brew_prefix}" ] && [ -f "${brew_prefix}/etc/bash_completion" ]; then # shellcheck source=/dev/null - source "$(brew --prefix nvm)/nvm.sh" -fi + source "${brew_prefix}/etc/bash_completion" + fi -# Configure Rust -if [ -d "$(brew --prefix rustup)/bin" ]; then - RUST_BIN="$(brew --prefix rustup)/bin" - export PATH="$PATH:$RUST_BIN" + unset brew_prefix + unset nvm_prefix fi - -################################################################################ -# macOS Aliases and Functions -################################################################################ - -update_brew() { - bold=$(tput bold); - normal=$(tput sgr0); - brew --version && - echo "${bold} > brew update${normal}" && - brew update && - echo "${bold} > brew upgrade${normal}" && - brew upgrade && - echo "${bold} > brew autoremove${normal}" && - brew autoremove && - echo "${bold} > brew cleanup${normal}" && - brew cleanup && - echo "${bold} > brew doctor${normal}" && - brew doctor; -} diff --git a/bash_secrets b/bash_secrets index c6faf66..c6616a2 100644 --- a/bash_secrets +++ b/bash_secrets @@ -1,5 +1,6 @@ # -*- bash -*- # shellcheck shell=bash +# ~/.bash_secrets: For storing sensitive settings and variables # (╯°□°)╯︵ ┻━┻ # You serious ? diff --git a/bashrc b/bashrc index 099c7c8..e974b83 100644 --- a/bashrc +++ b/bashrc @@ -2,162 +2,254 @@ # shellcheck shell=bash # ~/.bashrc: executed by bash(1) for non-login shells. -# If not running interactively, don't do anything -[ -z "$PS1" ] && return +################################################################################ +# Base Initialization +################################################################################ -# append to the history file, don't overwrite it -shopt -s histappend +# If not running interactively, don't do anything. +[[ $- == *i* ]] || return 0 -# check the window size after each command and, if necessary, -# update the values of LINES and COLUMNS. -shopt -s checkwinsize +# Set shell options - Keep LINES/COLUMNS in sync, append history +shopt -s checkwinsize histappend -# make less more friendly for non-text input files, see lesspipe(1) -[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" +# Add slash on tab through for symlinks +bind 'set mark-symlinked-directories on' 2>/dev/null + +# Make less more friendly for non-text input files, see lesspipe(1) +if lesspipe_bin=$(command -v lesspipe); then + eval "$(SHELL=/bin/sh "${lesspipe_bin}")" +fi # Load bash completions -if [ -f /etc/bash_completion ] && ! shopt -oq posix; then +if [[ -f /etc/bash_completion ]] && ! shopt -oq posix; then # shellcheck source=/dev/null source /etc/bash_completion fi -# Load common alias definitions -if [ -f ~/.bash_aliases ]; then - # shellcheck source=/dev/null - source ~/.bash_aliases -fi +################################################################################ +# Critical Environment Variables +################################################################################ + +readonly URDABASH_VERSION="1.0.0" +readonly URDABASH_OS="$(uname -s)" +export URDABASH_OS +export URDABASH_VERSION + +export XDG_CONFIG_HOME="${HOME}/.config" +export XDG_CACHE_HOME="${HOME}/.cache" +export XDG_DATA_HOME="${HOME}/.local/share" +export XDG_STATE_HOME="${HOME}/.local/state" + +################################################################################ +# Functions +################################################################################ + +# Private bins at the front, only once +_prepend_path_once() { + local dir=${1} + [[ -d ${dir} ]] || return + case ":${PATH}:" in + # Already present + *":${dir}:"*) ;; + # Prepend once + *) PATH="${dir}:${PATH}" ;; + esac +} + +_source_if_exists() { + # shellcheck source=/dev/null + [[ -n "${1}" && -r "${1}" ]] && source "${1}" +} + +_urda_version_check() { + local interval=604800 # 7 days + local state_dir="${XDG_STATE_HOME:-${HOME}/.local/state}/urda.bash" + local stamp="${state_dir}/last_check" now last=0 + local version_url="https://raw.githubusercontent.com/urda/urda.bash/refs/heads/master/VERSION" + + now=$(date +%s) + if [ -f "${stamp}" ]; then + last=$(stat -f %m "${stamp}" 2>/dev/null || stat -c %Y "${stamp}" 2>/dev/null || echo 0) + fi + (( now - last < interval )) && return + + # Update state timestamp + if ! mkdir -p "${state_dir}"; then + printf 'urda.bash update check skipped: cannot create %s\n' "${state_dir}" >&2 + return + fi + if ! touch "${stamp}"; then + printf 'urda.bash update check skipped: cannot write %s\n' "${stamp}" >&2 + return + fi + + local remote + if command -v curl >/dev/null 2>&1; then + remote=$(curl -fs -m 2 "${version_url}" 2>/dev/null) || return + elif command -v wget >/dev/null 2>&1; then + remote=$(wget -qO- --timeout=2 --tries=1 "${version_url}" 2>/dev/null) || return + else + return + fi + + remote=${remote//[[:space:]]/} + [ -z "${remote}" ] && return + [ "${remote}" != "${URDABASH_VERSION}" ] && \ + printf "An urda.bash update is available: '%s' -> '%s'\n" "${URDABASH_VERSION}" "${remote}" >&2 +} + +################################################################################ +# Sourcing +################################################################################ # Load common export definitions -if [ -f ~/.bash_exports ]; then - # shellcheck source=/dev/null - source ~/.bash_exports -fi +_source_if_exists "${HOME}/.bash_exports" -# Load any secrets -if [ -f ~/.bash_secrets ]; then - # shellcheck source=/dev/null - source ~/.bash_secrets -fi +# Load common alias definitions +_source_if_exists "${HOME}/.bash_aliases" -# If we have a private bin, include it at the FRONT of the path -if [ -d "$HOME/bin/" ]; then - PATH="$HOME/bin:$PATH" -fi +# Load any secrets +_source_if_exists "${HOME}/.bash_secrets" # Let's handle specific systems now -# OSX -if [ "$(uname)" == "Darwin" ]; then - if [ -f ~/.bash_osx ]; then - # shellcheck source=/dev/null - . ~/.bash_osx - fi -fi - -# Configure pyenv -if [ -x "$(command -v pyenv)" ]; then - export PYENV_ROOT="${HOME}/.pyenv" - export PATH="${PYENV_ROOT}/bin:${PATH}" - eval "$(pyenv init --path)" - eval "$(pyenv init -)" +case ${URDABASH_OS} in + # macOS / OSX + Darwin) + _source_if_exists "${HOME}/.bash_osx" + ;; + Linux) + _source_if_exists "${HOME}/.bash_linux" + ;; +esac + +_prepend_path_once "${HOME}/.local/bin" +_prepend_path_once "${HOME}/bin" + +################################################################################ +# Tooling +################################################################################ + +# ------------------------------ +# 1Password (op) +# ------------------------------ +_source_if_exists "${XDG_CONFIG_HOME}/op/plugins.sh" + +# ------------------------------ +# pyenv +# ------------------------------ +if command -v pyenv >/dev/null 2>&1; then + export PYENV_ROOT="${HOME}/.pyenv" + + _prepend_path_once "${PYENV_ROOT}/bin" + + eval "$(pyenv init -)" + if command -v pyenv-virtualenv >/dev/null 2>&1; then eval "$(pyenv virtualenv-init -)" - # v--- Custom Functions ---v - - deactivate () { - pyenv shell --unset - } + lsvirtualenv () { pyenv virtualenvs --bare --skip-aliases; } + mkvirtualenv () { pyenv virtualenv "${@}"; } + rmvirtualenv () { pyenv uninstall "${@}"; } + fi +fi - lsvirtualenv () { - pyenv virtualenvs --bare --skip-aliases - } +################################################################################ +# Update Check +################################################################################ + +_urda_version_check + +################################################################################ +# Prompt Functions +# --- +# UTF8 References +# +# - $'\xE2\x95\x90' ..... '═' +# - $'\xE2\x95\x94' ..... '╔' +# - $'\xE2\x95\xa0' ..... '╠' +# - $'\xE2\x95\x9A' ..... '╚' +################################################################################ + +# ╔═[user@host : /current/working/directory] +_ps1_header_line() { + local outline=${1} green=${2} blue=${3} reset=${4} + printf '%s%s%s[%s\\u@\\h%s %s: %s\\w%s]%s\\n%s' \ + "${outline}" $'\xE2\x95\x94' $'\xE2\x95\x90' "${green}" "${reset}" "${outline}" "${blue}" "${outline}" "${outline}" "${reset}" +} - mkvirtualenv () { - pyenv virtualenv "${@}" - } +# ╠═[git : branch_name] +_ps1_git_line() { + type __git_ps1 >/dev/null 2>&1 || return 0 + local branch + branch=$(__git_ps1 "%s") || branch="" + [[ -n "${branch}" ]] || return 0 + local outline=${1} green=${2} blue=${3} reset=${4} + printf '%s%s%s[%sgit%s %s: %s%s%s]%s\\n' \ + "${outline}" $'\xE2\x95\xa0' $'\xE2\x95\x90' "${green}" "${reset}" "${outline}" "${blue}" "${branch}" "${outline}" "${reset}" +} - rmvirtualenv() { - pyenv uninstall "${@}" - } +# ╠═[screen : screen_name] +_ps1_screen_line() { + [[ -n "${STY}" ]] || return 0 + local outline=${1} green=${2} blue=${3} reset=${4} + printf '%s%s%s[%sscreen%s %s: %s%s%s]%s\\n' \ + "${outline}" $'\xE2\x95\xa0' $'\xE2\x95\x90' "${green}" "${reset}" "${outline}" "${blue}" "${STY}" "${outline}" "${reset}" +} - workon () { - pyenv shell "${@}" - } -fi +# ╠═[virtualenv : virtualenv_name] +_ps1_virtualenv_line() { + local outline=${1} green=${2} blue=${3} reset=${4} venv_path=${5} + [[ -n "${venv_path}" ]] || return 0 + local venv=${venv_path##*/} # take only the last path segment + printf '%s%s%s[%svirtualenv%s %s: %s%s%s]%s\\n' \ + "${outline}" $'\xE2\x95\xa0' $'\xE2\x95\x90' "${green}" "${reset}" "${outline}" "${blue}" "${venv}" "${outline}" "${reset}" +} -# Is 1Password CLI available? -if [ -f "${HOME}"/.config/op/plugins.sh ]; then - # shellcheck source=/dev/null - source "${HOME}"/.config/op/plugins.sh -fi +# ╚═ $ +_ps1_footer_line() { + local outline=${1} term_char=${2} reset=${3} + printf '%s%s%s %s%s ' "${outline}" $'\xE2\x95\x9A' $'\xE2\x95\x90' "${term_char}" "${reset}" +} -set_ps1() { - # Define some box drawing hex - local double_horizontal=$'\xE2\x95\x90' # ═ - local double_down_right=$'\xE2\x95\x94' # ╔ - local double_vertical_right=$'\xE2\x95\xa0' # ╠ - local double_up_and_right=$'\xE2\x95\x9A' # ╚ - - # Set prompt variables - local BBlue='\[\e[1;34m\]' - local BGreen='\[\e[1;32m\]' - local BRed='\[\e[1;31m\]' - local BWhite='\[\e[1;37m\]' - - local Color_Off='\[\e[0m\]' - local Outline=$BWhite - local TermChar='$' - - # Determine if root - if (( EUID == 0 )); then - # We ARE root, change prompt details - local Outline=$BRed - local TermChar='#' - fi - - # Configure first prompt line - # - # ╔═[user@host : /current/working/directory] - PS1="$Outline${double_down_right}${double_horizontal}[$BGreen\\u@\\h$Color_Off $Outline: $BBlue\\w$Outline]$Outline\\n$Color_Off" - - # Screen Checks - # - # ╠═[screen : screen_name] - if [ -n "$STY" ]; then - # Screen is up - PS1+="$Outline${double_vertical_right}${double_horizontal}[${BGreen}screen$Color_Off $Outline: $BBlue$STY$Outline]$Color_Off\\n" - fi - - # Git check - # - # ╠═[git : branch_name] - if type __git_ps1 > /dev/null 2>&1 ; then - # Git is available, is this a git repo? - local Git_Branch - local Git_Branch_Length - Git_Branch=$(__git_ps1 "%s") - Git_Branch_Length=${#Git_Branch} - - if (( "$Git_Branch_Length" > 0 )); then - # We indeed do have branch info - PS1+="$Outline${double_vertical_right}${double_horizontal}[${BGreen}git$Color_Off $Outline: $BBlue$Git_Branch$Outline]$Color_Off\\n" - fi - fi - - # virtualenv check - # - # ╠═[virtualenv : virtualenv_name] - local venv="${PYENV_VERSION}" - if [[ ${venv} != "" ]]; then - # ${string##substring} - # Deletes longest match of $substring from front of $string. - PS1+="$Outline${double_vertical_right}${double_horizontal}[${BGreen}virtualenv${Color_Off} ${Outline}: ${BBlue}${venv}${Outline}]${Color_Off}\\n" - fi - - # Configure final prompt line - # - # ╚═ $ - PS1+="$Outline${double_up_and_right}${double_horizontal} $TermChar$Color_Off " +################################################################################ +# Prompt Setup +################################################################################ + +_set_ps1() { + # Set prompt variables + local BBlue='\[\e[1;34m\]' + local BGreen='\[\e[1;32m\]' + local BRed='\[\e[1;31m\]' + local BWhite='\[\e[1;37m\]' + + local Color_Off='\[\e[0m\]' + local Outline=$BWhite + local TermChar='$' + + # Determine if root + if (( EUID == 0 )); then + # We ARE root, change prompt details + Outline=$BRed + TermChar='#' + fi + + local prompt="" + prompt=$( + _ps1_header_line "${Outline}" "${BGreen}" "${BBlue}" "${Color_Off}" + _ps1_git_line "${Outline}" "${BGreen}" "${BBlue}" "${Color_Off}" + _ps1_screen_line "${Outline}" "${BGreen}" "${BBlue}" "${Color_Off}" + _ps1_virtualenv_line "${Outline}" "${BGreen}" "${BBlue}" "${Color_Off}" "${PYENV_VIRTUAL_ENV}" + _ps1_footer_line "${Outline}" "${TermChar}" "${Color_Off}" + ) + PS1=${prompt} } # Use function for prompts -PROMPT_COMMAND=set_ps1 +pc=${PROMPT_COMMAND%;} +case ";${pc};" in + *";_set_ps1;"*) + PROMPT_COMMAND="${pc}" + ;; + *) + PROMPT_COMMAND="${pc:+${pc};}_set_ps1;" + ;; +esac