# Managed by dotfiles/zsh/install.sh — edits made directly to ~/.zshrc will be # overwritten on the next bootstrap run. Edit zsh/zshrc in the dotfiles repo # instead. # Userland tools installed by dotfiles/bin/install.sh live here. case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac export ZSH="$HOME/.oh-my-zsh" ZSH_THEME="robbyrussell" plugins=(git) source "$ZSH/oh-my-zsh.sh" HISTSIZE=10000 SAVEHIST=10000 setopt SHARE_HISTORY setopt HIST_IGNORE_DUPS # Delete remote branches without fighting tab-completion. # # `git push origin --delete ` completes to `origin/foo`, but `origin/foo` # is a LOCAL remote-tracking name — the ref on the server is just `foo`. So the # command zsh completes for you is the one git rejects, with "remote ref does # not exist", which reads like the branch is missing rather than misnamed. # # grmb takes either form. It fetches with --prune first so branches already # deleted on the server stop being offered at all (that is the other half of # this annoyance, and `fetch.prune = true` from dotfiles/git/install.sh makes it # automatic everywhere else). # # Named grmb, not gbd: oh-my-zsh's git plugin already binds gbd to # `git branch --delete`, aliases resolve before functions, and the collision # fails in the worst possible way — `gbd origin/foo` would quietly try to # delete a LOCAL branch instead of the remote one. Check with `type ` # before adding any g* helper here. # # grmb origin/my-branch # completed form, works # grmb my-branch other # bare form, several at once grmb() { emulate -L zsh local remote=origin b rc=0 if [ "$#" -eq 0 ]; then print -u2 "grmb: usage: grmb ... (origin/ prefix optional)" return 2 fi git fetch --prune --quiet "$remote" || return 1 for b in "$@"; do b=${b#${remote}/} if git push "$remote" --delete "$b"; then print "deleted ${remote}/${b}" else print -u2 "grmb: could not delete ${b} (already gone? check: git ls-remote --heads ${remote})" rc=1 fi done return $rc } # Secrets from this user's 1Password vault, written by dotfiles/secrets/install.sh. # Reading a local 0600 file keeps shell startup off the network: nothing here # calls op. opc/opd refresh the file themselves; after editing the vault # anywhere else (the 1Password app, another box), run `ops`. _DOTFILES_CONF="$HOME/.config/dotfiles" [ -f "$_DOTFILES_CONF/secrets.env" ] && source "$_DOTFILES_CONF/secrets.env" # opr NAME read one field straight from the vault # opc NAME=VALUE create or update a field, then refresh and reload this # opc NAME shell; with no VALUE (NAME or NAME=) it prompts, hidden, # which keeps the secret out of shell history. # `opc NAME VALUE` also still works. # opd NAME delete a field, refresh, and unset it in this shell # opl [VAULT [ITEM]] show every field and its current value (default: your # vault and item from op.env) # ops re-fetch the vault into secrets.env and reload this shell # # The service-account token lives in op.env and is loaded only inside these # subshells, so it never lands in the environment of everything you run. _dotfiles_op() { [ -f "$_DOTFILES_CONF/op.env" ] || { print -u2 "no $_DOTFILES_CONF/op.env; run: ops"; return 1; } ( set -a; source "$_DOTFILES_CONF/op.env"; set +a; "$@" ) } _dotfiles_env_name() { [[ "$1" =~ '^[A-Za-z_][A-Za-z0-9_]*$' ]] && return 0 print -u2 "$2: '$1' is not a valid variable name"; return 2 } opr() { _dotfiles_env_name "$1" opr || return _dotfiles_op sh -c 'op read "op://$OP_VAULT/$OP_ITEM/$1"' opr "$1" } opc() { emulate -L zsh local name=$1 value if [[ $1 == *=* ]]; then # NAME=VALUE: split on the first '=' so values may contain '=' name=${1%%=*} value=${1#*=} elif (( $# >= 2 )); then value=$2 fi _dotfiles_env_name "$name" opc || return if [[ -z $value ]]; then read -rs "value?$name: " && print [[ -n $value ]] || { print -u2 "opc: empty value for $name; nothing changed"; return 1; } fi # [concealed] makes it a password field; an existing field of that name is updated. # op can exit non-zero with "(404) Not Found" after an edit it did save (seen with # op 2.34.1 and 2.39.0), so its exit code is ignored: the field is read back instead. _dotfiles_op sh -c ' clean() { printf "%s" "${1:-(no output)}" | sed "s/^\[ERROR\] [0-9/]* [0-9:]* //"; } ref="op://$OP_VAULT/$OP_ITEM/$1" err=$(op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[concealed]=$2" 2>&1 >/dev/null) if [ "$(op read "$ref" 2>/dev/null)" = "$2" ]; then printf "opc: saved %s\n" "$ref" >&2 else printf "opc: could not save %s\n op said: %s\n check vault, item and service-account write access with: opl\n" \ "$ref" "$(clean "$err")" >&2 exit 1 fi' opc "$name" "$value" && ops } opd() { _dotfiles_env_name "$1" opd || return # As in opc, op's exit code is unreliable here: check the item's field labels instead. _dotfiles_op sh -c ' clean() { printf "%s" "${1:-(no output)}" | sed "s/^\[ERROR\] [0-9/]* [0-9:]* //"; } ref="op://$OP_VAULT/$OP_ITEM/$1" err=$(op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[delete]" 2>&1 >/dev/null) if ! item=$(op item get "$OP_ITEM" --vault "$OP_VAULT" --format json 2>&1); then printf "opd: could not re-read %s to confirm the delete\n op said: %s\n" "$ref" "$(clean "$item")" >&2 exit 1 elif printf "%s" "$item" | jq -r ".fields[].label" | grep -qxF "$1"; then printf "opd: could not delete %s\n op said: %s\n" "$ref" "$(clean "$err")" >&2 exit 1 fi printf "opd: deleted %s\n" "$ref" >&2' opd "$1" \ && unset "$1" && ops } opl() { _dotfiles_op sh -c ' vault=${1:-$OP_VAULT} item=${2:-$OP_ITEM} printf "op://%s/%s\n" "$vault" "$item" >&2 op item get "$item" --vault "$vault" --reveal --format json' opl "$@" \ | jq -r ' [.fields[] | select(.purpose != "NOTES")] as $f | ([$f[] | .label // "" | length] | max // 0) as $w | $f[] | ((.label // "") + " " * ($w - ((.label // "") | length))) as $name | ((.value // "") | split("\n")) as $lines | "\($name) \($lines[0] // "")", ($lines[1:][] | (" " * ($w + 2)) + .)' } ops() { "${DOTFILES_DIR:-$HOME/.dotfiles}/secrets/install.sh" \ && source "$_DOTFILES_CONF/secrets.env" } # Per-machine customizations (secrets, keys, local overrides) live in # ~/.zshrc.local, which bootstrap never touches. See zsh/zshrc.local.example # for the variables modules look for. Loaded last so it can override anything. [ -f "$HOME/.zshrc.local" ] && source "$HOME/.zshrc.local"