17 Commits
Author SHA1 Message Date
Fabian Ising 874a823d1e [AC] Migrate from brew 2026-09-01 08:29:15 +02:00
Fabian Ising cc90b515c6 [MAC] Add DNS reset 2026-08-28 10:58:53 +02:00
Fabian Ising 13ffdfdb04 [NVIM] Make update checker portable 2026-08-24 09:44:08 +02:00
Fabian Ising 67885075d3 [NVIM] Keep update notices visible 2026-08-24 09:41:11 +02:00
Fabian Ising 9ffe78362e [NVIM] Defer plugin update checks 2026-08-24 09:41:11 +02:00
Fabian Ising f5389de7b8 Fix delzip 2026-08-24 09:34:52 +02:00
Fabian Ising 9e059859c6 [TOOLS] Move color test to tools repo 2026-08-12 17:49:16 +02:00
Fabian Ising 6f9e4910bb [ZSH] Add custom completions 2026-08-11 15:01:20 +02:00
Fabian Ising 104b8e2d8d [ZSH] Venv for agents 2026-08-03 11:02:40 +02:00
Fabian Ising 5cca1956fc [ALC] Fix ctrl-slash in alacritty 2026-08-03 11:02:26 +02:00
Fabian Ising 4c5f988d92 [ZSH] Fix path ordering 2026-08-03 11:02:02 +02:00
Fabian Ising 7afa58f3ec [ZSH] Remove claude venv 2026-07-30 14:43:26 +02:00
Fabian Ising 7d97559b20 [ZSH] Add env file 2026-07-30 14:41:28 +02:00
Fabian Ising 3e7a8d4f10 [ZSH] Macos completions 2026-07-28 10:23:36 +02:00
Fabian Ising cb5c767ca2 [ZSH] Note update has happened 2026-07-27 11:09:40 +02:00
Fabian Ising 81deaa75a4 [ZSH] Extract shared startup modules
Keep the server entrypoint focused on server policy while moving update checks, Antidote loading, interactive defaults, and prompt sourcing into reusable modules.
2026-07-27 10:57:11 +02:00
Fabian Ising 7ccb268cfb [ZSH] Path fixes 2026-07-27 10:39:34 +02:00
17 changed files with 835 additions and 318 deletions
+5
View File
@@ -61,3 +61,8 @@ y = 0
key = "Return" key = "Return"
mods = "Shift" mods = "Shift"
chars = "\u001B\r" chars = "\u001B\r"
[[keyboard.bindings]]
key = "Slash"
mods = "Control"
chars = "\u001F"
+3 -3
View File
@@ -8,7 +8,7 @@ This is a personal dotfiles repository targeting macOS (Apple Silicon + Intel) a
Setup is platform-specific and runs from the repo root: Setup is platform-specific and runs from the repo root:
- **macOS:** `./setup_mac.sh` — brews dependencies, creates a `neovim` virtualenv, then calls `./clone_and_link_mac.sh`. Uses GNU coreutils' `gln` (not `ln`), so coreutils must be installed first. - **macOS:** `./setup_mac.sh` — brews dependencies, installs the latest verified upstream Alacritty release, creates a `neovim` virtualenv, then calls `./clone_and_link_mac.sh`. Uses GNU coreutils' `gln` (not `ln`), so coreutils must be installed first.
- **Arch:** `./setup_arch.sh` — pacman deps, then `./clone_and_link.sh` + `./copy_fonts_arch.sh`. - **Arch:** `./setup_arch.sh` — pacman deps, then `./clone_and_link.sh` + `./copy_fonts_arch.sh`.
- `clone_and_link*.sh` create the symlinks (`~/.zsh`, `~/.zshrc`, `~/.tmux`, `~/.tmux.conf`, `~/.config/nvim`, `~/.config/alacritty`, virtualenvwrapper hooks). `link_config.sh` is a simpler standalone variant. - `clone_and_link*.sh` create the symlinks (`~/.zsh`, `~/.zshrc`, `~/.tmux`, `~/.tmux.conf`, `~/.config/nvim`, `~/.config/alacritty`, virtualenvwrapper hooks). `link_config.sh` is a simpler standalone variant.
- `general_setup.sh` switches the login shell to zsh and triggers plugin installs. - `general_setup.sh` switches the login shell to zsh and triggers plugin installs.
@@ -33,5 +33,5 @@ Practical rule: **edit the `.example` source, not the generated symlink target.*
## Maintenance ## Maintenance
- `./update_mac.sh` — macOS update runner. Default (no args) runs `--brew-update --cleanup --diagnostics --update-others`; other flags: `--system-update`, `--permission-fix` (strips Caskroom/App quarantine xattrs). Run `./update_mac.sh --help` for the list. `--update-others` also runs `claude --update` and `Codex --update`. - `./update_mac.sh` — macOS update runner. Default (no args) runs `--brew-update --cleanup --diagnostics --update-others`; other flags: `--system-update`, `--permission-fix` (strips Caskroom/App quarantine xattrs). Run `./update_mac.sh --help` for the list. `--update-others` also updates Alacritty from its verified upstream release, then runs `claude --update` and `Codex --update`.
- `brew_autoupdate`sets up scheduled brew auto-updates. - `brew_autoupdate`runs scheduled brew and Alacritty auto-updates.
+1
View File
@@ -6,5 +6,6 @@ export HOMEBREW_LOGS='/Users/ising/Library/Logs/Homebrew'
export SUDO_ASKPASS='/Users/ising/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate_sudo_gui' export SUDO_ASKPASS='/Users/ising/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate_sudo_gui'
defaults export com.apple.dock "/tmp/dock-layout.plist" defaults export com.apple.dock "/tmp/dock-layout.plist"
/bin/date && /opt/homebrew/bin/brew update && /opt/homebrew/bin/brew upgrade --formula -v && /opt/homebrew/bin/brew upgrade --cask -v && /opt/homebrew/bin/brew cleanup && /usr/bin/open -g /opt/homebrew/Library/Taps/homebrew/homebrew-autoupdate/notifier/brew-autoupdate.app /bin/date && /opt/homebrew/bin/brew update && /opt/homebrew/bin/brew upgrade --formula -v && /opt/homebrew/bin/brew upgrade --cask -v && /opt/homebrew/bin/brew cleanup && /usr/bin/open -g /opt/homebrew/Library/Taps/homebrew/homebrew-autoupdate/notifier/brew-autoupdate.app
/Users/ising/dotfiles/install_alacritty_mac.sh
defaults import com.apple.dock "/tmp/dock-layout.plist" defaults import com.apple.dock "/tmp/dock-layout.plist"
killall dock killall dock
+265
View File
@@ -0,0 +1,265 @@
#!/bin/bash
set -euo pipefail
readonly RELEASE_API="https://api.github.com/repos/alacritty/alacritty/releases/latest"
readonly APPLICATIONS_DIR="/Applications"
readonly APP_NAME="Alacritty.app"
temp_dir=""
mount_point=""
cleanup() {
if [[ -n "$mount_point" && -d "$mount_point" ]]; then
hdiutil detach "$mount_point" >/dev/null 2>&1 || true
fi
if [[ -n "$temp_dir" && -d "$temp_dir" ]]; then
rm -rf "$temp_dir"
fi
}
trap cleanup EXIT INT TERM
die() {
echo "Alacritty update failed: $*" >&2
exit 1
}
require_command() {
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}
app_version() {
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" \
"$1/Contents/Info.plist" 2>/dev/null || true
}
version_is_newer() {
local left="${1%%-*}"
local right="${2%%-*}"
local left_major left_minor left_patch
local right_major right_minor right_patch
IFS=. read -r left_major left_minor left_patch <<< "$left"
IFS=. read -r right_major right_minor right_patch <<< "$right"
[[ "$left_major" =~ ^[0-9]+$ && "$left_minor" =~ ^[0-9]+$ && "$left_patch" =~ ^[0-9]+$ ]] || return 1
[[ "$right_major" =~ ^[0-9]+$ && "$right_minor" =~ ^[0-9]+$ && "$right_patch" =~ ^[0-9]+$ ]] || return 1
(( 10#$left_major > 10#$right_major )) && return 0
(( 10#$left_major < 10#$right_major )) && return 1
(( 10#$left_minor > 10#$right_minor )) && return 0
(( 10#$left_minor < 10#$right_minor )) && return 1
(( 10#$left_patch > 10#$right_patch ))
}
run_for_applications() {
if [[ -w "$APPLICATIONS_DIR" ]]; then
"$@"
else
sudo "$@"
fi
}
safe_link() {
local source="$1"
local destination="$2"
if [[ -e "$destination" && ! -L "$destination" ]]; then
die "will not replace non-symlink: $destination"
fi
ln -sfn "$source" "$destination"
}
assert_linkable() {
local destination="$1"
if [[ -e "$destination" && ! -L "$destination" ]]; then
die "will not replace non-symlink: $destination"
fi
}
validate_shell_artifacts() {
local app="$1"
local brew_prefix="$2"
local resources="$app/Contents/Resources"
local source manpage section
for source in \
"$app/Contents/MacOS/alacritty" \
"$resources/completions/alacritty.bash" \
"$resources/completions/alacritty.fish" \
"$resources/completions/_alacritty" \
"$resources/61/alacritty" \
"$resources/61/alacritty-direct"; do
[[ -f "$source" ]] || die "required artifact is missing: ${source#"$app/"}"
done
assert_linkable "$brew_prefix/bin/alacritty"
assert_linkable "$brew_prefix/etc/bash_completion.d/alacritty"
assert_linkable "$brew_prefix/share/fish/vendor_completions.d/alacritty.fish"
assert_linkable "$brew_prefix/share/zsh/site-functions/_alacritty"
assert_linkable "$HOME/.terminfo/61/alacritty"
assert_linkable "$HOME/.terminfo/61/alacritty-direct"
for section in 1 5 7; do
for manpage in "$resources"/*."$section".gz; do
[[ -e "$manpage" ]] || continue
assert_linkable "$brew_prefix/share/man/man$section/${manpage##*/}"
done
done
}
install_shell_artifacts() {
local app="$APPLICATIONS_DIR/$APP_NAME"
local resources="$app/Contents/Resources"
local brew_prefix="$1"
local manpage section
mkdir -p \
"$brew_prefix/bin" \
"$brew_prefix/etc/bash_completion.d" \
"$brew_prefix/share/fish/vendor_completions.d" \
"$brew_prefix/share/zsh/site-functions" \
"$brew_prefix/share/man/man1" \
"$brew_prefix/share/man/man5" \
"$brew_prefix/share/man/man7" \
"$HOME/.terminfo/61"
safe_link "$app/Contents/MacOS/alacritty" "$brew_prefix/bin/alacritty"
safe_link "$resources/completions/alacritty.bash" "$brew_prefix/etc/bash_completion.d/alacritty"
safe_link "$resources/completions/alacritty.fish" "$brew_prefix/share/fish/vendor_completions.d/alacritty.fish"
safe_link "$resources/completions/_alacritty" "$brew_prefix/share/zsh/site-functions/_alacritty"
safe_link "$resources/61/alacritty" "$HOME/.terminfo/61/alacritty"
safe_link "$resources/61/alacritty-direct" "$HOME/.terminfo/61/alacritty-direct"
for section in 1 5 7; do
for manpage in "$resources"/*."$section".gz; do
[[ -e "$manpage" ]] || continue
safe_link "$manpage" "$brew_prefix/share/man/man$section/${manpage##*/}"
done
done
}
restore_previous_app() {
local destination="$1"
local backup="$2"
run_for_applications rm -rf "$destination"
if [[ -d "$backup" ]]; then
run_for_applications /usr/bin/ditto "$backup" "$destination"
fi
}
main() {
local destination="$APPLICATIONS_DIR/$APP_NAME"
local release_json latest_tag latest_version asset_name asset_url asset_digest
local expected_sha actual_sha dmg staged_app staged_version installed_version
local previous_app brew_prefix brew_managed=false
[[ "$(uname -s)" == "Darwin" ]] || die "this installer only supports macOS"
require_command brew
require_command curl
require_command jq
require_command hdiutil
require_command codesign
brew_prefix="$(brew --prefix)"
if brew list --cask alacritty >/dev/null 2>&1; then
brew_managed=true
fi
temp_dir="$(mktemp -d "${TMPDIR:-/tmp}/alacritty-update.XXXXXX")"
release_json="$temp_dir/release.json"
curl --fail --location --silent --show-error --retry 3 \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$RELEASE_API" -o "$release_json"
latest_tag="$(jq -er '.tag_name' "$release_json")"
latest_version="${latest_tag#v}"
asset_name="Alacritty-${latest_tag}.dmg"
asset_url="$(jq -er --arg asset "$asset_name" \
'.assets[] | select(.name == $asset) | .browser_download_url' "$release_json")"
asset_digest="$(jq -er --arg asset "$asset_name" \
'.assets[] | select(.name == $asset) | .digest' "$release_json")"
[[ "$asset_digest" == sha256:* ]] || die "GitHub did not provide a SHA-256 digest for $asset_name"
expected_sha="$(printf '%s' "${asset_digest#sha256:}" | tr '[:upper:]' '[:lower:]')"
[[ "$expected_sha" =~ ^[[:xdigit:]]{64}$ ]] || die "invalid SHA-256 digest for $asset_name"
installed_version=""
if [[ -d "$destination" ]]; then
installed_version="$(app_version "$destination")"
fi
if [[ "$installed_version" == "$latest_version" && "$brew_managed" == false ]]; then
validate_shell_artifacts "$destination" "$brew_prefix"
install_shell_artifacts "$brew_prefix"
echo "Alacritty $installed_version is already up to date."
return
fi
if [[ -n "$installed_version" ]] && version_is_newer "$installed_version" "$latest_version"; then
echo "Installed Alacritty $installed_version is newer than stable $latest_version; leaving it unchanged."
return
fi
echo "Installing Alacritty $latest_version from the upstream release."
dmg="$temp_dir/$asset_name"
curl --fail --location --silent --show-error --retry 3 "$asset_url" -o "$dmg"
actual_sha="$(shasum -a 256 "$dmg" | awk '{print $1}')"
[[ "$actual_sha" == "$expected_sha" ]] || die "SHA-256 mismatch for $asset_name"
mount_point="$temp_dir/mount"
mkdir -p "$mount_point"
hdiutil attach "$dmg" -nobrowse -readonly -mountpoint "$mount_point" >/dev/null
[[ -d "$mount_point/$APP_NAME" ]] || die "$APP_NAME is missing from $asset_name"
staged_app="$temp_dir/$APP_NAME"
/usr/bin/ditto "$mount_point/$APP_NAME" "$staged_app"
hdiutil detach "$mount_point" >/dev/null
mount_point=""
staged_version="$(app_version "$staged_app")"
[[ "$staged_version" == "$latest_version" ]] || die "release metadata says $latest_version, app says $staged_version"
codesign --verify --deep --strict "$staged_app" || die "the staged app has an invalid ad-hoc signature"
xattr -dr com.apple.quarantine "$staged_app" 2>/dev/null || true
validate_shell_artifacts "$staged_app" "$brew_prefix"
previous_app="$temp_dir/previous/$APP_NAME"
if [[ -d "$destination" ]]; then
mkdir -p "${previous_app%/*}"
/usr/bin/ditto "$destination" "$previous_app"
fi
if [[ "$brew_managed" == true ]]; then
echo "Migrating Alacritty away from the disabled Homebrew cask."
if ! brew uninstall --cask alacritty; then
restore_previous_app "$destination" "$previous_app"
install_shell_artifacts "$brew_prefix" || true
die "Homebrew could not uninstall its Alacritty cask"
fi
fi
if ! run_for_applications rm -rf "$destination" || \
! run_for_applications /usr/bin/ditto "$staged_app" "$destination"; then
restore_previous_app "$destination" "$previous_app"
die "could not install $destination"
fi
if ! codesign --verify --deep --strict "$destination" || \
[[ "$(app_version "$destination")" != "$latest_version" ]]; then
restore_previous_app "$destination" "$previous_app"
die "installed app validation failed; restored the previous app"
fi
install_shell_artifacts "$brew_prefix"
echo "Alacritty $latest_version installed successfully."
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi
+130 -3
View File
@@ -32,9 +32,6 @@ let g:loaded_perl_provider = 0
call plug#begin() call plug#begin()
if !exists('g:vscode') if !exists('g:vscode')
" Warn about plugin updates
Plug 'semanser/vim-outdated-plugins'
" Powerline replacement " Powerline replacement
Plug 'vim-airline/vim-airline' Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes' Plug 'vim-airline/vim-airline-themes'
@@ -112,6 +109,136 @@ endif
call plug#end() call plug#end()
" Check for plugin updates after the editor is responsive.
lua << EOF
local function check_plugin_updates()
local plugins = {}
for name, plugin in pairs(vim.g.plugs or {}) do
if vim.fn.isdirectory(plugin.dir) == 1 then
table.insert(plugins, { name = name, dir = plugin.dir })
end
end
table.sort(plugins, function(a, b) return a.name < b.name end)
local next_plugin = 1
local active = 0
local finished = 0
local failures = 0
local updates = {}
local concurrency = 2
local function git_command(args)
local command = {}
if vim.fn.executable('ionice') == 1 then
vim.list_extend(command, { 'ionice', '-c', '3' })
end
if vim.fn.executable('nice') == 1 then
vim.list_extend(command, { 'nice', '-n', '10' })
end
table.insert(command, 'git')
vim.list_extend(command, args)
return command
end
local function set_airline_status(message)
vim.g.plugin_update_status = message
local marker = '%{get(g:,"plugin_update_status","")}'
local warning = vim.g.airline_section_warning or ''
if not string.find(warning, 'plugin_update_status', 1, true) then
vim.g.airline_section_warning = warning .. ' ' .. marker
end
if vim.fn.exists(':AirlineRefresh') == 2 then
vim.cmd('AirlineRefresh')
end
end
local function report()
if #updates > 0 then
set_airline_status(string.format('[updates: %d]', #updates))
local message = string.format(
'%d plugin update%s available: %s',
#updates,
#updates == 1 and '' or 's',
table.concat(updates, ', ')
)
if failures > 0 then
message = message .. string.format(' (%d check%s failed)', failures, failures == 1 and '' or 's')
end
vim.notify(message, failures > 0 and vim.log.levels.WARN or vim.log.levels.INFO)
elseif failures > 0 then
set_airline_status('[update check failed]')
vim.notify(
string.format('Plugin update check incomplete: %d check%s failed', failures, failures == 1 and '' or 's'),
vim.log.levels.WARN
)
else
set_airline_status('')
vim.notify('All plugins up-to-date', vim.log.levels.INFO)
end
end
local pump
local function complete_one()
active = active - 1
finished = finished + 1
if finished == #plugins then
report()
else
pump()
end
end
local function check_one(plugin)
active = active + 1
vim.system(
git_command({ '-C', plugin.dir, 'fetch', '--quiet', '--no-tags', 'origin' }),
{ text = true, timeout = 20000 },
vim.schedule_wrap(function(fetch_result)
if fetch_result.code ~= 0 then
failures = failures + 1
complete_one()
return
end
vim.system(
git_command({ '-C', plugin.dir, 'rev-list', '--count', 'HEAD..@{upstream}' }),
{ text = true, timeout = 5000 },
vim.schedule_wrap(function(count_result)
local count = tonumber(vim.trim(count_result.stdout or ''))
if count_result.code ~= 0 or count == nil then
failures = failures + 1
elseif count > 0 then
table.insert(updates, plugin.name)
end
complete_one()
end)
)
end)
)
end
pump = function()
while active < concurrency and next_plugin <= #plugins do
local plugin = plugins[next_plugin]
next_plugin = next_plugin + 1
check_one(plugin)
end
end
if #plugins > 0 then
pump()
end
end
vim.api.nvim_create_autocmd('VimEnter', {
group = vim.api.nvim_create_augroup('plugin_update_check', { clear = true }),
once = true,
callback = function()
vim.defer_fn(check_plugin_updates, 1500)
end,
})
EOF
" Run PlugInstall if there are missing plugins " Run PlugInstall if there are missing plugins
autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)')) autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)'))
\| PlugInstall --sync | source $MYVIMRC \| PlugInstall --sync | source $MYVIMRC
+6 -4
View File
@@ -6,14 +6,16 @@
# Distributed under terms of the MIT license. # Distributed under terms of the MIT license.
# #
brew install neovim tmux alacritty coreutils cmake golang npm virtualenvwrapper brew install neovim tmux coreutils cmake golang jq npm virtualenvwrapper
source /opt/homebrew/bin/virtualenvwrapper.sh ./install_alacritty_mac.sh
BREW_PREFIX="$(brew --prefix)"
source "$BREW_PREFIX/bin/virtualenvwrapper.sh"
mkvirtualenv neovim mkvirtualenv neovim
pip3 install pynvim pip3 install pynvim
npm install -g neovim npm install -g neovim
echo For alacritty to work seemlessly with tmux, we will have to add '/usr/local/bin'\ echo For alacritty to work seamlessly with tmux, we will add the Homebrew bin directory\
to the path. This will require your root password. to the path. This will require your root password.
sudo launchctl config user path /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin sudo launchctl config user path /usr/bin:/bin:/usr/sbin:/sbin:"$BREW_PREFIX/bin":/usr/local/bin
cp $PWD/nvim/scheme.vim.template $PWD/nvim/scheme.vim cp $PWD/nvim/scheme.vim.template $PWD/nvim/scheme.vim
cp $PWD/.config/alacritty/schemes.yml.template $PWD/.config/alacritty/schemes.yml cp $PWD/.config/alacritty/schemes.yml.template $PWD/.config/alacritty/schemes.yml
./clone_and_link_mac.sh ./clone_and_link_mac.sh
-99
View File
@@ -1,99 +0,0 @@
#!/bin/bash
#
# This file echoes a bunch of 24-bit color codes
# to the terminal to demonstrate its functionality.
# The foreground escape sequence is ^[38;2;<r>;<g>;<b>m
# The background escape sequence is ^[48;2;<r>;<g>;<b>m
# <r> <g> <b> range from 0 to 255 inclusive.
# The escape sequence ^[0m returns output to default
setBackgroundColor()
{
echo -en "\x1b[48;2;$1;$2;$3""m"
}
resetOutput()
{
echo -en "\x1b[0m\n"
}
# Gives a color $1/255 % along HSV
# Who knows what happens when $1 is outside 0-255
# Echoes "$red $green $blue" where
# $red $green and $blue are integers
# ranging between 0 and 255 inclusive
rainbowColor()
{
let h=$1/43
let f=$1-43*$h
let t=$f*255/43
let q=255-t
if [ $h -eq 0 ]
then
echo "255 $t 0"
elif [ $h -eq 1 ]
then
echo "$q 255 0"
elif [ $h -eq 2 ]
then
echo "0 255 $t"
elif [ $h -eq 3 ]
then
echo "0 $q 255"
elif [ $h -eq 4 ]
then
echo "$t 0 255"
elif [ $h -eq 5 ]
then
echo "255 0 $q"
else
# execution should never reach here
echo "0 0 0"
fi
}
for i in `seq 0 127`; do
setBackgroundColor $i 0 0
echo -en " "
done
resetOutput
for i in `seq 255 128`; do
setBackgroundColor $i 0 0
echo -en " "
done
resetOutput
for i in `seq 0 127`; do
setBackgroundColor 0 $i 0
echo -n " "
done
resetOutput
for i in `seq 255 128`; do
setBackgroundColor 0 $i 0
echo -n " "
done
resetOutput
for i in `seq 0 127`; do
setBackgroundColor 0 0 $i
echo -n " "
done
resetOutput
for i in `seq 255 128`; do
setBackgroundColor 0 0 $i
echo -n " "
done
resetOutput
for i in `seq 0 127`; do
setBackgroundColor `rainbowColor $i`
echo -n " "
done
resetOutput
for i in `seq 255 128`; do
setBackgroundColor `rainbowColor $i`
echo -n " "
done
resetOutput
+11
View File
@@ -1,5 +1,13 @@
#!/bin/bash #!/bin/bash
SCRIPT_SOURCE="${BASH_SOURCE[0]}"
while [[ -L "$SCRIPT_SOURCE" ]]; do
SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_SOURCE")" >/dev/null 2>&1 && pwd)"
SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")"
[[ "$SCRIPT_SOURCE" != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE"
done
SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_SOURCE")" >/dev/null 2>&1 && pwd)"
ARCH=`uname -m` ARCH=`uname -m`
if [[ $ARCH == "arm64" ]]; then if [[ $ARCH == "arm64" ]]; then
PREFIX="/opt/homebrew/" PREFIX="/opt/homebrew/"
@@ -55,6 +63,9 @@ function update_others {
echo "Update other stuff" echo "Update other stuff"
echo "----------" echo "----------"
echo "-- ALACRITTY"
"$SCRIPT_DIR/install_alacritty_mac.sh" || echo "Alacritty update failed." >&2
if [ -x "$(command -v claude)" ]; then if [ -x "$(command -v claude)" ]; then
echo "-- CLAUDE" echo "-- CLAUDE"
claude --update claude --update
+1
View File
@@ -3,3 +3,4 @@
.p10k.zsh .p10k.zsh
.p10k.mac.zsh .p10k.mac.zsh
antigen/ antigen/
.env.local
+13 -7
View File
@@ -8,11 +8,16 @@ defaults write .GlobalPreferences AppleLocale en_DE
ARCH=`uname -m` ARCH=`uname -m`
if [[ $ARCH == "arm64" ]]; then if [[ $ARCH == "arm64" ]]; then
HOMEBREWPREFIX="/opt/homebrew" export HOMEBREW_PREFIX="/opt/homebrew"
else else
HOMEBREWPREFIX="/usr/local" export HOMEBREW_PREFIX="/usr/local"
fi fi
export PATH="$HOMEBREWPREFIX/opt/coreutils/libexec/gnubin:$HOME/.local/bin:$HOMEBREWPREFIX/bin:$PATH:$HOMEBREWPREFIX/sbin" export PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$HOME/.local/bin:$HOMEBREW_PREFIX/bin:$PATH:$HOMEBREW_PREFIX/sbin"
# macOS-specific completion functions.
fpath=("${ZDOTDIR:-$HOME}/.zsh/completions_mac" $fpath)
autoload -Uz _launchctl
compdef _launchctl launchctl
# test -e "${HOME}/.iterm2_shell_integration.zsh" && source "${HOME}/.iterm2_shell_integration.zsh" # test -e "${HOME}/.iterm2_shell_integration.zsh" && source "${HOME}/.iterm2_shell_integration.zsh"
@@ -27,12 +32,13 @@ if [ -f "$HOMEBREW_COMMAND_NOT_FOUND_HANDLER" ]; then
source "$HOMEBREW_COMMAND_NOT_FOUND_HANDLER"; source "$HOMEBREW_COMMAND_NOT_FOUND_HANDLER";
fi fi
export DYLD_FALLBACK_LIBRARY_PATH="$HOMEBREW_PREFIX/lib:${DYLD_FALLBACK_LIBRARY_PATH}" export DYLD_FALLBACK_LIBRARY_PATH="$HOMEBREW_PREFIX/lib${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}"
export GI_TYPELIB_PATH="$HOMEBREW_PREFIX/lib/girepository-1.0:${GI_TYPELIB_PATH}" export GI_TYPELIB_PATH="$HOMEBREW_PREFIX/lib/girepository-1.0${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}"
export PKG_CONFIG_PATH="$HOMEBREW_PREFIX/lib/pkgconfig:$HOMEBREW_PREFIX/share/pkgconfig:${PKG_CONFIG_PATH}" export PKG_CONFIG_PATH="$HOMEBREW_PREFIX/lib/pkgconfig:$HOMEBREW_PREFIX/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
alias claude="workon claude; claude; deactivate"
alias python="python3" alias python="python3"
# Tahoe 26 WindowServer bug: windows stay interactive but can't be moved by # Tahoe 26 WindowServer bug: windows stay interactive but can't be moved by
# mouse or API until the window-management daemon is restarted (no logout). # mouse or API until the window-management daemon is restarted (no logout).
alias unstick-windows="killall WindowManager" alias unstick-windows="killall WindowManager"
alias resetdns="sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder"
+46
View File
@@ -27,3 +27,49 @@ _venv_track_precmd() {
} }
autoload -Uz add-zsh-hook autoload -Uz add-zsh-hook
add-zsh-hook precmd _venv_track_precmd add-zsh-hook precmd _venv_track_precmd
# Which venv an agent shell should use: nearest .venv/venv walking up from $1
# (default $PWD, stopping above $HOME), else the shared fallback env.
_agent_venv_path() {
local d=${1:-$PWD} c
while [[ $d == */* && $d != $HOME ]]; do
for c in "$d/.venv" "$d/venv"; do
[[ -f $c/bin/activate ]] && { print -r -- "$c"; return 0 }
done
d=${d%/*}
done
[[ -f $HOME/python-envs/claude/bin/activate ]] && print -r -- "$HOME/python-envs/claude"
}
# Agent shells source this file once per session, rooted at the project dir, and
# snapshot the result: CLAUDECODE marks Claude Code, CODEX_SHELL the ChatGPT/Codex
# desktop app. Only VIRTUAL_ENV is set here; the block at the end of .zshrc turns it
# into an activation once PATH is fully built. Terminal launches use the wrappers below.
if [[ -n ${CLAUDECODE:-} || -n ${CODEX_SHELL:-} ]] && [[ -z ${VIRTUAL_ENV:-} ]]; then
_venv=$(_agent_venv_path)
[[ -n $_venv ]] && export VIRTUAL_ENV=$_venv
unset _venv
fi
# Claude Code takes PATH from its own process env: its shell snapshot writes
# `export PATH=` last, discarding whatever .zshrc built. So the venv has to be
# active *before* launch. The subshell keeps this shell's PATH untouched.
claude() {
if [[ -n ${VIRTUAL_ENV:-} ]]; then
command claude "$@"
return
fi
local v=$(_agent_venv_path)
( [[ -n $v ]] && source "$v/bin/activate"; exec command claude "$@" )
}
# Codex captures the profile's own PATH, so hand it VIRTUAL_ENV only and let the
# activation block at the end of .zshrc order PATH correctly. Pre-activating here
# would leave venv/bin behind gnubin and homebrew, same as tmux-inherited venvs.
codex() {
if [[ -n ${VIRTUAL_ENV:-} ]]; then
command codex "$@"
else
VIRTUAL_ENV=$(_agent_venv_path) command codex "$@"
fi
}
+35 -202
View File
@@ -1,212 +1,59 @@
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. # Powerlevel10k instant prompt and machine-local layers must remain first.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
[[ -f ~/.zsh/.powerline_config ]] && source ~/.zsh/.powerline_config [[ -f ~/.zsh/.powerline_config ]] && source ~/.zsh/.powerline_config
[[ -f ~/.zsh/.user_config ]] && source ~/.zsh/.user_config [[ -f ~/.zsh/.user_config ]] && source ~/.zsh/.user_config
[[ -f ~/.zsh/.virtual_env_config.zsh ]] && source ~/.zsh/.virtual_env_config.zsh [[ -f ~/.zsh/.virtual_env_config.zsh ]] && source ~/.zsh/.virtual_env_config.zsh
[[ -f ~/.zsh/.local_config ]] && source ~/.zsh/.local_config [[ -f ~/.zsh/.local_config ]] && source ~/.zsh/.local_config
[[ -f ~/.zsh/.env.local ]] && source ~/.zsh/.env.local
# On shell start, bring ~/dotfiles up to date by fast-forwarding the CURRENT source "${ZDOTDIR:-$HOME}/.zsh/lib/updates.zsh"
# branch to its upstream only. Never rebase or stash in the background: if the
# branch diverged or local edits block it, warn and leave it for a manual pull.
# Per-branch, so `server` on each device fast-forwards to origin/server.
update_dotfiles() {
local repo="$HOME/dotfiles"
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/dotfiles-update-notice"
local notice_tmp="$notice_file.$$.tmp"
# Bound the network call so a stalled fetch can't wedge this backgrounded
# (&!) job into a days-long orphan holding unreaped children. `timeout` is
# absent on macOS, so only use it when present. Disable ssh multiplexing so
# no daemonized mux master can inherit and keep our fds open past git's exit.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 15)
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO git -C "$repo" fetch --quiet </dev/null 2>/dev/null || return
git -C "$repo" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 || return # no upstream
git -C "$repo" merge-base --is-ancestor '@{u}' HEAD 2>/dev/null && return # already current
git -C "$repo" merge --ff-only --quiet '@{u}' 2>/dev/null && return
local notice="⚠ dotfiles: $(git -C "$repo" symbolic-ref --short HEAD) can't fast-forward to @{u} — run: git -C ~/dotfiles pull"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
}
update_dotfiles &!
# On shell start, check the security tools repo for upstream updates and queue a
# one-line notice if the checkout is behind. A precmd hook prints it between
# commands, rather than letting this background job write across other startup
# output or a prompt that is already being edited. Notify only (never auto-pull).
# The check stays read-only so it works even in root-owned /opt/tools:
# - `safe.directory=*` sidesteps git's "dubious ownership" refusal on a repo
# owned by root, which would otherwise fail every git command.
# - `ls-remote` queries the remote without writing into .git (a normal user
# can't write a root-owned .git, so `fetch` would fail there).
# Uses the first candidate that is a git checkout; covers global and per-user layouts.
check_tools_repo() {
local repo branch remote mergeref remote_sha local_sha cmd notice
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/tools-update-notice"
local notice_tmp="$notice_file.$$.tmp"
# See update_dotfiles: bound the network probe (timeout, absent on macOS) and
# disable ssh multiplexing so a stalled ls-remote can't orphan this &! job.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 10)
for repo in /opt/tools "$HOME/tools" "$HOME/tools/tools-repo"; do
local -a g=(git -c 'safe.directory=*' -C "$repo")
$g rev-parse --is-inside-work-tree >/dev/null 2>&1 || continue
branch=$($g symbolic-ref --short HEAD 2>/dev/null) || return
remote=$($g config "branch.$branch.remote" 2>/dev/null) || return # no upstream
mergeref=$($g config "branch.$branch.merge" 2>/dev/null) || return
remote_sha=$(GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO $g ls-remote "$remote" "$mergeref" </dev/null 2>/dev/null | awk '{print $1}')
local_sha=$($g rev-parse HEAD 2>/dev/null)
[[ -n "$remote_sha" && -n "$local_sha" ]] || return # remote unreachable
[[ "$remote_sha" == "$local_sha" ]] && return # already current
$g merge-base --is-ancestor "$remote_sha" HEAD 2>/dev/null && return # remote is an ancestor: ahead/equal (also true if we lack the object -> falls through to "behind")
[[ -w "$repo/.git" ]] && cmd="git -C $repo pull" || cmd="sudo git -C $repo pull"
notice="⬆ tools: updates available in $repo — run: $cmd"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
return
done
}
check_tools_repo &!
show_update_notices() {
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file claimed_notice
for notice_file in "$cache_dir"/{dotfiles,tools}-update-notice; do
[[ -s "$notice_file" ]] || continue
claimed_notice="$notice_file.$$.show"
mv "$notice_file" "$claimed_notice" 2>/dev/null || continue
command cat "$claimed_notice"
command rm -f "$claimed_notice"
done
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd show_update_notices
export LC_OSC52=1 export LC_OSC52=1
# Load Antidote # Completions for tools installed under ~/.local (e.g. tools-repo's safe*
mkdir -p ${ZDOTDIR:-~}/.cache/zsh # wrappers). Must precede antidote, whose completion plugin runs compinit.
static_file=${ZDOTDIR:-~}/.cache/zsh/.zsh_plugins.zsh fpath=("$HOME/.local/share/zsh/site-functions" $fpath)
plugins_txt=${ZDOTDIR:-~}/.zsh/.zsh_plugins.txt
# clone antidote if necessary
if ! [[ -e ${ZDOTDIR:-~}/.antidote ]]; then
git clone https://github.com/mattmc3/antidote.git ${ZDOTDIR:-~}/.antidote
fi
zstyle ':completion:*:ssh:*' hosts off plugins_txt=${ZDOTDIR:-$HOME}/.zsh/.zsh_plugins.txt
zstyle ':completion:*:scp:*' hosts off static_file=${ZDOTDIR:-$HOME}/.cache/zsh/.zsh_plugins.zsh
source "${ZDOTDIR:-$HOME}/.zsh/lib/antidote.zsh"
source "${ZDOTDIR:-$HOME}/.zsh/lib/interactive.zsh"
# Run rehash for external commands bindkey '^w' kill-region
zstyle ":completion:*:commands" rehash 1
# source antidote and load plugins from `${ZDOTDIR:-~}/.zsh_plugins.txt`
source ${ZDOTDIR:-~}/.antidote/antidote.zsh
antidote load ${plugins_txt} ${static_file}
setopt interactivecomments
# History options
HISTSIZE=100000 # Set the amount of lines you want saved
SAVEHIST=100000 # This is required to actually save them, needs to match with HISTSIZE
setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format.
setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits.
setopt SHARE_HISTORY # Share history between all sessions.
setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history.
setopt HIST_IGNORE_DUPS # Don\'t record an entry that was just recorded again.
setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate.
setopt HIST_FIND_NO_DUPS # Do not display a line previously found.
setopt HIST_IGNORE_SPACE # Don\'t record an entry starting with a space.
setopt HIST_SAVE_NO_DUPS # Don\'t write duplicate entries in the history file.
setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry.
# Clear screen by ctrl+q
bindkey '^q' clear-screen
# Bind Ctrl-W to kill-region
bindkey "^w" kill-region
bindkey -v bindkey -v
VI_MODE_SET_CURSOR=true VI_MODE_SET_CURSOR=true
(( $+commands[nvim] )) && alias vim=nvim
alias sudo='sudo '
(( $+commands[nvim] )) && export EDITOR='nvim'
(( $+commands[nvim] )) && export VISUAL='nvim'
alias ls="ls --color=always"
alias cgrep="grep --color=always"
alias cdiff="git diff --color-words --no-index"
delzip() {
unzip -Z -1 "$@" | xargs -I{} rm -rf {}
}
# mitmproxy
export MITMPROXY_SSLKEYLOGFILE="~/.mitmproxy/sslkeylogfile.txt"
[[ -f ~/.zsh/.mac_config.zsh ]] && source ~/.zsh/.mac_config.zsh [[ -f ~/.zsh/.mac_config.zsh ]] && source ~/.zsh/.mac_config.zsh
if `which go &>/dev/null`; then if (( $+commands[go] )); then
export PATH="$PATH:$(go env GOPATH)/bin" export GOPATH="$(go env GOPATH 2>/dev/null)"
export GOPATH=$(go env GOPATH) [[ -n "$GOPATH" ]] && path+=("$GOPATH/bin")
fi fi
# Use safecp/safemv from the tools-repo when available (after ~/.local/bin is on PATH) # Use safe wrappers after ~/.local/bin has been added to PATH.
(( $+commands[safecp] )) && alias cp=safecp (( $+commands[safecp] )) && alias cp=safecp
(( $+commands[safemv] )) && alias mv=safemv (( $+commands[safemv] )) && alias mv=safemv
(( $+commands[safescp] )) && alias scp=safescp (( $+commands[safescp] )) && alias scp=safescp
if [[ -f ~/.ssh/sudo_key ]]; then if [[ -f ~/.ssh/sudo_key ]]; then
[[ -e /tmp/sudo-agent.sock ]] || ssh-agent -a /tmp/sudo-agent.sock &> /dev/null [[ -e /tmp/sudo-agent.sock ]] || ssh-agent -a /tmp/sudo-agent.sock &>/dev/null
SSH_AUTH_SOCK=/tmp/sudo-agent.sock ssh-add -l | grep -q `ssh-keygen -lf ~/.ssh/sudo_key | awk '{print $2}'` || SSH_AUTH_SOCK=/tmp/sudo-agent.sock ssh-add ~/.ssh/sudo_key SSH_AUTH_SOCK=/tmp/sudo-agent.sock ssh-add -l |
grep -q "$(ssh-keygen -lf ~/.ssh/sudo_key | awk '{print $2}')" ||
SSH_AUTH_SOCK=/tmp/sudo-agent.sock ssh-add ~/.ssh/sudo_key
fi fi
# Sudo workaround for neovim
export SUDO_ASKPASS=$(command -v ssh-askpass) export SUDO_ASKPASS=$(command -v ssh-askpass)
# Workaround for async issues https://github.com/romkatv/powerlevel10k/issues/1554 if [[ -f ~/dotfiles/zsh/.p10k.mac.zsh ]]; then
unset ZSH_AUTOSUGGEST_USE_ASYNC p10k_config=~/dotfiles/zsh/.p10k.mac.zsh
else
# Fixate language p10k_config=~/dotfiles/zsh/.p10k.zsh
export LANG="en_US.UTF-8" fi
export LC_CTYPE="en_US.UTF-8" source "${ZDOTDIR:-$HOME}/.zsh/lib/prompt.zsh"
export TIME_STYLE="long-iso"
# Powerlevel 10k
# Remove padding on right side
ZLE_RPROMPT_INDENT=0
# To customize prompt, run `p10k configure` or edit ~/dotfiles/zsh/.p10k.zsh.
function load_p10k() {
if test -f ~/dotfiles/zsh/.p10k.mac.zsh; then
source ~/dotfiles/zsh/.p10k.mac.zsh
else
[[ ! -f ~/dotfiles/zsh/.p10k.zsh ]] || source ~/dotfiles/zsh/.p10k.zsh
fi
}
load_p10k
alias screenshot_mode='powerlevel10k_plugin_unload; export PS1="$ "; python3 ~/.config/alacritty/screenshot_mode.py on' alias screenshot_mode='powerlevel10k_plugin_unload; export PS1="$ "; python3 ~/.config/alacritty/screenshot_mode.py on'
alias screenshot_mode_off='prompt_powerlevel9k_setup; python3 ~/.config/alacritty/screenshot_mode.py off' alias screenshot_mode_off='prompt_powerlevel9k_setup; python3 ~/.config/alacritty/screenshot_mode.py off'
antidote update &> /dev/null &| antidote update &>/dev/null &|
# This speeds up pasting w/ autosuggest [[ -d "$HOME/.lmstudio/bin" ]] && path+=("$HOME/.lmstudio/bin")
# https://github.com/zsh-users/zsh-autosuggestions/issues/238 [[ -d "$HOME/.runai/bin" ]] && path+=("$HOME/.runai/bin")
pasteinit() { runai_bin="$HOME/.runai/bin/runai"
OLD_SELF_INSERT=${${(s.:.)widgets[self-insert]}[2,3]}
zle -N self-insert url-quote-magic # I wonder if you'd need `.url-quote-magic`?
}
pastefinish() {
zle -N self-insert $OLD_SELF_INSERT
}
zstyle :bracketed-paste-magic paste-init pasteinit
zstyle :bracketed-paste-magic paste-finish pastefinish
# https://github.com/zsh-users/zsh-autosuggestions/issues/351
ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)
export PATH=$PATH:/Users/ising/.runai/bin
runai_bin=/Users/ising/.runai/bin/runai
runai_completion=${ZDOTDIR:-$HOME}/.cache/zsh/runai-completion.zsh runai_completion=${ZDOTDIR:-$HOME}/.cache/zsh/runai-completion.zsh
if [[ -x "$runai_bin" ]]; then if [[ -x "$runai_bin" ]]; then
if [[ ! -s "$runai_completion" || "$runai_bin" -nt "$runai_completion" ]]; then if [[ ! -s "$runai_completion" || "$runai_bin" -nt "$runai_completion" ]]; then
@@ -220,25 +67,11 @@ if [[ -x "$runai_bin" ]]; then
[[ -s "$runai_completion" ]] && source "$runai_completion" [[ -s "$runai_completion" ]] && source "$runai_completion"
fi fi
# Re-activate an inherited virtualenv — MUST be the last PATH-affecting line. # Keep this last: activate must snapshot the fully built PATH for deactivate.
# typeset -U path PATH
# When VIRTUAL_ENV is present in the environment (a tmux pane split carries it [[ -z "${VIRTUAL_ENV:-}" ]] && unset VIRTUAL_ENV
# in via `-e VIRTUAL_ENV=...`, see .tmux.conf) we source the venv's activate if [[ -n "$VIRTUAL_ENV" && -f "$VIRTUAL_ENV/bin/activate" ]]; then
# script here rather than in .virtual_env_config.zsh. activate snapshots the if [[ ${PATH%%:*} != "$VIRTUAL_ENV/bin" ]]; then
# current PATH into _OLD_VIRTUAL_PATH and restores it on `deactivate`; if we
# activated earlier (before .mac_config.zsh, go, runai below all prepend to
# PATH) that snapshot would be incomplete and `deactivate` would strip
# ~/.local/bin & friends — dropping claude and co. off PATH. Keeping this last
# guarantees the snapshot is the fully-built PATH.
#
# ANY new PATH mutation must go ABOVE this block.
#
# A new tmux window clears the var to empty (`bind c ... -e VIRTUAL_ENV=`) to
# force the base environment; drop that empty value so nothing sees a bogus venv.
[ -z "${VIRTUAL_ENV:-}" ] && unset VIRTUAL_ENV
if [ -n "$VIRTUAL_ENV" ] && [ -f "$VIRTUAL_ENV/bin/activate" ]; then
# Guard against double-activation on a manual `source ~/.zshrc`.
if [[ ":$PATH:" != *":$VIRTUAL_ENV/bin:"* ]]; then
source "$VIRTUAL_ENV/bin/activate" source "$VIRTUAL_ENV/bin/activate"
fi fi
fi fi
+127
View File
@@ -0,0 +1,127 @@
#compdef launchctl
# macOS launchd completion.
_launchctl_domains() {
local uid=$EUID
local -a domains=(
'system:system services'
"user/${uid}:services for the current user"
"gui/${uid}:services in the current graphical session"
)
_describe -t domains 'launchd domain' domains
}
_launchctl_service_targets() {
local uid=$EUID label
local -a targets=()
while IFS=$'\t' read -r _ _ label; do
[[ -n $label ]] && targets+=("gui/${uid}/${label}")
done < <(launchctl list 2>/dev/null | tail -n +2)
if (( ${#targets} )); then
_describe -t services 'launchd service target' targets
else
_message 'launchd service target'
fi
}
_launchctl() {
local context state state_descr line
typeset -A opt_args
local -a commands=(
'bootstrap:register services or directories in a domain'
'bootout:unregister services or directories from a domain'
'enable:enable a service'
'disable:disable a service'
'kickstart:run or restart a service'
'kill:send a signal to a service'
'print:show a domain or service'
'print-disabled:show persistent enable and disable overrides'
'list:list loaded services'
'start:start a legacy service by label'
'stop:stop a legacy service by label'
'remove:remove a legacy service by label'
'load:load legacy plist files'
'unload:unload legacy plist files'
'setenv:set an environment variable'
'unsetenv:unset an environment variable'
'getenv:get an environment variable'
'limit:view or change resource limits'
'config:set persistent launchd configuration'
'dumpstate:dump launchd state'
'reboot:reboot a launchd domain or the system'
'version:show launchd version'
'help:show launchctl help'
)
_arguments -C \
'1:command:->command' \
'*::argument:->args'
case $state in
command)
_describe -t commands 'launchctl command' commands
;;
args)
case $words[2] in
bootstrap)
if (( CURRENT == 3 )); then
_launchctl_domains
else
_files -g '*.plist(-.)'
fi
;;
bootout)
if (( CURRENT == 3 )); then
_launchctl_domains
_launchctl_service_targets
else
_files -g '*.plist(-.)'
fi
;;
enable|disable|kickstart)
_launchctl_service_targets
;;
print)
_launchctl_domains
_launchctl_service_targets
;;
print-disabled)
_launchctl_domains
;;
load|unload)
_files -g '*.plist(-.)'
;;
start|stop|remove)
local -a labels
labels=(${${(f)"$(launchctl list 2>/dev/null)"}[2,-1]##*$'\t'})
_describe -t services 'service label' labels
;;
kill)
if (( CURRENT == 3 )); then
_signals
else
_launchctl_service_targets
fi
;;
setenv)
if (( CURRENT == 3 )); then
_parameters
else
_message 'value'
fi
;;
getenv|unsetenv)
_parameters
;;
*)
_message 'argument'
;;
esac
;;
esac
}
_launchctl "$@"
+12
View File
@@ -0,0 +1,12 @@
# Callers set plugins_txt and static_file before sourcing this module.
mkdir -p "${static_file:h}"
if [[ ! -e ${ZDOTDIR:-$HOME}/.antidote ]]; then
git clone https://github.com/mattmc3/antidote.git "${ZDOTDIR:-$HOME}/.antidote"
fi
zstyle ':completion:*:ssh:*' hosts off
zstyle ':completion:*:scp:*' hosts off
zstyle ':completion:*:commands' rehash 1
source "${ZDOTDIR:-$HOME}/.antidote/antidote.zsh"
antidote load "$plugins_txt" "$static_file"
+110
View File
@@ -0,0 +1,110 @@
setopt interactivecomments
HISTSIZE=100000
SAVEHIST=100000
setopt EXTENDED_HISTORY
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
setopt HIST_EXPIRE_DUPS_FIRST
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_FIND_NO_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_SAVE_NO_DUPS
setopt HIST_REDUCE_BLANKS
bindkey '^q' clear-screen
(( $+commands[nvim] )) && alias vim=nvim
(( $+commands[nvim] )) && export EDITOR=nvim VISUAL=nvim
alias sudo='sudo '
alias ls='ls --color=always'
alias cgrep='grep --color=always'
alias cdiff='git diff --color-words --no-index'
# Remove the files an archive extracted into the current directory.
# Lists what it will delete and asks first; -f skips the prompt, -n dry-runs.
# Directories are only removed if they end up empty.
delzip() {
emulate -L zsh
setopt local_options
local dry=0 force=0
while [[ $1 == -[nf] ]]; do
[[ $1 == -n ]] && dry=1 || force=1
shift
done
(( $# )) || { print -ru2 "usage: delzip [-n|-f] <archive.zip>..."; return 2 }
local zip entry parent
local -a entries files dirs
for zip in "$@"; do
[[ -f $zip ]] || { print -ru2 "delzip: no such file: $zip"; return 1 }
entries=("${(f)$(unzip -Z -1 -- $zip)}") || return 1
for entry in $entries; do
[[ -n $entry ]] || continue
# zip-slip guard: never touch anything outside the cwd
if [[ $entry == (/|~)* || $entry == (|*/)..(|/*) ]]; then
print -ru2 "delzip: refusing unsafe path: $entry"
return 1
fi
[[ $entry == */ ]] && dirs+=(${entry%/}) || files+=($entry)
# many archives omit directory entries, so infer them from the
# paths as well; unzip creates those dirs regardless
parent=${entry%/}
while [[ $parent == */* ]]; do
parent=${parent%/*}
dirs+=($parent)
done
done
done
dirs=(${(u)dirs})
# only keep what is actually on disk, so the list reflects reality;
# -L catches broken symlinks, which -e alone misses
local -a present
present=(); for entry in $files; do [[ -e $entry || -L $entry ]] && present+=($entry); done
files=($present)
present=(); for entry in $dirs; do [[ -d $entry ]] && present+=($entry); done
dirs=($present)
(( $#files + $#dirs )) || { print -ru2 "delzip: nothing to remove here (already gone?)"; return 1 }
if (( dry )); then
print -rl -- ${files} ${(O)dirs}
return 0
fi
if (( ! force )); then
[[ -t 0 ]] || { print -ru2 "delzip: not a terminal, use -f to skip the prompt"; return 1 }
print -r -- "delzip: remove ${#files} file(s) under ${PWD}:"
print -rl -- " "${^files[1,20]}
(( $#files > 20 )) && print -r -- " ... and $(( $#files - 20 )) more"
(( $#dirs )) && print -r -- "plus ${#dirs} directory/ies, if they end up empty."
read -q "?Proceed? [y/N] " || { print -r -- $'\naborted'; return 1 }
print
fi
(( $#files )) && rm -f -- $files
# deepest first, so parents can empty out; rmdir refuses non-empty dirs
for entry in ${(O)dirs}; do
[[ -d $entry ]] && rmdir -- $entry 2>/dev/null
done
return 0
}
export MITMPROXY_SSLKEYLOGFILE="$HOME/.mitmproxy/sslkeylogfile.txt"
unset ZSH_AUTOSUGGEST_USE_ASYNC
export LANG='en_US.UTF-8'
export LC_CTYPE='en_US.UTF-8'
export TIME_STYLE='long-iso'
pasteinit() {
OLD_SELF_INSERT=${${(s.:.)widgets[self-insert]}[2,3]}
zle -N self-insert url-quote-magic
}
pastefinish() {
zle -N self-insert "$OLD_SELF_INSERT"
}
zstyle :bracketed-paste-magic paste-init pasteinit
zstyle :bracketed-paste-magic paste-finish pastefinish
ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)
+3
View File
@@ -0,0 +1,3 @@
# Callers select p10k_config before sourcing this module.
ZLE_RPROMPT_INDENT=0
[[ -z "$p10k_config" || ! -f "$p10k_config" ]] || source "$p10k_config"
+67
View File
@@ -0,0 +1,67 @@
# Background repository maintenance shared by desktop and server shells.
update_dotfiles() {
local repo="$HOME/dotfiles"
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/dotfiles-update-notice"
local notice_tmp="$notice_file.$$.tmp"
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 15)
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO git -C "$repo" fetch --quiet </dev/null 2>/dev/null || return
git -C "$repo" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 || return
git -C "$repo" merge-base --is-ancestor '@{u}' HEAD 2>/dev/null && return
if git -C "$repo" merge --ff-only --quiet '@{u}' 2>/dev/null; then
local notice="⬆ dotfiles updated — run: exec zsh"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
return
fi
local notice="⚠ dotfiles: $(git -C "$repo" symbolic-ref --short HEAD) can't fast-forward to @{u} — run: git -C ~/dotfiles pull"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
}
update_dotfiles &!
check_tools_repo() {
local repo branch remote mergeref remote_sha local_sha cmd notice
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/tools-update-notice"
local notice_tmp="$notice_file.$$.tmp"
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 10)
for repo in /opt/tools "$HOME/tools" "$HOME/tools/tools-repo"; do
local -a g=(git -c 'safe.directory=*' -C "$repo")
$g rev-parse --is-inside-work-tree >/dev/null 2>&1 || continue
branch=$($g symbolic-ref --short HEAD 2>/dev/null) || return
remote=$($g config "branch.$branch.remote" 2>/dev/null) || return
mergeref=$($g config "branch.$branch.merge" 2>/dev/null) || return
remote_sha=$(GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO $g ls-remote "$remote" "$mergeref" </dev/null 2>/dev/null | awk '{print $1}')
local_sha=$($g rev-parse HEAD 2>/dev/null)
[[ -n "$remote_sha" && -n "$local_sha" ]] || return
[[ "$remote_sha" == "$local_sha" ]] && return
$g merge-base --is-ancestor "$remote_sha" HEAD 2>/dev/null && return
[[ -w "$repo/.git" ]] && cmd="git -C $repo pull" || cmd="sudo git -C $repo pull"
notice="⬆ tools: updates available in $repo — run: $cmd"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
return
done
}
check_tools_repo &!
show_update_notices() {
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file claimed_notice
for notice_file in "$cache_dir"/{dotfiles,tools}-update-notice; do
[[ -s "$notice_file" ]] || continue
claimed_notice="$notice_file.$$.show"
mv "$notice_file" "$claimed_notice" 2>/dev/null || continue
command cat "$claimed_notice"
command rm -f "$claimed_notice"
done
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd show_update_notices