Skip to content

Instantly share code, notes, and snippets.

@johnhpatton
Last active January 6, 2025 20:17
Show Gist options
  • Save johnhpatton/4f5bdf2ec53ed493ac395b2badec6de8 to your computer and use it in GitHub Desktop.
Save johnhpatton/4f5bdf2ec53ed493ac395b2badec6de8 to your computer and use it in GitHub Desktop.
Aries bashrc for working with various projects
[[ $- =~ i ]] && INTERACTIVE=1
# enum()
# Creates an enum from passed in list variable name.
# Usage:
# STATUS=(
# OK
# FAIL
# ) && enum "${STATUS[@]}"
# echo "$OK is 0"
# echo "$FAIL is 1"
function enum() {
local -a list=("$@")
for (( i=0; i < ${#list[@]}; i++ )); do eval "${list[i]}=$i"; done
}
# Log Levels
declare -a LOGLEVELS=(
"LLNOTIFY" # always print
"LLFAIL" # only print if LOGLEVEL is at least LLFAIL
"LLWARN" # only print if LOGLEVEL is at least LLWARN
"LLINFO" # only print if LOGLEVEL is at least LLINFO
"LLDEBUG" # only print if LOGLEVEL is at least LLDEBUG
) && enum "${LOGLEVELS[@]}"
LOGLEVEL="${LLFAIL}" # log fail and notify messages only
#LOGLEVEL="${LLWARN}" # log warn, error, and notify messages only
#LOGLEVEL="${LLINFO}" # log all but debug messages
#LOGLEVEL="${LLDEBUG}" # log all messages
function lognotify() {
local msg
local color=${INTERACTIVE:+'\e[1;36m'} # bold cyan
local reset=${INTERACTIVE:+'\e[m'}
local format=' %b%s%b'
printf -v msg "${format}" "${color}" "$*" "${reset}"
logmsg "${msg}"
}
function logdebug() {
(( LOGLEVEL >= LLDEBUG )) || return
local msg
local pfx_color=${INTERACTIVE:+'\e[0;37m'} # grey
local par_color=${INTERACTIVE:+'\e[1;37m'} # bold grey
local fxn_color=${INTERACTIVE:+'\e[1;97m'} # bright white
local msg_color=${INTERACTIVE:+'\e[0;97m'} # white
local reset=${INTERACTIVE:+'\e[m'}
local format='%b[%bDBUG%b]: %b(%b%s%b)%b %s%b'
printf -v msg "${format}" "${par_color}" "${pfx_color}" "${par_color}" "${pfx_color}" "${fxn_color}" "${FUNCNAME[1]}" "${pfx_color}" "${msg_color}" "$*" "${reset}"
logmsg "${msg}"
}
function loginfo() {
(( LOGLEVEL >= LLINFO )) || return
local msg
local pfx_color=${INTERACTIVE:+'\e[1;32m'} # bold green
local msg_color=${INTERACTIVE:+'\e[0;32m'} # green
local reset=${INTERACTIVE:+'\e[m'}
local format='%b[%bINFO%b]: %s%b'
printf -v msg "${format}" "${msg_color}" "${pfx_color}" "${msg_color}" "$*" "${reset}"
logmsg "${msg}"
}
function logwarn() {
(( LOGLEVEL >= LLWARN )) || return
local msg
local pfx_color=${INTERACTIVE:+'\e[1;33m'} # bold yellow
local msg_color=${INTERACTIVE:+'\e[1;93m'} # bright bold yellow
local reset=${INTERACTIVE:+'\e[m'}
local format='%b[%bWARN%b]: %s%b'
printf -v msg "${format}" "${msg_color}" "${pfx_color}" "${msg_color}" "$*" "${reset}"
logmsg "${msg}"
}
function logfail() {
(( LOGLEVEL >= LLFAIL )) || return
local msg
local pfx_color=${INTERACTIVE:+'\e[0;31m'} # red
local msg_color=${INTERACTIVE:+'\e[1;31m'} # bold red
local reset=${INTERACTIVE:+'\e[m'}
local format='%b[%bFAIL%b]: %s%b'
printf -v msg "${format}" "${msg_color}" "${pfx_color}" "${msg_color}" "$*" "${reset}"
logmsg "${msg}"
}
function logmsg() {
[[ $- =~ i ]] || return
if [[ ! ${FUNCNAME[1]} =~ ^log(debug|info|warn|fail|notify) ]]; then
logfail "${FUNCNAME[0]} called from ${FUNCNAME[1]}, disallowed; instead call: logdebug, loginfo, logwarn, logfail, logfatal, or lognotify, exiting."
fi
echo -e "$1"
}
function pathadd() {
local retval=0
local option
if [[ "${1:0:1}" = "-" ]]; then
option=$1 && shift
fi
# assign newpath, remove any trailing / character
local newpath="${1%/}"
local pathvar="$2"
# default to PATH
[ -z "$pathvar" ] && pathvar="PATH"
local pathval="${!pathvar}"
if [ -d "$newpath" ] && [[ ":$pathval:" != *":$newpath:"* ]]; then
if [ "$option" == "-p" ]; then
# prepend
pathval="$newpath${pathval:+":$pathval"}"
else
pathval="${pathval:+"$pathval:"}$newpath"
fi
eval $pathvar=$pathval
export $pathvar
logdebug "$newpath added to $pathvar."
elif [ ! -d "$newpath" ]; then
logdebug "$newpath folder does not exist, not updating $pathvar."
retval=1
else
logdebug "$newpath already in $pathvar, not updating."
fi
return $(( retval ))
}
# akamaicli macro
function akamaicli() {
type -P docker &>/dev/null || { echo "docker not found in path, exiting."; exit 1; }
if [[ `docker ps | grep akamai-cli$ | wc -l` -eq 1 ]]; then
docker exec -it akamai-cli akamai $@;
elif docker start akamai-cli > /dev/null 2>&1 && sleep 3 && docker exec -it akamai-cli akamai $@; then
return 0;
else
echo "Creating new docker container"
mkdir -p "$HOME"/.akamai-cli-docker
docker exec -it --mount source=vol-akamai-devops,target=$HOME/akamai --mount source=/cli,target=$HOME/.akamai-cli-docker --name akamai-cli akamai/cli > /dev/null 2>&1 && akamaicli $@;
fi;
}
# command_prompt()
#
# Function that is executed each time a command is run to update the PS1
# variable (ie: prompt). To configure, let's put some fonts in place.
# Download and install Gabriele Lana's Awesome Fonts:
#
# https://github.com/gabrielelana/awesome-terminal-fonts/archive/master.zip
#
# Unzip the archive and run the install.sh from the resulting
# awesome-terminal-fonts-master folder to install some fonts. Some .sh files
# will also be installed in ~/.fonts to provide constants for each font that
# can be used in scripts.
#
# If using git, install the git-prompt.sh script in your home folder and add
# to .bashrc:
#
# curl -L -o ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
# chown +x ~/.git-prompt.sh
# echo 'source ~/.git-prompt.sh' >> ~/.bashrc
#
# You'll need to log out and back in to pull that in, or you'll need to source
# the ~/.git-prompt.sh in your current session to test it.
#
# Color scheme is based on a dark background. If using a light background,
# you'll want to experiment with the colors.
function command_prompt() {
#=========================================================
# Capture EXIT_CODE MUST BE FIRST!!!
#=========================================================
local EXIT_CODE="$?"
[[ $- =~ i ]] || return
local debug=0
local fmt
local show_msg_in_ps1
local prompt_var="PS1" && PS1=""
(( EUID == 0 )) && prompt_var="SUDO_PS1" && SUDO_PS1=""
# exit code output variables
local exit_code=""
local exit_code_color=""
# CONSTANTS
# Colors for bash prompt
# foreground colors, use with echo -e
local RESET='\[\e[m\]' # No Color, reset
# normal bright high intensity bright + high intensity
local DKGREY='\[\e[0;30m\]'
local LTGREY='\[\e[1;30m\]'
local RED='\[\e[0;31m\]' BRED='\[\e[1;31m\]' IRED='\[\e[0;91m\]' BIRED='\[\e[1;91m\]'
local GREEN='\[\e[0;32m\]' BGREEN='\[\e[1;32m\]' IGREEN='\[\e[0;92m\]' BIGREEN='\[\e[1;92m\]'
local YELLOW='\[\e[0;33m\]' BYELLOW='\[\e[1;33m\]' IYELLOW='\[\e[0;93m\]' BIYELLOW='\[\e[1;93m\]'
local BLUE='\[\e[0;34m\]' BBLUE='\[\e[1;34m\]' IBLUE='\[\e[0;94m\]' BIBLUE='\[\e[1;94m\]'
local PURPLE='\[\e[0;35m\]' BPURPLE='\[\e[1;35m\]' IPURPLE='\[\e[0;95m\]' BIPURPLE='\[\e[1;95m\]'
local CYAN='\[\e[0;36m\]' BCYAN='\[\e[1;36m\]' ICYAN='\[\e[0;96m\]' BICYAN='\[\e[1;96m\]'
local GREY='\[\e[0;37m\]' BGREY='\[\e[1;37m\]'
local WHITE='\[\e[0;97m\]' BWHITE='\[\e[1;97m\]'
local sym_radioactive='\u2622'
local sym_warning='\u26A0'
local sym_check_mark='\u2714'
local sym_red_x='\u274C'
local sym_cancel='\u2718'
local sym_sparkle='\u2728'
local sym_arrow='\u2937'
local sym_line='\u2574'
local sym_left_arrow_up='\u2b11'
local sym_background_process='\u2749'
local sym_colon='\u003A'
local sym_checkmark='\u2713'
local sym_thumbs_up='\U1F44D'
local sym_thumbs_down='\U1F44E'
local sym_open_bracket="{"
local sym_close_bracket="}"
local sym_k8s='\u2388'
# is user in an SSH session?
local bracket_color="${BGREEN}" # default
if [[ $SSH_CLIENT ]] || [[ $SSH2_CLIENT ]]; then
bracket_color="${BPURPLE}"
fi
local info_bracket_color="${GREY}"
local open_info_group close_info_group
# user/host/workdir
local user_color
local user_host_workdir_ps1
(( EUID == 0 )) && user_color="${RED}" || user_color="${CYAN}"
local userfield='\u'
# 1 2 3 4
fmt='%b[%b%s%b@' # 1 2 3 4
printf -v user_host_workdir_ps1 "${fmt}" "${bracket_color}" "${user_color}" "${userfield}" "${RESET}"
local host_color="${CYAN}"
local hostfield='\h'
# 1 2 3 4
fmt='%s%b%s%b]' # 1 2 3 4
printf -v user_host_workdir_ps1 "${fmt}" "${user_host_workdir_ps1}" "${host_color}" "${hostfield}" "${bracket_color}"
local workdir_color="${YELLOW}"
local workdirfield='\W'
# 1 2 3 4 5
fmt='%s[%b%s%b]%b$ ' # 1 2 3 4 5
printf -v user_host_workdir_ps1 "${fmt}" "${user_host_workdir_ps1}" "${workdir_color}" "${workdirfield}" "${bracket_color}" "${RESET}"
# check if user has ~/.git-prompt.sh loaded and run __git_ps1 if possible,
# also disable showdirty and showuntracked if __git_ps1 takes a long time
# on the current git repo.
local git_ps1
if declare -F __git_ps1 &>/dev/null; then
local now="$SECONDS"
local git_detail=$(__git_ps1)
local git_ps1_time=$((SECONDS-now))
if (( git_ps1_time > 2 )); then
# is showDirtyState not disabled?
if [[ $(git config --bool bash.showDirtyState) != false ]]; then
show_msg_in_ps1="unsetting 'showDirtyState' in local repo to improve __git_ps1 performance (1)"
git config --bool bash.showDirtyState false
# is showUntrackedFiles not disabled?
elif [[ $(git config --bool bash.showUntrackedFiles) != false ]]; then
show_msg_in_ps1="unsetting 'showUntrackedFiles' in local repo to improve __git_ps1 performance (2)"
git config --bool bash.showUntrackedFiles false
else
show_msg_in_ps1="__git_ps1 performance is slow and performance settings have been adjusted, please investigate further"
fi
fi
# From git-sh-prompt or git-prompt.sh:
# See configure_git_prompt() for env var settings.
# GIT_PS1_STATESEPARATOR controls the branch state separator, default is " " (space)
# * - unstaged (GIT_PS1_SHOWDIRTYSTATE env var set to 1, or bash.showDirtyState not set to false)
# + - staged (GIT_PS1_SHOWDIRTYSTATE env var set to 1, or bash.showDirtyState not set to false)
# $ - stashed (GIT_PS1_SHOWSTASHSTATE env var set to 1)
# % - untracked (GIT_PS1_SHOWUNTRACKEDFILES env var set to 1, or bash.showUntrackedFiles not set to false)
# <> - diverged from upstream (GIT_PS1_SHOWUPSTREAM env var set to "auto")
# < - behind upstream (GIT_PS1_SHOWUPSTREAM env var set to "auto")
# > - ahead of upstream (GIT_PS1_SHOWUPSTREAM env var set to "auto")
local -A git_detail_level=()
git_detail_level["+"]="notify"
git_detail_level["$"]="info"
git_detail_level["*"]="warn"
git_detail_level["<"]="warn"
git_detail_level[">"]="warn"
git_detail_level["%"]="alert"
git_detail_level["<>"]="alert"
local -A git_detail_names=()
git_detail_names["*"]="unstaged"
git_detail_names["+"]="staged"
git_detail_names["$"]="stashed"
git_detail_names["<"]="behind"
git_detail_names[">"]="ahead"
git_detail_names["%"]="untracked"
git_detail_names["<>"]="diverged"
if [ -n "${git_detail}" ]; then
# clean up parens/brackets/leading & trailing spaces
git_detail=${git_detail#"${git_detail%%[![:space:]]*}"}
git_detail=${git_detail%"${git_detail##*[![:space:]]}"}
git_detail="${git_detail//[()\]]}"
local git_branch="${git_detail%${GIT_PS1_STATESEPARATOR-" "}*}"
local flag="${git_detail##*${GIT_PS1_STATESEPARATOR-" "}}"
flag+="${git_branch##*[!=]}"
git_branch="${git_branch%%=*}"
local -a git_flags=(${flag//@()/ })
local git_symbols
# colour branch name depending on state
# make the symbols easier to see.
local git_branch_color="${GREY}"
# staged files, committed but not pushed
if [[ "${flag}" =~ "+" ]]; then
git_branch_color="${CYAN}"
git_symbols+="${git_symbols:+ }${CYAN}staged${RESET}"
fi
# if there is something stashed
if [[ "${flag}" =~ "$" ]]; then
git_branch_color="${GREEN}"
git_symbols+="${git_symbols:+ }${GREEN}stashed${RESET}"
fi
# unstaged/uncommitted files (dirty)
if [[ "${flag}" =~ "*" ]]; then
git_branch_color="${YELLOW}"
git_symbols+="${git_symbols:+ }${YELLOW}unstaged${RESET}"
fi
# branch is diverged
if [[ "${flag}" =~ "<>" ]]; then
git_branch_color="${RED}"
git_symbols+="${git_symbols:+ }${RED}diverged${RESET}"
# branch is ahead
elif [[ "${flag}" =~ ">" ]]; then
git_branch_color="${YELLOW}"
git_symbols+="${git_symbols:+ }${YELLOW}ahead${RESET}"
# branch is behind
elif [[ "${flag}" =~ "<" ]]; then
git_branch_color="${YELLOW}"
git_symbols+="${git_symbols:+ }${YELLOW}behind${RESET}"
fi
# untracked files
if [[ "${flag}" =~ "%" ]]; then
git_branch_color="${RED}"
git_symbols+="${git_symbols:+ }${RED}untracked${RESET}"
fi
# no changes
if [ -z "${git_symbols}" ]; then
git_symbols="up-to-date"
fi
# 1 2 3 4 5 6 7
fmt='%b[[%b%s%b => %s%b]]%b'
printf -v git_ps1 "${fmt}" "${info_bracket_color}" "${git_branch_color}" "${git_branch}" "${RESET}" "${git_symbols}" "${info_bracket_color}" "${RESET}"
fi
fi
local az_ps1 azure_account t
local azure_account_color="${GREY}"
local now=$(date '+%s')
local active_az_login=0
if [ -s "${HOME}/.azure/msal_token_cache.json" ]; then
while read t; do
(( t > now )) && active_az_login=1 && break
done< <(jq -r '.AccessToken | to_entries[] | .value.expires_on' "${HOME}/.azure/msal_token_cache.json")
elif [ -s "${HOME}/.azure/accessTokens.json" ]; then
while read t; do
(( $(date -d "${t}" '+%s') > now )) && active_az_login=1 && break
done< <(cat "${token_file}" | jq -r '.[].expiresOn')
fi
if (( active_az_login )); then
azure_account=$(az account show | jq -r '.name')
# 1 2 3 4 5
fmt='%baz:[%b%s%b]%b'
printf -v az_ps1 "${fmt}" "${info_bracket_color}" "${azure_account_color}" "${azure_account}" "${info_bracket_color}" "${RESET}"
fi
local tf_workspace tf_ps1
local tf_workspace_color="${GREY}"
if [ -f ".terraform/environment" ]; then
[ -n "${TF_WORKSPACE}" ] && tf_workspace=${TF_WORKSPACE} || tf_workspace=$(cat .terraform/environment)
case ${tf_workspace} in
*dev*|*qa*|*tst*|*test*) tf_workspace_color=${BIGREEN};;
*uat*|*stg*|*stag*) tf_workspace_color=${BIBLUE};;
*prd*|*prod*) tf_workspace_color=${BIYELLOW};;
*) tf_workspace_color=${GREEN};;
esac
# 1 2 3 4
fmt='%btf:%b%s%b'
printf -v tf_ps1 "${fmt}" "${info_bracket_color}" "${tf_workspace_color}" "${tf_workspace}" "${RESET}"
fi
local ct_project ct_ps1
local ct_project_color="${GREY}"
if [ -n "${CTP_PROJECT_KEY}" ]; then
ct_project="${CTP_PROJECT_KEY}"
case ${ct_project} in
*dev*|*qa*|*test*) ct_project_color=${BIGREEN};;
*uat*|*stag*) ct_project_color=${BIBLUE};;
*prod*) ct_project_color=${BIYELLOW};;
*) ct_project_color=${GREEN};;
esac
# 1 2 3 4
fmt='%bct:%b%s%b'
printf -v ct_ps1 "${fmt}" "${info_bracket_color}" "${ct_project_color}" "${ct_project}" "${RESET}"
fi
# If commercetools and terraform environments are present, update ps1 to
# show if the project and workspace names match or not.
local tf_ct_ps1
if [ -n "${tf_workspace}" ] && [ -n "${ct_project}" ]; then
tf_ps1=""
ct_ps1=""
local tf_ct_color tf_ct_ps1 tf_ct_symbol
if [ "${tf_workspace}" == "${ct_project}" ]; then
tf_ct_color="${GREEN}"
# 1 2 3
fmt='tf%b<%b%s'
# 1 2 3
printf -v tf_ct_ps1 "${fmt}" "${tf_ct_color}" "${tf_workspace_color}" "${tf_workspace}"
# 1 2 3 4 5 6 7 8
fmt='%s%b>%b ct%b<%b%s%b>%b'
# 1 2 3 4 5 6 7 8
printf -v tf_ct_ps1 "${fmt}" "${tf_ct_ps1}" "${tf_ct_color}" "${RESET}" "${tf_ct_color}" "${ct_project_color}" "${ct_project}" "${tf_ct_color}" "${RESET}"
else
tf_ct_color="${BRED}"
# 1 2 3
fmt='tf%b<%b%s'
# 1 2 3
printf -v tf_ct_ps1 "${fmt}" "${tf_ct_color}" "${tf_workspace_color}" "${tf_workspace}"
# 1 2 3 4 5
fmt='%s %b==>%b %b %b<=='
# 1 2 3 4 5
printf -v tf_ct_ps1 "${fmt}" "${tf_ct_ps1}" "${tf_ct_color}" "${RESET}" "${sym_red_x}" "${tf_ct_color}"
# 1 2 3 4 5
fmt='%s %b%s%b>%bct'
# 1 2 3 4 5
printf -v tf_ct_ps1 "${fmt}" "${tf_ct_ps1}" "${ct_project_color}" "${ct_project}" "${tf_ct_color}" "${RESET}"
fi
fi
local k8s_ps1 kube_context
if type kubectl &>/dev/null; then
if [ -z "${KUBECONFIG}" ] && [ -f "${HOME}/.kube/config" ]; then
KUBECONFIG="${HOME}/.kube/config"
fi
if [ -f "${KUBECONFIG}" ]; then
kube_context=$(cat "${KUBECONFIG}" | grep -oP '^current-context: \K.+')
local kube_context_color="${GREY}"
case ${kube_context} in
*dev*|*qa*|*tst*|*test*|*nonprod*) kube_context_color=${BIGREEN};;
*uat*|*stg*|*stag*) kube_context_color=${YELLOW};;
*prd*|*prod*) kube_context_color=${YELLOW};;
esac
# 1 2 3 4
fmt='%bk8s:%b%s%b'
printf -v k8s_ps1 "${fmt}" "${info_bracket_color}" "${kube_context_color}" "${kube_context}" "${RESET}"
fi
fi
case ${EXIT_CODE} in
0) exit_code="${sym_check_mark}"
exit_code_color="${BGREEN}"
;;
127) exit_code="${sym_warning}" # command not found
exit_code_color="${BIRED}"
;;
130) exit_code="${sym_cancel}" # control-c
exit_code_color="${BYELLOW}"
;;
148) exit_code="${sym_sparkle}" # backgrounded command
exit_code_color="${BCYAN}"
;;
*) exit_code="${EXIT_CODE}" # other error code
exit_code_color="${RED}"
;;
esac
# backgrounded jobs in the current session.
local jobs_ps1
local job_count=$(jobs -r | wc -l ) # backgrounded jobs
if (( job_count > 0 )); then
# 1 2 3 4 5 6 7
fmt='%b%b%b%b%b%s%b'
printf -v jobs_ps1 "${fmt}" "${YELLOW}" "${sym_background_process}" "${RESET}" "${sym_colon}" "${WHITE}" "${job_count}" "${RESET}"
elif (( job_count > 3 )); then
# 1 2 3 4 5 6 7
fmt='%b%b%b%b%b%s%b'
printf -v jobs_ps1 "${fmt}" "${RED}" "${sym_background_process}" "${RESET}" "${sym_colon}" "${WHITE}" "${job_count}" "${RESET}"
fi
# paused jobs in the current session.
job_count=$(jobs -s | wc -l ) # paused / stopped jobs
if (( job_count > 0 )); then
# 1 2 3 4 5 6 7 8
fmt='%s%b%b%b%b%b%s%b'
printf -v jobs_ps1 "${fmt}" "${jobs_ps1}" "${LT_GREY}" "${sym_background_process}" "${RESET}" "${sym_colon}" "${WHITE}" "${job_count}" "${RESET}"
elif (( job_count > 3 )); then
# 1 2 3 4 5 6 7 8
fmt='%s%b%b%b%b%b%s%b'
printf -v jobs_ps1 "${fmt}" "${jobs_ps1}" "${LT_GREY}" "${sym_background_process}" "${RESET}" "${sym_colon}" "${WHITE}" "${job_count}" "${RESET}"
fi
# fun UX for prompt, arrow indicates preceding command exited with exit code it's pointing to
local arrow_and_exit_ps1
# 1 2 3 4 5
fmt='%b%b %b%b%b '
printf -v arrow_and_exit_ps1 "${fmt}" "${GREY}" "${sym_arrow}" "${exit_code_color}" "${exit_code}" "${RESET}"
# assemble the prompt
local prompt=""
# git
[ -n "${git_ps1}" ] && prompt+="${git_ps1}"
# azure
[ -n "${az_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${az_ps1}"; }
# gcp
# [ -n "${gcp_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${gcp_ps1}"; }
# aws
# [ -n "${aws_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${aws_ps1}"; }
# k8s
[ -n "${k8s_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${k8s_ps1}"; }
# terraform and commercetools
if [ -n "${tf_ct_ps1}" ]; then
[ -n "${prompt}" ] && prompt+=" "
prompt+="${tf_ct_ps1}"
else
[ -n "${tf_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${tf_ps1}"; }
[ -n "${ct_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${ct_ps1}"; }
fi
# hidden processes for the current tty session
[ -n "${jobs_ps1}" ] && { [ -n "${prompt}" ] && prompt+=" "; prompt+="${jobs_ps1}"; }
[ -n "${prompt}" ] && prompt+="\n"
prompt+="${arrow_and_exit_ps1}"
prompt+="${user_host_workdir_ps1}"
#(( EUID == 0 )) && SUDO_PS1="${prompt}" || PS1="${prompt}"
PS1="${prompt}"
}
# Configure git prompt, only needed to initialize an interactive shell.
function configure_git_prompt() {
# no need for bash completion or branch in prompt if non-interactive
[[ $- =~ i ]] || return
local git_prompt_sh=""
# Git
if type git &>/dev/null; then
if [ ! "$(git config --global credential.helper)" == "cache" ]; then
git config --global credential.helper 'cache --timeout=3600' ${git_credential_type}
alias gitdisablecache="git config --global --unset credential.helper"
fi
if [ -f "/usr/lib/git-core/git-sh-prompt" ]; then
git_prompt_sh="/usr/lib/git-core/git-sh-prompt"
else
git_prompt_sh="${HOME}/.git-prompt.sh"
if [ ! -f "${git_prompt_sh}" ]; then
loginfo "git-prompt.sh not found, installing... this is only needed once..."
curl -sL -o "${git_prompt_sh}" "https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh"
chmod +x "${git_prompt_sh}"
fi
fi
# if .git-prompt.sh exists, set options and execute it
if [ -n "${git_prompt_sh}" ]; then
GIT_PS1_SHOWDIRTYSTATE=true # this can get sluggish for large repositories
GIT_PS1_SHOWUNTRACKEDFILES=true # this runs a git ls-files command and can be rather sluggish
GIT_PS1_SHOWSTASHSTATE=true
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_HIDE_IF_PWD_IGNORED=true
GIT_PS1_SHOWCOLORHINTS=true
GIT_PS1_STATESEPARATOR=" "
source_rc "${git_prompt_sh}"
fi
fi
}
# Configure PS1 environment variable, initializes git env and updates the prompt command variable
function configure_ps1() {
[[ $- =~ i ]] || return
configure_git_prompt
# configure PROMPT_COMMAND which is executed each time before PS1
export PROMPT_COMMAND='command_prompt; history -a; history -c; history -r;'
}
declare OSINFO SYSTEM ARCH
declare MACHINE_TYPE="host"
function get_os_info() {
[[ $- =~ i ]] || return
(( OSINFO )) && return
pathadd -p "/usr/bin"
pathadd -p "/usr/sbin"
if type -P uname &>/dev/null; then
SYSTEM=$(uname -s)
ARCH="$(uname -m)"
elif [ -n "${OSTYPE}" ]; then
SYSTEM="${OSTYPE//[0-9.]/}"
else
logwarn "unable to determine operating system, presuming linux."
SYSTEM="linux"
fi
SYSTEM="${SYSTEM,,}"
if [ -z "${ARCH}" ]; then
if [ -n "${HOSTTYPE}" ]; then
ARCH="${HOSTTYPE}"
elif [ -n "${MACHTYPE}" ]; then
ARCH="${MACHTYPE}"
fi
fi
if [ -n "${ARCH}" ]; then
if [ "${ARCH}" == "aarch64" ]; then
ARCH="arm64"
elif [ "${ARCH}" == "x86_64" ]; then
ARCH="amd64"
fi
else
logwarn "unable to determine machine type, architecture-specific commands are disabled."
fi
if [ "${SYSTEM}" == "darwin" ]; then
ioreg -l | grep -q -E "Manufacturer" | grep -E "VirtualBox|Oracle|VMware|Parallels" && MACHINE_TYPE="vm"
elif type -P hostnamectl &>/dev/null; then
MACHINE_TYPE=$(hostnamectl chassis)
elif type -P "$HOME/.rvm/gems/default/bin/facter" &>/dev/null; then
case $("$HOME"/.rvm/gems/default/bin/facter virtual) in
physical) ;;
*) MACHINE_TYPE="vm";;
esac
elif type -P ioreg &>/dev/null && ioreg -l | grep "Manufacturer" | grep -sqE "VirtualBox|Oracle|VMware|Parallels"; then
MACHINE_TYPE="vm"
elif type -P lshw &>/dev/null && lshw | grep 'vendor' | grep -sqE "VirtualBox|Oracle|VMware|Parallels"; then
MACHINE_TYPE="vm"
elif [ -f "/proc/cpuinfo" ] && grep -sq '^flags.*\ hypervisor\ ' /proc/cpuinfo; then
MACHINE_TYPE="vm"
else
logwarn "unable to determine machine type, architecture-specific commands are disabled."
fi
export OSINFO=1
}
# configure_colors()
declare CLICOLOR=0 # also used by commands
function configure_colors() {
local retval=0
# no need for colors if non-interactive
[[ $- =~ i ]] || return $(( retval ))
(( CLICOLOR )) && return $(( retval ))
logdebug "configuring colors..."
# colored highlighting is awesome, enable if available
if type -P dircolors &>/dev/null; then
if [ ! -f "~/.dir_colors" ]; then
logdebug "generating dir colors"
if brew list gnu-sed &>/dev/null && [[ $PATH =~ gnu-sed\/libexec\/gnubin ]]; then
dircolors -p | sed '/# directory/c DIR 01;33 # directory for dark theme' > ~/.dir_colors
else
dircolors -p | sed -E 's@DIR.*directory@DIR 01;33 # directory for dark theme@' > ~/.dir_colors
fi
fi
logdebug "loading colors"
eval "`dircolors -b ~/.dir_colors`"
else
retval=1
fi
# Color for manpages in less makes manpages a little easier to read on dark background terminals
export LESS_TERMCAP_mb=$(tput bold; tput setaf 2) # green
export LESS_TERMCAP_md=$(tput bold; tput setaf 6) # cyan
export LESS_TERMCAP_me=$(tput sgr0)
export LESS_TERMCAP_so=$(tput bold; tput setaf 3; tput setab 4) # yellow on blue
export LESS_TERMCAP_se=$(tput rmso; tput sgr0)
export LESS_TERMCAP_us=$(tput smul; tput bold; tput setaf 7) # white
export LESS_TERMCAP_ue=$(tput rmul; tput sgr0)
export LESS_TERMCAP_mr=$(tput rev)
export LESS_TERMCAP_mh=$(tput dim)
export LESS_TERMCAP_ZN=$(tput ssubm)
export LESS_TERMCAP_ZV=$(tput rsubm)
export LESS_TERMCAP_ZO=$(tput ssupm)
export LESS_TERMCAP_ZW=$(tput rsupm)
CLICOLOR=1
}
# Initialize environment specific to Darwin / Mac OS X
function initialize_darwin() {
case "${ARCH}" in
arm64) HOMEBREW_PREFIX="/opt/homebrew";;
default) HOMEBREW_PREFIX="/usr/local";;
esac
if [ -d "${HOMEBREW_PREFIX}/bin" ]; then
export HOMEBREW_PREFIX
export HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}"
export HOMEBREW_CELLAR="${HOMEBREW_PREFIX}/Cellar"
pathadd -p "${HOMEBREW_PREFIX}/bin"
pathadd "${HOMEBREW_PREFIX}/share/man" "MANPATH"
pathadd "${HOMEBREW_PREFIX}/share/info" "INFOPATH"
# Core Utils, if installed
work_dir="${HOMEBREW_PREFIX}/opt/coreutils/libexec/gnubin"
pathadd -p "${work_dir}" && pathadd -p "${work_dir%/*}/gnuman" "MANPATH"
# GNU Diff Utils, if installed
work_dir="${HOMEBREW_PREFIX}/opt/diffutils/bin"
pathadd -p "${work_dir}" && pathadd -p "${work_dir%/*}/share/man" "MANPATH"
# GNU Tar, if installed
work_dir="${HOMEBREW_PREFIX}/opt/gnu-tar/libexec/gnubin"
pathadd -p "${work_dir}" && pathadd -p "${work_dir%/*}/gnuman" "MANPATH"
# Ruby installed by brew
work_dir="${HOMEBREW_PREFIX}/opt/ruby/bin"
pathadd -p "${work_dir}" && pathadd -p "${work_dir%/*}/share/man" "MANPATH"
fi
# Maven, if installed
export MAVEN_HOME=/usr/local/apache-maven
pathadd "${MAVEN_HOME}/bin" || unset MAVEN_HOME
# Android Debug Bridge
work_dir=$(readlink -f ~/Applications/platform-tools)
pathadd -p "${work_dir}"
git_credential_type="osxkeychain"
configure_colors || export LSCOLORS='DxGxcxdxCxcgcdabagacad'
}
# Initialize environment specific to linux
function initialize_linux() {
# Android Debug Bridge
work_dir=/opt/local/platform-tools
pathadd -p "${work_dir}"
export LOCAL_BIN=~/.local/bin
pathadd "${LOCAL_BIN}" || unset LOCAL_BIN
configure_colors || export LS_COLORS='rs=0:di=01;33:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:';
}
function configure_aliases() {
# no need for aliases if non-interactive
[[ $- =~ i ]] || return
local cmd_opts="--color=auto"
local ls_opts="${cmd_opts}"
[ -z "${LS_COLORS}" ] && ls_opts="-G"
# set aliases if unset
## Colorize the ls output ##
alias ls &>/dev/null || alias ls="$(which ls) ${ls_opts}"
## Use a long listing format ##
alias ll &>/dev/null || alias ll="$(which ls) -la ${ls_opts}"
## Show hidden files ##
alias l. &>/dev/null || alias l.="$(which ls) -d .* ${ls_opts}"
alias dir &>/dev/null || alias dir="$(which ls) --format=vertical ${ls_opts}"
alias vdir &>/dev/null || alias vdir="$(which ls) --format=long ${ls_opts}"
alias grep &>/dev/null || alias grep="$(which grep) ${cmd_opts}"
alias fgrep &>/dev/null || alias fgrep="$(which fgrep) ${cmd_opts}"
alias egrep &>/dev/null || alias egrep="$(which egrep) ${cmd_opts}"
# Terraform
alias tf &>/dev/null || alias tf="terraform"
# Show current network connections to servers
alias ipview="netstat -anpl | grep :80 | awk {'print \$5'} | cut -d\":\" -f1 | sort | uniq -c | sort -n | sed -e 's/^ *//' -e 's/ *\$//'"
# Show local open listeners
alias openports='netstat -nape --inet'
# Weather
alias weather &>/dev/null || alias weather="curl http://wttr.in/"
# Public IP
alias whatismyip="dig +short myip.opendns.com @resolver1.opendns.com"
if [ "${OS}" == "Linux" ]; then
alias ubupdate="sudo apt update && sudo apt dist-upgrade -y --auto-remove"
fi
}
function source_rc() {
local rcfile="$1"
local always="${2:-0}"
local retval=1
if [[ $- =~ i ]] || (( always )); then
if source "${rcfile}" &>/dev/null; then
retval=0
fi
fi
return $((retval))
}
function add_command_to_complete() {
local cmd="$1"
local completer="${2:-${cmd}}"
local retval=1
if [[ $- =~ i ]] && [ -n "${cmd}" ] && type -P "${cmd}" &>/dev/null && type -P "${completer}" &>/dev/null; then
complete -C $(which "${completer}") "${cmd}" &>/dev/null
retval=$?
fi
return $((retval))
}
function source_cmd_completion_bash() {
local cmd="$1"
shift
local retval=0
local cmd_opts
local -a opts=("$@")
(( ${#opts[@]} )) || opts=("completion" "bash")
local alt_opts=("--completion-bash")
local completion_script_dir="$HOME/.local/bash-completion"
local completion_script="${completion_script_dir}/${cmd}.completion"
[ -d "${completion_script_dir}" ] || mkdir -p "${completion_script_dir}"
if [[ $- =~ i ]] && [ -n "${cmd}" ] && type -P "${cmd}" &>/dev/null; then
if ! [ -f "${completion_script}" ]; then
{ logdebug "generating cmd completion script: ${cmd} ${cmd_opts}"; }
if ! "${cmd}" "${opts[@]}" > "${completion_script}" 2>/dev/null; then
if ! "${cmd}" "${alt_opts[@]}" > "${completion_script}" 2>/dev/null; then
logwarn "cmd completion script not generated!"
return 1
fi
fi
fi
if [ -f "${completion_script}" ]; then
{ printf -v cmd_opts "%s " "${opts[@]}"; logdebug "sourcing cmd completion: ${cmd}"; }
source "${completion_script}" &>/dev/null
logdebug "sourced cmd completion: ${retval}"
else
logdebug "cmd completion script missing for ${cmd}"
fi
else
logwarn "unable to source cmd completion: ${cmd} ${opts[@]}"
fi
return $((retval))
}
function configure_shell() {
local work_dir v rc msg
local -a bin_vers=()
local -a completion_enabled=()
# bash completion
local -a bash_completion=()
bash_completion+=("/usr/share/bash-completion/bash_completion")
bash_completion+=("/etc/bash_completion")
bash_completion+=("/usr/local/etc/bash_completion")
# commands for complete -C
local -A cmd_complete=()
cmd_complete["aws"]="aws_completer"
cmd_complete["terraform"]="terraform"
cmd_complete["packer"]="packer"
cmd_complete["vault"]="vault"
cmd_complete["consul"]="consul"
# commands for sourcing with completion bash arg
local -a cmd_completion_bash=()
cmd_completion_bash+=("k3d")
cmd_completion_bash+=("helm")
cmd_completion_bash+=("kubectl")
cmd_completion_bash+=("eksctl")
cmd_completion_bash+=("node")
cmd_completion_bash+=("npm")
for rc in "${bash_completion[@]}"; do
source_rc "${rc}" && completion_enabled+=("bash") && break
done
pathadd "${HOME}/bin"
work_dir=/usr/local/mysql/bin
pathadd "${work_dir}"
# Python Config
if type -P python3 &>/dev/null; then
if [ ! "$(readlink -f $(type -P python))" == "$(readlink -f $(type -P python3))" ]; then
lognotify "setting python alias, install python3-as-python package to set properly"
alias python="python3"
fi
fi
type -P python &>/dev/null && { v=$(python --version); bin_vers+=("python v${v##* }"); }
if source_rc "${HOME}/.nvm/nvm.sh"; then
export NVM_DIR="${HOME}/.nvm"
[ "$(nvm current)" == "none" ] && { loginfo "enabling stable node with nvm..."; nvm use stable &>/dev/null; }
bin_vers+=("node $(nvm version) (nvm managed)")
source_rc "${NVM_DIR}/bash_completion" && completion_enabled+=("nvm")
elif type -P node &>/dev/null; then
bin_vers+=("node $(node --version)")
fi
export GOROOT=/usr/local/go
pathadd "${GOROOT}/bin" && bin_vers+=("$(go version | awk '{print $3}' | sed 's/go\(.*\)/go \1/g')") || unset GOROOT
pathadd "${HOME}/google-cloud-sdk/bin" && { v=$(gcloud version 2>/dev/null | grep -i "google cloud sdk"); bin_vers+=("gcloud v${v##* }"); }
source_rc "${HOME}/google-cloud-sdk/completion.bash.inc" && completion_enabled+=("gcloud")
# RVM overrides any pre-installed ruby
if source_rc "${HOME}/.rvm/scripts/rvm"; then
v=$(ruby -v)
bin_vers+=("ruby v${v:5:5} (rvm managed)")
source_rc "${HOME}/.rvm/scripts/completion" && completion_enabled+=("rvm")
elif type -P ruby &>/dev/null; then
local v=$(ruby -v)
bin_vers+=("ruby v${v:5:5}")
fi
# Enable bash programmable completion features in interactive shells
for cmd in "${!cmd_complete[@]}"; do
add_command_to_complete "${cmd}" "${cmd_complete[$cmd]}" && completion_enabled+=("${cmd}")
done
for cmd in "${cmd_completion_bash[@]}"; do
source_cmd_completion_bash "${cmd}" && completion_enabled+=("${cmd}")
done
# added by travis gem
if source_rc "${HOME}/.travis/travis.sh"; then
bin_vers+=("travis cli")
fi
if [[ $- =~ i ]]; then
if (( ${#bin_vers[@]} )); then
printf -v msg "%s\n" "${bin_vers[@]/#/ - }"
loginfo "Installed tools:\n$msg"
fi
if (( ${#completion_enabled[@]} )); then
printf -v msg "%s\n" "${completion_enabled[@]/#/ - }"
loginfo "Command Line Completion Enabled:\n$msg"
fi
fi
}
declare REMOTE
function init_bash() {
get_os_info
# Disable the bell in interactive shell
if [[ $- =~ i ]]; then
lognotify "Initializing ${SYSTEM} with ${MACHINE_TYPE} architecture"
initialize_${SYSTEM}
bind "set bell-style visible"
[ -n "${LANG}" ] || export LANG="en_US.UTF-8"
[[ $SSH_CLIENT || $SSH2_CLIENT ]] && REMOTE=1 || REMOTE=0
# Get DPI of screen
if [ "${MACHINE_TYPE}" == "host" ] && type -P xrandr &>/dev/null && ! (( REMOTE == 0 )); then
# what was this doing??
read -r X Y < <(xrandr | grep -w connected | awk '{print $(NF-2)" "$(NF)}')
X="${X//[[:alpha:]]}"
Y="${Y//[[:alpha:]]}"
fi
# terminal size
read -r ROWS COLUMNS < <(stty size)
[ -z "$COLUMNS" ] && COLUMNS=140
# Make bash check it's window size after a process completes
shopt -s checkwinsize
# Make bash fix common folder name spelling mistakes (ect vs. etc)
shopt -s cdspell
## History settings
# Expand the history size
export HISTFILESIZE=1000000
export HISTSIZE=50000
# Don't put duplicate lines in the history and do not add lines that start with a space
export HISTCONTROL=erasedups:ignoredups:ignorespace
# Add timestamp
export HISTTIMEFORMAT='[%F@%T] '
# Set the default editor
type vim &>/dev/null && export EDITOR=vim || export EDITOR=vi
export VISUAL=$EDITOR
fi
configure_shell
configure_aliases
configure_ps1
# remove bashrc functions from env to avoid clutter
unset -f pathadd
unset -f get_os_info
unset -f configure_colors
unset -f configure_aliases
unset -f configure_git_prompt
unset -f configure_ps1
unset -f source_rc
unset -f add_command_to_complete
unset -f source_cmd_completion_bash
unset -f configure_shell
unset -f initialize_darwin
unset -f initialize_linux
unset -f lognotify
unset -f logdebug
unset -f loginfo
unset -f logwarn
unset -f logfail
unset -f logmsg
unset -f enum
[[ $- =~ i ]] || unset -f command_prompt
unset MACHINE_TYPE OSINFO SYSTEM ARCH REMOTE
unset LLSILENT LLFATAL LLFAIL LLWARN LLINFO LLDEBUG LOGLEVEL
}
init_bash
unset -f init_bash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment