13 Commits

Author SHA1 Message Date
Claude
7444f5b97b Remove pointless exports from ssh/rc, add process-model comment
ssh/rc runs as a sshd child process so exports never reach the user's
shell. SSH_REMOTE_AUTH_SOCK was set and exported but never used (a
leftover from a prior failed fix attempt). SSH_AUTH_SOCK was reassigned
to the symlink path and exported, also to no effect. Remove both.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA
2026-04-19 02:19:05 +00:00
Claude
c5e1157f47 Fix shenv clobbering forwarded SSH socket with local agent in tmux
ssh/rc env changes (including SSH_REMOTE_AUTH_SOCK) are lost because
ssh/rc runs as a sshd child process, not the user's shell. The shell
always receives SSH_AUTH_SOCK set to the raw forwarded socket path.

Fresh SSH login worked fine (step 1 catches the raw socket). The bug
was in tmux new windows: SSH_AUTH_SOCK there is our stable symlink, so
step 1 fails, then steps 2/3 look up the system agent and overwrite the
symlink that ssh/rc just set to the forwarded socket.

Fix: only run the system agent lookup when the stable symlink is already
broken. A valid symlink means ssh/rc (or a previous shenv run) already
set it correctly; don't clobber it.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA
2026-04-19 01:47:36 +00:00
Claude
6b50be84a9 Fix SSH agent forwarding clobbered by local agent in shenv
ssh/rc saves the raw forwarded socket in SSH_REMOTE_AUTH_SOCK before
rewriting SSH_AUTH_SOCK to the stable symlink. shenv was ignoring that
variable, so it saw SSH_AUTH_SOCK as "our link" and fell through to the
systemd lookup, which could overwrite the symlink with a local agent
socket and silently drop the forwarded one.

Now shenv checks SSH_REMOTE_AUTH_SOCK first, giving forwarded sockets
priority over any local agent.

https://claude.ai/code/session_01RhXaFzxJA5D2BcGcz18ipA
2026-04-19 01:43:12 +00:00
David Tomaschik
1804357162 Update skel 2026-04-14 10:27:17 -07:00
David Tomaschik
202d871a59 Merge branch 'main' of github.com:Matir/skel 2026-04-09 21:18:29 -07:00
David Tomaschik
467d916f33 Update zshrc 2026-04-09 21:18:24 -07:00
David Tomaschik
6d2bfdbcea Update custom starship shell 2026-04-09 18:03:14 -07:00
David Tomaschik
1d0a09c442 Bump Brewfile 2026-04-07 16:32:02 -07:00
David Tomaschik
37c765ae29 Update for bundles 2026-04-07 16:02:49 -07:00
David Tomaschik
41f8a49381 Remove missing brew entry 2026-04-07 14:53:41 -07:00
David Tomaschik
3f9c41d266 Merge branch 'main' of https://github.com/Matir/skel 2026-04-06 18:34:17 -07:00
David Tomaschik
69e4b83652 Fix brew manager 2026-04-06 18:34:06 -07:00
David Tomaschik
74c2472dd2 Fix zsh completion 2026-04-06 15:30:14 -07:00
9 changed files with 268 additions and 80 deletions

View File

@@ -9,8 +9,9 @@ fi
UPDATE_SCRIPT="bin/macos/update_brewfile" UPDATE_SCRIPT="bin/macos/update_brewfile"
if [[ -x "$UPDATE_SCRIPT" ]]; then if [[ -x "$UPDATE_SCRIPT" ]]; then
# Run in dry-run mode and see if there's output echo "🔍 Checking Brewfile synchronization..."
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run 2>/dev/null) # Run in dry-run mode with --add-only and see if there's output
DIFF_OUTPUT=$("$UPDATE_SCRIPT" --dry-run --add-only 2>/dev/null)
if [[ "$DIFF_OUTPUT" == *"Changes detected"* ]]; then if [[ "$DIFF_OUTPUT" == *"Changes detected"* ]]; then
echo "⚠️ Brewfile is out of sync with your installed packages." echo "⚠️ Brewfile is out of sync with your installed packages."
echo " Run '$UPDATE_SCRIPT' to synchronize it." echo " Run '$UPDATE_SCRIPT' to synchronize it."

