diff --git a/.bash_aliases b/.bash_aliases index f672c27..d287f8b 100644 --- a/.bash_aliases +++ b/.bash_aliases @@ -1,7 +1,7 @@ # shellcheck shell=bash # General System Utilities alias c='clear' -alias gh='history | grep' +alias grephist='history | grep' alias lt='ls --human-readable --size -1 -S --classify' alias sha='shasum -a 256' alias sshrestart='sudo /etc/init.d/ssh restart' @@ -33,18 +33,23 @@ alias speed='speedtest-cli --server 2406 --simple' alias dcdown='docker compose down' alias dcup='docker compose up' alias dexec='docker exec -it' +alias dockerclean='docker rm $(docker ps -a -q)' alias dockerdu='docker system df' alias dockerls='docker ps -a' +# shellcheck disable=SC2142 # $3 is awk's field variable, not a shell positional param +alias docker-rmi-untagged='docker rmi $(docker images | grep "^" | awk "{print \$3}")' +alias dockerstopall='docker stop $(docker ps -a -q)' alias dlogs='docker logs' # Development and Git startgit() { cd "$(git rev-parse --show-toplevel)" && git checkout master && git pull; } # File Management -alias rmrf='rm -rf' # Use with caution +alias rmrf='rm -rf' # Use with caution alias mkdir='mkdir -p' alias mv='mv -i' alias cp='cp -i' +alias chmod002='sudo chmod -R a=,a+rX,u+w,g+w' # Searching alias grep='grep --color=auto' @@ -52,7 +57,7 @@ alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto' # System Monitoring -alias top='htop' # Requires htop installed +alias top='htop' # Requires htop installed alias df='df -h' alias du='du -ch' @@ -70,9 +75,9 @@ alias ....='cd ../../../' alias .....='cd ../../../../' # Enhanced ls -alias ll='ls -lAFh' -alias la='ls -A' -alias l='ls -CF' +alias ll='ls -lAFha --color=auto --group-directories-first' +alias la='ls -A --color=auto --group-directories-first' +alias l='ls -CF --color=auto --group-directories-first' # Git Operations alias gs='git status' diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index c209c68..f75a79c 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -15,7 +15,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - +jobs: shellcheck: runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 84d96ad..42fc340 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,6 +44,7 @@ repos: hooks: - id: shfmt args: [--write, --indent, '2', --case-indent, --binary-next-line] + exclude: ^docker_aliases\.sh$ # zsh-specific ${(f)...} syntax is incompatible with shfmt - repo: https://github.com/adrienverge/yamllint rev: v1.35.1 diff --git a/docker_aliases.sh b/docker_aliases.sh index cf7d494..a38d2e0 100644 --- a/docker_aliases.sh +++ b/docker_aliases.sh @@ -254,6 +254,7 @@ function grpdown { services=$(_dc_group "$grp") [[ -z "$services" ]] && { _dc_red "No services in group: $grp"; return 1; } _dc_blue "Stopping $grp: $services" + # shellcheck disable=SC2086 # word splitting intentional — $services is space-separated list _dc_compose stop $services "$@" _dc_green "$grp stopped" } @@ -265,6 +266,7 @@ function grpup { services=$(_dc_group "$grp") [[ -z "$services" ]] && { _dc_red "No services in group: $grp"; return 1; } _dc_blue "Starting $grp: $services" + # shellcheck disable=SC2086 # word splitting intentional — $services is space-separated list _dc_compose up -d $services "$@" _dc_green "$grp started" } @@ -275,6 +277,7 @@ function grplogs { local services services=$(_dc_group "$grp") [[ -z "$services" ]] && { _dc_red "No services in group: $grp"; return 1; } + # shellcheck disable=SC2086 # word splitting intentional — $services is space-separated list _dc_compose logs -f $services "$@" } @@ -284,6 +287,7 @@ function grpps { local services services=$(_dc_group "$grp") [[ -z "$services" ]] && { _dc_red "No services in group: $grp"; return 1; } + # shellcheck disable=SC2086 # word splitting intentional — $services is space-separated list _dc_compose ps $services "$@" } @@ -294,6 +298,7 @@ function grprestart { services=$(_dc_group "$grp") [[ -z "$services" ]] && { _dc_red "No services in group: $grp"; return 1; } _dc_blue "Restarting $grp: $services" + # shellcheck disable=SC2086 # word splitting intentional — $services is space-separated list _dc_compose restart $services "$@" _dc_green "$grp restarted" } diff --git a/tmux_session_picker.sh b/tmux_session_picker.sh index 3521804..74b956a 100644 --- a/tmux_session_picker.sh +++ b/tmux_session_picker.sh @@ -40,105 +40,110 @@ _TMUX_SESS_PAIRS=( _TMUX_NOTABLE_PROCS=(claude vim nvim docker node python pwsh htop) # === Implementation === -if command -v tmux >/dev/null 2>&1 && - [[ $- == *i* ]] && [[ -n "$SSH_TTY" ]] && [[ -z "$TMUX" ]] && [[ -z "$NOTMUX" ]]; then +if command -v tmux >/dev/null 2>&1 \ + && [[ $- == *i* ]] && [[ -n "$SSH_TTY" ]] && [[ -z "$TMUX" ]] && [[ -z "$NOTMUX" ]]; then - # Create sessions for valid paths - for _pair in "${_TMUX_SESS_PAIRS[@]}"; do - _sess_name="${_pair%%=*}" - _sess_path="${_pair#*=}" - if [[ -d "$_sess_path" ]] && ! tmux has-session -t "$_sess_name" 2>/dev/null; then - tmux new-session -d -s "$_sess_name" -c "$_sess_path" - fi - done + # Create sessions for valid paths + for _pair in "${_TMUX_SESS_PAIRS[@]}"; do + _sess_name="${_pair%%=*}" + _sess_path="${_pair#*=}" + if [[ -d "$_sess_path" ]] && ! tmux has-session -t "$_sess_name" 2>/dev/null; then + tmux new-session -d -s "$_sess_name" -c "$_sess_path" + fi + done - # Detect notable processes in a tmux session via pstree - _tmux_procs() { - local sid=$1 - local pids procs="" - pids=$(tmux list-panes -t "$sid" -F '#{pane_pid}' 2>/dev/null) - for _pid in $pids; do - local tree - tree=$(pstree -A "$_pid" 2>/dev/null) || continue - for _proc in "${_TMUX_NOTABLE_PROCS[@]}"; do - [[ "$tree" == *"$_proc"* ]] && procs="${procs:+$procs, }$_proc" - done - done - printf '%s' "${procs:--}" - } + # Detect notable processes in a tmux session via pstree + _tmux_procs() { + local sid=$1 + local pids procs="" + pids=$(tmux list-panes -t "$sid" -F '#{pane_pid}' 2>/dev/null) + for _pid in $pids; do + local tree + tree=$(pstree -A "$_pid" 2>/dev/null) || continue + for _proc in "${_TMUX_NOTABLE_PROCS[@]}"; do + [[ "$tree" == *"$_proc"* ]] && procs="${procs:+$procs, }$_proc" + done + done + printf '%s' "${procs:--}" + } - # Relative time since last activity in a session - _tmux_age() { - local sid=$1 - local now age_s - now=$(date +%s) - local last - last=$(tmux display-message -t "$sid" -p '#{session_activity}' 2>/dev/null) || { printf '?'; return; } - age_s=$(( now - last )) - if (( age_s < 60 )); then printf 'just now' - elif (( age_s < 3600 )); then printf '%dm ago' "$(( age_s / 60 ))" - elif (( age_s < 86400 )); then printf '%dh ago' "$(( age_s / 3600 ))" - else printf '%dd ago' "$(( age_s / 86400 ))"; fi + # Relative time since last activity in a session + _tmux_age() { + local sid=$1 + local now age_s + now=$(date +%s) + local last + last=$(tmux display-message -t "$sid" -p '#{session_activity}' 2>/dev/null) || { + printf '?' + return } + age_s=$((now - last)) + if ((age_s < 60)); then + printf 'just now' + elif ((age_s < 3600)); then + printf '%dm ago' "$((age_s / 60))" + elif ((age_s < 86400)); then + printf '%dh ago' "$((age_s / 3600))" + else printf '%dd ago' "$((age_s / 86400))"; fi + } - # Session picker with loop (re-displays after creating new sessions) - while true; do - _tmux_names=() - _tmux_count=0 + # Session picker with loop (re-displays after creating new sessions) + while true; do + _tmux_names=() + _tmux_count=0 - printf '\n' - printf '# %-16s %s %-20s %s\n' 'SESSION' 'WIN' 'PROCESSES' 'LAST ACCESS' - printf -- '--- %-16s %s %-20s %s\n' '----------------' '---' '--------------------' '-------------------' + printf '\n' + printf '# %-16s %s %-20s %s\n' 'SESSION' 'WIN' 'PROCESSES' 'LAST ACCESS' + printf -- '--- %-16s %s %-20s %s\n' '----------------' '---' '--------------------' '-------------------' - while IFS='|' read -r _sname _swins _satt; do - (( _tmux_count++ )) - _tmux_names[$_tmux_count]="$_sname" - _label="${_sname}${_satt:+ *}" - _procs=$(_tmux_procs "$_sname") - _age=$(_tmux_age "$_sname") - printf '%-4s %-16s %3s %-20s %s\n' "${_tmux_count})" "$_label" "$_swins" "$_procs" "$_age" - done < <(tmux list-sessions -F '#{session_name}|#{session_windows}|#{?session_attached,*,}' 2>/dev/null) + while IFS='|' read -r _sname _swins _satt; do + ((_tmux_count++)) + _tmux_names[_tmux_count]="$_sname" + _label="${_sname}${_satt:+ *}" + _procs=$(_tmux_procs "$_sname") + _age=$(_tmux_age "$_sname") + printf '%-4s %-16s %3s %-20s %s\n' "${_tmux_count})" "$_label" "$_swins" "$_procs" "$_age" + done < <(tmux list-sessions -F '#{session_name}|#{session_windows}|#{?session_attached,*,}' 2>/dev/null) - printf '\n[1-%d] attach [n] new [s] skip\n' "$_tmux_count" - printf '> ' - read -r _choice + printf '\n[1-%d] attach [n] new [s] skip\n' "$_tmux_count" + printf '> ' + read -r _choice - case "$_choice" in - s|skip) - break - ;; - n|new) - printf 'Session name: ' - read -r _new_name - printf 'Start directory: ' - read -r _new_path - _new_path="${_new_path:-$HOME}" - if [[ -n "$_new_name" ]] && [[ -d "$_new_path" ]]; then - tmux new-session -d -s "$_new_name" -c "$_new_path" - else - printf 'Invalid name or path.\n' >&2 - continue - fi - ;; - [0-9]*) - if (( _choice >= 1 && _choice <= _tmux_count )); then - exec tmux attach -t "${_tmux_names[_choice]}" - else - printf 'Invalid selection.\n' >&2 - fi - ;; - "") - ;; - *) - # Try as session name directly - if tmux has-session -t "$_choice" 2>/dev/null; then - exec tmux attach -t "$_choice" - else - printf 'Unknown session: %s\n' "$_choice" >&2 - fi - ;; - esac - done + case "$_choice" in + s | skip) + break + ;; + n | new) + printf 'Session name: ' + read -r _new_name + printf 'Start directory: ' + read -r _new_path + _new_path="${_new_path:-$HOME}" + if [[ -n "$_new_name" ]] && [[ -d "$_new_path" ]]; then + tmux new-session -d -s "$_new_name" -c "$_new_path" + else + printf 'Invalid name or path.\n' >&2 + continue + fi + ;; + [0-9]*) + if ((_choice >= 1 && _choice <= _tmux_count)); then + exec tmux attach -t "${_tmux_names[_choice]}" + else + printf 'Invalid selection.\n' >&2 + fi + ;; + "") ;; + *) + # Try as session name directly + if tmux has-session -t "$_choice" 2>/dev/null; then + exec tmux attach -t "$_choice" + else + printf 'Unknown session: %s\n' "$_choice" >&2 + fi + ;; + esac + done - unset _pair _sess_name _sess_path _tmux_names _tmux_count _sname _swins _satt _choice _new_name _new_path _label _procs _age _pid _proc + unset _pair _sess_name _sess_path _tmux_names _tmux_count _sname _swins _satt _choice _new_name _new_path _label _procs _age _pid _proc fi