diff options
Diffstat (limited to 'config/profile')
| -rwxr-xr-x | config/profile | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/config/profile b/config/profile new file mode 100755 index 0000000..8f63f9e --- /dev/null +++ b/config/profile @@ -0,0 +1,357 @@ +# shellcheck shell=bash +# profiling helpers +__profile_enabled=false +__timestamp() { + # date +%s + perl -MTime::HiRes=time -e 'printf "%d\n", time * 1000' +} + if [ $__profile_enabled = true ]; then + __previous_timestamp=$(__timestamp) + fi +_profile() { + if [ $__profile_enabled = true ]; then + _new_timestamp=$(__timestamp) + _diff=$((_new_timestamp - __previous_timestamp)) + echo "$_diff $1" + __previous_timestamp=$_new_timestamp + fi +} + +_profile entry + +# The INTERACTIVE layer. +# +# Plain environment (PATH, XDG, locale, tool homes) lives in config/env, which +# is POSIX sh and is loaded by *every* shell — see its header. This file holds +# only what must NOT run in every shell: aliases, history, terminal setup, and +# anything expensive (theme, jenv, and whatever profile.d brings in). +# +# Reached via ~/.zshrc (interactive zsh) and ~/.profile (login bash). Sourcing +# config/env here too, because a login bash reaches ~/.profile without ever +# passing through ~/.zshenv. It is idempotent, so the double load is harmless. +if [ -r "$HOME/.env" ]; then + # shellcheck source=/dev/null + . "$HOME/.env" +else + # No ~/.env yet. A pull that does not run link.sh (by hand, or a sync without + # --link) can leave this file updated and the ~/.env symlink still absent + # - which would silently cost it PATH, XDG, locale and EDITOR. Fall back to + # the copy next to this file, found through the ~/.profile symlink that got + # us here. Harmless once link.sh has run. + __dotfiles_self=$(readlink "$HOME/.profile" 2>/dev/null) + if [ -n "$__dotfiles_self" ] && [ -r "${__dotfiles_self%/*}/env" ]; then + # shellcheck source=/dev/null + . "${__dotfiles_self%/*}/env" + fi + unset __dotfiles_self +fi +_profile env + +# Everything below this point is bash/zsh syntax, and this file is not read +# only by bash and zsh: Ubuntu's /bin/sh is dash, and `sh -l` sources +# ~/.profile. dash cannot parse `[[ ]]` (noisy but survivable) and refuses a +# function named `exit`, which is a POSIX special builtin - that one is a fatal +# parse error, so dash dies here and never runs the command it was given. That +# silently broke every `sh -lc '...'` on every Linux host; it surfaced as a +# background daemon spawned that way failing before it could even create the +# log file its error message pointed at. macOS is unaffected: its /bin/sh is +# bash, so BASH_VERSION is set and the rest of the file still runs there. +# +# config/env above is POSIX and has already been sourced, so a plain sh keeps +# PATH, XDG, locale and EDITOR. It just stops before the interactive layer, +# which it had no use for anyway. +if [ -z "${BASH_VERSION-}" ] && [ -z "${ZSH_VERSION-}" ]; then + return 0 2>/dev/null || exit 0 +fi + +# Marks that this file is already running, so ~/.bashrc (which we may source at +# the very end, for login bash) doesn't source us straight back. See the header +# of config/bashrc for the full two-way handshake. +__dotfiles_profile=1 + +if [[ "$(uname)" == "Darwin" ]] +then + # Sometimes, when invoked via Karabiner, the HOME variable + # will not be defined, even with a login shell + : "${HOME:="/Users/$(whoami)"}" +elif [[ "$(uname)" == "Linux" ]] +then + if command -v wl-copy >/dev/null && command -v wl-paste >/dev/null; then + alias pbcopy='wl-copy' + alias pbpaste='wl-paste' + else + alias pbcopy='xclip -selection clipboard' + alias pbpaste='xclip -selection clipboard -o' + fi +fi +_profile clipboard_tools + +# ripgrep +# Remind to use rg instead of ag +# ag() { +# >&2 echo "ERROR: You decided to switch to 'rg' (ripgrep)" +# return 1 +# # command ag "$@" +# } +# if [[ -r "$HOME/.config/ripgrep/config" ]]; then +# export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/config" +# fi + +# XDG_{CONFIG,CACHE,DATA}_HOME are set in config/env. + +# Set history file location and size. +SHELL_NAME="$(basename "$SHELL")" +SHELL_CACHE="$XDG_CACHE_HOME/$SHELL_NAME" +mkdir -p "$SHELL_CACHE" +HISTFILE="$SHELL_CACHE/history" +HISTSIZE=1000000 +# SAVEHIST is read by zsh itself, which shellcheck can't lint for. +# shellcheck disable=SC2034 +SAVEHIST=1000000 + +# detach from tmux by default +# Guard at call time, not definition time. +# functions and sources them in non-interactive tool shells that inherit +# $TMUX, where a bare `exit` must really exit (not detach the terminal). +exit() { + if [[ -n "${TMUX:-}" ]] && [[ $- == *i* ]]; then + tmux detach + else + builtin exit "$@" + fi +} +exists() { command -v "$1" >/dev/null 2>&1; } # Check if command exists (is in path). +lspath() { echo "$PATH" | tr ':' '\n'; } # list PATH entries +pathadd() { [[ -d "$1" ]] && PATH="$1"$(echo ":$PATH:" | sed "s|:$1:|:|" | sed 's|:$||'); } +pathapp() { [[ -d "$1" ]] && PATH=$(echo ":$PATH:" | sed "s|:$1:|:|" | sed 's|^:||')"$1"; } + +# GPG +GPG_TTY=$(tty) +export GPG_TTY +pbsign() { pbpaste | gpg --clearsign | pbcopy; } +pbverify() { pbpaste | gpg --verify; } + +# Show processes listening on TCP sockets +listening() { + lsof -n -P -i4TCP -sTCP:LISTEN \ + | awk '{ print $9,$1,$2 }' \ + | awk -F '\:' '{ print $2 }' \ + | grep -E -v '^$' \ + | column -t \ + | sort -n +} + +# https://sites.google.com/site/jimmyxu101/testing/use-tcpdump-to-monitor-http-traffic +httpdump() { + sudo tcpdump -A -i "${2:-lo0}" -s 0 -q "tcp port $1 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)" +} + +alias clc="fc -ln -1 | sed -e 's/\\\\n/\\n/g' -e 's/\\\\t/\\t/g' | pbcopy" + +# bash + +# DISABLE_AUTO_TITLE, the core PATH entries and the less/PAGER settings are all +# in config/env. +# use bash like vi ! +# set -o vi +_profile bash + +# when using sudo, use alias expansion (otherwise sudo ignores your aliases) +alias sudo='sudo ' + +# color +export CLICOLOR=1 +THEME_COLORS="$(theme --colors)" +export THEME_COLORS +if [ "$(theme --mode)" = "light" ]; then + export LSCOLORS=GxFxCxDxBxegedabagaced +else + export LSCOLORS=GxFxBxDxCxegedabagacad +fi +if exists bat ; then + # best for light: Coldark-Cold, Github, Monokai Extended Light, OneHalfLight, gruvbox-light + # best for dark: 1337, Coldark-Dark, Monokai Extended, OneHalfDark, gruvbox-dark + # config file in $XDG_CONFIG_HOME/bat/config takes precedence to this env var + export BAT_THEME='ansi' # works well with both light and dark themes + export BAT_STYLE='plain' + export MANPAGER="sh -c 'col -bx | bat -l man -p'" + export FZF_COMPLETION_OPTS='--preview '\''bat --color=always --style=numbers --line-range=:500 {}'\' +fi +if exists lsd +then + alias ls='lsd' +fi +_profile color + +# VISUAL/EDITOR and the node environment are set in config/env. + +# # ruby +# _profile ruby-start +# export RBENV_ROOT="${RBENV_ROOT:-"$XDG_CACHE_HOME/rbenv"}" # Set rbenv location. +# activate-rbenv() { +# if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi +# } +# activate-rbenv +# if [ -d "$HOME/.rbenv/bin" ] ; then +# pathapp "$HOME/.rbenv/bin" +# eval "$(rbenv init -)" +# fi + +# # add user installs bin to path +# if which ruby >/dev/null && which gem >/dev/null; then +# pathapp "$(ruby -rubygems -e 'puts Gem.user_dir')/bin" +# fi +# _profile ruby-done + +# python +# export VIRTUAL_ENV_DISABLE_PROMPT=1 # Add the virtualenv prompt myself. +# LANG/LC_* are set in config/env — mosh needs a UTF-8 locale in shells that +# never reach this file. +# If you want to use pyenv +# if [ -x "$(command -v pyenv)" ] ; then +# export PYENV_ROOT="$XDG_DATA_HOME/pyenv" +# pathadd "$PYENV_ROOT/bin" +# eval "$(pyenv init --path)" +# fi +# Instead of virtualenvwrapper's: +# mkvirtualenv -p <path/to/python> <name> +# I prefer to use: +# virtualenv --python=<path/to/python> .venv +# If you want to use virtualenvwrapper, uncomment this: +# if [ -r "$HOME/.local/pipx/venvs" ] ; then +# export WORKON_HOME="$XDG_DATA_HOME/virtualenvs" +# export VIRTUALENVWRAPPER_PYTHON="$HOME/.local/pipx/venvs/virtualenvwrapper/bin/python3.9" +# source "$HOME/.local/bin/virtualenvwrapper.sh" +# fi +export PYENV_VIRTUALENV_DISABLE_PROMPT=1 +# The Python user bin is on PATH via config/env. +_profile python + + +# go — GOPATH and the go bin dirs are in config/env. +godocserver() { godoc "-http=localhost:6060"; } +_profile go + +# rust — CARGO_HOME/RUSTUP_HOME and the cargo bin dir are in config/env, which +# also replaces what rustup's own $CARGO_HOME/env does (it only prepends PATH). + +# GNU Arm Embedded Toolchain, used to build joeycastillo/Sensor-Watch, is on +# PATH via config/env. + +# java +if exists jenv > /dev/null && [[ -z "$JENV_LOADED" ]]; then + eval "$(jenv init - --no-rehash)"; + # export JAVA_HOME="${JAVA_HOME:-"$(jenv javahome)"}" + # export IDEA_JDK=$JAVA_HOME + # to make sure JAVA_HOME is set, enable this plugin - just need to run once: + # jenv enable-plugin export +fi +_profile jenv-init + +# A gradlew / gradle wrapper. +# https://github.com/gdubw/gng +# This would be nice but it breaks existing IntelliJ projects +# export GRADLE_USER_HOME="$XDG_CACHE_HOME/gradle" # Also contains gradle.properties (symlink from XDG_CONFIG_HOME). +if exists gw > /dev/null; then + alias gradle=gw +fi + +_profile java + +# ANDROID_HOME and the colima/testcontainers docker vars are in config/env. + +# Interactive setup linked in from elsewhere, one file per concern, in name +# order. This repo ships none: it is where a machine's own layer plugs in, with +# whatever needs a secret or names a host -- a token wrapper, API keys. +# ~/.localprofile, below, is for hand-written per-host setup and still has the +# last word. +# +# A function only to scope nullglob: zsh aborts on a glob matching nothing, and +# bash would hand the loop the literal pattern. The files are sourced inside it, +# so their plain assignments, exports and functions are global, but `local` or +# `typeset` in them would not be -- and under zsh neither would a `setopt`, +# which local_options reverts when the function returns. +__dotfiles_source_dir() { + if [ -n "${ZSH_VERSION-}" ]; then + setopt local_options null_glob + else + # Not `$(shopt -p nullglob)`: that exits 1 when the option is off, and a + # caller that sources this under `set -e` would die here without a word. + local __dotfiles_nullglob='' + shopt -q nullglob || __dotfiles_nullglob='shopt -u nullglob' + shopt -s nullglob + fi + local __dotfiles_f + for __dotfiles_f in "$1"/*.sh; do + # shellcheck source=/dev/null + . "$__dotfiles_f" + done + if [ -z "${ZSH_VERSION-}" ] && [ -n "$__dotfiles_nullglob" ]; then + eval "$__dotfiles_nullglob" + fi +} +__dotfiles_source_dir "${XDG_CONFIG_HOME:-$HOME/.config}/shell/profile.d" +unset -f __dotfiles_source_dir +_profile profile.d + +macbook_chassis_regulatory_model() { + ioreg -l \ + | grep '"regulatory-model-number"' \ + | sed -E 's/.*<([^>]+).*/\1/' \ + | cut -c1-10 \ + | xxd -r -p \ + | tr -d '\000' +} +_profile misc + +if [[ -r /proc/cpuinfo ]] && \ + grep -qi 'raspberry pi' /proc/cpuinfo +then + system_temperature() { + paste \ + <(cat /sys/class/thermal/thermal_zone*/type) \ + <(cat /sys/class/thermal/thermal_zone*/temp | sed 's/\(.\)..$/.\1°C/' ) \ + | column -s $'\t' -t + if [[ -f /sys/class/thermal/cooling_device0/cur_state ]] + then + echo "Fan state: $(cat /sys/class/thermal/cooling_device0/cur_state)" + else + echo "No fan" + fi + } +fi +_profile raspberry_pi + +# The kimi / cursor-agent / claude env vars are all in config/env. + +if [ -n "$ZSH_VERSION" ]; then + [[ -d /opt/homebrew/share/zsh/site-functions/ ]] && fpath+=(/opt/homebrew/share/zsh/site-functions/) +fi +_profile zsh_specific + +# Disable XON/XOFF flow control so Ctrl-Q/Ctrl-S work in apps like nvim +stty -ixon 2>/dev/null +# The Ghostty bin dir is on PATH via config/env. +_profile misc + +# Interactive host-local setup. Plain host-local *environment* goes in +# ~/.localenv, which config/env sources for every shell. +# shellcheck source=/dev/null +[ -r "$HOME/.localprofile" ] && source "$HOME/.localprofile" +_profile local + +# A *login* bash reads this file but never ~/.bashrc, so login and non-login +# interactive bash would otherwise diverge. Source it here to converge them. +# Skipped when ~/.bashrc is what sourced us, and for zsh, which has no such gap +# (a login interactive zsh reads both ~/.zprofile and ~/.zshrc). +if [ -n "${BASH_VERSION:-}" ] && [ -z "${__dotfiles_bashrc:-}" ] && [ -r "$HOME/.bashrc" ]; then + # shellcheck source=/dev/null + . "$HOME/.bashrc" +fi +_profile bashrc + +_profile exit + +# ~/bin is prepended to PATH in config/env. + +# vim: foldmethod=marker filetype=sh tw=100 |