View File

@@ -1,5 +1,6 @@
tap "dart-lang/dart" tap "dart-lang/dart"
tap "sass/sass" tap "sass/sass"
brew "ack" brew "ack"
brew "acme.sh" brew "acme.sh"
brew "age" brew "age"
@@ -7,14 +8,16 @@ brew "autoconf"
brew "automake" brew "automake"
brew "b2-tools" brew "b2-tools"
brew "bat" brew "bat"
brew "bazelisk"
brew "binwalk"
brew "cask" brew "cask"
brew "ccache" brew "ccache"
brew "certbot" brew "certbot"
brew "cmake" brew "cmake"
brew "colima" brew "colima"
brew "devcontainer" brew "devcontainer"
brew "difftastic"
brew "dfu-util" brew "dfu-util"
brew "difftastic"
brew "direnv" brew "direnv"
brew "duck" brew "duck"
brew "earthly" brew "earthly"
@@ -27,6 +30,7 @@ brew "git-lfs"
brew "gnupg" brew "gnupg"
brew "go" brew "go"
brew "gradle" brew "gradle"
brew "hf"
brew "htop" brew "htop"
brew "httpie" brew "httpie"
brew "huggingface-cli" brew "huggingface-cli"
@@ -40,11 +44,12 @@ brew "mosh"
brew "neovim" brew "neovim"
brew "ninja" brew "ninja"
brew "nmap" brew "nmap"
brew "protobuf" brew "ollama"
brew "p7zip" brew "p7zip"
brew "pipenv" brew "pipenv"
brew "pipx" brew "pipx"
brew "pkgconf" brew "pkgconf"
brew "protobuf"
brew "pwgen" brew "pwgen"
brew "pwntools" brew "pwntools"
brew "qemu" brew "qemu"
@@ -53,7 +58,8 @@ brew "ripgrep"
brew "ruby" brew "ruby"
brew "ruby@3.3" brew "ruby@3.3"
brew "rustup" brew "rustup"
brew "scroll-reverser" brew "sass/sass/migrator"
brew "sass/sass/sass"
brew "shellcheck" brew "shellcheck"
brew "smartmontools" brew "smartmontools"
brew "starship" brew "starship"
@@ -64,8 +70,7 @@ brew "wget"
brew "yt-dlp" brew "yt-dlp"
brew "zlib" brew "zlib"
brew "zsh-syntax-highlighting" brew "zsh-syntax-highlighting"
brew "sass/sass/migrator"
brew "sass/sass/sass"
cask "codeql" cask "codeql"
cask "cyberduck" cask "cyberduck"
cask "font-fira-code-nerd-font" cask "font-fira-code-nerd-font"
@@ -82,6 +87,7 @@ cask "iterm2"
cask "macfuse" cask "macfuse"
cask "meld" cask "meld"
cask "mitmproxy" cask "mitmproxy"
cask "processmonitor"
cask "raycast" cask "raycast"
cask "rectangle" cask "rectangle"
cask "scroll-reverser" cask "scroll-reverser"

View File

