Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
Hello, im looking for some feedback on my nix-dawrin setup. This is the setup im currently using, its works, but the structure is cobbled together from multiple sources, and im not experienced eno...
#1: Initial revision
Feedback for a Nix-Darwin configuration
Hello, im looking for some feedback on my nix-dawrin setup. This is the setup im currently using, its works, but the structure is cobbled together from multiple sources, and im not experienced enough with nix to have a good feeling about best practices or correct usage of features. would love some feedback on how to improve and clean up / simplify the code; about language features im not using/missusing ; tips of where i could split up some code, or merge for a more concise code-structure. some point i know specifically already: * settings a default shell: i have settings for a default / login shell, but it doesn't seems to stick correctly. * split apps.nix, extract the config for yabai / skhd / homebrew to their own file, not sure if the preferred way is .nix files or external, 'native' config files that i import into nix * would like to move more GUI apps to homemanger, but my understanding is that MacOS doesn't handle installing GUI apps through the nix store very well. So right now ive opted to do GUI apps via homebrew, but it feels cleaner if i could get rid of homebrew completely, but maybe that is not possible. * any good good flake from other users that could benefit my setup would be great to hear about. If anything is unclear just let me know, and hopefully i can clarify, or provide some sort of answer at least. Link to github with current version: https://github.com/hesthub/nix-config/tree/337c091ba90af6d48b570b834e0c4552081806b1 flake.nix ``` { description = "Nix for macOS configuration"; nixConfig = { experimental-features = ["nix-command" "flakes"]; }; inputs = { nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-unstable"; home-manager = { url = "github:nix-community/home-manager/master"; inputs.nixpkgs.follows = "nixpkgs-darwin"; }; darwin = { url = "github:lnl7/nix-darwin"; inputs.nixpkgs.follows = "nixpkgs-darwin"; }; }; outputs = inputs @ { self, nixpkgs, darwin, home-manager, ... }: { darwinConfigurations.mbp = darwin.lib.darwinSystem { system = "x86_64-darwin"; modules = [ ./modules/nix-core.nix ./modules/system.nix ./modules/apps.nix ./modules/host-users.nix home-manager.darwinModules.home-manager { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; home-manager.extraSpecialArgs = inputs; home-manager.users.hest = import ./home; } ]; }; # nix code formater formatter.x86_64-darwin = nixpkgs.legacyPackages.x86_64-darwin.alejandra; }; } ``` Makefile ``` include .env export darwin: nix build .#darwinConfigurations.mbp.system \ --extra-experimental-features 'nix-command flakes' ./result/sw/bin/darwin-rebuild switch --impure --flake .#mbp bootstrap: # install nix curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install # install homebrew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ssh_keys: # Generate ssh keys ssh-keygen -t ed25519 -C "$$M_MAIL" -f ~/.ssh/$$M_ID -N $$PASSWORD ssh-keygen -t rsa -C "$$G_MAIL" -f ~/.ssh/$$G_ID -N $$PASSWORD ssh-keygen -t ed25519 -C "$$C_MAIL" -f ~/.ssh/$$C_ID -N $$PASSWORD # add keys to ssh-agent ssh-add --apple-use-keychain ~/.ssh/$$M_ID ssh-add --apple-use-keychain ~/.ssh/$$C_ID ssh-add --apple-use-keychain ~/.ssh/$$G_ID post-fix: # add fish to available shell if not already present if ! grep -qF "/etc/profiles/per-user/${M_USER}/bin/fish" /etc/shells; \ then echo "/etc/profiles/per-user/${M_USER}/bin/fish" | sudo tee -a /etc/shells; fi # add nix user bin to path if not already present if ! grep -qF "/etc/profiles/per-user/${M_USER}/bin" /etc/paths; \ then echo "/etc/profiles/per-user/${M_USER}/bin" | sudo tee -a /etc/paths; fi # copy iterm config to dynamic profiles if not already present if [[ ! -f /Users/${M_USER}/Library/Application\ Support/iTerm2/DynamicProfiles/iterm2.json ]];\ then cp ./dotfiles/iterm2.json /Users/${M_USER}/Library/Application\ Support/iTerm2/DynamicProfiles/; fi # rtx add plugins and install them rtx plugin add dotnet rtx plugin add azure-cli rtx install # enable shims support for IDEA IDEs ( they support asdf but not mise nativly) ln -s ~/.local/share/mise ~/.asdf # add complettion for mise in fish mise completion fish > ~/.config/fish/completions/mise.fish update: nix flake update history: nix profile history --profile /nix/var/nix/profiles/system gc: sudo nix profile wipe-history --profile /nix/var/nix/profiles/system --older-than 7d sudo nix store gc --debug fmt: nix fmt .PHONY: clean clean: rm -rf result ``` ## inside the dir /modules nix-core.nix ``` { pkgs, lib, ... }: { nix.settings.experimental-features = ["nix-command" "flakes"]; nixpkgs.config.allowUnfree = true; services.nix-daemon.enable = true; nix.package = pkgs.nix; programs.nix-index.enable = true; nix.gc = { automatic = lib.mkDefault true; options = lib.mkDefault "--delete-older-than 1w"; }; nix.settings.auto-optimise-store = true; } ``` host-users.nix ``` {...} @ args: let hostname = "mbp"; username = "hest"; in { networking.hostName = hostname; networking.computerName = hostname; system.defaults.smb.NetBIOSName = hostname; users.users."${username}" = { home = "/Users/${username}"; description = username; shell = "/run/current-system/sw/bin/fish"; }; nix.settings.trusted-users = [username]; } ``` apps.nix ``` {pkgs, ...}: { environment.systemPackages = with pkgs; [ neovim git yabai skhd ]; environment.variables.EDITOR = "nvim"; environment.loginShell = "/etc/profiles/per-user/hest/bin/fish"; services.skhd = { enable = true; skhdConfig = '' # toggle window split type alt - e : yabai -m window --toggle split # rotate tree alt - r : yabai -m space --rotate 90 # mirror tree y-axis alt - y : yabai -m space --mirror y-axis # mirror tree x-axis alt - x : yabai -m space --mirror x-axis # balance size of windows shift + alt - 0 : yabai -m space --balance # get name of curretn window shift + alt - v : yabai -m query --windows --window | jq | pbcopy # toggle window native fullscreen shift + alt - f : yabai -m window --toggle native-fullscreen # toggle window float shift + alt - t : yabai -m window --toggle float && yabai -m window --grid 4:4:1:1:2:2 # fast focus desktop alt+shift - 1 : yabai -m space --focus i alt+shift - 2 : yabai -m space --focus ii alt+shift - 3 : yabai -m space --focus iii alt+shift - 4 : yabai -m space --focus iv alt+shift - 5 : yabai -m space --focus v ctrl + alt - 1 : yabai -m window --space i; yabai -m space --focus i ctrl + alt - 2 : yabai -m window --space ii; yabai -m space --focus ii ctrl + alt - 3 : yabai -m window --space iii; yabai -m space --focus iii ctrl + alt - 4 : yabai -m window --space iv; yabai -m space --focus iv ctrl + alt - 5 : yabai -m window --space v; yabai -m space --focus v # toggle stack icons shift + alt - b : hs -c 'stackline.config:toggle("appearance.showIcons")' #reset layout ( undo stacking ) ctrl + alt - b: yabai -m space --layout bsp #stack windows cmd + ctrl - left : yabai -m window west --stack $(yabai -m query --windows --window | jq -r '.id') cmd + ctrl - down : yabai -m window south --stack $(yabai -m query --windows --window | jq -r '.id') cmd + ctrl - up : yabai -m window north --stack $(yabai -m query --windows --window | jq -r '.id') cmd + ctrl - right : yabai -m window east --stack $(yabai -m query --windows --window | jq -r '.id') # focus up/down stack ctrl - up : yabai -m window --focus stack.prev ctrl - down : yabai -m window --focus stack.next # shift windows and focus space ctrl + shift - left : yabai -m window --space prev; yabai -m space --focus prev ctrl + shift - right : yabai -m window --space next; yabai -m space --focus next ''; }; services.yabai = { enable = true; package = pkgs.yabai; enableScriptingAddition = true; config = { focus_follows_mouse = "autoraise"; window_placement = "second_child"; window_topmost = "off"; window_shadow = "on"; window_opacity = "off"; active_window_opacity = "1.0"; normal_window_opacity = "0.90"; window_border = "off"; window_border_width = "6"; active_window_border_color = "0xff775759"; normal_window_border_color = "0xff555555"; insert_feedback_color = "0xffd75f5f"; split_ratio = "0.50"; auto_balance = "off"; mouse_modifier = "fn"; mouse_action1 = "move"; mouse_action2 = "resize"; mouse_drop_action = "swap"; mouse_follows_focus = "on"; layout = "bsp"; top_padding = "10"; bottom_padding = "10"; left_padding = "10"; right_padding = "10"; window_gap = "06"; }; extraConfig = '' sudo yabai --load-sa yabai -m signal --add event=dock_did_restart action="sudo yabai --load-sa" # yabai -m space --create # yabai -m space --create # yabai -m space --create # yabai -m space --create yabai -m space 1 --label i yabai -m space 2 --label ii yabai -m space 3 --label iii yabai -m space 4 --label iv yabai -m space 5 --label v yabai -m rule --add app="^System Preferences$" manage=off yabai -m rule --add app="^Calculator$" manage=off yabai -m rule --add app="^JetBrains Toolbox$" manage=off yabai -m rule --add app="^Simulator$" manage=off ''; }; homebrew = { enable = true; onActivation = { autoUpdate = false; # 'zap': uninstalls all formulae(and related files) not listed here. # cleanup = "zap"; }; masApps = { Keynote = 409183694; # Xcode = 497799835; Amphetamine = 937984704; }; taps = [ #"homebrew/cask" "homebrew/cask-fonts" "homebrew/services" "homebrew/cask-versions" ]; brews = [ "mas" "tmux" "curl" # no not install curl via nixpkgs, it's not working well on macOS! ]; casks = [ "aldente" "bitwarden" "docker" "insomnia" "iterm2" "jetbrains-toolbox" "logitech-g-hub" "maccy" "microsoft-teams" "mqttx" "openlens" "postman" "raycast" "slack" "tailscale" "visual-studio-code" "warp" "zoom" ]; }; } ``` system.nix ``` {pkgs, ...}: ################################################################################### # # macOS's System configuration # # All the configuration options are documented here: # https://daiderd.com/nix-darwin/manual/index.html#sec-options # and see the source code of this project to get more undocumented options: # https://github.com/rgcr/m-cli # ################################################################################### { system = { # activationScripts are executed every time you boot the system or run `nixos-rebuild` / `darwin-rebuild`. activationScripts.postUserActivation.text = '' # activateSettings -u will reload the settings from the database and apply them to the current session, # so we do not need to logout and login again to make the changes take effect. /System/Library/PrivateFrameworks/SystemAdministration.framework/Resources/activateSettings -u ''; keyboard = { enableKeyMapping = true; # enable key mapping so that we can use `option` as `control` remapCapsLockToEscape = true; # remap caps lock to escape, useful for vim users swapLeftCommandAndLeftAlt = false; }; defaults = { dock = { autohide = true; show-recents = false; # disable recent apps static-only = true; }; finder = { _FXShowPosixPathInTitle = true; # show full path in finder title AppleShowAllExtensions = false; # show all file extensions FXEnableExtensionChangeWarning = false; # disable warning when changing file extension QuitMenuItem = true; # enable quit menu item ShowPathbar = true; # show path bar ShowStatusBar = true; # show status bar }; trackpad = { Clicking = true; # enable tap to click() TrackpadRightClick = true; # enable two finger right click TrackpadThreeFingerDrag = false; # enable three finger drag }; NSGlobalDomain = { "com.apple.swipescrolldirection" = false; # enable natural scrolling(default to true) "com.apple.sound.beep.feedback" = 0; # disable beep sound when pressing volume up/down key "com.apple.keyboard.fnState" = true; # enable function keys AppleInterfaceStyle = "Dark"; # dark mode AppleKeyboardUIMode = 3; # Mode 3 enables full keyboard control. ApplePressAndHoldEnabled = true; # enable press and hold InitialKeyRepeat = 15; # normal minimum is 15 (225 ms), maximum is 120 (1800 ms) # sets how fast it repeats once it starts. KeyRepeat = 3; # normal minimum is 2 (30 ms), maximum is 120 (1800 ms) NSAutomaticCapitalizationEnabled = false; # disable auto capitalization(自动大写) NSAutomaticDashSubstitutionEnabled = false; # disable auto dash substitution(智能破折号替换) NSAutomaticPeriodSubstitutionEnabled = false; # disable auto period substitution(智能句号替换) NSAutomaticQuoteSubstitutionEnabled = false; # disable auto quote substitution(智能引号替换) NSAutomaticSpellingCorrectionEnabled = false; # disable auto spelling correction(自动拼写检查) NSNavPanelExpandedStateForSaveMode = true; # expand save panel by default(保存文件时的路径选择/文件名输入页) NSNavPanelExpandedStateForSaveMode2 = true; _HIHideMenuBar = true; # hide menu bar }; # Customize settings that not supported by nix-darwin directly # see the source code of this project to get more undocumented options: # https://github.com/rgcr/m-cli # # All custom entries can be found by running `defaults read` command. # or `defaults read xxx` to read a specific domain. CustomUserPreferences = { ".GlobalPreferences" = { # automatically switch to a new space when switching to the application AppleSpacesSwitchOnActivate = true; }; NSGlobalDomain = { # Add a context menu item for showing the Web Inspector in web views WebKitDeveloperExtras = true; }; "com.apple.finder" = { ShowExternalHardDrivesOnDesktop = false; ShowHardDrivesOnDesktop = false; ShowMountedServersOnDesktop = true; ShowRemovableMediaOnDesktop = true; _FXSortFoldersFirst = true; # When performing a search, search the current folder by default FXDefaultSearchScope = "SCcf"; }; "com.apple.desktopservices" = { # Avoid creating .DS_Store files on network or USB volumes DSDontWriteNetworkStores = true; DSDontWriteUSBStores = true; }; "com.apple.screensaver" = { # Require password immediately after sleep or screen saver begins askForPassword = 1; askForPasswordDelay = 0; }; "com.apple.screencapture" = { location = "~/Desktop"; type = "png"; }; "com.apple.AdLib" = { allowApplePersonalizedAdvertising = false; }; "com.apple.ImageCapture".disableHotPlug = true; }; loginwindow = { GuestEnabled = false; # disable guest user SHOWFULLNAME = true; # show full name in login window }; }; }; # Add ability to used TouchID for sudo authentication security.pam.enableSudoTouchIdAuth = true; programs.fish.enable = true; environment.shells = [ pkgs.fish ]; fonts = { # use fonts specified by user rather than default ones fontDir.enable = true; fonts = with pkgs; [ material-design-icons font-awesome (nerdfonts.override { fonts = [ "FiraCode" "JetBrainsMono" ]; }) ]; }; } ``` ## Inside dir /home core.nix ``` {pkgs, ...}: { home.packages = with pkgs; [ jq diff-so-fancy bat btop #dust eza gotop wget lf tree tldr glow kubectl k9s jc dotnetPackages.Nuget unixtools.watch ]; programs = { neovim = { enable = true; defaultEditor = true; vimAlias = true; }; bat = { enable = true; config = { theme = "Nord"; style = "numbers,changes,header"; }; }; eza = { enable = true; git = true; icons = true; }; ssh = { enable = true; addKeysToAgent = "yes"; }; navi.enable = true; nushell.enable = true; ripgrep.enable = true; awscli.enable = true; }; } ``` default.nix ``` {...}: let username = "hest"; in { imports = [ ./core.nix ./mise.nix ./git.nix ./fish.nix ./k9s.nix ]; home = { username = "${username}"; homeDirectory = "/Users/${username}"; stateVersion = "23.05"; }; # might be dangerous on macos ? xdg.enable = true; xdg.configHome = "/Users/${username}/.config"; programs.home-manager.enable = true; } ``` fish.nix ``` let insultfunction = builtins.readFile ./fish-functions/insulter.fish; in { config, lib, pkgs, ... }: { home.packages = with pkgs; [ fish fzf grc fishPlugins.autopair fishPlugins.colored-man-pages fishPlugins.done fishPlugins.fzf fishPlugins.grc fishPlugins.sponge fishPlugins.z ]; programs.fish = { enable = true; shellInit = '' /Users/hest/.local/bin/mise activate fish --shims | source ''; interactiveShellInit = '' insulter /Users/hest/.local/bin/mise activate fish | source set fish_greeting # Disable greeting abbr -a -- .. "cd .." abbr -a -- ... "cd ../.." abbr -a -- .... "cd ../../.." abbr -a -- ..... "cd ../../../.." abbr -a -- - "cd -" ''; shellAbbrs = { k = "kubectl"; g = "git"; cat = "bat"; vi = "nvim"; vim = "nvim"; ls = "eza -1 -F --group-directories-first"; lsa = "eza -1 -F --group-directories-first -a"; ll = "eza -1 -F --group-directories-first -l --git"; lla = "eza -1 -F --group-directories-first -l -a --git"; lt = "eza -1 -F -T"; gitbt = "git log --graph --simplify-by-decoration --pretty=format:'%d' --all"; azlistdev = "az containerapp list --subscription ${builtins.getEnv "AZ_SUB_DEV"} --resource-group ${builtins.getEnv "AZ_RG_DEV"} | jq '.[] | \"\\(.properties.runningStatus) \\(.name)\"'"; azlistprod = "az containerapp list --subscription ${builtins.getEnv "AZ_SUB_PROD"} --resource-group ${builtins.getEnv "AZ_RG_PROD"} | jq '.[] | \"\\(.properties.runningStatus) \\(.name)\"'"; azdev = "az containerapp logs show --subscription ${builtins.getEnv "AZ_SUB_DEV"} --resource-group ${builtins.getEnv "AZ_RG_DEV"} --follow --format text -n"; azprod = "az containerapp logs show --subscription ${builtins.getEnv "AZ_SUB_PROD"} --resource-group ${builtins.getEnv "AZ_RG_PROD"} --follow --format text -n"; }; functions = { update_git = '' set back (pwd) for d in (find . -type d -name .git) cd "$d/.." pwd git pull cd $back end ''; gcp = '' git add . git commit -m "$argv" git push origin HEAD ''; insulter = insultfunction; gitignore = "curl -sL https://www.gitignore.io/api/$argv"; }; plugins = [ { name = "autopair"; src = pkgs.fishPlugins.autopair.src; } { name = "done"; src = pkgs.fishPlugins.done.src; } { name = "fzf"; src = pkgs.fishPlugins.fzf.src; } { name = "grc"; src = pkgs.fishPlugins.grc.src; } { name = "sponge"; src = pkgs.fishPlugins.sponge.src; } { name = "z"; src = pkgs.fishPlugins.z.src; } ]; }; } ``` git.nix ``` { config, lib, pkgs, ... }: { home.activation.removeExistingGitconfig = lib.hm.dag.entryBefore ["checkLinkTargets"] '' rm -f ~/.gitconfig ''; programs.git = { enable = true; lfs.enable = true; diff-so-fancy.enable = true; includes = [ { condition = "gitdir:${builtins.getEnv "M_DIR"}"; contents = { core.sshCommand = "ssh -i ~/.ssh/${builtins.getEnv "M_ID"}"; user = { name = "${builtins.getEnv "M_USER"}"; email = "${builtins.getEnv "M_MAIL"}"; }; }; } { condition = "gitdir:${builtins.getEnv "C_DIR"}"; contents = { core.sshCommand = "ssh -i ~/.ssh/${builtins.getEnv "C_ID"}"; user = { name = "${builtins.getEnv "C_USER"}"; email = "${builtins.getEnv "C_MAIL"}"; }; }; } { condition = "gitdir:${builtins.getEnv "G_DIR"}"; contents = { core.sshCommand = "ssh -i ~/.ssh/${builtins.getEnv "G_ID"}"; user = { name = "${builtins.getEnv "G_USER"}"; email = "${builtins.getEnv "G_MAIL"}"; }; }; } ]; extraConfig = { init.defaultBranch = "main"; push.autoSetupRemote = true; pull.rebase = true; }; aliases = { br = "branch"; co = "checkout"; st = "status"; ls = "log --pretty=format:\"%C(yellow)%h%Cred%d\\\\ %Creset%s%Cblue\\\\ [%cn]\" --decorate"; ll = "log --pretty=format:\"%C(yellow)%h%Cred%d\\\\ %Creset%s%Cblue\\\\ [%cn]\" --decorate --numstat"; lg = "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"; cm = "commit -m"; ca = "commit -am"; dc = "diff --cached"; amend = "commit --amend -m"; # aliases for submodule update = "submodule update --init --recursive"; foreach = "submodule foreach"; }; ignores = [ "!.vscode/extensions.json" "!.vscode/launch.json" "!.vscode/settings.json" "!.vscode/tasks.json" "!flake.lock" ".classpath" ".env" ".flattened-pom.xml" ".gradle/" ".idea/" ".mise.toml" ".project" ".rtx.toml" ".vscode/*" "*.code-workspace" "*.iml" "*.lock" "*.settings" "*.tgz" "bin/" "build/" "out/" ]; }; } ``` k9s.nix ``` { config, lib, pkgs, ... }: { programs.k9s = { enable = true; views = {}; skins = { skin = { k9s = { body = { fgColor = "#DADEE8"; bgColor = "default"; logoColor = "#B48EAD"; }; prompt = { fgColor = "#DADEE8"; bgColor = "#30343F"; suggestColor = "#D08770"; }; info = { fgColor = "#81A1C1"; sectionColor = "#DADEE8"; }; dialog = { fgColor = "#DADEE8"; bgColor = "default"; buttonFgColor = "#DADEE8"; buttonBgColor = "#B48EAD"; buttonFocusFgColor = "#EBCB8B"; buttonFocusBgColor = "#81A1C1"; labelFgColor = "#D08770"; fieldFgColor = "#DADEE8"; }; frame = { border = { fgColor = "#D9DEE8"; focusColor = "#383D4A"; }; menu = { fgColor = "#DADEE8"; keyColor = "#81A1C1"; numKeyColor = "#81A1C1"; }; crumbs = { fgColor = "#DADEE8"; bgColor = "#383D4A"; activeColor = "#383D4A"; }; status = { newColor = "#88C0D0"; modifyColor = "#B48EAD"; addColor = "#A3BE8C"; errorColor = "#BF616A"; highlightColor = "#D08770"; killColor = "#8891A7"; completedColor = "#8891A7"; }; title = { fgColor = "#DADEE8"; bgColor = "#383D4A"; highlightColor = "#D08770"; counterColor = "#B48EAD"; filterColor = "#81A1C1"; }; }; views = { charts = { bgColor = "default"; defaultDialColors = ["#B48EAD" "#BF616A"]; defaultChartColors = ["#B48EAD" "#BF616A"]; }; table = { fgColor = "#DADEE8"; bgColor = "default"; header = { fgColor = "#DADEE8"; bgColor = "default"; sorterColor = "#88C0D0"; }; }; xray = { fgColor = "#DADEE8"; bgColor = "default"; cursorColor = "#383D4A"; graphicColor = "#B48EAD"; showIcons = false; }; yaml = { keyColor = "#81A1C1"; colonColor = "#B48EAD"; valueColor = "#DADEE8"; }; logs = { fgColor = "#DADEE8"; bgColor = "default"; indicator = { fgColor = "#DADEE8"; bgColor = "#B48EAD"; toggleOnColor = "#B48EAD"; toggleOffColor = "#88C0D0"; }; }; help = { fgColor = "#DADEE8"; bgColor = "#30343F"; indicator = { fgColor = "#BF616A"; }; }; }; }; }; }; }; } ``` mise.nix ``` # wont install tools automaticly # mise activate fish | source # mise use --global go@1.21.4 { config, lib, pkgs, ... }: { programs.mise = { enable = true; globalConfig = { tools = { go = "latest"; java = ["openjdk-21"]; nodejs = ["lts" "18"]; dotnet = ["7" "8"]; }; settings = { verbose = false; experimental = false; }; }; }; } ``` ...