From 3c9049194b19ef604244d680294897a07f25625e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:01:22 +0000 Subject: [PATCH 1/5] feat: migrate from homebrew to nix with home-manager Move all CLI tools from Homebrew to a declarative Nix home-manager configuration (flake.nix + home.nix). Homebrew is retained solely for GUI / cask applications (browsers, fonts, desktop apps). - Add flake.nix with multi-platform home-manager support - Add home.nix declaring all CLI packages previously in .brew - Slim down .brew to GUI / cask-only installs - Update Makefile with run-nix target and stow ignores - Update .profile and .bashrc to source Nix paths - Update .gitignore for Nix build artifacts - Update README.md with Nix installation and usage docs Co-authored-by: bdossantos <245284+bdossantos@users.noreply.github.com> Agent-Logs-Url: https://github.com/bdossantos/dotfiles/sessions/839644e4-65a7-4221-aa3c-fdc6dbf749bd --- .aliases | 4 +- .bashrc | 29 ++++++++-- .brew | 123 +++++-------------------------------------- .gitignore | 2 + .profile | 19 +++++-- Makefile | 35 +++++++++++- README.md | 42 ++++++++++++--- flake.nix | 38 ++++++++++++++ home.nix | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 313 insertions(+), 131 deletions(-) create mode 100644 flake.nix create mode 100644 home.nix diff --git a/.aliases b/.aliases index fc13f7e..1d6d439 100644 --- a/.aliases +++ b/.aliases @@ -302,8 +302,8 @@ if [[ $OS == 'Darwin' ]]; then # Stopwatch alias timer='echo "Timer started. Stop with Ctrl-D." && date && time cat && date' - # Get OS X Software Updates, and update installed Ruby gems, Homebrew, npm, and their installed packages - alias update='sudo softwareupdate -i -a; brew update; brew upgrade; brew cleanup; npm install npm -g; npm update -g; sudo gem update --system; sudo gem update' + # Get OS X Software Updates, and update Nix, Homebrew, npm, and their installed packages + alias update='sudo softwareupdate -i -a; nix flake update --flake ~/.dotfiles 2>/dev/null; home-manager switch --flake ~/.dotfiles 2>/dev/null; brew update; brew upgrade; brew cleanup; npm install npm -g; npm update -g; sudo gem update --system; sudo gem update' # IP addresses alias ip="dig +short myip.opendns.com @resolver1.opendns.com" diff --git a/.bashrc b/.bashrc index df13140..9bdabe3 100644 --- a/.bashrc +++ b/.bashrc @@ -46,11 +46,23 @@ if [ -r /etc/profile.d/bash_completion.sh ]; then source /etc/profile.d/bash_completion.sh fi -# brew +# nix & home-manager +if [ -e "${HOME}/.nix-profile/etc/profile.d/hm-session-vars.sh" ]; then + # shellcheck disable=SC1091 + source "${HOME}/.nix-profile/etc/profile.d/hm-session-vars.sh" +fi + +# brew (kept for GUI / cask apps) if command -v brew &>/dev/null; then eval "$(brew shellenv)" fi +# bash completions — Nix profile +if [ -r "${HOME}/.nix-profile/etc/profile.d/bash_completion.sh" ]; then + # shellcheck disable=SC1091 + source "${HOME}/.nix-profile/etc/profile.d/bash_completion.sh" +fi + # https://docs.brew.sh/Shell-Completion if [ -r "${HOMEBREW_PREFIX}/etc/profile.d/bash_completion.sh" ]; then export BASH_COMPLETION_COMPAT_DIR="${HOMEBREW_PREFIX}/etc/bash_completion.d" @@ -67,14 +79,21 @@ fi PROMPT_COMMAND="history -a;$PROMPT_COMMAND" # chruby -if [ -f "${HOMEBREW_PREFIX}/share/chruby/chruby.sh" ]; then +CHRUBY_SH="" +if [ -f "${HOME}/.nix-profile/share/chruby/chruby.sh" ]; then + CHRUBY_SH="${HOME}/.nix-profile/share/chruby" +elif [ -f "${HOMEBREW_PREFIX}/share/chruby/chruby.sh" ]; then + CHRUBY_SH="${HOMEBREW_PREFIX}/share/chruby" +fi + +if [ -n "$CHRUBY_SH" ]; then # shellcheck disable=SC2034 RUBIES=("${HOME}/.rubies/*") # shellcheck source=/dev/null - source "${HOMEBREW_PREFIX}/share/chruby/chruby.sh" + source "${CHRUBY_SH}/chruby.sh" # shellcheck source=/dev/null - source "${HOMEBREW_PREFIX}/share/chruby/auto.sh" + source "${CHRUBY_SH}/auto.sh" fi if command -v zoxide &>/dev/null; then @@ -86,7 +105,7 @@ if command -v direnv &>/dev/null; then fi # gcloud -# For Homebrew-installed gcloud-cli +# For Homebrew-installed gcloud-cli (GUI apps are still managed via Homebrew) if command -v brew &>/dev/null; then GCLOUD_SDK="${HOMEBREW_PREFIX}/share/google-cloud-sdk" if [ -f "${GCLOUD_SDK}/path.bash.inc" ] && diff --git a/.brew b/.brew index 2ad483e..5e0e343 100755 --- a/.brew +++ b/.brew @@ -1,4 +1,8 @@ #!/usr/bin/env bash +# +# Install GUI / cask-only applications via Homebrew. +# CLI tools are managed declaratively with Nix — see flake.nix and home.nix. +# set -o errexit set -o pipefail @@ -21,129 +25,27 @@ echo "--> Detected CPU Brand: ${CPU_BRAND}" echo "--> Make sure we're using the latest Homebrew" brew update -echo '--> Upgrade any already-installed formulae' +echo '--> Upgrade any already-installed casks' brew upgrade -echo '--> Install all brew Packages' +echo '--> Install GUI / cask applications' brew install \ - ack \ - advancecomp \ + 1password \ anki \ - ansifilter \ - ast-grep \ - autoenv \ - awscli \ - bash \ - bash-completion@2 \ - bat \ - bazel \ - binutils \ - cheat \ - chruby \ - cli53 \ - closure-compiler \ - coreutils \ - ctags \ - curl \ - diff-so-fancy \ - diffutils \ - ed \ - editorconfig \ - entr \ - eza \ - fd \ - findomain \ - findutils \ firefox \ font-hack \ font-hack-nerd-font \ font-jetbrains-mono \ - fzf \ - gawk \ - gcc \ gcloud-cli \ ghostty \ - gifsicle \ - git \ - gitlint \ - gnu-indent \ - gnu-sed \ - gnu-tar \ - gnu-which \ - go \ - go-jsonnet \ google-chrome \ - gpatch \ - gpg \ - grep \ - gsl \ - gzip \ - hadolint \ - htmlcompressor \ - htop \ imageoptim \ - ipcalc \ iterm2 \ - jhead \ - jpeg \ - jpegoptim \ - jq \ - k9s \ - kops \ - kubectx \ - lazygit \ - less \ - lesspipe \ - libyaml \ - logcli \ - luarocks \ - make \ - miller \ micro-snitch \ - moreutils \ - nmap \ omnigraffle \ - openssh \ - openssl \ - optipng \ - packer \ - pigz \ - pngcrush \ - pngquant \ - pre-commit \ - prettier \ - pv \ - pwgen \ - python@3.9 \ - readline \ - ripgrep \ - ruby-build \ - ruby-install \ - ruff \ - s3cmd \ - shellcheck \ - shfmt \ - shpotify \ spotify \ - ssh-copy-id \ - starship \ - stow \ - tcpdump \ - terminal-notifier \ - tfenv \ - the_silver_searcher \ - tig \ - tldr \ - tmux \ tor-browser \ - tree \ - vale \ - vim \ - vlc \ - watch \ - wget \ - xz \ - zoxide + vlc # 1password and little-snitch should be installed globally ... brew install --appdir /Applications \ @@ -165,8 +67,6 @@ echo '--> Install/update Homebrew cask' brew update -brew tap homebrew/cask-drivers - brew install \ gpg-suite \ keybase \ @@ -174,6 +74,9 @@ brew install \ virtualbox " +echo '--> Clean up Homebrew' +brew autoremove && brew cleanup + ## ruby mkdir -p "${HOME}/.rubies" @@ -185,9 +88,9 @@ RUBIES=( for ruby in "${RUBIES[@]}"; do if [[ ! -f "${HOME}/.rubies/ruby-${ruby}/bin/ruby" ]]; then - "${HOME}/.homebrew/bin/ruby-install" "ruby" "$ruby" + ruby-install "ruby" "$ruby" fi done # Disable the macOS default ssh-agent for your user. -launchctl disable user/$UID/com.openssh.ssh-agent +launchctl disable "user/$UID/com.openssh.ssh-agent" diff --git a/.gitignore b/.gitignore index 5278fd7..4aca293 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ .config .gnupg/ nvim.appimage +result +.direnv diff --git a/.profile b/.profile index 88d42cd..a021da3 100644 --- a/.profile +++ b/.profile @@ -1,7 +1,16 @@ +# Nix +if [ -e "${HOME}/.nix-profile/etc/profile.d/nix.sh" ]; then + # shellcheck disable=SC1091 + . "${HOME}/.nix-profile/etc/profile.d/nix.sh" +elif [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then + # shellcheck disable=SC1091 + . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' +fi + # PATH -export PATH="$HOME/.homebrew/bin:$HOME/.homebrew/sbin:$HOME/bin:/usr/local/bin:/usr/local/sbin:$PATH" +export PATH="$HOME/.nix-profile/bin:$HOME/.homebrew/bin:$HOME/.homebrew/sbin:$HOME/bin:/usr/local/bin:/usr/local/sbin:$PATH" -# Homebrew prefix +# Homebrew prefix (kept for GUI / cask apps) export HOMEBREW_PREFIX='/usr/local' if command -v brew >/dev/null 2>&1; then HOMEBREW_PREFIX="$(brew --prefix)" @@ -54,7 +63,7 @@ elif less_pipe=$(command -v lesspipe.sh); then fi # man -export MANPATH="$HOME/.homebrew/share/man${MANPATH+:$MANPATH}:" +export MANPATH="$HOME/.nix-profile/share/man:$HOME/.homebrew/share/man${MANPATH+:$MANPATH}:" # Don't clear the screen after quitting a manual page export MANPAGER='less -X' @@ -101,7 +110,9 @@ if [ ! -d "$TMPDIR" ]; then fi # diff-highligh -if [ -d "${HOMEBREW_PREFIX}/share/git-core/contrib/diff-highlight/" ]; then +if [ -d "${HOME}/.nix-profile/share/git-core/contrib/diff-highlight/" ]; then + export PATH="${PATH}:${HOME}/.nix-profile/share/git-core/contrib/diff-highlight" +elif [ -d "${HOMEBREW_PREFIX}/share/git-core/contrib/diff-highlight/" ]; then export PATH="${PATH}:${HOMEBREW_PREFIX}/share/git-core/contrib/diff-highlight" fi diff --git a/Makefile b/Makefile index 3e948e7..8357471 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,19 @@ OS = "$(uname)" SHELL := /usr/bin/env bash +SYSTEM := $(shell uname -m)-$(shell uname -s | tr '[:upper:]' '[:lower:]') + +# Map uname output to Nix system identifiers +ifeq ($(SYSTEM),arm64-darwin) + NIX_SYSTEM := aarch64-darwin +else ifeq ($(SYSTEM),x86_64-darwin) + NIX_SYSTEM := x86_64-darwin +else ifeq ($(SYSTEM),x86_64-linux) + NIX_SYSTEM := x86_64-linux +else ifeq ($(SYSTEM),aarch64-linux) + NIX_SYSTEM := aarch64-linux +else + NIX_SYSTEM := x86_64-linux +endif .DEFAULT_GOAL := help @@ -12,6 +26,7 @@ install: ## Install all the things @make install-dotfiles \ install-vundle \ install-nvim-deps \ + run-nix @[[ $OS == 'Darwin' ]] \ && make install-homebrew run-brew @@ -26,6 +41,9 @@ install-dotfiles: ## Pull and Install dotfiles --ignore='README.md' \ --ignore='LICENCE' \ --ignore='Makefile' \ + --ignore='flake.nix' \ + --ignore='flake.lock' \ + --ignore='home.nix' \ --override='.bashrc' \ --override='.profile' \ --override='.bash_profile' @@ -53,9 +71,19 @@ pre-commit: ## Run pre-commit tests $(info --> Run pre-commit) @pre-commit run --all-files -run-brew: ## Run ./.brew +run-brew: ## Run ./.brew (GUI / cask apps only) @bash -x .brew +run-nix: ## Apply Nix home-manager configuration + $(info --> Apply home-manager configuration for $(NIX_SYSTEM)) + @command -v home-manager >/dev/null || { \ + echo 'home-manager not found. Install Nix and home-manager first:'; \ + echo ' curl --proto =https --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install'; \ + echo ' nix run home-manager -- switch --flake .#$(NIX_SYSTEM)'; \ + exit 1; \ + } + @home-manager switch --flake '.#$(NIX_SYSTEM)' + test: ## Run tests suite @$(MAKE) pre-commit @@ -66,4 +94,7 @@ uninstall-dotfiles: ## Uninstall dotfiles --ignore='.github' \ --ignore='README.md' \ --ignore='LICENCE' \ - --ignore='Makefile' + --ignore='Makefile' \ + --ignore='flake.nix' \ + --ignore='flake.lock' \ + --ignore='home.nix' diff --git a/README.md b/README.md index ef0cb4f..2d87482 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,11 @@ Personal dotfiles for macOS, managed with [GNU Stow](https://www.gnu.org/software/stow/). +CLI tools are managed declaratively with [Nix](https://nixos.org/) and +[home-manager](https://nix-community.github.io/home-manager/). GUI / cask +applications are still installed via +[Homebrew](https://brew.sh/). + ## What's included | File / directory | Description | @@ -13,7 +18,8 @@ Personal dotfiles for macOS, managed with | `.config/ghostty/` | [Ghostty](https://ghostty.org) terminal with Dracula colour scheme | | `.aliases` | Handy shell aliases | | `.bash_profile` / `.bashrc` / `.profile` | Bash startup files | -| `.brew` | Homebrew formulae and casks | +| `.brew` | Homebrew casks (GUI apps only) | +| `flake.nix` / `home.nix` | Nix home-manager configuration (CLI tools) | | `.gitconfig` | Git settings (diff-so-fancy, GPG signing, …) | | `.gitmessage` | Conventional Commits message template | | `.gnupg/` | GPG configuration | @@ -29,6 +35,13 @@ Personal dotfiles for macOS, managed with - **git** - **[GNU Stow](https://www.gnu.org/software/stow/)** — `brew install stow` (macOS) or `apt install stow` (Debian/Ubuntu) +- **[Nix](https://nixos.org/)** — install with the + [Determinate Nix Installer](https://zero-to-nix.com/start/install): + + ```bash + curl --proto '=https' --tlsv1.2 -sSf -L \ + https://install.determinate.systems/nix | sh -s -- install + ``` ## Installation @@ -45,6 +58,25 @@ cd ~/.dotfiles make uninstall ``` +## Package management + +### CLI tools (Nix + home-manager) + +All command-line tools are declared in `home.nix` and installed via +home-manager. To add or remove a CLI tool, edit `home.nix` and apply: + +```bash +make run-nix +``` + +### GUI / cask applications (Homebrew) + +GUI applications, fonts, and other casks are still installed via Homebrew: + +```bash +make run-brew +``` + ## macOS setup ### Sensible macOS defaults @@ -57,18 +89,12 @@ su - admin -c "env PATH=$PATH:/usr/sbin/ bash -x $HOME/.dotfiles/.macos" su - admin -c "env PATH=$PATH:/usr/sbin/ bash -x $HOME/.dotfiles/.macos_hardening" ``` -### Install Homebrew formulae and apps - -```bash -make run-brew -``` - ## Bash ### Set Bash as default shell ```bash -chsh -s "$(brew --prefix)/bin/bash" +chsh -s "$(command -v bash)" exec $SHELL ``` diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1dec024 --- /dev/null +++ b/flake.nix @@ -0,0 +1,38 @@ +{ + description = "bdossantos dotfiles — managed with home-manager"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { nixpkgs, home-manager, ... }: + let + supportedSystems = [ + "aarch64-darwin" + "x86_64-darwin" + "x86_64-linux" + "aarch64-linux" + ]; + + mkHomeConfiguration = + system: + home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.${system}; + modules = [ ./home.nix ]; + }; + in + { + homeConfigurations = builtins.listToAttrs ( + map (system: { + name = system; + value = mkHomeConfiguration system; + }) supportedSystems + ); + }; +} diff --git a/home.nix b/home.nix new file mode 100644 index 0000000..e24ff76 --- /dev/null +++ b/home.nix @@ -0,0 +1,152 @@ +{ pkgs, lib, ... }: + +let + isDarwin = pkgs.stdenv.isDarwin; + isLinux = pkgs.stdenv.isLinux; + username = "bdossantos"; + homeDirectory = if isDarwin then "/Users/${username}" else "/home/${username}"; +in +{ + home.username = username; + home.homeDirectory = homeDirectory; + home.stateVersion = "24.11"; + + programs.home-manager.enable = true; + + home.packages = + with pkgs; + [ + # --- search & grep --- + ack + ripgrep + silver-searcher + ast-grep + + # --- file & directory utilities --- + coreutils + diffutils + fd + findutils + moreutils + tree + eza + entr + watch + + # --- text processing --- + gawk + gnused + gnugrep + jq + miller + ed + + # --- compression --- + advancecomp + gzip + pigz + xz + + # --- image optimisation --- + gifsicle + jhead + jpegoptim + libjpeg + optipng + pngcrush + pngquant + + # --- networking --- + curl + ipcalc + nmap + openssh + wget + + # --- shells & prompts --- + bash + bash-completion + starship + + # --- version control --- + diff-so-fancy + git + gitlint + lazygit + tig + + # --- editors --- + vim + + # --- terminal multiplexers --- + tmux + + # --- gnu replacements --- + gnutar + gnupatch + gnumake + + # --- languages & runtimes --- + go + go-jsonnet + python3 + + # --- ruby --- + chruby + ruby-build + + # --- linters & formatters --- + hadolint + pre-commit + nodePackages.prettier + ruff + shellcheck + shfmt + vale + editorconfig-core-c + + # --- cloud & infrastructure --- + awscli2 + k9s + kops + kubectx + packer + + # --- security & crypto --- + gnupg + openssl + + # --- misc cli tools --- + ansifilter + bat + cheat + ctags + fzf + htop + less + lesspipe + luarocks + pv + pwgen + readline + s3cmd + stow + tldr + zoxide + + # --- libraries --- + gsl + libyaml + + # --- build tools --- + bazel + gcc + ] + ++ lib.optionals isDarwin [ + terminal-notifier + ] + ++ lib.optionals isLinux [ + binutils + tcpdump + ]; +} From 172619672063efbe5b54a1719245b4acaeace1de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:02:42 +0000 Subject: [PATCH 2/5] fix: address code review feedback - Quote --proto flag in Makefile echo text for clarity - Convert update alias to a function with proper command checks Co-authored-by: bdossantos <245284+bdossantos@users.noreply.github.com> Agent-Logs-Url: https://github.com/bdossantos/dotfiles/sessions/839644e4-65a7-4221-aa3c-fdc6dbf749bd --- .aliases | 11 ++++++++++- Makefile | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.aliases b/.aliases index 1d6d439..fe9b400 100644 --- a/.aliases +++ b/.aliases @@ -303,7 +303,16 @@ if [[ $OS == 'Darwin' ]]; then alias timer='echo "Timer started. Stop with Ctrl-D." && date && time cat && date' # Get OS X Software Updates, and update Nix, Homebrew, npm, and their installed packages - alias update='sudo softwareupdate -i -a; nix flake update --flake ~/.dotfiles 2>/dev/null; home-manager switch --flake ~/.dotfiles 2>/dev/null; brew update; brew upgrade; brew cleanup; npm install npm -g; npm update -g; sudo gem update --system; sudo gem update' + update() { + sudo softwareupdate -i -a + if command -v nix &>/dev/null; then + nix flake update --flake ~/.dotfiles + home-manager switch --flake ~/.dotfiles + fi + brew update && brew upgrade && brew cleanup + npm install npm -g && npm update -g + sudo gem update --system && sudo gem update + } # IP addresses alias ip="dig +short myip.opendns.com @resolver1.opendns.com" diff --git a/Makefile b/Makefile index 8357471..a0da5b8 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ run-nix: ## Apply Nix home-manager configuration $(info --> Apply home-manager configuration for $(NIX_SYSTEM)) @command -v home-manager >/dev/null || { \ echo 'home-manager not found. Install Nix and home-manager first:'; \ - echo ' curl --proto =https --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install'; \ + echo ' curl --proto "=https" --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install'; \ echo ' nix run home-manager -- switch --flake .#$(NIX_SYSTEM)'; \ exit 1; \ } From 1cd5779bafd3032d7eb3badb8b77e1c2894c8c72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:34:23 +0000 Subject: [PATCH 3/5] feat: add nix-darwin and remove .brew file Replace the Homebrew .brew shell script with a declarative nix-darwin configuration (darwin.nix). On macOS, a single `darwin-rebuild switch` now manages CLI tools (via home-manager), GUI/cask apps (via Homebrew module), and system settings. - Add darwin.nix with homebrew.casks and system config - Update flake.nix with nix-darwin input and darwinConfigurations - Add ruby-install to home.nix - Remove .brew (fully replaced by darwin.nix) - Update Makefile with run-darwin target, install-rubies, remove install-homebrew and run-brew - Update .bashrc, .profile, .aliases, README.md Agent-Logs-Url: https://github.com/bdossantos/dotfiles/sessions/2faa8162-bb22-410f-8c13-72ec4551de09 Co-authored-by: bdossantos <245284+bdossantos@users.noreply.github.com> --- .aliases | 8 +++-- .bashrc | 4 +-- .brew | 96 ------------------------------------------------------ .profile | 2 +- Makefile | 43 +++++++++++++++--------- README.md | 52 ++++++++++++++++++++++------- darwin.nix | 75 ++++++++++++++++++++++++++++++++++++++++++ flake.nix | 43 +++++++++++++++++++++--- home.nix | 1 + 9 files changed, 190 insertions(+), 134 deletions(-) delete mode 100755 .brew create mode 100644 darwin.nix diff --git a/.aliases b/.aliases index fe9b400..7e56426 100644 --- a/.aliases +++ b/.aliases @@ -302,14 +302,16 @@ if [[ $OS == 'Darwin' ]]; then # Stopwatch alias timer='echo "Timer started. Stop with Ctrl-D." && date && time cat && date' - # Get OS X Software Updates, and update Nix, Homebrew, npm, and their installed packages + # Get OS X Software Updates and apply nix-darwin / home-manager changes update() { sudo softwareupdate -i -a - if command -v nix &>/dev/null; then + if command -v darwin-rebuild &>/dev/null; then + nix flake update --flake ~/.dotfiles + darwin-rebuild switch --flake ~/.dotfiles + elif command -v nix &>/dev/null; then nix flake update --flake ~/.dotfiles home-manager switch --flake ~/.dotfiles fi - brew update && brew upgrade && brew cleanup npm install npm -g && npm update -g sudo gem update --system && sudo gem update } diff --git a/.bashrc b/.bashrc index 9bdabe3..c2f2a2d 100644 --- a/.bashrc +++ b/.bashrc @@ -52,7 +52,7 @@ if [ -e "${HOME}/.nix-profile/etc/profile.d/hm-session-vars.sh" ]; then source "${HOME}/.nix-profile/etc/profile.d/hm-session-vars.sh" fi -# brew (kept for GUI / cask apps) +# brew (managed by nix-darwin on macOS) if command -v brew &>/dev/null; then eval "$(brew shellenv)" fi @@ -105,7 +105,7 @@ if command -v direnv &>/dev/null; then fi # gcloud -# For Homebrew-installed gcloud-cli (GUI apps are still managed via Homebrew) +# gcloud-cli is managed by nix-darwin's Homebrew module on macOS if command -v brew &>/dev/null; then GCLOUD_SDK="${HOMEBREW_PREFIX}/share/google-cloud-sdk" if [ -f "${GCLOUD_SDK}/path.bash.inc" ] && diff --git a/.brew b/.brew deleted file mode 100755 index 5e0e343..0000000 --- a/.brew +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash -# -# Install GUI / cask-only applications via Homebrew. -# CLI tools are managed declaratively with Nix — see flake.nix and home.nix. -# - -set -o errexit -set -o pipefail -set -o nounset - -DEBUG=${DEBUG:=0} -[[ $DEBUG -eq 1 ]] && set -o xtrace - -export HOMEBREW_CASK_OPTS="--appdir=${HOME}/Applications --fontdir=${HOME}/Library/Fonts --no-binaries --require-sha" -export HOMEBREW_NO_ANALYTICS=1 -export HOMEBREW_NO_AUTO_UPDATE=1 -export HOMEBREW_NO_INSECURE_REDIRECT=1 -export PATH="${HOME}/.homebrew/bin:${PATH}" - -CPU_BRAND="$(sysctl -n machdep.cpu.brand_string)" -export CPU_BRAND - -echo "--> Detected CPU Brand: ${CPU_BRAND}" - -echo "--> Make sure we're using the latest Homebrew" -brew update - -echo '--> Upgrade any already-installed casks' -brew upgrade - -echo '--> Install GUI / cask applications' -brew install \ - 1password \ - anki \ - firefox \ - font-hack \ - font-hack-nerd-font \ - font-jetbrains-mono \ - gcloud-cli \ - ghostty \ - google-chrome \ - imageoptim \ - iterm2 \ - micro-snitch \ - omnigraffle \ - spotify \ - tor-browser \ - vlc - -# 1password and little-snitch should be installed globally ... -brew install --appdir /Applications \ - 1password \ - little-snitch - -# Yes, I use a non privileged user for daily use -echo "--> login as admin to install some casks system-wide in /Applications" - -su admin -c " -while true; do sudo -n true; sleep 60; kill -0 $$ || exit; done 2>/dev/null & - -make -f ${HOME}/.dotfiles/Makefile install-homebrew - -export HOMEBREW_CASK_OPTS='--appdir=/Applications --no-binaries --require-sha' -export PATH='/Users/admin/.homebrew/bin:${PATH}' - -echo '--> Install/update Homebrew cask' - -brew update - -brew install \ - gpg-suite \ - keybase \ - vagrant \ - virtualbox -" - -echo '--> Clean up Homebrew' -brew autoremove && brew cleanup - -## ruby -mkdir -p "${HOME}/.rubies" - -RUBIES=( - 3.4.1 - 3.3.6 - 3.2.6 -) - -for ruby in "${RUBIES[@]}"; do - if [[ ! -f "${HOME}/.rubies/ruby-${ruby}/bin/ruby" ]]; then - ruby-install "ruby" "$ruby" - fi -done - -# Disable the macOS default ssh-agent for your user. -launchctl disable "user/$UID/com.openssh.ssh-agent" diff --git a/.profile b/.profile index a021da3..0afedc7 100644 --- a/.profile +++ b/.profile @@ -10,7 +10,7 @@ fi # PATH export PATH="$HOME/.nix-profile/bin:$HOME/.homebrew/bin:$HOME/.homebrew/sbin:$HOME/bin:/usr/local/bin:/usr/local/sbin:$PATH" -# Homebrew prefix (kept for GUI / cask apps) +# Homebrew prefix (managed by nix-darwin on macOS) export HOMEBREW_PREFIX='/usr/local' if command -v brew >/dev/null 2>&1; then HOMEBREW_PREFIX="$(brew --prefix)" diff --git a/Makefile b/Makefile index a0da5b8..20fbc05 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,12 @@ help: install: ## Install all the things @make install-dotfiles \ install-vundle \ - install-nvim-deps \ - run-nix - @[[ $OS == 'Darwin' ]] \ - && make install-homebrew run-brew + install-nvim-deps + @if [[ $OS == 'Darwin' ]]; then \ + make run-darwin; \ + else \ + make run-nix; \ + fi install-dotfiles: ## Pull and Install dotfiles @git pull -q && git submodule update --init --recursive -q @@ -44,18 +46,11 @@ install-dotfiles: ## Pull and Install dotfiles --ignore='flake.nix' \ --ignore='flake.lock' \ --ignore='home.nix' \ + --ignore='darwin.nix' \ --override='.bashrc' \ --override='.profile' \ --override='.bash_profile' -install-homebrew: ## Install homebrew, the missing package manager for OS X - $(info --> Install homebrew) - @mkdir -m 0700 -p ~/.homebrew - @if [[ ! -f ~/.homebrew/bin/brew ]]; then \ - curl -L https://github.com/Homebrew/brew/tarball/master \ - | tar xz --strip 1 -C ~/.homebrew; \ - fi - install-vundle: ## Install Vundle, the plug-in manager for Vim $(info --> Install Vundle) @mkdir -p ~/.vim/bundle/ ~/.vimswap ~/.vimundo ~/.tmp @@ -67,14 +62,29 @@ install-nvim-deps: ## Install/setup Neovim dependencies $(info --> Setup Neovim directories) @mkdir -p ~/.vimswap ~/.vimundo ~/.tmp +install-rubies: ## Install Ruby versions via ruby-install + $(info --> Install Ruby versions) + @mkdir -p ~/.rubies + @for ruby in 3.4.1 3.3.6 3.2.6; do \ + if [[ ! -f "$$HOME/.rubies/ruby-$$ruby/bin/ruby" ]]; then \ + ruby-install ruby "$$ruby"; \ + fi; \ + done + pre-commit: ## Run pre-commit tests $(info --> Run pre-commit) @pre-commit run --all-files -run-brew: ## Run ./.brew (GUI / cask apps only) - @bash -x .brew +run-darwin: ## Apply nix-darwin configuration (macOS — includes home-manager + Homebrew casks) + $(info --> Apply nix-darwin configuration for $(NIX_SYSTEM)) + @command -v darwin-rebuild >/dev/null && { \ + darwin-rebuild switch --flake '.#$(NIX_SYSTEM)'; \ + } || { \ + echo 'Bootstrapping nix-darwin for the first time...'; \ + nix run nix-darwin -- switch --flake '.#$(NIX_SYSTEM)'; \ + } -run-nix: ## Apply Nix home-manager configuration +run-nix: ## Apply Nix home-manager configuration (Linux) $(info --> Apply home-manager configuration for $(NIX_SYSTEM)) @command -v home-manager >/dev/null || { \ echo 'home-manager not found. Install Nix and home-manager first:'; \ @@ -97,4 +107,5 @@ uninstall-dotfiles: ## Uninstall dotfiles --ignore='Makefile' \ --ignore='flake.nix' \ --ignore='flake.lock' \ - --ignore='home.nix' + --ignore='home.nix' \ + --ignore='darwin.nix' diff --git a/README.md b/README.md index 2d87482..afa6682 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,13 @@ Personal dotfiles for macOS, managed with [GNU Stow](https://www.gnu.org/software/stow/). -CLI tools are managed declaratively with [Nix](https://nixos.org/) and -[home-manager](https://nix-community.github.io/home-manager/). GUI / cask -applications are still installed via -[Homebrew](https://brew.sh/). +On macOS, system configuration and packages are managed declaratively with +[nix-darwin](https://github.com/LnL7/nix-darwin) (which includes +[home-manager](https://nix-community.github.io/home-manager/) for user-level +packages and [Homebrew](https://brew.sh/) for GUI / cask applications). + +On Linux, [home-manager](https://nix-community.github.io/home-manager/) is +used standalone for CLI tools. ## What's included @@ -18,8 +21,9 @@ applications are still installed via | `.config/ghostty/` | [Ghostty](https://ghostty.org) terminal with Dracula colour scheme | | `.aliases` | Handy shell aliases | | `.bash_profile` / `.bashrc` / `.profile` | Bash startup files | -| `.brew` | Homebrew casks (GUI apps only) | -| `flake.nix` / `home.nix` | Nix home-manager configuration (CLI tools) | +| `flake.nix` | Nix flake (nix-darwin + home-manager) | +| `darwin.nix` | macOS system config (Homebrew casks, launchd, …) | +| `home.nix` | Home-manager config (CLI tools, shared across OS) | | `.gitconfig` | Git settings (diff-so-fancy, GPG signing, …) | | `.gitmessage` | Conventional Commits message template | | `.gnupg/` | GPG configuration | @@ -43,6 +47,15 @@ applications are still installed via https://install.determinate.systems/nix | sh -s -- install ``` +- **[Homebrew](https://brew.sh/)** (macOS only) — nix-darwin manages casks + through Homebrew, so it must be installed first: + + ```bash + mkdir -m 0700 -p ~/.homebrew + curl -L https://github.com/Homebrew/brew/tarball/master \ + | tar xz --strip 1 -C ~/.homebrew + ``` + ## Installation ```bash @@ -51,6 +64,9 @@ cd ~/.dotfiles make install ``` +On a fresh macOS where `darwin-rebuild` is not yet available, nix-darwin is +bootstrapped automatically via `nix run nix-darwin -- switch`. + ## Uninstallation ```bash @@ -60,21 +76,33 @@ make uninstall ## Package management -### CLI tools (Nix + home-manager) +### macOS (nix-darwin) + +A single `darwin-rebuild switch` manages **everything** — CLI tools (via +home-manager), GUI applications (via Homebrew casks), and system settings: + +```bash +make run-darwin +``` + +- **CLI tools** — edit `home.nix`, then `make run-darwin` +- **GUI / cask apps** — edit `darwin.nix` (`homebrew.casks`), then + `make run-darwin` + +### Linux (home-manager) -All command-line tools are declared in `home.nix` and installed via -home-manager. To add or remove a CLI tool, edit `home.nix` and apply: +On Linux, only home-manager is used for CLI tools: ```bash make run-nix ``` -### GUI / cask applications (Homebrew) +### Ruby versions -GUI applications, fonts, and other casks are still installed via Homebrew: +Ruby versions are installed via `ruby-install` (managed by Nix): ```bash -make run-brew +make install-rubies ``` ## macOS setup diff --git a/darwin.nix b/darwin.nix new file mode 100644 index 0000000..9efad1c --- /dev/null +++ b/darwin.nix @@ -0,0 +1,75 @@ +{ pkgs, ... }: + +let + username = "bdossantos"; + homeDirectory = "/Users/${username}"; +in +{ + system.primaryUser = username; + + # Nix settings + nix.settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + }; + + # Homebrew — declaratively manage GUI / cask applications + homebrew = { + enable = true; + prefix = "${homeDirectory}/.homebrew"; + + onActivation = { + autoUpdate = true; + upgrade = true; + cleanup = "uninstall"; + }; + + global = { + autoUpdate = false; + }; + + caskArgs = { + appdir = "~/Applications"; + fontdir = "~/Library/Fonts"; + no_binaries = true; + require_sha = true; + }; + + brews = [ + "gcloud-cli" + ]; + + casks = [ + "1password" + "anki" + "firefox" + "font-hack" + "font-hack-nerd-font" + "font-jetbrains-mono" + "ghostty" + "google-chrome" + "gpg-suite" + "imageoptim" + "iterm2" + "keybase" + "little-snitch" + "micro-snitch" + "omnigraffle" + "spotify" + "tor-browser" + "vagrant" + "virtualbox" + "vlc" + ]; + }; + + # Disable the macOS default ssh-agent + system.activationScripts.postActivation.text = '' + echo "Disabling macOS default ssh-agent..." + launchctl disable "user/$(id -u ${username})/com.openssh.ssh-agent" 2>/dev/null || true + ''; + + system.stateVersion = 5; +} diff --git a/flake.nix b/flake.nix index 1dec024..c04cb2d 100644 --- a/flake.nix +++ b/flake.nix @@ -1,9 +1,14 @@ { - description = "bdossantos dotfiles — managed with home-manager"; + description = "bdossantos dotfiles — managed with nix-darwin and home-manager"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + nix-darwin = { + url = "github:nix-community/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; @@ -11,15 +16,38 @@ }; outputs = - { nixpkgs, home-manager, ... }: + { + nixpkgs, + nix-darwin, + home-manager, + ... + }: let - supportedSystems = [ + supportedDarwinSystems = [ "aarch64-darwin" "x86_64-darwin" + ]; + + supportedLinuxSystems = [ "x86_64-linux" "aarch64-linux" ]; + mkDarwinConfiguration = + system: + nix-darwin.lib.darwinSystem { + inherit system; + modules = [ + ./darwin.nix + home-manager.darwinModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = false; + home-manager.users.bdossantos = import ./home.nix; + } + ]; + }; + mkHomeConfiguration = system: home-manager.lib.homeManagerConfiguration { @@ -28,11 +56,18 @@ }; in { + darwinConfigurations = builtins.listToAttrs ( + map (system: { + name = system; + value = mkDarwinConfiguration system; + }) supportedDarwinSystems + ); + homeConfigurations = builtins.listToAttrs ( map (system: { name = system; value = mkHomeConfiguration system; - }) supportedSystems + }) supportedLinuxSystems ); }; } diff --git a/home.nix b/home.nix index e24ff76..c973b17 100644 --- a/home.nix +++ b/home.nix @@ -94,6 +94,7 @@ in # --- ruby --- chruby ruby-build + ruby-install # --- linters & formatters --- hadolint From f32b0210c8195957425dbc3f99c7184c7276b7aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:36:13 +0000 Subject: [PATCH 4/5] fix: address code review feedback - Fix OS variable expansion in Makefile (use $(shell uname)) - Rename CHRUBY_SH to CHRUBY_DIR for clarity - Add comment explaining useUserPackages = false choice Agent-Logs-Url: https://github.com/bdossantos/dotfiles/sessions/2faa8162-bb22-410f-8c13-72ec4551de09 Co-authored-by: bdossantos <245284+bdossantos@users.noreply.github.com> --- .bashrc | 12 ++++++------ Makefile | 4 ++-- flake.nix | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.bashrc b/.bashrc index c2f2a2d..d11515c 100644 --- a/.bashrc +++ b/.bashrc @@ -79,21 +79,21 @@ fi PROMPT_COMMAND="history -a;$PROMPT_COMMAND" # chruby -CHRUBY_SH="" +CHRUBY_DIR="" if [ -f "${HOME}/.nix-profile/share/chruby/chruby.sh" ]; then - CHRUBY_SH="${HOME}/.nix-profile/share/chruby" + CHRUBY_DIR="${HOME}/.nix-profile/share/chruby" elif [ -f "${HOMEBREW_PREFIX}/share/chruby/chruby.sh" ]; then - CHRUBY_SH="${HOMEBREW_PREFIX}/share/chruby" + CHRUBY_DIR="${HOMEBREW_PREFIX}/share/chruby" fi -if [ -n "$CHRUBY_SH" ]; then +if [ -n "$CHRUBY_DIR" ]; then # shellcheck disable=SC2034 RUBIES=("${HOME}/.rubies/*") # shellcheck source=/dev/null - source "${CHRUBY_SH}/chruby.sh" + source "${CHRUBY_DIR}/chruby.sh" # shellcheck source=/dev/null - source "${CHRUBY_SH}/auto.sh" + source "${CHRUBY_DIR}/auto.sh" fi if command -v zoxide &>/dev/null; then diff --git a/Makefile b/Makefile index 20fbc05..c026383 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OS = "$(uname)" +OS := $(shell uname) SHELL := /usr/bin/env bash SYSTEM := $(shell uname -m)-$(shell uname -s | tr '[:upper:]' '[:lower:]') @@ -26,7 +26,7 @@ install: ## Install all the things @make install-dotfiles \ install-vundle \ install-nvim-deps - @if [[ $OS == 'Darwin' ]]; then \ + @if [[ "$(OS)" == 'Darwin' ]]; then \ make run-darwin; \ else \ make run-nix; \ diff --git a/flake.nix b/flake.nix index c04cb2d..6cde50f 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,8 @@ home-manager.darwinModules.home-manager { home-manager.useGlobalPkgs = true; + # Keep packages in ~/.nix-profile/bin for compatibility with + # the manually managed PATH in .profile home-manager.useUserPackages = false; home-manager.users.bdossantos = import ./home.nix; } From a021b7fbb07848a68448e680bc287f0c16d06eea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:38:44 +0000 Subject: [PATCH 5/5] fix(darwin): remove iterm2, keybase, omnigraffle, vagrant and virtualbox casks Agent-Logs-Url: https://github.com/bdossantos/dotfiles/sessions/8de49c1d-c36c-42f6-82aa-8955e1c79c24 Co-authored-by: bdossantos <245284+bdossantos@users.noreply.github.com> --- darwin.nix | 5 ----- 1 file changed, 5 deletions(-) diff --git a/darwin.nix b/darwin.nix index 9efad1c..afb5d74 100644 --- a/darwin.nix +++ b/darwin.nix @@ -52,15 +52,10 @@ in "google-chrome" "gpg-suite" "imageoptim" - "iterm2" - "keybase" "little-snitch" "micro-snitch" - "omnigraffle" "spotify" "tor-browser" - "vagrant" - "virtualbox" "vlc" ]; };