@@ -7,16 +7,69 @@ import sys
import argparse import argparse
import difflib import difflib
import tempfile
# Regex to match brew/cask/tap/mas lines # Regex to match brew/cask/tap/mas lines
PKG_RE = re.compile(r'^\s*(brew|cask|tap|mas)\s+["\']([^"\']+)["\'](.*)$') PKG_RE = re.compile(r'^\s*(brew|cask|tap|mas)\s+["\']([^"\']+)["\'](.*)$')
def colorize_diff(lines):
for line in lines:
if line.startswith('+') and not line.startswith('+++'):
yield f"\033[32m{line}\033[0m"
elif line.startswith('-') and not line.startswith('---'):
yield f"\033[31m{line}\033[0m"
elif line.startswith('^'):
yield f"\033[36m{line}\033[0m"
else:
yield line
class Entry:
def sort_key(self): raise NotImplementedError()
def to_lines(self): raise NotImplementedError()
class PackageEntry(Entry):
def __init__(self, pkg_type, name, options, comments=None):
self.pkg_type = pkg_type
self.name = name
self.options = options.strip()
self.comments = comments or []
def sort_key(self):
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
return (order.get(self.pkg_type, 4), self.name)
def to_lines(self):
res = list(self.comments)
pkg_line = f'{self.pkg_type} "{self.name}"'
if self.options:
if not self.options.startswith(','):
pkg_line += ' '
pkg_line += self.options
res.append(pkg_line)
return res
class TextEntry(Entry):
def __init__(self, lines, is_header=True):
self.lines = lines
self.is_header = is_header
def sort_key(self):
# Header is -1, Trailing is 5 (after all package types 0-4)
return (-1 if self.is_header else 5, "")
def to_lines(self):
return self.lines
def get_repo_root(): def get_repo_root():
script_dir = os.path.dirname(os.path.realpath(__file__))
try: try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'],
cwd=script_dir,
stderr=subprocess.STDOUT).decode().strip() stderr=subprocess.STDOUT).decode().strip()
return root return root
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return os.getcwd() # If not in a git repo, go up 2 levels from script_dir (bin/macos/)
return os.path.abspath(os.path.join(script_dir, '..', '..'))
def get_ignore_list(repo_root): def get_ignore_list(repo_root):
ignore = set() ignore = set()
@@ -34,11 +87,19 @@ def get_ignore_list(repo_root):
ignore.add(line) ignore.add(line)
return ignore return ignore
def get_current_packages(): def get_current_packages(args):
"""Runs brew bundle dump and returns lines.""" """Runs brew bundle dump and returns lines."""
env = os.environ.copy()
env["HOMEBREW_NO_AUTO_UPDATE"] = "1"
env["HOMEBREW_NO_INSTALL_CLEANUP"] = "1"
env["HOMEBREW_NO_ENV_HINTS"] = "1"
cmd = ['brew', 'bundle', 'dump', '--file=-']
if args.no_mas: cmd.append('--no-mas')
if args.no_vscode: cmd.append('--no-vscode')
try: try:
output = subprocess.check_output(['brew', 'bundle', 'dump', '--file=-'], output = subprocess.check_output(cmd, env=env, stderr=subprocess.DEVNULL).decode()
stderr=subprocess.DEVNULL).decode()
return output.splitlines() return output.splitlines()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr) print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr)
@@ -48,8 +109,9 @@ def parse_brewfile(content):
""" """
Parses Brewfile content. Parses Brewfile content.
Returns: Returns:
- entries: list of Entry objects
- conditional_pkgs: set of (type, name) - conditional_pkgs: set of (type, name)
- preserved_footer: string (everything from first conditional onwards) - footer: string (everything from first conditional onwards)
""" """
lines = content.splitlines() lines = content.splitlines()
conditional_pkgs = set() conditional_pkgs = set()
@@ -60,7 +122,7 @@ def parse_brewfile(content):
for i, line in enumerate(lines): for i, line in enumerate(lines):
stripped = line.strip() stripped = line.strip()
if stripped.startswith(('if ', 'unless ', 'case ')) and not stripped.endswith('; end'): if stripped.startswith(('if ', 'unless ', 'case ', 'def ', 'begin ')) and not stripped.endswith('; end'):
if first_conditional_idx == -1: if first_conditional_idx == -1:
# Look back for comments that might belong to this block # Look back for comments that might belong to this block
j = i - 1 j = i - 1
@@ -77,13 +139,35 @@ def parse_brewfile(content):
if stripped == 'end' or stripped.endswith('; end'): if stripped == 'end' or stripped.endswith('; end'):
in_conditional -= 1 in_conditional -= 1
if first_conditional_idx == -1: unconditional_lines = lines[:first_conditional_idx] if first_conditional_idx != -1 else lines
return set(), "" footer = "\n".join(lines[first_conditional_idx:]) if first_conditional_idx != -1 else ""
footer = "\n".join(lines[first_conditional_idx:]) entries = []
return conditional_pkgs, footer comment_buffer = []
first_pkg_seen = False
for line in unconditional_lines:
match = PKG_RE.match(line)
if match:
if not first_pkg_seen:
if comment_buffer:
entries.append(TextEntry(comment_buffer, is_header=True))
comment_buffer = []
first_pkg_seen = True
entries.append(PackageEntry(match.group(1), match.group(2), match.group(3), comment_buffer))
comment_buffer = []
else:
comment_buffer.append(line)
if comment_buffer:
entries.append(TextEntry(comment_buffer, is_header=False))
return entries, conditional_pkgs, footer
def main(args): def main(args):
if sys.platform != "darwin":
print(f"Warning: Running on {sys.platform}. Brewfile is primarily for macOS.", file=sys.stderr)
repo_root = get_repo_root() repo_root = get_repo_root()
brewfile_path = os.path.join(repo_root, 'Brewfile') brewfile_path = os.path.join(repo_root, 'Brewfile')
@@ -94,39 +178,84 @@ def main(args):
with open(brewfile_path) as f: with open(brewfile_path) as f:
old_content = f.read() old_content = f.read()
conditional_pkgs, footer = parse_brewfile(old_content) old_entries, conditional_pkgs, footer = parse_brewfile(old_content)
ignore_list = get_ignore_list(repo_root) ignore_list = get_ignore_list(repo_root)
dumped_lines = get_current_packages() old_pkg_map = {}
for e in old_entries:
new_unconditional_lines = [] if isinstance(e, PackageEntry):
key = (e.pkg_type, e.name)
if key not in old_pkg_map:
old_pkg_map[key] = e
dumped_lines = get_current_packages(args)
dumped_pkgs = []
for line in dumped_lines: for line in dumped_lines:
match = PKG_RE.match(line) match = PKG_RE.match(line)
if match: if match:
pkg_type, pkg_name = match.group(1), match.group(2) pkg_type, pkg_name, pkg_options = match.group(1), match.group(2), match.group(3)
if pkg_name in ignore_list: if pkg_name in ignore_list or (pkg_type, pkg_name) in conditional_pkgs:
continue
if (pkg_type, pkg_name) in conditional_pkgs:
continue continue
dumped_pkgs.append(PackageEntry(pkg_type, pkg_name, pkg_options))
# If it's not a package line (e.g. comment from dump), we can skip or keep new_entries = []
if line.strip(): for e in old_entries:
new_unconditional_lines.append(line) if isinstance(e, TextEntry) and e.is_header:
new_entries.append(e)
# Sort lines by type (tap, brew, cask, mas) then name seen_in_new = set()
def sort_key(line): added_count = 0
match = PKG_RE.match(line) removed_count = 0
if not match: return (4, line) merged_count = 0
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
return (order.get(match.group(1), 4), match.group(2))
new_unconditional_lines.sort(key=sort_key) if args.add_only:
for e in old_entries:
if isinstance(e, PackageEntry):
new_entries.append(e)
seen_in_new.add((e.pkg_type, e.name))
for d in dumped_pkgs:
if (d.pkg_type, d.name) not in seen_in_new:
new_entries.append(d)
seen_in_new.add((d.pkg_type, d.name))
added_count += 1
else:
for d in dumped_pkgs:
key = (d.pkg_type, d.name)
if key in seen_in_new: continue
if key in old_pkg_map:
merged = old_pkg_map[key]
if not merged.options:
merged.options = d.options
new_entries.append(merged)
merged_count += 1
else:
new_entries.append(d)
added_count += 1
seen_in_new.add(key)
# Build new content # Check for removals
new_content = "\n".join(new_unconditional_lines) for key in old_pkg_map:
if key not in seen_in_new:
removed_count += 1
for e in old_entries:
if isinstance(e, TextEntry) and not e.is_header:
new_entries.append(e)
new_entries.sort(key=lambda x: x.sort_key())
output_lines = []
last_type = None
for e in new_entries:
if isinstance(e, PackageEntry):
if last_type and e.pkg_type != last_type:
output_lines.append("")
last_type = e.pkg_type
output_lines.extend(e.to_lines())
new_content = "\n".join(output_lines)
if footer: if footer:
if new_unconditional_lines: if output_lines and output_lines[-1].strip():
new_content += "\n\n" new_content += "\n\n"
new_content += footer.strip() + "\n" new_content += footer.strip() + "\n"
else: else:
@@ -137,20 +266,33 @@ def main(args):
else: else:
if args.dry_run: if args.dry_run:
print("Changes detected (dry run):") print("Changes detected (dry run):")
diff = difflib.unified_diff( diff = list(difflib.unified_diff(
old_content.splitlines(keepends=True), old_content.splitlines(keepends=True),
new_content.splitlines(keepends=True), new_content.splitlines(keepends=True),
fromfile='Brewfile (original)', fromfile='Brewfile (original)',
tofile='Brewfile (new)' tofile='Brewfile (new)'
) ))
sys.stdout.writelines(diff) if sys.stdout.isatty():
sys.stdout.writelines(colorize_diff(diff))
else:
sys.stdout.writelines(diff)
else: else:
with open(brewfile_path, 'w') as f: dir_name = os.path.dirname(brewfile_path)
f.write(new_content) with tempfile.NamedTemporaryFile('w', dir=dir_name, delete=False) as tf:
tf.write(new_content)
tempname = tf.name
os.replace(tempname, brewfile_path)
print("Brewfile updated.") print("Brewfile updated.")
if args.verbose:
print(f"Summary: {added_count} added, {removed_count} removed, {merged_count} kept/merged.")
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update Brewfile while preserving conditionals.") parser = argparse.ArgumentParser(description="Update Brewfile while preserving conditionals.")
parser.add_argument("--dry-run", action="store_true", help="Show changes without applying them.") parser.add_argument("--dry-run", action="store_true", help="Show changes without applying them.")
parser.add_argument("--add-only", action="store_true", help="Only add missing entries, do not remove existing ones.")
parser.add_argument("--verbose", "-v", action="store_true", help="Print summary of changes.")
parser.add_argument("--no-mas", action="store_true", help="Do not include Mac App Store apps.")
parser.add_argument("--no-vscode", action="store_true", help="Do not include VSCode extensions.")
args = parser.parse_args() args = parser.parse_args()
main(args) main(args)

