26 Commits

Author SHA1 Message Date
David Tomaschik
9770514d6a Finish brew updates 2026-05-25 15:11:06 -07:00
David Tomaschik
e1724c77f3 Update Brewfile 2026-05-22 18:23:18 -07:00
David Tomaschik
ecbc25e5ac Improve skelify 2026-05-20 14:35:14 -07:00
David Tomaschik
ea33840ef6 Merge branch 'main' of https://github.com/Matir/skel 2026-05-19 17:02:08 -07:00
David Tomaschik
1048775da3 Update skel 2026-05-19 17:02:05 -07:00
David Tomaschik
c9af80bc5d Merge branch 'main' of https://github.com/Matir/skel 2026-05-19 13:44:52 -07:00
David Tomaschik
d8b4991419 End homebrew hints 2026-05-19 13:44:38 -07:00
David Tomaschik
158d9f6e4e Update Brewfile 2026-05-09 18:47:15 -07:00
David Tomaschik
b1d1c42a02 bump brewfile 2026-05-07 13:43:44 -07:00
David Tomaschik
f8ec9cc338 fix update_brewfile 2026-05-07 08:56:32 -07:00
David Tomaschik
6e3c3dd269 Move keybase to off-corp brewfile 2026-05-07 08:50:45 -07:00
David Tomaschik
77b8374871 Add difft difftool 2026-05-07 08:41:59 -07:00
David Tomaschik
4645682b5c Merge branch 'main' of github.com:Matir/skel 2026-05-07 01:16:56 -07:00
David Tomaschik
75bdebb497 bump 2026-05-07 01:16:54 -07:00
David Tomaschik
4e72b9b18c Update gitconfig 2026-04-28 15:20:24 -07:00
David Tomaschik
bd2c2287cd Add more functions 2026-04-23 10:34:21 -07:00
David Tomaschik
db2c02bd2d Cleanup 2026-04-21 15:59:38 -07:00
David Tomaschik
fec16225e4 Build update-authorized-keys 2026-04-21 14:39:32 -07:00
David Tomaschik
fa6a878487 Fix SSH agent forwarding clobbered by local agent in shenv (#14)
* 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

* 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

* 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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 19:20:43 -07: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
40 changed files with 894 additions and 163 deletions

View File

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

View File

@@ -1,5 +1,7 @@
tap "dart-lang/dart"
tap "holtwick/tap"
tap "sass/sass"
brew "ack"
brew "acme.sh"
brew "age"
@@ -7,18 +9,23 @@ brew "autoconf"
brew "automake"
brew "b2-tools"
brew "bat"
brew "binwalk"
brew "cask"
brew "ccache"
brew "certbot"
brew "cloudflared"
brew "cmake"
brew "colima"
brew "devcontainer"
brew "difftastic"
brew "dfu-util"
brew "difftastic"
brew "direnv"
brew "duck"
brew "dust"
brew "earthly"
brew "esptool"
brew "fish"
brew "fq"
brew "gh"
brew "ghidra", link: false
brew "git"
@@ -27,33 +34,41 @@ brew "git-lfs"
brew "gnupg"
brew "go"
brew "gradle"
brew "hf"
brew "holtwick/tap/bx"
brew "htop"
brew "httpie"
brew "huggingface-cli"
brew "hugo"
brew "imagemagick"
brew "john-jumbo"
brew "jq"
brew "kubeconform"
brew "kubectx"
brew "librsvg"
brew "lima"
brew "minikube"
brew "mise"
brew "mosh"
brew "neovim"
brew "ninja"
brew "nmap"
brew "protobuf"
brew "ollama"
brew "p7zip"
brew "pipenv"
brew "pipx"
brew "pkgconf"
brew "protobuf"
brew "pwgen"
brew "pwntools"
brew "python@3.13"
brew "qemu"
brew "restic"
brew "ripgrep"
brew "ruby"
brew "ruby@3.3"
brew "rustup"
brew "scroll-reverser"
brew "sass/sass/migrator"
brew "sass/sass/sass"
brew "shellcheck"
brew "smartmontools"
brew "starship"
@@ -64,8 +79,7 @@ brew "wget"
brew "yt-dlp"
brew "zlib"
brew "zsh-syntax-highlighting"
brew "sass/sass/migrator"
brew "sass/sass/sass"
cask "codeql"
cask "cyberduck"
cask "font-fira-code-nerd-font"
@@ -82,11 +96,13 @@ cask "iterm2"
cask "macfuse"
cask "meld"
cask "mitmproxy"
cask "processmonitor"
cask "raycast"
cask "rectangle"
cask "scroll-reverser"
cask "temurin"
cask "veracrypt"
cask "wezterm"
cask "zulu@17"
def is_corp?
@@ -94,13 +110,22 @@ def is_corp?
`profiles status -type enrollment 2>/dev/null`.include?("Enrolled via DEP: Yes")
end
if is_corp?
brew "bazelisk", link: false
end
# non-corp
if !is_corp?
brew "bazel"
brew "bazelisk"
brew "openssh"
brew "virt-manager"
cask "claude-code"
cask "cryptomator"
cask "keepassxc"
cask "gcloud-cli"
cask "google-cloud-sdk"
cask "keybase"
cask "orbstack"
cask "jordanbaird-ice"
end

View File

@@ -12,8 +12,17 @@ fi
trap "test -f ${FILENAME} && rm -f ${FILENAME}" EXIT
IOENGINE="libaio"
DIRECT=1
if [ "$(uname)" = "Darwin" ]; then
IOENGINE="posixaio"
# macOS doesn't support O_DIRECT in the same way, but fio's direct=1
# handles it via F_NOCACHE if supported.
DIRECT=1
fi
fio --loops=5 --size=${BENCHMARK_SIZE} --filename=${FILENAME} \
--stonewall --ioengine=libaio --direct=1 \
--stonewall --ioengine=${IOENGINE} --direct=${DIRECT} \
--name=Seqread --bs=1m --rw=read \
--name=Seqwrite --bs=1m --rw=write \
--name=512Kread --bs=512k --rw=randread \

View File

@@ -1,6 +1,10 @@
#!/bin/bash
CHROME_BINS="google-chrome-beta google-chrome"
if [ "$(uname)" = "Darwin" ]; then
CHROME_BINS="${CHROME_BINS} /Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
fi
for bin in ${CHROME_BINS} ; do
if command -v ${bin} >/dev/null 2>&1 ; then
CHROME=$(command -v ${bin})
@@ -18,4 +22,4 @@ export HOME=${HOME}/.chrome-pentest
mkdir -p ${HOME}
# Launch chrome for burp
exec ${CHROME} --user-data-dir=${HOME}/chrome-pentest --proxy-server=127.0.0.1:8080
exec "${CHROME}" --user-data-dir=${HOME}/chrome-pentest --proxy-server=127.0.0.1:8080

View File

@@ -7,16 +7,69 @@ import sys
import argparse
import difflib
import tempfile
# Regex to match brew/cask/tap/mas lines
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():
script_dir = os.path.dirname(os.path.realpath(__file__))
try:
root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'],
cwd=script_dir,
stderr=subprocess.STDOUT).decode().strip()
return root
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):
ignore = set()
@@ -34,11 +87,19 @@ def get_ignore_list(repo_root):
ignore.add(line)
return ignore
def get_current_packages():
def get_current_packages(args):
"""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:
output = subprocess.check_output(['brew', 'bundle', 'dump', '--file=-'],
stderr=subprocess.DEVNULL).decode()
output = subprocess.check_output(cmd, env=env, stderr=subprocess.DEVNULL).decode()
return output.splitlines()
except subprocess.CalledProcessError:
print("Error: 'brew bundle dump' failed. Is homebrew installed?", file=sys.stderr)
@@ -48,9 +109,9 @@ def parse_brewfile(content):
"""
Parses Brewfile content.
Returns:
- unconditional_lines: list of strings
- entries: list of Entry objects
- conditional_pkgs: set of (type, name)
- preserved_footer: string (everything from first conditional onwards)
- footer: string (everything from first conditional onwards)
"""
lines = content.splitlines()
conditional_pkgs = set()
@@ -78,14 +139,35 @@ def parse_brewfile(content):
if stripped == 'end' or stripped.endswith('; end'):
in_conditional -= 1
if first_conditional_idx == -1:
return lines, set(), ""
unconditional_lines = lines[:first_conditional_idx] if first_conditional_idx != -1 else lines
footer = "\n".join(lines[first_conditional_idx:]) if first_conditional_idx != -1 else ""
unconditional_lines = lines[:first_conditional_idx]
footer = "\n".join(lines[first_conditional_idx:])
return unconditional_lines, conditional_pkgs, footer
entries = []
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):
if sys.platform != "darwin":
print(f"Warning: Running on {sys.platform}. Brewfile is primarily for macOS.", file=sys.stderr)
repo_root = get_repo_root()
brewfile_path = os.path.join(repo_root, 'Brewfile')
@@ -96,66 +178,90 @@ def main(args):
with open(brewfile_path) as f:
old_content = f.read()
unconditional_lines, conditional_pkgs, footer = parse_brewfile(old_content)
old_entries, conditional_pkgs, footer = parse_brewfile(old_content)
ignore_list = get_ignore_list(repo_root)
dumped_lines = get_current_packages()
old_pkg_map = {}
for e in old_entries:
if isinstance(e, PackageEntry):
key = (e.pkg_type, e.name)
if key not in old_pkg_map:
old_pkg_map[key] = e
new_unconditional_lines = []
seen_pkgs = set()
if args.add_only:
# First, keep existing unconditional packages
for line in unconditional_lines:
dumped_lines = get_current_packages(args)
dumped_pkgs = []
for line in dumped_lines:
match = PKG_RE.match(line)
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 or (pkg_type, pkg_name) in conditional_pkgs:
continue
new_unconditional_lines.append(line)
seen_pkgs.add((pkg_type, pkg_name))
elif line.strip() and not line.strip().startswith('#'):
# Keep other non-comment lines
new_unconditional_lines.append(line)
dumped_pkgs.append(PackageEntry(pkg_type, pkg_name, pkg_options))
# Then, add new packages from dump
for line in dumped_lines:
match = PKG_RE.match(line)
if match:
pkg_type, pkg_name = match.group(1), match.group(2)
if (pkg_type, pkg_name) not in seen_pkgs and pkg_name not in ignore_list and (pkg_type, pkg_name) not in conditional_pkgs:
new_unconditional_lines.append(line)
seen_pkgs.add((pkg_type, pkg_name))
new_entries = []
for e in old_entries:
if isinstance(e, TextEntry) and e.is_header:
new_entries.append(e)
seen_in_new = set()
added_count = 0
removed_count = 0
merged_count = 0
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 line in dumped_lines:
match = PKG_RE.match(line)
if match:
pkg_type, pkg_name = match.group(1), match.group(2)
if pkg_name in ignore_list:
continue
if (pkg_type, pkg_name) in conditional_pkgs:
continue
if (pkg_type, pkg_name) in seen_pkgs:
continue
seen_pkgs.add((pkg_type, pkg_name))
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)
# If it's not a package line (e.g. comment from dump), we can skip or keep
if line.strip():
new_unconditional_lines.append(line)
# Check for removals
for key in old_pkg_map:
if key not in seen_in_new:
removed_count += 1
# Sort lines by type (tap, brew, cask, mas) then name
def sort_key(line):
match = PKG_RE.match(line)
if not match: return (4, line)
order = {'tap': 0, 'brew': 1, 'cask': 2, 'mas': 3}
return (order.get(match.group(1), 4), match.group(2))
for e in old_entries:
if isinstance(e, TextEntry) and not e.is_header:
new_entries.append(e)
new_unconditional_lines.sort(key=sort_key)
new_entries.sort(key=lambda x: x.sort_key())
# Build new content
new_content = "\n".join(new_unconditional_lines)
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
lines = e.to_lines()
# If we just added a blank line, and the new lines start with one, skip the first new line
if output_lines and output_lines[-1] == "" and lines and lines[0] == "":
output_lines.extend(lines[1:])
else:
output_lines.extend(lines)
new_content = "\n".join(output_lines)
if footer:
if new_unconditional_lines:
if output_lines and output_lines[-1].strip():
new_content += "\n\n"
new_content += footer.strip() + "\n"
else:
@@ -166,21 +272,33 @@ def main(args):
else:
if args.dry_run:
print("Changes detected (dry run):")
diff = difflib.unified_diff(
diff = list(difflib.unified_diff(
old_content.splitlines(keepends=True),
new_content.splitlines(keepends=True),
fromfile='Brewfile (original)',
tofile='Brewfile (new)'
)
))
if sys.stdout.isatty():
sys.stdout.writelines(colorize_diff(diff))
else:
sys.stdout.writelines(diff)
else:
with open(brewfile_path, 'w') as f:
f.write(new_content)
dir_name = os.path.dirname(brewfile_path)
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.")
if args.verbose:
print(f"Summary: {added_count} added, {removed_count} removed, {merged_count} kept/merged.")
if __name__ == "__main__":
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("--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()
main(args)

63
bin/quartz Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
set -euo pipefail
# QUARTZ_DIR search logic
if [ -z "${QUARTZ_DIR:-}" ]; then
if [ -f "quartz.config.ts" ]; then
QUARTZ_DIR="$PWD"
elif [ -d "$HOME/Personal/notes-quartz" ]; then
QUARTZ_DIR="$HOME/Personal/notes-quartz"
elif [ -d "$HOME/Projects/notes-quartz" ]; then
QUARTZ_DIR="$HOME/Projects/notes-quartz"
else
echo "Error: QUARTZ_DIR could not be found." >&2
exit 1
fi
fi
if [ ! -d "$QUARTZ_DIR" ]; then
echo "Error: QUARTZ_DIR '$QUARTZ_DIR' is not a directory." >&2
exit 1
fi
# NOTES_DIR search logic
PARSE_NOTES_DIR=""
# Use a copy of args to find -d/--directory
ARGS=("$@")
for ((i=0; i<${#ARGS[@]}; i++)); do
if [[ "${ARGS[i]}" == "-d" || "${ARGS[i]}" == "--directory" ]]; then
if [[ $((i+1)) -lt ${#ARGS[@]} ]]; then
PARSE_NOTES_DIR="${ARGS[i+1]}"
fi
break
fi
done
if [ -n "$PARSE_NOTES_DIR" ]; then
NOTES_DIR="$PARSE_NOTES_DIR"
elif [ -z "${NOTES_DIR:-}" ]; then
if [ -d "$HOME/Notes" ]; then
NOTES_DIR="$HOME/Notes"
elif [ -d "$HOME/Personal/notes" ]; then
NOTES_DIR="$HOME/Personal/notes"
elif [ -d "$HOME/Projects/notes" ]; then
NOTES_DIR="$HOME/Projects/notes"
else
echo "Error: NOTES_DIR could not be found." >&2
exit 1
fi
fi
if [ ! -d "$NOTES_DIR" ]; then
echo "Error: NOTES_DIR '$NOTES_DIR' is not a directory." >&2
exit 1
fi
cd "$QUARTZ_DIR"
# Run npx quartz
# Following the prompt's structure but using NOTES_DIR for the flag
# npx quartz ${argv[1]} --directory ${NOTES_DIR} "$@"
# We use ${1:-} for argv[1] to handle cases with no arguments.
npx quartz "${1:-}" --directory "$NOTES_DIR" "$@"

View File

@@ -5,8 +5,14 @@
set -ue
TOOLS="flameshot scrot"
if [ "$(uname)" = "Darwin" ]; then
TOOLS="screencapture ${TOOLS}"
fi
SCREENDIR=${SCREENDIR:-${HOME}/Pictures/Screenshots}
SCROT_FORMAT="%F-%T.png"
# Filename for screencapture
FILE_NAME=$(date "+%Y-%m-%d-%H%M%S.png")
function default_screenshot_command {
for tool in ${TOOLS} ; do
@@ -41,10 +47,29 @@ function scrot_full_capture {
scrot "${SCREENDIR}/${SCROT_FORMAT}"
}
function mac_capture {
local mode="${1}"
local target="${SCREENDIR}/${FILE_NAME}"
case "${mode}" in
region)
screencapture -i "${target}"
;;
window)
screencapture -i -w "${target}"
;;
full)
screencapture "${target}"
;;
esac
}
case "${CMD}" in
region|window|full)
mkdir -p "${SCREENDIR}"
case "${TOOL}" in
screencapture)
mac_capture "${CMD}"
;;
flameshot)
case "${CMD}" in
region|window)

310
bin/update-authorized-keys Executable file
View File

@@ -0,0 +1,310 @@
#!/usr/bin/env bash
# update-authorized-keys - Manage ~/.ssh/authorized_keys from multiple sources
#
# BEHAVIOR:
# 1. Collects SSH public keys from one or more source directories (default: ~/.ssh/authorized_keys.d).
# 2. Skips empty files and files symlinked to /dev/null (masking).
# 3. Deterministically concatenates keys into a "managed block" wrapped in markers:
# # BEGIN UPDATE-AUTHORIZED-KEYS
# # END UPDATE-AUTHORIZED-KEYS
# 4. Deduplicates managed keys: if the same key (including options) is found in multiple files,
# it is included once with a comment listing all source filenames.
# 5. Preserves "manual" keys found in the target file outside the markers.
# 6. Removes manual keys that exactly match a managed key (options + key data).
# 7. Validates every proposed key individually using 'ssh-keygen -l -f'.
# 8. Optionally validates the whole file with 'authorized-keys-test' if available.
# 9. Displays a unified diff and prompts for confirmation before atomic replacement.
# 10. Supports a --dry-run mode and a --self-test mode for verifying logic.
set -o nounset
set -o errexit
set -o pipefail
CLEANUP_FILES=()
cleanup() {
rm -rf "${CLEANUP_FILES[@]}"
}
trap cleanup EXIT
# Configuration
DEFAULT_DIR="${HOME}/.ssh/authorized_keys.d"
DEFAULT_TARGET="${HOME}/.ssh/authorized_keys"
BEGIN_MARKER="# BEGIN UPDATE-AUTHORIZED-KEYS"
END_MARKER="# END UPDATE-AUTHORIZED-KEYS"
# State
SOURCE_DIRS=()
TARGET_FILE="${DEFAULT_TARGET}"
DRY_RUN=0
usage() {
cat <<EOF
Usage: $(basename "$0") [options]
Options:
--dir DIR Primary directory for managed keys (default: ${DEFAULT_DIR})
--extra-dir DIR Additional directory to scan for keys (can be repeated)
--target FILE Target authorized_keys file (default: ${DEFAULT_TARGET})
--dry-run Show changes and validate without modifying the target
--self-test Run internal suite of tests to verify script logic
--help Show this help message
EOF
}
run_self_test() {
echo "Running self-test..."
local test_root=$(mktemp -d)
CLEANUP_FILES+=("${test_root}")
local d1="${test_root}/d1"
local d2="${test_root}/d2"
local target="${test_root}/target"
mkdir -p "${d1}" "${d2}"
local key1="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5XoR7N7X5X key1"
local key2="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL9YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6YpS8O8Y6Y key2"
local key_man="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM0ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7ZqT9P9Z7Z manual"
local long_opt="environment=\"VAR=VERY_LONG_VALUE_THAT_EXCEEDS_TWENTY_CHARS\""
echo "${key1}" > "${d1}/k1"
echo "${key1}" > "${d2}/k1_dup"
echo "${key2}" > "${d2}/k2"
echo "${long_opt} ${key1}" > "${d1}/k1_long"
echo "${long_opt} ${key2}" > "${d1}/k2_long"
ln -s /dev/null "${d1}/masked"
cat <<EOF > "${target}"
${key_man}
${key1} # This should be removed as it's now managed
EOF
echo "Executing script in test mode..."
# Pipe "y" to handle the TTY check if we are not in a TTY during test
echo "y" | "$0" --dir "${d1}" --extra-dir "${d2}" --target "${target}" > /dev/null
local content=$(cat "${target}")
echo -n "Check markers... "
if [[ "${content}" == *"${BEGIN_MARKER}"* && "${content}" == *"${END_MARKER}"* ]]; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check managed deduplication... "
if grep -q "Source: k1, k1_dup" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check long option deduplication (should NOT deduplicate different keys)... "
if grep -q "k1_long" "${target}" && grep -q "k2_long" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check manual key preservation... "
if grep -q "manual" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo -n "Check manual key filtering... "
local manual_count=$(grep -c "${key1}" "${target}")
# key1 appears twice in managed block (once plain, once with long opt)
# and it was in manual block. The manual one should be removed.
# So we expect 2 occurrences in the final file (both in managed block).
if [[ ${manual_count} -eq 2 ]]; then echo "OK"; else echo "FAIL (Found ${manual_count} occurrences, expected 2)"; exit 1; fi
echo -n "Check masking... "
if ! grep -q "masked" "${target}"; then echo "OK"; else echo "FAIL"; exit 1; fi
echo "Self-test passed successfully!"
exit 0
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--dir)
[[ -z "${2:-}" ]] && { echo "Error: --dir requires an argument" >&2; exit 1; }
SOURCE_DIRS+=("$2"); shift 2 ;;
--extra-dir)
[[ -z "${2:-}" ]] && { echo "Error: --extra-dir requires an argument" >&2; exit 1; }
SOURCE_DIRS+=("$2"); shift 2 ;;
--target)
[[ -z "${2:-}" ]] && { echo "Error: --target requires an argument" >&2; exit 1; }
TARGET_FILE="$2"; shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
--self-test) run_self_test ;;
--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done
if [[ ${#SOURCE_DIRS[@]} -eq 0 ]]; then
SOURCE_DIRS+=("${DEFAULT_DIR}")
fi
mkdir -p "$(dirname "${TARGET_FILE}")"
TMP_FILE=$(mktemp)
CLEANUP_FILES+=("${TMP_FILE}")
collect_keys() {
local dirs=("${@}")
for dir in "${dirs[@]}"; do
if [[ ! -d "${dir}" ]]; then continue; fi
# Use a glob to avoid parsing ls
for file in "${dir}"/*; do
[[ ! -e "${file}" ]] && continue
[[ ! -f "${file}" || ! -s "${file}" ]] && continue
if [[ -L "${file}" && "$(readlink "${file}")" == "/dev/null" ]]; then continue; fi
while read -r line; do
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
# Use a specific delimiter that is unlikely to be in the key or filename
# If using tabs, ensure we only split on the first one in AWK
printf "%s\t%s\n" "$(basename "${file}")" "${line}"
done < "${file}"
done
done
}
# Use a HEREDOC for the complex AWK script to avoid shell interpolation issues
MANAGED_BLOCK=$(collect_keys "${SOURCE_DIRS[@]}" | awk -F'\t' '
{
# Splitting on the first tab manually to be robust
tab_idx = index($0, "\t")
source = substr($0, 1, tab_idx - 1)
full_line = substr($0, tab_idx + 1)
# Signature detection: all options + key type + key data
# (Excludes the comment at the end)
n = split(full_line, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
# A key line is [options] <type> <base64> [comment]
# We stop after the base64 part. Key types start with known prefixes.
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
# Fallback if no key type found (should not happen with valid keys)
if (sig == "") sig = full_line
if (!(sig in keys)) {
keys[sig] = full_line
order[++count] = sig
}
sources[sig] = (sources[sig] ? sources[sig] ", " : "") source
}
END {
for (i=1; i<=count; i++) {
sig = order[i]
print "# Source: " sources[sig]
print keys[sig]
}
}')
MANUAL_KEYS=""
if [[ -f "${TARGET_FILE}" ]]; then
MANUAL_KEYS=$(awk -v begin="${BEGIN_MARKER}" -v end="${END_MARKER}" '
BEGIN { inside=0 }
$0 == begin { inside=1; next }
$0 == end { inside=0; next }
!inside { print $0 }
' "${TARGET_FILE}")
fi
MANAGED_SIGS_TMP=$(mktemp)
echo "${MANAGED_BLOCK}" | awk '/^[^#]/ {
n = split($0, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
if (sig != "") print sig
}' > "${MANAGED_SIGS_TMP}"
FINAL_MANUAL_KEYS=$(echo "${MANUAL_KEYS}" | awk -v sigs_file="${MANAGED_SIGS_TMP}" '
BEGIN {
while ((getline line < sigs_file) > 0) {
managed[line] = 1
}
close(sigs_file)
}
{
if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*#/) {
print $0
next
}
n = split($0, parts, " ")
sig = ""
for (i=1; i<=n; i++) {
sig = (sig == "" ? parts[i] : sig " " parts[i])
if (parts[i] ~ /^(ssh-|ecdsa-|sk-)/ && i < n) {
sig = sig " " parts[i+1]
break
}
}
if (!(sig in managed)) {
print $0
}
}')
rm -f "${MANAGED_SIGS_TMP}"
{
if [[ -n "${MANAGED_BLOCK}" ]]; then
echo "${BEGIN_MARKER}"
echo "${MANAGED_BLOCK}"
echo "${END_MARKER}"
fi
echo "${FINAL_MANUAL_KEYS}"
} > "${TMP_FILE}"
echo "Validating proposed changes..."
VALID=1
while read -r line; do
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
if ! echo "${line}" | ssh-keygen -l -f - >/dev/null 2>&1; then
echo "ERROR: Invalid SSH key detected: ${line}" >&2
VALID=0
fi
done < "${TMP_FILE}"
if command -v authorized-keys-test >/dev/null 2>&1; then
if ! authorized-keys-test "${TMP_FILE}"; then
echo "ERROR: Proposed file failed authorized-keys-test." >&2
VALID=0
fi
fi
if [[ ${VALID} -eq 0 ]]; then
echo "Validation failed. Aborting." >&2
exit 1
fi
if [[ -f "${TARGET_FILE}" ]]; then
diff -u "${TARGET_FILE}" "${TMP_FILE}" || true
else
echo "Target file does not exist. Proposed content:"
cat "${TMP_FILE}"
fi
if [[ ${DRY_RUN} -eq 1 ]]; then
echo "Dry run complete. No changes made."
exit 0
fi
if [[ -t 0 ]]; then
echo -n "Apply these changes to ${TARGET_FILE}? [y/N] "
read -r response
elif [[ ! -t 0 ]]; then
# Read from pipe or file if provided
if ! read -r response; then
echo "Non-interactive shell detected and no input provided. Aborting."
exit 1
fi
fi
if [[ "${response}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
chmod 0600 "${TMP_FILE}"
mv "${TMP_FILE}" "${TARGET_FILE}"
echo "Changes applied successfully."
else
echo "Aborted."
exit 1
fi

View File

@@ -1,9 +1,6 @@
# General aliases, should only be sourced in interactive shells
# Try to keep in sync with ~/.config/fish/conf.d/aliases.fish
# Cryptsetup alias
alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Timestamp in a machine-sortable form
alias tstamp="date '+%Y%m%d-%H%M%S'"
@@ -13,12 +10,6 @@ alias mdcode="sed 's/^/ /'"
# Intel format plz
alias objdump="command objdump -M intel"
# Drop caches for swap issues
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
# get git working directory
alias gitroot="git rev-parse --show-toplevel"
@@ -31,20 +22,37 @@ alias ipy="ipython3 --no-banner"
# Skip the header on bc
alias bc="command bc -q"
# Get a decently readable df
alias dfh="df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Clear the GPG agent
alias clear-gpg-agent="echo RELOADAGENT | gpg-connect-agent"
# Earthly ssh
alias earthly='earthly --ssh-auth-sock ""'
if [ "$(uname)" = "Linux" ]; then
# Cryptsetup alias
alias luksFormat='cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Drop caches for swap issues
alias drop_caches="echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp='printf "%02.2f\n" "$(cat /sys/class/thermal/thermal_zone0/temp)e-3"'
# Get a decently readable df
alias dfh="df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Battery details
alias bat-details='upower -i $(upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate='nvidia-settings --display=:0 -q RefreshRate -t'
# Earthly ssh
alias earthly='earthly --ssh-auth-sock ""'
# to clipboard
alias toclip='xclip -selection clipboard'
elif [ "$(uname)" = "Darwin" ]; then
# Get a decently readable df
alias dfh="df -h"
# to clipboard
alias toclip='pbcopy'
fi

4
dotfiles/bxignore Normal file
View File

@@ -0,0 +1,4 @@
# Credentials
.ssh
Passwords.kdbx
Passwords.kdbx.age

View File

@@ -1,6 +1,3 @@
# Cryptsetup alias
alias luksFormat 'cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Timestamp in a machine-sortable form
alias tstamp "date '+%Y%m%d-%H%M%S'"
@@ -10,12 +7,6 @@ alias mdcode "sed 's/^/ /'"
# Intel format plz
alias objdump "command objdump -M intel"
# Drop caches for swap issues
alias drop_caches "echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp 'printf "%02.2f\n" (cat /sys/class/thermal/thermal_zone0/temp)e-3'
# get git working directory
alias gitroot "git rev-parse --show-toplevel"
@@ -28,23 +19,40 @@ alias ipy "ipython3 --no-banner"
# Skip the header on bc
alias bc "command bc -q"
# Get a decently readable df
alias dfh "df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Clear the GPG agent
alias clear-gpg-agent "echo RELOADAGENT | gpg-connect-agent"
# Earthly ssh
alias earthly 'earthly --ssh-auth-sock ""'
if test (uname) = "Linux"
# Cryptsetup alias
alias luksFormat 'cryptsetup luksFormat --type=luks2 --pbkdf-memory=2560000 --pbkdf=argon2id -i 15000 -s 512 -h sha256 -c aes-xts-plain64'
# Drop caches for swap issues
alias drop_caches "echo 3 | sudo /usr/bin/tee /proc/sys/vm/drop_caches"
# dump acpi temperature
alias gettemp 'printf "%02.2f\n" (cat /sys/class/thermal/thermal_zone0/temp)e-3'
# Get a decently readable df
alias dfh "df -h -x tmpfs -x devtmpfs -x squashfs -x fuse -x efivarfs"
# Battery details
alias bat-details 'upower -i (upower -e | grep battery)'
# Nvidia refresh rate
alias nvidia-refresh-rate 'nvidia-settings --display=:0 -q RefreshRate -t'
# Earthly ssh
alias earthly 'earthly --ssh-auth-sock ""'
# to clipboard
alias toclip 'xclip -selection clipboard'
else if test (uname) = "Darwin"
# Get a decently readable df
alias dfh "df -h"
# to clipboard
alias toclip 'pbcopy'
end
# On some systems, bat is batcat
if not command -v bat >/dev/null 2>&1

View File

@@ -30,5 +30,10 @@ if status --is-interactive
install_fisher
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
if test (uname) = "Darwin"
fish_add_path --move --path {$HOME}/bin/macos
else if test (uname) = "Linux"
fish_add_path --move --path {$HOME}/bin/linux
end

View File

@@ -9,9 +9,8 @@ uv_venv_auto = true
[tools]
age = "latest"
age-plugin-yubikey = "latest"
usage = "latest"
uv = "latest"
uv = "0.11.8"
[hooks]
postinstall = "mise sync python --uv"

View File

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

View File

@@ -19,14 +19,17 @@
[difftool]
prompt = false
[difftool "difftastic"]
cmd = difft "$LOCAL" "$REMOTE"
[alias]
st = status
last = log -1 HEAD
# Thanks to
# http://durdn.com/blog/2012/11/22/must-have-git-aliases-advanced-examples/
logs = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate
logs = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate
lg = log -p
ll = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat
ll = log --pretty=format:"%C(yellow)%h%Cred%d %Creset%s%Cblue [%cn]" --decorate --numstat
files = ls-files
ls = ls-files
lol = log --graph --pretty=format:'%C(yellow)%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cblue(%cr)%Creset' --abbrev-commit --date=relative
@@ -92,8 +95,12 @@
process = git-lfs filter-process
[include]
path = ~/.gitconfig.d/aliases
path = ~/.gitconfig.d/override
path = ~/.gitconfig.d/local
[includeIf "gitdir:~/personal/"]
[includeIf "gitdir/i:~/personal/"]
path = ~/.gitconfig.d/personal
[rerere]
enabled = true

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

@@ -15,6 +15,7 @@ export DEBEMAIL="david@systemoverlord.com"
export DEBFULLNAME="David Tomaschik"
export LESS="-MR"
export QUOTING_STYLE="literal" # Coreutils quotes
export HOMEBREW_NO_ENV_HINTS=1
# Fix gnome-terminal
if [ "$TERM" = "xterm" ] && [ "$COLORTERM" = "gnome-terminal" ] ; then
@@ -113,13 +114,17 @@ _is_link_path() {
_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
_CANDIDATE="${SSH_AUTH_SOCK}"
fi
# 2. If no candidate yet, or we're currently using the link, try to find the "real" system agent.
if [ -z "${_CANDIDATE}" ] || _is_link_path "${SSH_AUTH_SOCK:-}"; then
# 2. Only look for a system agent if the stable link is already broken. If the link is
# 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=""
if [ "$(uname)" = "Darwin" ]; then
_FOUND=$(launchctl getenv SSH_AUTH_SOCK 2>/dev/null)
@@ -143,8 +148,8 @@ if [ -z "${_CANDIDATE}" ] || _is_link_path "${SSH_AUTH_SOCK:-}"; then
fi
fi
# 3. Last resort: search common paths if we still don't have a valid candidate.
if [ ! -S "${_CANDIDATE}" ]; then
# 3. Last resort: search common paths if we still don't have a candidate and the link is broken.
if [ ! -S "${_CANDIDATE}" ] && [ ! -S "${_SSH_AUTH_LINK}" ]; then
_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
if [ -S "${_p}" ] && ! _is_link_path "${_p}"; then
@@ -189,6 +194,9 @@ if [ "$(uname)" = "Darwin" ] ; then
# Using id -u for better POSIX compatibility than $UID
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-$TMPDIR/runtime-$(id -u)}"
export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
export PATH="${HOME}/bin/macos:${PATH}"
elif [ "$(uname)" = "Linux" ] ; then
export PATH="${HOME}/bin/linux:${PATH}"
fi
if test -e "$HOME/.localenv"; then

View File

@@ -2,19 +2,19 @@
# Roughly based on this article:
# 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"
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.
# This ensures that tmux (which uses the static path) always points to a
# current agent.
mkdir -p "$(dirname "${REMOTE_LINK}")"
ln -sf "${SSH_AUTH_SOCK}" "${REMOTE_LINK}"
SSH_AUTH_SOCK="${REMOTE_LINK}"
export SSH_AUTH_SOCK
fi
# if stdin is a tty, don't do the cookie step

View File

@@ -45,7 +45,7 @@ set -g window-status-current-style fg=colour235,bg=colour33,bold
set -g status-interval 60
set -g status-left-length 30
set -g status-left '/#h: #S/ '
set -g status-right '#{?pane_title,/#{pane_title}/ ,}#(cut -d " " -f 1-3 /proc/loadavg)#[default] #[fg=colour166]%H:%M#[default]'
set -g status-right '#{?pane_title,/#{pane_title}/ ,}#(uptime | rev | cut -d":" -f1 | rev | sed s/,//g)#[default] #[fg=colour166]%H:%M#[default]'
# Advanced mouse mode from http://tangledhelix.com/blog/2012/07/16/tmux-and-mouse-mode/
# Toggle mouse on
@@ -65,8 +65,9 @@ bind M \
display 'Mouse: OFF'
# tmux X clipboard integration
bind C-c run "tmux show-buffer | xsel -i -b"
bind C-v run "tmux set-buffer -- \"$(xsel -o -b)\"; tmux paste-buffer"
if-shell 'test "$(uname)" = "Darwin"' \
'bind C-c run "tmux show-buffer | pbcopy"; bind C-v run "tmux set-buffer -- \"$(pbpaste)\"; tmux paste-buffer"' \
'bind C-c run "tmux show-buffer | xsel -i -b"; bind C-v run "tmux set-buffer -- \"$(xsel -o -b)\"; tmux paste-buffer"'
# List of plugins
set -g @plugin 'tmux-plugins/tpm'

View File

@@ -185,6 +185,9 @@ have_command() {
if test -d ${HOME}/.local/bin ; then
export PATH="${HOME}/.local/bin:${PATH}"
fi
if test -d ${HOME}/.npm-packages/bin ; then
export PATH="${HOME}/.npm-packages/bin:${PATH}"
fi
# Source extras and aliases if interactive
if [[ $- == *i* ]] ; then
@@ -192,6 +195,15 @@ if [[ $- == *i* ]] ; then
source_if_existing $HOME/.aliases.local
# zsh-only-ism to avoid error if glob doesn't expand
# 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
source "$file"
done
@@ -206,6 +218,11 @@ if [[ $- == *i* ]] ; then
zstyle ':completion:*' users root ${USER}
# Modules after fpath
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
DUMPFILE="${ZDOTDIR:-$HOME}/.zcompdump"
@@ -271,8 +288,13 @@ if [ -x /usr/bin/ack-grep ] ; then
alias ack='/usr/bin/ack-grep'
fi
# I want this first always
# I want these first always
PATH="${HOME}/bin:${PATH}"
if [[ "$(uname)" == "Darwin" ]]; then
PATH="${HOME}/bin/macos:${PATH}"
elif [[ "$(uname)" == "Linux" ]]; then
PATH="${HOME}/bin/linux:${PATH}"
fi
# Load any local settings
source_if_existing $HOME/.zshrc.local

View File

@@ -18,8 +18,15 @@ alert() {
icon="error"
fi
if [ "$(uname)" = "Darwin" ]; then
# macOS notification
local title="Finished: '$*'"
local msg="Exit code: $ret"
osascript -e "display notification \"$msg\" with title \"$title\""
else
# Send the notification with the executed command
notify-send --urgency=low -i "$icon" "Finished: '$@'"
fi
# Return the original exit code
return $ret

View File

@@ -1,7 +1,12 @@
if [ "$(uname)" = "Darwin" ]; then
ANDROID_HOME=$HOME/Library/Android/sdk
else
ANDROID_HOME=$HOME/Android/Sdk
fi
if test -d $ANDROID_HOME ; then
PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools
if test -d "${ANDROID_HOME}" ; then
export ANDROID_HOME
PATH="${PATH}:${ANDROID_HOME}/emulator:${ANDROID_HOME}/platform-tools"
else
unset ANDROID_HOME
fi

View File

@@ -6,7 +6,11 @@ if have_command nasm && have_command objdump ; then
local TMPF=`mktemp`
local bytes
local byte
$NASM -f elf -o $TMPF $1
local format="elf"
if [[ "$OSTYPE" == darwin* ]]; then
format="macho64"
fi
$NASM -f $format -o $TMPF $1
$OBJDUMP -M intel -d $TMPF | grep '^ ' | cut -f2 | while read -A bytes ; do
for byte in $bytes ; do
echo -n "\\\\x$byte"

View File

@@ -1,5 +1,11 @@
function dumpenv {
if [ "$(uname)" = "Linux" ]; then
tr '\0' '\n' < /proc/${1}/environ
elif [ "$(uname)" = "Darwin" ]; then
# macOS doesn't have /proc, use ps instead.
# Note: this may truncate if environment is very large.
ps -p ${1} -wwwe -o command= | tr ' ' '\n' | grep '='
fi
}
if test -x "/sbin/starship" ; then

View File

@@ -1,8 +1,20 @@
test -f /usr/share/source-highlight/src-hilite-lesspipe.sh && \
# Find src-hilite-lesspipe.sh
_SRCHILITE=""
for _p in /usr/share/source-highlight/src-hilite-lesspipe.sh /opt/homebrew/bin/src-hilite-lesspipe.sh /usr/local/bin/src-hilite-lesspipe.sh ; do
if [ -f "$_p" ] ; then
_SRCHILITE="$_p"
break
fi
done
if [ -n "$_SRCHILITE" ] ; then
function srcless {
if [ $# -ne 1 ] ; then
echo "$0 <what>" > /dev/stderr
echo "Usage: srcless <file>" > /dev/stderr
return 1
fi
/usr/share/source-highlight/src-hilite-lesspipe.sh $1 | less -R
"$_SRCHILITE" "$1" | less -R
}
fi
unset _SRCHILITE _p

View File

@@ -3,6 +3,17 @@
# Skelify -- move a file to my .skel and setup symlinks
function skelify {
local -A opts
zparseopts -D -A opts -overlay:
local overlay_name="${opts[--overlay]}"
local base_skel_dir="${HOME}/.skel/dotfiles"
local extra_args=()
if [[ -n "${overlay_name}" ]]; then
base_skel_dir="${HOME}/.skel/dotfile_overlays/${overlay_name}"
extra_args=(--overlay "${overlay_name}")
fi
local target
local whichdir
local relhome
@@ -10,16 +21,19 @@ function skelify {
local fulltarget
for target in $~@; do
if test -d ${target} ; then
skelify ${target}/* || return 1
skelify "${extra_args[@]}" ${target}/* || return 1
elif test -f ${target} ; then
if ! whichdir=$(cd $(dirname $target) && pwd); then
echo Could not find directory for $target >/dev/stderr
return 1
fi
fname=$(basename ${target})
relhome=${whichdir#${HOME}/}
fulltarget="${whichdir}/${fname}"
if [[ ${relhome} == ${whichdir} ]] ; then
if [[ ${whichdir} == ${HOME} ]] ; then
relhome=""
elif [[ ${whichdir} == ${HOME}/* ]] ; then
relhome=${whichdir#${HOME}/}
else
echo ${whichdir} is not in home >/dev/stderr
return 1
fi
@@ -32,7 +46,7 @@ function skelify {
return 1
fi
echo ${target}
local skeldir="${HOME}/.skel/dotfiles/${relhome}"
local skeldir="${base_skel_dir}/${relhome}"
mkdir -p "${skeldir}"
mv ${target} "${skeldir}/${fname}"
ln -s "${skeldir}/${fname}" "${fulltarget}"

56
dotfiles/zshrc.d/util.zsh Normal file
View File

@@ -0,0 +1,56 @@
# utility function to "open" a file
o() {
if [[ "$OSTYPE" == "darwin"* ]]; then
open "$@"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
xdg-open "$@"
else
echo "Unknown OS"
fi
}
# Copy from stdin to the system clipboard
syscopy() {
if command -v pbcopy >/dev/null 2>&1; then
# macOS
pbcopy "$@"
elif command -v wl-copy >/dev/null 2>&1; then
# Linux Wayland
wl-copy "$@"
elif command -v xclip >/dev/null 2>&1; then
# Linux X11
xclip -selection clipboard "$@"
elif command -v xsel >/dev/null 2>&1; then
# Linux X11 (alternative)
xsel --clipboard --input "$@"
elif command -v clip.exe >/dev/null 2>&1; then
# Windows WSL
clip.exe "$@"
else
echo "Error: No clipboard utility found. Please install pbcopy, wl-copy, xclip, or xsel." >&2
return 1
fi
}
# Paste from the system clipboard to stdout
syspaste() {
if command -v pbpaste >/dev/null 2>&1; then
# macOS
pbpaste "$@"
elif command -v wl-paste >/dev/null 2>&1; then
# Linux Wayland
wl-paste "$@"
elif command -v xclip >/dev/null 2>&1; then
# Linux X11
xclip -selection clipboard -o "$@"
elif command -v xsel >/dev/null 2>&1; then
# Linux X11 (alternative)
xsel --clipboard --output "$@"
elif command -v powershell.exe >/dev/null 2>&1; then
# Windows WSL
powershell.exe -noprofile -command Get-Clipboard "$@"
else
echo "Error: No clipboard utility found. Please install pbpaste, wl-paste, xclip, or xsel." >&2
return 1
fi
}