diff --git a/.10_macos.bash b/.10_macos.bash index bd149dc..e008292 100755 --- a/.10_macos.bash +++ b/.10_macos.bash @@ -3,19 +3,17 @@ if [[ "${OSTYPE:-}" != "darwin"* ]]; then fi -alias dl="cd ~/Downloads" -alias dt="cd ~/Desktop" - -# macOS has no `sha1sum`, so use `shasum` as a fallback -command -v sha1sum > /dev/null || alias sha1sum="shasum" -command -v sha256sum > /dev/null || alias sha256sum="shasum --algorithm 256" - if [[ ! -x "$(command -v brew)" && -f /opt/homebrew/bin/brew ]]; then eval "$(/opt/homebrew/bin/brew shellenv)" fi + +##### App installs ##### + +# Homebrew packages if [[ -x "$(command -v brew)" ]]; then command -v gawk > /dev/null || brew install gawk + command -v gdate > /dev/null || brew install coreutils command -v gsed > /dev/null || brew install gnu-sed command -v jq > /dev/null || brew install jq command -v rename > /dev/null || brew install rename @@ -29,12 +27,28 @@ if [[ -x "$(command -v brew)" ]]; then fi fi +# App store applications +if [[ -x "$(command -v brew)" && ! -x "$(command -v mas)" ]]; then + brew install mas + # Installed applications aren't enumerated immediately, `mas list` may return nothing +fi +# if [[ -x "$(command -v mas)" ]]; then +# mas_list=$(mas list) +# # 1Password for Safari +# # echo "${mas_list}" | grep '^1569813296 ' &> /dev/null || mas install 1569813296 +# # Menu World Time +# # echo "${mas_list}" | grep '^1446377255 ' &> /dev/null || mas install 1446377255 +# fi + # macOS DNS flush flush() { sudo dscacheutil -flushcache sudo killall -HUP mDNSResponder } + +##### Aliases ##### + # Prefer GNU's coreutils binaries command -v gdate > /dev/null && alias date="gdate" command -v gsed > /dev/null && alias sed="gsed" @@ -42,6 +56,13 @@ command -v gsed > /dev/null && alias sed="gsed" # macOS has no `md5sum`, so use `md5` as a fallback command -v md5sum > /dev/null || alias md5sum="md5" +# macOS has no `sha1sum`, so use `shasum` as a fallback +command -v sha1sum > /dev/null || alias sha1sum="shasum" +command -v sha256sum > /dev/null || alias sha256sum="shasum --algorithm 256" + +alias dl="cd ~/Downloads" +alias dt="cd ~/Desktop" + alias lsports="sudo lsof -iTCP -sTCP:LISTEN -n -P" alias lsp=lsports diff --git a/.20_docker.bash b/.20_docker.bash index 1028cab..94e39d6 100755 --- a/.20_docker.bash +++ b/.20_docker.bash @@ -104,7 +104,7 @@ __docker_funcs() { # @param {string} $1 Container name # @param {number=} $2 Tail length dlogs() { - docker logs --tail ${2:-0} --follow "$1" + docker logs --tail "${2:-0}" --follow "$1" } # Execute `mysql` interactively in a MySQL server container diff --git a/.20_git.bash b/.20_git.bash index 58d8ac9..dbdde87 100644 --- a/.20_git.bash +++ b/.20_git.bash @@ -29,8 +29,8 @@ __git_funcs() { # shellcheck disable=SC2139 alias g${al}="git ${al}" if type __git_aliased_command &> /dev/null; then - complete_func=_git_$(__git_aliased_command ${al}) - type ${complete_func} &> /dev/null && __git_complete g${al} ${complete_func} + complete_func=_git_$(__git_aliased_command "${al}") + type "${complete_func}" &> /dev/null && __git_complete "g${al}" "${complete_func}" fi done @@ -86,7 +86,7 @@ __git_funcs() { # Get the most recent versions from Git tags # @param {number=} $1 Number of versions to show gvs() { - git tag --sort=-version:refname | grep -E '^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' | head -${1:-10} + git tag --sort=-version:refname | grep -E '^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' | head "-${1:-10}" } # Get the most recent version from Git tags diff --git a/.20_kubernetes.bash b/.20_kubernetes.bash index 6b078bf..e4d645a 100755 --- a/.20_kubernetes.bash +++ b/.20_kubernetes.bash @@ -61,158 +61,176 @@ __kube_funcs() { # List all Kubernetes config maps, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kconfigmaps() { - kubectl get configmaps ${1:+--selector="app=$1"} + kubectl get configmaps ${1:+--selector="app=$1"} "${@:2}" } # List all Kubernetes container names, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kcontainers() { - kubectl get pods --output=jsonpath="{.items[*].spec.containers[*].name}" ${1:+--selector="app=$1"} | tr -s '[[:space:]]' '\n' | sort -u + kubectl get pods --output=jsonpath="{.items[*].spec.containers[*].name}" ${1:+--selector="app=$1"} "${@:2}" | tr -s '[[:space:]]' '\n' | sort -u } # Change to a kubectl context # @param {string} $1 Context name + # @param {...string} Additional kubectl options alias kcontext="kubectl config use-context" # List all Kubernetes cron jobs, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kcrons() { - kubectl get cronjob ${1:+--selector="app=$1"} + kubectl get cronjob ${1:+--selector="app=$1"} "${@:2}" } # List all Kubernetes daemon sets - kdaemonsets() { - kubectl get daemonsets - } + # @param {...string} Additional kubectl options + alias kdaemonsets="kubectl get daemonsets" alias kds=kdaemonsets # List all Kubernetes container names given a deployment name # @param {string} $1 Deployment name + # @param {...string} Additional kubectl options kdcontainers() { - kubectl get deployment --output=jsonpath="{.spec.template.spec.containers[*].name}" "$1" | tr -s '[[:space:]]' '\n' | sort -u + kubectl get deployment --output=jsonpath="{.spec.template.spec.containers[*].name}" "$1" "${@:2}" | tr -s '[[:space:]]' '\n' | sort -u } # Delete a Kubernetes pod # @param {string} $1 Pod name + # @param {...string} Additional kubectl options kdel() { - kubectl delete pod "$1" + kubectl delete pod "$1" "${@:2}" } # List all Kubernetes deployment names, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kdeployments() { - kubectl get deployments --output=jsonpath="{.items[*].metadata.name}" ${1:+--selector="app=$1"} | tr -s '[[:space:]]' '\n' | sort -u + kubectl get deployments --output=jsonpath="{.items[*].metadata.name}" ${1:+--selector="app=$1"} "${@:2}" | tr -s '[[:space:]]' '\n' | sort -u } # Open a port forward session to a remote Kubernetes deployment # @param {string} $1 Deployment name # @param {number} $2 Local+remote OR local port number # @param {number=} $3 Remote port number + # @param {...string} Additional kubectl options kforward() { - kubectl port-forward "deployment/$1" "$2${3:+:$3}" + kubectl port-forward "deployment/$1" "$2${3:+:$3}" "${@:4}" } # Show the Kubernetes rollout history for a deployment # @param {string} $1 Deployment name # @param {number=} $2 Revision number + # @param {...string} Additional kubectl options khistory() { - kubectl rollout history "deployment/$1" ${2:+--revision "$2"} + kubectl rollout history "deployment/$1" ${2:+--revision "$2"} "${@:3}" } # List all Kubernetes container image names, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kimages() { - kubectl get pods --output=jsonpath="{.items[*].spec.containers[*].image}" ${1:+--selector="app=$1"} | tr -s '[[:space:]]' '\n' | sort -u + kubectl get pods --output=jsonpath="{.items[*].spec.containers[*].image}" ${1:+--selector="app=$1"} "${@:2}" | tr -s '[[:space:]]' '\n' | sort -u } # List all Kubernetes ingresses, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kingress() { - kubectl get ingress ${1:+--selector="app=$1"} + kubectl get ingress ${1:+--selector="app=$1"} "${@:2}" } # List all Kubernetes jobs, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kjobs() { - kubectl get jobs --label-columns="app" ${1:+--selector="app=$1"} + kubectl get jobs --label-columns="app" ${1:+--selector="app=$1"} "${@:2}" } # Follow the logs from all Kubernetes containers with a given app label # @param {string} $1 App label # @param {number=} $2 Tail length + # @param {...string} Additional kubectl options klogs() { if [[ -x "$(command -v stern)" ]]; then - stern --timestamps --tail ${2:-0} --selector "app=$1" + stern --timestamps --tail "${2:-0}" --selector "app=$1" else - kubectl logs --all-containers --timestamps --follow --max-log-requests=9999 --tail=${2:-0} --selector="app=$1" + kubectl logs --all-containers --timestamps --follow --max-log-requests=9999 "--tail=${2:-0}" --selector="app=$1" fi } # List all Kubernetes namespaces - knamespaces() { - kubectl get namespaces - } + # @param {...string} Additional kubectl options + alias knamespaces="kubectl get namespaces" alias kns=knamespaces # List all Kubernetes nodes - knodes() { - kubectl get nodes --label-columns="nodegroup-name" - } + # @param {...string} Additional kubectl options + alias knodes="kubectl get nodes --label-columns='nodegroup-name'" # Get the pod name of the newest running Kubernetes containers given an app label # @param {string} $1 App label + # @param {...string} Additional kubectl options kpod() { - kubectl get pods --selector="app=$1" --field-selector=status.phase=Running --sort-by=".metadata.creationTimestamp" --show-labels | tail -n +2 | sed 's/[ ][ ]*/ /g' | sort -u -k6,6 | awk '{print $1}' + kubectl get pods --selector="app=$1" --field-selector=status.phase=Running --sort-by=".metadata.creationTimestamp" --show-labels "${@:2}" | tail -n +2 | sed 's/[ ][ ]*/ /g' | sort -u -k6,6 | awk '{print $1}' } # List all Kubernetes pods, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kpods() { - kubectl get pods --label-columns="app" ${1:+--selector="app=$1"} + kubectl get pods --label-columns="app" ${1:+--selector="app=$1"} "${@:2}" } # Reboot all Kubernetes deployments with a given app label # @param {string} $1 App label + # @param {...string} Additional kubectl options kreboot() { if [[ -z "$1" ]]; then return 1 fi for DEPLOYMENT in $(kdeployments "$1"); do - kubectl rollout restart "deployment/${DEPLOYMENT}" + kubectl rollout restart "deployment/${DEPLOYMENT}" "${@:2}" done } # Get the most recent revision number for a Kubernetes deployment # @param {string} $1 Deployment name + # @param {...string} Additional kubectl options krevision() { - khistory "$1" | grep -E '^[0-9]\+' | sort --sort=numeric --reverse | head -1 | awk '{print $1}' + khistory "$1" "${@:2}" | grep -E '^[0-9]\+' | sort --sort=numeric --reverse | head -1 | awk '{print $1}' } # Roll back a Kubernetes deployment # @param {string} $1 Deployment name # @param {number=} $1 Revision number + # @param {...string} Additional kubectl options krollback() { - kubectl rollout undo "deployment/$1" ${2:+--to-revision=$2} + kubectl rollout undo "deployment/$1" ${2:+--to-revision=$2} "${@:3}" } # List all Kubernetes replica sets, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options kreplicasets() { - kubectl get rs ${1:+--selector="app=$1"} + kubectl get rs ${1:+--selector="app=$1"} "${@:2}" } alias krs=kreplicasets # List all Kubernetes secrets, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options ksecrets() { - kubectl get secrets ${1:+--selector="app=$1"} + kubectl get secrets ${1:+--selector="app=$1"} "${@:2}" } # Describe a Kubernetes service to get info such as labels, IP, and load balancer ingress # @param {string=} $1 App label + # @param {...string} Additional kubectl options kservices() { - kubectl get services ${1:+--selector="app=$1"} + kubectl get services ${1:+--selector="app=$1"} "${@:2}" } # Execute `sh` interactively in the Kubernetes pod @@ -222,15 +240,15 @@ __kube_funcs() { } # List all Kubernetes stateful sets - kstatefulsets() { - kubectl get statefulsets - } + # @param {...string} Additional kubectl options + alias kstatefulsets="kubectl get statefulsets" alias kss=kstatefulsets # Emulate a `top` command for Kubernetes pods, optionally filtering to an application # @param {string=} $1 App label + # @param {...string} Additional kubectl options ktop() { - watch "kubectl top pods --containers ${1:+--selector="app=$1"} | awk 'NR == 1; NR > 1 {print \$0 | \"sort -n -k3 -r\"}'" + watch "kubectl top pods --containers ${1:+--selector="app=$1"} \"${*:2}\" | awk 'NR == 1; NR > 1 {print \$0 | \"sort -n -k3 -r\"}'" } } __kube_funcs diff --git a/.20_nodejs.bash b/.20_nodejs.bash index 43482c0..8ea951f 100755 --- a/.20_nodejs.bash +++ b/.20_nodejs.bash @@ -34,9 +34,9 @@ __nodejs_volta __nodejs_funcs() { # @link https://github.com/npm/npm/issues/15536#issuecomment-392657820 ndeprecated() { - jq -r '.dependencies,.devDependencies|keys[] as $k|"\($k)@\(.[$k])"' package.json | while read line; do \ - printf "$line: " - [ "$(npm show "$line" | grep --count --extended-regexp '^DEPRECATED')" != "0" ] && \ + jq -r '.dependencies,.devDependencies|keys[] as $k|"\($k)@\(.[$k])"' package.json | while read -r line; do \ + printf "%s: " "${line}" + [ "$(npm show "${line}" | grep --count --extended-regexp '^DEPRECATED')" != "0" ] && \ printf "\e[1;31m""DEPRECATED\n""\e[0m" || \ printf "\e[1;32m""not deprecated\n""\e[0m" done diff --git a/.20_python.bash b/.20_python.bash new file mode 100644 index 0000000..8b87b5d --- /dev/null +++ b/.20_python.bash @@ -0,0 +1,39 @@ +__python_pyenv() { + PYENV_ROOT="${HOME}/.pyenv" + if [[ ! -d "${PYENV_ROOT}" ]]; then + return 0 + fi + export PYENV_ROOT + unset -f pyenv &> /dev/null + + # `pyenv init -` but with some portability tweaks + # shellcheck disable=SC1078 + PATH="$(bash --norc -ec 'IFS=:; paths=($PATH); + for i in ${!paths[@]}; do + if [[ ${paths[i]} == "''${PYENV_ROOT}/shims''" ]]; then unset '\''paths[i]'\''; + fi; done; + echo "${paths[*]}"' )" + PATH="${PYENV_ROOT}/shims:${PATH}" + export PATH + PYENV_SHELL=$(basename "${SHELL}") + export PYENV_SHELL + source "$(dirname "$(readlink -f "$(which pyenv)")")/../completions/pyenv.zsh" + command pyenv rehash 2>/dev/null + pyenv() { + local command + command="${1:-}" + if [ "$#" -gt 0 ]; then + shift + fi + + case "$command" in + rehash|shell) + eval "$(pyenv "sh-$command" "$@")" + ;; + *) + command pyenv "$command" "$@" + ;; + esac + } +} +__python_pyenv diff --git a/.20_temporal.bash b/.20_temporal.bash index 0f7caa2..c68af29 100644 --- a/.20_temporal.bash +++ b/.20_temporal.bash @@ -50,7 +50,7 @@ __tctl_funcs() { local temp_sep=';' local column_headers - column_headers=$(echo "${tctl_output}" | head -${column_count} | awk '{print $1}') + column_headers=$(echo "${tctl_output}" | head "-${column_count}" | awk '{print $1}') { echo "${column_headers}" echo "${tctl_output}" | sed 's/^[^ ]*\s*//g' | sed 's/ *$//g' diff --git a/.everythingrc b/.everythingrc index 1ffc7c8..f6da370 100755 --- a/.everythingrc +++ b/.everythingrc @@ -6,6 +6,10 @@ # https://docs.brew.sh/Shell-Completion : ${HOMEBREW_PREFIX:=$(if type brew &> /dev/null; then brew --prefix; fi)} +if [[ ! -x "$(command -v brew)" && -f /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" +fi + ##### Bash ##### diff --git a/.githooks/post-merge b/.githooks/post-merge index 293f125..e2a130b 100755 --- a/.githooks/post-merge +++ b/.githooks/post-merge @@ -1,11 +1,24 @@ #!/usr/bin/env bash set -euo pipefail +# https://git-scm.com/docs/githooks#_description +# Before Git invokes a hook, it changes its working directory to either $GIT_DIR in a bare repository or the +# root of the working tree in a non-bare repository. + +# https://git-scm.com/docs/githooks#_post_merge +# This hook is invoked by git-merge[1], which happens when a git pull is done on a local repository. +# The hook takes a single parameter, a status flag specifying whether or not the merge being done was a +# squash merge. This hook cannot affect the outcome of git merge and is not executed, if the merge +# failed due to conflicts. + echo "↓↓↓↓↓ emmercm/dotfiles post-merge ↓↓↓↓↓" echo "Running install.sh" ./install.sh +echo "Running settings.sh" +./settings.sh + echo "↑↑↑↑↑ emmercm/dotfiles post-merge ↑↑↑↑↑" diff --git a/settings.sh b/settings.sh index 4cc3d6e..5d1f5ee 100755 --- a/settings.sh +++ b/settings.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euo pipefail +# https://emmer.dev/blog/automate-your-macos-defaults/ + # Git settings if [[ -x "$(command -v git)" && -s ~/.gitignore_global ]]; then @@ -60,9 +62,13 @@ if [[ "${OSTYPE:-}" == "darwin"* ]]; then defaults -currentHost write com.apple.controlcenter BatteryShowPercentage -bool true # Clock options... show AM/PM: false defaults write com.apple.menuextra.clock Show24Hour -int 1 + # Siri: don't show in menu bar + defaults write com.apple.Siri StatusMenuVisible -int 0 killall SystemUIServer # ***** Settings > Siri & Spotlight ***** + # Ask Siri: false + defaults write com.apple.assistant.support "Assistant Enabled" -int 0 # ***** Settings > Privacy & Security *****