View File

@@ -30,5 +30,8 @@ if status --is-interactive
install_fisher install_fisher
end end
# Want this at the bottom to put this path first # Want these at the bottom to put them first in PATH
fish_add_path --move --path {$HOME}/bin fish_add_path --move --path {$HOME}/bin
if test (uname) = "Darwin"
fish_add_path --move --path {$HOME}/bin/macos
end

View File

@@ -51,3 +51,4 @@ fi
""" """
style = "bold blue" style = "bold blue"
format = "♊[$output](blue) " format = "♊[$output](blue) "
shell = ["/bin/sh", "-c"]

View File

@@ -0,0 +1,9 @@
[alias]
commit-assumed = "!f() { \
file=\"$1\"; \
shift; \
git update-index --no-assume-unchanged \"$file\" && \
git add \"$file\" && \
git commit \"$@\" && \
git update-index --assume-unchanged \"$file\"; \
}; f"

View File

@@ -113,13 +113,17 @@ _is_link_path() {
_CANDIDATE="" _CANDIDATE=""
# 1. If current environment has a valid socket that is NOT our link, it's a prime candidate (e.g. SSH forwarding). # 1. If current environment has a valid socket that is NOT our link, it's a prime candidate
# (e.g. fresh SSH login: sshd sets SSH_AUTH_SOCK to the raw forwarded socket before ssh/rc
# rewrites it to the stable symlink; the shell inherits the original raw path).
if [ -S "${SSH_AUTH_SOCK:-}" ] && ! _is_link_path "${SSH_AUTH_SOCK}"; then if [ -S "${SSH_AUTH_SOCK:-}" ] && ! _is_link_path "${SSH_AUTH_SOCK}"; then
_CANDIDATE="${SSH_AUTH_SOCK}" _CANDIDATE="${SSH_AUTH_SOCK}"
fi fi
# 2. If no candidate yet, or we're currently using the link, try to find the "real" system agent. # 2. Only look for a system agent if the stable link is already broken. If the link is
if [ -z "${_CANDIDATE}" ] || _is_link_path "${SSH_AUTH_SOCK:-}"; then # valid (e.g. a tmux pane where SSH_AUTH_SOCK points to our symlink which ssh/rc just
# updated to the forwarded socket), leave it alone — don't clobber it with a local agent.
if [ -z "${_CANDIDATE}" ] && [ ! -S "${_SSH_AUTH_LINK}" ]; then
_FOUND="" _FOUND=""
if [ "$(uname)" = "Darwin" ]; then if [ "$(uname)" = "Darwin" ]; then
_FOUND=$(launchctl getenv SSH_AUTH_SOCK 2>/dev/null) _FOUND=$(launchctl getenv SSH_AUTH_SOCK 2>/dev/null)
@@ -143,8 +147,8 @@ if [ -z "${_CANDIDATE}" ] || _is_link_path "${SSH_AUTH_SOCK:-}"; then
fi fi
fi fi
# 3. Last resort: search common paths if we still don't have a valid candidate. # 3. Last resort: search common paths if we still don't have a candidate and the link is broken.
if [ ! -S "${_CANDIDATE}" ]; then if [ ! -S "${_CANDIDATE}" ] && [ ! -S "${_SSH_AUTH_LINK}" ]; then
_U=$(id -u) _U=$(id -u)
for _p in "/run/user/${_U}/keyring/ssh" "/run/user/${_U}/ssh-agent.socket" "/run/user/${_U}/openssh_agent" "/run/user/${_U}/gnupg/S.gpg-agent.ssh"; do for _p in "/run/user/${_U}/keyring/ssh" "/run/user/${_U}/ssh-agent.socket" "/run/user/${_U}/openssh_agent" "/run/user/${_U}/gnupg/S.gpg-agent.ssh"; do
if [ -S "${_p}" ] && ! _is_link_path "${_p}"; then if [ -S "${_p}" ] && ! _is_link_path "${_p}"; then
@@ -189,6 +193,7 @@ if [ "$(uname)" = "Darwin" ] ; then
# Using id -u for better POSIX compatibility than $UID # Using id -u for better POSIX compatibility than $UID
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-$TMPDIR/runtime-$(id -u)}" export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-$TMPDIR/runtime-$(id -u)}"
export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
export PATH="${HOME}/bin/macos:${PATH}"
fi fi
if test -e "$HOME/.localenv"; then if test -e "$HOME/.localenv"; then

