diff --git a/home/netty-worktree.nix b/home/netty-worktree.nix new file mode 100644 index 0000000..2b97894 --- /dev/null +++ b/home/netty-worktree.nix @@ -0,0 +1,23 @@ +{ + config, + lib, + pkgs, + ... +}: let + customScripts = import ../scripts {inherit config lib pkgs;}; +in { + home.packages = builtins.attrValues customScripts.nettyPackages; + + programs.zsh.initContent = lib.mkAfter '' + wtc() { + if [[ $# -ne 1 ]]; then + printf 'usage: wtc \n' >&2 + return 1 + fi + + local worktree_path + worktree_path=$(command wt-create "$1") || return + cd -- "$worktree_path" || return + } + ''; +} diff --git a/home/netty.nix b/home/netty.nix index 6da8357..066b82e 100644 --- a/home/netty.nix +++ b/home/netty.nix @@ -1,6 +1,6 @@ -{ ... }: -{ +{...}: { imports = [ ./common.nix + ./netty-worktree.nix ]; } diff --git a/home/scripts.nix b/home/scripts.nix index d0b8170..a62e465 100644 --- a/home/scripts.nix +++ b/home/scripts.nix @@ -3,14 +3,14 @@ lib, pkgs, ... -}: -let - customScripts = import ../scripts { inherit config lib pkgs; }; -in -{ - home.packages = builtins.attrValues customScripts.packages; +}: let + customScripts = import ../scripts {inherit config lib pkgs;}; +in { + home.packages = + builtins.attrValues customScripts.commonPackages + ++ lib.optionals pkgs.stdenv.isDarwin (builtins.attrValues customScripts.darwinPackages); - home.activation.initializeThemeState = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + home.activation.initializeThemeState = lib.hm.dag.entryAfter ["writeBoundary"] '' mkdir -p "${customScripts.theme.paths.stateDir}" "${customScripts.theme.paths.fzfDir}" "${customScripts.theme.paths.ghosttyDir}" "${customScripts.theme.paths.tmuxDir}" if [[ -f "${customScripts.theme.paths.stateFile}" ]]; then diff --git a/home/zsh.nix b/home/zsh.nix index 3279601..be01baa 100644 --- a/home/zsh.nix +++ b/home/zsh.nix @@ -3,15 +3,14 @@ lib, pkgs, ... -}: -{ +}: { home.file.".oh-my-zsh/custom/themes/agnoster.zsh-theme".source = ../config/agnoster.zsh-theme; - home.activation.ensureOhMyZshCache = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + home.activation.ensureOhMyZshCache = lib.hm.dag.entryAfter ["writeBoundary"] '' mkdir -p "${config.xdg.cacheHome}/oh-my-zsh" ''; - home.packages = [ pkgs.oh-my-zsh ]; + home.packages = [pkgs.oh-my-zsh]; programs.zsh = { enable = true; @@ -22,40 +21,42 @@ autosuggestion.enable = true; syntaxHighlighting.enable = true; - shellAliases = { - co = "codex --dangerously-bypass-approvals-and-sandbox"; - ca = "cursor-agent"; - cc = "claude"; - ch = "claude-handoff"; - cl = "clear"; - gc = "git commit"; - gd = "git diff"; - gk = "git checkout"; - gp = "git push"; - gpo = "git pull origin"; - gs = "git status"; - ld = "lumen diff"; - lg = "lazygit"; - nim = "nvim ."; - } - // lib.optionalAttrs pkgs.stdenv.isDarwin { - tailscale = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"; - }; + shellAliases = + { + co = "codex --dangerously-bypass-approvals-and-sandbox"; + ca = "cursor-agent"; + cc = "claude"; + ch = "claude-handoff"; + cl = "clear"; + gc = "git commit"; + gd = "git diff"; + gk = "git checkout"; + gp = "git push"; + gpo = "git pull origin"; + gs = "git status"; + ld = "lumen diff"; + lg = "lazygit"; + nim = "nvim ."; + } + // lib.optionalAttrs pkgs.stdenv.isDarwin { + tailscale = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"; + }; - envExtra = '' - if [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - export NODE_NO_WARNINGS=1 - '' - + lib.optionalString pkgs.stdenv.isDarwin '' - # Ghostty shell integration expects a resource directory; the Nix app - # bundle lives in the store instead of /Applications. - export GHOSTTY_RESOURCES_DIR="${pkgs.ghostty-bin}/Applications/Ghostty.app/Contents/Resources/ghostty" - '' - + '' - export MANPAGER="nvim +Man!" - ''; + envExtra = + '' + if [[ -f "$HOME/.cargo/env" ]]; then + . "$HOME/.cargo/env" + fi + export NODE_NO_WARNINGS=1 + '' + + lib.optionalString pkgs.stdenv.isDarwin '' + # Ghostty shell integration expects a resource directory; the Nix app + # bundle lives in the store instead of /Applications. + export GHOSTTY_RESOURCES_DIR="${pkgs.ghostty-bin}/Applications/Ghostty.app/Contents/Resources/ghostty" + '' + + '' + export MANPAGER="nvim +Man!" + ''; initContent = lib.mkMerge [ (lib.mkOrder 550 '' @@ -86,7 +87,9 @@ export BUN_INSTALL="$HOME/.bun" export PNPM_HOME="${ - if pkgs.stdenv.isDarwin then "$HOME/Library/pnpm" else "${config.xdg.dataHome}/pnpm" + if pkgs.stdenv.isDarwin + then "$HOME/Library/pnpm" + else "${config.xdg.dataHome}/pnpm" }" bindkey -v typeset -U path PATH @@ -103,9 +106,9 @@ "/run/current-system/sw/bin" "/nix/var/nix/profiles/default/bin" ${lib.optionalString pkgs.stdenv.isDarwin '' - "/opt/homebrew/bin" - "/opt/homebrew/sbin" - ''} + "/opt/homebrew/bin" + "/opt/homebrew/sbin" + ''} $path ) @@ -249,15 +252,17 @@ _codex_apply_highlight_styles - if command -v wt >/dev/null 2>&1; then - eval "$(command wt config shell init zsh)" + ${lib.optionalString pkgs.stdenv.isDarwin '' + if command -v wt >/dev/null 2>&1; then + eval "$(command wt config shell init zsh)" - # `wt` changes directories by sourcing directives into the current shell, - # so wrappers around it must stay shell functions instead of scripts. - wtc() { - wt switch --create --base @ "$@" - } - fi + # `wt` changes directories by sourcing directives into the current shell, + # so wrappers around it must stay shell functions instead of scripts. + wtc() { + wt switch --create --base @ "$@" + } + fi + ''} '') (lib.mkAfter '' diff --git a/scripts/default.nix b/scripts/default.nix index f32a0f6..a1fcb55 100644 --- a/scripts/default.nix +++ b/scripts/default.nix @@ -2,22 +2,20 @@ config, lib, pkgs, -}: -let - theme = import ../lib/theme.nix { inherit config; }; +}: let + theme = import ../lib/theme.nix {inherit config;}; tmuxConfigs = { dark = pkgs.writeText "tmux-theme-dark.conf" (theme.renderTmux "dark"); light = pkgs.writeText "tmux-theme-light.conf" (theme.renderTmux "light"); }; - mkScript = - { - file, - name, - runtimeInputs ? [ ], - replacements ? { }, - }: + mkScript = { + file, + name, + runtimeInputs ? [], + replacements ? {}, + }: pkgs.writeShellApplication { inherit name runtimeInputs; text = lib.replaceStrings (builtins.attrNames replacements) (builtins.attrValues replacements) ( @@ -25,11 +23,11 @@ let ); }; - packages = { + commonPackages = { ga = mkScript { name = "ga"; file = ./ga.sh; - runtimeInputs = with pkgs; [ git ]; + runtimeInputs = with pkgs; [git]; }; ghpr = mkScript { @@ -73,7 +71,7 @@ let ni = mkScript { name = "ni"; file = ./ni.sh; - runtimeInputs = with pkgs; [ nix ]; + runtimeInputs = with pkgs; [nix]; }; theme = mkScript { @@ -104,13 +102,30 @@ let "@TMUX_CONFIG@" = "${config.xdg.configHome}/tmux/tmux.conf"; }; }; + }; + darwinPackages = { wtc = mkScript { name = "wtc"; file = ./wtc.sh; }; }; -in -{ - inherit packages theme tmuxConfigs; + + nettyPackages = { + wt-create = mkScript { + name = "wt-create"; + file = ./wt-create.sh; + runtimeInputs = with pkgs; [coreutils git gnused]; + }; + + wt-path = mkScript { + name = "wt-path"; + file = ./wt-path.sh; + runtimeInputs = with pkgs; [coreutils git gnused]; + }; + }; +in { + inherit commonPackages darwinPackages nettyPackages theme tmuxConfigs; + + packages = commonPackages; } diff --git a/scripts/wt-create.sh b/scripts/wt-create.sh new file mode 100644 index 0000000..278a91f --- /dev/null +++ b/scripts/wt-create.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ $# -ne 1 ]]; then + printf 'usage: wt-create \n' >&2 + exit 1 +fi + +branch_name=$1 +repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || { + printf 'wt-create: not inside a git repository\n' >&2 + exit 1 +} + +target_path=$(wt-path "$branch_name") + +if [[ -e "$target_path" ]]; then + printf 'wt-create: path already exists: %s\n' "$target_path" >&2 + exit 1 +fi + +if git -C "$repo_root" show-ref --verify --quiet "refs/heads/$branch_name"; then + git -C "$repo_root" worktree add -- "$target_path" "$branch_name" 1>&2 +else + git -C "$repo_root" worktree add -b "$branch_name" -- "$target_path" HEAD 1>&2 +fi + +printf '%s\n' "$target_path" diff --git a/scripts/wt-path.sh b/scripts/wt-path.sh new file mode 100644 index 0000000..9d8abb9 --- /dev/null +++ b/scripts/wt-path.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ $# -ne 1 ]]; then + printf 'usage: wt-path \n' >&2 + exit 1 +fi + +common_git_dir=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) || { + printf 'wt-path: not inside a git repository\n' >&2 + exit 1 +} + +repo_root=$(cd "${common_git_dir}/.." && pwd -P) || { + printf 'wt-path: failed to resolve repository root\n' >&2 + exit 1 +} + +worktree_name=$1 +clean_name=$(printf '%s' "$worktree_name" | sed -E 's#[^[:alnum:]._-]+#-#g; s#-+#-#g; s#(^[.-]+|[.-]+$)##g') + +if [[ -z "$clean_name" ]]; then + printf 'wt-path: %s does not produce a usable path name\n' "$worktree_name" >&2 + exit 1 +fi + +repo_parent=$(dirname "$repo_root") +repo_name=$(basename "$repo_root") + +printf '%s/%s-%s\n' "$repo_parent" "$repo_name" "$clean_name"