View File

@@ -2,19 +2,19 @@
# Roughly based on this article: # Roughly based on this article:
# https://werat.github.io/2017/02/04/tmux-ssh-agent-forwarding.html # https://werat.github.io/2017/02/04/tmux-ssh-agent-forwarding.html
#
# NOTE: this file is executed by sshd as a child process, NOT sourced by the
# user's shell. Any variable assignments or exports here have no effect on the
# shell environment the user will land in.
REMOTE_LINK="${HOME}/.ssh/ssh_auth_sock" REMOTE_LINK="${HOME}/.ssh/ssh_auth_sock"
if [ -S "${SSH_AUTH_SOCK}" ] ; then if [ -S "${SSH_AUTH_SOCK}" ] ; then
SSH_REMOTE_AUTH_SOCK="${SSH_AUTH_SOCK}"
export SSH_REMOTE_AUTH_SOCK
# Always update the symlink to the latest session's socket. # Always update the symlink to the latest session's socket.
# This ensures that tmux (which uses the static path) always points to a # This ensures that tmux (which uses the static path) always points to a
# current agent. # current agent.
mkdir -p "$(dirname "${REMOTE_LINK}")" mkdir -p "$(dirname "${REMOTE_LINK}")"
ln -sf "${SSH_AUTH_SOCK}" "${REMOTE_LINK}" ln -sf "${SSH_AUTH_SOCK}" "${REMOTE_LINK}"
SSH_AUTH_SOCK="${REMOTE_LINK}"
export SSH_AUTH_SOCK
fi fi
# if stdin is a tty, don't do the cookie step # if stdin is a tty, don't do the cookie step

View File

@@ -1,6 +1,7 @@
# For interactive shells # For interactive shells
[[ -n "$ZSH_PROFILE" ]] && { [[ -n "$ZSH_PROFILE" ]] && {
zshrc_start_time=$(date +%s.%N) zmodload zsh/datetime
zshrc_start_time=$EPOCHREALTIME
zmodload zsh/zprof zmodload zsh/zprof
} }
HISTFILE=~/.zhistory HISTFILE=~/.zhistory
@@ -191,6 +192,15 @@ if [[ $- == *i* ]] ; then
source_if_existing $HOME/.aliases.local source_if_existing $HOME/.aliases.local
# zsh-only-ism to avoid error if glob doesn't expand # zsh-only-ism to avoid error if glob doesn't expand
# specifically sets NULLGLOB for this one glob # specifically sets NULLGLOB for this one glob
typeset -gA _deferred_comps
compdef() {
# Store the arguments: first arg is the function, the rest are the commands
local func=$1
shift
for cmd in "$@"; do
_deferred_comps[$cmd]=$func
done
}
for file in $HOME/.zshrc.d/[a-zA-Z0-9]*.zsh(N) ; do for file in $HOME/.zshrc.d/[a-zA-Z0-9]*.zsh(N) ; do
source "$file" source "$file"
done done
@@ -205,19 +215,27 @@ if [[ $- == *i* ]] ; then
zstyle ':completion:*' users root ${USER} zstyle ':completion:*' users root ${USER}
# Modules after fpath # Modules after fpath
autoload -Uz compinit autoload -Uz compinit
for cmd func in "${(@kv)_deferred_comps}"; do
compdef "$func" "$cmd"
done
unset _deferred_comps
# Regenerate zcompdump if it's older than any file in fpath # Regenerate zcompdump if it's older than any file in fpath
DUMPFILE="${ZDOTDIR:-$HOME}/.zcompdump" DUMPFILE="${ZDOTDIR:-$HOME}/.zcompdump"
updated_files=(${^fpath}(N.om[1])) # Find the newest file across all fpath directories to detect edits/additions
# (N.om[1]) = Null-glob, plain files only, sort by mtime, pick the first (newest)
local newest_comp=(${^fpath}/*(N.om[1]))
if [[ ! -f "$DUMPFILE" || ( ${#updated_files} -gt 0 && "$updated_files[1]" -nt "$DUMPFILE" ) ]]; then # If any completion file was modified, or dump doesn't exist, we might need to update.
compinit -i -D "$DUMPFILE" if [[ ! -f "$DUMPFILE" || ( -n "$newest_comp" && "$newest_comp" -nt "$DUMPFILE" ) ]]; then
# Asynchronously compile the dump file compinit -i -d "$DUMPFILE"
{ zcompile "$DUMPFILE" } &! # Asynchronously compile the dump file with an atomic rename
{ zcompile "$DUMPFILE.zwc.tmp" "$DUMPFILE" && mv -f "$DUMPFILE.zwc.tmp" "$DUMPFILE.zwc" } &!
else else
compinit -C -i -D "$DUMPFILE" compinit -C -i -d "$DUMPFILE"
fi fi
unset DUMPFILE updated_files unset DUMPFILE newest_comp
autoload -Uz promptinit && promptinit autoload -Uz promptinit && promptinit
# Virtualenvwrapper # Virtualenvwrapper
source_first_existing \ source_first_existing \
@@ -267,8 +285,11 @@ if [ -x /usr/bin/ack-grep ] ; then
alias ack='/usr/bin/ack-grep' alias ack='/usr/bin/ack-grep'
fi fi
# I want this first always # I want these first always
PATH="${HOME}/bin:${PATH}" PATH="${HOME}/bin:${PATH}"
if [[ "$(uname)" == "Darwin" ]]; then
PATH="${HOME}/bin/macos:${PATH}"
fi
# Load any local settings # Load any local settings
source_if_existing $HOME/.zshrc.local source_if_existing $HOME/.zshrc.local
@@ -292,9 +313,9 @@ fi
typeset -U PATH typeset -U PATH
if [[ -n "$ZSH_PROFILE" ]]; then if [[ -n "$ZSH_PROFILE" ]]; then
zshrc_end_time=$(date +%s.%N) zshrc_end_time=$EPOCHREALTIME
elapsed_seconds=$(echo "$zshrc_end_time - $zshrc_start_time" | bc -l) # Calculation in ms using zsh floating point math
elapsed_ms=$(printf "%.0f" "$(echo "$elapsed_seconds * 1000" | bc -l)") elapsed_ms=$(( (zshrc_end_time - zshrc_start_time) * 1000 ))
echo "zshrc done: ${elapsed_ms}ms" printf "zshrc done: %.0fms\n" "$elapsed_ms"
zprof zprof
fi fi