aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaiden Fey <kookie@spacekookie.de>2020-09-21 14:12:32 +0200
committerKatharina Fey <kookie@spacekookie.de>2020-09-21 14:12:32 +0200
commitf80843dd45d7acd563d0a5b014cec3a2ea686fc2 (patch)
tree87189d873d6f932d85f9c1a480462b37d96cd6a5
parente0800985dab8f8ebb4cebdfd7e361fd1fafdb2a7 (diff)
parent9b1b55ba0264a55add4b7b4e022bdc2832b531f6 (diff)
Merge commit '9b1b55ba0264a55add4b7b4e022bdc2832b531f6'
-rw-r--r--home-manager/.github/CODEOWNERS214
-rw-r--r--home-manager/.github/ISSUE_TEMPLATE.md23
-rw-r--r--home-manager/.github/PULL_REQUEST_TEMPLATE.md44
-rw-r--r--home-manager/.github/workflows/test.yml18
-rw-r--r--home-manager/.gitignore1
-rw-r--r--home-manager/.gitlab-ci.yml21
-rw-r--r--home-manager/.travis.yml13
l---------home-manager/CONTRIBUTING.adoc1
-rw-r--r--home-manager/CONTRIBUTING.md164
l---------home-manager/FAQ.adoc1
-rw-r--r--home-manager/FAQ.md151
-rw-r--r--home-manager/LICENSE2
-rw-r--r--home-manager/README.md111
-rw-r--r--home-manager/default.nix8
-rw-r--r--home-manager/doc/contributing.adoc255
-rw-r--r--home-manager/doc/default.nix10
-rw-r--r--home-manager/doc/faq.adoc171
-rw-r--r--home-manager/doc/installation.xml76
-rw-r--r--home-manager/doc/man-home-manager.xml14
-rw-r--r--home-manager/doc/man-pages.xml2
-rw-r--r--home-manager/doc/manual.xml14
-rw-r--r--home-manager/doc/release-notes/release-notes.adoc19
-rw-r--r--home-manager/doc/release-notes/release-notes.xml15
-rw-r--r--home-manager/doc/release-notes/rl-1903.adoc2
-rw-r--r--home-manager/doc/release-notes/rl-1909.adoc4
-rw-r--r--home-manager/doc/release-notes/rl-2003.adoc49
-rw-r--r--home-manager/doc/release-notes/rl-2009.adoc97
-rw-r--r--home-manager/doc/writing-modules.adoc187
-rw-r--r--home-manager/flake.nix23
-rwxr-xr-xhome-manager/format11
-rw-r--r--home-manager/home-manager/completion.bash2
-rw-r--r--home-manager/home-manager/home-manager124
-rw-r--r--home-manager/home-manager/home-manager.nix4
-rw-r--r--home-manager/home-manager/install.nix19
-rw-r--r--home-manager/modules/accounts/email.nix71
-rw-r--r--home-manager/modules/files.nix55
-rw-r--r--home-manager/modules/home-environment.nix124
-rwxr-xr-xhome-manager/modules/lib-bash/activation-init.sh38
-rw-r--r--home-manager/modules/lib/default.nix4
-rw-r--r--home-manager/modules/lib/file-type.nix30
-rw-r--r--home-manager/modules/lib/gvariant.nix156
-rw-r--r--home-manager/modules/lib/maintainers.nix44
-rw-r--r--home-manager/modules/lib/types-dag.nix63
-rw-r--r--home-manager/modules/lib/types.nix78
-rw-r--r--home-manager/modules/misc/dconf.nix25
-rw-r--r--home-manager/modules/misc/debug.nix26
-rw-r--r--home-manager/modules/misc/gtk.nix146
-rw-r--r--home-manager/modules/misc/news.nix357
-rw-r--r--home-manager/modules/misc/nixpkgs.nix4
-rw-r--r--home-manager/modules/misc/numlock.nix2
-rw-r--r--home-manager/modules/misc/tmpfiles.nix49
-rw-r--r--home-manager/modules/misc/version.nix2
-rw-r--r--home-manager/modules/misc/vte.nix51
-rw-r--r--home-manager/modules/misc/xdg-mime-apps.nix2
-rw-r--r--home-manager/modules/misc/xdg-mime.nix10
-rw-r--r--home-manager/modules/misc/xdg-user-dirs.nix27
-rw-r--r--home-manager/modules/misc/xdg.nix10
-rw-r--r--home-manager/modules/modules.nix60
-rw-r--r--home-manager/modules/programs/abook.nix40
-rw-r--r--home-manager/modules/programs/alacritty.nix9
-rw-r--r--home-manager/modules/programs/alot.nix342
-rw-r--r--home-manager/modules/programs/aria2.nix61
-rw-r--r--home-manager/modules/programs/astroid.nix4
-rw-r--r--home-manager/modules/programs/autorandr.nix39
-rw-r--r--home-manager/modules/programs/bash.nix11
-rw-r--r--home-manager/modules/programs/bat.nix29
-rw-r--r--home-manager/modules/programs/broot.nix6
-rw-r--r--home-manager/modules/programs/dircolors.nix223
-rw-r--r--home-manager/modules/programs/direnv.nix16
-rw-r--r--home-manager/modules/programs/eclipse.nix12
-rw-r--r--home-manager/modules/programs/emacs.nix16
-rw-r--r--home-manager/modules/programs/firefox.nix112
-rw-r--r--home-manager/modules/programs/fish.nix497
-rw-r--r--home-manager/modules/programs/fzf.nix12
-rw-r--r--home-manager/modules/programs/getmail.nix6
-rw-r--r--home-manager/modules/programs/git.nix63
-rw-r--r--home-manager/modules/programs/gnome-terminal.nix42
-rw-r--r--home-manager/modules/programs/go.nix16
-rw-r--r--home-manager/modules/programs/htop.nix46
-rw-r--r--home-manager/modules/programs/i3status.nix208
-rw-r--r--home-manager/modules/programs/info.nix78
-rw-r--r--home-manager/modules/programs/kakoune.nix70
-rw-r--r--home-manager/modules/programs/kitty.nix91
-rw-r--r--home-manager/modules/programs/lf.nix219
-rw-r--r--home-manager/modules/programs/lieer-accounts.nix69
-rw-r--r--home-manager/modules/programs/lieer.nix93
-rw-r--r--home-manager/modules/programs/man.nix66
-rw-r--r--home-manager/modules/programs/mbsync.nix4
-rw-r--r--home-manager/modules/programs/mcfly.nix79
-rw-r--r--home-manager/modules/programs/mpv.nix2
-rw-r--r--home-manager/modules/programs/msmtp.nix4
-rw-r--r--home-manager/modules/programs/ncmpcpp.nix135
-rw-r--r--home-manager/modules/programs/ne.nix95
-rw-r--r--home-manager/modules/programs/neomutt-accounts.nix18
-rw-r--r--home-manager/modules/programs/neomutt.nix36
-rw-r--r--home-manager/modules/programs/neovim.nix16
-rw-r--r--home-manager/modules/programs/newsboat.nix6
-rw-r--r--home-manager/modules/programs/notmuch.nix4
-rw-r--r--home-manager/modules/programs/nushell.nix68
-rw-r--r--home-manager/modules/programs/offlineimap.nix5
-rw-r--r--home-manager/modules/programs/powerline-go.nix123
-rw-r--r--home-manager/modules/programs/qutebrowser.nix268
-rw-r--r--home-manager/modules/programs/rofi.nix13
-rw-r--r--home-manager/modules/programs/ssh.nix55
-rw-r--r--home-manager/modules/programs/starship.nix23
-rw-r--r--home-manager/modules/programs/termite.nix2
-rw-r--r--home-manager/modules/programs/tmux.nix98
-rw-r--r--home-manager/modules/programs/vim.nix2
-rw-r--r--home-manager/modules/programs/vscode.nix66
-rw-r--r--home-manager/modules/programs/waybar.nix363
-rw-r--r--home-manager/modules/programs/zoxide.nix79
-rw-r--r--home-manager/modules/programs/zplug.nix60
-rw-r--r--home-manager/modules/programs/zsh.nix54
-rw-r--r--home-manager/modules/services/clipmenu.nix43
-rw-r--r--home-manager/modules/services/compton.nix320
-rw-r--r--home-manager/modules/services/dropbox.nix77
-rw-r--r--home-manager/modules/services/dunst.nix4
-rw-r--r--home-manager/modules/services/emacs.nix152
-rw-r--r--home-manager/modules/services/fluidsynth.nix57
-rw-r--r--home-manager/modules/services/gnome-keyring.nix2
-rw-r--r--home-manager/modules/services/kanshi.nix194
-rw-r--r--home-manager/modules/services/keynav.nix29
-rw-r--r--home-manager/modules/services/lieer-accounts.nix25
-rw-r--r--home-manager/modules/services/lieer.nix66
-rw-r--r--home-manager/modules/services/lorri.nix17
-rw-r--r--home-manager/modules/services/mako.nix316
-rw-r--r--home-manager/modules/services/mpd.nix2
-rw-r--r--home-manager/modules/services/picom.nix311
-rw-r--r--home-manager/modules/services/pulseeffects.nix55
-rw-r--r--home-manager/modules/services/screen-locker.nix13
-rw-r--r--home-manager/modules/services/spotifyd.nix18
-rw-r--r--home-manager/modules/services/status-notifier-watcher.nix9
-rw-r--r--home-manager/modules/services/syncthing.nix2
-rw-r--r--home-manager/modules/services/udiskie.nix4
-rw-r--r--home-manager/modules/services/unison.nix2
-rw-r--r--home-manager/modules/services/window-managers/i3-sway/i3.nix257
-rw-r--r--home-manager/modules/services/window-managers/i3-sway/lib/functions.nix127
-rw-r--r--home-manager/modules/services/window-managers/i3-sway/lib/options.nix763
-rw-r--r--home-manager/modules/services/window-managers/i3-sway/sway.nix416
-rw-r--r--home-manager/modules/services/window-managers/i3.nix856
-rw-r--r--home-manager/modules/services/xscreensaver.nix4
-rw-r--r--home-manager/modules/systemd-activate.rb95
-rw-r--r--home-manager/modules/systemd.nix4
-rw-r--r--home-manager/modules/targets/darwin.nix16
-rw-r--r--home-manager/modules/targets/generic-linux.nix52
-rw-r--r--home-manager/modules/xresources.nix31
-rw-r--r--home-manager/nix-darwin/default.nix38
-rw-r--r--home-manager/nixos/default.nix13
-rw-r--r--home-manager/tests/default.nix54
-rw-r--r--home-manager/tests/lib/types/dag-submodule.nix43
-rw-r--r--home-manager/tests/lib/types/default.nix3
-rw-r--r--home-manager/tests/lib/types/gvariant-merge.nix62
-rw-r--r--home-manager/tests/meta/default.nix1
-rw-r--r--home-manager/tests/meta/formatting.nix27
-rw-r--r--home-manager/tests/modules/accounts/email-test-accounts.nix1
-rw-r--r--home-manager/tests/modules/files/default.nix2
-rw-r--r--home-manager/tests/modules/files/out-of-store-symlink.nix29
-rw-r--r--home-manager/tests/modules/files/target-with-shellvar.nix15
-rw-r--r--home-manager/tests/modules/home-environment/session-variables-expected.txt3
-rw-r--r--home-manager/tests/modules/misc/debug/default.nix25
-rw-r--r--home-manager/tests/modules/misc/fontconfig/default.nix19
-rw-r--r--home-manager/tests/modules/misc/numlock/default.nix1
-rw-r--r--home-manager/tests/modules/misc/numlock/numlock.nix18
-rw-r--r--home-manager/tests/modules/misc/xsession/basic-xprofile-expected.txt2
-rw-r--r--home-manager/tests/modules/misc/xsession/basic.nix2
-rw-r--r--home-manager/tests/modules/misc/xsession/keyboard-without-layout.nix2
-rw-r--r--home-manager/tests/modules/programs/abook/default.nix4
-rw-r--r--home-manager/tests/modules/programs/abook/no-settings.nix16
-rw-r--r--home-manager/tests/modules/programs/abook/with-settings.cfg21
-rw-r--r--home-manager/tests/modules/programs/abook/with-settings.nix39
-rw-r--r--home-manager/tests/modules/programs/alacritty/example-settings.nix5
-rw-r--r--home-manager/tests/modules/programs/alot/alot-expected.conf37
-rw-r--r--home-manager/tests/modules/programs/alot/alot.nix36
-rw-r--r--home-manager/tests/modules/programs/alot/default.nix1
-rw-r--r--home-manager/tests/modules/programs/aria2/default.nix1
-rw-r--r--home-manager/tests/modules/programs/aria2/settings.nix41
-rw-r--r--home-manager/tests/modules/programs/autorandr/basic-configuration.conf10
-rw-r--r--home-manager/tests/modules/programs/autorandr/basic-configuration.nix48
-rw-r--r--home-manager/tests/modules/programs/autorandr/default.nix1
-rw-r--r--home-manager/tests/modules/programs/bash/session-variables-expected.txt2
-rw-r--r--home-manager/tests/modules/programs/bash/session-variables.nix2
-rw-r--r--home-manager/tests/modules/programs/dircolors/default.nix1
-rw-r--r--home-manager/tests/modules/programs/dircolors/settings-expected.conf133
-rw-r--r--home-manager/tests/modules/programs/dircolors/settings.nix27
-rw-r--r--home-manager/tests/modules/programs/direnv/bash.nix17
-rw-r--r--home-manager/tests/modules/programs/direnv/default.nix6
-rw-r--r--home-manager/tests/modules/programs/direnv/nix-direnv.nix18
-rw-r--r--home-manager/tests/modules/programs/direnv/stdlib-and-nix-direnv.nix23
-rw-r--r--home-manager/tests/modules/programs/direnv/stdlib.nix19
-rw-r--r--home-manager/tests/modules/programs/fish/default.nix5
-rw-r--r--home-manager/tests/modules/programs/fish/functions.nix48
-rw-r--r--home-manager/tests/modules/programs/fish/no-functions.nix22
-rw-r--r--home-manager/tests/modules/programs/fish/plugins.nix60
-rw-r--r--home-manager/tests/modules/programs/getmail/getmail.nix3
-rw-r--r--home-manager/tests/modules/programs/git/git-expected-include.conf3
-rw-r--r--home-manager/tests/modules/programs/git/git-expected.conf58
-rw-r--r--home-manager/tests/modules/programs/git/git-with-email-expected.conf20
-rw-r--r--home-manager/tests/modules/programs/git/git-with-email.nix2
-rw-r--r--home-manager/tests/modules/programs/git/git-with-str-extra-config-expected.conf4
-rw-r--r--home-manager/tests/modules/programs/git/git.nix19
-rw-r--r--home-manager/tests/modules/programs/i3status/default.nix4
-rw-r--r--home-manager/tests/modules/programs/i3status/with-custom.nix67
-rw-r--r--home-manager/tests/modules/programs/i3status/with-default.nix73
-rw-r--r--home-manager/tests/modules/programs/kakoune/default.nix7
-rw-r--r--home-manager/tests/modules/programs/kakoune/no-plugins.nix13
-rw-r--r--home-manager/tests/modules/programs/kakoune/use-plugins.nix18
-rw-r--r--home-manager/tests/modules/programs/kakoune/whitespace-highlighter-corner-cases.nix25
-rw-r--r--home-manager/tests/modules/programs/kakoune/whitespace-highlighter.nix25
-rw-r--r--home-manager/tests/modules/programs/lf/all-options.nix86
-rw-r--r--home-manager/tests/modules/programs/lf/default.nix5
-rw-r--r--home-manager/tests/modules/programs/lf/minimal-options.nix18
-rw-r--r--home-manager/tests/modules/programs/lf/no-pv-keybind.nix43
-rw-r--r--home-manager/tests/modules/programs/lieer/default.nix1
-rw-r--r--home-manager/tests/modules/programs/lieer/lieer-expected.json1
-rw-r--r--home-manager/tests/modules/programs/lieer/lieer.nix23
-rw-r--r--home-manager/tests/modules/programs/man/apropos.nix22
-rw-r--r--home-manager/tests/modules/programs/man/default.nix4
-rw-r--r--home-manager/tests/modules/programs/man/no-manpath.nix13
-rw-r--r--home-manager/tests/modules/programs/mbsync/mbsync.nix3
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp-linux/default.nix1
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config-expected-config1
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config.nix25
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp/default.nix4
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-empty-settings.nix16
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-bindings16
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-config4
-rw-r--r--home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings.nix60
-rw-r--r--home-manager/tests/modules/programs/ne/default.nix4
-rw-r--r--home-manager/tests/modules/programs/ne/defprefs.nix36
-rw-r--r--home-manager/tests/modules/programs/ne/passthroughs.nix73
-rw-r--r--home-manager/tests/modules/programs/neomutt/default.nix5
-rw-r--r--home-manager/tests/modules/programs/neomutt/hm-example.com-msmtp-expected.conf33
-rw-r--r--home-manager/tests/modules/programs/neomutt/neomutt-with-msmtp.nix39
-rw-r--r--home-manager/tests/modules/programs/neomutt/neomutt.nix5
-rw-r--r--home-manager/tests/modules/programs/newsboat/default.nix5
-rw-r--r--home-manager/tests/modules/programs/newsboat/newsboat-basics-2003.nix35
-rw-r--r--home-manager/tests/modules/programs/newsboat/newsboat-basics-urls-2003.txt3
-rw-r--r--home-manager/tests/modules/programs/nushell/default.nix1
-rw-r--r--home-manager/tests/modules/programs/nushell/settings-expected.toml5
-rw-r--r--home-manager/tests/modules/programs/nushell/settings.nix34
-rw-r--r--home-manager/tests/modules/programs/powerline-go/default.nix1
-rw-r--r--home-manager/tests/modules/programs/powerline-go/standard.nix28
-rw-r--r--home-manager/tests/modules/programs/qutebrowser/default.nix4
-rw-r--r--home-manager/tests/modules/programs/qutebrowser/keybindings.nix39
-rw-r--r--home-manager/tests/modules/programs/qutebrowser/settings.nix48
-rw-r--r--home-manager/tests/modules/programs/rofi/assert-on-both-theme-and-colors.nix5
-rw-r--r--home-manager/tests/modules/programs/ssh/default-config-expected.conf1
-rw-r--r--home-manager/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf1
-rw-r--r--home-manager/tests/modules/programs/ssh/match-blocks-attrs-expected.conf5
-rw-r--r--home-manager/tests/modules/programs/ssh/match-blocks-attrs.nix3
-rw-r--r--home-manager/tests/modules/programs/starship/default.nix1
-rw-r--r--home-manager/tests/modules/programs/starship/settings-expected.toml27
-rw-r--r--home-manager/tests/modules/programs/starship/settings.nix49
-rw-r--r--home-manager/tests/modules/programs/texlive/texlive-minimal.nix18
-rw-r--r--home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.conf1
-rw-r--r--home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.nix4
-rw-r--r--home-manager/tests/modules/programs/tmux/emacs-with-plugins.conf3
-rw-r--r--home-manager/tests/modules/programs/tmux/hm-session-vars.sh5
-rw-r--r--home-manager/tests/modules/programs/tmux/secure-socket-enabled.nix3
-rw-r--r--home-manager/tests/modules/programs/tmux/vi-all-true.conf1
-rw-r--r--home-manager/tests/modules/programs/vscode/default.nix1
-rw-r--r--home-manager/tests/modules/programs/vscode/keybindings.nix53
-rw-r--r--home-manager/tests/modules/programs/waybar/broken-settings.nix80
-rw-r--r--home-manager/tests/modules/programs/waybar/default.nix8
-rw-r--r--home-manager/tests/modules/programs/waybar/settings-complex-expected.json46
-rw-r--r--home-manager/tests/modules/programs/waybar/settings-complex.nix59
-rw-r--r--home-manager/tests/modules/programs/waybar/styling-expected.css23
-rw-r--r--home-manager/tests/modules/programs/waybar/styling.nix46
-rw-r--r--home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix24
-rw-r--r--home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.service16
-rw-r--r--home-manager/tests/modules/programs/zplug/default.nix1
-rw-r--r--home-manager/tests/modules/programs/zplug/modules.nix50
-rw-r--r--home-manager/tests/modules/services/dropbox/basic-configuration.nix25
-rw-r--r--home-manager/tests/modules/services/dropbox/default.nix1
-rw-r--r--home-manager/tests/modules/services/emacs/default.nix5
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-emacsclient.desktop12
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-service-emacs.service12
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-service.nix37
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.service9
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.socket12
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-26.nix40
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.service9
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.socket12
-rw-r--r--home-manager/tests/modules/services/emacs/emacs-socket-27.nix42
-rw-r--r--home-manager/tests/modules/services/fluidsynth/default.nix1
-rw-r--r--home-manager/tests/modules/services/fluidsynth/service.nix24
-rw-r--r--home-manager/tests/modules/services/kanshi/basic-configuration.conf15
-rw-r--r--home-manager/tests/modules/services/kanshi/basic-configuration.nix52
-rw-r--r--home-manager/tests/modules/services/kanshi/default.nix1
-rw-r--r--home-manager/tests/modules/services/lieer/default.nix1
-rw-r--r--home-manager/tests/modules/services/lieer/lieer-service-expected.service8
-rw-r--r--home-manager/tests/modules/services/lieer/lieer-service-expected.timer9
-rw-r--r--home-manager/tests/modules/services/lieer/lieer-service.nix34
-rw-r--r--home-manager/tests/modules/services/polybar/basic-configuration.conf21
-rw-r--r--home-manager/tests/modules/services/polybar/basic-configuration.nix48
-rw-r--r--home-manager/tests/modules/services/polybar/default.nix1
-rw-r--r--home-manager/tests/modules/services/sxhkd/configuration.nix7
-rw-r--r--home-manager/tests/modules/services/sxhkd/service.nix2
-rw-r--r--home-manager/tests/modules/services/window-managers/i3/default.nix5
-rw-r--r--home-manager/tests/modules/services/window-managers/i3/i3-followmouse-expected.conf105
-rw-r--r--home-manager/tests/modules/services/window-managers/i3/i3-followmouse.nix29
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/default.nix6
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-default.conf117
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-default.nix33
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-followmouse-expected.conf94
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy-expected.conf94
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy.nix37
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-followmouse.nix37
-rw-r--r--home-manager/tests/modules/services/window-managers/sway/sway-post-2003.nix35
-rw-r--r--home-manager/tests/modules/systemd/services.nix2
-rw-r--r--home-manager/tests/modules/systemd/session-variables.nix2
-rw-r--r--home-manager/tests/modules/systemd/timers.nix4
-rw-r--r--home-manager/tests/modules/targets-darwin/darwin.nix20
-rw-r--r--home-manager/tests/modules/targets-darwin/default.nix5
-rw-r--r--home-manager/tests/modules/targets-linux/default.nix1
-rw-r--r--home-manager/tests/modules/targets-linux/generic-linux.nix22
-rw-r--r--home-manager/tests/modules/xresources/default.nix5
-rw-r--r--home-manager/tests/modules/xresources/empty.nix13
318 files changed, 12918 insertions, 2627 deletions
diff --git a/home-manager/.github/CODEOWNERS b/home-manager/.github/CODEOWNERS
new file mode 100644
index 00000000000..8f6b504af0c
--- /dev/null
+++ b/home-manager/.github/CODEOWNERS
@@ -0,0 +1,214 @@
+* @rycee
+
+/flake.nix @bqv @kisik21
+
+/modules/home-environment.nix @rycee
+
+/modules/misc/dconf.nix @gnidorah @rycee
+
+/modules/misc/fontconfig.nix @rycee
+/tests/modules/misc/fontconfig @rycee
+
+/modules/misc/gtk.nix @rycee
+
+/modules/misc/news.nix @rycee
+
+/modules/misc/numlock.nix @evanjs
+/tests/modules/misc/numlock @evanjs
+
+/modules/misc/pam.nix @rycee
+/tests/modules/misc/pam @rycee
+
+/modules/misc/qt.nix @rycee
+
+/modules/misc/submodule-support.nix @rycee
+
+/modules/misc/tmpfiles.nix @dawidsowa
+
+/modules/misc/vte.nix @rycee
+
+/modules/misc/xdg-mime-apps.nix @pacien
+
+/modules/misc/xdg-user-dirs.nix @pacien
+
+/modules/programs/aria2.nix @JustinLovinger
+
+/modules/programs/autorandr.nix @uvNikita
+
+/modules/programs/bash.nix @rycee
+
+/modules/programs/bat.nix @marsam
+
+/modules/programs/beets.nix @rycee
+
+/modules/programs/broot.nix @aheaume
+
+/modules/programs/dircolors.nix @JustinLovinger
+
+/modules/programs/direnv.nix @rycee
+
+/modules/programs/eclipse.nix @rycee
+
+/modules/programs/emacs.nix @rycee
+
+/modules/programs/firefox.nix @rycee
+
+/modules/programs/git.nix @rycee
+
+/modules/programs/gnome-terminal.nix @rycee
+
+/modules/programs/go.nix @rvolosatovs
+
+/modules/programs/home-manager.nix @rycee
+
+/modules/programs/i3status.nix @JustinLovinger
+
+/modules/programs/keychain.nix @marsam
+
+/modules/programs/lesspipe.nix @rycee
+
+/modules/programs/lf.nix @owm111
+/tests/modules/programs/lf @owm111
+
+/modules/programs/lieer.nix @tadfisher
+
+/modules/programs/lsd.nix @marsam
+
+/modules/programs/matplotlib.nix @rprospero
+
+/modules/programs/mcfly.nix @marsam
+
+/modules/programs/mpv.nix @tadeokondrak
+
+/modules/programs/ncmpcpp.nix @olmokramer
+/tests/modules/programs/ncmpcpp @olmokramer
+/tests/modules/programs/ncmpcpp-linux @olmokramer
+
+/modules/programs/ne.nix @cwyc
+/tests/modules/programs/ne @cwyc
+
+/modules/programs/noti.nix @marsam
+
+/modules/programs/nushell.nix @Philipp-M
+/tests/modules/programs/nushell @Philipp-M
+
+/modules/programs/obs-studio.nix @adisbladis
+
+/modules/programs/opam.nix @marsam
+
+/modules/programs/openssh.nix @rycee
+
+/modules/programs/password-store.nix @pacien
+
+/modules/programs/pazi.nix @marsam
+
+/modules/programs/pidgin.nix @rycee
+
+/modules/programs/powerline-go.nix @DamienCassou
+
+/modules/programs/rtorrent.nix @marsam
+
+/modules/programs/ssh.nix @rycee
+
+/modules/programs/starship.nix @marsam
+
+/modules/programs/texlive.nix @rycee
+
+/modules/programs/waybar.nix @berbiche
+/tests/modules/programs/waybar @berbiche
+
+/modules/programs/z-lua.nix @marsam
+
+/modules/programs/zathura.nix @rprospero
+
+/modules/programs/zoxide.nix @marsam
+
+/modules/services/cbatticon.nix @pmiddend
+
+/modules/services/clipmenu.nix @DamienCassou
+
+/modules/services/dropbox.nix @eyJhb
+/tests/modules/services/dropbox @eyJhb
+
+/modules/services/dunst.nix @rycee
+
+/modules/services/emacs.nix @tadfisher
+
+/modules/services/flameshot.nix @moredhel
+
+/modules/services/fluidsynth.nix @Valodim
+
+/modules/services/gnome-keyring.nix @rycee
+
+/modules/services/gpg-agent.nix @rycee
+
+/modules/services/grobi.nix @mbrgm
+
+/modules/services/hound.nix @adisbladis
+
+/modules/services/imapnotify.nix @nickhu
+
+/modules/services/kanshi.nix @nurelin
+/tests/modules/services/kanshi @nurelin
+
+/modules/services/kdeconnect.nix @adisbladis
+
+/modules/services/keepassx.nix @rycee
+
+/modules/services/lieer.nix @tadfisher
+
+/modules/services/lorri.nix @Gerschtli
+
+/modules/services/mako.nix @onny
+
+/modules/services/mbsync.nix @pjones
+
+/modules/services/mpdris2.nix @pjones
+
+/modules/services/muchsync.nix @pacien
+
+/modules/services/network-manager-applet.nix @rycee
+
+/modules/services/parcellite.nix @gleber
+
+/modules/services/password-store-sync.nix @pacien
+
+/modules/services/pasystray.nix @pltanton
+
+/modules/services/pulseeffects.nix @jonringer
+
+/modules/services/random-background.nix @rycee
+
+/modules/services/redshift.nix @rycee
+
+/modules/services/status-notifier-watcher.nix @pltanton
+
+/modules/services/syncthing.nix @rycee
+
+/modules/services/taffybar.nix @rycee
+
+/modules/services/tahoe-lafs.nix @rycee
+
+/modules/services/taskwarrior-sync.nix @minijackson @pacien
+
+/modules/services/udiskie.nix @rycee
+
+/modules/services/unison.nix @pacien
+
+/modules/services/window-managers/i3-sway/sway.nix @alexarice
+
+/modules/services/xcape.nix @nickhu
+
+/modules/services/xembed-sni-proxy.nix @rycee
+
+/modules/services/xscreensaver.nix @rycee
+
+/modules/services/xsuspender.nix @offlinehacker
+
+/modules/systemd.nix @rycee
+
+/modules/xcursor.nix @league
+
+/modules/xresources.nix @rycee
+
+/modules/xsession.nix @rycee
diff --git a/home-manager/.github/ISSUE_TEMPLATE.md b/home-manager/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000000..75c91499a58
--- /dev/null
+++ b/home-manager/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,23 @@
+### Issue description
+
+<!--
+Please describe the issue. For support and help please use the IRC
+channel #home-manager @ freenode.net instead.
+-->
+
+### Meta
+
+#### Maintainer CC
+
+<!--
+Please @ people who are in the `meta.maintainers` list of the
+offending module. If in doubt, check `git blame` for whoever last
+touched something.
+-->
+
+#### Technical details
+
+<!--
+Please run `nix-shell -p nix-info --run "nix-info -m"` and paste the
+result.
+-->
diff --git a/home-manager/.github/PULL_REQUEST_TEMPLATE.md b/home-manager/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..eb6eca76250
--- /dev/null
+++ b/home-manager/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,44 @@
+### Description
+
+<!--
+
+Please provide a brief description of your change.
+
+-->
+
+### Checklist
+
+<!--
+
+Please go through the following checklist before opening a non-WIP
+pull-request.
+
+Also make sure to read the guidelines found at
+
+ https://github.com/rycee/home-manager/blob/master/doc/contributing.adoc#sec-guidelines
+
+-->
+
+- [ ] Change is backwards compatible.
+
+- [ ] Code formatted with `./format`.
+
+- [ ] Code tested through `nix-shell --pure tests -A run.all`.
+
+- [ ] Test cases updated/added. See [example](https://github.com/rycee/home-manager/commit/f3fbb50b68df20da47f9b0def5607857fcc0d021#diff-b61a6d542f9036550ba9c401c80f00ef).
+
+- [ ] Commit messages are formatted like
+
+ ```
+ {component}: {description}
+
+ {long description}
+ ```
+
+ See [CONTRIBUTING](https://github.com/rycee/home-manager/blob/master/doc/contributing.adoc#sec-commit-style) for more information and [recent commit messages](https://github.com/rycee/home-manager/commits/master) for examples.
+
+- If this PR adds a new module
+
+ - [ ] Added myself as module maintainer. See [example](https://github.com/rycee/home-manager/blob/068ff76a10e95820f886ac46957edcff4e44621d/modules/programs/lesspipe.nix#L6).
+
+ - [ ] Added myself and the module files to `.github/CODEOWNERS`.
diff --git a/home-manager/.github/workflows/test.yml b/home-manager/.github/workflows/test.yml
new file mode 100644
index 00000000000..12be93e2973
--- /dev/null
+++ b/home-manager/.github/workflows/test.yml
@@ -0,0 +1,18 @@
+name: Test
+on:
+ pull_request:
+ schedule:
+ - cron: "30 2 * * *"
+jobs:
+ tests:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v2
+ - uses: cachix/install-nix-action@v10
+ with:
+ nix_path: nixpkgs=channel:nixos-unstable
+ - run: nix-shell . -A install
+ - run: nix-shell --pure --max-jobs 4 tests -A run.all
diff --git a/home-manager/.gitignore b/home-manager/.gitignore
index d6944e3ddc1..3526db71892 100644
--- a/home-manager/.gitignore
+++ b/home-manager/.gitignore
@@ -1 +1,2 @@
+/flake.lock
/result*
diff --git a/home-manager/.gitlab-ci.yml b/home-manager/.gitlab-ci.yml
index f7c14abb69e..2c5c5a67ffe 100644
--- a/home-manager/.gitlab-ci.yml
+++ b/home-manager/.gitlab-ci.yml
@@ -1,8 +1,8 @@
image: nixos/nix:latest
variables:
- # Pinned 2020-01-01.
- NIX_PATH: "nixpkgs=https://github.com/NixOS/nixpkgs/archive/b0bbacb52134a7e731e549f4c0a7a2a39ca6b481.tar.gz"
+ # Pinned 2020-03-28.
+ NIX_PATH: "nixpkgs=https://github.com/NixOS/nixpkgs/archive/05f0934825c2a0750d4888c4735f9420c906b388.tar.gz"
stages:
- test
@@ -11,9 +11,10 @@ stages:
Run tests:
stage: test
script:
- - nix-shell tests -A run.files-text
- only:
- - master
+ - nix-shell --pure tests -A run.files-text
+ rules:
+ - if: $CI_COMMIT_BRANCH == "master"
+ when: always
pages:
stage: deploy
@@ -26,8 +27,9 @@ pages:
artifacts:
paths:
- public
- only:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH == "master"
+ when: always
Deploy NUR:
stage: deploy
@@ -37,5 +39,6 @@ Deploy NUR:
trigger:
project: rycee/nur-expressions
branch: master
- only:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH =~ /^release-/
+ when: always
diff --git a/home-manager/.travis.yml b/home-manager/.travis.yml
deleted file mode 100644
index e0712e1e41f..00000000000
--- a/home-manager/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: nix
-
-os:
- - linux
- - osx
-
-before_script:
- - mkdir -m 0755 -p /nix/var/nix/{profiles,gcroots}/per-user/$USER
-
-script:
- - ./format -c
- - nix-shell . -A install
- - nix-shell tests -A run.all
diff --git a/home-manager/CONTRIBUTING.adoc b/home-manager/CONTRIBUTING.adoc
new file mode 120000
index 00000000000..b615999d110
--- /dev/null
+++ b/home-manager/CONTRIBUTING.adoc
@@ -0,0 +1 @@
+doc/contributing.adoc \ No newline at end of file
diff --git a/home-manager/CONTRIBUTING.md b/home-manager/CONTRIBUTING.md
deleted file mode 100644
index 8e64b3b45f9..00000000000
--- a/home-manager/CONTRIBUTING.md
+++ /dev/null
@@ -1,164 +0,0 @@
-Contributing
-============
-
-Thanks for wanting to contribute to Home Manager! These are some
-guidelines to make the process as smooth as possible for both you and
-the Home Manager developers.
-
-If you are only looking to report a problem then it is sufficient to
-read through the following section on reporting issues. If you instead
-want to directly contribute improvements and additions then please
-have a look at everything here.
-
-Reporting an issue
-------------------
-
-If you notice a problem with Home Manager and want to report it then
-have a look among the already [open issues][], if you find one
-matching yours then feel free to comment on it to add any additional
-information you may have.
-
-If no matching issue exists then go to the [new issue][] page and
-write a description of your problem. Include as much information as
-you can, ideally also include relevant excerpts from your Home Manager
-configuration.
-
-Contributing code
------------------
-
-If you want to contribute code to improve Home Manager then please
-follow these guidelines.
-
-### Fork and create a pull request ###
-
-If you have not previously forked Home Manager then you need to do
-that first. Have a look at GitHub's "[Fork A Repo][]" for instructions
-on how to do this.
-
-Once you have a fork of Home Manager you should create a branch
-starting at the most recent `master`. Give your branch a reasonably
-descriptive name. Perform your changes on this branch and when you are
-happy with the result push the branch to GitHub and
-[create a pull request][].
-
-Assuming your clone is at `$HOME/devel/home-manager` then you can make
-the `home-manager` command use it by either
-
-1. overriding the default path by using the `-I` command line option:
-
- home-manager -I home-manager=$HOME/devel/home-manager
-
- or
-
-2. changing the default path by ensuring your configuration includes
-
- programs.home-manager.enable = true;
- programs.home-manager.path = "$HOME/devel/home-manager";
-
- and running `home-manager switch` to activate the change.
- Afterwards, `home-manager build` and `home-manager switch` will
- use your cloned repository.
-
-The first option is good if you only temporarily want to use your
-clone.
-
-### Commits ###
-
-The commits in your pull request should be reasonably self-contained,
-that is, each commit should make sense in isolation. In particular,
-you will be asked to amend any commit that introduces syntax errors or
-similar problems even if they are fixed in a later commit.
-
-The commit messages should follow the format
-
- {component}: {description}
-
- {long description}
-
-where `{component}` refers to the code component (or module) your
-change affects, `{description}` is a brief description of your change,
-and `{long description}` is an optional clarifying description. Note,
-`{description}` should start with a lower case letter. As a rare
-exception, if there is no clear component, or your change affects many
-components, then the `{component}` part is optional.
-
-When adding a new module, say `foo.nix`, we use the fixed commit
-format `foo: add module`. You can, of course, still include a long
-description if you wish.
-
-In addition to the above commit message guidelines, try to follow the
-[seven rules][] as much as possible.
-
-### Style guidelines ###
-
-The code in Home Manager is formatted by the [nixfmt][] tool and the
-formatting is checked in the pull request tests. Run the `format` tool
-inside the project repository before submitting your pull request.
-
-Note, we prefer `lowerCamelCase` for variable and attribute names with
-the accepted exception of variables directly referencing packages in
-Nixpkgs which use a hyphenated style. For example, the Home Manager
-option `services.gpg-agent.enableSshSupport` references the
-`gpg-agent` package in Nixpkgs.
-
-### News ###
-
-Home Manager includes a system for presenting news to the user. When
-making a change you, therefore, have the option to also include an
-associated news entry. In general, a news entry should only be added
-for truly noteworthy news. For example, a bug fix or new option does
-generally not need a news entry.
-
-If you do have a change worthy of a news entry then please add one in
-[`news.nix`][] but you should follow some basic guidelines:
-
-- The entry timestamp should be in ISO-8601 format having "+00:00" as
- time zone. For example, "2017-09-13T17:10:14+00:00". A suitable
- timestamp can be produced by the command
-
- date --iso-8601=second --universal
-
-- The entry condition should be as specific as possible. For example,
- if you are changing or deprecating a specific option then you could
- restrict the news to those users who actually use this option.
-
-- Wrap the news message so that it will fit in the typical terminal,
- that is, at most 80 characters wide. Ideally a bit less.
-
-- Unlike commit messages, news will be read without any connection to
- the Home Manager source code. It is therefore important to make the
- message understandable in isolation and to those who do not have
- knowledge of the Home Manager internals. To this end it should be
- written in more descriptive, prose like way.
-
-- If you refer to an option then write its full attribute path. That
- is, instead of writing
-
- > The option 'foo' has been deprecated, please use 'bar' instead.
-
- it should read
-
- > The option 'services.myservice.foo' has been deprecated, please
- > use 'services.myservice.bar' instead.
-
-- A new module, say `foo.nix`, should always include a news entry
- that has a message along the lines of
-
- > A new module is available: 'services.foo'.
-
- If the module is platform specific, e.g., a service module using
- systemd, then a condition like
-
- ```
- condition = hostPlatform.isLinux;
- ```
-
- should be added.
-
-[open issues]: https://github.com/rycee/home-manager/issues
-[new issue]: https://github.com/rycee/home-manager/issues/new
-[Fork A Repo]: https://help.github.com/articles/fork-a-repo/
-[create a pull request]: https://help.github.com/articles/creating-a-pull-request/
-[seven rules]: https://chris.beams.io/posts/git-commit/#seven-rules
-[`news.nix`]: https://github.com/rycee/home-manager/blob/master/modules/misc/news.nix
-[nixfmt]: https://github.com/serokell/nixfmt/
diff --git a/home-manager/FAQ.adoc b/home-manager/FAQ.adoc
new file mode 120000
index 00000000000..7d90da71196
--- /dev/null
+++ b/home-manager/FAQ.adoc
@@ -0,0 +1 @@
+doc/faq.adoc \ No newline at end of file
diff --git a/home-manager/FAQ.md b/home-manager/FAQ.md
deleted file mode 100644
index a83a90f6cab..00000000000
--- a/home-manager/FAQ.md
+++ /dev/null
@@ -1,151 +0,0 @@
-Frequently Asked Questions (FAQ)
-================================
-
-Why is there a collision error when switching generation?
----------------------------------------------------------
-
-Home Manager currently installs packages into the user environment,
-precisely as if the packages were installed through
-`nix-env --install`. This means that you will get a collision error if
-your Home Manager configuration attempts to install a package that you
-already have installed manually, that is, packages that shows up when
-you run `nix-env --query`.
-
-For example, imagine you have the `hello` package installed in your
-environment
-
-```console
-$ nix-env --query
-hello-2.10
-```
-
-and your Home Manager configuration contains
-
- home.packages = [ pkgs.hello ];
-
-Then attempting to switch to this configuration will result in an
-error similar to
-
-```console
-$ home-manager switch
-these derivations will be built:
- /nix/store/xg69wsnd1rp8xgs9qfsjal017nf0ldhm-home-manager-path.drv
-[…]
-Activating installPackages
-replacing old ‘home-manager-path’
-installing ‘home-manager-path’
-building path(s) ‘/nix/store/b5c0asjz9f06l52l9812w6k39ifr49jj-user-environment’
-Wide character in die at /nix/store/64jc9gd2rkbgdb4yjx3nrgc91bpjj5ky-buildenv.pl line 79.
-collision between ‘/nix/store/fmwa4axzghz11cnln5absh31nbhs9lq1-home-manager-path/bin/hello’ and ‘/nix/store/c2wyl8b9p4afivpcz8jplc9kis8rj36d-hello-2.10/bin/hello’; use ‘nix-env --set-flag priority NUMBER PKGNAME’ to change the priority of one of the conflicting packages
-builder for ‘/nix/store/b37x3s7pzxbasfqhaca5dqbf3pjjw0ip-user-environment.drv’ failed with exit code 2
-error: build of ‘/nix/store/b37x3s7pzxbasfqhaca5dqbf3pjjw0ip-user-environment.drv’ failed
-```
-
-The solution is typically to uninstall the package from the
-environment using `nix-env --uninstall` and reattempt the Home Manager
-generation switch.
-
-Why are the session variables not set?
---------------------------------------
-
-Home Manager is only able to set session variables automatically if it
-manages your Bash or Z shell configuration. If you don't want to let
-Home Manager manage your shell then you will have to manually source
-the
-
- ~/.nix-profile/etc/profile.d/hm-session-vars.sh
-
-file in an appropriate way. In Bash and Z shell this can be done by
-adding
-
-```sh
-. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
-```
-
-to your `.profile` and `.zshrc` files, respectively. The
-`hm-session-vars.sh` file should work in most Bourne-like shells.
-
-How do set up a configuration for multiple users/machines?
-----------------------------------------------------------
-
-A typical way to prepare a repository of configurations for multiple
-logins and machines is to prepare one "top-level" file for each unique
-combination.
-
-For example, if you have two machines, called "kronos" and "rhea" on
-which you want to configure your user "jane" then you could create the
-files
-
-- `kronos-jane.nix`,
-- `rhea-jane.nix`, and
-- `common.nix`
-
-in your repository. On the kronos and rhea machines you can then make
-`~jane/.config/nixpkgs/home.nix` be a symbolic link to the
-corresponding file in your configuration repository.
-
-The `kronos-jane.nix` and `rhea-jane.nix` files follow the format
-
-```nix
-{ ... }:
-
-{
- imports = [ ./common.nix ];
-
- # Various options that are specific for this machine/user.
-}
-```
-
-while the `common.nix` file contains configuration shared across the
-two logins. Of course, instead of just a single `common.nix` file you
-can have multiple ones, even one per program or service.
-
-You can get some inspiration from the [Post your home-manager home.nix
-file!][1] Reddit thread.
-
-[1]: https://www.reddit.com/r/NixOS/comments/9bb9h9/post_your_homemanager_homenix_file/
-
-Why do I get an error message about `ca.desrt.dconf`?
------------------------------------------------------
-
-You are most likely trying to configure the GTK or Gnome Terminal but
-the DBus session is not aware of the dconf service. The full error you
-might get is
-
- error: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name ca.desrt.dconf was not provided by any .service files
-
-The solution on NixOS is to add
-
- services.dbus.packages = with pkgs; [ gnome3.dconf ];
-
-to your system configuration.
-
-How do I install packages from Nixpkgs unstable?
-------------------------------------------------
-
-If you are using a stable version of Nixpkgs but would like to install
-some particular packages from Nixpkgs unstable then you can import the
-unstable Nixpkgs and refer to its packages within your configuration.
-Something like
-
-```nix
-{ pkgs, config, ... }:
-
-let
-
- pkgsUnstable = import <nixpkgs-unstable> {};
-
-in
-
-{
- home.packages = [
- pkgsUnstable.foo
- ];
-
- # …
-}
-```
-
-should work provided you have a Nix channel called `nixpkgs-unstable`.
-Note, the package will not be affected by any package overrides,
-overlays, etc.
diff --git a/home-manager/LICENSE b/home-manager/LICENSE
index a7566dbf194..122b9b4a56f 100644
--- a/home-manager/LICENSE
+++ b/home-manager/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2017-2019 Robert Helgesson and Home Manager contributors
+Copyright (c) 2017-2020 Robert Helgesson and Home Manager contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/home-manager/README.md b/home-manager/README.md
index 5171ff657f8..5e538e8447b 100644
--- a/home-manager/README.md
+++ b/home-manager/README.md
@@ -6,8 +6,8 @@ using the [Nix][] package manager together with the Nix libraries
found in [Nixpkgs][]. Before attempting to use Home Manager please
read the warning below.
-For a more systematic overview of all the options Home Manager
-provides please see the [Home Manager manual][configuration options].
+For a more systematic overview of Home Manager and its available
+options, please see the [Home Manager manual][manual].
Words of warning
----------------
@@ -16,13 +16,18 @@ This project is under development. I personally use it to manage
several user configurations but it may fail catastrophically for you.
So beware!
+Before using Home Manager you should be comfortable using the Nix
+language and the various tools in the Nix ecosystem. Reading through
+the [Nix Pills][] document is a good way to familiarize yourself with
+them.
+
In some cases Home Manager cannot detect whether it will overwrite a
previous manual configuration. For example, the Gnome Terminal module
will write to your dconf store and cannot tell whether a configuration
that it is about to be overwrite was from a previous Home Manager
generation or from manual configuration.
-Home Manager targets [NixOS][] unstable and NixOS version 19.09 (the
+Home Manager targets [NixOS][] unstable and NixOS version 20.03 (the
current stable version), it may or may not work on other Linux
distributions and NixOS versions.
@@ -46,21 +51,12 @@ Installation
Currently the easiest way to install Home Manager is as follows:
-1. Make sure you have a working Nix installation. If you are not
- using NixOS then you may here have to run
-
- ```console
- $ mkdir -m 0755 -p /nix/var/nix/{profiles,gcroots}/per-user/$USER
- ```
-
- since Home Manager uses these directories to manage your profile
- generations. On NixOS these should already be available.
-
- Also make sure that your user is able to build and install Nix
- packages. For example, you should be able to successfully run a
- command like `nix-instantiate '<nixpkgs>' -A hello` without having
- to switch to the root user. For a multi-user install of Nix this
- means that your user must be covered by the
+1. Make sure you have a working Nix installation. Specifically, make
+ sure that your user is able to build and install Nix packages. For
+ example, you should be able to successfully run a command like
+ `nix-instantiate '<nixpkgs>' -A hello` without having to switch to
+ the root user. For a multi-user install of Nix this means that
+ your user must be covered by the
[`allowed-users`][nixAllowedUsers] Nix option. On NixOS you can
control this option using the
[`nix.allowedUsers`][nixosAllowedUsers] system option.
@@ -75,11 +71,11 @@ Currently the easiest way to install Home Manager is as follows:
if you are following Nixpkgs master or an unstable channel and
```console
- $ nix-channel --add https://github.com/rycee/home-manager/archive/release-19.09.tar.gz home-manager
+ $ nix-channel --add https://github.com/rycee/home-manager/archive/release-20.03.tar.gz home-manager
$ nix-channel --update
```
- if you follow a Nixpkgs version 19.09 channel.
+ if you follow a Nixpkgs version 20.03 channel.
On NixOS you may need to log out and back in for the channel to
become available. On non-NixOS you may have to add
@@ -158,7 +154,13 @@ To satisfy the above setup we should elaborate the
programs.firefox = {
enable = true;
- enableIcedTea = true;
+ profiles = {
+ myprofile = {
+ settings = {
+ "general.smoothScroll" = false;
+ };
+ };
+ };
};
services.gpg-agent = {
@@ -303,6 +305,71 @@ in your system configuration and
in your Home Manager configuration.
+Flakes
+------
+
+Home Manager includes a flake.nix for compatibility with [NixOS flakes](https://nixos.wiki/wiki/Flakes) for those
+that wish to use it as a module. A bare-minimum flake.nix would be as follows:
+
+```nix
+{
+ description = "NixOS configuration";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ home-manager.url = "github:rycee/home-manager";
+ };
+
+ outputs = inputs: {
+ nixosConfigurations = {
+ hostname = let
+ system = "x86_64-linux";
+ pkgs = inputs.nixpkgs.legacyPackages.${system};
+ inherit (inputs.nixpkgs) lib;
+
+ # Things in this set are passed to modules and accessible
+ # in the top-level arguments (e.g. `{ pkgs, lib, inputs, ... }:`).
+ specialArgs = {
+ inherit inputs;
+ };
+
+ hm-nixos-as-super = { config, ... }: {
+ # Submodules have merge semantics, making it possible to amend
+ # the `home-manager.users` submodule for additional functionality.
+ options.home-manager.users = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.submoduleWith {
+ modules = [ ];
+ # Makes specialArgs available to Home Manager modules as well.
+ specialArgs = specialArgs // {
+ # Allow accessing the parent NixOS configuration.
+ super = config;
+ };
+ });
+ };
+ };
+
+ modules = [
+ ./configuration.nix
+ inputs.home.nixosModules.home-manager
+ hm-nixos-as-super
+ ];
+ in lib.nixosSystem { inherit system modules specialArgs; };
+ };
+ };
+}
+```
+
+Releases
+--------
+
+Home Manager is developed against `nixpkgs-unstable` branch, which
+often causes it to contain tweaks for changes/packages not yet
+released in stable NixOS. To avoid breaking users' configurations,
+Home Manager is released in branches corresponding to NixOS releases
+(e.g. `release-20.03`). These branches get fixes, but usually not new
+modules. If you need a module to be backported, then feel free to open
+an issue.
+
[Bash]: https://www.gnu.org/software/bash/
[Nix]: https://nixos.org/nix/
[NixOS]: https://nixos.org/
@@ -310,8 +377,10 @@ in your Home Manager configuration.
[nixAllowedUsers]: https://nixos.org/nix/manual/#conf-allowed-users
[nixosAllowedUsers]: https://nixos.org/nixos/manual/options.html#opt-nix.allowedUsers
[Z shell]: http://zsh.sourceforge.net/
+[manual]: https://rycee.gitlab.io/home-manager/
[configuration options]: https://rycee.gitlab.io/home-manager/options.html
[#home-manager]: https://webchat.freenode.net/?url=irc%3A%2F%2Firc.freenode.net%2Fhome-manager
[freenode]: https://freenode.net/
[channel logs]: https://logs.nix.samueldr.com/home-manager/
[samueldr]: https://github.com/samueldr/
+[Nix Pills]: https://nixos.org/nixos/nix-pills/
diff --git a/home-manager/default.nix b/home-manager/default.nix
index 545b0114191..4219d370c49 100644
--- a/home-manager/default.nix
+++ b/home-manager/default.nix
@@ -1,10 +1,18 @@
{ pkgs ? import <nixpkgs> { } }:
rec {
+ docs = with import ./doc { inherit pkgs; }; {
+ html = manual.html;
+ manPages = manPages;
+ json = options.json;
+ };
+
home-manager = pkgs.callPackage ./home-manager { path = toString ./.; };
install =
pkgs.callPackage ./home-manager/install.nix { inherit home-manager; };
nixos = import ./nixos;
+
+ path = ./.;
}
diff --git a/home-manager/doc/contributing.adoc b/home-manager/doc/contributing.adoc
new file mode 100644
index 00000000000..a6c7a3278e7
--- /dev/null
+++ b/home-manager/doc/contributing.adoc
@@ -0,0 +1,255 @@
+[[ch-contributing]]
+== Contributing
+
+:open-issues: https://github.com/rycee/home-manager/issues
+:new-issue: https://github.com/rycee/home-manager/issues/new
+:fork-a-repo: https://help.github.com/articles/fork-a-repo/
+:create-a-pull-request: https://help.github.com/articles/creating-a-pull-request/
+:seven-rules: https://chris.beams.io/posts/git-commit/#seven-rules
+:news-nix: https://github.com/rycee/home-manager/blob/master/modules/misc/news.nix
+:nixfmt: https://github.com/serokell/nixfmt/
+:example-commit-message: https://github.com/rycee/home-manager/commit/69f8e47e9e74c8d3d060ca22e18246b7f7d988ef
+
+Contributions to Home Manager are very welcome. To make the process as smooth as possible for both you and the Home Manager maintainers we provide some guidelines that we ask you to follow. See <<sec-contrib-getting-started>> for information on how to set up a suitable development environment and <<sec-guidelines>> for the actual guidelines.
+
+This text is mainly directed at those who would like to make code contributions to Home Manager. If you just want to report a bug then first look among the already {open-issues}[open issues], if you find one matching yours then feel free to comment on it to add any additional information you may have. If no matching issue exists then go to the {new-issue}[new issue] page and write a description of your problem. Include as much information as you can, ideally also include relevant excerpts from your Home Manager configuration.
+
+[[sec-contrib-getting-started]]
+=== Getting started
+
+If you have not previously forked Home Manager then you need to do that first. Have a look at GitHub's {fork-a-repo}[Fork a repo] for instructions on how to do this.
+
+Once you have a fork of Home Manager you should create a branch starting at the most recent `master` branch. Give your branch a reasonably descriptive name. Commit your changes to this branch and when you are happy with the result and it fulfills <<sec-guidelines>> then push the branch to GitHub and {create-a-pull-request}[create a pull request].
+
+Assuming your clone is at `$HOME/devel/home-manager` then you can make the `home-manager` command use it by either
+
+1. overriding the default path by using the `-I` command line option:
++
+[source,console]
+$ home-manager -I home-manager=$HOME/devel/home-manager
++
+or
+
+2. changing the default path by ensuring your configuration includes
++
+[source,nix]
+----
+programs.home-manager.enable = true;
+programs.home-manager.path = "$HOME/devel/home-manager";
+----
++
+and running `home-manager switch` to activate the change. Afterwards, `home-manager build` and `home-manager switch` will use your cloned repository.
+
+The first option is good if you only temporarily want to use your clone.
+
+[[sec-guidelines]]
+=== Guidelines
+:irc-home-manager: https://webchat.freenode.net/?url=irc%3A%2F%2Firc.freenode.net%2Fhome-manager
+:valuable-options: https://github.com/Infinisil/rfcs/blob/config-option/rfcs/0042-config-option.md#valuable-options
+:rfc-42: https://github.com/Infinisil/rfcs/blob/config-option/rfcs/0042-config-option.md
+
+If your contribution satisfy the following rules then there is a good chance it will be merged without too much trouble. The rules are enforced by the Home Manager maintainers and to a lesser extent the Home Manager CI system.
+
+If you are uncertain how these rules affect the change you would like to make then feel free to start a discussion in the {irc-home-manager}[#home-manager] IRC channel, ideally before you start developing.
+
+[[sec-guidelines-back-compat]]
+==== Maintain backward compatibility
+
+Your contribution should never cause another user's existing configuration to break. Home Manager is used in many different environments and you should consider how you change may effect others. For example,
+
+- Does your change work for people that do not use NixOS? Consider other GNU/Linux distributions and macOS.
+- Does your change work for people whose configuration is built on one system and deployed on another system?
+
+[[sec-guidelines-forward-compat]]
+==== Keep forward compatibility in mind
+
+The master branch of Home Manager tracks the unstable channel of Nixpkgs, which may update package versions at any time. It is therefore important to consider how a package update may affect your code and try to reduce the risk of breakage.
+
+The most effective way to reduce this risk is to follow the advice in <<sec-guidelines-valuable-options>>.
+
+[[sec-guidelines-valuable-options]]
+==== Add only valuable options
+
+When creating a new module it is tempting to include every option supported by the software. This is _strongly_ discouraged. Providing many options increases maintenance burden and risk of breakage considerably. This is why only the most {valuable-options}[important software options] should be modeled explicitly. Less important options should be expressible through an `extraConfig` escape hatch.
+
+A good rule of thumb for the first implementation of a module is to only add explicit options for those settings that absolutely must be set for the software to function correctly. It follows that a module for software that provides sensible default values for all settings would require no explicit options at all.
+
+If the software uses a structured configuration format like a JSON, YAML, INI, TOML, or even a plain list of key/value pairs then consider using a `settings` option as described in {rfc-42}[Nix RFC 42].
+
+[[sec-guidelines-add-tests]]
+==== Add relevant tests
+
+If at all possible, make sure to add new tests and expand existing tests so that your change will keep working in the future. See <<sec-tests>> for more information about the Home Manager test suite.
+
+All contributed code _must_ pass the test suite.
+
+[[sec-guidelines-module-maintainer]]
+
+==== Add relevant documentation
+:docbook: https://tdg.docbook.org/
+:asciidoc: https://asciidoc.org/
+:docbook-rocks: https://docbook.rocks/
+
+Many code changes require changing the documentation as well. Module options should be documented with DocBook. See {docbook-rocks}[DocBook rocks!] for a quick introduction and {docbook}[DocBook 5: The Definitive Guide] for in-depth information of DocBook. Home Manager is itself documented using a combination of DocBook and {asciidoc}[AsciiDoc]. All text is hosted in Home Manager's Git repository.
+
+The HTML version of the manual containing both the module option descriptions and the documentation of Home Manager can be generated and opened by typing the following in a shell within a clone of the Home Manager Git repository:
+
+[source,console]
+$ nix-build -A docs.html
+$ xdg-open ./result/share/doc/home-manager/index.html
+
+When you have made changes to a module, it is a good idea to check that the man page version of the module options looks good:
+
+[source,console]
+$ nix-build -A docs.manPages
+$ man ./result/share/man/man5/home-configuration.nix.5
+
+==== Add yourself as a module maintainer
+
+Every new module _must_ include a named maintainer using the `meta.maintainers` attribute. If you are a user of a module that currently lacks a maintainer then please consider adopting it.
+
+If you are present in the NixOS maintainer list then you can use that entry. If you are not then you can add yourself to `modules/lib/maintainers.nix` in the Home Manager project.
+
+Also add yourself to `.github/CODEOWNERS` as owner of the associated module files, including the test files. You will then be automatically added as a reviewer on any new pull request that touches your files.
+
+Maintainers are encouraged to join the IRC channel and participate when they have opportunity.
+
+[[sec-guidelines-code-style]]
+==== Format your code
+
+Make sure your code is formatted as described in <<sec-code-style>>. To maintain consistency throughout the project you are encouraged to browse through existing code and adopt its style also in new code.
+
+[[sec-guidelines-commit-message-style]]
+==== Format your commit messages
+
+Similar to <<sec-guidelines-code-style>> we encourage a consistent commit message format as described in <<sec-commit-style>>.
+
+[[sec-guidelines-news-style]]
+==== Format your news entries
+
+If your contribution includes a change that should be communicated to users of Home Manager then you can add a news entry. The entry must be formatted as described in <<sec-news>>.
+
+When new modules are added a news entry should be included but you do not need to create this entry manually. The merging maintainer will create the entry for you. This is to reduce the risk of merge conflicts.
+
+[[sec-guidelines-conditional-modules]]
+==== Use conditional modules and news
+
+Home Manager includes a number of modules that are only usable on some of the supported platforms. The most common example of platform specific modules are those that define systemd user services, which only works on Linux systems.
+
+If you add a module that is platform specific then make sure to include a condition in the `loadModule` function call. This will make the module accessible only on systems where the condition evaluates to `true`.
+
+Similarly, if you are adding a news entry then it should be shown only to users that may find it relevant, see <<sec-news>> for a description of conditional news.
+
+[[sec-guidelines-licensing]]
+==== Mind the license
+
+The Home Manager project is covered by the MIT license and we can only accept contributions that fall under this license, or are licensed in a compatible way. When you contribute self written code and documentation it is assumed that you are doing so under the MIT license.
+
+A potential gotcha with respect to licensing are option descriptions. Often it is convenient to copy from the upstream software documentation. When this is done it is important to verify that the license of the upstream documentation allows redistribution under the terms of the MIT license.
+
+[[sec-commit-style]]
+=== Commits
+
+The commits in your pull request should be reasonably self-contained, that is, each commit should make sense in isolation. In particular, you will be asked to amend any commit that introduces syntax errors or similar problems even if they are fixed in a later commit.
+
+The commit messages should follow the {seven-rules}[seven rules]. We also ask you to include the affected code component or module in the first line. That is, a commit message should follow the template
+
+----
+{component}: {description}
+
+{long description}
+----
+
+where `{component}` refers to the code component (or module) your change affects, `{description}` is a very brief description of your change, and `{long description}` is an optional clarifying description. Note, `{description}` should start with a lower case letter. As a rare exception, if there is no clear component, or your change affects many components, then the `{component}` part is optional. See <<ex-commit-message>> for a commit message that fulfills these requirements.
+
+[[ex-commit-message]]
+.Compliant commit message
+===============================================================================
+The commit {example-commit-message}[69f8e47e9e74c8d3d060ca22e18246b7f7d988ef] contains the commit message
+
+----
+starship: allow running in Emacs if vterm is used
+
+The vterm buffer is backed by libvterm and can handle Starship prompts
+without issues.
+----
+
+which ticks all the boxes necessary to be accepted in Home Manager.
+===============================================================================
+
+Finally, when adding a new module, say `programs/foo.nix`, we use the fixed commit format `foo: add module`. You can, of course, still include a long description if you wish.
+
+[[sec-code-style]]
+=== Code Style
+
+The code in Home Manager is formatted by the {nixfmt}[nixfmt] tool and the formatting is checked in the pull request tests. Run the `format` tool inside the project repository before submitting your pull request.
+
+Keep lines at a reasonable width, ideally 80 characters or less. This also applies to string literals.
+
+We prefer `lowerCamelCase` for variable and attribute names with the accepted exception of variables directly referencing packages in Nixpkgs which use a hyphenated style. For example, the Home Manager option `services.gpg-agent.enableSshSupport` references the `gpg-agent` package in Nixpkgs.
+
+[[sec-news]]
+=== News
+
+Home Manager includes a system for presenting news to the user. When making a change you, therefore, have the option to also include an associated news entry. In general, a news entry should only be added for truly noteworthy news. For example, a bug fix or new option does generally not need a news entry.
+
+If you do have a change worthy of a news entry then please add one in {news-nix}[`news.nix`] but you should follow some basic guidelines:
+
+- The entry timestamp should be in ISO-8601 format having "+00:00" as time zone. For example, "2017-09-13T17:10:14+00:00". A suitable timestamp can be produced by the command
++
+[source,console]
+$ date --iso-8601=second --universal
+
+- The entry condition should be as specific as possible. For example, if you are changing or deprecating a specific option then you could restrict the news to those users who actually use this option.
+
+- Wrap the news message so that it will fit in the typical terminal, that is, at most 80 characters wide. Ideally a bit less.
+
+- Unlike commit messages, news will be read without any connection to the Home Manager source code. It is therefore important to make the message understandable in isolation and to those who do not have knowledge of the Home Manager internals. To this end it should be written in more descriptive, prose like way.
+
+- If you refer to an option then write its full attribute path. That is, instead of writing
++
+----
+The option 'foo' has been deprecated, please use 'bar' instead.
+----
++
+it should read
++
+----
+The option 'services.myservice.foo' has been deprecated, please
+use 'services.myservice.bar' instead.
+----
+
+- A new module, say `foo.nix`, should always include a news entry that has a message along the lines of
++
+----
+A new module is available: 'services.foo'.
+----
++
+If the module is platform specific, e.g., a service module using systemd, then a condition like
++
+[source,nix]
+condition = hostPlatform.isLinux;
++
+should be added. If you contribute a module then you don't need to add this entry, the merger will create an entry for you.
+
+[[sec-tests]]
+=== Tests
+
+Home Manager includes a basic test suite and it is highly recommended to include at least one test when adding a module. Tests are typically in the form of "golden tests" where, for example, a generated configuration file is compared to a known correct file.
+
+It is relatively easy to create tests by modeling the existing tests, found in the `tests` project directory.
+
+The full Home Manager test suite can be run by executing
+
+[source,console]
+$ nix-shell --pure tests -A run.all
+
+in the project root. List all test cases through
+
+[source,console]
+$ nix-shell --pure tests -A list
+
+and run an individual test, for example `alacritty-empty-settings`, through
+
+[source,console]
+$ nix-shell --pure tests -A run.alacritty-empty-settings
diff --git a/home-manager/doc/default.nix b/home-manager/doc/default.nix
index 638027b0a78..88e9756140e 100644
--- a/home-manager/doc/default.nix
+++ b/home-manager/doc/default.nix
@@ -1,6 +1,7 @@
-{
+{ pkgs
+
# Note, this should be "the standard library" + HM extensions.
-lib, pkgs }:
+, lib ? import ../modules/lib/stdlib-extended.nix pkgs.lib }:
let
@@ -8,8 +9,8 @@ let
name = "nmd";
owner = "rycee";
repo = "nmd";
- rev = "b437898c2b137c39d9c5f9a1cf62ec630f14d9fc";
- sha256 = "18j1nh53cfpjpdiwn99x9kqpvr0s7hwngyc0a93xf4sg88ww93lq";
+ rev = "2398aa79ab12aa7aba14bc3b08a6efd38ebabdc5";
+ sha256 = "0yxb48afvccn8vvpkykzcr4q1rgv8jsijqncia7a5ffzshcrwrnh";
};
nmd = import nmdSrc { inherit lib pkgs; };
@@ -41,6 +42,7 @@ let
pathName = "home-manager";
modulesDocs = [ hmModulesDocs ];
documentsDirectory = ./.;
+ documentType = "book";
chunkToc = ''
<toc>
<d:tocentry xmlns:d="http://docbook.org/ns/docbook" linkend="book-home-manager-manual"><?dbhtml filename="index.html"?>
diff --git a/home-manager/doc/faq.adoc b/home-manager/doc/faq.adoc
new file mode 100644
index 00000000000..b8215254dd7
--- /dev/null
+++ b/home-manager/doc/faq.adoc
@@ -0,0 +1,171 @@
+[[ch-faq]]
+== Frequently Asked Questions (FAQ)
+
+=== Why is there a collision error when switching generation?
+
+Home Manager currently installs packages into the user environment, precisely as if the packages were installed through `nix-env --install`. This means that you will get a collision error if your Home Manager configuration attempts to install a package that you already have installed manually, that is, packages that shows up when you run `nix-env --query`.
+
+For example, imagine you have the `hello` package installed in your environment
+
+[source,console]
+----
+$ nix-env --query
+hello-2.10
+----
+
+and your Home Manager configuration contains
+
+[source,nix]
+----
+home.packages = [ pkgs.hello ];
+----
+
+Then attempting to switch to this configuration will result in an error similar to
+
+[source,console]
+----
+$ home-manager switch
+these derivations will be built:
+ /nix/store/xg69wsnd1rp8xgs9qfsjal017nf0ldhm-home-manager-path.drv
+[…]
+Activating installPackages
+replacing old ‘home-manager-path’
+installing ‘home-manager-path’
+building path(s) ‘/nix/store/b5c0asjz9f06l52l9812w6k39ifr49jj-user-environment’
+Wide character in die at /nix/store/64jc9gd2rkbgdb4yjx3nrgc91bpjj5ky-buildenv.pl line 79.
+collision between ‘/nix/store/fmwa4axzghz11cnln5absh31nbhs9lq1-home-manager-path/bin/hello’ and ‘/nix/store/c2wyl8b9p4afivpcz8jplc9kis8rj36d-hello-2.10/bin/hello’; use ‘nix-env --set-flag priority NUMBER PKGNAME’ to change the priority of one of the conflicting packages
+builder for ‘/nix/store/b37x3s7pzxbasfqhaca5dqbf3pjjw0ip-user-environment.drv’ failed with exit code 2
+error: build of ‘/nix/store/b37x3s7pzxbasfqhaca5dqbf3pjjw0ip-user-environment.drv’ failed
+----
+
+The solution is typically to uninstall the package from the environment using `nix-env --uninstall` and reattempt the Home Manager generation switch.
+
+=== Why are the session variables not set?
+
+Home Manager is only able to set session variables automatically if it manages your Bash or Z shell configuration. If you don't want to let Home Manager manage your shell then you will have to manually source the `~/.nix-profile/etc/profile.d/hm-session-vars.sh` file in an appropriate way. In Bash and Z shell this can be done by adding
+
+[source,bash]
+----
+. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
+----
+
+to your `.profile` and `.zshrc` files, respectively. The `hm-session-vars.sh` file should work in most Bourne-like shells.
+
+=== How do set up a configuration for multiple users/machines?
+:post-your-homenix: https://www.reddit.com/r/NixOS/comments/9bb9h9/post_your_homemanager_homenix_file/
+
+A typical way to prepare a repository of configurations for multiple logins and machines is to prepare one "top-level" file for each unique combination.
+
+For example, if you have two machines, called "kronos" and "rhea" on which you want to configure your user "jane" then you could create the files
+
+- `kronos-jane.nix`,
+- `rhea-jane.nix`, and
+- `common.nix`
+
+in your repository. On the kronos and rhea machines you can then make `~jane/.config/nixpkgs/home.nix` be a symbolic link to the corresponding file in your configuration repository.
+
+The `kronos-jane.nix` and `rhea-jane.nix` files follow the format
+
+[source,nix]
+----
+{ ... }:
+
+{
+ imports = [ ./common.nix ];
+
+ # Various options that are specific for this machine/user.
+}
+----
+
+while the `common.nix` file contains configuration shared across the two logins. Of course, instead of just a single `common.nix` file you can have multiple ones, even one per program or service.
+
+You can get some inspiration from the {post-your-homenix}[Post your home-manager home.nix file!] Reddit thread.
+
+=== Why do I get an error message about `ca.desrt.dconf`?
+
+You are most likely trying to configure the GTK or Gnome Terminal but the DBus session is not aware of the dconf service. The full error you might get is
+
+----
+error: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name ca.desrt.dconf was not provided by any .service files
+----
+
+The solution on NixOS is to add
+
+[source,nix]
+services.dbus.packages = with pkgs; [ gnome3.dconf ];
+
+to your system configuration.
+
+=== How do I install packages from Nixpkgs unstable?
+
+If you are using a stable version of Nixpkgs but would like to install some particular packages from Nixpkgs unstable – or some other channel – then you can import the unstable Nixpkgs and refer to its packages within your configuration. Something like
+
+[source,nix]
+----
+{ pkgs, config, ... }:
+
+let
+
+ pkgsUnstable = import <nixpkgs-unstable> {};
+
+in
+
+{
+ home.packages = [
+ pkgsUnstable.foo
+ ];
+
+ # …
+}
+----
+
+should work provided you have a Nix channel called `nixpkgs-unstable`.
+
+You can add the `nixpkgs-unstable` channel by running
+
+[source,console]
+----
+# nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs-unstable
+# nix-channel --update
+----
+
+Note, the package will not be affected by any package overrides, overlays, etc.
+
+=== How do I override the package used by a module?
+:nixpkgs-overlays: https://nixos.org/nixpkgs/manual/#chap-overlays
+
+By default Home Manager will install the package provided by your chosen `nixpkgs` channel but occasionally you might end up needing to change this package. This can typically be done in two ways.
+
+1. If the module provides a `package` option, such as `programs.beets.package`, then this is the recommended way to perform the override. For example,
++
+[source,nix]
+programs.beets.package = pkgs.beets.override { enableCheck = true; };
+
+2. If no `package` option is available then you can typically override the relevant package using an {nixpkgs-overlays}[overlay].
++
+For example, if you want to use the `programs.skim` module but use the `skim` package from Nixpkgs unstable, then a configuration like
++
+[source,nix]
+----
+{ pkgs, config, ... }:
+
+let
+
+ pkgsUnstable = import <nixpkgs-unstable> {};
+
+in
+
+{
+ programs.skim.enable = true;
+
+ nixpkgs.overlays = [
+ (self: super: {
+ skim = pkgsUnstable.skim;
+ })
+ ];
+
+ # …
+}
+----
++
+should work OK.
diff --git a/home-manager/doc/installation.xml b/home-manager/doc/installation.xml
index 52119886800..3e1f75abb59 100644
--- a/home-manager/doc/installation.xml
+++ b/home-manager/doc/installation.xml
@@ -44,22 +44,12 @@
<orderedlist>
<listitem>
<para>
- Make sure you have a working Nix installation. If you are not using NixOS
- then it may be necessary to run
- </para>
-<screen>
-<prompt>$</prompt> <userinput>mkdir -m 0755 -p /nix/var/nix/{profiles,gcroots}/per-user/$USER</userinput>
-</screen>
- <para>
- since Home Manager uses these directories to manage your profile
- generations. On NixOS these should already be available.
- </para>
- <para>
- Also make sure that your user is able to build and install Nix packages.
- For example, you should be able to successfully run a command like
- <literal>nix-instantiate '&lt;nixpkgs&gt;' -A hello</literal> without
- having to switch to the root user. For a multi-user install of Nix this
- means that your user must be covered by the
+ Make sure you have a working Nix installation. Specifically, make
+ sure that your user is able to build and install Nix packages.
+ For example, you should be able to successfully run a command
+ like <literal>nix-instantiate '&lt;nixpkgs&gt;' -A hello</literal>
+ without having to switch to the root user. For a multi-user
+ install of Nix this means that your user must be covered by the
<link xlink:href="https://nixos.org/nix/manual/#conf-allowed-users"><literal>allowed-users</literal></link>
Nix option. On NixOS you can control this option using the
<link xlink:href="https://nixos.org/nixos/manual/options.html#opt-nix.allowedUsers"><literal>nix.allowedUsers</literal></link>
@@ -79,11 +69,11 @@
if you are following Nixpkgs master or an unstable channel and
</para>
<screen>
-<prompt>$</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-19.09.tar.gz home-manager</userinput>
+<prompt>$</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-20.03.tar.gz home-manager</userinput>
<prompt>$</prompt> <userinput>nix-channel --update</userinput>
</screen>
<para>
- if you follow a Nixpkgs version 19.09 channel.
+ if you follow a Nixpkgs version 20.03 channel.
</para>
<para>
On NixOS you may need to log out and back in for the channel to become
@@ -159,7 +149,7 @@ $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
a Home Manager channel, for example
</para>
-<screen>
+<screen language="console">
<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/master.tar.gz home-manager</userinput>
<prompt>#</prompt> <userinput>nix-channel --update</userinput>
</screen>
@@ -168,13 +158,13 @@ $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
if you are following Nixpkgs master or an unstable channel and
</para>
-<screen>
-<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-19.09.tar.gz home-manager</userinput>
+<screen language="console">
+<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-20.03.tar.gz home-manager</userinput>
<prompt>#</prompt> <userinput>nix-channel --update</userinput>
</screen>
<para>
- if you follow a Nixpkgs version 19.09 channel.
+ if you follow a Nixpkgs version 20.03 channel.
</para>
<para>
@@ -225,6 +215,23 @@ home-manager.useUserPackages = true;
become the default value in the future.
</para>
</note>
+
+ <note>
+ <para>
+ By default, Home Manager uses a private <literal>pkgs</literal> instance
+ that is configured via the <option>home-manager.users.&lt;name&gt;.nixpkgs</option> options.
+ To instead use the global <literal>pkgs</literal> that is configured via
+ the system level <option>nixpkgs</option> options, set
+ </para>
+<programlisting language="nix">
+home-manager.useGlobalPkgs = true;
+</programlisting>
+ <para>
+ This saves an extra Nixpkgs evaluation, adds consistency, and removes the
+ dependency on <envar>NIX_PATH</envar>, which is otherwise used for
+ importing Nixpkgs.
+ </para>
+ </note>
</section>
<section xml:id="sec-install-nix-darwin-module">
<title>nix-darwin module</title>
@@ -241,7 +248,7 @@ home-manager.useUserPackages = true;
a Home Manager channel, for example
</para>
-<screen>
+<screen language="console">
<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/master.tar.gz home-manager</userinput>
<prompt>#</prompt> <userinput>nix-channel --update</userinput>
</screen>
@@ -250,13 +257,13 @@ home-manager.useUserPackages = true;
if you are following Nixpkgs master or an unstable channel and
</para>
-<screen>
-<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-19.09.tar.gz home-manager</userinput>
+<screen language="console">
+<prompt>#</prompt> <userinput>nix-channel --add https://github.com/rycee/home-manager/archive/release-20.03.tar.gz home-manager</userinput>
<prompt>#</prompt> <userinput>nix-channel --update</userinput>
</screen>
<para>
- if you follow a Nixpkgs version 19.09 channel.
+ if you follow a Nixpkgs version 20.03 channel.
</para>
<para>
@@ -306,5 +313,22 @@ home-manager.useUserPackages = true;
value in the future.
</para>
</note>
+
+ <note>
+ <para>
+ By default, Home Manager uses a private <literal>pkgs</literal> instance
+ that is configured via the <option>home-manager.users.&lt;name&gt;.nixpkgs</option> options.
+ To instead use the global <literal>pkgs</literal> that is configured via
+ the system level <option>nixpkgs</option> options, set
+ </para>
+<programlisting language="nix">
+home-manager.useGlobalPkgs = true;
+</programlisting>
+ <para>
+ This saves an extra Nixpkgs evaluation, adds consistency, and removes the
+ dependency on <envar>NIX_PATH</envar>, which is otherwise used for
+ importing Nixpkgs.
+ </para>
+ </note>
</section>
</chapter>
diff --git a/home-manager/doc/man-home-manager.xml b/home-manager/doc/man-home-manager.xml
index 117bc494687..90f58063ea9 100644
--- a/home-manager/doc/man-home-manager.xml
+++ b/home-manager/doc/man-home-manager.xml
@@ -18,6 +18,10 @@
</arg>
<arg choice="plain">
+ instantiate
+ </arg>
+
+ <arg choice="plain">
edit
</arg>
@@ -165,6 +169,16 @@
</varlistentry>
<varlistentry>
<term>
+ <option>instantiate</option>
+ </term>
+ <listitem>
+ <para>
+ Instantiate the configuration and print the resulting derivation.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
<option>edit</option>
</term>
<listitem>
diff --git a/home-manager/doc/man-pages.xml b/home-manager/doc/man-pages.xml
index 616c2ef291b..bb484ae019b 100644
--- a/home-manager/doc/man-pages.xml
+++ b/home-manager/doc/man-pages.xml
@@ -4,7 +4,7 @@
<title>Home Manager Reference Pages</title>
<info>
<author><personname>Home Manager contributors</personname></author>
- <copyright><year>2017–2019</year><holder>Home Manager contributors</holder>
+ <copyright><year>2017–2020</year><holder>Home Manager contributors</holder>
</copyright>
</info>
<xi:include href="man-configuration.xml" />
diff --git a/home-manager/doc/manual.xml b/home-manager/doc/manual.xml
index 8ff81308b2c..ff355d6ce1e 100644
--- a/home-manager/doc/manual.xml
+++ b/home-manager/doc/manual.xml
@@ -13,9 +13,14 @@
Manager.
</para>
<para>
- If you encounter problems or bugs then please report them on the
- <link xlink:href="https://github.com/rycee/home-manager/issues">Home Manager
- issue tracker</link>.
+ If you encounter problems then please reach out on the IRC channel
+ <link xlink:href="https://webchat.freenode.net/?url=irc%3A%2F%2Firc.freenode.net%2Fhome-manager">#home-manager</link>
+ hosted by <link xlink:href="https://freenode.net/">freenode</link>.
+ The <link xlink:href="https://logs.nix.samueldr.com/home-manager/">channel logs</link>
+ are hosted courtesy of <link xlink:href="https://github.com/samueldr/">samueldr</link>.
+ If your problem is caused by a bug in Home Manager then it should
+ be reported on the
+ <link xlink:href="https://github.com/rycee/home-manager/issues">Home Manager issue tracker</link>.
</para>
<note>
<para>
@@ -26,6 +31,9 @@
</note>
</preface>
<xi:include href="installation.xml" />
+ <xi:include href="writing-modules.xml" />
+ <xi:include href="contributing.xml" />
+ <xi:include href="faq.xml" />
<appendix xml:id="ch-options">
<title>Configuration Options</title>
<xi:include href="./nmd-result/home-manager-options.xml" />
diff --git a/home-manager/doc/release-notes/release-notes.adoc b/home-manager/doc/release-notes/release-notes.adoc
new file mode 100644
index 00000000000..9a98e3850c3
--- /dev/null
+++ b/home-manager/doc/release-notes/release-notes.adoc
@@ -0,0 +1,19 @@
+[[ch-release-notes]]
+[appendix]
+== Release Notes
+
+This section lists the release notes for stable versions of Home Manager and the current unstable version.
+
+:leveloffset: 1
+
+include::rl-2009.adoc[]
+
+include::rl-2003.adoc[]
+
+include::rl-1909.adoc[]
+
+include::rl-1903.adoc[]
+
+include::rl-1809.adoc[]
+
+:leveloffset: 0
diff --git a/home-manager/doc/release-notes/release-notes.xml b/home-manager/doc/release-notes/release-notes.xml
deleted file mode 100644
index 2c1f5fcde3b..00000000000
--- a/home-manager/doc/release-notes/release-notes.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<appendix xmlns="http://docbook.org/ns/docbook"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:xi="http://www.w3.org/2001/XInclude"
- version="5.0"
- xml:id="ch-release-notes">
- <title>Release Notes</title>
- <para>
- This section lists the release notes for stable versions of Home Manager and
- the current unstable version.
- </para>
- <xi:include href="rl-2003.xml" />
- <xi:include href="rl-1909.xml" />
- <xi:include href="rl-1903.xml" />
- <xi:include href="rl-1809.xml" />
-</appendix>
diff --git a/home-manager/doc/release-notes/rl-1903.adoc b/home-manager/doc/release-notes/rl-1903.adoc
index 6dfdc67f5bf..1cba4235d60 100644
--- a/home-manager/doc/release-notes/rl-1903.adoc
+++ b/home-manager/doc/release-notes/rl-1903.adoc
@@ -5,7 +5,7 @@ The 19.03 release branch became the stable branch in April, 2019.
[[sec-release-19.03-highlights]]
=== Highlights
-:opt-home-file-source: opt-home.file._name__.source
+:opt-home-file-source: opt-home.file._name_.source
This release has the following notable changes:
diff --git a/home-manager/doc/release-notes/rl-1909.adoc b/home-manager/doc/release-notes/rl-1909.adoc
index 2cc90c164de..89bbbdc2b4f 100644
--- a/home-manager/doc/release-notes/rl-1909.adoc
+++ b/home-manager/doc/release-notes/rl-1909.adoc
@@ -8,8 +8,8 @@ The 19.09 release branch became the stable branch in October, 2019.
This release has the following notable changes:
-* The <<opt-programs.firefox.enableGoogleTalk>> and
- <<opt-programs.firefox.enableIcedTea>> options are now deprecated
+* The `programs.firefox.enableGoogleTalk` and
+ `programs.firefox.enableIcedTea` options are now deprecated
and will only work if Firefox ESR 52.x is used.
* The `home-manager` tool now provides an `uninstall` sub-command that
diff --git a/home-manager/doc/release-notes/rl-2003.adoc b/home-manager/doc/release-notes/rl-2003.adoc
index fc1dcd7cfe2..5832e2e5ab5 100644
--- a/home-manager/doc/release-notes/rl-2003.adoc
+++ b/home-manager/doc/release-notes/rl-2003.adoc
@@ -1,8 +1,7 @@
[[sec-release-20.03]]
-== Release 20.03 (unreleased)
+== Release 20.03
-This is the current unstable branch and the information in this
-section is therefore not final.
+The 20.03 release branch became the stable branch in April, 2020.
[[sec-release-20.03-highlights]]
=== Highlights
@@ -69,6 +68,47 @@ use
home-manager.users.jane = { config.config = "foo"; };
----
+* The `services.compton` module has been deprecated and instead the
+new module `services.picom` should be used. This is because Nixpkgs no
+longer packages compton, and instead packages the (mostly) compatible
+fork called picom.
+
+* The list form of the <<opt-programs.ssh.matchBlocks>> option has
+been deprecated and configurations requiring match blocks in a defined
+order should switch to using DAG entries instead. For example, a
+configuration
++
+[source,nix]
+----
+programs.ssh.matchBlocks = [
+ {
+ host = "alpha.foo.com";
+ user = "jd";
+ }
+ {
+ host = "*.foo.com";
+ user = "john.doe";
+ }
+];
+----
++
+can be expressed along the lines of
++
+[source,nix]
+----
+programs.ssh.matchBlocks = {
+ "*.example.com" = {
+ user = "john.doe";
+ }
+ "alpha.example.com" = lib.hm.dag.entryBefore ["*.example.com"] {
+ user = "jd";
+ }
+};
+----
++
+Support for the list form will be removed in Home Manager version
+20.09.
+
[[sec-release-20.03-state-version-changes]]
=== State Version Changes
@@ -81,3 +121,6 @@ changes are only active if the `home.stateVersion` option is set to
using the xdg module. Also, the default value is fixed to
`$HOME/.zsh_history` and `dotDir` path is not prepended to it
anymore.
+* The newsboat module will now default in displaying `queries` before `urls` in
+ its main window. This makes sense in the case when one has a lot of URLs and
+ few queries.
diff --git a/home-manager/doc/release-notes/rl-2009.adoc b/home-manager/doc/release-notes/rl-2009.adoc
new file mode 100644
index 00000000000..c1939ab5af9
--- /dev/null
+++ b/home-manager/doc/release-notes/rl-2009.adoc
@@ -0,0 +1,97 @@
+[[sec-release-20.09]]
+== Release 20.09 (unreleased)
+
+This is the current unstable branch and the information in this
+section is therefore not final.
+
+[[sec-release-20.09-highlights]]
+=== Highlights
+
+This release has the following notable changes:
+
+* Nothing has happened.
+
+[[sec-release-20.09-state-version-changes]]
+=== State Version Changes
+
+The state version in this release includes the changes below. These
+changes are only active if the `home.stateVersion` option is set to
+"20.09" or later.
+
+* The options <<opt-home.homeDirectory>> and <<opt-home.username>> no
+longer have default values and must therefore be provided in your
+configuration. Previously their values would default to the content of
+the environment variables `HOME` and `USER`, respectively.
++
+--
+Further, the options <<opt-xdg.cacheHome>>, <<opt-xdg.configHome>>,
+and <<opt-xdg.dataHome>> will no longer be affected by the
+`XDG_CACHE_HOME`, `XDG_CONFIG_HOME`, and `XDG_DATA_HOME` environment
+variables. They now unconditionally default to
+
+- `"${config.home.homeDirectory}/.cache"`,
+- `"${config.home.homeDirectory}/.config"`, and
+- `"${config.home.homeDirectory}/.local/share"`.
+
+If you choose to switch to state version 20.09 then you must set these
+options if you use non-default XDG base directory paths.
+
+The initial configuration generated by
+
+[source,console]
+$ nix-shell '<home-manager>' -A install
+
+will automatically include these options, when necessary.
+--
+
+* Git's `smtpEncryption` option is now set to `tls` only if both <<opt-accounts.email.accounts.\_name_.smtp.tls.enable>> and <<opt-accounts.email.accounts.\_name_.smtp.tls.useStartTls>> are `true`. If only <<opt-accounts.email.accounts.\_name_.smtp.tls.enable>> is `true`, `ssl` is used instead.
+
+* The `nixpkgs` module no longer references `<nixpkgs>`. Before it would do so when building the `pkgs` module argument. Starting with state version 20.09, the `pkgs` argument is instead built from the same Nixpkgs that was used to initialize the Home Manager modules. This is useful, for example, when using Home Manager within a Nix Flake. If you want to keep using `<nixpkgs>` with state version ≥ 20.09 then add
++
+[source,nix]
+_module.args.pkgsPath = <nixpkgs>;
++
+to your Home Manager configuration.
+
+* The options `wayland.windowManager.sway.config.bars` and `opt-xsession.windowManager.i3.config.bars` have been changed so that most of the suboptions are now nullable and default to `null`. The default for these two options has been changed to manually set the old defaults for each suboption. The overall effect is that if the `bars` options is not set, then the default remains the same. On the other hand, something like:
++
+--
+[source,nix]
+----
+bars = [ {
+ command = "waybar";
+} ];
+----
+will now create the config:
+....
+bar {
+ swaybar_command waybar
+}
+....
+instead of
+....
+bar {
+
+ font pango:monospace 8
+ mode dock
+ hidden_state hide
+ position bottom
+ status_command /nix/store/h7s6i9q1z5fxrlyyw5ls8vqxhf5bcs5a-i3status-2.13/bin/i3status
+ swaybar_command waybar
+ workspace_buttons yes
+ strip_workspace_numbers no
+ tray_output primary
+ colors {
+ background #000000
+ statusline #ffffff
+ separator #666666
+ focused_workspace #4c7899 #285577 #ffffff
+ active_workspace #333333 #5f676a #ffffff
+ inactive_workspace #333333 #222222 #888888
+ urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #2f343a #900000 #ffffff
+ }
+
+}
+....
+--
diff --git a/home-manager/doc/writing-modules.adoc b/home-manager/doc/writing-modules.adoc
new file mode 100644
index 00000000000..0f3336ff2c0
--- /dev/null
+++ b/home-manager/doc/writing-modules.adoc
@@ -0,0 +1,187 @@
+[[ch-writing-modules]]
+== Writing Home Manager Modules
+:writing-nixos-modules: https://nixos.org/nixos/manual/index.html#sec-writing-modules
+
+The module system in Home Manager is based entirely on the NixOS module system so we will here only highlight aspects that are specific for Home Manager. For information about the module system as such please refer to the {writing-nixos-modules}[Writing NixOS Modules] chapter of the NixOS manual.
+
+[[sec-option-types]]
+=== Option Types
+:wikipedia-dag: https://en.wikipedia.org/w/index.php?title=Directed_acyclic_graph&oldid=939656095
+:gvariant-description: https://developer.gnome.org/glib/stable/glib-GVariant.html#glib-GVariant.description
+
+Overall the basic option types are the same in Home Manager as NixOS. A few Home Manager options, however, make use of custom types that are worth describing in more detail. These are the option types `dagOf` and `gvariant` that are used, for example, by <<opt-programs.ssh.matchBlocks>> and <<opt-dconf.settings>>.
+
+`hm.types.dagOf`::
+Options of this type have attribute sets as values where each member is a node in a {wikipedia-dag}[directed acyclic graph] (DAG). This allows the attribute set entries to express dependency relations among themselves. This can, for example, be used to control the order of match blocks in a OpenSSH client configuration or the order of activation script blocks in <<opt-home.activation>>.
++
+A number of functions are provided to create DAG nodes. The functions are shown below with examples using an option `foo.bar` of type `hm.types.dagOf types.int`.
++
+`hm.dag.entryAnywhere (value: T)`:::
+Indicates that `value` can be placed anywhere within the DAG. This is also the default for plain attribute set entries, that is
++
+[source,nix]
+----
+foo.bar = {
+ a = hm.dag.entryAnywhere 0;
+}
+----
++
+and
++
+[source,nix]
+----
+foo.bar = {
+ a = 0;
+}
+----
++
+are equivalent.
++
+`hm.dag.entryAfter (afters: list string) (value: T)`:::
+Indicates that `value` must be placed _after_ each of the attribute names in the given list. For example
++
+[source,nix]
+----
+foo.bar = {
+ a = 0;
+ b = hm.dag.entryAfter [ "a" ] 1;
+}
+----
++
+would place `b` after `a` in the graph.
++
+`hm.dag.entryBefore (befores: list string) (value: T)`:::
+Indicates that `value` must be placed _before_ each of the attribute names in the given list. For example
++
+[source,nix]
+----
+foo.bar = {
+ b = hm.dag.entryBefore [ "a" ] 1;
+ a = 0;
+}
+----
++
+would place `b` before `a` in the graph.
++
+`hm.dag.entryBetween (befores: list string) (afters: list string) (value: T)`:::
+Indicates that `value` must be placed _before_ the attribute names in the first list and _after_ the attribute names in the second list. For example
++
+[source,nix]
+----
+foo.bar = {
+ a = 0;
+ c = hm.dag.entryBetween [ "b" ] [ "a" ] 2;
+ b = 1;
+}
+----
++
+would place `c` before `b` and after `a` in the graph.
+
+`hm.types.gvariant`::
+This type is useful for options representing {gvariant-description}[GVariant] values. The type accepts all primitive GVariant types as well as arrays and tuples. Dictionaries are not currently supported.
++
+To create a GVariant value you can use a number of provided functions. Examples assume an option `foo.bar` of type `hm.types.gvariant`.
++
+`hm.gvariant.mkBoolean (v: bool)`:::
+Takes a Nix value `v` to a GVariant `boolean` value. Note, Nix booleans are automatically coerced using this function. That is,
++
+[source,nix]
+----
+foo.bar = hm.gvariant.mkBoolean true;
+----
++
+is equivalent to
++
+[source,nix]
+----
+foo.bar = true;
+----
+`hm.gvariant.mkString (v: string)`:::
+Takes a Nix value `v` to a GVariant `string` value. Note, Nix strings are automatically coerced using this function. That is,
++
+[source,nix]
+----
+foo.bar = hm.gvariant.mkString "a string";
+----
++
+is equivalent to
++
+[source,nix]
+----
+foo.bar = "a string";
+----
+`hm.gvariant.mkObjectpath (v: string)`:::
+Takes a Nix value `v` to a GVariant `objectpath` value.
+`hm.gvariant.mkUchar (v: string)`:::
+Takes a Nix value `v` to a GVariant `uchar` value.
+`hm.gvariant.mkInt16 (v: int)`:::
+Takes a Nix value `v` to a GVariant `int16` value.
+`hm.gvariant.mkUint16 (v: int)`:::
+Takes a Nix value `v` to a GVariant `uint16` value.
+`hm.gvariant.mkInt32 (v: int)`:::
+Takes a Nix value `v` to a GVariant `int32` value. Note, Nix integers are automatically coerced using this function. That is,
++
+[source,nix]
+----
+foo.bar = hm.gvariant.mkInt32 7;
+----
++
+is equivalent to
++
+[source,nix]
+----
+foo.bar = 7;
+----
+`hm.gvariant.mkUint32 (v: int)`:::
+Takes a Nix value `v` to a GVariant `uint32` value.
+`hm.gvariant.mkInt64 (v: int)`:::
+Takes a Nix value `v` to a GVariant `int64` value.
+`hm.gvariant.mkUint64 (v: int)`:::
+Takes a Nix value `v` to a GVariant `uint64` value.
+`hm.gvariant.mkDouble (v: double)`:::
+Takes a Nix value `v` to a GVariant `double` value. Note, Nix floats are automatically coerced using this function. That is,
++
+[source,nix]
+----
+foo.bar = hm.gvariant.mkDouble 3.14;
+----
++
+is equivalent to
++
+[source,nix]
+----
+foo.bar = 3.14;
+----
++
+`hm.gvariant.mkArray type elements`:::
+Builds a GVariant array containing the given list of elements, where each element is a GVariant value of the given type. The `type` value can be constructed using
++
+--
+- `hm.gvariant.type.string`
+- `hm.gvariant.type.boolean`
+- `hm.gvariant.type.uchar`
+- `hm.gvariant.type.int16`
+- `hm.gvariant.type.uint16`
+- `hm.gvariant.type.int32`
+- `hm.gvariant.type.uint32`
+- `hm.gvariant.type.int64`
+- `hm.gvariant.type.uint64`
+- `hm.gvariant.type.double`
+- `hm.gvariant.type.arrayOf type`
+- `hm.gvariant.type.maybeOf type`
+- `hm.gvariant.type.tupleOf types`
+--
++
+where `type` and `types` are themselves a type and list of types, respectively.
++
+`hm.gvariant.mkEmptyArray type`:::
+An alias of `hm.gvariant.mkArray type []`.
++
+`hm.gvariant.mkNothing type`:::
+Builds a GVariant maybe value whose (non-existent) element is of the given type. The `type` value is constructed as described for the `mkArray` function above.
++
+`hm.gvariant.mkJust element`:::
+Builds a GVariant maybe value containing the given GVariant element.
++
+`hm.gvariant.mkTuple elements`:::
+Builds a GVariant tuple containing the given list of elements, where each element is a GVariant value.
diff --git a/home-manager/flake.nix b/home-manager/flake.nix
new file mode 100644
index 00000000000..2d53d603a61
--- /dev/null
+++ b/home-manager/flake.nix
@@ -0,0 +1,23 @@
+{
+ description = "Home Manager for Nix";
+
+ outputs = { self, nixpkgs }: rec {
+ nixosModules.home-manager = import ./nixos;
+
+ darwinModules.home-manager = import ./nix-darwin;
+
+ lib = {
+ homeManagerConfiguration = { configuration, system, homeDirectory
+ , username
+ , pkgs ? builtins.getAttr system nixpkgs.outputs.legacyPackages
+ , check ? true }@args:
+ import ./modules {
+ inherit pkgs check;
+ configuration = { ... }: {
+ imports = [ configuration ];
+ home = { inherit homeDirectory username; };
+ };
+ };
+ };
+ };
+}
diff --git a/home-manager/format b/home-manager/format
index 539ae60de22..8c771b6db7d 100755
--- a/home-manager/format
+++ b/home-manager/format
@@ -1,5 +1,5 @@
#! /usr/bin/env nix-shell
-#! nix-shell -i bash -p findutils nixfmt
+#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/05f0934825c2a0750d4888c4735f9420c906b388.tar.gz -i bash -p findutils nixfmt
CHECK_ARG=
@@ -20,26 +20,20 @@ find . -name '*.nix' \
\
! -path ./home-manager/default.nix \
! -path ./home-manager/home-manager.nix \
- ! -path ./modules/accounts/email.nix \
! -path ./modules/default.nix \
! -path ./modules/files.nix \
! -path ./modules/home-environment.nix \
! -path ./modules/lib/default.nix \
! -path ./modules/lib/file-type.nix \
- ! -path ./modules/lib/types.nix \
! -path ./modules/manual.nix \
! -path ./modules/misc/dconf.nix \
- ! -path ./modules/misc/gtk.nix \
! -path ./modules/misc/news.nix \
! -path ./modules/misc/nixpkgs.nix \
! -path ./modules/misc/xdg.nix \
! -path ./modules/modules.nix \
! -path ./modules/programs/afew.nix \
- ! -path ./modules/programs/alot.nix \
! -path ./modules/programs/bash.nix \
- ! -path ./modules/programs/emacs.nix \
! -path ./modules/programs/firefox.nix \
- ! -path ./modules/programs/fish.nix \
! -path ./modules/programs/gpg.nix \
! -path ./modules/programs/lesspipe.nix \
! -path ./modules/programs/neovim.nix \
@@ -60,11 +54,8 @@ find . -name '*.nix' \
! -path ./tests/modules/home-environment/session-variables.nix \
! -path ./tests/modules/programs/gpg/override-defaults.nix \
! -path ./tests/modules/programs/tmux/default.nix \
- ! -path ./tests/modules/programs/tmux/disable-confirmation-prompt.nix \
- ! -path ./tests/modules/programs/tmux/secure-socket-enabled.nix \
! -path ./tests/modules/programs/zsh/session-variables.nix \
! -path ./tests/modules/services/sxhkd/service.nix \
- ! -path ./tests/modules/systemd/default.nix \
! -path ./tests/modules/systemd/services.nix \
! -path ./tests/modules/systemd/session-variables.nix \
-exec nixfmt $CHECK_ARG {} +
diff --git a/home-manager/home-manager/completion.bash b/home-manager/home-manager/completion.bash
index 501b87279fa..e9f04578916 100644
--- a/home-manager/home-manager/completion.bash
+++ b/home-manager/home-manager/completion.bash
@@ -278,7 +278,7 @@ _home-manager_completions ()
#--------------------------#
local Subcommands
- Subcommands=( "help" "edit" "build" "switch" "generations" "remove-generations" "expire-generations" "packages" "news" "uninstall" )
+ Subcommands=( "help" "edit" "build" "instantiate" "switch" "generations" "remove-generations" "expire-generations" "packages" "news" "uninstall" )
# ^ « home-manager »'s subcommands.
diff --git a/home-manager/home-manager/home-manager b/home-manager/home-manager/home-manager
index d5a4c4f41a1..2ad91dc90c4 100644
--- a/home-manager/home-manager/home-manager
+++ b/home-manager/home-manager/home-manager
@@ -74,36 +74,48 @@ function setHomeManagerNixPath() {
done
}
-function doBuildAttr() {
+function doInstantiate() {
setConfigFile
setHomeManagerNixPath
- local extraArgs="$*"
+ local extraArgs=()
for p in "${EXTRA_NIX_PATH[@]}"; do
- extraArgs="$extraArgs -I $p"
+ extraArgs=("${extraArgs[@]}" "-I" "$p")
done
if [[ -v VERBOSE ]]; then
- extraArgs="$extraArgs --show-trace"
+ extraArgs=("${extraArgs[@]}" "--show-trace")
fi
- # shellcheck disable=2086
- if [[ -v USE_NIX2_COMMAND ]]; then
- nix build \
- -f "<home-manager/home-manager/home-manager.nix>" \
- $extraArgs \
- "${PASSTHROUGH_OPTS[@]}" \
- --argstr confPath "$HOME_MANAGER_CONFIG" \
- --argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
- else
- nix-build \
- "<home-manager/home-manager/home-manager.nix>" \
- $extraArgs \
- "${PASSTHROUGH_OPTS[@]}" \
- --argstr confPath "$HOME_MANAGER_CONFIG" \
- --argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
+ nix-instantiate \
+ "<home-manager/home-manager/home-manager.nix>" \
+ "${extraArgs[@]}" \
+ "${PASSTHROUGH_OPTS[@]}" \
+ --argstr confPath "$HOME_MANAGER_CONFIG" \
+ --argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
+}
+
+function doBuildAttr() {
+ setConfigFile
+ setHomeManagerNixPath
+
+ local extraArgs=("$@")
+
+ for p in "${EXTRA_NIX_PATH[@]}"; do
+ extraArgs=("${extraArgs[@]}" "-I" "$p")
+ done
+
+ if [[ -v VERBOSE ]]; then
+ extraArgs=("${extraArgs[@]}" "--show-trace")
fi
+
+ nix-build \
+ "<home-manager/home-manager/home-manager.nix>" \
+ "${extraArgs[@]}" \
+ "${PASSTHROUGH_OPTS[@]}" \
+ --argstr confPath "$HOME_MANAGER_CONFIG" \
+ --argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
}
# Presents news to the user. Takes as argument the path to a "news
@@ -169,13 +181,8 @@ function doBuild() {
local exitCode
- if [[ -v USE_NIX2_COMMAND ]]; then
- doBuildAttr activationPackage \
- && exitCode=0 || exitCode=1
- else
- doBuildAttr --attr activationPackage \
- && exitCode=0 || exitCode=1
- fi
+ doBuildAttr --attr activationPackage \
+ && exitCode=0 || exitCode=1
presentNews "$newsInfo"
@@ -197,17 +204,10 @@ function doSwitch() {
# before activation completes.
generation="$WORK_DIR/generation"
- if [[ -v USE_NIX2_COMMAND ]]; then
- doBuildAttr \
- --out-link "$generation" \
- activationPackage \
- && "$generation/activate" || exitCode=1
- else
- doBuildAttr \
- --out-link "$generation" \
- --attr activationPackage \
- && "$generation/activate" || exitCode=1
- fi
+ doBuildAttr \
+ --out-link "$generation" \
+ --attr activationPackage \
+ && "$generation/activate" || exitCode=1
presentNews "$newsInfo"
@@ -221,7 +221,7 @@ function doListGens() {
color="always"
fi
- pushd "/nix/var/nix/profiles/per-user/$USER" > /dev/null
+ pushd "$NIX_STATE_DIR/profiles/per-user/$USER" > /dev/null
# shellcheck disable=2012
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
| cut -d' ' -f 4- \
@@ -234,7 +234,7 @@ function doListGens() {
function doRmGenerations() {
setVerboseAndDryRun
- pushd "/nix/var/nix/profiles/per-user/$USER" > /dev/null
+ pushd "$NIX_STATE_DIR/profiles/per-user/$USER" > /dev/null
for generationId in "$@"; do
local linkName="home-manager-$generationId-link"
@@ -254,11 +254,11 @@ function doRmGenerations() {
function doRmAllGenerations() {
$DRY_RUN_CMD rm $VERBOSE_ARG \
- "/nix/var/nix/profiles/per-user/$USER/home-manager"*
+ "$NIX_STATE_DIR/profiles/per-user/$USER/home-manager"*
}
function doExpireGenerations() {
- local profileDir="/nix/var/nix/profiles/per-user/$USER"
+ local profileDir="$NIX_STATE_DIR/profiles/per-user/$USER"
local generations
generations="$( \
@@ -307,23 +307,14 @@ function buildNews() {
local output
output="$WORK_DIR/news-info.sh"
- if [[ -v USE_NIX2_COMMAND ]]; then
- doBuildAttr \
- --out-link "$output" \
- --quiet \
- --arg check false \
- --argstr newsReadIdsFile "$(newsReadIdsFile)" \
- newsInfo
- else
- doBuildAttr \
- --out-link "$output" \
- --no-build-output \
- --quiet \
- --arg check false \
- --argstr newsReadIdsFile "$(newsReadIdsFile)" \
- --attr newsInfo \
- > /dev/null
- fi
+ doBuildAttr \
+ --out-link "$output" \
+ --no-build-output \
+ --quiet \
+ --arg check false \
+ --argstr newsReadIdsFile "$(newsReadIdsFile)" \
+ --attr newsInfo \
+ > /dev/null
echo "$output"
}
@@ -375,13 +366,14 @@ function doUninstall() {
y|Y)
echo "Switching to empty Home Manager configuration..."
HOME_MANAGER_CONFIG="$(mktemp --tmpdir home-manager.XXXXXXXXXX)"
- echo "{}" > "$HOME_MANAGER_CONFIG"
+ echo "{ lib, ... }: { home.file = lib.mkForce {}; }" > "$HOME_MANAGER_CONFIG"
doSwitch
+ $DRY_RUN_CMD nix-env -e home-manager-path || true
rm "$HOME_MANAGER_CONFIG"
$DRY_RUN_CMD rm $VERBOSE_ARG -r \
"${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
$DRY_RUN_CMD rm $VERBOSE_ARG \
- "/nix/var/nix/gcroots/per-user/$USER/current-home"
+ "$NIX_STATE_DIR/gcroots/per-user/$USER/current-home"
;;
*)
echo "Yay!"
@@ -441,6 +433,8 @@ function doHelp() {
echo
echo " build Build configuration into result directory"
echo
+ echo " instantiate Instantiate the configuration and print the resulting derivation"
+ echo
echo " switch Build and activate configuration"
echo
echo " generations List all home environment generations"
@@ -461,6 +455,8 @@ function doHelp() {
echo " uninstall Remove Home Manager"
}
+readonly NIX_STATE_DIR="${NIX_STATE_DIR:-/nix/var/nix}"
+
EXTRA_NIX_PATH=()
HOME_MANAGER_CONFIG_ATTRIBUTE=""
PASSTHROUGH_OPTS=()
@@ -471,12 +467,9 @@ while [[ $# -gt 0 ]]; do
opt="$1"
shift
case $opt in
- build|edit|expire-generations|generations|help|news|packages|remove-generations|switch|uninstall)
+ build|instantiate|edit|expire-generations|generations|help|news|packages|remove-generations|switch|uninstall)
COMMAND="$opt"
;;
- -2)
- USE_NIX2_COMMAND=1
- ;;
-A)
HOME_MANAGER_CONFIG_ATTRIBUTE="$1"
shift
@@ -542,6 +535,9 @@ case $COMMAND in
build)
doBuild
;;
+ instantiate)
+ doInstantiate
+ ;;
switch)
doSwitch
;;
diff --git a/home-manager/home-manager/home-manager.nix b/home-manager/home-manager/home-manager.nix
index 7a6748942c8..04c2d28d32b 100644
--- a/home-manager/home-manager/home-manager.nix
+++ b/home-manager/home-manager/home-manager.nix
@@ -1,6 +1,6 @@
{ pkgs ? import <nixpkgs> {}
, confPath
-, confAttr
+, confAttr ? null
, check ? true
, newsReadIdsFile ? null
}:
@@ -11,7 +11,7 @@ let
env = import ../modules {
configuration =
- if confAttr == ""
+ if confAttr == "" || confAttr == null
then confPath
else (import confPath).${confAttr};
pkgs = pkgs;
diff --git a/home-manager/home-manager/install.nix b/home-manager/home-manager/install.nix
index 87aae50470e..87252730e6f 100644
--- a/home-manager/home-manager/install.nix
+++ b/home-manager/home-manager/install.nix
@@ -12,6 +12,18 @@ runCommand "home-manager-install" {
echo
echo "Creating initial Home Manager configuration..."
+ nl=$'\n'
+ xdgVars=""
+ if [[ -v XDG_CACHE_HOME && $XDG_CACHE_HOME != "$HOME/.cache" ]]; then
+ xdgVars="$xdgVars xdg.cacheHome = \"$XDG_CACHE_HOME\";$nl"
+ fi
+ if [[ -v XDG_CONFIG_HOME && $XDG_CONFIG_HOME != "$HOME/.config" ]]; then
+ xdgVars="$xdgVars xdg.configHome = \"$XDG_CONFIG_HOME\";$nl"
+ fi
+ if [[ -v XDG_DATA_HOME && $XDG_DATA_HOME != "$HOME/.local/share" ]]; then
+ xdgVars="$xdgVars xdg.dataHome = \"$XDG_DATA_HOME\";$nl"
+ fi
+
mkdir -p "$(dirname "$confFile")"
cat > $confFile <<EOF
{ config, pkgs, ... }:
@@ -20,6 +32,11 @@ runCommand "home-manager-install" {
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
+ # Home Manager needs a bit of information about you and the
+ # paths it should manage.
+ home.username = "$USER";
+ home.homeDirectory = "$HOME";
+ $xdgVars
# This value determines the Home Manager release that your
# configuration is compatible with. This helps avoid breakage
# when a new Home Manager release introduces backwards
@@ -28,7 +45,7 @@ runCommand "home-manager-install" {
# You can update Home Manager without changing this value. See
# the Home Manager release notes for a list of state version
# changes in each release.
- home.stateVersion = "19.09";
+ home.stateVersion = "20.09";
}
EOF
fi
diff --git a/home-manager/modules/accounts/email.nix b/home-manager/modules/accounts/email.nix
index b3a9db947a9..1e7aff94611 100644
--- a/home-manager/modules/accounts/email.nix
+++ b/home-manager/modules/accounts/email.nix
@@ -106,7 +106,7 @@ let
tls = mkOption {
type = tlsModule;
- default = {};
+ default = { };
description = ''
Configuration for secure connections.
'';
@@ -136,7 +136,7 @@ let
tls = mkOption {
type = tlsModule;
- default = {};
+ default = { };
description = ''
Configuration for secure connections.
'';
@@ -209,7 +209,7 @@ let
aliases = mkOption {
type = types.listOf (types.strMatching ".*@.*");
- default = [];
+ default = [ ];
example = [ "webmaster@example.org" "admin@example.org" ];
description = "Alternative email addresses of this account.";
};
@@ -276,7 +276,7 @@ let
};
};
};
- default = {};
+ default = { };
description = ''
Standard email folders.
'';
@@ -292,7 +292,7 @@ let
signature = mkOption {
type = signatureModule;
- default = {};
+ default = { };
description = ''
Signature configuration.
'';
@@ -332,9 +332,7 @@ let
(mkIf (config.flavor == "gmail.com") {
userName = mkDefault config.address;
- imap = {
- host = "imap.gmail.com";
- };
+ imap = { host = "imap.gmail.com"; };
smtp = {
host = "smtp.gmail.com";
@@ -343,20 +341,14 @@ let
})
(mkIf (config.flavor == "runbox.com") {
- imap = {
- host = "mail.runbox.com";
- };
+ imap = { host = "mail.runbox.com"; };
- smtp = {
- host = "mail.runbox.com";
- };
+ smtp = { host = "mail.runbox.com"; };
})
];
};
-in
-
-{
+in {
options.accounts.email = {
certificatesFile = mkOption {
type = types.path;
@@ -373,9 +365,7 @@ in
default = "${config.home.homeDirectory}/Maildir";
defaultText = "$HOME/Maildir";
apply = p:
- if hasPrefix "/" p
- then p
- else "${config.home.homeDirectory}/${p}";
+ if hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
description = ''
The base directory for account maildir directories. May be a
relative path, in which case it is relative the home
@@ -384,40 +374,23 @@ in
};
accounts = mkOption {
- type = types.attrsOf (types.submodule [
- mailAccountOpts
- (import ../programs/alot-accounts.nix pkgs)
- (import ../programs/astroid-accounts.nix)
- (import ../programs/getmail-accounts.nix)
- (import ../programs/mbsync-accounts.nix)
- (import ../programs/msmtp-accounts.nix)
- (import ../programs/neomutt-accounts.nix)
- (import ../programs/notmuch-accounts.nix)
- (import ../programs/offlineimap-accounts.nix)
- ]);
- default = {};
+ type = types.attrsOf (types.submodule mailAccountOpts);
+ default = { };
description = "List of email accounts.";
};
};
- config = mkIf (cfg.accounts != {}) {
+ config = mkIf (cfg.accounts != { }) {
assertions = [
- (
- let
- primaries =
- catAttrs "name"
- (filter (a: a.primary)
- (attrValues cfg.accounts));
- in
- {
- assertion = length primaries == 1;
- message =
- "Must have exactly one primary mail account but found "
- + toString (length primaries)
- + optionalString (length primaries > 1)
- (", namely " + concatStringsSep ", " primaries);
- }
- )
+ (let
+ primaries =
+ catAttrs "name" (filter (a: a.primary) (attrValues cfg.accounts));
+ in {
+ assertion = length primaries == 1;
+ message = "Must have exactly one primary mail account but found "
+ + toString (length primaries) + optionalString (length primaries > 1)
+ (", namely " + concatStringsSep ", " primaries);
+ })
];
};
}
diff --git a/home-manager/modules/files.nix b/home-manager/modules/files.nix
index 94b3aa565a8..09ecf715497 100644
--- a/home-manager/modules/files.nix
+++ b/home-manager/modules/files.nix
@@ -39,10 +39,24 @@ in
};
config = {
+ lib.file.mkOutOfStoreSymlink = path:
+ let
+ pathStr = toString path;
+ name = hm.strings.storeFileName (baseNameOf pathStr);
+ in
+ pkgs.runCommandLocal name {} ''ln -s ${escapeShellArg pathStr} $out'';
+
# This verifies that the links we are about to create will not
# overwrite an existing file.
home.activation.checkLinkTargets = hm.dag.entryBefore ["writeBoundary"] (
let
+ # Paths that should be forcibly overwritten by Home Manager.
+ # Caveat emptor!
+ forcedPaths =
+ concatMapStringsSep " " (p: ''"$HOME/${p}"'')
+ (mapAttrsToList (n: v: v.target)
+ (filterAttrs (n: v: v.force) cfg));
+
check = pkgs.writeText "check" ''
. ${./lib-bash/color-echo.sh}
@@ -50,12 +64,25 @@ in
# considered part of a Home Manager generation.
homeFilePattern="$(readlink -e "${builtins.storeDir}")/*-home-manager-files/*"
+ forcedPaths=(${forcedPaths})
+
newGenFiles="$1"
shift
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
- if [[ -e "$targetPath" \
+
+ forced=""
+ for forcedPath in "''${forcedPaths[@]}"; do
+ if [[ $targetPath == $forcedPath* ]]; then
+ forced="yeah"
+ break
+ fi
+ done
+
+ if [[ -n $forced ]]; then
+ $VERBOSE_ECHO "Skipping collision check for $targetPath"
+ elif [[ -e "$targetPath" \
&& ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
if [[ ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
@@ -63,10 +90,10 @@ in
errorEcho "Existing file '$backup' would be clobbered by backing up '$targetPath'"
collision=1
else
- warnEcho "Existing file '$targetPath' is in the way, will be moved to '$backup'"
+ warnEcho "Existing file '$targetPath' is in the way of '$sourcePath', will be moved to '$backup'"
fi
else
- errorEcho "Existing file '$targetPath' is in the way"
+ errorEcho "Existing file '$targetPath' is in the way of '$sourcePath'"
collision=1
fi
fi
@@ -142,7 +169,7 @@ in
if [[ -e "$newGenFiles/$relativePath" ]] ; then
$VERBOSE_ECHO "Checking $targetPath: exists"
elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
- warnEcho "Path '$targetPath' not link into Home Manager generation. Skipping delete."
+ warnEcho "Path '$targetPath' does not link into a Home Manager generation. Skipping delete."
else
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
$DRY_RUN_CMD rm $VERBOSE_ARG "$targetPath"
@@ -197,8 +224,7 @@ in
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
echo "Creating profile generation $newGenNum"
- $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenProfilePath"
- $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG $(basename "$newGenProfilePath") "$genProfilePath"
+ $DRY_RUN_CMD nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
$DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenGcPath"
else
echo "No change so reusing latest profile generation $oldGenNum"
@@ -231,7 +257,7 @@ in
home-files = pkgs.runCommand
"home-manager-files"
{
- nativeBuildInputs = [ pkgs.xlibs.lndir ];
+ nativeBuildInputs = [ pkgs.xorg.lndir ];
preferLocalBuild = true;
allowSubstitutes = false;
}
@@ -290,12 +316,15 @@ in
}
'' + concatStrings (
mapAttrsToList (n: v: ''
- insertFile "${sourceStorePath v}" \
- "${v.target}" \
- "${if v.executable == null
- then "inherit"
- else builtins.toString v.executable}" \
- "${builtins.toString v.recursive}"
+ insertFile ${
+ escapeShellArgs [
+ (sourceStorePath v)
+ v.target
+ (if v.executable == null
+ then "inherit"
+ else toString v.executable)
+ (toString v.recursive)
+ ]}
'') cfg
));
};
diff --git a/home-manager/modules/home-environment.nix b/home-manager/modules/home-environment.nix
index 0b2fb8f4ef9..2947433993e 100644
--- a/home-manager/modules/home-environment.nix
+++ b/home-manager/modules/home-environment.nix
@@ -16,11 +16,35 @@ let
'';
};
- address = mkOption {
+ ctype = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
- The language to use for addresses.
+ Character classification category.
+ '';
+ };
+
+ numeric = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for numerical values.
+ '';
+ };
+
+ time = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for formatting times.
+ '';
+ };
+
+ collate = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for collation (alphabetical ordering).
'';
};
@@ -32,6 +56,14 @@ let
'';
};
+ messages = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for messages, application UI languages, etc.
+ '';
+ };
+
paper = mkOption {
default = null;
type = types.nullOr types.str;
@@ -40,13 +72,38 @@ let
'';
};
- time = mkOption {
+ name = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
- The language to use for formatting times.
+ The language to use for personal names.
+ '';
+ };
+
+ address = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for addresses.
+ '';
+ };
+
+ telephone = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for telephone numbers.
'';
};
+
+ measurement = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The language to use for measurement values.
+ '';
+ };
+
};
};
@@ -125,14 +182,23 @@ in
options = {
home.username = mkOption {
type = types.str;
- defaultText = "$USER";
+ defaultText = literalExample ''
+ "$USER" for state version < 20.09,
+ undefined for state version ≥ 20.09
+ '';
+ example = "jane.doe";
description = "The user's username.";
};
home.homeDirectory = mkOption {
type = types.path;
- defaultText = "$HOME";
- description = "The user's home directory.";
+ defaultText = literalExample ''
+ "$HOME" for state version < 20.09,
+ undefined for state version ≥ 20.09
+ '';
+ apply = toString;
+ example = "/home/jane.doe";
+ description = "The user's home directory. Must be an absolute path.";
};
home.profileDirectory = mkOption {
@@ -198,6 +264,16 @@ in
'';
};
+ home.sessionVariablesExtra = mkOption {
+ type = types.lines;
+ default = "";
+ internal = true;
+ description = ''
+ Extra configuration to add to the
+ <filename>hm-session-vars.sh</filename> file.
+ '';
+ };
+
home.packages = mkOption {
type = types.listOf types.package;
default = [];
@@ -226,8 +302,8 @@ in
type = types.bool;
description = ''
Whether the activation script should start with an empty
- <envvar>PATH</envvar> variable. When <literal>false</literal>
- then the user's <envvar>PATH</envvar> will be used.
+ <envar>PATH</envar> variable. When <literal>false</literal>
+ then the user's <envar>PATH</envar> will be used.
'';
};
@@ -317,13 +393,17 @@ in
}
];
- home.username = mkDefault (builtins.getEnv "USER");
- home.homeDirectory = mkDefault (builtins.getEnv "HOME");
+ home.username =
+ mkIf (versionOlder config.home.stateVersion "20.09")
+ (mkDefault (builtins.getEnv "USER"));
+ home.homeDirectory =
+ mkIf (versionOlder config.home.stateVersion "20.09")
+ (mkDefault (builtins.getEnv "HOME"));
home.profileDirectory =
if config.submoduleSupport.enable
&& config.submoduleSupport.externalPackageInstall
- then config.home.path
+ then "/etc/profiles/per-user/${cfg.username}"
else cfg.homeDirectory + "/.nix-profile";
home.sessionVariables =
@@ -332,13 +412,27 @@ in
in
(maybeSet "LANG" cfg.language.base)
//
- (maybeSet "LC_ADDRESS" cfg.language.address)
+ (maybeSet "LC_CTYPE" cfg.language.ctype)
+ //
+ (maybeSet "LC_NUMERIC" cfg.language.numeric)
+ //
+ (maybeSet "LC_TIME" cfg.language.time)
+ //
+ (maybeSet "LC_COLLATE" cfg.language.collate)
//
(maybeSet "LC_MONETARY" cfg.language.monetary)
//
+ (maybeSet "LC_MESSAGES" cfg.language.messages)
+ //
(maybeSet "LC_PAPER" cfg.language.paper)
//
- (maybeSet "LC_TIME" cfg.language.time);
+ (maybeSet "LC_NAME" cfg.language.name)
+ //
+ (maybeSet "LC_ADDRESS" cfg.language.address)
+ //
+ (maybeSet "LC_TELEPHONE" cfg.language.telephone)
+ //
+ (maybeSet "LC_MEASUREMENT" cfg.language.measurement);
home.packages = [
# Provide a file holding all session variables.
@@ -352,7 +446,7 @@ in
export __HM_SESS_VARS_SOURCED=1
${config.lib.shell.exportAll cfg.sessionVariables}
- '';
+ '' + cfg.sessionVariablesExtra;
}
)
];
diff --git a/home-manager/modules/lib-bash/activation-init.sh b/home-manager/modules/lib-bash/activation-init.sh
index 5cdb66e5920..f95008ee75b 100755
--- a/home-manager/modules/lib-bash/activation-init.sh
+++ b/home-manager/modules/lib-bash/activation-init.sh
@@ -1,15 +1,19 @@
#!/usr/bin/env bash
function setupVars() {
- local profilesPath="/nix/var/nix/profiles/per-user/$USER"
- local gcPath="/nix/var/nix/gcroots/per-user/$USER"
- local greatestGenNum
+ local nixStateDir="${NIX_STATE_DIR:-/nix/var/nix}"
+ local profilesPath="$nixStateDir/profiles/per-user/$USER"
+ local gcPath="$nixStateDir/gcroots/per-user/$USER"
+
+ genProfilePath="$profilesPath/home-manager"
+ newGenPath="@GENERATION_DIR@";
+ newGenGcPath="$gcPath/current-home"
+ local greatestGenNum
greatestGenNum=$( \
- find "$profilesPath" -name 'home-manager-*-link' \
- | sed 's/^.*-\([0-9]*\)-link$/\1/' \
- | sort -rn \
- | head -1)
+ nix-env --list-generations --profile "$genProfilePath" \
+ | tail -1 \
+ | sed -E 's/ *([[:digit:]]+) .*/\1/')
if [[ -n $greatestGenNum ]] ; then
oldGenNum=$greatestGenNum
@@ -18,15 +22,15 @@ function setupVars() {
newGenNum=1
fi
- if [[ -e $gcPath/current-home ]] ; then
- oldGenPath="$(readlink -e "$gcPath/current-home")"
+ if [[ -e $profilesPath/home-manager ]] ; then
+ oldGenPath="$(readlink -e "$profilesPath/home-manager")"
fi
$VERBOSE_ECHO "Sanity checking oldGenNum and oldGenPath"
if [[ -v oldGenNum && ! -v oldGenPath
|| ! -v oldGenNum && -v oldGenPath ]]; then
- errorEcho "Invalid profile number and GC root values! These must be"
- errorEcho "either both empty or both set but are now set to"
+ errorEcho "Invalid profile number and current profile values! These"
+ errorEcho "must be either both empty or both set but are now set to"
errorEcho " '${oldGenNum:-}' and '${oldGenPath:-}'"
errorEcho "If you don't mind losing previous profile generations then"
errorEcho "the easiest solution is probably to run"
@@ -35,12 +39,6 @@ function setupVars() {
errorEcho "and trying home-manager switch again. Good luck!"
exit 1
fi
-
-
- genProfilePath="$profilesPath/home-manager"
- newGenPath="@GENERATION_DIR@";
- newGenProfilePath="$profilesPath/home-manager-$newGenNum-link"
- newGenGcPath="$gcPath/current-home"
}
if [[ -v VERBOSE ]]; then
@@ -53,6 +51,11 @@ fi
echo "Starting home manager activation"
+# Verify that we can connect to the Nix store and/or daemon. This will
+# also create the necessary directories in profiles and gcroots.
+$VERBOSE_ECHO "Sanity checking Nix"
+nix-build --expr '{}' --no-out-link
+
setupVars
if [[ -v DRY_RUN ]] ; then
@@ -78,6 +81,5 @@ else
fi
$VERBOSE_ECHO " newGenPath=$newGenPath"
$VERBOSE_ECHO " newGenNum=$newGenNum"
-$VERBOSE_ECHO " newGenProfilePath=$newGenProfilePath"
$VERBOSE_ECHO " newGenGcPath=$newGenGcPath"
$VERBOSE_ECHO " genProfilePath=$genProfilePath"
diff --git a/home-manager/modules/lib/default.nix b/home-manager/modules/lib/default.nix
index 6b2bbacc249..7c2c72f709c 100644
--- a/home-manager/modules/lib/default.nix
+++ b/home-manager/modules/lib/default.nix
@@ -16,8 +16,10 @@ rec {
entryBefore = d.dagEntryBefore;
};
+ gvariant = import ./gvariant.nix { inherit lib; };
+ maintainers = import ./maintainers.nix;
strings = import ./strings.nix { inherit lib; };
- types = import ./types.nix { inherit dag lib; };
+ types = import ./types.nix { inherit dag gvariant lib; };
shell = import ./shell.nix { inherit lib; };
zsh = import ./zsh.nix { inherit lib; };
diff --git a/home-manager/modules/lib/file-type.nix b/home-manager/modules/lib/file-type.nix
index 3096a6d37bb..56a3a1286a0 100644
--- a/home-manager/modules/lib/file-type.nix
+++ b/home-manager/modules/lib/file-type.nix
@@ -11,7 +11,7 @@ with lib;
# Arguments:
# - basePathDesc docbook compatible description of the base path
# - basePath the file base path
- fileType = basePathDesc: basePath: types.loaOf (types.submodule (
+ fileType = basePathDesc: basePath: types.attrsOf (types.submodule (
{ name, config, ... }: {
options = {
target = mkOption {
@@ -21,6 +21,7 @@ with lib;
absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
in
removePrefix (homeDirectory + "/") absPath;
+ defaultText = literalExample "<name>";
description = ''
Path to target file relative to ${basePathDesc}.
'';
@@ -29,17 +30,20 @@ with lib;
text = mkOption {
default = null;
type = types.nullOr types.lines;
- description = "Text of the file.";
+ description = ''
+ Text of the file. If this option is null then
+ <link linkend="opt-home.file._name_.source">home.file.&lt;name?&gt;.source</link>
+ must be set.
+ '';
};
source = mkOption {
type = types.path;
description = ''
- Path of the source file. The file name must not start
- with a period since Nix will not allow such names in
- the Nix store.
- </para><para>
- This may refer to a directory.
+ Path of the source file or directory. If
+ <link linkend="opt-home.file._name_.text">home.file.&lt;name?&gt;.text</link>
+ is non-null then this option will automatically point to a file
+ containing that text.
'';
};
@@ -80,6 +84,18 @@ with lib;
into place.
'';
};
+
+ force = mkOption {
+ type = types.bool;
+ default = false;
+ visible = false;
+ description = ''
+ Whether the target path should be unconditionally replaced
+ by the managed file source. Warning, this will silently
+ delete the target regardless of whether it is a file or
+ link.
+ '';
+ };
};
config = {
diff --git a/home-manager/modules/lib/gvariant.nix b/home-manager/modules/lib/gvariant.nix
new file mode 100644
index 00000000000..92aa7d98371
--- /dev/null
+++ b/home-manager/modules/lib/gvariant.nix
@@ -0,0 +1,156 @@
+# A partial and basic implementation of GVariant formatted strings.
+#
+# Note, this API is not considered fully stable and it might therefore
+# change in backwards incompatible ways without prior notice.
+
+{ lib }:
+
+with lib;
+
+let
+
+ mkPrimitive = t: v: {
+ _type = "gvariant";
+ type = t;
+ value = v;
+ __toString = self: "@${self.type} ${toString self.value}";
+ };
+
+ type = {
+ arrayOf = t: "a${t}";
+ maybeOf = t: "m${t}";
+ tupleOf = ts: "(${concatStrings ts})";
+ string = "s";
+ boolean = "b";
+ uchar = "y";
+ int16 = "n";
+ uint16 = "q";
+ int32 = "i";
+ uint32 = "u";
+ int64 = "x";
+ uint64 = "t";
+ double = "d";
+ };
+
+ # Returns the GVariant type of a given Nix value. If no type can be
+ # found for the value then the empty string is returned.
+ typeOf = v:
+ with type;
+ if builtins.isBool v then
+ boolean
+ else if builtins.isInt v then
+ int32
+ else if builtins.isFloat v then
+ double
+ else if builtins.isString v then
+ string
+ else if builtins.isList v then
+ let elemType = elemTypeOf v;
+ in if elemType == "" then "" else arrayOf elemType
+ else if builtins.isAttrs v && v ? type then
+ v.type
+ else
+ "";
+
+ elemTypeOf = vs:
+ if builtins.isList vs then
+ if vs == [ ] then "" else typeOf (head vs)
+ else
+ "";
+
+ mkMaybe = elemType: elem:
+ mkPrimitive (type.maybeOf elemType) elem // {
+ __toString = self:
+ if self.value == null then
+ "@${self.type} nothing"
+ else
+ "just ${toString self.value}";
+ };
+
+in rec {
+
+ inherit type typeOf;
+
+ isArray = hasPrefix "a";
+ isMaybe = hasPrefix "m";
+ isTuple = hasPrefix "(";
+
+ # Returns the GVariant value that most closely matches the given Nix
+ # value. If no GVariant value can be found then `null` is returned.
+ #
+ # No support for dictionaries, maybe types, or variants.
+ mkValue = v:
+ if builtins.isBool v then
+ mkBoolean v
+ else if builtins.isInt v then
+ mkInt32 v
+ else if builtins.isFloat v then
+ mkDouble v
+ else if builtins.isString v then
+ mkString v
+ else if builtins.isList v then
+ if v == [ ] then mkArray type.string [ ] else mkArray (elemTypeOf v) v
+ else if builtins.isAttrs v && (v._type or "") == "gvariant" then
+ v
+ else
+ null;
+
+ mkArray = elemType: elems:
+ mkPrimitive (type.arrayOf elemType) (map mkValue elems) // {
+ __toString = self:
+ "@${self.type} [${concatMapStringsSep "," toString self.value}]";
+ };
+
+ mkEmptyArray = elemType: mkArray elemType [ ];
+
+ mkNothing = elemType: mkMaybe elemType null;
+
+ mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
+
+ mkTuple = elems:
+ let
+ gvarElems = map mkValue elems;
+ tupleType = type.tupleOf (map (e: e.type) gvarElems);
+ in mkPrimitive tupleType gvarElems // {
+ __toString = self:
+ "@${self.type} (${concatMapStringsSep "," toString self.value})";
+ };
+
+ mkBoolean = v:
+ mkPrimitive type.boolean v // {
+ __toString = self: if self.value then "true" else "false";
+ };
+
+ mkString = v:
+ mkPrimitive type.string v // {
+ __toString = self: "'${escape [ "'" "\\" ] self.value}'";
+ };
+
+ mkObjectpath = v:
+ mkPrimitive type.string v // {
+ __toString = self: "objectpath '${escape [ "'" ] self.value}'";
+ };
+
+ mkUchar = mkPrimitive type.uchar;
+
+ mkInt16 = mkPrimitive type.int16;
+
+ mkUint16 = mkPrimitive type.uint16;
+
+ mkInt32 = v:
+ mkPrimitive type.int32 v // {
+ __toString = self: toString self.value;
+ };
+
+ mkUint32 = mkPrimitive type.uint32;
+
+ mkInt64 = mkPrimitive type.int64;
+
+ mkUint64 = mkPrimitive type.uint64;
+
+ mkDouble = v:
+ mkPrimitive type.double v // {
+ __toString = self: toString self.value;
+ };
+
+}
diff --git a/home-manager/modules/lib/maintainers.nix b/home-manager/modules/lib/maintainers.nix
new file mode 100644
index 00000000000..8cb8781228b
--- /dev/null
+++ b/home-manager/modules/lib/maintainers.nix
@@ -0,0 +1,44 @@
+# Home Manager maintainers.
+#
+# This attribute set contains Home Manager module maintainers that do
+# not have an entry in the Nixpkgs maintainer list [1]. Entries here
+# are expected to be follow the same format as described in [1].
+#
+# [1] https://github.com/NixOS/nixpkgs/blob/fca0d6e093c82b31103dc0dacc48da2a9b06e24b/maintainers/maintainer-list.nix#LC1
+
+{
+ justinlovinger = {
+ name = "Justin Lovinger";
+ email = "git@justinlovinger.com";
+ github = "JustinLovinger";
+ githubId = 7183441;
+ };
+ owm111 = {
+ email = "7798336+owm111@users.noreply.github.com";
+ name = "Owen McGrath";
+ github = "owm111";
+ githubId = 7798336;
+ };
+ cwyc = {
+ email = "cwyc@users.noreply.github.com";
+ name = "cwyc";
+ github = "cwyc";
+ githubId = 16950437;
+ };
+ berbiche = {
+ name = "Nicolas Berbiche";
+ email = "berbiche@users.noreply.github.com";
+ github = "berbiche";
+ githubId = 20448408;
+ keys = [{
+ longkeyid = "rsa4096/0xB461292445C6E696";
+ fingerprint = "D446 E58D 87A0 31C7 EC15 88D7 B461 2924 45C6 E696";
+ }];
+ };
+ olmokramer = {
+ name = "Olmo Kramer";
+ email = "olmokramer@users.noreply.github.com";
+ github = "olmokramer";
+ githubId = 3612514;
+ };
+}
diff --git a/home-manager/modules/lib/types-dag.nix b/home-manager/modules/lib/types-dag.nix
index 4dbdb907b0e..2efb12645d4 100644
--- a/home-manager/modules/lib/types-dag.nix
+++ b/home-manager/modules/lib/types-dag.nix
@@ -7,16 +7,25 @@ let
isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);
dagContentType = elemType:
- types.submodule {
+ types.submodule ({ name, ... }: {
options = {
data = mkOption { type = elemType; };
after = mkOption { type = with types; uniq (listOf str); };
before = mkOption { type = with types; uniq (listOf str); };
};
- };
+ config = mkIf (elemType.name == "submodule") {
+ data._module.args.dagName = name;
+ };
+ });
-in {
+in rec {
# A directed acyclic graph of some inner type.
+ #
+ # Note, if the element type is a submodule then the `name` argument
+ # will always be set to the string "data" since it picks up the
+ # internal structure of the DAG values. To give access to the
+ # "actual" attribute name a new submodule argument is provided with
+ # the name `dagName`.
dagOf = elemType:
let
convertAllToDags = let
@@ -51,34 +60,40 @@ in {
let padWidth = stringLength (toString (length list));
in fixedWidthNumber padWidth i;
- convertAllToDags = defs:
+ convertAll = loc: defs:
let
- convertAttrValue = n: v:
- if isDagEntry v then v else dag.entryAnywhere v;
-
- convertListValue = namePrefix: vs:
+ convertListValue = namePrefix: def:
let
+ vs = def.value;
pad = paddedIndexStr vs;
- makeEntry = i: v:
- nameValuePair "${namePrefix}.${pad i}" (dag.entryAnywhere v);
- in listToAttrs (imap1 makeEntry vs);
+ makeEntry = i: v: nameValuePair "${namePrefix}.${pad i}" v;
+ warning = ''
+ In file ${def.file}
+ a list is being assigned to the option '${
+ concatStringsSep "." loc
+ }'.
+ This will soon be an error due to the list form being deprecated.
+ Please use the attribute set form instead with DAG functions to
+ express the desired order of entries.
+ '';
+ in warn warning (listToAttrs (imap1 makeEntry vs));
- convertValue = i: value:
- if isList value then
- convertListValue "unnamed-${paddedIndexStr defs i}" value
+ convertValue = i: def:
+ if isList def.value then
+ convertListValue "unnamed-${paddedIndexStr defs i}" def
else
- mapAttrs convertAttrValue value;
- in imap1 (i: def: def // { value = convertValue i def.value; }) defs;
+ def.value;
+ in imap1 (i: def: def // { value = convertValue i def; }) defs;
- attrEquivalent = types.attrsOf (dagContentType elemType);
+ dagType = dagOf elemType;
in mkOptionType rec {
- name = "dagOf";
- description = "DAG of ${elemType.description}s";
- check = x: isAttrs x || isList x;
- merge = loc: defs: attrEquivalent.merge loc (convertAllToDags defs);
- getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]);
- getSubModules = elemType.getSubModules;
- substSubModules = m: dagOf (elemType.substSubModules m);
+ name = "listOrDagOf";
+ description = "list or DAG of ${elemType.description}s";
+ check = x: isList x || dagType.check x;
+ merge = loc: defs: dagType.merge loc (convertAll loc defs);
+ getSubOptions = dagType.getSubOptions;
+ getSubModules = dagType.getSubModules;
+ substSubModules = m: listOrDagOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
};
}
diff --git a/home-manager/modules/lib/types.nix b/home-manager/modules/lib/types.nix
index 78a875f519e..64a6b4a34fa 100644
--- a/home-manager/modules/lib/types.nix
+++ b/home-manager/modules/lib/types.nix
@@ -1,4 +1,5 @@
-{ lib, dag ? import ./dag.nix { inherit lib; } }:
+{ lib, dag ? import ./dag.nix { inherit lib; }
+, gvariant ? import ./gvariant.nix { inherit lib; } }:
with lib;
@@ -6,31 +7,84 @@ let
typesDag = import ./types-dag.nix { inherit dag lib; };
-in
+ # Needed since the type is called gvariant and its merge attribute
+ # must refer back to the type.
+ gvar = gvariant;
-{
+in rec {
inherit (typesDag) dagOf listOrDagOf;
selectorFunction = mkOptionType {
name = "selectorFunction";
- description =
- "Function that takes an attribute set and returns a list"
+ description = "Function that takes an attribute set and returns a list"
+ " containing a selection of the values of the input set";
check = isFunction;
- merge = _loc: defs:
- as: concatMap (select: select as) (getValues defs);
+ merge = _loc: defs: as: concatMap (select: select as) (getValues defs);
};
overlayFunction = mkOptionType {
name = "overlayFunction";
- description =
- "An overlay function, takes self and super and returns"
+ description = "An overlay function, takes self and super and returns"
+ " an attribute set overriding the desired attributes.";
check = isFunction;
- merge = _loc: defs:
- self: super:
- foldl' (res: def: mergeAttrs res (def.value self super)) {} defs;
+ merge = _loc: defs: self: super:
+ foldl' (res: def: mergeAttrs res (def.value self super)) { } defs;
+ };
+
+ fontType = types.submodule {
+ options = {
+ package = mkOption {
+ type = types.nullOr types.package;
+ default = null;
+ example = literalExample "pkgs.dejavu_fonts";
+ description = ''
+ Package providing the font. This package will be installed
+ to your profile. If <literal>null</literal> then the font
+ is assumed to already be available in your profile.
+ '';
+ };
+
+ name = mkOption {
+ type = types.str;
+ example = "DejaVu Sans 8";
+ description = ''
+ The family name and size of the font within the package.
+ '';
+ };
+ };
+ };
+
+ gvariant = mkOptionType rec {
+ name = "gvariant";
+ description = "GVariant value";
+ check = v: gvar.mkValue v != null;
+ merge = loc: defs:
+ let
+ vdefs = map (d: d // { value = gvar.mkValue d.value; }) defs;
+ vals = map (d: d.value) vdefs;
+ defTypes = map (x: x.type) vals;
+ sameOrNull = x: y: if x == y then y else null;
+ # A bit naive to just check the first entry…
+ sharedDefType = foldl' sameOrNull (head defTypes) defTypes;
+ allChecked = all (x: check x) vals;
+ in if sharedDefType == null then
+ throw ("Cannot merge definitions of `${showOption loc}' with"
+ + " mismatched GVariant types given in"
+ + " ${showFiles (getFiles defs)}.")
+ else if gvar.isArray sharedDefType && allChecked then
+ (types.listOf gvariant).merge loc
+ (map (d: d // { value = d.value.value; }) vdefs)
+ else if gvar.isTuple sharedDefType && allChecked then
+ mergeOneOption loc defs
+ else if gvar.isMaybe sharedDefType && allChecked then
+ mergeOneOption loc defs
+ else if gvar.type.string == sharedDefType && allChecked then
+ types.str.merge loc defs
+ else if gvar.type.double == sharedDefType && allChecked then
+ types.float.merge loc defs
+ else
+ mergeDefaultOption loc defs;
};
}
diff --git a/home-manager/modules/misc/dconf.nix b/home-manager/modules/misc/dconf.nix
index f5c9bf71456..5fc7748a76b 100644
--- a/home-manager/modules/misc/dconf.nix
+++ b/home-manager/modules/misc/dconf.nix
@@ -9,22 +9,7 @@ let
toDconfIni = generators.toINI { mkKeyValue = mkIniKeyValue; };
mkIniKeyValue = key: value:
- let
- tweakVal = v:
- if isString v then "'${v}'"
- else if isList v then tweakList v
- else if isBool v then (if v then "true" else "false")
- else toString v;
-
- # Assume empty list is a list of strings, see #769
- tweakList = v:
- if v == [] then "@as []"
- else "[" + concatMapStringsSep "," tweakVal v + "]";
-
- in
- "${key}=${tweakVal value}";
-
- primitive = with types; either bool (either int (either float str));
+ "${key}=${toString (hm.gvariant.mkValue value)}";
in
@@ -43,8 +28,7 @@ in
};
settings = mkOption {
- type = with types;
- attrsOf (attrsOf (either primitive (listOf primitive)));
+ type = with types; attrsOf (attrsOf hm.types.gvariant);
default = {};
example = literalExample ''
{
@@ -53,6 +37,7 @@ in
show-thousands = true;
base = 10;
word-size = 64;
+ window-position = lib.hm.gvariant.mkTuple [100 100];
};
}
'';
@@ -76,9 +61,9 @@ in
fi
if [[ -v DRY_RUN ]]; then
- echo $DCONF_DBUS_RUN_SESSION ${pkgs.gnome3.dconf}/bin/dconf load / "<" ${iniFile}
+ echo $DCONF_DBUS_RUN_SESSION ${pkgs.dconf}/bin/dconf load / "<" ${iniFile}
else
- $DCONF_DBUS_RUN_SESSION ${pkgs.gnome3.dconf}/bin/dconf load / < ${iniFile}
+ $DCONF_DBUS_RUN_SESSION ${pkgs.dconf}/bin/dconf load / < ${iniFile}
fi
unset DCONF_DBUS_RUN_SESSION
diff --git a/home-manager/modules/misc/debug.nix b/home-manager/modules/misc/debug.nix
new file mode 100644
index 00000000000..d27d496b423
--- /dev/null
+++ b/home-manager/modules/misc/debug.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+ options.home = {
+ enableDebugInfo = mkEnableOption "" // {
+ description = ''
+ Some Nix-packages provide debug symbols for
+ <command>gdb</command> in the <literal>debug</literal>-output.
+ This option ensures that those are automatically fetched from
+ the binary cache if available and <command>gdb</command> is
+ configured to find those symbols.
+ '';
+ };
+ };
+
+ config = mkIf config.home.enableDebugInfo {
+ home.extraOutputsToInstall = [ "debug" ];
+
+ home.sessionVariables = {
+ NIX_DEBUG_INFO_DIRS =
+ "$NIX_DEBUG_INFO_DIRS\${NIX_DEBUG_INFO_DIRS:+:}${config.home.profileDirectory}/lib/debug";
+ };
+ };
+}
diff --git a/home-manager/modules/misc/gtk.nix b/home-manager/modules/misc/gtk.nix
index 1222db4ecab..bf25aaaf664 100644
--- a/home-manager/modules/misc/gtk.nix
+++ b/home-manager/modules/misc/gtk.nix
@@ -11,44 +11,22 @@ let
toGtk3Ini = generators.toINI {
mkKeyValue = key: value:
let
- value' =
- if isBool value then (if value then "true" else "false")
- else toString value;
- in
- "${key}=${value'}";
+ value' = if isBool value then
+ (if value then "true" else "false")
+ else
+ toString value;
+ in "${key}=${value'}";
};
formatGtk2Option = n: v:
let
- v' =
- if isBool v then (if v then "true" else "false")
- else if isString v then "\"${v}\""
- else toString v;
- in
- "${n} = ${v'}";
-
- fontType = types.submodule {
- options = {
- package = mkOption {
- type = types.nullOr types.package;
- default = null;
- example = literalExample "pkgs.dejavu_fonts";
- description = ''
- Package providing the font. This package will be installed
- to your profile. If <literal>null</literal> then the font
- is assumed to already be available in your profile.
- '';
- };
-
- name = mkOption {
- type = types.str;
- example = "DejaVu Sans 8";
- description = ''
- The family name and size of the font within the package.
- '';
- };
- };
- };
+ v' = if isBool v then
+ (if v then "true" else "false")
+ else if isString v then
+ ''"${v}"''
+ else
+ toString v;
+ in "${n} = ${v'}";
themeType = types.submodule {
options = {
@@ -71,13 +49,11 @@ let
};
};
-in
-
-{
+in {
meta.maintainers = [ maintainers.rycee ];
imports = [
- (mkRemovedOptionModule ["gtk" "gtk3" "waylandSupport"] ''
+ (mkRemovedOptionModule [ "gtk" "gtk3" "waylandSupport" ] ''
This options is not longer needed and can be removed.
'')
];
@@ -87,7 +63,7 @@ in
enable = mkEnableOption "GTK 2/3 configuration";
font = mkOption {
- type = types.nullOr fontType;
+ type = types.nullOr hm.types.fontType;
default = null;
description = ''
The font to use in GTK+ 2/3 applications.
@@ -119,10 +95,20 @@ in
};
gtk3 = {
+ bookmarks = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "file:///home/jane/Documents" ];
+ description = "Bookmarks in the sidebar of the GTK file browser";
+ };
+
extraConfig = mkOption {
type = with types; attrsOf (either bool (either int str));
- default = {};
- example = { gtk-cursor-blink = false; gtk-recent-files-limit = 20; };
+ default = { };
+ example = {
+ gtk-cursor-blink = false;
+ gtk-recent-files-limit = 20;
+ };
description = ''
Extra configuration options to add to
<filename>~/.config/gtk-3.0/settings.ini</filename>.
@@ -141,48 +127,38 @@ in
};
};
- config = mkIf cfg.enable (
- let
- ini =
- optionalAttrs (cfg.font != null)
- { gtk-font-name = cfg.font.name; }
- //
- optionalAttrs (cfg.theme != null)
- { gtk-theme-name = cfg.theme.name; }
- //
- optionalAttrs (cfg.iconTheme != null)
- { gtk-icon-theme-name = cfg.iconTheme.name; };
-
- dconfIni =
- optionalAttrs (cfg.font != null)
- { font-name = cfg.font.name; }
- //
- optionalAttrs (cfg.theme != null)
- { gtk-theme = cfg.theme.name; }
- //
- optionalAttrs (cfg.iconTheme != null)
- { icon-theme = cfg.iconTheme.name; };
-
- optionalPackage = opt:
- optional (opt != null && opt.package != null) opt.package;
- in
- {
- home.packages =
- optionalPackage cfg.font
- ++ optionalPackage cfg.theme
- ++ optionalPackage cfg.iconTheme;
-
- home.file.".gtkrc-2.0".text =
- concatStringsSep "\n" (
- mapAttrsToList formatGtk2Option ini
- ) + "\n" + cfg2.extraConfig;
-
- xdg.configFile."gtk-3.0/settings.ini".text =
- toGtk3Ini { Settings = ini // cfg3.extraConfig; };
-
- xdg.configFile."gtk-3.0/gtk.css".text = cfg3.extraCss;
-
- dconf.settings."org/gnome/desktop/interface" = dconfIni;
- }
- );
+ config = mkIf cfg.enable (let
+ ini = optionalAttrs (cfg.font != null) { gtk-font-name = cfg.font.name; }
+ // optionalAttrs (cfg.theme != null) { gtk-theme-name = cfg.theme.name; }
+ // optionalAttrs (cfg.iconTheme != null) {
+ gtk-icon-theme-name = cfg.iconTheme.name;
+ };
+
+ dconfIni = optionalAttrs (cfg.font != null) { font-name = cfg.font.name; }
+ // optionalAttrs (cfg.theme != null) { gtk-theme = cfg.theme.name; }
+ // optionalAttrs (cfg.iconTheme != null) {
+ icon-theme = cfg.iconTheme.name;
+ };
+
+ optionalPackage = opt:
+ optional (opt != null && opt.package != null) opt.package;
+ in {
+ home.packages = optionalPackage cfg.font ++ optionalPackage cfg.theme
+ ++ optionalPackage cfg.iconTheme;
+
+ home.file.".gtkrc-2.0".text =
+ concatStringsSep "\n" (mapAttrsToList formatGtk2Option ini) + "\n"
+ + cfg2.extraConfig;
+
+ xdg.configFile."gtk-3.0/settings.ini".text =
+ toGtk3Ini { Settings = ini // cfg3.extraConfig; };
+
+ xdg.configFile."gtk-3.0/gtk.css".text = cfg3.extraCss;
+
+ xdg.configFile."gtk-3.0/bookmarks" = mkIf (cfg3.bookmarks != [ ]) {
+ text = concatStringsSep "\n" cfg3.bookmarks;
+ };
+
+ dconf.settings."org/gnome/desktop/interface" = dconfIni;
+ });
}
diff --git a/home-manager/modules/misc/news.nix b/home-manager/modules/misc/news.nix
index 6b01617fc55..b031af99685 100644
--- a/home-manager/modules/misc/news.nix
+++ b/home-manager/modules/misc/news.nix
@@ -452,7 +452,7 @@ in
{
time = "2017-12-11T17:23:12+00:00";
- condition = config.home.activation ? reloadSystemD;
+ condition = config.home.activation ? reloadSystemd;
message = ''
The Boolean option 'systemd.user.startServices' is now
available. When enabled the current naive systemd unit
@@ -1318,6 +1318,361 @@ in
A new module is available: 'programs.neomutt'.
'';
}
+
+ {
+ time = "2020-02-23T10:19:48+00:00";
+ message = ''
+ A new module is available: 'programs.kitty'.
+ '';
+ }
+
+ {
+ time = "2020-02-26T21:20:55+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'wayland.windowManager.sway'
+ '';
+ }
+
+ {
+ time = "2020-03-04T18:55:03+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'programs.abook'
+ '';
+ }
+
+ {
+ time = "2020-03-07T11:43:26+00:00";
+ condition = config.programs.fish.enable;
+ message = ''
+ The option 'programs.fish.functions' has been reworked in
+ order to support all available flags, such as
+ '--description', '--on-event', and more.
+ '';
+ }
+
+ {
+ time = "2020-03-07T13:11:43+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ The NixOS module has a new option: 'home-manager.useGlobalPkgs'.
+
+ This enables using the system configuration's 'pkgs'
+ argument in Home Manager.
+
+ To learn more, see the installation section of the manual
+
+ https://rycee.gitlab.io/home-manager/#sec-install-nixos-module
+ '';
+ }
+
+ {
+ time = "2020-03-07T14:12:50+00:00";
+ message = ''
+ A new module is available: 'programs.lieer'.
+ '';
+ }
+
+ {
+ time = "2020-03-07T14:12:50+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.lieer'.
+ '';
+ }
+
+ {
+ time = "2020-03-15T16:55:28+00:00";
+ condition = config.programs.firefox.enable;
+ message = ''
+ In anticipation of Firefox dropping support for extension
+ sideloading[1], we now install extensions directly to
+ Firefox profiles managed through Home Manager's
+
+ 'programs.firefox.profiles'
+
+ option.
+
+ Unfortunately this will most likely trigger an "Existing
+ file is in the way" error when activating your configuration
+ since Firefox keeps a copy of the add-on in the location
+ Home Manager wants to overwrite. If this is the case, remove
+ the listed '.xpi' files and try again.
+
+ This change also means that extensions installed through
+ Home Manager may disappear from unmanaged profiles in future
+ Firefox releases.
+
+ [1] https://blog.mozilla.org/addons/2019/10/31/firefox-to-discontinue-sideloaded-extensions/
+ '';
+ }
+
+ {
+ time = "2020-03-17T21:56:26+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.keynav'.
+ '';
+ }
+
+ {
+ time = "2020-03-24T22:17:20+00:00";
+ condition = config.services.compton.enable;
+ message = ''
+ The 'services.compton' module has been deprecated and
+ instead the new module 'services.picom' should be used. This
+ is because Nixpkgs no longer packages compton, and instead
+ packages the (mostly) compatible fork called picom.
+
+ The 'services.compton' and 'services.picom' modules have a
+ few differences:
+
+ - 'services.picom' has a new 'experimentalBackends'
+ option.
+
+ - 'vSync' is now a boolean value on 'services.picom', as
+ opposed to the string in 'services.compton'.
+
+ Migrating to the new picom service is simple - just change
+ all references to 'services.compton' to 'services.picom',
+ and adhere to the above changes.
+
+ The deprecated 'services.compton' will eventually be removed
+ in the future. Please update your configurations to use
+ 'services.picom' as soon as possible.
+ '';
+ }
+
+ {
+ time = "2020-04-08T09:33:05+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'targets.genericLinux'.
+
+ When enabled, this module will configure various settings
+ and environment variables to make Home Manager and programs
+ installed through Nix work better on GNU/Linux distributions
+ other than NixOS.
+
+ It should not be enabled if your Home Manager configuration
+ is deployed on a NixOS host.
+ '';
+ }
+
+ {
+ time = "2020-04-08T11:51:15+00:00";
+ message = ''
+ A new module is available: 'programs.qutebrowser'
+ '';
+ }
+
+ {
+ time = "2020-04-09T09:19:38+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.mako'
+ '';
+ }
+
+ {
+ time = "2020-04-23T19:45:26+00:00";
+ message = ''
+ A new module is available: 'programs.lf'
+ '';
+ }
+
+ {
+ time = "2020-04-26T13:46:28+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.pulseeffects'
+ '';
+ }
+
+ {
+ time = "2020-05-03T11:13:07+00:00";
+ message = ''
+ A new module is available: 'programs.i3status'
+ '';
+ }
+
+ {
+ time = "2020-05-03T11:21:42+00:00";
+ message = ''
+ A new module is available: 'programs.aria2'
+ '';
+ }
+
+ {
+ time = "2020-05-04T21:19:43+00:00";
+ condition = config.programs.git.enable;
+ message = ''
+ The Git module now supports the 'delta' syntax highlighter.
+
+ It can be enabled through the option 'programs.git.delta.enable'.
+ '';
+ }
+
+ {
+ time = "2020-05-12T20:09:54+00:00";
+ message = ''
+ A new module is available: 'programs.dircolors'
+ '';
+ }
+
+ {
+ time = "2020-05-26T17:13:58+00:00";
+ message = ''
+ A new module is available: 'programs.zoxide'
+ '';
+ }
+
+ {
+ time = "2020-06-03T17:46:11+00:00";
+ condition = config.programs.ssh.enable;
+ message = ''
+ The ssh module now supports the 'ServerAliveCountMax' option
+ both globally through
+
+ programs.ssh.serverAliveCountMax
+
+ and per match blocks
+
+ programs.ssh.matchBlocks.<name>.serverAliveCountMax
+ '';
+ }
+
+ {
+ time = "2020-06-11T18:06:37+00:00";
+ condition = hostPlatform.isLinux && config.services.emacs.enable;
+ message = ''
+ The Emacs service now supports systemd socket activation.
+
+ It can be enabled through the option 'services.emacs.socketActivation.enable'.
+ '';
+ }
+
+ {
+ time = "2020-06-12T17:48:01+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.clipmenu'
+ '';
+ }
+
+ {
+ time = "2020-06-12T07:08:09+00:00";
+ condition = config.programs.bash.enable;
+ message = ''
+ A new module is available: 'programs.powerline-go'
+ '';
+ }
+
+ {
+ time = "2020-06-14T13:30:19+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'service.fluidsynth'
+ '';
+ }
+
+ {
+ time = "2020-06-17T22:17:52+00:00";
+ condition = config.programs.git.enable;
+ message = ''
+ Since May 1, 2020 string values in Git configurations are
+ automatically escaped. If you have any manually escaped characters,
+ then you may need to restore them to their unescaped form to avoid
+ double escaping.
+
+ In other words, if you now have something along the lines of
+
+ programs.git.aliases.hello = '''"!echo $'Hello\\nWorld'"''';
+
+ you must replace it by the unescaped form
+
+ programs.git.aliases.hello = "!echo $'Hello\nWorld'";
+
+ Apologies for the belated notification!
+ '';
+ }
+
+ {
+ time = "2020-06-23T20:06:39+00:00";
+ message = ''
+ A new module is available: 'programs.ne'
+ '';
+ }
+
+ {
+ time = "2020-07-24T15:03:11+00:00";
+ message = ''
+ A new module is available: 'programs.nushell'.
+ '';
+ }
+
+ {
+ time = "2020-07-25T21:04:59+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.dropbox'.
+ '';
+ }
+
+ {
+ time = "2020-08-13T22:15:27+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'programs.waybar'
+ '';
+ }
+
+ {
+ time = "2020-08-14T22:44:20+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'services.kanshi'
+ '';
+ }
+
+ {
+ time = "2020-08-25T22:14:01+00:00";
+ message = ''
+ A new module is available: 'programs.mcfly'
+ '';
+ }
+
+ {
+ time = "2020-09-01T18:38:18+00:00";
+ message = ''
+ A new module is available: 'programs.ncmpcpp'
+ '';
+ }
+
+ {
+ time = "2020-09-11T10:06:47+00:00";
+ condition = hostPlatform.isLinux && config.targets.genericLinux.enable;
+ message = ''
+ A new option 'targets.genericLinux.extraXdgDataDirs' is available
+ to setup the user environment with the OS's data files.
+
+ This is useful for example to get Bash completion for
+ 'systemctl' which shouldn't be installed through Home Manager.
+
+ This is also useful to have non Home Manager applications
+ available in menus.
+ '';
+ }
+
+ {
+ time = "2020-09-09T06:54:59+00:00";
+ condition = config.programs.man.enable;
+ message = ''
+ A new option 'programs.man.generateCaches' was added to
+ support the apropos command.
+ '';
+ }
];
};
}
diff --git a/home-manager/modules/misc/nixpkgs.nix b/home-manager/modules/misc/nixpkgs.nix
index 7b0904a5f20..511dbec10b2 100644
--- a/home-manager/modules/misc/nixpkgs.nix
+++ b/home-manager/modules/misc/nixpkgs.nix
@@ -1,6 +1,6 @@
# Adapted from Nixpkgs.
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, pkgsPath, ... }:
with lib;
@@ -49,7 +49,7 @@ let
merge = lib.mergeOneOption;
};
- _pkgs = import <nixpkgs> (
+ _pkgs = import pkgsPath (
filterAttrs (n: v: v != null) config.nixpkgs
);
diff --git a/home-manager/modules/misc/numlock.nix b/home-manager/modules/misc/numlock.nix
index 199dd317daa..c823f6dbdd2 100644
--- a/home-manager/modules/misc/numlock.nix
+++ b/home-manager/modules/misc/numlock.nix
@@ -7,6 +7,8 @@ let
cfg = config.xsession.numlock;
in {
+ meta.maintainers = [ maintainers.evanjs ];
+
options = { xsession.numlock.enable = mkEnableOption "Num Lock"; };
config = mkIf cfg.enable {
diff --git a/home-manager/modules/misc/tmpfiles.nix b/home-manager/modules/misc/tmpfiles.nix
new file mode 100644
index 00000000000..c46fe2c553a
--- /dev/null
+++ b/home-manager/modules/misc/tmpfiles.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.systemd.user.tmpfiles;
+
+in {
+ meta.maintainers = [ maintainers.dawidsowa ];
+
+ options.systemd.user.tmpfiles.rules = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "L /home/user/Documents - - - - /mnt/data/Documents" ];
+ description = ''
+ Rules for creating and cleaning up temporary files
+ automatically. See
+ <citerefentry>
+ <refentrytitle>tmpfiles.d</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry>
+ for the exact format.
+ '';
+ };
+
+ config = mkIf (cfg.rules != [ ]) {
+ xdg = {
+ dataFile."user-tmpfiles.d/home-manager.conf" = {
+ text = ''
+ # This file is created automatically and should not be modified.
+ # Please change the option ‘systemd.user.tmpfiles.rules’ instead.
+ ${concatStringsSep "\n" cfg.rules}
+ '';
+ onChange = "${pkgs.systemd}/bin/systemd-tmpfiles --user --create";
+ };
+ configFile = {
+ "systemd/user/basic.target.wants/systemd-tmpfiles-setup.service".source =
+ "${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-setup.service";
+ "systemd/user/systemd-tmpfiles-setup.service".source =
+ "${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-setup.service";
+ "systemd/user/timers.target.wants/systemd-tmpfiles-clean.timer".source =
+ "${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.timer";
+ "systemd/user/systemd-tmpfiles-clean.service".source =
+ "${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.service";
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/misc/version.nix b/home-manager/modules/misc/version.nix
index 1352aadc614..fbeb3ec539a 100644
--- a/home-manager/modules/misc/version.nix
+++ b/home-manager/modules/misc/version.nix
@@ -5,7 +5,7 @@ with lib;
{
options = {
home.stateVersion = mkOption {
- type = types.enum [ "18.09" "19.03" "19.09" "20.03" ];
+ type = types.enum [ "18.09" "19.03" "19.09" "20.03" "20.09" ];
default = "18.09";
description = ''
It is occasionally necessary for Home Manager to change
diff --git a/home-manager/modules/misc/vte.nix b/home-manager/modules/misc/vte.nix
new file mode 100644
index 00000000000..fbe38c0163e
--- /dev/null
+++ b/home-manager/modules/misc/vte.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ meta.maintainers = [ maintainers.rycee ];
+
+ options.programs = let
+ description = ''
+ Whether to enable integration with terminals using the VTE
+ library. This will let the terminal track the current working
+ directory.
+ '';
+ in {
+ bash.enableVteIntegration = mkEnableOption "" // { inherit description; };
+
+ zsh.enableVteIntegration = mkEnableOption "" // { inherit description; };
+ };
+
+ config = mkMerge [
+ (mkIf config.programs.bash.enableVteIntegration {
+ # Unfortunately we have to do a little dance here to fix two
+ # problems with the upstream vte.sh file:
+ #
+ # - It does `PROMPT_COMMAND="__vte_prompt_command"` which
+ # clobbers any previously assigned prompt command.
+ #
+ # - Its `__vte_prompt_command` function runs commands that will
+ # overwrite the exit status of the command the user ran.
+ programs.bash.initExtra = ''
+ __HM_PROMPT_COMMAND="''${PROMPT_COMMAND:+''${PROMPT_COMMAND%;};}__hm_vte_prompt_command"
+ . ${pkgs.vte}/etc/profile.d/vte.sh
+ if [[ $(type -t __vte_prompt_command) = function ]]; then
+ __hm_vte_prompt_command() {
+ local old_exit_status=$?
+ __vte_prompt_command
+ return $old_exit_status
+ }
+ PROMPT_COMMAND="$__HM_PROMPT_COMMAND"
+ fi
+ unset __HM_PROMPT_COMMAND
+ '';
+ })
+
+ (mkIf config.programs.zsh.enableVteIntegration {
+ programs.zsh.initExtra = ''
+ . ${pkgs.vte}/etc/profile.d/vte.sh
+ '';
+ })
+ ];
+}
diff --git a/home-manager/modules/misc/xdg-mime-apps.nix b/home-manager/modules/misc/xdg-mime-apps.nix
index 7ba4083b3c0..81d2ba0fcbe 100644
--- a/home-manager/modules/misc/xdg-mime-apps.nix
+++ b/home-manager/modules/misc/xdg-mime-apps.nix
@@ -74,7 +74,7 @@ in {
config = mkIf cfg.enable {
# Deprecated but still used by some applications.
- home.file.".local/share/applications/mimeapps.list".source =
+ xdg.dataFile."applications/mimeapps.list".source =
config.xdg.configFile."mimeapps.list".source;
xdg.configFile."mimeapps.list".text =
diff --git a/home-manager/modules/misc/xdg-mime.nix b/home-manager/modules/misc/xdg-mime.nix
index 32006e025ff..5999e1299c9 100644
--- a/home-manager/modules/misc/xdg-mime.nix
+++ b/home-manager/modules/misc/xdg-mime.nix
@@ -27,10 +27,18 @@ in {
home.packages = [
# Explicitly install package to provide basic mime types.
pkgs.shared-mime-info
+
+ # Make sure the target directories will be real directories.
+ (pkgs.runCommandLocal "dummy-xdg-mime-dirs1" { } ''
+ mkdir -p $out/share/{applications,mime/packages}
+ '')
+ (pkgs.runCommandLocal "dummy-xdg-mime-dirs2" { } ''
+ mkdir -p $out/share/{applications,mime/packages}
+ '')
];
home.extraProfileCommands = ''
- if [[ -w $out/share/mime && -d $out/share/mime/packages ]]; then
+ if [[ -w $out/share/mime && -w $out/share/mime/packages && -d $out/share/mime/packages ]]; then
XDG_DATA_DIRS=$out/share \
PKGSYSTEM_ENABLE_FSYNC=0 \
${pkgs.buildPackages.shared-mime-info}/bin/update-mime-database \
diff --git a/home-manager/modules/misc/xdg-user-dirs.nix b/home-manager/modules/misc/xdg-user-dirs.nix
index da9d3c43ad9..a1db6b115a1 100644
--- a/home-manager/modules/misc/xdg-user-dirs.nix
+++ b/home-manager/modules/misc/xdg-user-dirs.nix
@@ -89,15 +89,22 @@ in {
};
config = mkIf cfg.enable {
- xdg.configFile."user-dirs.dirs".text = generators.toKeyValue { } ({
- XDG_DESKTOP_DIR = cfg.desktop;
- XDG_DOCUMENTS_DIR = cfg.documents;
- XDG_DOWNLOAD_DIR = cfg.download;
- XDG_MUSIC_DIR = cfg.music;
- XDG_PICTURES_DIR = cfg.pictures;
- XDG_PUBLICSHARE_DIR = cfg.publicShare;
- XDG_TEMPLATES_DIR = cfg.templates;
- XDG_VIDEOS_DIR = cfg.videos;
- } // cfg.extraConfig);
+ xdg.configFile."user-dirs.dirs".text = let
+ options = {
+ XDG_DESKTOP_DIR = cfg.desktop;
+ XDG_DOCUMENTS_DIR = cfg.documents;
+ XDG_DOWNLOAD_DIR = cfg.download;
+ XDG_MUSIC_DIR = cfg.music;
+ XDG_PICTURES_DIR = cfg.pictures;
+ XDG_PUBLICSHARE_DIR = cfg.publicShare;
+ XDG_TEMPLATES_DIR = cfg.templates;
+ XDG_VIDEOS_DIR = cfg.videos;
+ } // cfg.extraConfig;
+
+ # For some reason, these need to be wrapped with quotes to be valid.
+ wrapped = mapAttrs (_: value: ''"${value}"'') options;
+ in generators.toKeyValue { } wrapped;
+
+ xdg.configFile."user-dirs.conf".text = "enabled=False";
};
}
diff --git a/home-manager/modules/misc/xdg.nix b/home-manager/modules/misc/xdg.nix
index 84ab4ada59a..7420e8e92b3 100644
--- a/home-manager/modules/misc/xdg.nix
+++ b/home-manager/modules/misc/xdg.nix
@@ -85,12 +85,20 @@ in
};
})
- (mkIf (!cfg.enable) {
+ # Legacy non-deterministic setup.
+ (mkIf (!cfg.enable && versionOlder config.home.stateVersion "20.09") {
xdg.cacheHome = getXdgDir "XDG_CACHE_HOME" defaultCacheHome;
xdg.configHome = getXdgDir "XDG_CONFIG_HOME" defaultConfigHome;
xdg.dataHome = getXdgDir "XDG_DATA_HOME" defaultDataHome;
})
+ # "Modern" deterministic setup.
+ (mkIf (!cfg.enable && versionAtLeast config.home.stateVersion "20.09") {
+ xdg.cacheHome = mkDefault defaultCacheHome;
+ xdg.configHome = mkDefault defaultConfigHome;
+ xdg.dataHome = mkDefault defaultDataHome;
+ })
+
{
home.file = mkMerge [
cfg.configFile
diff --git a/home-manager/modules/modules.nix b/home-manager/modules/modules.nix
index 64418dbae11..08c978b177d 100644
--- a/home-manager/modules/modules.nix
+++ b/home-manager/modules/modules.nix
@@ -5,6 +5,9 @@
# Whether to enable module type checking.
, check ? true
+
+# If disabled, the pkgs attribute passed to this function is used instead.
+, useNixpkgsModule ? true
}:
with lib;
@@ -13,8 +16,6 @@ let
hostPlatform = pkgs.stdenv.hostPlatform;
- checkPlatform = any (meta.platformMatch pkgs.stdenv.hostPlatform);
-
loadModule = file: { condition ? true }: {
inherit file condition;
};
@@ -25,23 +26,28 @@ let
(loadModule ./home-environment.nix { })
(loadModule ./manual.nix { })
(loadModule ./misc/dconf.nix { })
+ (loadModule ./misc/debug.nix { })
(loadModule ./misc/fontconfig.nix { })
(loadModule ./misc/gtk.nix { })
(loadModule ./misc/lib.nix { })
(loadModule ./misc/news.nix { })
- (loadModule ./misc/nixpkgs.nix { })
+ (loadModule ./misc/nixpkgs.nix { condition = useNixpkgsModule; })
(loadModule ./misc/numlock.nix { condition = hostPlatform.isLinux; })
(loadModule ./misc/pam.nix { })
(loadModule ./misc/qt.nix { })
(loadModule ./misc/submodule-support.nix { })
+ (loadModule ./misc/tmpfiles.nix { condition = hostPlatform.isLinux; })
(loadModule ./misc/version.nix { })
+ (loadModule ./misc/vte.nix { })
(loadModule ./misc/xdg-mime.nix { condition = hostPlatform.isLinux; })
(loadModule ./misc/xdg-mime-apps.nix { condition = hostPlatform.isLinux; })
(loadModule ./misc/xdg-user-dirs.nix { condition = hostPlatform.isLinux; })
(loadModule ./misc/xdg.nix { })
+ (loadModule ./programs/abook.nix { condition = hostPlatform.isLinux; })
(loadModule ./programs/afew.nix { })
(loadModule ./programs/alacritty.nix { })
(loadModule ./programs/alot.nix { })
+ (loadModule ./programs/aria2.nix { })
(loadModule ./programs/astroid.nix { })
(loadModule ./programs/autorandr.nix { })
(loadModule ./programs/bash.nix { })
@@ -51,6 +57,7 @@ let
(loadModule ./programs/browserpass.nix { })
(loadModule ./programs/chromium.nix { condition = hostPlatform.isLinux; })
(loadModule ./programs/command-not-found/command-not-found.nix { })
+ (loadModule ./programs/dircolors.nix { })
(loadModule ./programs/direnv.nix { })
(loadModule ./programs/eclipse.nix { })
(loadModule ./programs/emacs.nix { })
@@ -65,30 +72,40 @@ let
(loadModule ./programs/gpg.nix { })
(loadModule ./programs/home-manager.nix { })
(loadModule ./programs/htop.nix { })
+ (loadModule ./programs/i3status.nix { })
(loadModule ./programs/info.nix { })
(loadModule ./programs/irssi.nix { })
+ (loadModule ./programs/lieer.nix { })
(loadModule ./programs/jq.nix { })
(loadModule ./programs/kakoune.nix { })
(loadModule ./programs/keychain.nix { })
+ (loadModule ./programs/kitty.nix { })
(loadModule ./programs/lesspipe.nix { })
+ (loadModule ./programs/lf.nix { })
(loadModule ./programs/lsd.nix { })
(loadModule ./programs/man.nix { })
(loadModule ./programs/matplotlib.nix { })
(loadModule ./programs/mbsync.nix { })
+ (loadModule ./programs/mcfly.nix { })
(loadModule ./programs/mercurial.nix { })
(loadModule ./programs/mpv.nix { })
(loadModule ./programs/msmtp.nix { })
+ (loadModule ./programs/ncmpcpp.nix { })
+ (loadModule ./programs/ne.nix { })
(loadModule ./programs/neomutt.nix { })
(loadModule ./programs/neovim.nix { })
(loadModule ./programs/newsboat.nix { })
(loadModule ./programs/noti.nix { })
(loadModule ./programs/notmuch.nix { })
+ (loadModule ./programs/nushell.nix { })
(loadModule ./programs/obs-studio.nix { })
(loadModule ./programs/offlineimap.nix { })
(loadModule ./programs/opam.nix { })
(loadModule ./programs/password-store.nix { })
(loadModule ./programs/pazi.nix { })
(loadModule ./programs/pidgin.nix { })
+ (loadModule ./programs/powerline-go.nix { })
+ (loadModule ./programs/qutebrowser.nix { })
(loadModule ./programs/readline.nix { })
(loadModule ./programs/rofi.nix { })
(loadModule ./programs/rtorrent.nix { })
@@ -103,27 +120,37 @@ let
(loadModule ./programs/vim.nix { })
(loadModule ./programs/vscode.nix { })
(loadModule ./programs/vscode/haskell.nix { })
+ (loadModule ./programs/waybar.nix { condition = hostPlatform.isLinux; })
(loadModule ./programs/z-lua.nix { })
(loadModule ./programs/zathura.nix { })
+ (loadModule ./programs/zoxide.nix { })
+ (loadModule ./programs/zplug.nix { })
(loadModule ./programs/zsh.nix { })
(loadModule ./services/blueman-applet.nix { })
(loadModule ./services/cbatticon.nix { condition = hostPlatform.isLinux; })
+ (loadModule ./services/clipmenu.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/compton.nix { })
+ (loadModule ./services/dropbox.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/dunst.nix { })
(loadModule ./services/dwm-status.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/emacs.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/flameshot.nix { })
+ (loadModule ./services/fluidsynth.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/getmail.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/gnome-keyring.nix { })
(loadModule ./services/gpg-agent.nix { })
(loadModule ./services/grobi.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/hound.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/imapnotify.nix { condition = hostPlatform.isLinux; })
+ (loadModule ./services/kanshi.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/kbfs.nix { })
(loadModule ./services/kdeconnect.nix { })
(loadModule ./services/keepassx.nix { })
(loadModule ./services/keybase.nix { })
+ (loadModule ./services/keynav.nix { condition = hostPlatform.isLinux; })
+ (loadModule ./services/lieer.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/lorri.nix { condition = hostPlatform.isLinux; })
+ (loadModule ./services/mako.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/mbsync.nix { })
(loadModule ./services/mpd.nix { })
(loadModule ./services/mpdris2.nix { condition = hostPlatform.isLinux; })
@@ -134,7 +161,9 @@ let
(loadModule ./services/parcellite.nix { })
(loadModule ./services/password-store-sync.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/pasystray.nix { })
+ (loadModule ./services/picom.nix { })
(loadModule ./services/polybar.nix { })
+ (loadModule ./services/pulseeffects.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/random-background.nix { })
(loadModule ./services/redshift.nix { })
(loadModule ./services/rsibreak.nix { condition = hostPlatform.isLinux; })
@@ -152,13 +181,16 @@ let
(loadModule ./services/unison.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/window-managers/awesome.nix { })
(loadModule ./services/window-managers/bspwm/default.nix { condition = hostPlatform.isLinux; })
- (loadModule ./services/window-managers/i3.nix { })
+ (loadModule ./services/window-managers/i3-sway/i3.nix { })
+ (loadModule ./services/window-managers/i3-sway/sway.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/window-managers/xmonad.nix { })
(loadModule ./services/xcape.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/xembed-sni-proxy.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/xscreensaver.nix { })
(loadModule ./services/xsuspender.nix { condition = hostPlatform.isLinux; })
(loadModule ./systemd.nix { })
+ (loadModule ./targets/darwin.nix { condition = hostPlatform.isDarwin; })
+ (loadModule ./targets/generic-linux.nix { condition = hostPlatform.isLinux; })
(loadModule ./xcursor.nix { })
(loadModule ./xresources.nix { })
(loadModule ./xsession.nix { })
@@ -168,12 +200,20 @@ let
modules = map (getAttr "file") (filter (getAttr "condition") allModules);
- pkgsModule = {
- config._module.args.baseModules = modules;
- config._module.args.pkgs = lib.mkDefault pkgs;
- config._module.check = check;
- config.lib = lib.hm;
- config.nixpkgs.system = mkDefault pkgs.system;
+ pkgsModule = { config, ... }: {
+ config = {
+ _module.args.baseModules = modules;
+ _module.args.pkgsPath = lib.mkDefault (
+ if versionAtLeast config.home.stateVersion "20.09" then
+ pkgs.path
+ else
+ <nixpkgs>);
+ _module.args.pkgs = lib.mkDefault pkgs;
+ _module.check = check;
+ lib = lib.hm;
+ } // optionalAttrs useNixpkgsModule {
+ nixpkgs.system = mkDefault pkgs.system;
+ };
};
in
diff --git a/home-manager/modules/programs/abook.nix b/home-manager/modules/programs/abook.nix
new file mode 100644
index 00000000000..4ddc080ad51
--- /dev/null
+++ b/home-manager/modules/programs/abook.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.abook;
+
+in {
+ options.programs.abook = {
+ enable = mkEnableOption "Abook";
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ field pager = Pager
+ view CONTACT = name, email
+ set autosave=true
+ '';
+ description = ''
+ Extra lines added to <filename>$HOME/.config/abook/abookrc</filename>.
+ Available configuration options are described in the abook repository:
+ <link xlink:href="https://sourceforge.net/p/abook/git/ci/master/tree/sample.abookrc" />.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.abook ];
+ xdg.configFile."abook/abookrc" = mkIf (cfg.extraConfig != "") {
+ text = ''
+ # Generated by Home Manager.
+ # See http://abook.sourceforge.net/
+
+ ${cfg.extraConfig}
+ '';
+ };
+ };
+}
diff --git a/home-manager/modules/programs/alacritty.nix b/home-manager/modules/programs/alacritty.nix
index 69b9ea9673d..ea908f2b056 100644
--- a/home-manager/modules/programs/alacritty.nix
+++ b/home-manager/modules/programs/alacritty.nix
@@ -11,6 +11,13 @@ in {
programs.alacritty = {
enable = mkEnableOption "Alacritty";
+ package = mkOption {
+ type = types.package;
+ default = pkgs.alacritty;
+ defaultText = literalExample "pkgs.alacritty";
+ description = "The Alacritty package to install.";
+ };
+
settings = mkOption {
type = types.attrs;
default = { };
@@ -41,7 +48,7 @@ in {
config = mkMerge [
(mkIf cfg.enable {
- home.packages = [ pkgs.alacritty ];
+ home.packages = [ cfg.package ];
xdg.configFile."alacritty/alacritty.yml" = mkIf (cfg.settings != { }) {
text =
diff --git a/home-manager/modules/programs/alot.nix b/home-manager/modules/programs/alot.nix
index 2b28f34caa3..e907cd3e0ac 100644
--- a/home-manager/modules/programs/alot.nix
+++ b/home-manager/modules/programs/alot.nix
@@ -7,167 +7,231 @@ let
cfg = config.programs.alot;
- alotAccounts = filter (a: a.notmuch.enable)
- (attrValues config.accounts.email.accounts);
+ alotAccounts =
+ filter (a: a.notmuch.enable) (attrValues config.accounts.email.accounts);
boolStr = v: if v then "True" else "False";
- accountStr = account: with account;
- concatStringsSep "\n" (
- [ "[[${name}]]" ]
- ++ mapAttrsToList (n: v: n + "=" + v) (
- {
- address = address;
- realname = realName;
- sendmail_command =
- optionalString (alot.sendMailCommand != null) alot.sendMailCommand;
- sent_box = "maildir" + "://" + maildir.absPath + "/" + folders.sent;
- draft_box = "maildir" + "://"+ maildir.absPath + "/" + folders.drafts;
- }
- // optionalAttrs (aliases != []) {
- aliases = concatStringsSep "," aliases;
- }
- // optionalAttrs (gpg != null) {
- gpg_key = gpg.key;
- encrypt_by_default = if gpg.encryptByDefault then "all" else "none";
- sign_by_default = boolStr gpg.signByDefault;
- }
- // optionalAttrs (signature.showSignature != "none") {
- signature = pkgs.writeText "signature.txt" signature.text;
- signature_as_attachment =
- boolStr (signature.showSignature == "attach");
- }
- )
- ++ [ alot.extraConfig ]
- ++ [ "[[[abook]]]" ]
- ++ mapAttrsToList (n: v: n + "=" + v) alot.contactCompletion
- );
-
- configFile =
- let
- bindingsToStr = attrSet:
- concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${v}") attrSet);
- in
- ''
- # Generated by Home Manager.
- # See http://alot.readthedocs.io/en/latest/configuration/config_options.html
-
- ${cfg.extraConfig}
-
- [bindings]
- ${bindingsToStr cfg.bindings.global}
-
- [[bufferlist]]
- ${bindingsToStr cfg.bindings.bufferlist}
- [[search]]
- ${bindingsToStr cfg.bindings.search}
- [[envelope]]
- ${bindingsToStr cfg.bindings.envelope}
- [[taglist]]
- ${bindingsToStr cfg.bindings.taglist}
- [[thread]]
- ${bindingsToStr cfg.bindings.thread}
-
- [accounts]
-
- ${concatStringsSep "\n\n" (map accountStr alotAccounts)}
- '';
-
-in
-
-{
- options.programs.alot = {
- enable = mkOption {
- type = types.bool;
- default = false;
- example = true;
- description = ''
- Whether to enable the Alot mail user agent. Alot uses the
- Notmuch email system and will therefore be automatically
- enabled for each email account that is managed by Notmuch.
- '';
- };
+ mkKeyValue = key: value:
+ let value' = if isBool value then boolStr value else toString value;
+ in "${key} = ${value'}";
+
+ mk2ndLevelSectionName = name: "[" + name + "]";
+
+ tagSubmodule = types.submodule {
+ options = {
+ translated = mkOption {
+ type = types.nullOr types.str;
+ description = ''
+ Fixed string representation for this tag. The tag can be
+ hidden from view, if the key translated is set to
+ <literal>""</literal>, the empty string.
+ '';
+ };
- hooks = mkOption {
- type = types.lines;
- default = "";
- description = ''
- Content of the hooks file.
- '';
- };
+ translation = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ A pair of strings that define a regular substitution to
+ compute the string representation on the fly using
+ <literal>re.sub</literal>.
+ '';
+ };
- bindings = mkOption {
- type = types.submodule {
- options = {
- global = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Global keybindings.";
- };
+ normal = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "'','', 'white','light red', 'white','#d66'";
+ description = ''
+ How to display the tag when unfocused.
+ See <link xlink:href="https://alot.readthedocs.io/en/latest/configuration/theming.html#tagstring-formatting"/>.
+ '';
+ };
- bufferlist = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Bufferlist mode keybindings.";
- };
+ focus = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "How to display the tag when focused.";
+ };
+ };
+ };
- search = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Search mode keybindings.";
- };
+ accountStr = account:
+ with account;
+ concatStringsSep "\n" ([ "[[${name}]]" ]
+ ++ mapAttrsToList (n: v: n + "=" + v) ({
+ address = address;
+ realname = realName;
+ sendmail_command =
+ optionalString (alot.sendMailCommand != null) alot.sendMailCommand;
+ sent_box = "maildir" + "://" + maildir.absPath + "/" + folders.sent;
+ draft_box = "maildir" + "://" + maildir.absPath + "/" + folders.drafts;
+ } // optionalAttrs (aliases != [ ]) {
+ aliases = concatStringsSep "," aliases;
+ } // optionalAttrs (gpg != null) {
+ gpg_key = gpg.key;
+ encrypt_by_default = if gpg.encryptByDefault then "all" else "none";
+ sign_by_default = boolStr gpg.signByDefault;
+ } // optionalAttrs (signature.showSignature != "none") {
+ signature = pkgs.writeText "signature.txt" signature.text;
+ signature_as_attachment = boolStr (signature.showSignature == "attach");
+ }) ++ [ alot.extraConfig ] ++ [ "[[[abook]]]" ]
+ ++ mapAttrsToList (n: v: n + "=" + v) alot.contactCompletion);
+
+ configFile = let
+ bindingsToStr = attrSet:
+ concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${v}") attrSet);
+ in ''
+ # Generated by Home Manager.
+ # See http://alot.readthedocs.io/en/latest/configuration/config_options.html
+
+ ${generators.toKeyValue { inherit mkKeyValue; } cfg.settings}
+ ${cfg.extraConfig}
+ [tags]
+ '' + (let
+ submoduleToAttrs = m:
+ filterAttrs (name: v: name != "_module" && v != null) m;
+ in generators.toINI { mkSectionName = mk2ndLevelSectionName; }
+ (mapAttrs (name: x: submoduleToAttrs x) cfg.tags)) + ''
+ [bindings]
+ ${bindingsToStr cfg.bindings.global}
+
+ [[bufferlist]]
+ ${bindingsToStr cfg.bindings.bufferlist}
+ [[search]]
+ ${bindingsToStr cfg.bindings.search}
+ [[envelope]]
+ ${bindingsToStr cfg.bindings.envelope}
+ [[taglist]]
+ ${bindingsToStr cfg.bindings.taglist}
+ [[thread]]
+ ${bindingsToStr cfg.bindings.thread}
+
+ [accounts]
+
+ ${concatStringsSep "\n\n" (map accountStr alotAccounts)}
+ '';
+
+in {
+ options = {
+ programs.alot = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Whether to enable the Alot mail user agent. Alot uses the
+ Notmuch email system and will therefore be automatically
+ enabled for each email account that is managed by Notmuch.
+ '';
+ };
- envelope = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Envelope mode keybindings.";
- };
+ hooks = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Content of the hooks file.
+ '';
+ };
- taglist = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Taglist mode keybindings.";
+ bindings = mkOption {
+ type = types.submodule {
+ options = {
+ global = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Global keybindings.";
+ };
+
+ bufferlist = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Bufferlist mode keybindings.";
+ };
+
+ search = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Search mode keybindings.";
+ };
+
+ envelope = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Envelope mode keybindings.";
+ };
+
+ taglist = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Taglist mode keybindings.";
+ };
+
+ thread = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Thread mode keybindings.";
+ };
};
+ };
+ default = { };
+ description = ''
+ Keybindings.
+ '';
+ };
- thread = mkOption {
- type = types.attrsOf types.str;
- default = {};
- description = "Thread mode keybindings.";
- };
+ tags = mkOption {
+ type = types.attrsOf tagSubmodule;
+ default = { };
+ description = "How to display the tags.";
+ };
+
+ settings = mkOption {
+ type = with types;
+ let primitive = either (either (either str int) bool) float;
+ in attrsOf primitive;
+ default = {
+ initial_command = "search tag:inbox AND NOT tag:killed";
+ auto_remove_unread = true;
+ handle_mouse = true;
+ prefer_plaintext = true;
};
+ example = literalExample ''
+ {
+ auto_remove_unread = true;
+ ask_subject = false;
+ thread_indent_replies = 2;
+ }
+ '';
+ description = ''
+ Configuration options added to alot configuration file.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Extra lines added to alot configuration file.
+ '';
};
- default = {};
- description = ''
- Keybindings.
- '';
};
- extraConfig = mkOption {
- type = types.lines;
- default = ''
- auto_remove_unread = True
- ask_subject = False
- handle_mouse = True
- initial_command = "search tag:inbox AND NOT tag:killed"
- input_timeout = 0.3
- prefer_plaintext = True
- thread_indent_replies = 4
- '';
- description = ''
- Extra lines added to alot configuration file.
- '';
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./alot-accounts.nix pkgs));
};
};
config = mkIf cfg.enable {
- home.packages = [ pkgs.alot ];
+ home.packages = [ pkgs.alot ];
xdg.configFile."alot/config".text = configFile;
- xdg.configFile."alot/hooks.py".text =
- ''
+ xdg.configFile."alot/hooks.py" = mkIf (cfg.hooks != "") {
+ text = ''
# Generated by Home Manager.
- ''
- + cfg.hooks;
+ '' + cfg.hooks;
+ };
};
}
diff --git a/home-manager/modules/programs/aria2.nix b/home-manager/modules/programs/aria2.nix
new file mode 100644
index 00000000000..d1317ff7616
--- /dev/null
+++ b/home-manager/modules/programs/aria2.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.aria2;
+
+ formatLine = n: v:
+ let
+ formatValue = v:
+ if builtins.isBool v then
+ (if v then "true" else "false")
+ else
+ toString v;
+ in "${n}=${formatValue v}";
+in {
+ meta.maintainers = [ hm.maintainers.justinlovinger ];
+
+ options.programs.aria2 = {
+ enable = mkEnableOption "aria2";
+
+ settings = mkOption {
+ type = with types; attrsOf (oneOf [ bool float int str ]);
+ default = { };
+ description = ''
+ Options to add to <filename>aria2.conf</filename> file.
+ See
+ <citerefentry>
+ <refentrytitle>aria2c</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ listen-port = 60000;
+ dht-listen-port = 60000;
+ seed-ratio = 1.0;
+ max-upload-limit = "50K";
+ ftp-pasv = true;
+ }
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Extra lines added to <filename>aria2.conf</filename> file.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.aria2 ];
+
+ xdg.configFile."aria2/aria2.conf".text = concatStringsSep "\n" ([ ]
+ ++ mapAttrsToList formatLine cfg.settings
+ ++ optional (cfg.extraConfig != "") cfg.extraConfig);
+ };
+}
diff --git a/home-manager/modules/programs/astroid.nix b/home-manager/modules/programs/astroid.nix
index 8b3762fac0b..af12b10edbb 100644
--- a/home-manager/modules/programs/astroid.nix
+++ b/home-manager/modules/programs/astroid.nix
@@ -98,6 +98,10 @@ in {
'';
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./astroid-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/autorandr.nix b/home-manager/modules/programs/autorandr.nix
index 02fc77c1e58..40cad704db9 100644
--- a/home-manager/modules/programs/autorandr.nix
+++ b/home-manager/modules/programs/autorandr.nix
@@ -55,6 +55,13 @@ let
default = true;
};
+ crtc = mkOption {
+ type = types.nullOr types.ints.unsigned;
+ description = "Output video display controller.";
+ default = null;
+ example = 0;
+ };
+
primary = mkOption {
type = types.bool;
description = "Whether output should be marked as primary";
@@ -244,21 +251,22 @@ let
]);
fingerprintToString = name: edid: "${name} ${edid}";
configToString = name: config:
- if config.enable then ''
- output ${name}
- ${optionalString (config.position != "") "pos ${config.position}"}
- ${optionalString config.primary "primary"}
- ${optionalString (config.dpi != null) "dpi ${toString config.dpi}"}
- ${optionalString (config.gamma != "") "gamma ${config.gamma}"}
- ${optionalString (config.mode != "") "mode ${config.mode}"}
- ${optionalString (config.rate != "") "rate ${config.rate}"}
- ${optionalString (config.rotate != null) "rotate ${config.rotate}"}
- ${optionalString (config.scale != null)
- ((if config.scale.method == "factor" then "scale" else "scale-from")
- + " ${toString config.scale.x}x${toString config.scale.y}")}
- ${optionalString (config.transform != null) ("transform "
- + concatMapStringsSep "," toString (flatten config.transform))}
- '' else ''
+ if config.enable then
+ concatStringsSep "\n" ([ "output ${name}" ]
+ ++ optional (config.position != "") "pos ${config.position}"
+ ++ optional (config.crtc != null) "crtc ${toString config.crtc}"
+ ++ optional config.primary "primary"
+ ++ optional (config.dpi != null) "dpi ${toString config.dpi}"
+ ++ optional (config.gamma != "") "gamma ${config.gamma}"
+ ++ optional (config.mode != "") "mode ${config.mode}"
+ ++ optional (config.rate != "") "rate ${config.rate}"
+ ++ optional (config.rotate != null) "rotate ${config.rotate}"
+ ++ optional (config.transform != null) ("transform "
+ + concatMapStringsSep "," toString (flatten config.transform))
+ ++ optional (config.scale != null)
+ ((if config.scale.method == "factor" then "scale" else "scale-from")
+ + " ${toString config.scale.x}x${toString config.scale.y}"))
+ else ''
output ${name}
off
'';
@@ -315,6 +323,7 @@ in {
eDP1.enable = false;
DP1 = {
enable = true;
+ crtc = 0;
primary = true;
position = "0x0";
mode = "3840x2160";
diff --git a/home-manager/modules/programs/bash.nix b/home-manager/modules/programs/bash.nix
index 82a9fbe8f8b..45fe368bddc 100644
--- a/home-manager/modules/programs/bash.nix
+++ b/home-manager/modules/programs/bash.nix
@@ -82,7 +82,12 @@ in
shellAliases = mkOption {
default = {};
type = types.attrsOf types.str;
- example = { ll = "ls -l"; ".." = "cd .."; };
+ example = literalExample ''
+ {
+ ll = "ls -l";
+ ".." = "cd ..";
+ }
+ '';
description = ''
An attribute set that maps aliases (the top level attribute names in
this option) to command strings or directly to build outputs.
@@ -171,10 +176,10 @@ in
${aliasesStr}
- ${cfg.initExtra}
-
${optionalString cfg.enableAutojump
". ${pkgs.autojump}/share/autojump/autojump.bash"}
+
+ ${cfg.initExtra}
fi
'';
diff --git a/home-manager/modules/programs/bat.nix b/home-manager/modules/programs/bat.nix
index aa0df9abd45..e2b30ea9333 100644
--- a/home-manager/modules/programs/bat.nix
+++ b/home-manager/modules/programs/bat.nix
@@ -24,14 +24,35 @@ in {
'';
};
+ themes = mkOption {
+ type = types.attrsOf types.lines;
+ default = { };
+ example = literalExample ''
+ {
+ dracula = builtins.readFile (pkgs.fetchFromGitHub {
+ owner = "dracula";
+ repo = "sublime"; # Bat uses sublime syntax for its themes
+ rev = "26c57ec282abcaa76e57e055f38432bd827ac34e";
+ sha256 = "019hfl4zbn4vm4154hh3bwk6hm7bdxbr1hdww83nabxwjn99ndhv";
+ } + "/Dracula.tmTheme");
+ }
+ '';
+ description = ''
+ Additional themes to provide.
+ '';
+ };
+
};
config = mkIf cfg.enable {
home.packages = [ pkgs.bat ];
- xdg.configFile."bat/config" = mkIf (cfg.config != { }) {
- text = concatStringsSep "\n"
- (mapAttrsToList (n: v: ''--${n}="${v}"'') cfg.config);
- };
+ xdg.configFile = mkMerge ([{
+ "bat/config" = mkIf (cfg.config != { }) {
+ text = concatStringsSep "\n"
+ (mapAttrsToList (n: v: ''--${n}="${v}"'') cfg.config);
+ };
+ }] ++ flip mapAttrsToList cfg.themes
+ (name: body: { "bat/themes/${name}.tmTheme" = { text = body; }; }));
};
}
diff --git a/home-manager/modules/programs/broot.nix b/home-manager/modules/programs/broot.nix
index eac31b56801..6951e035d32 100644
--- a/home-manager/modules/programs/broot.nix
+++ b/home-manager/modules/programs/broot.nix
@@ -172,11 +172,11 @@ in {
xdg.configFile."broot/conf.toml".source = configFile brootConf;
# Dummy file to prevent broot from trying to reinstall itself
- xdg.configFile."broot/launcher/installed".text = "";
+ xdg.configFile."broot/launcher/installed-v1".text = "";
programs.bash.initExtra = mkIf cfg.enableBashIntegration (
- # Using mkAfter to make it more likely to appear after other
- # manipulations of the prompt.
+ # Using mkAfter to make it more likely to appear after other
+ # manipulations of the prompt.
mkAfter ''
# This script was automatically generated by the broot function
# More information can be found in https://github.com/Canop/broot
diff --git a/home-manager/modules/programs/dircolors.nix b/home-manager/modules/programs/dircolors.nix
new file mode 100644
index 00000000000..026de72d711
--- /dev/null
+++ b/home-manager/modules/programs/dircolors.nix
@@ -0,0 +1,223 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.dircolors;
+
+ formatLine = n: v: "${n} ${toString v}";
+in {
+ meta.maintainers = [ hm.maintainers.justinlovinger ];
+
+ options.programs.dircolors = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to manage <filename>.dir_colors</filename>
+ and set <code>LS_COLORS</code>.
+ '';
+ };
+
+ enableBashIntegration = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable Bash integration.
+ '';
+ };
+
+ enableFishIntegration = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable Fish integration.
+ '';
+ };
+
+ enableZshIntegration = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable Zsh integration.
+ '';
+ };
+
+ settings = mkOption {
+ type = with types; attrsOf str;
+ default = { };
+ description = ''
+ Options to add to <filename>.dir_colors</filename> file.
+ See <command>dircolors --print-database</command>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ OTHER_WRITABLE = "30;46";
+ ".sh" = "01;32";
+ ".csh" = "01;32";
+ }
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Extra lines added to <filename>.dir_colors</filename> file.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ # Add default settings from `dircolors --print-database`.
+ programs.dircolors.settings = {
+ RESET = mkDefault "0";
+ DIR = mkDefault "01;34";
+ LINK = mkDefault "01;36";
+ MULTIHARDLINK = mkDefault "00";
+ FIFO = mkDefault "40;33";
+ SOCK = mkDefault "01;35";
+ DOOR = mkDefault "01;35";
+ BLK = mkDefault "40;33;01";
+ CHR = mkDefault "40;33;01";
+ ORPHAN = mkDefault "40;31;01";
+ MISSING = mkDefault "00";
+ SETUID = mkDefault "37;41";
+ SETGID = mkDefault "30;43";
+ CAPABILITY = mkDefault "30;41";
+ STICKY_OTHER_WRITABLE = mkDefault "30;42";
+ OTHER_WRITABLE = mkDefault "34;42";
+ STICKY = mkDefault "37;44";
+ EXEC = mkDefault "01;32";
+ ".tar" = mkDefault "01;31";
+ ".tgz" = mkDefault "01;31";
+ ".arc" = mkDefault "01;31";
+ ".arj" = mkDefault "01;31";
+ ".taz" = mkDefault "01;31";
+ ".lha" = mkDefault "01;31";
+ ".lz4" = mkDefault "01;31";
+ ".lzh" = mkDefault "01;31";
+ ".lzma" = mkDefault "01;31";
+ ".tlz" = mkDefault "01;31";
+ ".txz" = mkDefault "01;31";
+ ".tzo" = mkDefault "01;31";
+ ".t7z" = mkDefault "01;31";
+ ".zip" = mkDefault "01;31";
+ ".z" = mkDefault "01;31";
+ ".dz" = mkDefault "01;31";
+ ".gz" = mkDefault "01;31";
+ ".lrz" = mkDefault "01;31";
+ ".lz" = mkDefault "01;31";
+ ".lzo" = mkDefault "01;31";
+ ".xz" = mkDefault "01;31";
+ ".zst" = mkDefault "01;31";
+ ".tzst" = mkDefault "01;31";
+ ".bz2" = mkDefault "01;31";
+ ".bz" = mkDefault "01;31";
+ ".tbz" = mkDefault "01;31";
+ ".tbz2" = mkDefault "01;31";
+ ".tz" = mkDefault "01;31";
+ ".deb" = mkDefault "01;31";
+ ".rpm" = mkDefault "01;31";
+ ".jar" = mkDefault "01;31";
+ ".war" = mkDefault "01;31";
+ ".ear" = mkDefault "01;31";
+ ".sar" = mkDefault "01;31";
+ ".rar" = mkDefault "01;31";
+ ".alz" = mkDefault "01;31";
+ ".ace" = mkDefault "01;31";
+ ".zoo" = mkDefault "01;31";
+ ".cpio" = mkDefault "01;31";
+ ".7z" = mkDefault "01;31";
+ ".rz" = mkDefault "01;31";
+ ".cab" = mkDefault "01;31";
+ ".wim" = mkDefault "01;31";
+ ".swm" = mkDefault "01;31";
+ ".dwm" = mkDefault "01;31";
+ ".esd" = mkDefault "01;31";
+ ".jpg" = mkDefault "01;35";
+ ".jpeg" = mkDefault "01;35";
+ ".mjpg" = mkDefault "01;35";
+ ".mjpeg" = mkDefault "01;35";
+ ".gif" = mkDefault "01;35";
+ ".bmp" = mkDefault "01;35";
+ ".pbm" = mkDefault "01;35";
+ ".pgm" = mkDefault "01;35";
+ ".ppm" = mkDefault "01;35";
+ ".tga" = mkDefault "01;35";
+ ".xbm" = mkDefault "01;35";
+ ".xpm" = mkDefault "01;35";
+ ".tif" = mkDefault "01;35";
+ ".tiff" = mkDefault "01;35";
+ ".png" = mkDefault "01;35";
+ ".svg" = mkDefault "01;35";
+ ".svgz" = mkDefault "01;35";
+ ".mng" = mkDefault "01;35";
+ ".pcx" = mkDefault "01;35";
+ ".mov" = mkDefault "01;35";
+ ".mpg" = mkDefault "01;35";
+ ".mpeg" = mkDefault "01;35";
+ ".m2v" = mkDefault "01;35";
+ ".mkv" = mkDefault "01;35";
+ ".webm" = mkDefault "01;35";
+ ".ogm" = mkDefault "01;35";
+ ".mp4" = mkDefault "01;35";
+ ".m4v" = mkDefault "01;35";
+ ".mp4v" = mkDefault "01;35";
+ ".vob" = mkDefault "01;35";
+ ".qt" = mkDefault "01;35";
+ ".nuv" = mkDefault "01;35";
+ ".wmv" = mkDefault "01;35";
+ ".asf" = mkDefault "01;35";
+ ".rm" = mkDefault "01;35";
+ ".rmvb" = mkDefault "01;35";
+ ".flc" = mkDefault "01;35";
+ ".avi" = mkDefault "01;35";
+ ".fli" = mkDefault "01;35";
+ ".flv" = mkDefault "01;35";
+ ".gl" = mkDefault "01;35";
+ ".dl" = mkDefault "01;35";
+ ".xcf" = mkDefault "01;35";
+ ".xwd" = mkDefault "01;35";
+ ".yuv" = mkDefault "01;35";
+ ".cgm" = mkDefault "01;35";
+ ".emf" = mkDefault "01;35";
+ ".ogv" = mkDefault "01;35";
+ ".ogx" = mkDefault "01;35";
+ ".aac" = mkDefault "00;36";
+ ".au" = mkDefault "00;36";
+ ".flac" = mkDefault "00;36";
+ ".m4a" = mkDefault "00;36";
+ ".mid" = mkDefault "00;36";
+ ".midi" = mkDefault "00;36";
+ ".mka" = mkDefault "00;36";
+ ".mp3" = mkDefault "00;36";
+ ".mpc" = mkDefault "00;36";
+ ".ogg" = mkDefault "00;36";
+ ".ra" = mkDefault "00;36";
+ ".wav" = mkDefault "00;36";
+ ".oga" = mkDefault "00;36";
+ ".opus" = mkDefault "00;36";
+ ".spx" = mkDefault "00;36";
+ ".xspf" = mkDefault "00;36";
+ };
+
+ home.file.".dir_colors".text = concatStringsSep "\n" ([ ]
+ ++ optional (cfg.extraConfig != "") cfg.extraConfig
+ ++ mapAttrsToList formatLine cfg.settings) + "\n";
+
+ programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
+ eval $(${pkgs.coreutils}/bin/dircolors -b ~/.dir_colors)
+ '';
+
+ programs.fish.shellInit = mkIf cfg.enableFishIntegration ''
+ eval (${pkgs.coreutils}/bin/dircolors -c ~/.dir_colors)
+ '';
+
+ # Set `LS_COLORS` before Oh My Zsh and `initExtra`.
+ programs.zsh.initExtraBeforeCompInit = mkIf cfg.enableZshIntegration ''
+ eval $(${pkgs.coreutils}/bin/dircolors -b ~/.dir_colors)
+ '';
+ };
+}
diff --git a/home-manager/modules/programs/direnv.nix b/home-manager/modules/programs/direnv.nix
index beb40a96261..1d1374b8e26 100644
--- a/home-manager/modules/programs/direnv.nix
+++ b/home-manager/modules/programs/direnv.nix
@@ -70,6 +70,11 @@ in {
Whether to enable Fish integration.
'';
};
+
+ enableNixDirenvIntegration = mkEnableOption ''
+ <link
+ xlink:href="https://github.com/nix-community/nix-direnv">nix-direnv</link>,
+ a fast, persistent use_nix implementation for direnv'';
};
config = mkIf cfg.enable {
@@ -78,12 +83,15 @@ in {
xdg.configFile."direnv/config.toml" =
mkIf (cfg.config != { }) { source = configFile cfg.config; };
- xdg.configFile."direnv/direnvrc" =
- mkIf (cfg.stdlib != "") { text = cfg.stdlib; };
+ xdg.configFile."direnv/direnvrc" = let
+ text = concatStringsSep "\n" (optional (cfg.stdlib != "") cfg.stdlib
+ ++ optional cfg.enableNixDirenvIntegration
+ "source ${pkgs.nix-direnv}/share/nix-direnv/direnvrc");
+ in mkIf (text != "") { inherit text; };
programs.bash.initExtra = mkIf cfg.enableBashIntegration (
- # Using mkAfter to make it more likely to appear after other
- # manipulations of the prompt.
+ # Using mkAfter to make it more likely to appear after other
+ # manipulations of the prompt.
mkAfter ''
eval "$(${pkgs.direnv}/bin/direnv hook bash)"
'');
diff --git a/home-manager/modules/programs/eclipse.nix b/home-manager/modules/programs/eclipse.nix
index 8ce605b106a..21973ab937e 100644
--- a/home-manager/modules/programs/eclipse.nix
+++ b/home-manager/modules/programs/eclipse.nix
@@ -13,6 +13,16 @@ in {
programs.eclipse = {
enable = mkEnableOption "Eclipse";
+ package = mkOption {
+ type = types.package;
+ default = pkgs.eclipses.eclipse-platform;
+ defaultText = literalExample "pkgs.eclipses.eclipse-platform";
+ example = literalExample "pkgs.eclipses.eclipse-java";
+ description = ''
+ The Eclipse package to install.
+ '';
+ };
+
enableLombok = mkOption {
type = types.bool;
default = false;
@@ -40,7 +50,7 @@ in {
config = mkIf cfg.enable {
home.packages = [
(pkgs.eclipses.eclipseWithPlugins {
- eclipse = pkgs.eclipses.eclipse-platform;
+ eclipse = cfg.package;
jvmArgs = cfg.jvmArgs ++ optional cfg.enableLombok
"-javaagent:${pkgs.lombok}/share/java/lombok.jar";
plugins = cfg.plugins;
diff --git a/home-manager/modules/programs/emacs.nix b/home-manager/modules/programs/emacs.nix
index 987a9f2431e..b785f71358c 100644
--- a/home-manager/modules/programs/emacs.nix
+++ b/home-manager/modules/programs/emacs.nix
@@ -8,16 +8,12 @@ let
# Copied from all-packages.nix, with modifications to support
# overrides.
- emacsPackages =
- let
- epkgs = pkgs.emacsPackagesGen cfg.package;
- in
- epkgs.overrideScope' cfg.overrides;
- emacsWithPackages = emacsPackages.emacsWithPackages;
+ emacsPackages = let epkgs = pkgs.emacsPackagesFor cfg.package;
+ in epkgs.overrideScope' cfg.overrides;
-in
+ emacsWithPackages = emacsPackages.emacsWithPackages;
-{
+in {
meta.maintainers = [ maintainers.rycee ];
options = {
@@ -33,7 +29,7 @@ in
};
extraPackages = mkOption {
- default = self: [];
+ default = self: [ ];
type = hm.types.selectorFunction;
defaultText = "epkgs: []";
example = literalExample "epkgs: [ epkgs.emms epkgs.magit ]";
@@ -45,7 +41,7 @@ in
};
overrides = mkOption {
- default = self: super: {};
+ default = self: super: { };
type = hm.types.overlayFunction;
defaultText = "self: super: {}";
example = literalExample ''
diff --git a/home-manager/modules/programs/firefox.nix b/home-manager/modules/programs/firefox.nix
index 17c64752d66..d5003f59edc 100644
--- a/home-manager/modules/programs/firefox.nix
+++ b/home-manager/modules/programs/firefox.nix
@@ -23,8 +23,15 @@ let
then "${firefoxConfigPath}/Profiles"
else firefoxConfigPath;
+ # The extensions path shared by all profiles; will not be supported
+ # by future Firefox versions.
extensionPath = "extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+ extensionsEnvPkg = pkgs.buildEnv {
+ name = "hm-firefox-extensions";
+ paths = cfg.extensions;
+ };
+
profiles =
flip mapAttrs' cfg.profiles (_: profile:
nameValuePair "Profile${toString profile.id}" {
@@ -59,6 +66,13 @@ in
{
meta.maintainers = [ maintainers.rycee ];
+ imports = [
+ (mkRemovedOptionModule ["programs" "firefox" "enableGoogleTalk"]
+ "Support for this option has been removed.")
+ (mkRemovedOptionModule ["programs" "firefox" "enableIcedTea"]
+ "Support for this option has been removed.")
+ ];
+
options = {
programs.firefox = {
enable = mkEnableOption "Firefox";
@@ -87,9 +101,29 @@ in
]
'';
description = ''
- List of Firefox add-on packages to install. Note, it is
- necessary to manually enable these extensions inside Firefox
- after the first installation.
+ List of Firefox add-on packages to install. Some
+ pre-packaged add-ons are accessible from NUR,
+ <link xlink:href="https://github.com/nix-community/NUR"/>.
+ Once you have NUR installed run
+
+ <screen language="console">
+ <prompt>$</prompt> <userinput>nix-env -f '&lt;nixpkgs&gt;' -qaP -A nur.repos.rycee.firefox-addons</userinput>
+ </screen>
+
+ to list the available Firefox add-ons.
+
+ </para><para>
+
+ Note that it is necessary to manually enable these
+ extensions inside Firefox after the first installation.
+
+ </para><para>
+
+ Extensions listed here will only be available in Firefox
+ profiles managed through the
+ <link linkend="opt-programs.firefox.profiles">programs.firefox.profiles</link>
+ option. This is due to recent changes in the way Firefox
+ handles extension side-loading.
'';
};
@@ -137,7 +171,7 @@ in
userChrome = mkOption {
type = types.lines;
default = "";
- description = "Custom Firefox CSS.";
+ description = "Custom Firefox user chrome CSS.";
example = ''
/* Hide tab bar in FF Quantum */
@-moz-document url("chrome://browser/content/browser.xul") {
@@ -153,6 +187,16 @@ in
'';
};
+ userContent = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Custom Firefox user content CSS.";
+ example = ''
+ /* Hide scrollbar in FF Quantum */
+ *{scrollbar-width:none !important}
+ '';
+ };
+
path = mkOption {
type = types.str;
default = name;
@@ -176,38 +220,6 @@ in
default = false;
description = "Whether to enable the unfree Adobe Flash plugin.";
};
-
- enableGoogleTalk = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable the unfree Google Talk plugin. This option
- is <emphasis>deprecated</emphasis> and will only work if
-
- <programlisting language="nix">
- programs.firefox.package = pkgs.firefox-esr-52-unwrapped;
- </programlisting>
-
- and the <option>plugin.load_flash_only</option> Firefox
- option has been disabled.
- '';
- };
-
- enableIcedTea = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable the Java applet plugin. This option is
- <emphasis>deprecated</emphasis> and will only work if
-
- <programlisting language="nix">
- programs.firefox.package = pkgs.firefox-esr-52-unwrapped;
- </programlisting>
-
- and the <option>plugin.load_flash_only</option> Firefox
- option has been disabled.
- '';
- };
};
};
@@ -250,8 +262,6 @@ in
# The configuration expected by the Firefox wrapper.
fcfg = {
enableAdobeFlash = cfg.enableAdobeFlash;
- enableGoogleTalkPlugin = cfg.enableGoogleTalk;
- icedtea = cfg.enableIcedTea;
};
# A bit of hackery to force a config into the wrapper.
@@ -273,17 +283,10 @@ in
home.file = mkMerge (
[{
- "${mozillaConfigPath}/${extensionPath}" = mkIf (cfg.extensions != []) (
- let
- extensionsEnv = pkgs.buildEnv {
- name = "hm-firefox-extensions";
- paths = cfg.extensions;
- };
- in {
- source = "${extensionsEnv}/share/mozilla/${extensionPath}";
- recursive = true;
- }
- );
+ "${mozillaConfigPath}/${extensionPath}" = mkIf (cfg.extensions != []) {
+ source = "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
+ recursive = true;
+ };
"${firefoxConfigPath}/profiles.ini" = mkIf (cfg.profiles != {}) {
text = profilesIni;
@@ -295,10 +298,21 @@ in
text = profile.userChrome;
};
+ "${profilesPath}/${profile.path}/chrome/userContent.css" =
+ mkIf (profile.userContent != "") {
+ text = profile.userContent;
+ };
+
"${profilesPath}/${profile.path}/user.js" =
mkIf (profile.settings != {} || profile.extraConfig != "") {
text = mkUserJs profile.settings profile.extraConfig;
};
+
+ "${profilesPath}/${profile.path}/extensions" = mkIf (cfg.extensions != []) {
+ source = "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
+ recursive = true;
+ force = true;
+ };
})
);
};
diff --git a/home-manager/modules/programs/fish.nix b/home-manager/modules/programs/fish.nix
index 87a17b85507..730afa79262 100644
--- a/home-manager/modules/programs/fish.nix
+++ b/home-manager/modules/programs/fish.nix
@@ -6,119 +6,310 @@ let
cfg = config.programs.fish;
- abbrsStr = concatStringsSep "\n" (
- mapAttrsToList (k: v: "abbr --add --global ${k} '${v}'") cfg.shellAbbrs
- );
+ pluginModule = types.submodule ({ config, ... }: {
+ options = {
+ src = mkOption {
+ type = types.path;
+ description = ''
+ Path to the plugin folder.
+ </para><para>
+ Relevant pieces will be added to the fish function path and
+ the completion path. The <filename>init.fish</filename> and
+ <filename>key_binding.fish</filename> files are sourced if
+ they exist.
+ '';
+ };
- aliasesStr = concatStringsSep "\n" (
- mapAttrsToList (k: v: "alias ${k}='${v}'") cfg.shellAliases
- );
+ name = mkOption {
+ type = types.str;
+ description = ''
+ The name of the plugin.
+ '';
+ };
+ };
+ });
-in
+ functionModule = types.submodule {
+ options = {
+ body = mkOption {
+ type = types.lines;
+ description = ''
+ The function body.
+ '';
+ };
+
+ argumentNames = mkOption {
+ type = with types; nullOr (either str (listOf str));
+ default = null;
+ description = ''
+ Assigns the value of successive command line arguments to the names
+ given.
+ '';
+ };
-{
+ description = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ A description of what the function does, suitable as a completion
+ description.
+ '';
+ };
+
+ wraps = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ Causes the function to inherit completions from the given wrapped
+ command.
+ '';
+ };
+
+ onEvent = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ Tells fish to run this function when the specified named event is
+ emitted. Fish internally generates named events e.g. when showing the
+ prompt.
+ '';
+ };
+
+ onVariable = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ Tells fish to run this function when the specified variable changes
+ value.
+ '';
+ };
+
+ onJobExit = mkOption {
+ type = with types; nullOr (either str int);
+ default = null;
+ description = ''
+ Tells fish to run this function when the job with the specified group
+ ID exits. Instead of a PID, the stringer <literal>caller</literal> can
+ be specified. This is only legal when in a command substitution, and
+ will result in the handler being triggered by the exit of the job
+ which created this command substitution.
+ '';
+ };
+
+ onProcessExit = mkOption {
+ type = with types; nullOr (either str int);
+ default = null;
+ example = "$fish_pid";
+ description = ''
+ Tells fish to run this function when the fish child process with the
+ specified process ID exits. Instead of a PID, for backwards
+ compatibility, <literal>%self</literal> can be specified as an alias
+ for <literal>$fish_pid</literal>, and the function will be run when
+ the current fish instance exits.
+ '';
+ };
+
+ onSignal = mkOption {
+ type = with types; nullOr (either str int);
+ default = null;
+ example = [ "SIGHUP" "HUP" 1 ];
+ description = ''
+ Tells fish to run this function when the specified signal is
+ delievered. The signal can be a signal number or signal name.
+ '';
+ };
+
+ noScopeShadowing = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Allows the function to access the variables of calling functions.
+ '';
+ };
+
+ inheritVariable = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ Snapshots the value of the specified variable and defines a local
+ variable with that same name and value when the function is defined.
+ '';
+ };
+ };
+ };
+
+ abbrsStr = concatStringsSep "\n"
+ (mapAttrsToList (k: v: "abbr --add --global -- ${k} ${escapeShellArg v}")
+ cfg.shellAbbrs);
+
+ aliasesStr = concatStringsSep "\n"
+ (mapAttrsToList (k: v: "alias ${k} ${escapeShellArg v}") cfg.shellAliases);
+
+in {
options = {
programs.fish = {
- enable = mkEnableOption "fish friendly interactive shell";
+ enable = mkEnableOption "fish, the friendly interactive shell";
package = mkOption {
+ type = types.package;
default = pkgs.fish;
defaultText = literalExample "pkgs.fish";
description = ''
The fish package to install. May be used to change the version.
'';
- type = types.package;
};
shellAliases = mkOption {
- default = {};
+ type = with types; attrsOf str;
+ default = { };
+ example = literalExample ''
+ {
+ ll = "ls -l";
+ ".." = "cd ..";
+ }
+ '';
description = ''
- Set of aliases for fish shell. See
- <option>environment.shellAliases</option> for an option
- format description.
+ An attribute set that maps aliases (the top level attribute names
+ in this option) to command strings or directly to build outputs.
'';
- type = types.attrs;
};
shellAbbrs = mkOption {
- default = {};
+ type = with types; attrsOf str;
+ default = { };
+ example = {
+ l = "less";
+ gco = "git checkout";
+ };
description = ''
- Set of abbreviations for fish shell.
+ An attribute set that maps aliases (the top level attribute names
+ in this option) to abbreviations. Abbreviations are expanded with
+ the longer phrase after they are entered.
'';
- type = types.attrs;
};
shellInit = mkOption {
+ type = types.lines;
default = "";
description = ''
- Shell script code called during fish shell initialisation.
+ Shell script code called during fish shell
+ initialisation.
'';
- type = types.lines;
};
loginShellInit = mkOption {
+ type = types.lines;
default = "";
description = ''
- Shell script code called during fish login shell initialisation.
+ Shell script code called during fish login shell
+ initialisation.
'';
- type = types.lines;
};
interactiveShellInit = mkOption {
+ type = types.lines;
default = "";
description = ''
- Shell script code called during interactive fish shell initialisation.
+ Shell script code called during interactive fish shell
+ initialisation.
'';
- type = types.lines;
};
promptInit = mkOption {
+ type = types.lines;
default = "";
description = ''
Shell script code used to initialise fish prompt.
'';
- type = types.lines;
};
};
+
+ programs.fish.plugins = mkOption {
+ type = types.listOf pluginModule;
+ default = [ ];
+ example = literalExample ''
+ [
+ {
+ name = "z";
+ src = pkgs.fetchFromGitHub {
+ owner = "jethrokuan";
+ repo = "z";
+ rev = "ddeb28a7b6a1f0ec6dae40c636e5ca4908ad160a";
+ sha256 = "0c5i7sdrsp0q3vbziqzdyqn4fmp235ax4mn4zslrswvn8g3fvdyh";
+ };
+ }
+
+ # oh-my-fish plugins are stored in their own repositories, which
+ # makes them simple to import into home-manager.
+ {
+ name = "fasd";
+ src = pkgs.fetchFromGitHub {
+ owner = "oh-my-fish";
+ repo = "plugin-fasd";
+ rev = "38a5b6b6011106092009549e52249c6d6f501fba";
+ sha256 = "06v37hqy5yrv5a6ssd1p3cjd9y3hnp19d3ab7dag56fs1qmgyhbs";
+ };
+ }
+ ]
+ '';
+ description = ''
+ The plugins to source in
+ <filename>conf.d/99plugins.fish</filename>.
+ '';
+ };
+
+ programs.fish.functions = mkOption {
+ type = with types; attrsOf (either lines functionModule);
+ default = { };
+ example = literalExample ''
+ {
+ __fish_command_not_found_handler = {
+ body = "__fish_default_command_not_found_handler $argv[1]";
+ onEvent = "fish_command_not_found";
+ };
+
+ gitignore = "curl -sL https://www.gitignore.io/api/$argv";
+ }
+ '';
+ description = ''
+ Basic functions to add to fish. For more information see
+ <link xlink:href="https://fishshell.com/docs/current/cmds/function.html"/>.
+ '';
+ };
+
};
- config = mkIf cfg.enable {
- home.packages = [ cfg.package ];
+ config = mkIf cfg.enable (mkMerge [
+ {
+ home.packages = [ cfg.package ];
- xdg.dataFile."fish/home-manager_generated_completions".source =
- let
+ xdg.dataFile."fish/home-manager_generated_completions".source = let
# paths later in the list will overwrite those already linked
- destructiveSymlinkJoin =
- args_@{ name
- , paths
- , preferLocalBuild ? true
- , allowSubstitutes ? false
- , postBuild ? ""
- , ...
- }:
+ destructiveSymlinkJoin = args_@{ name, paths, preferLocalBuild ? true
+ , allowSubstitutes ? false, postBuild ? "", ... }:
let
- args = removeAttrs args_ [ "name" "postBuild" ]
- // { inherit preferLocalBuild allowSubstitutes; }; # pass the defaults
- in pkgs.runCommand name args
- ''
- mkdir -p $out
- for i in $paths; do
- if [ -z "$(find $i -prune -empty)" ]; then
- cp -srf $i/* $out
- fi
- done
- ${postBuild}
- '';
- generateCompletions = package: pkgs.runCommand
- "${package.name}-fish-completions"
- {
+ args = removeAttrs args_ [ "name" "postBuild" ] // {
+ # pass the defaults
+ inherit preferLocalBuild allowSubstitutes;
+ };
+ in pkgs.runCommand name args ''
+ mkdir -p $out
+ for i in $paths; do
+ if [ -z "$(find $i -prune -empty)" ]; then
+ cp -srf $i/* $out
+ fi
+ done
+ ${postBuild}
+ '';
+
+ generateCompletions = package:
+ pkgs.runCommand "${package.name}-fish-completions" {
src = package;
nativeBuildInputs = [ pkgs.python2 ];
buildInputs = [ cfg.package ];
preferLocalBuild = true;
allowSubstitutes = false;
- }
- ''
+ } ''
mkdir -p $out
if [ -d $src/share/man ]; then
find $src/share/man -type f \
@@ -126,66 +317,144 @@ in
> /dev/null
fi
'';
- in
- destructiveSymlinkJoin {
- name = "${config.home.username}-fish-completions";
- paths =
- let
- cmp = (a: b: (a.meta.priority or 0) > (b.meta.priority or 0));
- in
- map generateCompletions (sort cmp config.home.packages);
+ in destructiveSymlinkJoin {
+ name = "${config.home.username}-fish-completions";
+ paths =
+ let cmp = (a: b: (a.meta.priority or 0) > (b.meta.priority or 0));
+ in map generateCompletions (sort cmp config.home.packages);
+ };
+
+ programs.fish.interactiveShellInit = ''
+ # add completions generated by Home Manager to $fish_complete_path
+ begin
+ set -l joined (string join " " $fish_complete_path)
+ set -l prev_joined (string replace --regex "[^\s]*generated_completions.*" "" $joined)
+ set -l post_joined (string replace $prev_joined "" $joined)
+ set -l prev (string split " " (string trim $prev_joined))
+ set -l post (string split " " (string trim $post_joined))
+ set fish_complete_path $prev "${config.xdg.dataHome}/fish/home-manager_generated_completions" $post
+ end
+ '';
+
+ xdg.configFile."fish/config.fish".text = ''
+ # ~/.config/fish/config.fish: DO NOT EDIT -- this file has been generated
+ # automatically by home-manager.
+
+ # if we haven't sourced the general config, do it
+ if not set -q __fish_general_config_sourced
+
+ set -p fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions
+ fenv source ${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh > /dev/null
+ set -e fish_function_path[1]
+
+ ${cfg.shellInit}
+ # and leave a note so we don't source this config section again from
+ # this very shell (children will source the general config anew)
+ set -g __fish_general_config_sourced 1
+
+ end
+
+ # if we haven't sourced the login config, do it
+ status --is-login; and not set -q __fish_login_config_sourced
+ and begin
+
+ # Login shell initialisation
+ ${cfg.loginShellInit}
+
+ # and leave a note so we don't source this config section again from
+ # this very shell (children will source the general config anew)
+ set -g __fish_login_config_sourced 1
+
+ end
+
+ # if we haven't sourced the interactive config, do it
+ status --is-interactive; and not set -q __fish_interactive_config_sourced
+ and begin
+
+ # Abbreviations
+ ${abbrsStr}
+
+ # Aliases
+ ${aliasesStr}
+
+ # Prompt initialisation
+ ${cfg.promptInit}
+
+ # Interactive shell intialisation
+ ${cfg.interactiveShellInit}
+
+ # and leave a note so we don't source this config section again from
+ # this very shell (children will source the general config anew,
+ # allowing configuration changes in, e.g, aliases, to propagate)
+ set -g __fish_interactive_config_sourced 1
+
+ end
+ '';
+ }
+ {
+ xdg.configFile = mapAttrs' (name: def: {
+ name = "fish/functions/${name}.fish";
+ value = {
+ text = let
+ modifierStr = n: v: optional (v != null) ''--${n}="${toString v}"'';
+ modifierStrs = n: v: optional (v != null) "--${n}=${toString v}";
+ modifierBool = n: v: optional (v != null && v) "--${n}";
+
+ mods = with def;
+ modifierStr "description" description ++ modifierStr "wraps" wraps
+ ++ modifierStr "on-event" onEvent
+ ++ modifierStr "on-variable" onVariable
+ ++ modifierStr "on-job-exit" onJobExit
+ ++ modifierStr "on-process-exit" onProcessExit
+ ++ modifierStr "on-signal" onSignal
+ ++ modifierBool "no-scope-shadowing" noScopeShadowing
+ ++ modifierStr "inherit-variable" inheritVariable
+ ++ modifierStrs "argument-names" argumentNames;
+
+ modifiers = if isAttrs def then " ${toString mods}" else "";
+ body = if isAttrs def then def.body else def;
+ in ''
+ function ${name}${modifiers}
+ ${body}
+ end
+ '';
};
+ }) cfg.functions;
+ }
- programs.fish.interactiveShellInit = ''
- # add completions generated by Home Manager to $fish_complete_path
- begin
- set -l joined (string join " " $fish_complete_path)
- set -l prev_joined (string replace --regex "[^\s]*generated_completions.*" "" $joined)
- set -l post_joined (string replace $prev_joined "" $joined)
- set -l prev (string split " " (string trim $prev_joined))
- set -l post (string split " " (string trim $post_joined))
- set fish_complete_path $prev "${config.xdg.dataHome}/fish/home-manager_generated_completions" $post
- end
- '';
-
- xdg.configFile."fish/config.fish".text = ''
- # ~/.config/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
- # if we haven't sourced the general config, do it
- if not set -q __fish_general_config_sourced
- set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
- fenv source ${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh > /dev/null
- set -e fish_function_path[1]
-
- ${cfg.shellInit}
- # and leave a note so we don't source this config section again from
- # this very shell (children will source the general config anew)
- set -g __fish_general_config_sourced 1
- end
- # if we haven't sourced the login config, do it
- status --is-login; and not set -q __fish_login_config_sourced
- and begin
-
- ${cfg.loginShellInit}
- # and leave a note so we don't source this config section again from
- # this very shell (children will source the general config anew)
- set -g __fish_login_config_sourced 1
- end
- # if we haven't sourced the interactive config, do it
- status --is-interactive; and not set -q __fish_interactive_config_sourced
- and begin
- # Abbrs
- ${abbrsStr}
-
- # Aliases
- ${aliasesStr}
-
- ${cfg.promptInit}
- ${cfg.interactiveShellInit}
- # and leave a note so we don't source this config section again from
- # this very shell (children will source the general config anew,
- # allowing configuration changes in, e.g, aliases, to propagate)
- set -g __fish_interactive_config_sourced 1
- end
- '';
- };
+ # Each plugin gets a corresponding conf.d/plugin-NAME.fish file to load
+ # in the paths and any initialization scripts.
+ (mkIf (length cfg.plugins > 0) {
+ xdg.configFile = mkMerge ((map (plugin: {
+ "fish/conf.d/plugin-${plugin.name}.fish".text = ''
+ # Plugin ${plugin.name}
+ set -l plugin_dir ${plugin.src}
+
+ # Set paths to import plugin components
+ if test -d $plugin_dir/functions
+ set fish_function_path $fish_function_path[1] $plugin_dir/functions $fish_function_path[2..-1]
+ end
+
+ if test -d $plugin_dir/completions
+ set fish_complete_path $fish_complete_path[1] $plugin_dir/completions $fish_complete_path[2..-1]
+ end
+
+ # Source initialization code if it exists.
+ if test -d $plugin_dir/conf.d
+ for f in $plugin_dir/conf.d/*.fish
+ source $f
+ end
+ end
+
+ if test -f $plugin_dir/key_bindings.fish
+ source $plugin_dir/key_bindings.fish
+ end
+
+ if test -f $plugin_dir/init.fish
+ source $plugin_dir/init.fish
+ end
+ '';
+ }) cfg.plugins));
+ })
+ ]);
}
diff --git a/home-manager/modules/programs/fzf.nix b/home-manager/modules/programs/fzf.nix
index 36eb3a1cdba..3aee57768ea 100644
--- a/home-manager/modules/programs/fzf.nix
+++ b/home-manager/modules/programs/fzf.nix
@@ -100,6 +100,14 @@ in {
Whether to enable Zsh integration.
'';
};
+
+ enableFishIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Fish integration.
+ '';
+ };
};
config = mkIf cfg.enable {
@@ -130,5 +138,9 @@ in {
. ${pkgs.fzf}/share/fzf/key-bindings.zsh
fi
'';
+
+ programs.fish.shellInit = mkIf cfg.enableFishIntegration ''
+ source ${pkgs.fzf}/share/fzf/key-bindings.fish && fzf_key_bindings
+ '';
};
}
diff --git a/home-manager/modules/programs/getmail.nix b/home-manager/modules/programs/getmail.nix
index 2c3919dcf2f..f83c469ff24 100644
--- a/home-manager/modules/programs/getmail.nix
+++ b/home-manager/modules/programs/getmail.nix
@@ -49,6 +49,12 @@ let
".getmail/getmail${if a.primary then "rc" else a.name}";
in {
+ options = {
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./getmail-accounts.nix));
+ };
+ };
+
config = mkIf getmailEnabled {
home.file = foldl' (a: b: a // b) { }
(map (a: { "${renderConfigFilepath a}".text = renderAccountConfig a; })
diff --git a/home-manager/modules/programs/git.nix b/home-manager/modules/programs/git.nix
index a56aa10d50e..312269de316 100644
--- a/home-manager/modules/programs/git.nix
+++ b/home-manager/modules/programs/git.nix
@@ -19,10 +19,20 @@ let
else
''${section} "${subsection}"'';
+ mkValueString = v:
+ let
+ escapedV = ''
+ "${
+ replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
+ }"'';
+ in generators.mkValueStringDefault { } (if isString v then escapedV else v);
+
# generation for multiple ini values
mkKeyValue = k: v:
- let mkKeyValue = generators.mkKeyValueDefault { } "=" k;
- in concatStringsSep "\n" (map mkKeyValue (toList v));
+ let
+ mkKeyValue =
+ generators.mkKeyValueDefault { inherit mkValueString; } " = " k;
+ in concatStringsSep "\n" (map (kv: " " + mkKeyValue kv) (toList v));
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
gitFlattenAttrs = let
@@ -205,6 +215,36 @@ in {
'';
};
};
+
+ delta = {
+ enable = mkEnableOption "" // {
+ description = ''
+ Whether to enable the <command>delta</command> syntax highlighter.
+ See <link xlink:href="https://github.com/dandavison/delta" />.
+ '';
+ };
+
+ options = mkOption {
+ type = with types;
+ let
+ primitiveType = either str (either bool int);
+ sectionType = attrsOf primitiveType;
+ in attrsOf (either primitiveType sectionType);
+ default = { };
+ example = {
+ features = "decorations";
+ whitespace-error-style = "22 reverse";
+ decorations = {
+ commit-decoration-style = "bold yellow box ul";
+ file-style = "bold yellow ul";
+ file-decoration-style = "none";
+ };
+ };
+ description = ''
+ Options to configure delta.
+ '';
+ };
+ };
};
};
@@ -237,7 +277,14 @@ in {
genIdentity = name: account:
with account;
nameValuePair "sendemail.${name}" ({
- smtpEncryption = if smtp.tls.enable then "tls" else "";
+ smtpEncryption = if smtp.tls.enable then
+ (if smtp.tls.useStartTls
+ || versionOlder config.home.stateVersion "20.09" then
+ "tls"
+ else
+ "ssl")
+ else
+ "";
smtpServer = smtp.host;
smtpUser = userName;
from = address;
@@ -299,5 +346,15 @@ in {
([ "git-lfs" "smudge" ] ++ skipArg ++ [ "--" "%f" ]);
};
})
+
+ (mkIf cfg.delta.enable {
+ programs.git.iniContent =
+ let deltaCommand = "${pkgs.gitAndTools.delta}/bin/delta";
+ in {
+ core.pager = deltaCommand;
+ interactive.diffFilter = "${deltaCommand} --color-only";
+ delta = cfg.delta.options;
+ };
+ })
]);
}
diff --git a/home-manager/modules/programs/gnome-terminal.nix b/home-manager/modules/programs/gnome-terminal.nix
index 570a1fc7df0..f1b15862130 100644
--- a/home-manager/modules/programs/gnome-terminal.nix
+++ b/home-manager/modules/programs/gnome-terminal.nix
@@ -6,11 +6,6 @@ let
cfg = config.programs.gnome-terminal;
- vteInitStr = ''
- # gnome-terminal: Show current directory in the terminal window title.
- . ${pkgs.gnome3.vte}/etc/profile.d/vte.sh
- '';
-
backForeSubModule = types.submodule ({ ... }: {
options = {
foreground = mkOption {
@@ -81,6 +76,12 @@ let
description = "The terminal colors, null to use system default.";
};
+ cursorBlinkMode = mkOption {
+ default = "system";
+ type = types.enum [ "system" "on" "off" ];
+ description = "The cursor blink mode.";
+ };
+
cursorShape = mkOption {
default = "block";
type = types.enum [ "block" "ibeam" "underline" ];
@@ -121,6 +122,20 @@ let
The number of scrollback lines to keep, null for infinite.
'';
};
+
+ customCommand = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ The command to use to start the shell, or null for default shell.
+ '';
+ };
+
+ loginShell = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Run command as a login shell.";
+ };
};
});
@@ -130,7 +145,14 @@ let
scrollbar-policy = if pcfg.showScrollbar then "always" else "never";
scrollback-lines = pcfg.scrollbackLines;
cursor-shape = pcfg.cursorShape;
- } // (if (pcfg.font == null) then {
+ cursor-blink-mode = pcfg.cursorBlinkMode;
+ login-shell = pcfg.loginShell;
+ } // (if (pcfg.customCommand != null) then {
+ use-custom-command = true;
+ custom-command = pcfg.customCommand;
+ } else {
+ use-custom-command = false;
+ }) // (if (pcfg.font == null) then {
use-system-font = true;
} else {
use-system-font = false;
@@ -179,7 +201,7 @@ in {
themeVariant = mkOption {
default = "default";
- type = types.enum [ "default" "light" "dark" ];
+ type = types.enum [ "default" "light" "dark" "system" ];
description = "The theme variation to request";
};
@@ -192,7 +214,7 @@ in {
};
config = mkIf cfg.enable {
- home.packages = [ pkgs.gnome3.gnome_terminal ];
+ home.packages = [ pkgs.gnome3.gnome-terminal ];
dconf.settings = let dconfPath = "org/gnome/terminal/legacy";
in {
@@ -210,7 +232,7 @@ in {
(n: v: nameValuePair ("${dconfPath}/profiles:/:${n}") (buildProfileSet v))
cfg.profile;
- programs.bash.initExtra = mkBefore vteInitStr;
- programs.zsh.initExtra = vteInitStr;
+ programs.bash.enableVteIntegration = true;
+ programs.zsh.enableVteIntegration = true;
};
}
diff --git a/home-manager/modules/programs/go.nix b/home-manager/modules/programs/go.nix
index 983769d26af..4b85ec854ad 100644
--- a/home-manager/modules/programs/go.nix
+++ b/home-manager/modules/programs/go.nix
@@ -62,6 +62,18 @@ in {
example = ".local/bin.go";
description = "GOBIN relative to HOME";
};
+
+ goPrivate = mkOption {
+ type = with types; listOf str;
+ default = [ ];
+ example = [ "*.corp.example.com" "rsc.io/private" ];
+ description = ''
+ The <envar>GOPRIVATE</envar> environment variable controls
+ which modules the go command considers to be private (not
+ available publicly) and should therefore not use the proxy
+ or checksum database.
+ '';
+ };
};
};
@@ -85,5 +97,9 @@ in {
home.sessionVariables.GOBIN =
builtins.toPath "${config.home.homeDirectory}/${cfg.goBin}";
})
+
+ (mkIf (cfg.goPrivate != [ ]) {
+ home.sessionVariables.GOPRIVATE = concatStringsSep "," cfg.goPrivate;
+ })
]);
}
diff --git a/home-manager/modules/programs/htop.nix b/home-manager/modules/programs/htop.nix
index 84966040534..1fb397cdc38 100644
--- a/home-manager/modules/programs/htop.nix
+++ b/home-manager/modules/programs/htop.nix
@@ -61,6 +61,9 @@ let
CGROUP = 112;
OOM = 113;
IO_PRIORITY = 114;
+ M_PSS = 118;
+ M_SWAP = 119;
+ M_PSSWP = 120;
};
# Mapping from names to defaults
@@ -76,16 +79,32 @@ let
Hostname = 2;
AllCPUs = 1;
AllCPUs2 = 1;
+ AllCPUs4 = 1;
LeftCPUs = 1;
RightCPUs = 1;
+ Right = 1;
+ CPUs = 1;
LeftCPUs2 = 1;
RightCPUs2 = 1;
+ LeftCPUs4 = 1;
+ RightCPUs4 = 1;
Blank = 2;
+ PressureStallCPUSome = 2;
+ PressureStallIOSome = 2;
+ PressureStallIOFull = 2;
+ PressureStallMemorySome = 2;
+ PressureStallMemoryFull = 2;
+ ZFSARC = 2;
+ ZFSCARC = 2;
CPU = 1;
"CPU(1)" = 1;
"CPU(2)" = 1;
"CPU(3)" = 1;
"CPU(4)" = 1;
+ "CPU(5)" = 1;
+ "CPU(6)" = 1;
+ "CPU(7)" = 1;
+ "CPU(8)" = 1;
};
singleMeterType = let
@@ -268,6 +287,18 @@ in {
description = "Count CPUs from 0 instead of 1.";
};
+ showCpuUsage = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Show CPU usage frequency.";
+ };
+
+ showCpuFrequency = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Show CPU frequency.";
+ };
+
updateProcessNames = mkOption {
type = types.bool;
default = false;
@@ -287,6 +318,12 @@ in {
description = "Which color scheme to use.";
};
+ enableMouse = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Enable mouse support.";
+ };
+
delay = mkOption {
type = types.int;
default = 15;
@@ -328,6 +365,11 @@ in {
type = meterType;
};
+ vimMode = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Vim key bindings.";
+ };
};
config = mkIf cfg.enable {
@@ -357,14 +399,18 @@ in {
header_margin=${bool cfg.headerMargin}
detailed_cpu_time=${bool cfg.detailedCpuTime}
cpu_count_from_zero=${bool cfg.cpuCountFromZero}
+ show_cpu_usage=${bool cfg.showCpuUsage}
+ show_cpu_frequency=${bool cfg.showCpuFrequency}
update_process_names=${bool cfg.updateProcessNames}
account_guest_in_cpu_meter=${bool cfg.accountGuestInCpuMeter}
color_scheme=${toString cfg.colorScheme}
+ enable_mouse=${bool cfg.enableMouse}
delay=${toString cfg.delay}
left_meters=${list leftMeters}
left_meter_modes=${list leftModes}
right_meters=${list rightMeters}
right_meter_modes=${list rightModes}
+ vim_mode=${bool cfg.vimMode}
'';
};
}
diff --git a/home-manager/modules/programs/i3status.nix b/home-manager/modules/programs/i3status.nix
new file mode 100644
index 00000000000..c1e12fe71d7
--- /dev/null
+++ b/home-manager/modules/programs/i3status.nix
@@ -0,0 +1,208 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.i3status;
+
+ enabledModules = filterAttrs (n: v: v.enable) cfg.modules;
+
+ formatOrder = n: ''order += "${n}"'';
+
+ formatModule = n: v:
+ let
+ formatLine = n: v:
+ let
+ formatValue = v:
+ if isBool v then
+ (if v then "true" else "false")
+ else if isString v then
+ ''"${v}"''
+ else
+ toString v;
+ in "${n} = ${formatValue v}";
+ in ''
+ ${n} {
+ ${concatStringsSep "\n " (mapAttrsToList formatLine v)}
+ }
+ '';
+
+ settingsType = with types; attrsOf (oneOf [ bool int str ]);
+
+ sortAttrNamesByPosition = comparator: set:
+ let pos = n: set."${n}".position;
+ in sort (a: b: comparator (pos a) (pos b)) (attrNames set);
+in {
+ meta.maintainers = [ hm.maintainers.justinlovinger ];
+
+ options.programs.i3status = {
+ enable = mkEnableOption "i3status";
+
+ enableDefault = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether or not to enable
+ the default configuration.
+ '';
+ };
+
+ general = mkOption {
+ type = settingsType;
+ default = { };
+ description = ''
+ Configuration to add to i3status <filename>config</filename>
+ <code>general</code> section.
+ See
+ <citerefentry>
+ <refentrytitle>i3status</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ colors = true;
+ color_good = "#e0e0e0";
+ color_degraded = "#d7ae00";
+ color_bad = "#f69d6a";
+ interval = 1;
+ }
+ '';
+ };
+
+ modules = mkOption {
+ type = types.attrsOf (types.submodule {
+ options = {
+ enable = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether or not to enable this module.
+ '';
+ };
+ position = mkOption {
+ type = with types; either int float;
+ description = ''
+ Position of this module in i3status <code>order</code>.
+ '';
+ };
+ settings = mkOption {
+ type = settingsType;
+ default = { };
+ description = ''
+ Configuration to add to this i3status module.
+ See
+ <citerefentry>
+ <refentrytitle>i3status</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ format = "♪ %volume";
+ format_muted = "♪ muted (%volume)";
+ device = "pulse:1";
+ }
+ '';
+ };
+ };
+ });
+ default = { };
+ description = ''
+ Modules to add to i3status <filename>config</filename> file.
+ See
+ <citerefentry>
+ <refentrytitle>i3status</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ "volume master" = {
+ position = 1;
+ settings = {
+ format = "♪ %volume";
+ format_muted = "♪ muted (%volume)";
+ device = "pulse:1";
+ };
+ };
+ "disk /" = {
+ position = 2;
+ settings = {
+ format = "/ %avail";
+ };
+ };
+ }
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ programs.i3status = mkIf cfg.enableDefault {
+ general = {
+ colors = mkDefault true;
+ interval = mkDefault 5;
+ };
+
+ modules = {
+ ipv6 = { position = mkDefault 1; };
+
+ "wireless _first_" = {
+ position = mkDefault 2;
+ settings = {
+ format_up = mkDefault "W: (%quality at %essid) %ip";
+ format_down = mkDefault "W: down";
+ };
+ };
+
+ "ethernet _first_" = {
+ position = mkDefault 3;
+ settings = {
+ format_up = mkDefault "E: %ip (%speed)";
+ format_down = mkDefault "E: down";
+ };
+ };
+
+ "battery all" = {
+ position = mkDefault 4;
+ settings = { format = mkDefault "%status %percentage %remaining"; };
+ };
+
+ "disk /" = {
+ position = mkDefault 5;
+ settings = { format = mkDefault "%avail"; };
+ };
+
+ load = {
+ position = mkDefault 6;
+ settings = { format = mkDefault "%1min"; };
+ };
+
+ memory = {
+ position = mkDefault 7;
+ settings = {
+ format = mkDefault "%used | %available";
+ threshold_degraded = mkDefault "1G";
+ format_degraded = mkDefault "MEMORY < %available";
+ };
+ };
+
+ "tztime local" = {
+ position = mkDefault 8;
+ settings = { format = mkDefault "%Y-%m-%d %H:%M:%S"; };
+ };
+ };
+ };
+
+ home.packages = [ pkgs.i3status ];
+
+ xdg.configFile."i3status/config".text = concatStringsSep "\n" ([ ]
+ ++ optional (cfg.general != { }) (formatModule "general" cfg.general)
+ ++ map formatOrder (sortAttrNamesByPosition lessThan enabledModules)
+ ++ mapAttrsToList formatModule
+ (mapAttrs (n: v: v.settings) enabledModules));
+ };
+}
diff --git a/home-manager/modules/programs/info.nix b/home-manager/modules/programs/info.nix
index 9e4a5d4aaff..a7d2692b515 100644
--- a/home-manager/modules/programs/info.nix
+++ b/home-manager/modules/programs/info.nix
@@ -1,22 +1,21 @@
-# info.nix -- install texinfo, set INFOPATH, create `dir` file
+# info.nix -- install texinfo and create `dir` file
# This is a helper for the GNU info documentation system. By default,
# the `info` command (and the Info subsystem within Emacs) gives easy
# access to the info files stored system-wide, but not info files in
# your ~/.nix-profile.
-# We set $INFOPATH to include `/run/current-system/sw/share/info` and
-# `~/.nix-profile/share/info` but it's not enough. Although info can
-# then find files when you explicitly ask for them, it doesn't show
-# them to you in the table of contents on startup. To do that requires
-# a `dir` file. NixOS keeps the system-wide `dir` file up to date, but
-# ignores home-installed packages.
+# Specifically, although info can then find files when you explicitly
+# ask for them, it doesn't show them to you in the table of contents
+# on startup. To do that requires a `dir` file. NixOS keeps the
+# system-wide `dir` file up to date, but ignores files installed in
+# user profiles.
-# So this module contains an activation script that generates the
-# `dir` for your home profile. Then when you start info (and both
-# `dir` files are in your $INFOPATH), it will *merge* the contents of
-# the two files, showing you a unified table of contents for all
-# packages. This is really nice.
+# This module contains extra profile commands that generate the `dir`
+# for your home profile. Then when you start info (and both `dir`
+# files are in your $INFOPATH), it will *merge* the contents of the
+# two files, showing you a unified table of contents for all packages.
+# This is really nice.
{ config, lib, pkgs, ... }:
@@ -26,50 +25,39 @@ let
cfg = config.programs.info;
- # Indexes info files found in this location
- homeInfoPath = "${config.home.profileDirectory}/share/info";
-
# Installs this package -- the interactive just means that it
# includes the curses `info` program. We also use `install-info`
# from this package in the activation script.
infoPkg = pkgs.texinfoInteractive;
in {
- options = {
- programs.info = {
- enable = mkEnableOption "GNU Info";
+ imports = [
+ (mkRemovedOptionModule [ "programs" "info" "homeInfoDirLocation" ] ''
+ The `dir` file is now generated as part of the Home Manager profile and
+ will no longer be placed in your home directory.
+ '')
+ ];
- homeInfoDirLocation = mkOption {
- default = "\${XDG_CACHE_HOME:-$HOME/.cache}/info";
- description = ''
- Directory in which to store the info <filename>dir</filename>
- file within your home.
- '';
- };
- };
- };
+ options.programs.info.enable = mkEnableOption "GNU Info";
config = mkIf cfg.enable {
- home.sessionVariables.INFOPATH =
- "${cfg.homeInfoDirLocation}\${INFOPATH:+:}\${INFOPATH}";
+ home.packages = [
+ infoPkg
- home.activation.createHomeInfoDir =
- hm.dag.entryAfter [ "installPackages" ] ''
- oPATH=$PATH
- export PATH="${lib.makeBinPath [ pkgs.gzip ]}''${PATH:+:}$PATH"
- $DRY_RUN_CMD mkdir -p "${cfg.homeInfoDirLocation}"
- $DRY_RUN_CMD rm -f "${cfg.homeInfoDirLocation}/dir"
- if [[ -d "${homeInfoPath}" ]]; then
- find -L "${homeInfoPath}" \( -name '*.info' -o -name '*.info.gz' \) \
- -exec $DRY_RUN_CMD ${infoPkg}/bin/install-info '{}' \
- "${cfg.homeInfoDirLocation}/dir" \;
- fi
- export PATH="$oPATH"
- unset oPATH
- '';
-
- home.packages = [ infoPkg ];
+ # Make sure the target directory is a real directory.
+ (pkgs.runCommandLocal "dummy-info-dir1" { } "mkdir -p $out/share/info")
+ (pkgs.runCommandLocal "dummy-info-dir2" { } "mkdir -p $out/share/info")
+ ];
home.extraOutputsToInstall = [ "info" ];
+
+ home.extraProfileCommands = let infoPath = "$out/share/info";
+ in ''
+ if [[ -w "${infoPath}" && ! -e "${infoPath}/dir" ]]; then
+ PATH="${lib.makeBinPath [ pkgs.gzip infoPkg ]}''${PATH:+:}$PATH" \
+ find -L "${infoPath}" \( -name '*.info' -o -name '*.info.gz' \) \
+ -exec install-info '{}' "${infoPath}/dir" ';'
+ fi
+ '';
};
}
diff --git a/home-manager/modules/programs/kakoune.nix b/home-manager/modules/programs/kakoune.nix
index faf2542dc70..6db311a1376 100644
--- a/home-manager/modules/programs/kakoune.nix
+++ b/home-manager/modules/programs/kakoune.nix
@@ -49,6 +49,7 @@ let
"InsertCompletionShow"
"InsertCompletionHide"
"InsertCompletionSelect"
+ "ModuleLoaded"
];
example = "SetOption";
description = ''
@@ -96,16 +97,7 @@ let
keyMapping = types.submodule {
options = {
mode = mkOption {
- type = types.enum [
- "insert"
- "normal"
- "prompt"
- "menu"
- "user"
- "goto"
- "view"
- "object"
- ];
+ type = types.str;
example = "user";
description = ''
The mode in which the mapping takes effect.
@@ -497,6 +489,10 @@ let
};
};
+ kakouneWithPlugins = pkgs.wrapKakoune pkgs.kakoune-unwrapped {
+ configure = { plugins = cfg.plugins; };
+ };
+
configFile = let
wrapOptions = with cfg.config.wrapLines;
concatStrings [
@@ -513,6 +509,25 @@ let
"${optionalString (separator != null) " -separator ${separator}"}"
];
+ showWhitespaceOptions = with cfg.config.showWhitespace;
+ let
+ quoteSep = sep:
+ if sep == "'" then
+ ''"'"''
+ else if lib.strings.stringLength sep == 1 then
+ "'${sep}'"
+ else
+ sep; # backwards compat, in case sep == "' '", etc.
+
+ in concatStrings [
+ (optionalString (tab != null) " -tab ${quoteSep tab}")
+ (optionalString (tabStop != null) " -tabpad ${quoteSep tabStop}")
+ (optionalString (space != null) " -spc ${quoteSep space}")
+ (optionalString (nonBreakingSpace != null)
+ " -nbsp ${quoteSep nonBreakingSpace}")
+ (optionalString (lineFeed != null) " -lf ${quoteSep lineFeed}")
+ ];
+
uiOptions = with cfg.config.ui;
concatStringsSep " " [
"ncurses_set_title=${if setTitle then "true" else "false"}"
@@ -533,6 +548,21 @@ let
}"
];
+ userModeString = mode:
+ optionalString (!builtins.elem mode [
+ "insert"
+ "normal"
+ "prompt"
+ "menu"
+ "user"
+ "goto"
+ "view"
+ "object"
+ ]) "try %{declare-user-mode ${mode}}";
+
+ userModeStrings = map userModeString
+ (lists.unique (map (km: km.mode) cfg.config.keyMappings));
+
keyMappingString = km:
concatStringsSep " " [
"map global"
@@ -566,12 +596,14 @@ let
++ optional (autoComplete != null)
"set-option global autocomplete ${concatStringsSep "|" autoComplete}"
++ optional (autoReload != null)
- "set-option global/ autoreload ${autoReload}"
+ "set-option global autoreload ${autoReload}"
++ optional (wrapLines != null && wrapLines.enable)
"add-highlighter global/ wrap${wrapOptions}"
++ optional (numberLines != null && numberLines.enable)
"add-highlighter global/ number-lines${numberLinesOptions}"
++ optional showMatching "add-highlighter global/ show-matching"
+ ++ optional (showWhitespace != null && showWhitespace.enable)
+ "add-highlighter global/ show-whitespaces${showWhitespaceOptions}"
++ optional (scrollOff != null)
"set-option global scrolloff ${toString scrollOff.lines},${
toString scrollOff.columns
@@ -580,7 +612,8 @@ let
++ [ "# UI options" ]
++ optional (ui != null) "set-option global ui_options ${uiOptions}"
- ++ [ "# Key mappings" ] ++ map keyMappingString keyMappings
+ ++ [ "# User modes" ] ++ userModeStrings ++ [ "# Key mappings" ]
+ ++ map keyMappingString keyMappings
++ [ "# Hooks" ] ++ map hookString hooks);
in pkgs.writeText "kakrc"
@@ -605,11 +638,22 @@ in {
<filename>~/.config/kak/kakrc</filename>.
'';
};
+
+ plugins = mkOption {
+ type = with types; listOf package;
+ default = [ ];
+ example = literalExample "[ pkgs.kakounePlugins.kak-fzf ]";
+ description = ''
+ List of kakoune plugins to install. To get a list of
+ supported plugins run:
+ <command>nix-env -f '&lt;nixpkgs&gt;' -qaP -A kakounePlugins</command>.
+ '';
+ };
};
};
config = mkIf cfg.enable {
- home.packages = [ pkgs.kakoune ];
+ home.packages = [ kakouneWithPlugins ];
xdg.configFile."kak/kakrc".source = configFile;
};
}
diff --git a/home-manager/modules/programs/kitty.nix b/home-manager/modules/programs/kitty.nix
new file mode 100644
index 00000000000..313a0bfadd7
--- /dev/null
+++ b/home-manager/modules/programs/kitty.nix
@@ -0,0 +1,91 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.kitty;
+
+ eitherStrBoolInt = with types; either str (either bool int);
+
+ optionalPackage = opt:
+ optional (opt != null && opt.package != null) opt.package;
+
+ toKittyConfig = generators.toKeyValue {
+ mkKeyValue = key: value:
+ let
+ value' = if isBool value then
+ (if value then "yes" else "no")
+ else
+ toString value;
+ in "${key} ${value'}";
+ };
+
+ toKittyKeybindings = generators.toKeyValue {
+ mkKeyValue = key: command: "map ${key} ${command}";
+ };
+
+in {
+ options.programs.kitty = {
+ enable = mkEnableOption "Kitty terminal emulator";
+
+ settings = mkOption {
+ type = types.attrsOf eitherStrBoolInt;
+ default = { };
+ example = literalExample ''
+ {
+ scrollback_lines = 10000;
+ enable_audio_bell = false;
+ update_check_interval = 0;
+ }
+ '';
+ description = ''
+ Configuration written to
+ <filename>~/.config/kitty/kitty.conf</filename>. See
+ <link xlink:href="https://sw.kovidgoyal.net/kitty/conf.html" />
+ for the documentation.
+ '';
+ };
+
+ font = mkOption {
+ type = types.nullOr hm.types.fontType;
+ default = null;
+ description = "The font to use.";
+ };
+
+ keybindings = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Mapping of keybindings to actions.";
+ example = literalExample ''
+ {
+ "ctrl+c" = "copy_or_interrupt";
+ "ctrl+f>2" = "set_font_size 20";
+ }
+ '';
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = "Additional configuration to add.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.kitty ] ++ optionalPackage cfg.font;
+
+ xdg.configFile."kitty/kitty.conf".text = ''
+ # Generated by Home Manager.
+ # See https://sw.kovidgoyal.net/kitty/conf.html
+
+ ${optionalString (cfg.font != null) "font_family ${cfg.font.name}"}
+
+ ${toKittyConfig cfg.settings}
+
+ ${toKittyKeybindings cfg.keybindings}
+
+ ${cfg.extraConfig}
+ '';
+ };
+}
diff --git a/home-manager/modules/programs/lf.nix b/home-manager/modules/programs/lf.nix
new file mode 100644
index 00000000000..ee4e9b5bfce
--- /dev/null
+++ b/home-manager/modules/programs/lf.nix
@@ -0,0 +1,219 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.lf;
+
+ knownSettings = {
+ anchorfind = types.bool;
+ color256 = types.bool;
+ dircounts = types.bool;
+ dirfirst = types.bool;
+ drawbox = types.bool;
+ globsearch = types.bool;
+ icons = types.bool;
+ hidden = types.bool;
+ ignorecase = types.bool;
+ ignoredia = types.bool;
+ incsearch = types.bool;
+ preview = types.bool;
+ reverse = types.bool;
+ smartcase = types.bool;
+ smartdia = types.bool;
+ wrapscan = types.bool;
+ wrapscroll = types.bool;
+ number = types.bool;
+ relativenumber = types.bool;
+ findlen = types.int;
+ period = types.int;
+ scrolloff = types.int;
+ tabstop = types.int;
+ errorfmt = types.str;
+ filesep = types.str;
+ ifs = types.str;
+ promptfmt = types.str;
+ shell = types.str;
+ sortby = types.str;
+ timefmt = types.str;
+ ratios = types.str;
+ info = types.str;
+ shellopts = types.str;
+ };
+
+ lfSettingsType = types.submodule {
+ options = let
+ opt = name: type:
+ mkOption {
+ type = types.nullOr type;
+ default = null;
+ visible = false;
+ };
+ in mapAttrs opt knownSettings;
+ };
+in {
+ meta.maintainers = [ hm.maintainers.owm111 ];
+
+ options = {
+ programs.lf = {
+ enable = mkEnableOption "lf";
+
+ settings = mkOption {
+ type = lfSettingsType;
+ default = { };
+ example = {
+ tabstop = 4;
+ number = true;
+ ratios = "1:1:2";
+ };
+ description = ''
+ An attribute set of lf settings. The attribute names and corresponding
+ values must be among the following supported options.
+
+ <informaltable frame="none"><tgroup cols="1"><tbody>
+ ${concatStringsSep "\n" (mapAttrsToList (n: v: ''
+ <row>
+ <entry><varname>${n}</varname></entry>
+ <entry>${v.description}</entry>
+ </row>
+ '') knownSettings)}
+ </tbody></tgroup></informaltable>
+
+ See the lf documentation for detailed descriptions of these options.
+ Note, use <varname>previewer</varname> to set lf's
+ <varname>previewer</varname> option, and
+ <varname>extraConfig</varname> for any other option not listed above.
+ All string options are quoted with double quotes.
+ '';
+ };
+
+ commands = mkOption {
+ type = with types; attrsOf (nullOr str);
+ default = { };
+ example = {
+ get-mime-type = ''%xdg-mime query filetype "$f"'';
+ open = "$$OPENER $f";
+ };
+ description = ''
+ Commands to declare. Commands set to null or an empty string are
+ deleted.
+ '';
+ };
+
+ keybindings = mkOption {
+ type = with types; attrsOf (nullOr str);
+ default = { };
+ example = {
+ gh = "cd ~";
+ D = "trash";
+ i = "$less $f";
+ U = "!du -sh";
+ gg = null;
+ };
+ description =
+ "Keys to bind. Keys set to null or an empty string are deleted.";
+ };
+
+ cmdKeybindings = mkOption {
+ type = with types; attrsOf (nullOr str);
+ default = { };
+ example = literalExample ''{ "<c-g>" = "cmd-escape"; }'';
+ description = ''
+ Keys to bind to command line commands which can only be one of the
+ builtin commands. Keys set to null or an empty string are deleted.
+ '';
+ };
+
+ previewer.source = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ example = literalExample ''
+ pkgs.writeShellScript "pv.sh" '''
+ #!/bin/sh
+
+ case "$1" in
+ *.tar*) tar tf "$1";;
+ *.zip) unzip -l "$1";;
+ *.rar) unrar l "$1";;
+ *.7z) 7z l "$1";;
+ *.pdf) pdftotext "$1" -;;
+ *) highlight -O ansi "$1" || cat "$1";;
+ esac
+ '''
+ '';
+ description = ''
+ Script or executable to use to preview files. Sets lf's
+ <varname>previewer</varname> option.
+ '';
+ };
+
+ previewer.keybinding = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "i";
+ description = ''
+ Key to bind to the script at <varname>previewer.source</varname> and
+ pipe through less. Setting to null will not bind any key.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ $mkdir -p ~/.trash
+ '';
+ description = "Custom lfrc lines.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.lf ];
+
+ xdg.configFile."lf/lfrc".text = let
+ fmtSetting = k: v:
+ optionalString (v != null) "set ${
+ if isBool v then
+ "${optionalString (!v) "no"}${k}"
+ else
+ "${k} ${if isInt v then toString v else ''"${v}"''}"
+ }";
+
+ settingsStr = concatStringsSep "\n" (filter (x: x != "")
+ (mapAttrsToList fmtSetting
+ (builtins.intersectAttrs knownSettings cfg.settings)));
+
+ fmtCmdMap = before: k: v:
+ "${before} ${k}${optionalString (v != null && v != "") " ${v}"}";
+ fmtCmd = fmtCmdMap "cmd";
+ fmtMap = fmtCmdMap "map";
+ fmtCmap = fmtCmdMap "cmap";
+
+ commandsStr = concatStringsSep "\n" (mapAttrsToList fmtCmd cfg.commands);
+ keybindingsStr =
+ concatStringsSep "\n" (mapAttrsToList fmtMap cfg.keybindings);
+ cmdKeybindingsStr =
+ concatStringsSep "\n" (mapAttrsToList fmtCmap cfg.cmdKeybindings);
+
+ previewerStr = optionalString (cfg.previewer.source != null) ''
+ set previewer ${cfg.previewer.source}
+ ${optionalString (cfg.previewer.keybinding != null) ''
+ map ${cfg.previewer.keybinding} ''$${cfg.previewer.source} "$f" | less -R
+ ''}
+ '';
+ in ''
+ ${settingsStr}
+
+ ${commandsStr}
+
+ ${keybindingsStr}
+
+ ${cmdKeybindingsStr}
+
+ ${previewerStr}
+
+ ${cfg.extraConfig}
+ '';
+ };
+}
diff --git a/home-manager/modules/programs/lieer-accounts.nix b/home-manager/modules/programs/lieer-accounts.nix
new file mode 100644
index 00000000000..238049065b3
--- /dev/null
+++ b/home-manager/modules/programs/lieer-accounts.nix
@@ -0,0 +1,69 @@
+{ lib, ... }:
+
+with lib;
+
+{
+ options.lieer = {
+ enable = mkEnableOption "lieer Gmail synchronization for notmuch";
+
+ timeout = mkOption {
+ type = types.ints.unsigned;
+ default = 0;
+ description = ''
+ HTTP timeout in seconds. 0 means forever or system timeout.
+ '';
+ };
+
+ replaceSlashWithDot = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Replace '/' with '.' in Gmail labels.
+ '';
+ };
+
+ dropNonExistingLabels = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Allow missing labels on the Gmail side to be dropped.
+ '';
+ };
+
+ ignoreTagsLocal = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ description = ''
+ Set custom tags to ignore when syncing from local to
+ remote (after translations).
+ '';
+ };
+
+ ignoreTagsRemote = mkOption {
+ type = types.listOf types.str;
+ default = [
+ "CATEGORY_FORUMS"
+ "CATEGORY_PROMOTIONS"
+ "CATEGORY_UPDATES"
+ "CATEGORY_SOCIAL"
+ "CATEGORY_PERSONAL"
+ ];
+ description = ''
+ Set custom tags to ignore when syncing from remote to
+ local (before translations).
+ '';
+ };
+
+ notmuchSetupWarning = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Warn if Notmuch is not also enabled for this account.
+ </para><para>
+ This can safely be disabled if <command>notmuch init</command>
+ has been used to configure this account outside of Home
+ Manager.
+ '';
+ };
+ };
+}
diff --git a/home-manager/modules/programs/lieer.nix b/home-manager/modules/programs/lieer.nix
new file mode 100644
index 00000000000..e34a247af46
--- /dev/null
+++ b/home-manager/modules/programs/lieer.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.programs.lieer;
+
+ lieerAccounts =
+ filter (a: a.lieer.enable) (attrValues config.accounts.email.accounts);
+
+ nonGmailAccounts =
+ map (a: a.name) (filter (a: a.flavor != "gmail.com") lieerAccounts);
+
+ nonGmailConfigHelp =
+ map (name: ''accounts.email.accounts.${name}.flavor = "gmail.com";'')
+ nonGmailAccounts;
+
+ missingNotmuchAccounts = map (a: a.name)
+ (filter (a: !a.notmuch.enable && a.lieer.notmuchSetupWarning)
+ lieerAccounts);
+
+ notmuchConfigHelp =
+ map (name: "accounts.email.accounts.${name}.notmuch.enable = true;")
+ missingNotmuchAccounts;
+
+ configFile = account: {
+ name = "${account.maildir.absPath}/.gmailieer.json";
+ value = {
+ text = builtins.toJSON {
+ inherit (account.lieer) timeout;
+ account = account.address;
+ replace_slash_with_dot = account.lieer.replaceSlashWithDot;
+ drop_non_existing_label = account.lieer.dropNonExistingLabels;
+ ignore_tags = account.lieer.ignoreTagsLocal;
+ ignore_remote_labels = account.lieer.ignoreTagsRemote;
+ } + "\n";
+ };
+ };
+
+in {
+ meta.maintainers = [ maintainers.tadfisher ];
+
+ options = {
+ programs.lieer.enable =
+ mkEnableOption "lieer Gmail synchronization for notmuch";
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./lieer-accounts.nix));
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ (mkIf (missingNotmuchAccounts != [ ]) {
+ warnings = [''
+ lieer is enabled for the following email accounts, but notmuch is not:
+
+ ${concatStringsSep "\n " missingNotmuchAccounts}
+
+ Notmuch can be enabled with:
+
+ ${concatStringsSep "\n " notmuchConfigHelp}
+
+ If you have configured notmuch outside of Home Manager, you can suppress this
+ warning with:
+
+ programs.lieer.notmuchSetupWarning = false;
+ ''];
+ })
+
+ {
+ assertions = [{
+ assertion = nonGmailAccounts == [ ];
+ message = ''
+ lieer is enabled for non-Gmail accounts:
+
+ ${concatStringsSep "\n " nonGmailAccounts}
+
+ If these accounts are actually Gmail accounts, you can
+ fix this error with:
+
+ ${concatStringsSep "\n " nonGmailConfigHelp}
+ '';
+ }];
+
+ home.packages = [ pkgs.gmailieer ];
+
+ # Notmuch should ignore non-mail files created by lieer.
+ programs.notmuch.new.ignore = [ "/.*[.](json|lock|bak)$/" ];
+
+ home.file = listToAttrs (map configFile lieerAccounts);
+ }
+ ]);
+}
diff --git a/home-manager/modules/programs/man.nix b/home-manager/modules/programs/man.nix
index 0ed376780d4..b235b02fe2d 100644
--- a/home-manager/modules/programs/man.nix
+++ b/home-manager/modules/programs/man.nix
@@ -4,19 +4,69 @@ with lib;
{
options = {
- programs.man.enable = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Whether to enable manual pages and the <command>man</command>
- command. This also includes "man" outputs of all
- <literal>home.packages</literal>.
- '';
+ programs.man = {
+ enable = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable manual pages and the <command>man</command>
+ command. This also includes "man" outputs of all
+ <literal>home.packages</literal>.
+ '';
+ };
+
+ generateCaches = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to generate the manual page index caches using
+ <citerefentry>
+ <refentrytitle>mandb</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>. This allows searching for a page or
+ keyword using utilities like <citerefentry>
+ <refentrytitle>apropos</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>.
+ </para><para>
+ This feature is disabled by default because it slows down
+ building. If you don't mind waiting a few more seconds when
+ Home Manager builds a new generation, you may safely enable
+ this option.
+ '';
+ };
};
};
config = mkIf config.programs.man.enable {
home.packages = [ pkgs.man ];
home.extraOutputsToInstall = [ "man" ];
+
+ # This is mostly copy/pasted/adapted from NixOS' documentation.nix.
+ home.file = mkIf config.programs.man.generateCaches {
+ ".manpath".text = let
+ # Generate a directory containing installed packages' manpages.
+ manualPages = pkgs.buildEnv {
+ name = "man-paths";
+ paths = config.home.packages;
+ pathsToLink = [ "/share/man" ];
+ extraOutputsToInstall = [ "man" ];
+ ignoreCollisions = true;
+ };
+
+ # Generate a database of all manpages in ${manualPages}.
+ manualCache = pkgs.runCommandLocal "man-cache" { } ''
+ # Generate a temporary man.conf so mandb knows where to
+ # write cache files.
+ echo "MANDB_MAP ${manualPages}/share/man $out" > man.conf
+
+ # Run mandb to generate cache files:
+ ${pkgs.man-db}/bin/mandb -C man.conf --no-straycats --create \
+ ${manualPages}/share/man
+ '';
+ in ''
+ MANDB_MAP ${config.home.profileDirectory}/share/man ${manualCache}
+ '';
+ };
};
}
diff --git a/home-manager/modules/programs/mbsync.nix b/home-manager/modules/programs/mbsync.nix
index 6ade10986fe..f2814b393d0 100644
--- a/home-manager/modules/programs/mbsync.nix
+++ b/home-manager/modules/programs/mbsync.nix
@@ -122,6 +122,10 @@ in {
'';
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./mbsync-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/mcfly.nix b/home-manager/modules/programs/mcfly.nix
new file mode 100644
index 00000000000..1206f9da566
--- /dev/null
+++ b/home-manager/modules/programs/mcfly.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+
+ cfg = config.programs.mcfly;
+
+in {
+ meta.maintainers = [ maintainers.marsam ];
+
+ options.programs.mcfly = {
+ enable = mkEnableOption "mcfly";
+
+ keyScheme = mkOption {
+ type = types.enum [ "emacs" "vim" ];
+ default = "emacs";
+ description = ''
+ Key scheme to use.
+ '';
+ };
+
+ enableLightTheme = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable light mode theme.
+ '';
+ };
+
+ enableBashIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Bash integration.
+ '';
+ };
+
+ enableZshIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Zsh integration.
+ '';
+ };
+
+ enableFishIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Fish integration.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ home.packages = [ pkgs.mcfly ];
+
+ programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
+ source "${pkgs.mcfly}/share/mcfly/mcfly.bash"
+ '';
+
+ programs.zsh.initExtra = mkIf cfg.enableZshIntegration ''
+ source "${pkgs.mcfly}/share/mcfly/mcfly.zsh"
+ '';
+
+ programs.fish.shellInit = mkIf cfg.enableFishIntegration ''
+ source "${pkgs.mcfly}/share/mcfly/mcfly.fish"
+ if status is-interactive
+ mcfly_key_bindings
+ end
+ '';
+
+ home.sessionVariables.MCFLY_KEY_SCHEME = cfg.keyScheme;
+ }
+
+ (mkIf cfg.enableLightTheme { home.sessionVariables.MCFLY_LIGHT = "TRUE"; })
+ ]);
+}
diff --git a/home-manager/modules/programs/mpv.nix b/home-manager/modules/programs/mpv.nix
index 3a4e5092f9a..a5b0517fe0a 100644
--- a/home-manager/modules/programs/mpv.nix
+++ b/home-manager/modules/programs/mpv.nix
@@ -125,7 +125,7 @@ in {
(if cfg.scripts == [ ] then
pkgs.mpv
else
- pkgs.mpv-with-scripts.override { scripts = cfg.scripts; })
+ pkgs.wrapMpv pkgs.mpv-unwrapped { scripts = cfg.scripts; })
];
}
(mkIf (cfg.config != { } || cfg.profiles != { }) {
diff --git a/home-manager/modules/programs/msmtp.nix b/home-manager/modules/programs/msmtp.nix
index f34fd72f8b1..7b6704860e0 100644
--- a/home-manager/modules/programs/msmtp.nix
+++ b/home-manager/modules/programs/msmtp.nix
@@ -56,6 +56,10 @@ in {
'';
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./msmtp-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/ncmpcpp.nix b/home-manager/modules/programs/ncmpcpp.nix
new file mode 100644
index 00000000000..a39baab6ca5
--- /dev/null
+++ b/home-manager/modules/programs/ncmpcpp.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.ncmpcpp;
+
+ renderSettings = settings:
+ concatStringsSep "\n" (mapAttrsToList renderSetting settings);
+
+ renderSetting = name: value: "${name}=${renderValue value}";
+
+ renderValue = option:
+ {
+ int = toString option;
+ bool = if option then "yes" else "no";
+ string = option;
+ }.${builtins.typeOf option};
+
+ renderBindings = bindings: concatStringsSep "\n" (map renderBinding bindings);
+
+ renderBinding = { key, command }:
+ concatStringsSep "\n " ([ ''def_key "${key}"'' ] ++ maybeWrapList command);
+
+ maybeWrapList = xs: if isList xs then xs else [ xs ];
+
+ valueType = with types; oneOf [ bool int str ];
+
+ bindingType = types.submodule ({ name, config, ... }: {
+ options = {
+ key = mkOption {
+ type = types.str;
+ description = "Key to bind.";
+ example = "j";
+ };
+
+ command = mkOption {
+ type = with types; either str (listOf str);
+ description = "Command or sequence of commands to be executed.";
+ example = "scroll_down";
+ };
+ };
+ });
+
+in {
+ meta.maintainers = with maintainers; [ olmokramer ];
+
+ options.programs.ncmpcpp = {
+ enable =
+ mkEnableOption "ncmpcpp - an ncurses Music Player Daemon (MPD) client";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.ncmpcpp;
+ defaultText = literalExample "pkgs.ncmpcpp";
+ description = ''
+ Package providing the <code>ncmpcpp</code> command.
+ '';
+ example =
+ literalExample "pkgs.ncmpcpp.override { visualizerSupport = true; }";
+ };
+
+ mpdMusicDir = mkOption {
+ type = types.nullOr types.path;
+ default = let mpdCfg = config.services.mpd;
+ in if pkgs.stdenv.hostPlatform.isLinux && mpdCfg.enable then
+ mpdCfg.musicDirectory
+ else
+ null;
+ defaultText = literalExample ''
+ if pkgs.stdenv.hostPlatform.isLinux && config.services.mpd.enable then
+ config.services.mpd.musicDirectory
+ else
+ null
+ '';
+ description = ''
+ Value of the <code>mpd_music_dir</code> setting. On Linux platforms the
+ value of <varname>services.mpd.musicDirectory</varname> is used as the
+ default if <varname>services.mpd.enable</varname> is
+ <literal>true</literal>.
+ '';
+ example = "~/music";
+ };
+
+ settings = mkOption {
+ type = types.attrsOf valueType;
+ default = { };
+ description = ''
+ Attribute set from name of a setting to its value. For available options
+ see
+ <citerefentry>
+ <refentrytitle>ncmpcpp</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>.
+ '';
+ example = { ncmpcpp_directory = "~/.local/share/ncmpcpp"; };
+ };
+
+ bindings = mkOption {
+ type = types.listOf bindingType;
+ default = [ ];
+ description = "List of keybindings.";
+ example = literalExample ''
+ [
+ { key = "j"; command = "scroll_down"; }
+ { key = "k"; command = "scroll_up"; }
+ { key = "J"; command = [ "select_item" "scroll_down" ]; }
+ { key = "K"; command = [ "select_item" "scroll_up" ]; }
+ ]
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ warnings = mkIf (cfg.settings ? mpd_music_dir && cfg.mpdMusicDir != null) [
+ ("programs.ncmpcpp.settings.mpd_music_dir will be overridden by"
+ + " programs.ncmpcpp.mpdMusicDir.")
+ ];
+
+ home.packages = [ cfg.package ];
+
+ xdg.configFile = {
+ "ncmpcpp/config" = let
+ settings = cfg.settings // optionalAttrs (cfg.mpdMusicDir != null) {
+ mpd_music_dir = toString cfg.mpdMusicDir;
+ };
+ in mkIf (settings != { }) { text = renderSettings settings + "\n"; };
+
+ "ncmpcpp/bindings" = mkIf (cfg.bindings != [ ]) {
+ text = renderBindings cfg.bindings + "\n";
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/programs/ne.nix b/home-manager/modules/programs/ne.nix
new file mode 100644
index 00000000000..a88d23d9133
--- /dev/null
+++ b/home-manager/modules/programs/ne.nix
@@ -0,0 +1,95 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.ne;
+
+ autoPrefFiles = let
+ autoprefs = cfg.automaticPreferences
+ // optionalAttrs (cfg.defaultPreferences != "") {
+ ".default" = cfg.defaultPreferences;
+ };
+
+ gen = fileExtension: configText:
+ nameValuePair ".ne/${fileExtension}#ap" {
+ text = configText;
+ }; # Generates [path].text format expected by home.file.
+ in mapAttrs' gen autoprefs;
+
+in {
+ meta.maintainers = [ hm.maintainers.cwyc ];
+
+ options.programs.ne = {
+ enable = mkEnableOption "ne";
+
+ keybindings = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ KEY 7f BS
+ SEQ "\x1b[1;5D" 7f
+ '';
+ description = ''
+ Keybinding file for ne.
+ '';
+ };
+
+ defaultPreferences = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Default preferences for ne.
+ </para><para>
+ Equivalent to <literal>programs.ne.automaticPreferences.".default"</literal>.
+ '';
+ };
+
+ automaticPreferences = mkOption {
+ type = types.attrsOf types.lines;
+ default = { };
+ example = literalExample ''
+ {
+ nix = '''
+ TAB 0
+ TS 2
+ ''';
+ js = '''
+ TS 4
+ ''';
+ }
+ '';
+ description = ''
+ Automatic preferences files for ne.
+ '';
+ };
+
+ menus = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Menu configuration file for ne.";
+ };
+
+ virtualExtensions = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ sh 1 ^#!\s*/.*\b(bash|sh|ksh|zsh)\s*
+ csh 1 ^#!\s*/.*\b(csh|tcsh)\s*
+ '';
+ description = "Virtual extensions configuration file for ne.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.ne ];
+
+ home.file = {
+ ".ne/.keys" = mkIf (cfg.keybindings != "") { text = cfg.keybindings; };
+ ".ne/.extensions" =
+ mkIf (cfg.virtualExtensions != "") { text = cfg.virtualExtensions; };
+ ".ne/.menus" = mkIf (cfg.menus != "") { text = cfg.menus; };
+ } // autoPrefFiles;
+ };
+}
diff --git a/home-manager/modules/programs/neomutt-accounts.nix b/home-manager/modules/programs/neomutt-accounts.nix
index 033db38eb0a..009cf1fa7e8 100644
--- a/home-manager/modules/programs/neomutt-accounts.nix
+++ b/home-manager/modules/programs/neomutt-accounts.nix
@@ -8,7 +8,16 @@ with lib;
sendMailCommand = mkOption {
type = types.nullOr types.str;
- default = null;
+ default = if config.msmtp.enable then
+ "msmtpq --read-envelope-from --read-recipients"
+ else
+ null;
+ defaultText = literalExample ''
+ if config.msmtp.enable then
+ "msmtpq --read-envelope-from --read-recipients"
+ else
+ null
+ '';
example = "msmtpq --read-envelope-from --read-recipients";
description = ''
Command to send a mail. If not set, neomutt will be in charge of sending mails.
@@ -24,11 +33,4 @@ with lib;
'';
};
};
-
- config = mkIf config.neomutt.enable {
- neomutt.sendMailCommand = mkOptionDefault (if config.msmtp.enable then
- "msmtpq --read-envelope-from --read-recipients"
- else
- null);
- };
}
diff --git a/home-manager/modules/programs/neomutt.nix b/home-manager/modules/programs/neomutt.nix
index 85af0353b6c..f2a6bbfff08 100644
--- a/home-manager/modules/programs/neomutt.nix
+++ b/home-manager/modules/programs/neomutt.nix
@@ -38,6 +38,19 @@ let
};
};
+ sortOptions = [
+ "date"
+ "date-received"
+ "from"
+ "mailbox-order"
+ "score"
+ "size"
+ "spam"
+ "subject"
+ "threads"
+ "to"
+ ];
+
bindModule = types.submodule {
options = {
map = mkOption {
@@ -98,7 +111,9 @@ let
} else
let
smtpProto = if smtp.tls.enable then "smtps" else "smtp";
- smtpBaseUrl = "${smtpProto}://${escape userName}@${smtp.host}";
+ smtpPort = if smtp.port != null then ":${toString smtp.port}" else "";
+ smtpBaseUrl =
+ "${smtpProto}://${escape userName}@${smtp.host}${smtpPort}";
in {
smtp_url = "'${smtpBaseUrl}'";
smtp_pass = "'`${passCmd}`'";
@@ -211,18 +226,9 @@ in {
};
sort = mkOption {
- type = types.enum [
- "date"
- "date-received"
- "from"
- "mailbox-order"
- "score"
- "size"
- "spam"
- "subject"
- "threads"
- "to"
- ];
+ # allow users to choose any option from sortOptions, or any option prefixed with "reverse-"
+ type = types.enum
+ (sortOptions ++ (map (option: "reverse-" + option) sortOptions));
default = "threads";
description = "Sorting method on messages.";
};
@@ -258,6 +264,10 @@ in {
description = "Extra configuration appended to the end.";
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./neomutt-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/neovim.nix b/home-manager/modules/programs/neovim.nix
index 4101dc0f4e7..858f5576ad1 100644
--- a/home-manager/modules/programs/neovim.nix
+++ b/home-manager/modules/programs/neovim.nix
@@ -43,7 +43,7 @@ in
type = types.bool;
default = false;
description = ''
- Symlink `vi` to `nvim` binary.
+ Symlink <command>vi</command> to <command>nvim</command> binary.
'';
};
@@ -51,7 +51,15 @@ in
type = types.bool;
default = false;
description = ''
- Symlink `vim` to `nvim` binary.
+ Symlink <command>vim</command> to <command>nvim</command> binary.
+ '';
+ };
+
+ vimdiffAlias = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Alias <command>vimdiff</command> to <command>nvim -d</command>.
'';
};
@@ -203,5 +211,9 @@ in
configure = cfg.configure // moduleConfigure;
};
+
+ programs.bash.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; };
+ programs.fish.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; };
+ programs.zsh.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; };
};
}
diff --git a/home-manager/modules/programs/newsboat.nix b/home-manager/modules/programs/newsboat.nix
index 6b59ed713d8..793b30680bf 100644
--- a/home-manager/modules/programs/newsboat.nix
+++ b/home-manager/modules/programs/newsboat.nix
@@ -102,7 +102,11 @@ in {
mkQueryEntry = n: v: ''"query:${n}:${escape [ ''"'' ] v}"'';
queries = mapAttrsToList mkQueryEntry cfg.queries;
- in concatStringsSep "\n" (urls ++ queries) + "\n";
+ in concatStringsSep "\n"
+ (if versionAtLeast config.home.stateVersion "20.03" then
+ queries ++ urls
+ else
+ urls ++ queries) + "\n";
home.file.".newsboat/config".text = ''
max-items ${toString cfg.maxItems}
diff --git a/home-manager/modules/programs/notmuch.nix b/home-manager/modules/programs/notmuch.nix
index 7e7a140b20c..9070d755671 100644
--- a/home-manager/modules/programs/notmuch.nix
+++ b/home-manager/modules/programs/notmuch.nix
@@ -143,6 +143,10 @@ in {
};
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./notmuch-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/nushell.nix b/home-manager/modules/programs/nushell.nix
new file mode 100644
index 00000000000..1eb42f9515c
--- /dev/null
+++ b/home-manager/modules/programs/nushell.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.nushell;
+
+ configFile = config:
+ pkgs.runCommand "config.toml" {
+ buildInputs = [ pkgs.remarshal ];
+ preferLocalBuild = true;
+ allowSubstitutes = false;
+ } ''
+ remarshal -if json -of toml \
+ < ${pkgs.writeText "config.json" (builtins.toJSON config)} \
+ > $out
+ '';
+
+in {
+ meta.maintainers = [ maintainers.Philipp-M ];
+
+ options.programs.nushell = {
+ enable = mkEnableOption "nushell";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.nushell;
+ defaultText = literalExample "pkgs.nushell";
+ description = "The package to use for nushell.";
+ };
+
+ settings = mkOption {
+ type = with types;
+ let
+ prim = oneOf [ bool int str ];
+ primOrPrimAttrs = either prim (attrsOf prim);
+ entry = either prim (listOf primOrPrimAttrs);
+ entryOrAttrsOf = t: either entry (attrsOf t);
+ entries = entryOrAttrsOf (entryOrAttrsOf entry);
+ in attrsOf entries // { description = "Nushell configuration"; };
+ default = { };
+ example = literalExample ''
+ {
+ edit_mode = "vi";
+ startup = [ "alias la [] { ls -a }" "alias e [msg] { echo $msg }" ];
+ key_timeout = 10;
+ completion_mode = "circular";
+ no_auto_pivot = true;
+ }
+ '';
+ description = ''
+ Configuration written to
+ <filename>~/.config/nushell/config.toml</filename>.
+ </para><para>
+ See <link xlink:href="https://www.nushell.sh/book/en/configuration.html" /> for the full list
+ of options.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+
+ xdg.configFile."nu/config.toml" =
+ mkIf (cfg.settings != { }) { source = configFile cfg.settings; };
+ };
+}
diff --git a/home-manager/modules/programs/offlineimap.nix b/home-manager/modules/programs/offlineimap.nix
index 4ce12ec0a61..b6ba847e9b7 100644
--- a/home-manager/modules/programs/offlineimap.nix
+++ b/home-manager/modules/programs/offlineimap.nix
@@ -147,6 +147,11 @@ in {
'';
};
};
+
+ accounts.email.accounts = mkOption {
+ type = with types;
+ attrsOf (submodule (import ./offlineimap-accounts.nix));
+ };
};
config = mkIf cfg.enable {
diff --git a/home-manager/modules/programs/powerline-go.nix b/home-manager/modules/programs/powerline-go.nix
new file mode 100644
index 00000000000..a4cd233cf70
--- /dev/null
+++ b/home-manager/modules/programs/powerline-go.nix
@@ -0,0 +1,123 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.powerline-go;
+
+ # Convert an option value to a string to be passed as argument to
+ # powerline-go:
+ valueToString = value:
+ if builtins.isList value then
+ builtins.concatStringsSep "," (builtins.map valueToString value)
+ else if builtins.isAttrs value then
+ valueToString
+ (mapAttrsToList (key: val: "${valueToString key}=${valueToString val}")
+ value)
+ else
+ builtins.toString value;
+
+ modulesArgument = optionalString (cfg.modules != null)
+ "-modules ${valueToString cfg.modules}";
+
+ newlineArgument = optionalString cfg.newline "-newline";
+
+ pathAliasesArgument = optionalString (cfg.pathAliases != null)
+ "-path-aliases ${valueToString cfg.pathAliases}";
+
+ otherSettingPairArgument = name: value:
+ if value == true then "-${name}" else "-${name} ${valueToString value}";
+
+ otherSettingsArgument = optionalString (cfg.settings != { })
+ (concatStringsSep " "
+ (mapAttrsToList otherSettingPairArgument cfg.settings));
+
+ commandLineArguments = ''
+ ${modulesArgument} ${newlineArgument} ${pathAliasesArgument} ${otherSettingsArgument}
+ '';
+
+in {
+ meta.maintainers = [ maintainers.DamienCassou ];
+
+ options = {
+ programs.powerline-go = {
+ enable = mkEnableOption
+ "Powerline-go, a beautiful and useful low-latency prompt for your shell";
+
+ modules = mkOption {
+ default = null;
+ type = types.nullOr (types.listOf types.str);
+ description = ''
+ List of module names to load. The list of all available
+ modules as well as the choice of default ones are at
+ <link xlink:href="https://github.com/justjanne/powerline-go"/>.
+ '';
+ example = [ "host" "ssh" "cwd" "gitlite" "jobs" "exit" ];
+ };
+
+ newline = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Set to true if the prompt should be on a line of its own.
+ '';
+ example = true;
+ };
+
+ pathAliases = mkOption {
+ default = null;
+ type = types.nullOr (types.attrsOf types.str);
+ description = ''
+ Pairs of full-path and corresponding desired short name. You
+ may use '~' to represent your home directory but you should
+ protect it to avoid shell substitution.
+ '';
+ example = literalExample ''
+ { "\\~/projects/home-manager" = "prj:home-manager"; }
+ '';
+ };
+
+ settings = mkOption {
+ default = { };
+ type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
+ description = ''
+ This can be any key/value pair as described in
+ <link xlink:href="https://github.com/justjanne/powerline-go"/>.
+ '';
+ example = literalExample ''
+ {
+ hostname-only-if-ssh = true;
+ numeric-exit-codes = true;
+ cwd-max-depth = 7;
+ ignore-repos = [ "/home/me/big-project" "/home/me/huge-project" ];
+ }
+ '';
+ };
+
+ extraUpdatePS1 = mkOption {
+ default = "";
+ description = "Shell code to execute after the prompt is set.";
+ example = ''
+ PS1=$PS1"NixOS> ";
+ '';
+ type = types.str;
+ };
+ };
+ };
+
+ config = mkIf (cfg.enable && config.programs.bash.enable) {
+ programs.bash.initExtra = ''
+ function _update_ps1() {
+ local old_exit_status=$?
+ PS1="$(${pkgs.powerline-go}/bin/powerline-go -error $old_exit_status ${commandLineArguments})"
+ ${cfg.extraUpdatePS1}
+ return $old_exit_status
+ }
+
+ if [ "$TERM" != "linux" ]; then
+ PROMPT_COMMAND="_update_ps1;$PROMPT_COMMAND"
+ fi
+ '';
+ };
+}
diff --git a/home-manager/modules/programs/qutebrowser.nix b/home-manager/modules/programs/qutebrowser.nix
new file mode 100644
index 00000000000..798363fb187
--- /dev/null
+++ b/home-manager/modules/programs/qutebrowser.nix
@@ -0,0 +1,268 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.qutebrowser;
+
+ formatLine = o: n: v:
+ let
+ formatValue = v:
+ if builtins.isNull v then
+ "None"
+ else if builtins.isBool v then
+ (if v then "True" else "False")
+ else if builtins.isString v then
+ ''"${v}"''
+ else if builtins.isList v then
+ "[${concatStringsSep ", " (map formatValue v)}]"
+ else
+ builtins.toString v;
+ in if builtins.isAttrs v then
+ concatStringsSep "\n" (mapAttrsToList (formatLine "${o}${n}.") v)
+ else
+ "${o}${n} = ${formatValue v}";
+
+ formatDictLine = o: n: v: ''${o}['${n}'] = "${v}"'';
+
+ formatKeyBindings = m: b:
+ let
+ formatKeyBinding = m: k: c:
+ ''config.bind("${k}", "${escape [ ''"'' ] c}", mode="${m}")'';
+ in concatStringsSep "\n" (mapAttrsToList (formatKeyBinding m) b);
+
+in {
+ options.programs.qutebrowser = {
+ enable = mkEnableOption "qutebrowser";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.qutebrowser;
+ defaultText = literalExample "pkgs.qutebrowser";
+ description = "Qutebrowser package to install.";
+ };
+
+ aliases = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = ''
+ Aliases for commands.
+ '';
+ };
+
+ searchEngines = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = ''
+ Search engines that can be used via the address bar. Maps a search
+ engine name (such as <literal>DEFAULT</literal>, or
+ <literal>ddg</literal>) to a URL with a <literal>{}</literal>
+ placeholder. The placeholder will be replaced by the search term, use
+ <literal>{{</literal> and <literal>}}</literal> for literal
+ <literal>{/}</literal> signs. The search engine named
+ <literal>DEFAULT</literal> is used when
+ <literal>url.auto_search</literal> is turned on and something else than
+ a URL was entered to be opened. Other search engines can be used by
+ prepending the search engine name to the search term, for example
+ <literal>:open google qutebrowser</literal>.
+ '';
+ example = literalExample ''
+ {
+ w = "https://en.wikipedia.org/wiki/Special:Search?search={}&go=Go&ns0=1";
+ aw = "https://wiki.archlinux.org/?search={}";
+ nw = "https://nixos.wiki/index.php?search={}";
+ g = "https://www.google.com/search?hl=en&q={}";
+ }
+ '';
+ };
+
+ settings = mkOption {
+ type = types.attrs;
+ default = { };
+ description = ''
+ Options to add to qutebrowser <filename>config.py</filename> file.
+ See <link xlink:href="https://qutebrowser.org/doc/help/settings.html"/>
+ for options.
+ '';
+ example = literalExample ''
+ {
+ colors = {
+ hints = {
+ bg = "#000000";
+ fg = "#ffffff";
+ };
+ tabs.bar.bg = "#000000";
+ };
+ tabs.tabs_are_windows = true;
+ }
+ '';
+ };
+
+ keyMappings = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = ''
+ This setting can be used to map keys to other keys. When the key used
+ as dictionary-key is pressed, the binding for the key used as
+ dictionary-value is invoked instead. This is useful for global
+ remappings of keys, for example to map Ctrl-[ to Escape. Note that when
+ a key is bound (via <literal>bindings.default</literal> or
+ <literal>bindings.commands</literal>), the mapping is ignored.
+ '';
+ };
+
+ enableDefaultBindings = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Disable to prevent loading default key bindings.
+ '';
+ };
+
+ keyBindings = mkOption {
+ type = types.attrsOf (types.attrsOf types.str);
+ default = { };
+ description = ''
+ Key bindings mapping keys to commands in different modes. This setting
+ is a dictionary containing mode names and dictionaries mapping keys to
+ commands: <literal>{mode: {key: command}}</literal> If you want to map
+ a key to another key, check the <literal>keyMappings</literal> setting
+ instead. For modifiers, you can use either <literal>-</literal> or
+ <literal>+</literal> as delimiters, and these names:
+
+ <itemizedlist>
+ <listitem><para>
+ Control: <literal>Control</literal>, <literal>Ctrl</literal>
+ </para></listitem>
+ <listitem><para>
+ Meta: <literal>Meta</literal>, <literal>Windows</literal>,
+ <literal>Mod4</literal>
+ </para></listitem>
+ <listitem><para>
+ Alt: <literal>Alt</literal>, <literal>Mod1</literal>
+ </para></listitem>
+ <listitem><para>
+ Shift: <literal>Shift</literal>
+ </para></listitem>
+ </itemizedlist>
+
+ For simple keys (no <literal>&lt;&gt;</literal>-signs), a capital
+ letter means the key is pressed with Shift. For special keys (with
+ <literal>&lt;&gt;</literal>-signs), you need to explicitly add
+ <literal>Shift-</literal> to match a key pressed with shift. If you
+ want a binding to do nothing, bind it to the <literal>nop</literal>
+ command. If you want a default binding to be passed through to the
+ website, bind it to null. Note that some commands which are only useful
+ for bindings (but not used interactively) are hidden from the command
+ completion. See <literal>:</literal>help for a full list of available
+ commands. The following modes are available:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>normal</literal></term>
+ <listitem><para>
+ Default mode, where most commands are invoked.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>insert</literal></term>
+ <listitem><para>
+ Entered when an input field is focused on a website, or by
+ pressing i in normal mode. Passes through almost all keypresses
+ to the website, but has some bindings like
+ <literal>&lt;Ctrl-e&gt;</literal> to open an external editor.
+ Note that single keys can’t be bound in this mode.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hint</literal></term>
+ <listitem><para>
+ Entered when f is pressed to select links with the keyboard. Note
+ that single keys can’t be bound in this mode.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>passthrough</literal></term>
+ <listitem><para>
+ Similar to insert mode, but passes through all keypresses except
+ <literal>&lt;Escape&gt;</literal> to leave the mode. It might be
+ useful to bind <literal>&lt;Escape&gt;</literal> to some other
+ key in this mode if you want to be able to send an Escape key to
+ the website as well. Note that single keys can’t be bound in this
+ mode.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>command</literal></term>
+ <listitem><para>
+ Entered when pressing the : key in order to enter a command. Note
+ that single keys can’t be bound in this mode.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>prompt</literal></term>
+ <listitem><para>
+ Entered when there’s a prompt to display, like for download
+ locations or when invoked from JavaScript.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>yesno</literal></term>
+ <listitem><para>
+ Entered when there’s a yes/no prompt displayed.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>caret</literal></term>
+ <listitem><para>
+ Entered when pressing the v mode, used to select text using the
+ keyboard.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>register</literal></term>
+ <listitem><para>
+ Entered when qutebrowser is waiting for a register name/key for
+ commands like <literal>:set-mark</literal>.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ '';
+ example = literalExample ''
+ {
+ normal = {
+ "<Ctrl-v>" = "spawn mpv {url}";
+ ",p" = "spawn --userscript qute-pass";
+ ",l" = '''config-cycle spellcheck.languages ["en-GB"] ["en-US"]''';
+ };
+ prompt = {
+ "<Ctrl-y>" = "prompt-yes";
+ };
+ }
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Extra lines added to qutebrowser <filename>config.py</filename> file.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+
+ xdg.configFile."qutebrowser/config.py".text = concatStringsSep "\n" ([ ]
+ ++ mapAttrsToList (formatLine "c.") cfg.settings
+ ++ mapAttrsToList (formatDictLine "c.aliases") cfg.aliases
+ ++ mapAttrsToList (formatDictLine "c.url.searchengines") cfg.searchEngines
+ ++ mapAttrsToList (formatDictLine "c.bindings.key_mappings")
+ cfg.keyMappings
+ ++ optional (!cfg.enableDefaultBindings) "c.bindings.default = {}"
+ ++ mapAttrsToList formatKeyBindings cfg.keyBindings
+ ++ optional (cfg.extraConfig != "") cfg.extraConfig);
+ };
+}
diff --git a/home-manager/modules/programs/rofi.nix b/home-manager/modules/programs/rofi.nix
index f344e88e2ff..734bcc423e6 100644
--- a/home-manager/modules/programs/rofi.nix
+++ b/home-manager/modules/programs/rofi.nix
@@ -131,6 +131,17 @@ in {
enable = mkEnableOption
"Rofi: A window switcher, application launcher and dmenu replacement";
+ package = mkOption {
+ default = pkgs.rofi;
+ type = types.package;
+ description = ''
+ Package providing the <command>rofi</command> binary.
+ '';
+ example = literalExample ''
+ pkgs.rofi.override { plugins = [ pkgs.rofi-emoji ]; };
+ '';
+ };
+
width = mkOption {
default = null;
type = types.nullOr types.int;
@@ -295,7 +306,7 @@ in {
'';
}];
- home.packages = [ pkgs.rofi ];
+ home.packages = [ cfg.package ];
home.file."${cfg.configPath}".text = ''
${setOption "width" cfg.width}
diff --git a/home-manager/modules/programs/ssh.nix b/home-manager/modules/programs/ssh.nix
index 6b0747dd9b1..ae1f221803c 100644
--- a/home-manager/modules/programs/ssh.nix
+++ b/home-manager/modules/programs/ssh.nix
@@ -56,7 +56,7 @@ let
};
};
- matchBlockModule = types.submodule ({ name, ... }: {
+ matchBlockModule = types.submodule ({ dagName, ... }: {
options = {
host = mkOption {
type = types.str;
@@ -143,6 +143,15 @@ let
"Set timeout in seconds after which response will be requested.";
};
+ serverAliveCountMax = mkOption {
+ type = types.ints.positive;
+ default = 3;
+ description = ''
+ Sets the number of server alive messages which may be sent
+ without SSH receiving any messages back from the server.
+ '';
+ };
+
sendEnv = mkOption {
type = types.listOf types.str;
default = [];
@@ -266,7 +275,7 @@ let
};
};
- config.host = mkDefault name;
+ config.host = mkDefault dagName;
});
matchBlockStr = cf: concatStringsSep "\n" (
@@ -281,7 +290,9 @@ let
++ optional (cf.addressFamily != null) " AddressFamily ${cf.addressFamily}"
++ optional (cf.sendEnv != []) " SendEnv ${unwords cf.sendEnv}"
++ optional (cf.serverAliveInterval != 0)
- " ServerAliveInterval ${toString cf.serverAliveInterval}"
+ " ServerAliveInterval ${toString cf.serverAliveInterval}"
+ ++ optional (cf.serverAliveCountMax != 3)
+ " ServerAliveCountMax ${toString cf.serverAliveCountMax}"
++ optional (cf.compression != null) " Compression ${yn cf.compression}"
++ optional (!cf.checkHostIP) " CheckHostIP no"
++ optional (cf.proxyCommand != null) " ProxyCommand ${cf.proxyCommand}"
@@ -325,6 +336,15 @@ in
'';
};
+ serverAliveCountMax = mkOption {
+ type = types.ints.positive;
+ default = 3;
+ description = ''
+ Sets the default number of server alive messages which may be
+ sent without SSH receiving any messages back from the server.
+ '';
+ };
+
hashKnownHosts = mkOption {
default = false;
type = types.bool;
@@ -392,7 +412,7 @@ in
};
matchBlocks = mkOption {
- type = types.loaOf matchBlockModule;
+ type = hm.types.listOrDagOf matchBlockModule;
default = {};
example = literalExample ''
{
@@ -400,7 +420,7 @@ in
hostname = "example.com";
user = "john";
};
- foo = {
+ foo = lib.hm.dag.entryBefore ["john.example.com"] {
hostname = "example.com";
identityFile = "/home/john/.ssh/foo_rsa";
};
@@ -408,11 +428,15 @@ in
'';
description = ''
Specify per-host settings. Note, if the order of rules matter
- then this must be a list. See
+ then use the DAG functions to express the dependencies as
+ shown in the example.
+ </para><para>
+ See
<citerefentry>
<refentrytitle>ssh_config</refentrytitle>
<manvolnum>5</manvolnum>
- </citerefentry>.
+ </citerefentry>
+ for more information.
'';
};
};
@@ -432,23 +456,30 @@ in
checkLocal = block: any' checkBindAndHost block.localForwards;
checkRemote = block: any' checkBindAndHost block.remoteForwards;
checkMatchBlock = block: all (fn: fn block) [ checkLocal checkRemote checkDynamic ];
- in any' checkMatchBlock (builtins.attrValues cfg.matchBlocks);
+ in any' checkMatchBlock (map (block: block.data) (builtins.attrValues cfg.matchBlocks));
message = "Forwarded paths cannot have ports.";
}
];
- home.file.".ssh/config".text = ''
+ home.file.".ssh/config".text =
+ let
+ sortedMatchBlocks = hm.dag.topoSort cfg.matchBlocks;
+ sortedMatchBlocksStr = builtins.toJSON sortedMatchBlocks;
+ matchBlocks =
+ if sortedMatchBlocks ? result
+ then sortedMatchBlocks.result
+ else abort "Dependency cycle in SSH match blocks: ${sortedMatchBlocksStr}";
+ in ''
${concatStringsSep "\n" (
mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides)}
- ${concatStringsSep "\n\n" (
- map matchBlockStr (
- builtins.attrValues cfg.matchBlocks))}
+ ${concatStringsSep "\n\n" (map (block: matchBlockStr block.data) matchBlocks)}
Host *
ForwardAgent ${yn cfg.forwardAgent}
Compression ${yn cfg.compression}
ServerAliveInterval ${toString cfg.serverAliveInterval}
+ ServerAliveCountMax ${toString cfg.serverAliveCountMax}
HashKnownHosts ${yn cfg.hashKnownHosts}
UserKnownHostsFile ${cfg.userKnownHostsFile}
ControlMaster ${cfg.controlMaster}
diff --git a/home-manager/modules/programs/starship.nix b/home-manager/modules/programs/starship.nix
index 7c7819865f7..8462d331501 100644
--- a/home-manager/modules/programs/starship.nix
+++ b/home-manager/modules/programs/starship.nix
@@ -31,8 +31,23 @@ in {
};
settings = mkOption {
- type = types.attrs;
+ type = with types;
+ let
+ prim = either bool (either int str);
+ primOrPrimAttrs = either prim (attrsOf prim);
+ entry = either prim (listOf primOrPrimAttrs);
+ entryOrAttrsOf = t: either entry (attrsOf t);
+ entries = entryOrAttrsOf (entryOrAttrsOf entry);
+ in attrsOf entries // { description = "Starship configuration"; };
default = { };
+ example = literalExample ''
+ {
+ add_newline = false;
+ prompt_order = [ "line_break" "package" "line_break" "character" ];
+ scan_timeout = 10;
+ character.symbol = "➜";
+ }
+ '';
description = ''
Configuration written to
<filename>~/.config/starship.toml</filename>.
@@ -74,7 +89,7 @@ in {
mkIf (cfg.settings != { }) { source = configFile cfg.settings; };
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
- if [[ -z $INSIDE_EMACS ]]; then
+ if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
eval "$(${cfg.package}/bin/starship init bash)"
fi
'';
@@ -85,8 +100,8 @@ in {
fi
'';
- programs.fish.shellInit = mkIf cfg.enableFishIntegration ''
- if test -z "$INSIDE_EMACS"
+ programs.fish.promptInit = mkIf cfg.enableFishIntegration ''
+ if test "$TERM" != "dumb" -a \( -z "$INSIDE_EMACS" -o "$INSIDE_EMACS" = "vterm" \)
eval (${cfg.package}/bin/starship init fish)
end
'';
diff --git a/home-manager/modules/programs/termite.nix b/home-manager/modules/programs/termite.nix
index 8a05db03558..e3d704424e8 100644
--- a/home-manager/modules/programs/termite.nix
+++ b/home-manager/modules/programs/termite.nix
@@ -362,7 +362,7 @@ in {
${optionalString "cursor" cfg.cursorColor}
${optionalString "cursor_foreground" cfg.cursorForegroundColor}
${optionalString "foreground" cfg.foregroundColor}
- ${optionalString "foregroundBold" cfg.foregroundBoldColor}
+ ${optionalString "foreground_bold" cfg.foregroundBoldColor}
${optionalString "highlight" cfg.highlightColor}
${cfg.colorsExtra}
diff --git a/home-manager/modules/programs/tmux.nix b/home-manager/modules/programs/tmux.nix
index 766bc6238ba..a71c302ac6f 100644
--- a/home-manager/modules/programs/tmux.nix
+++ b/home-manager/modules/programs/tmux.nix
@@ -6,7 +6,7 @@ let
cfg = config.programs.tmux;
- pluginName = p: if types.package.check p then p.name else p.plugin.name;
+ pluginName = p: if types.package.check p then p.pname else p.plugin.pname;
pluginModule = types.submodule {
options = {
@@ -31,6 +31,13 @@ let
boolToStr = value: if value then "on" else "off";
tmuxConf = ''
+ ${optionalString cfg.sensibleOnTop ''
+ # ============================================= #
+ # Start with defaults from the Sensible plugin #
+ # --------------------------------------------- #
+ run-shell ${pkgs.tmuxPlugins.sensible.rtp}
+ # ============================================= #
+ ''}
set -g default-terminal "${cfg.terminal}"
set -g base-index ${toString cfg.baseIndex}
setw -g pane-base-index ${toString cfg.baseIndex}
@@ -74,10 +81,40 @@ let
setw -g clock-mode-style ${if cfg.clock24 then "24" else "12"}
set -s escape-time ${toString cfg.escapeTime}
set -g history-limit ${toString cfg.historyLimit}
-
- ${cfg.extraConfig}
'';
+ configPlugins = {
+ assertions = [(
+ let
+ hasBadPluginName = p: !(hasPrefix "tmuxplugin" (pluginName p));
+ badPlugins = filter hasBadPluginName cfg.plugins;
+ in
+ {
+ assertion = badPlugins == [];
+ message =
+ "Invalid tmux plugin (not prefixed with \"tmuxplugins\"): "
+ + concatMapStringsSep ", " pluginName badPlugins;
+ }
+ )];
+
+ home.file.".tmux.conf".text = ''
+ # ============================================= #
+ # Load plugins with Home Manager #
+ # --------------------------------------------- #
+
+ ${(concatMapStringsSep "\n\n" (p: ''
+ # ${pluginName p}
+ # ---------------------
+ ${p.extraConfig or ""}
+ run-shell ${
+ if types.package.check p
+ then p.rtp
+ else p.plugin.rtp
+ }
+ '') cfg.plugins)}
+ # ============================================= #
+ '';
+ };
in
{
@@ -214,7 +251,7 @@ in
};
secureSocket = mkOption {
- default = true;
+ default = pkgs.stdenv.isLinux;
type = types.bool;
description = ''
Store tmux socket under <filename>/run</filename>, which is more
@@ -258,63 +295,22 @@ in
};
config = mkIf cfg.enable (
- mkMerge [
+ mkMerge ([
{
home.packages = [ cfg.package ]
++ optional cfg.tmuxinator.enable pkgs.tmuxinator
++ optional cfg.tmuxp.enable pkgs.tmuxp;
-
- home.file.".tmux.conf".text = tmuxConf;
}
-
- (mkIf cfg.sensibleOnTop {
- home.file.".tmux.conf".text = mkBefore ''
- # ============================================= #
- # Start with defaults from the Sensible plugin #
- # --------------------------------------------- #
- run-shell ${pkgs.tmuxPlugins.sensible.rtp}
- # ============================================= #
- '';
- })
-
(mkIf cfg.secureSocket {
home.sessionVariables = {
TMUX_TMPDIR = ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
};
})
- (mkIf (cfg.plugins != []) {
- assertions = [(
- let
- hasBadPluginName = p: !(hasPrefix "tmuxplugin" (pluginName p));
- badPlugins = filter hasBadPluginName cfg.plugins;
- in
- {
- assertion = badPlugins == [];
- message =
- "Invalid tmux plugin (not prefixed with \"tmuxplugins\"): "
- + concatMapStringsSep ", " pluginName badPlugins;
- }
- )];
-
- home.file.".tmux.conf".text = mkAfter ''
- # ============================================= #
- # Load plugins with Home Manager #
- # --------------------------------------------- #
-
- ${(concatMapStringsSep "\n\n" (p: ''
- # ${pluginName p}
- # ---------------------
- ${p.extraConfig or ""}
- run-shell ${
- if types.package.check p
- then p.rtp
- else p.plugin.rtp
- }
- '') cfg.plugins)}
- # ============================================= #
- '';
- })
- ]
+ # config file ~/.tmux.conf
+ { home.file.".tmux.conf".text = mkBefore tmuxConf; }
+ (mkIf (cfg.plugins != []) configPlugins)
+ { home.file.".tmux.conf".text = mkAfter cfg.extraConfig; }
+ ])
);
}
diff --git a/home-manager/modules/programs/vim.nix b/home-manager/modules/programs/vim.nix
index 39826a9a5d6..3325bf22516 100644
--- a/home-manager/modules/programs/vim.nix
+++ b/home-manager/modules/programs/vim.nix
@@ -5,7 +5,7 @@ with lib;
let
cfg = config.programs.vim;
- defaultPlugins = [ pkgs.vimPlugins.sensible ];
+ defaultPlugins = [ pkgs.vimPlugins.vim-sensible ];
knownSettings = {
background = types.enum [ "dark" "light" ];
diff --git a/home-manager/modules/programs/vscode.nix b/home-manager/modules/programs/vscode.nix
index cf7ac722210..099760c834a 100644
--- a/home-manager/modules/programs/vscode.nix
+++ b/home-manager/modules/programs/vscode.nix
@@ -20,11 +20,14 @@ let
"vscodium" = "vscode-oss";
}.${vscodePname};
- configFilePath =
+ userDir =
if pkgs.stdenv.hostPlatform.isDarwin then
- "Library/Application Support/${configDir}/User/settings.json"
+ "Library/Application Support/${configDir}/User"
else
- "${config.xdg.configHome}/${configDir}/User/settings.json";
+ "${config.xdg.configHome}/${configDir}/User";
+
+ configFilePath = "${userDir}/settings.json";
+ keybindingsFilePath = "${userDir}/keybindings.json";
# TODO: On Darwin where are the extensions?
extensionPath = ".${extensionDir}/extensions";
@@ -59,6 +62,45 @@ in
'';
};
+ keybindings = mkOption {
+ type = types.listOf (types.submodule {
+ options = {
+ key = mkOption {
+ type = types.str;
+ example = "ctrl+c";
+ description = "The key or key-combination to bind.";
+ };
+
+ command = mkOption {
+ type = types.str;
+ example = "editor.action.clipboardCopyAction";
+ description = "The VS Code command to execute.";
+ };
+
+ when = mkOption {
+ type = types.str;
+ default = "";
+ example = "textInputFocus";
+ description = "Optional context filter.";
+ };
+ };
+ });
+ default = [];
+ example = literalExample ''
+ [
+ {
+ key = "ctrl+c";
+ command = "editor.action.clipboardCopyAction";
+ when = "textInputFocus";
+ }
+ ]
+ '';
+ description = ''
+ Keybindings written to Visual Studio Code's
+ <filename>keybindings.json</filename>.
+ '';
+ };
+
extensions = mkOption {
type = types.listOf types.package;
default = [];
@@ -77,15 +119,13 @@ in
# Adapted from https://discourse.nixos.org/t/vscode-extensions-setup/1801/2
home.file =
let
+ subDir = "share/vscode/extensions";
toPaths = path:
- let
- p = "${path}/share/vscode/extensions";
- in
- # Links every dir in p to the extension path.
- mapAttrsToList (k: v:
- {
- "${extensionPath}/${k}".source = "${p}/${k}";
- }) (builtins.readDir p);
+ # Links every dir in path to the extension path.
+ mapAttrsToList (k: _:
+ {
+ "${extensionPath}/${k}".source = "${path}/${subDir}/${k}";
+ }) (builtins.readDir (path + "/${subDir}"));
toSymlink = concatMap toPaths cfg.extensions;
in
foldr
@@ -95,6 +135,10 @@ in
mkIf (cfg.userSettings != {}) {
text = builtins.toJSON cfg.userSettings;
};
+ "${keybindingsFilePath}" =
+ mkIf (cfg.keybindings != []) {
+ text = builtins.toJSON cfg.keybindings;
+ };
}
toSymlink;
};
diff --git a/home-manager/modules/programs/waybar.nix b/home-manager/modules/programs/waybar.nix
new file mode 100644
index 00000000000..369f6e32aba
--- /dev/null
+++ b/home-manager/modules/programs/waybar.nix
@@ -0,0 +1,363 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+ cfg = config.programs.waybar;
+
+ # Used when generating warnings
+ modulesPath = "programs.waybar.settings.[].modules";
+
+ # Taken from <https://github.com/Alexays/Waybar/blob/adaf84304865e143e4e83984aaea6f6a7c9d4d96/src/factory.cpp>
+ defaultModuleNames = [
+ "sway/mode"
+ "sway/workspaces"
+ "sway/window"
+ "wlr/taskbar"
+ "idle_inhibitor"
+ "memory"
+ "cpu"
+ "clock"
+ "disk"
+ "tray"
+ "network"
+ "backlight"
+ "pulseaudio"
+ "mpd"
+ "temperature"
+ "bluetooth"
+ "battery"
+ ];
+
+ isValidCustomModuleName = x:
+ elem x defaultModuleNames || (hasPrefix "custom/" x && stringLength x > 7);
+
+ margins = let
+ mkMargin = name: {
+ "margin-${name}" = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ example = 10;
+ description = "Margins value without unit.";
+ };
+ };
+ margins = map mkMargin [ "top" "left" "bottom" "right" ];
+ in foldl' mergeAttrs { } margins;
+
+ waybarBarConfig = with lib.types;
+ submodule {
+ options = {
+ layer = mkOption {
+ type = nullOr (enum [ "top" "bottom" ]);
+ default = null;
+ description = ''
+ Decide if the bar is displayed in front (<code>"top"</code>)
+ of the windows or behind (<code>"bottom"</code>).
+ '';
+ example = "top";
+ };
+
+ output = mkOption {
+ type = nullOr (either str (listOf str));
+ default = null;
+ example = literalExample ''
+ [ "DP-1" "!DP-2" "!DP-3" ]
+ '';
+ description = ''
+ Specifies on which screen this bar will be displayed.
+ Exclamation mark(!) can be used to exclude specific output.
+ '';
+ };
+
+ position = mkOption {
+ type = nullOr (enum [ "top" "bottom" "left" "right" ]);
+ default = null;
+ example = "right";
+ description = "Bar position relative to the output.";
+ };
+
+ height = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Height to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ width = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Width to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ modules-left = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the left.";
+ example = literalExample ''
+ [ "sway/workspaces" "sway/mode" "wlr/taskbar" ]
+ '';
+ };
+
+ modules-center = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed in the center.";
+ example = literalExample ''
+ [ "sway/window" ]
+ '';
+ };
+
+ modules-right = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the right.";
+ example = literalExample ''
+ [ "mpd" "custom/mymodule#with-css-id" "temperature" ]
+ '';
+ };
+
+ modules = mkOption {
+ type = attrsOf unspecified;
+ default = { };
+ description = "Modules configuration.";
+ example = literalExample ''
+ {
+ "sway/window" = {
+ max-length = 50;
+ };
+ "clock" = {
+ format-alt = "{:%a, %d. %b %H:%M}";
+ };
+ }
+ '';
+ };
+
+ margin = mkOption {
+ type = nullOr str;
+ default = null;
+ description = "Margins value using the CSS format without units.";
+ example = "20 5";
+ };
+
+ inherit (margins) margin-top margin-left margin-bottom margin-right;
+
+ name = mkOption {
+ type = nullOr str;
+ default = null;
+ description =
+ "Optional name added as a CSS class, for styling multiple waybars.";
+ example = "waybar-1";
+ };
+
+ gtk-layer-shell = mkOption {
+ type = nullOr bool;
+ default = null;
+ example = false;
+ description =
+ "Option to disable the use of gtk-layer-shell for popups.";
+ };
+ };
+ };
+in {
+ meta.maintainers = [ hm.maintainers.berbiche ];
+
+ options.programs.waybar = with lib.types; {
+ enable = mkEnableOption "Waybar";
+
+ package = mkOption {
+ type = package;
+ default = pkgs.waybar;
+ defaultText = literalExample "${pkgs.waybar}";
+ description = ''
+ Waybar package to use. Set to <code>null</code> to use the default module.
+ '';
+ };
+
+ settings = mkOption {
+ type = listOf waybarBarConfig;
+ default = [ ];
+ description = ''
+ Configuration for Waybar, see <link
+ xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for supported values.
+ '';
+ example = literalExample ''
+ [
+ {
+ layer = "top";
+ position = "top";
+ height = 30;
+ output = [
+ "eDP-1"
+ "HDMI-A-1"
+ ];
+ modules-left = [ "sway/workspaces" "sway/mode" "wlr/taskbar" ];
+ modules-center = [ "sway/window" "custom/hello-from-waybar" ];
+ modules-right = [ "mpd" "custom/mymodule#with-css-id" "temperature" ];
+ modules = {
+ "sway/workspaces" = {
+ disable-scroll = true;
+ all-outputs = true;
+ };
+ "custom/hello-from-waybar" = {
+ format = "hello {}";
+ max-length = 40;
+ interval = "once";
+ exec = pkgs.writeShellScript "hello-from-waybar" '''
+ echo "from within waybar"
+ ''';
+ };
+ };
+ }
+ ]
+ '';
+ };
+
+ systemd.enable = mkEnableOption "Waybar systemd integration";
+
+ style = mkOption {
+ type = nullOr str;
+ default = null;
+ description = ''
+ CSS style of the bar.
+ See <link xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for the documentation.
+ '';
+ example = ''
+ * {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ }
+ window#waybar {
+ background: #16191C;
+ color: #AAB2BF;
+ }
+ #workspaces button {
+ padding: 0 5px;
+ }
+ '';
+ };
+ };
+
+ config = let
+ # Inspired by https://github.com/NixOS/nixpkgs/pull/89781
+ writePrettyJSON = name: x:
+ pkgs.runCommandLocal name { } ''
+ ${pkgs.jq}/bin/jq . > $out <<<${escapeShellArg (builtins.toJSON x)}
+ '';
+
+ configSource = let
+ # Removes nulls because Waybar ignores them for most values
+ removeNulls = filterAttrs (_: v: v != null);
+
+ # Makes the actual valid configuration Waybar accepts
+ # (strips our custom settings before converting to JSON)
+ makeConfiguration = configuration:
+ let
+ # The "modules" option is not valid in the JSON
+ # as its descendants have to live at the top-level
+ settingsWithoutModules =
+ filterAttrs (n: _: n != "modules") configuration;
+ settingsModules =
+ optionalAttrs (configuration.modules != { }) configuration.modules;
+ in removeNulls (settingsWithoutModules // settingsModules);
+ # The clean list of configurations
+ finalConfiguration = map makeConfiguration cfg.settings;
+ in writePrettyJSON "waybar-config.json" finalConfiguration;
+
+ warnings = let
+ mkUnreferencedModuleWarning = name:
+ "The module '${name}' defined in '${modulesPath}' is not referenced "
+ + "in either `modules-left`, `modules-center` or `modules-right` of Waybar's options";
+ mkUndefinedModuleWarning = settings: name:
+ let
+ # Locations where the module is undefined (a combination modules-{left,center,right})
+ locations = flip filter [ "left" "center" "right" ]
+ (x: elem name settings."modules-${x}");
+ mkPath = loc: "'${modulesPath}-${loc}'";
+ # The modules-{left,center,right} configuration that includes
+ # an undefined module
+ path = concatMapStringsSep " and " mkPath locations;
+ in "The module '${name}' defined in ${path} is neither "
+ + "a default module or a custom module declared in '${modulesPath}'";
+ mkInvalidModuleNameWarning = name:
+ "The custom module '${name}' defined in '${modulesPath}' is not a valid "
+ + "module name. A custom module's name must start with 'custom/' "
+ + "like 'custom/mymodule' for instance";
+
+ # Find all modules in `modules-{left,center,right}` and `modules` not declared/referenced.
+ # `cfg.settings` is a list of Waybar configurations
+ # and we need to preserve the index for appropriate warnings
+ allFaultyModules = flip map cfg.settings (settings:
+ let
+ allModules = unique
+ (concatMap (x: attrByPath [ "modules-${x}" ] [ ] settings) [
+ "left"
+ "center"
+ "right"
+ ]);
+ declaredModules = attrNames settings.modules;
+ # Modules declared in `modules` but not referenced in `modules-{left,center,right}`
+ unreferencedModules = subtractLists allModules declaredModules;
+ # Modules listed in modules-{left,center,right} that are not default modules
+ nonDefaultModules = subtractLists defaultModuleNames allModules;
+ # Modules referenced in `modules-{left,center,right}` but not declared in `modules`
+ undefinedModules = subtractLists declaredModules nonDefaultModules;
+ # Check for invalid module names
+ invalidModuleNames =
+ filter (m: !isValidCustomModuleName m) (attrNames settings.modules);
+ in {
+ # The Waybar bar configuration (since config.settings is a list)
+ settings = settings;
+ undef = undefinedModules;
+ unref = unreferencedModules;
+ invalidName = invalidModuleNames;
+ });
+
+ allWarnings = flip concatMap allFaultyModules
+ ({ settings, undef, unref, invalidName }:
+ let
+ unreferenced = map mkUnreferencedModuleWarning unref;
+ undefined = map (mkUndefinedModuleWarning settings) undef;
+ invalid = map mkInvalidModuleNameWarning invalidName;
+ in undefined ++ unreferenced ++ invalid);
+ in allWarnings;
+
+ in mkIf cfg.enable (mkMerge [
+ { home.packages = [ cfg.package ]; }
+ (mkIf (cfg.settings != [ ]) {
+ # Generate warnings about defined but unreferenced modules
+ inherit warnings;
+
+ xdg.configFile."waybar/config".source = configSource;
+ })
+ (mkIf (cfg.style != null) {
+ xdg.configFile."waybar/style.css".text = cfg.style;
+ })
+ (mkIf cfg.systemd.enable {
+ systemd.user.services.waybar = {
+ Unit = {
+ Description =
+ "Highly customizable Wayland bar for Sway and Wlroots based compositors.";
+ Documentation = "https://github.com/Alexays/Waybar/wiki";
+ PartOf = [ "graphical-session.target" ];
+ Requisite = [ "dbus.service" ];
+ After = [ "dbus.service" ];
+ };
+
+ Service = {
+ Type = "dbus";
+ BusName = "fr.arouillard.waybar";
+ ExecStart = "${cfg.package}/bin/waybar";
+ Restart = "always";
+ RestartSec = "1sec";
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+ };
+ })
+ ]);
+}
diff --git a/home-manager/modules/programs/zoxide.nix b/home-manager/modules/programs/zoxide.nix
new file mode 100644
index 00000000000..842ff109294
--- /dev/null
+++ b/home-manager/modules/programs/zoxide.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.zoxide;
+
+in {
+ meta.maintainers = [ maintainers.marsam ];
+
+ options.programs.zoxide = {
+ enable = mkEnableOption "zoxide";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.zoxide;
+ defaultText = literalExample "pkgs.zoxide";
+ description = ''
+ Zoxide package to install.
+ '';
+ };
+
+ options = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "--no-aliases" ];
+ description = ''
+ List of options to pass to zoxide.
+ '';
+ };
+
+ enableBashIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Bash integration.
+ '';
+ };
+
+ enableZshIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Zsh integration.
+ '';
+ };
+
+ enableFishIntegration = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable Fish integration.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+
+ programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
+ eval "$(${cfg.package}/bin/zoxide init bash ${
+ concatStringsSep " " cfg.options
+ })"
+ '';
+
+ programs.zsh.initExtra = mkIf cfg.enableZshIntegration ''
+ eval "$(${cfg.package}/bin/zoxide init zsh ${
+ concatStringsSep " " cfg.options
+ })"
+ '';
+
+ programs.fish.shellInit = mkIf cfg.enableFishIntegration ''
+ ${cfg.package}/bin/zoxide init fish ${
+ concatStringsSep " " cfg.options
+ } | source
+ '';
+ };
+}
diff --git a/home-manager/modules/programs/zplug.nix b/home-manager/modules/programs/zplug.nix
new file mode 100644
index 00000000000..6cb5e98e313
--- /dev/null
+++ b/home-manager/modules/programs/zplug.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.zsh.zplug;
+
+ pluginModule = types.submodule ({ config, ... }: {
+ options = {
+ name = mkOption {
+ type = types.str;
+ description = "The name of the plugin.";
+ };
+
+ tags = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ description = "The plugin tags.";
+ };
+ };
+
+ });
+
+in {
+ options.programs.zsh.zplug = {
+ enable = mkEnableOption "zplug - a zsh plugin manager";
+
+ plugins = mkOption {
+ default = [ ];
+ type = types.listOf pluginModule;
+ description = "List of zplug plugins.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.zplug ];
+
+ programs.zsh.initExtraBeforeCompInit = ''
+ source ${pkgs.zplug}/init.zsh
+
+ ${optionalString (cfg.plugins != [ ]) ''
+ ${concatStrings (map (plugin: ''
+ zplug "${plugin.name}"${
+ optionalString (plugin.tags != [ ]) ''
+ ${concatStrings (map (tag: ", ${tag}") plugin.tags)}
+ ''
+ }
+ '') cfg.plugins)}
+ ''}
+
+ if ! zplug check; then
+ zplug install
+ fi
+
+ zplug load
+ '';
+
+ };
+}
diff --git a/home-manager/modules/programs/zsh.nix b/home-manager/modules/programs/zsh.nix
index c5694e1f704..ed65d5fe487 100644
--- a/home-manager/modules/programs/zsh.nix
+++ b/home-manager/modules/programs/zsh.nix
@@ -18,6 +18,10 @@ let
mapAttrsToList (k: v: "alias ${k}=${lib.escapeShellArg v}") cfg.shellAliases
);
+ globalAliasesStr = concatStringsSep "\n" (
+ mapAttrsToList (k: v: "alias -g ${k}=${lib.escapeShellArg v}") cfg.shellGlobalAliases
+ );
+
zdotdir = "$HOME/" + cfg.dotDir;
bindkeyCommands = {
@@ -152,6 +156,17 @@ let
Name of the theme to be used by oh-my-zsh.
'';
};
+
+ extraConfig = mkOption {
+ default = "";
+ example = ''
+ zstyle :omz:plugins:ssh-agent identities id_rsa id_rsa2 id_github
+ '';
+ type = types.lines;
+ description = ''
+ Extra settings for plugins.
+ '';
+ };
};
};
@@ -170,6 +185,14 @@ in
type = types.nullOr types.bool;
};
+ cdpath = mkOption {
+ default = [];
+ description = ''
+ List of paths to autocomplete calls to `cd`.
+ '';
+ type = types.listOf types.str;
+ };
+
dotDir = mkOption {
default = null;
example = ".config/zsh";
@@ -183,7 +206,12 @@ in
shellAliases = mkOption {
default = {};
- example = { ll = "ls -l"; ".." = "cd .."; };
+ example = literalExample ''
+ {
+ ll = "ls -l";
+ ".." = "cd ..";
+ }
+ '';
description = ''
An attribute set that maps aliases (the top level attribute names in
this option) to command strings or directly to build outputs.
@@ -191,6 +219,21 @@ in
type = types.attrsOf types.str;
};
+ shellGlobalAliases = mkOption {
+ default = {};
+ example = literalExample ''
+ {
+ UUID = "$(uuidgen | tr -d \\n)";
+ G = "| grep";
+ }
+ '';
+ description = ''
+ Similar to <varname><link linkend="opt-programs.zsh.shellAliases">opt-programs.zsh.shellAliases</link></varname>,
+ but are substituted anywhere on a line.
+ '';
+ type = types.attrsOf types.str;
+ };
+
enableCompletion = mkOption {
default = true;
description = ''
@@ -357,6 +400,10 @@ in
home.file."${relToDotDir ".zshrc"}".text = ''
typeset -U path cdpath fpath manpath
+ ${optionalString (cfg.cdpath != []) ''
+ cdpath+=(${concatStringsSep " " cfg.cdpath})
+ ''}
+
for profile in ''${(z)NIX_PROFILES}; do
fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
done
@@ -393,6 +440,8 @@ in
${envVarsStr}
${optionalString cfg.oh-my-zsh.enable ''
+ # oh-my-zsh extra settings for plugins
+ ${cfg.oh-my-zsh.extraConfig}
# oh-my-zsh configuration generated by NixOS
${optionalString (cfg.oh-my-zsh.plugins != [])
"plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})"
@@ -433,6 +482,9 @@ in
# Aliases
${aliasesStr}
+
+ # Global Aliases
+ ${globalAliasesStr}
'';
}
diff --git a/home-manager/modules/services/clipmenu.nix b/home-manager/modules/services/clipmenu.nix
new file mode 100644
index 00000000000..2e1c10e43d8
--- /dev/null
+++ b/home-manager/modules/services/clipmenu.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.clipmenu;
+
+in {
+ meta.maintainers = [ maintainers.DamienCassou ];
+
+ options.services.clipmenu = {
+ enable = mkEnableOption "clipmenu, the clipboard management daemon";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.clipmenu;
+ defaultText = "pkgs.clipmenu";
+ description = "clipmenu derivation to use.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+
+ systemd.user.services.clipmenu = {
+ Unit = {
+ Description = "Clipboard management daemon";
+ After = [ "graphical-session.target" ];
+ };
+
+ Service = {
+ ExecStart = "${cfg.package}/bin/clipmenud";
+ Environment = "PATH=${
+ makeBinPath
+ (with pkgs; [ coreutils findutils gnugrep gnused systemd ])
+ }";
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+ };
+ };
+}
diff --git a/home-manager/modules/services/compton.nix b/home-manager/modules/services/compton.nix
index c5b96af34da..0b8e7232b45 100644
--- a/home-manager/modules/services/compton.nix
+++ b/home-manager/modules/services/compton.nix
@@ -1,291 +1,43 @@
{ config, lib, pkgs, ... }:
-with lib;
-with builtins;
-
-let
-
- cfg = config.services.compton;
-
- configFile = pkgs.writeText "compton.conf" (optionalString cfg.fade ''
- # fading
- fading = true;
- fade-delta = ${toString cfg.fadeDelta};
- fade-in-step = ${elemAt cfg.fadeSteps 0};
- fade-out-step = ${elemAt cfg.fadeSteps 1};
- fade-exclude = ${toJSON cfg.fadeExclude};
- '' + optionalString cfg.shadow ''
-
- # shadows
- shadow = true;
- shadow-offset-x = ${toString (elemAt cfg.shadowOffsets 0)};
- shadow-offset-y = ${toString (elemAt cfg.shadowOffsets 1)};
- shadow-opacity = ${cfg.shadowOpacity};
- shadow-exclude = ${toJSON cfg.shadowExclude};
- no-dock-shadow = ${toJSON cfg.noDockShadow};
- no-dnd-shadow = ${toJSON cfg.noDNDShadow};
- '' + optionalString cfg.blur ''
-
- # blur
- blur-background = true;
- blur-background-exclude = ${toJSON cfg.blurExclude};
- '' + ''
-
- # opacity
- active-opacity = ${cfg.activeOpacity};
- inactive-opacity = ${cfg.inactiveOpacity};
- menu-opacity = ${cfg.menuOpacity};
- opacity-rule = ${toJSON cfg.opacityRule};
-
- # other options
- backend = ${toJSON cfg.backend};
- vsync = ${toJSON cfg.vSync};
- refresh-rate = ${toString cfg.refreshRate};
- '' + cfg.extraOptions);
-
-in {
-
- options.services.compton = {
- enable = mkEnableOption "Compton X11 compositor";
-
- blur = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Enable background blur on transparent windows.
- '';
- };
-
- blurExclude = mkOption {
- type = types.listOf types.str;
- default = [ ];
- example = [ "class_g = 'slop'" "class_i = 'polybar'" ];
- description = ''
- List of windows to exclude background blur.
- See the
- <citerefentry>
- <refentrytitle>compton</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- man page for more examples.
- '';
- };
-
- fade = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Fade windows in and out.
- '';
- };
-
- fadeDelta = mkOption {
- type = types.int;
- default = 10;
- example = 5;
- description = ''
- Time between fade animation step (in ms).
- '';
- };
-
- fadeSteps = mkOption {
- type = types.listOf types.str;
- default = [ "0.028" "0.03" ];
- example = [ "0.04" "0.04" ];
- description = ''
- Opacity change between fade steps (in and out).
- '';
- };
-
- fadeExclude = mkOption {
- type = types.listOf types.str;
- default = [ ];
- example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ];
- description = ''
- List of conditions of windows that should not be faded.
- See the
- <citerefentry>
- <refentrytitle>compton</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- man page for more examples.
- '';
- };
-
- shadow = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Draw window shadows.
- '';
- };
-
- shadowOffsets = mkOption {
- type = types.listOf types.int;
- default = [ (-15) (-15) ];
- example = [ (-10) (-15) ];
- description = ''
- Horizontal and vertical offsets for shadows (in pixels).
- '';
- };
-
- shadowOpacity = mkOption {
- type = types.str;
- default = "0.75";
- example = "0.8";
- description = ''
- Window shadows opacity (number in range 0 - 1).
- '';
- };
-
- shadowExclude = mkOption {
- type = types.listOf types.str;
- default = [ ];
- example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ];
- description = ''
- List of conditions of windows that should have no shadow.
- See the
- <citerefentry>
- <refentrytitle>compton</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- man page for more examples.
- '';
- };
-
- noDockShadow = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Avoid shadow on docks.
- '';
- };
-
- noDNDShadow = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Avoid shadow on drag-and-drop windows.
- '';
- };
-
- activeOpacity = mkOption {
- type = types.str;
- default = "1.0";
- example = "0.8";
- description = ''
- Opacity of active windows.
- '';
- };
-
- inactiveOpacity = mkOption {
- type = types.str;
- default = "1.0";
- example = "0.8";
- description = ''
- Opacity of inactive windows.
- '';
- };
-
- menuOpacity = mkOption {
- type = types.str;
- default = "1.0";
- example = "0.8";
- description = ''
- Opacity of dropdown and popup menu.
- '';
- };
-
- opacityRule = mkOption {
- type = types.listOf types.str;
- default = [ ];
- example = [ "87:class_i ?= 'scratchpad'" "91:class_i ?= 'xterm'" ];
- description = ''
- List of opacity rules.
- See the
- <citerefentry>
- <refentrytitle>compton</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- man page for more examples.
- '';
- };
-
- backend = mkOption {
- type = types.str;
- default = "glx";
- description = ''
- Backend to use: <literal>glx</literal> or <literal>xrender</literal>.
- '';
- };
-
- vSync = mkOption {
- type = types.str;
- default = "none";
- example = "opengl-swc";
- description = ''
- Enable vertical synchronization using the specified method.
- See the
- <citerefentry>
- <refentrytitle>compton</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- man page for available methods.
- '';
- };
-
- refreshRate = mkOption {
- type = types.int;
- default = 0;
- example = 60;
- description = ''
- Screen refresh rate (0 = automatically detect).
- '';
- };
-
- package = mkOption {
- type = types.package;
- default = pkgs.compton;
- defaultText = literalExample "pkgs.compton";
- example = literalExample "pkgs.compton";
- description = ''
- Compton derivation to use.
- '';
- };
-
- extraOptions = mkOption {
- type = types.str;
- default = "";
- example = ''
- unredir-if-possible = true;
- dbe = true;
- '';
- description = ''
- Additional Compton configuration.
- '';
- };
+with lib; {
+ imports = let
+ old = n: [ "services" "compton" n ];
+ new = n: [ "services" "picom" n ];
+ in [
+ (mkRenamedOptionModule (old "activeOpacity") (new "activeOpacity"))
+ (mkRenamedOptionModule (old "backend") (new "backend"))
+ (mkRenamedOptionModule (old "blur") (new "blur"))
+ (mkRenamedOptionModule (old "blurExclude") (new "blurExclude"))
+ (mkRenamedOptionModule (old "extraOptions") (new "extraOptions"))
+ (mkRenamedOptionModule (old "fade") (new "fade"))
+ (mkRenamedOptionModule (old "fadeDelta") (new "fadeDelta"))
+ (mkRenamedOptionModule (old "fadeExclude") (new "fadeExclude"))
+ (mkRenamedOptionModule (old "fadeSteps") (new "fadeSteps"))
+ (mkRenamedOptionModule (old "inactiveDim") (new "inactiveDim"))
+ (mkRenamedOptionModule (old "inactiveOpacity") (new "inactiveOpacity"))
+ (mkRenamedOptionModule (old "menuOpacity") (new "menuOpacity"))
+ (mkRenamedOptionModule (old "noDNDShadow") (new "noDNDShadow"))
+ (mkRenamedOptionModule (old "noDockShadow") (new "noDockShadow"))
+ (mkRenamedOptionModule (old "opacityRule") (new "opacityRule"))
+ (mkRenamedOptionModule (old "package") (new "package"))
+ (mkRenamedOptionModule (old "refreshRate") (new "refreshRate"))
+ (mkRenamedOptionModule (old "shadow") (new "shadow"))
+ (mkRenamedOptionModule (old "shadowExclude") (new "shadowExclude"))
+ (mkRenamedOptionModule (old "shadowOffsets") (new "shadowOffsets"))
+ (mkRenamedOptionModule (old "shadowOpacity") (new "shadowOpacity"))
+ (mkChangedOptionModule (old "vSync") (new "vSync") (v: v != "none"))
+ ];
+
+ options.services.compton.enable = mkEnableOption "Compton X11 compositor" // {
+ visible = false;
};
- config = mkIf cfg.enable {
- home.packages = [ cfg.package ];
-
- systemd.user.services.compton = {
- Unit = {
- Description = "Compton X11 compositor";
- After = [ "graphical-session-pre.target" ];
- PartOf = [ "graphical-session.target" ];
- };
-
- Install = { WantedBy = [ "graphical-session.target" ]; };
+ config = mkIf config.services.compton.enable {
+ warnings = [
+ "Obsolete option `services.compton.enable' is used. It was renamed to `services.picom.enable'."
+ ];
- Service = {
- ExecStart = "${cfg.package}/bin/compton --config ${configFile}";
- Restart = "always";
- RestartSec = 3;
- } // optionalAttrs (cfg.backend == "glx") {
- # Temporarily fixes corrupt colours with Mesa 18.
- Environment = [ "allow_rgb10_configs=false" ];
- };
- };
+ services.picom.enable = true;
};
}
diff --git a/home-manager/modules/services/dropbox.nix b/home-manager/modules/services/dropbox.nix
new file mode 100644
index 00000000000..bcf3ba2b457
--- /dev/null
+++ b/home-manager/modules/services/dropbox.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.dropbox;
+ baseDir = ".dropbox-hm";
+ dropboxCmd = "${pkgs.dropbox-cli}/bin/dropbox";
+ homeBaseDir = "${config.home.homeDirectory}/${baseDir}";
+
+in {
+ meta.maintainers = [ maintainers.eyjhb ];
+
+ options = {
+ services.dropbox = {
+ enable = mkEnableOption "Dropbox daemon";
+
+ path = mkOption {
+ type = types.path;
+ default = "${config.home.homeDirectory}/Dropbox";
+ defaultText =
+ literalExample ''"''${config.home.homeDirectory}/Dropbox"'';
+ apply = toString; # Prevent copies to Nix store.
+ description = "Where to put the Dropbox directory.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.dropbox-cli ];
+
+ systemd.user.services.dropbox = {
+ Unit = { Description = "dropbox"; };
+
+ Install = { WantedBy = [ "default.target" ]; };
+
+ Service = {
+ Environment = [ "HOME=${homeBaseDir}" "DISPLAY=" ];
+
+ Type = "forking";
+ PIDFile = "${homeBaseDir}/.dropbox/dropbox.pid";
+
+ Restart = "on-failure";
+ PrivateTmp = true;
+ ProtectSystem = "full";
+ Nice = 10;
+
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecStop = "${dropboxCmd} stop";
+ ExecStart = toString (pkgs.writeShellScript "dropbox-start" ''
+ # ensure we have the dirs we need
+ $DRY_RUN_CMD ${pkgs.coreutils}/bin/mkdir $VERBOSE_ARG -p \
+ ${homeBaseDir}/{.dropbox,.dropbox-dist,Dropbox}
+
+ # symlink them as needed
+ if [[ ! -d ${config.home.homeDirectory}/.dropbox ]]; then
+ $DRY_RUN_CMD ${pkgs.coreutils}/bin/ln $VERBOSE_ARG -s \
+ ${homeBaseDir}/.dropbox ${config.home.homeDirectory}/.dropbox
+ fi
+
+ if [[ ! -d ${escapeShellArg cfg.path} ]]; then
+ $DRY_RUN_CMD ${pkgs.coreutils}/bin/ln $VERBOSE_ARG -s \
+ ${homeBaseDir}/Dropbox ${escapeShellArg cfg.path}
+ fi
+
+ # get the dropbox bins if needed
+ if [[ ! -f $HOME/.dropbox-dist/VERSION ]]; then
+ ${pkgs.coreutils}/bin/yes | ${dropboxCmd} update
+ fi
+
+ ${dropboxCmd} start
+ '');
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/services/dunst.nix b/home-manager/modules/services/dunst.nix
index d32e875137b..5fbbb884a8e 100644
--- a/home-manager/modules/services/dunst.nix
+++ b/home-manager/modules/services/dunst.nix
@@ -45,7 +45,7 @@ let
};
hicolorTheme = {
- package = pkgs.hicolor_icon_theme;
+ package = pkgs.hicolor-icon-theme;
name = "hicolor";
size = "32x32";
};
@@ -89,6 +89,8 @@ in {
config = mkIf cfg.enable (mkMerge [
{
+ home.packages = [ (getOutput "man" pkgs.dunst) ];
+
xdg.dataFile."dbus-1/services/org.knopwob.dunst.service".source =
"${pkgs.dunst}/share/dbus-1/services/org.knopwob.dunst.service";
diff --git a/home-manager/modules/services/emacs.nix b/home-manager/modules/services/emacs.nix
index 5b0e88db72d..a73b750c513 100644
--- a/home-manager/modules/services/emacs.nix
+++ b/home-manager/modules/services/emacs.nix
@@ -7,36 +7,138 @@ let
cfg = config.services.emacs;
emacsCfg = config.programs.emacs;
emacsBinPath = "${emacsCfg.finalPackage}/bin";
+ emacsVersion = getVersion emacsCfg.finalPackage;
+
+ # Adapted from upstream emacs.desktop
+ clientDesktopItem = pkgs.makeDesktopItem rec {
+ name = "emacsclient";
+ desktopName = "Emacs Client";
+ genericName = "Text Editor";
+ comment = "Edit text";
+ mimeType =
+ "text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;";
+ exec = "${emacsBinPath}/emacsclient ${
+ concatStringsSep " " cfg.client.arguments
+ } %F";
+ icon = "emacs";
+ type = "Application";
+ terminal = "false";
+ categories = "Utility;TextEditor;";
+ extraEntries = ''
+ StartupWMClass=Emacs
+ '';
+ };
+
+ # Match the default socket path for the Emacs version so emacsclient continues
+ # to work without wrapping it. It might be worthwhile to allow customizing the
+ # socket path, but we would want to wrap emacsclient in the user profile to
+ # connect to the alternative socket by default for Emacs 26, and set
+ # EMACS_SOCKET_NAME for Emacs 27.
+ #
+ # As systemd doesn't perform variable expansion for the ListenStream param, we
+ # would also have to solve the problem of matching the shell path to the path
+ # used in the socket unit, which would likely involve templating. It seems of
+ # little value for the most common use case of one Emacs daemon per user
+ # session.
+ socketPath = if versionAtLeast emacsVersion "27" then
+ "%t/emacs/server"
+ else
+ "%T/emacs%U/server";
in {
- options.services.emacs = { enable = mkEnableOption "the Emacs daemon"; };
-
- config = mkIf cfg.enable {
- assertions = [{
- assertion = emacsCfg.enable;
- message = "The Emacs service module requires"
- + " 'programs.emacs.enable = true'.";
- }];
-
- systemd.user.services.emacs = {
- Unit = {
- Description = "Emacs: the extensible, self-documenting text editor";
- Documentation =
- "info:emacs man:emacs(1) https://gnu.org/software/emacs/";
-
- # Avoid killing the Emacs session, which may be full of
- # unsaved buffers.
- X-RestartIfChanged = false;
- };
+ meta.maintainers = [ maintainers.tadfisher ];
- Service = {
- ExecStart =
- "${pkgs.runtimeShell} -l -c 'exec ${emacsBinPath}/emacs --fg-daemon'";
- ExecStop = "${emacsBinPath}/emacsclient --eval '(kill-emacs)'";
- Restart = "on-failure";
+ options.services.emacs = {
+ enable = mkEnableOption "the Emacs daemon";
+
+ client = {
+ enable = mkEnableOption "generation of Emacs client desktop file";
+ arguments = mkOption {
+ type = with types; listOf str;
+ default = [ "-c" ];
+ description = ''
+ Command-line arguments to pass to <command>emacsclient</command>.
+ '';
};
+ };
- Install = { WantedBy = [ "default.target" ]; };
+ # Attrset for forward-compatibility; there may be a need to customize the
+ # socket path, though allowing for such is not easy to do as systemd socket
+ # units don't perform variable expansion for 'ListenStream'.
+ socketActivation = {
+ enable = mkEnableOption "systemd socket activation for the Emacs service";
};
};
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ assertions = [
+ {
+ assertion = emacsCfg.enable;
+ message = "The Emacs service module requires"
+ + " 'programs.emacs.enable = true'.";
+ }
+ {
+ assertion = cfg.socketActivation.enable
+ -> versionAtLeast emacsVersion "26";
+ message = "Socket activation requires Emacs 26 or newer.";
+ }
+ ];
+
+ systemd.user.services.emacs = {
+ Unit = {
+ Description = "Emacs: the extensible, self-documenting text editor";
+ Documentation =
+ "info:emacs man:emacs(1) https://gnu.org/software/emacs/";
+
+ # Avoid killing the Emacs session, which may be full of
+ # unsaved buffers.
+ X-RestartIfChanged = false;
+ };
+
+ Service = {
+ # We wrap ExecStart in a login shell so Emacs starts with the user's
+ # environment, most importantly $PATH and $NIX_PROFILES. It may be
+ # worth investigating a more targeted approach for user services to
+ # import the user environment.
+ ExecStart = ''
+ ${pkgs.runtimeShell} -l -c "${emacsBinPath}/emacs --fg-daemon${
+ # In case the user sets 'server-directory' or 'server-name' in
+ # their Emacs config, we want to specify the socket path explicitly
+ # so launching 'emacs.service' manually doesn't break emacsclient
+ # when using socket activation.
+ optionalString cfg.socketActivation.enable
+ "=${escapeShellArg socketPath}"
+ }"'';
+ # We use '(kill-emacs 0)' to avoid exiting with a failure code, which
+ # would restart the service immediately.
+ ExecStop = "${emacsBinPath}/emacsclient --eval '(kill-emacs 0)'";
+ Restart = "on-failure";
+ };
+ } // optionalAttrs (!cfg.socketActivation.enable) {
+ Install = { WantedBy = [ "default.target" ]; };
+ };
+
+ home.packages = optional cfg.client.enable clientDesktopItem;
+ }
+
+ (mkIf cfg.socketActivation.enable {
+ systemd.user.sockets.emacs = {
+ Unit = {
+ Description = "Emacs: the extensible, self-documenting text editor";
+ Documentation =
+ "info:emacs man:emacs(1) https://gnu.org/software/emacs/";
+ };
+
+ Socket = {
+ ListenStream = socketPath;
+ FileDescriptorName = "server";
+ SocketMode = "0600";
+ DirectoryMode = "0700";
+ };
+
+ Install = { WantedBy = [ "sockets.target" ]; };
+ };
+ })
+ ]);
}
diff --git a/home-manager/modules/services/fluidsynth.nix b/home-manager/modules/services/fluidsynth.nix
new file mode 100644
index 00000000000..18913fe5426
--- /dev/null
+++ b/home-manager/modules/services/fluidsynth.nix
@@ -0,0 +1,57 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.fluidsynth;
+
+in {
+ meta.maintainers = [ maintainers.valodim ];
+
+ options = {
+ services.fluidsynth = {
+ enable = mkEnableOption "fluidsynth midi synthesizer";
+
+ soundFont = mkOption {
+ type = types.path;
+ default = "${pkgs.soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2";
+ description = ''
+ The soundfont file to use, in SoundFont 2 format.
+ '';
+ };
+
+ extraOptions = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "--sample-rate 96000" ];
+ description = ''
+ Extra arguments, added verbatim to the fluidsynth command. See
+ <citerefentry>
+ <refentrytitle>fluidsynth.conf</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>.
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.user.services.fluidsynth = {
+ Unit = {
+ Description = "FluidSynth Daemon";
+ Documentation = "man:fluidsynth(1)";
+ BindsTo = [ "pulseaudio.service" ];
+ After = [ "pulseaudio.service" ];
+ };
+
+ Install = { WantedBy = [ "default.target" ]; };
+
+ Service = {
+ ExecStart = "${pkgs.fluidsynth}/bin/fluidsynth -a pulseaudio -si ${
+ lib.concatStringsSep " " cfg.extraOptions
+ } ${cfg.soundFont}";
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/services/gnome-keyring.nix b/home-manager/modules/services/gnome-keyring.nix
index 6d8317dcffc..ce39cea93f9 100644
--- a/home-manager/modules/services/gnome-keyring.nix
+++ b/home-manager/modules/services/gnome-keyring.nix
@@ -36,7 +36,7 @@ in {
args = concatStringsSep " " ([ "--start" "--foreground" ]
++ optional (cfg.components != [ ])
("--components=" + concatStringsSep "," cfg.components));
- in "${pkgs.gnome3.gnome_keyring}/bin/gnome-keyring-daemon ${args}";
+ in "${pkgs.gnome3.gnome-keyring}/bin/gnome-keyring-daemon ${args}";
Restart = "on-abort";
};
diff --git a/home-manager/modules/services/kanshi.nix b/home-manager/modules/services/kanshi.nix
new file mode 100644
index 00000000000..4e5e5f104e6
--- /dev/null
+++ b/home-manager/modules/services/kanshi.nix
@@ -0,0 +1,194 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.kanshi;
+
+ outputModule = types.submodule {
+ options = {
+
+ criteria = mkOption {
+ type = types.str;
+ description = ''
+ The criteria can either be an output name, an output description or "*".
+ The latter can be used to match any output.
+
+ On
+ <citerefentry>
+ <refentrytitle>sway</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>,
+ output names and descriptions can be obtained via
+ <literal>swaymsg -t get_outputs</literal>.
+ '';
+ };
+
+ status = mkOption {
+ type = types.nullOr (types.enum [ "enable" "disable" ]);
+ default = null;
+ description = ''
+ Enables or disables the specified output.
+ '';
+ };
+
+ mode = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "1920x1080@60Hz";
+ description = ''
+ &lt;width&gt;x&lt;height&gt;[@&lt;rate&gt;[Hz]]
+ </para><para>
+ Configures the specified output to use the specified mode.
+ Modes are a combination of width and height (in pixels) and
+ a refresh rate (in Hz) that your display can be configured to use.
+ '';
+ };
+
+ position = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "1600,0";
+ description = ''
+ &lt;x&gt;,&lt;y&gt;
+ </para><para>
+ Places the output at the specified position in the global coordinates
+ space.
+ '';
+ };
+
+ scale = mkOption {
+ type = types.nullOr types.float;
+ default = null;
+ example = 2;
+ description = ''
+ Scales the output by the specified scale factor.
+ '';
+ };
+
+ transform = mkOption {
+ type = types.nullOr (types.enum [
+ "normal"
+ "90"
+ "180"
+ "270"
+ "flipped"
+ "flipped-90"
+ "flipped-180"
+ "flipped-270"
+ ]);
+ default = null;
+ description = ''
+ Sets the output transform.
+ '';
+ };
+ };
+ };
+
+ outputStr = { criteria, status, mode, position, scale, transform, ... }:
+ ''output "${criteria}"'' + optionalString (status != null) " ${status}"
+ + optionalString (mode != null) " mode ${mode}"
+ + optionalString (position != null) " position ${position}"
+ + optionalString (scale != null) " scale ${toString scale}"
+ + optionalString (transform != null) " transform ${transform}";
+
+ profileModule = types.submodule {
+ options = {
+ outputs = mkOption {
+ type = types.listOf outputModule;
+ default = [ ];
+ description = ''
+ Outputs configuration.
+ '';
+ };
+
+ exec = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example =
+ "\${pkg.sway}/bin/swaymsg workspace 1, move workspace to eDP-1";
+ description = ''
+ Command executed after the profile is succesfully applied.
+ '';
+ };
+ };
+ };
+
+ profileStr = name:
+ { outputs, exec, ... }:
+ ''
+ profile ${name} {
+ ${concatStringsSep "\n " (map outputStr outputs)}
+ '' + optionalString (exec != null) " exec ${exec}\n" + ''
+ }
+ '';
+in {
+
+ meta.maintainers = [ maintainers.nurelin ];
+
+ options.services.kanshi = {
+ enable = mkEnableOption
+ "kanshi, a Wayland daemon that automatically configures outputs";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.kanshi;
+ defaultText = literalExample "pkgs.kanshi";
+ description = ''
+ kanshi derivation to use.
+ '';
+ };
+
+ profiles = mkOption {
+ type = types.attrsOf profileModule;
+ default = { };
+ description = ''
+ List of profiles.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Extra configuration lines to append to the kanshi
+ configuration file.
+ '';
+ };
+
+ systemdTarget = mkOption {
+ type = types.str;
+ default = "sway-session.target";
+ description = ''
+ Systemd target to bind to.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ xdg.configFile."kanshi/config".text = ''
+ ${concatStringsSep "\n" (mapAttrsToList profileStr cfg.profiles)}
+ ${cfg.extraConfig}
+ '';
+
+ systemd.user.services.kanshi = {
+ Unit = {
+ Description = "Dynamic output configuration";
+ Documentation = "man:kanshi(1)";
+ PartOf = cfg.systemdTarget;
+ Requires = cfg.systemdTarget;
+ After = cfg.systemdTarget;
+ };
+
+ Service = {
+ Type = "simple";
+ ExecStart = "${cfg.package}/bin/kanshi";
+ Restart = "always";
+ };
+
+ Install = { WantedBy = [ cfg.systemdTarget ]; };
+ };
+ };
+}
diff --git a/home-manager/modules/services/keynav.nix b/home-manager/modules/services/keynav.nix
new file mode 100644
index 00000000000..c7f1df373b8
--- /dev/null
+++ b/home-manager/modules/services/keynav.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.keynav;
+
+in {
+ options.services.keynav = { enable = mkEnableOption "keynav"; };
+
+ config = mkIf cfg.enable {
+ systemd.user.services.keynav = {
+ Unit = {
+ Description = "keynav";
+ After = [ "graphical-session-pre.target" ];
+ PartOf = [ "graphical-session.target" ];
+ };
+
+ Service = {
+ ExecStart = "${pkgs.keynav}/bin/keynav";
+ RestartSec = 3;
+ Restart = "always";
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+ };
+ };
+}
diff --git a/home-manager/modules/services/lieer-accounts.nix b/home-manager/modules/services/lieer-accounts.nix
new file mode 100644
index 00000000000..187f7dff980
--- /dev/null
+++ b/home-manager/modules/services/lieer-accounts.nix
@@ -0,0 +1,25 @@
+{ lib, ... }:
+
+with lib;
+
+{
+ options.lieer.sync = {
+ enable = mkEnableOption "lieer synchronization service";
+
+ frequency = mkOption {
+ type = types.str;
+ default = "*:0/5";
+ description = ''
+ How often to synchronize the account.
+ </para><para>
+ This value is passed to the systemd timer configuration as the
+ onCalendar option. See
+ <citerefentry>
+ <refentrytitle>systemd.time</refentrytitle>
+ <manvolnum>7</manvolnum>
+ </citerefentry>
+ for more information about the format.
+ '';
+ };
+ };
+}
diff --git a/home-manager/modules/services/lieer.nix b/home-manager/modules/services/lieer.nix
new file mode 100644
index 00000000000..571e2af75c8
--- /dev/null
+++ b/home-manager/modules/services/lieer.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.lieer;
+
+ syncAccounts = filter (a: a.lieer.enable && a.lieer.sync.enable)
+ (attrValues config.accounts.email.accounts);
+
+ escapeUnitName = name:
+ let
+ good = upperChars ++ lowerChars ++ stringToCharacters "0123456789-_";
+ subst = c: if any (x: x == c) good then c else "-";
+ in stringAsChars subst name;
+
+ serviceUnit = account: {
+ name = escapeUnitName "lieer-${account.name}";
+ value = {
+ Unit = {
+ Description = "lieer Gmail synchronization for ${account.name}";
+ ConditionPathExists = "${account.maildir.absPath}/.gmailieer.json";
+ };
+
+ Service = {
+ Type = "oneshot";
+ ExecStart = "${pkgs.gmailieer}/bin/gmi sync";
+ WorkingDirectory = account.maildir.absPath;
+ };
+ };
+ };
+
+ timerUnit = account: {
+ name = escapeUnitName "lieer-${account.name}";
+ value = {
+ Unit = {
+ Description = "lieer Gmail synchronization for ${account.name}";
+ };
+
+ Timer = {
+ OnCalendar = account.lieer.sync.frequency;
+ RandomizedDelaySec = 30;
+ };
+
+ Install = { WantedBy = [ "timers.target" ]; };
+ };
+ };
+
+in {
+ meta.maintainers = [ maintainers.tadfisher ];
+
+ options = {
+ services.lieer.enable =
+ mkEnableOption "lieer Gmail synchronization service";
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./lieer-accounts.nix));
+ };
+ };
+
+ config = mkIf cfg.enable {
+ programs.lieer.enable = true;
+ systemd.user.services = listToAttrs (map serviceUnit syncAccounts);
+ systemd.user.timers = listToAttrs (map timerUnit syncAccounts);
+ };
+}
diff --git a/home-manager/modules/services/lorri.nix b/home-manager/modules/services/lorri.nix
index 3b2c244e3c0..6183699088b 100644
--- a/home-manager/modules/services/lorri.nix
+++ b/home-manager/modules/services/lorri.nix
@@ -9,10 +9,19 @@ let
in {
meta.maintainers = [ maintainers.gerschtli ];
- options = { services.lorri.enable = mkEnableOption "lorri build daemon"; };
+ options.services.lorri = {
+ enable = mkEnableOption "lorri build daemon";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.lorri;
+ defaultText = literalExample "pkgs.lorri";
+ description = "Which lorri package to install.";
+ };
+ };
config = mkIf cfg.enable {
- home.packages = [ pkgs.lorri ];
+ home.packages = [ cfg.package ];
systemd.user = {
services.lorri = {
@@ -24,7 +33,7 @@ in {
};
Service = {
- ExecStart = "${pkgs.lorri}/bin/lorri daemon";
+ ExecStart = "${cfg.package}/bin/lorri daemon";
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = "read-only";
@@ -32,7 +41,7 @@ in {
Environment = let
path = with pkgs;
makeSearchPath "bin" [ nix gitMinimal gnutar gzip ];
- in "PATH=${path}";
+ in [ "PATH=${path}" ];
};
};
diff --git a/home-manager/modules/services/mako.nix b/home-manager/modules/services/mako.nix
new file mode 100644
index 00000000000..77ea3011678
--- /dev/null
+++ b/home-manager/modules/services/mako.nix
@@ -0,0 +1,316 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.mako;
+
+in {
+ meta.maintainers = [ maintainers.onny ];
+
+ options = {
+ programs.mako = {
+ enable = mkEnableOption ''
+ Mako, lightweight notification daemon for Wayland
+ '';
+
+ maxVisible = mkOption {
+ default = 5;
+ type = types.nullOr types.int;
+ description = ''
+ Set maximum number of visible notifications. Set -1 to show all.
+ '';
+ };
+
+ sort = mkOption {
+ default = "-time";
+ type =
+ types.nullOr (types.enum [ "+time" "-time" "+priority" "-priority" ]);
+ description = ''
+ Sorts incoming notifications by time and/or priority in ascending(+)
+ or descending(-) order.
+ '';
+ };
+
+ output = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ Show notifications on the specified output. If empty, notifications
+ will appear on the focused output. Requires the compositor to support
+ the Wayland protocol xdg-output-unstable-v1 version 2.
+ '';
+ };
+
+ layer = mkOption {
+ default = "top";
+ type =
+ types.nullOr (types.enum [ "background" "bottom" "top" "overlay" ]);
+ description = ''
+ Arrange mako at the specified layer, relative to normal windows.
+ Supported values are background, bottom, top, and overlay. Using
+ overlay will cause notifications to be displayed above fullscreen
+ windows, though this may also occur at top depending on your
+ compositor.
+ '';
+ };
+
+ anchor = mkOption {
+ default = "top-right";
+ type = types.nullOr (types.enum [
+ "top-right"
+ "top-center"
+ "top-left"
+ "bottom-right"
+ "bottom-center"
+ "bottom-left"
+ "center"
+ ]);
+ description = ''
+ Show notifications at the specified position on the output.
+ Supported values are top-right, top-center, top-left, bottom-right,
+ bottom-center, bottom-left, and center.
+ '';
+ };
+
+ font = mkOption {
+ default = "monospace 10";
+ type = types.nullOr types.str;
+ description = ''
+ Font to use, in Pango format.
+ '';
+ };
+
+ backgroundColor = mkOption {
+ default = "#285577FF";
+ type = types.nullOr types.str;
+ description = ''
+ Set popup background color to a specific color, represented in hex
+ color code.
+ '';
+ };
+
+ textColor = mkOption {
+ default = "#FFFFFFFF";
+ type = types.nullOr types.str;
+ description = ''
+ Set popup text color to a specific color, represented in hex color
+ code.
+ '';
+ };
+
+ width = mkOption {
+ default = 300;
+ type = types.nullOr types.int;
+ description = ''
+ Set width of notification popups in specified number of pixels.
+ '';
+ };
+
+ height = mkOption {
+ default = 100;
+ type = types.nullOr types.int;
+ description = ''
+ Set maximum height of notification popups. Notifications whose text
+ takes up less space are shrunk to fit.
+ '';
+ };
+
+ margin = mkOption {
+ default = "10";
+ type = types.nullOr types.str;
+ description = ''
+ Set margin of each edge specified in pixels. Specify single value to
+ apply margin on all sides. Two comma-seperated values will set
+ vertical and horizontal edges seperately. Four comma-seperated will
+ give each edge a seperate value.
+ For example: 10,20,5 will set top margin to 10, left and right to 20
+ and bottom to five.
+ '';
+ };
+
+ padding = mkOption {
+ default = "5";
+ type = types.nullOr types.str;
+ description = ''
+ Set padding of each edge specified in pixels. Specify single value to
+ apply margin on all sides. Two comma-seperated values will set
+ vertical and horizontal edges seperately. Four comma-seperated will
+ give each edge a seperate value.
+ For example: 10,20,5 will set top margin to 10, left and right to 20
+ and bottom to five.
+ '';
+ };
+
+ borderSize = mkOption {
+ default = 1;
+ type = types.nullOr types.int;
+ description = ''
+ Set popup border size to the specified number of pixels.
+ '';
+ };
+
+ borderColor = mkOption {
+ default = "#4C7899FF";
+ type = types.nullOr types.str;
+ description = ''
+ Set popup border color to a specific color, represented in hex color
+ code.
+ '';
+ };
+
+ borderRadius = mkOption {
+ default = 0;
+ type = types.nullOr types.int;
+ description = ''
+ Set popup corner radius to the specified number of pixels.
+ '';
+ };
+
+ progressColor = mkOption {
+ default = "over #5588AAFF";
+ type = types.nullOr types.str;
+ description = ''
+ Set popup progress indicator color to a specific color,
+ represented in hex color code. To draw the progress
+ indicator on top of the background color, use the
+ <literal>over</literal> attribute. To replace the background
+ color, use the <literal>source</literal> attribute (this can
+ be useful when the notification is semi-transparent).
+ '';
+ };
+
+ icons = mkOption {
+ default = true;
+ type = types.nullOr types.bool;
+ description = ''
+ Whether or not to show icons in notifications.
+ '';
+ };
+
+ maxIconSize = mkOption {
+ default = 64;
+ type = types.nullOr types.int;
+ description = ''
+ Set maximum icon size to the specified number of pixels.
+ '';
+ };
+
+ iconPath = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ Paths to search for icons when a notification specifies a name
+ instead of a full path. Colon-delimited. This approximates the search
+ algorithm used by the XDG Icon Theme Specification, but does not
+ support any of the theme metadata. Therefore, if you want to search
+ parent themes, you'll need to add them to the path manually.
+ </para><para>
+ The <filename>/usr/share/icons/hicolor</filename> and
+ <filename>/usr/share/pixmaps</filename> directories are
+ always searched.
+ '';
+ };
+
+ markup = mkOption {
+ default = true;
+ type = types.nullOr types.bool;
+ description = ''
+ If 1, enable Pango markup. If 0, disable Pango markup. If enabled,
+ Pango markup will be interpreted in your format specifier and in the
+ body of notifications.
+ '';
+ };
+
+ actions = mkOption {
+ default = true;
+ type = types.nullOr types.bool;
+ description = ''
+ Applications may request an action to be associated with activating a
+ notification. Disabling this will cause mako to ignore these requests.
+ '';
+ };
+
+ format = mkOption {
+ default = "<b>%s</b>\\n%b";
+ type = types.nullOr types.str;
+ description = ''
+ Set notification format string to format. See FORMAT SPECIFIERS for
+ more information. To change this for grouped notifications, set it
+ within a grouped criteria.
+ '';
+ };
+
+ defaultTimeout = mkOption {
+ default = 0;
+ type = types.nullOr types.int;
+ description = ''
+ Set the default timeout to timeout in milliseconds. To disable the
+ timeout, set it to zero.
+ '';
+ };
+
+ ignoreTimeout = mkOption {
+ default = false;
+ type = types.nullOr types.bool;
+ description = ''
+ If set, mako will ignore the expire timeout sent by notifications
+ and use the one provided by default-timeout instead.
+ '';
+ };
+
+ groupBy = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = ''
+ A comma-separated list of criteria fields that will be compared to
+ other visible notifications to determine if this one should form a
+ group with them. All listed criteria must be exactly equal for two
+ notifications to group.
+ '';
+ };
+
+ };
+ };
+
+ config = let
+ boolToString = v: if v then "true" else "false";
+ optionalBoolean = name: val:
+ lib.optionalString (val != null) "${name}=${boolToString val}";
+ optionalInteger = name: val:
+ lib.optionalString (val != null) "${name}=${toString val}";
+ optionalString = name: val:
+ lib.optionalString (val != null) "${name}=${val}";
+ in mkIf cfg.enable {
+ home.packages = [ pkgs.mako ];
+ xdg.configFile."mako/config".text = ''
+ ${optionalInteger "max-visible" cfg.maxVisible}
+ ${optionalString "sort" cfg.sort}
+ ${optionalString "output" cfg.output}
+ ${optionalString "layer" cfg.layer}
+ ${optionalString "anchor" cfg.anchor}
+
+ ${optionalString "font" cfg.font}
+ ${optionalString "background-color" cfg.backgroundColor}
+ ${optionalString "text-color" cfg.textColor}
+ ${optionalInteger "width" cfg.width}
+ ${optionalInteger "height" cfg.height}
+ ${optionalString "margin" cfg.margin}
+ ${optionalString "padding" cfg.padding}
+ ${optionalInteger "border-size" cfg.borderSize}
+ ${optionalString "border-color" cfg.borderColor}
+ ${optionalInteger "border-radius" cfg.borderRadius}
+ ${optionalString "progress-color" cfg.progressColor}
+ ${optionalBoolean "icons" cfg.icons}
+ ${optionalInteger "max-icon-size" cfg.maxIconSize}
+ ${optionalString "icon-path" cfg.iconPath}
+ ${optionalBoolean "markup" cfg.markup}
+ ${optionalBoolean "actions" cfg.actions}
+ ${optionalString "format" cfg.format}
+ ${optionalInteger "default-timeout" cfg.defaultTimeout}
+ ${optionalBoolean "ignore-timeout" cfg.ignoreTimeout}
+ ${optionalString "group-by" cfg.groupBy}
+ '';
+ };
+}
diff --git a/home-manager/modules/services/mpd.nix b/home-manager/modules/services/mpd.nix
index 2aa1cd3a9fe..13b3ae78f26 100644
--- a/home-manager/modules/services/mpd.nix
+++ b/home-manager/modules/services/mpd.nix
@@ -42,7 +42,7 @@ in {
};
musicDirectory = mkOption {
- type = types.path;
+ type = with types; either path str;
default = "${config.home.homeDirectory}/music";
defaultText = "$HOME/music";
apply = toString; # Prevent copies to Nix store.
diff --git a/home-manager/modules/services/picom.nix b/home-manager/modules/services/picom.nix
new file mode 100644
index 00000000000..4c4da8de697
--- /dev/null
+++ b/home-manager/modules/services/picom.nix
@@ -0,0 +1,311 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+with builtins;
+
+let
+
+ cfg = config.services.picom;
+
+ configFile = pkgs.writeText "picom.conf" (optionalString cfg.fade ''
+ # fading
+ fading = true;
+ fade-delta = ${toString cfg.fadeDelta};
+ fade-in-step = ${elemAt cfg.fadeSteps 0};
+ fade-out-step = ${elemAt cfg.fadeSteps 1};
+ fade-exclude = ${toJSON cfg.fadeExclude};
+ '' + optionalString cfg.shadow ''
+
+ # shadows
+ shadow = true;
+ shadow-offset-x = ${toString (elemAt cfg.shadowOffsets 0)};
+ shadow-offset-y = ${toString (elemAt cfg.shadowOffsets 1)};
+ shadow-opacity = ${cfg.shadowOpacity};
+ shadow-exclude = ${toJSON cfg.shadowExclude};
+ '' + optionalString cfg.blur ''
+
+ # blur
+ blur-background = true;
+ blur-background-exclude = ${toJSON cfg.blurExclude};
+ '' + ''
+
+ # opacity
+ active-opacity = ${cfg.activeOpacity};
+ inactive-opacity = ${cfg.inactiveOpacity};
+ inactive-dim = ${cfg.inactiveDim};
+ opacity-rule = ${toJSON cfg.opacityRule};
+
+ wintypes:
+ {
+ dock = { shadow = ${toJSON (!cfg.noDockShadow)}; };
+ dnd = { shadow = ${toJSON (!cfg.noDNDShadow)}; };
+ popup_menu = { opacity = ${cfg.menuOpacity}; };
+ dropdown_menu = { opacity = ${cfg.menuOpacity}; };
+ };
+
+ # other options
+ backend = ${toJSON cfg.backend};
+ vsync = ${toJSON cfg.vSync};
+ refresh-rate = ${toString cfg.refreshRate};
+ '' + cfg.extraOptions);
+
+in {
+
+ options.services.picom = {
+ enable = mkEnableOption "Picom X11 compositor";
+
+ blur = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Enable background blur on transparent windows.
+ '';
+ };
+
+ blurExclude = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "class_g = 'slop'" "class_i = 'polybar'" ];
+ description = ''
+ List of windows to exclude background blur.
+ See the
+ <citerefentry>
+ <refentrytitle>picom</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ man page for more examples.
+ '';
+ };
+
+ experimentalBackends = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to use the new experimental backends.
+ '';
+ };
+
+ fade = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Fade windows in and out.
+ '';
+ };
+
+ fadeDelta = mkOption {
+ type = types.int;
+ default = 10;
+ example = 5;
+ description = ''
+ Time between fade animation step (in ms).
+ '';
+ };
+
+ fadeSteps = mkOption {
+ type = types.listOf types.str;
+ default = [ "0.028" "0.03" ];
+ example = [ "0.04" "0.04" ];
+ description = ''
+ Opacity change between fade steps (in and out).
+ '';
+ };
+
+ fadeExclude = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ];
+ description = ''
+ List of conditions of windows that should not be faded.
+ See the
+ <citerefentry>
+ <refentrytitle>picom</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ man page for more examples.
+ '';
+ };
+
+ shadow = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Draw window shadows.
+ '';
+ };
+
+ shadowOffsets = mkOption {
+ type = types.listOf types.int;
+ default = [ (-15) (-15) ];
+ example = [ (-10) (-15) ];
+ description = ''
+ Horizontal and vertical offsets for shadows (in pixels).
+ '';
+ };
+
+ shadowOpacity = mkOption {
+ type = types.str;
+ default = "0.75";
+ example = "0.8";
+ description = ''
+ Window shadows opacity (number in range 0 - 1).
+ '';
+ };
+
+ shadowExclude = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "window_type *= 'menu'" "name ~= 'Firefox$'" "focused = 1" ];
+ description = ''
+ List of conditions of windows that should have no shadow.
+ See the
+ <citerefentry>
+ <refentrytitle>picom</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ man page for more examples.
+ '';
+ };
+
+ noDockShadow = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Avoid shadow on docks.
+ '';
+ };
+
+ noDNDShadow = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Avoid shadow on drag-and-drop windows.
+ '';
+ };
+
+ activeOpacity = mkOption {
+ type = types.str;
+ default = "1.0";
+ example = "0.8";
+ description = ''
+ Opacity of active windows.
+ '';
+ };
+
+ inactiveDim = mkOption {
+ type = types.str;
+ default = "0.0";
+ example = "0.2";
+ description = ''
+ Dim inactive windows.
+ '';
+ };
+
+ inactiveOpacity = mkOption {
+ type = types.str;
+ default = "1.0";
+ example = "0.8";
+ description = ''
+ Opacity of inactive windows.
+ '';
+ };
+
+ menuOpacity = mkOption {
+ type = types.str;
+ default = "1.0";
+ example = "0.8";
+ description = ''
+ Opacity of dropdown and popup menu.
+ '';
+ };
+
+ opacityRule = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "87:class_i ?= 'scratchpad'" "91:class_i ?= 'xterm'" ];
+ description = ''
+ List of opacity rules.
+ See the
+ <citerefentry>
+ <refentrytitle>picom</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ man page for more examples.
+ '';
+ };
+
+ backend = mkOption {
+ type = types.str;
+ default = "glx";
+ description = ''
+ Backend to use: <literal>glx</literal> or <literal>xrender</literal>.
+ '';
+ };
+
+ vSync = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Enable vertical synchronization.
+ '';
+ };
+
+ refreshRate = mkOption {
+ type = types.int;
+ default = 0;
+ example = 60;
+ description = ''
+ Screen refresh rate (0 = automatically detect).
+ '';
+ };
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.picom;
+ defaultText = literalExample "pkgs.picom";
+ example = literalExample "pkgs.picom";
+ description = ''
+ picom derivation to use.
+ '';
+ };
+
+ extraOptions = mkOption {
+ type = types.str;
+ default = "";
+ example = ''
+ unredir-if-possible = true;
+ dbe = true;
+ '';
+ description = ''
+ Additional Picom configuration.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+
+ systemd.user.services.picom = {
+ Unit = {
+ Description = "Picom X11 compositor";
+ After = [ "graphical-session-pre.target" ];
+ PartOf = [ "graphical-session.target" ];
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+
+ Service = let
+ experimentalBackendsFlag =
+ if cfg.experimentalBackends then " --experimental-backends" else "";
+ in {
+ ExecStart = "${cfg.package}/bin/picom --config ${configFile}"
+ + experimentalBackendsFlag;
+ Restart = "always";
+ RestartSec = 3;
+ } // optionalAttrs (cfg.backend == "glx") {
+ # Temporarily fixes corrupt colours with Mesa 18.
+ Environment = [ "allow_rgb10_configs=false" ];
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/services/pulseeffects.nix b/home-manager/modules/services/pulseeffects.nix
new file mode 100644
index 00000000000..445b1c0a1f8
--- /dev/null
+++ b/home-manager/modules/services/pulseeffects.nix
@@ -0,0 +1,55 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.pulseeffects;
+
+ presetOpts = optionalString (cfg.preset != "") "--load-preset ${cfg.preset}";
+
+in {
+ meta.maintainers = [ maintainers.jonringer ];
+
+ options.services.pulseeffects = {
+ enable = mkEnableOption "Pulseeffects daemon";
+
+ preset = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Which preset to use when starting pulseeffects.
+ Will likely need to launch pulseeffects to initially create preset.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ # running pulseeffects will just attach itself to gapplication service
+ # at-spi2-core is to minimize journalctl noise of:
+ # "AT-SPI: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files"
+ home.packages = [ pkgs.pulseeffects pkgs.at-spi2-core ];
+
+ # Will need to add `services.dbus.packages = with pkgs; [ gnome3.dconf ];`
+ # to /etc/nixos/configuration.nix for daemon to work correctly
+
+ systemd.user.services.pulseeffects = {
+ Unit = {
+ Description = "Pulseeffects daemon";
+ Requires = [ "dbus.service" ];
+ After = [ "graphical-session-pre.target" ];
+ PartOf = [ "graphical-session.target" "pulseaudio.service" ];
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+
+ Service = {
+ ExecStart =
+ "${pkgs.pulseeffects}/bin/pulseeffects --gapplication-service ${presetOpts}";
+ ExecStop = "${pkgs.pulseeffects}/bin/pulseeffects --quit";
+ Restart = "on-failure";
+ RestartSec = 5;
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/services/screen-locker.nix b/home-manager/modules/services/screen-locker.nix
index 30591a7d1a5..554d64f9abe 100644
--- a/home-manager/modules/services/screen-locker.nix
+++ b/home-manager/modules/services/screen-locker.nix
@@ -17,6 +17,14 @@ in {
example = "\${pkgs.i3lock}/bin/i3lock -n -c 000000";
};
+ enableDetectSleep = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to reset timers when awaking from sleep.
+ '';
+ };
+
inactiveInterval = mkOption {
type = types.int;
default = 10;
@@ -42,7 +50,6 @@ in {
Extra command-line arguments to pass to <command>xss-lock</command>.
'';
};
-
};
config = mkIf cfg.enable {
@@ -58,10 +65,10 @@ in {
Service = {
ExecStart = concatStringsSep " " ([
"${pkgs.xautolock}/bin/xautolock"
- "-detectsleep"
"-time ${toString cfg.inactiveInterval}"
"-locker '${pkgs.systemd}/bin/loginctl lock-session $XDG_SESSION_ID'"
- ] ++ cfg.xautolockExtraOptions);
+ ] ++ optional cfg.enableDetectSleep "-detectsleep"
+ ++ cfg.xautolockExtraOptions);
};
};
diff --git a/home-manager/modules/services/spotifyd.nix b/home-manager/modules/services/spotifyd.nix
index bc231814eba..dfe0ecd318e 100644
--- a/home-manager/modules/services/spotifyd.nix
+++ b/home-manager/modules/services/spotifyd.nix
@@ -14,6 +14,18 @@ in {
options.services.spotifyd = {
enable = mkEnableOption "SpotifyD connect";
+ package = mkOption {
+ type = types.package;
+ default = pkgs.spotifyd;
+ defaultText = literalExample "pkgs.spotifyd";
+ example =
+ literalExample "(pkgs.spotifyd.override { withKeyring = true; })";
+ description = ''
+ The <literal>spotifyd</literal> package to use.
+ Can be used to specify extensions.
+ '';
+ };
+
settings = mkOption {
type = types.attrsOf (types.attrsOf types.str);
default = { };
@@ -21,7 +33,7 @@ in {
example = literalExample ''
{
global = {
- user = "Alex";
+ username = "Alex";
password = "foo";
device_name = "nix";
};
@@ -31,7 +43,7 @@ in {
};
config = mkIf cfg.enable {
- home.packages = [ pkgs.spotifyd ];
+ home.packages = [ cfg.package ];
systemd.user.services.spotifyd = {
Unit = {
@@ -43,7 +55,7 @@ in {
Service = {
ExecStart =
- "${pkgs.spotifyd}/bin/spotifyd --no-daemon --config-path ${configFile}";
+ "${cfg.package}/bin/spotifyd --no-daemon --config-path ${configFile}";
Restart = "always";
RestartSec = 12;
};
diff --git a/home-manager/modules/services/status-notifier-watcher.nix b/home-manager/modules/services/status-notifier-watcher.nix
index 3c3e54877b4..ed0537e22e1 100644
--- a/home-manager/modules/services/status-notifier-watcher.nix
+++ b/home-manager/modules/services/status-notifier-watcher.nix
@@ -34,7 +34,14 @@ in {
Before = [ "taffybar.service" ];
};
- Service = { ExecStart = "${cfg.package}/bin/status-notifier-watcher"; };
+ Service = {
+ ExecStart = "${cfg.package}/bin/status-notifier-watcher";
+ # Delay the unit start a bit to allow the program to get fully
+ # set up before letting dependent services start. This is
+ # brittle and a better solution using, e.g., `BusName=` might
+ # be possible.
+ ExecStartPost = "${pkgs.coreutils}/bin/sleep 1";
+ };
Install = {
WantedBy = [ "graphical-session.target" "taffybar.service" ];
diff --git a/home-manager/modules/services/syncthing.nix b/home-manager/modules/services/syncthing.nix
index 2ef10540164..4622ac2e941 100644
--- a/home-manager/modules/services/syncthing.nix
+++ b/home-manager/modules/services/syncthing.nix
@@ -19,6 +19,8 @@ with lib;
config = mkMerge [
(mkIf config.services.syncthing.enable {
+ home.packages = [ (getOutput "man" pkgs.syncthing) ];
+
systemd.user.services = {
syncthing = {
Unit = {
diff --git a/home-manager/modules/services/udiskie.nix b/home-manager/modules/services/udiskie.nix
index 2444d68ff93..ca31021cb5c 100644
--- a/home-manager/modules/services/udiskie.nix
+++ b/home-manager/modules/services/udiskie.nix
@@ -81,9 +81,7 @@ in {
PartOf = [ "graphical-session.target" ];
};
- Service = {
- ExecStart = "${pkgs.udiskie}/bin/udiskie -2 ${commandArgs}";
- };
+ Service = { ExecStart = "${pkgs.udiskie}/bin/udiskie ${commandArgs}"; };
Install = { WantedBy = [ "graphical-session.target" ]; };
};
diff --git a/home-manager/modules/services/unison.nix b/home-manager/modules/services/unison.nix
index 93c59e8fd62..a9cf23fb66e 100644
--- a/home-manager/modules/services/unison.nix
+++ b/home-manager/modules/services/unison.nix
@@ -60,7 +60,7 @@ let
};
};
- serialiseArg = key: val: "-${key}=${escapeShellArg val}";
+ serialiseArg = key: val: escapeShellArg "-${key}=${escape [ "=" ] val}";
serialiseArgs = args: concatStringsSep " " (mapAttrsToList serialiseArg args);
diff --git a/home-manager/modules/services/window-managers/i3-sway/i3.nix b/home-manager/modules/services/window-managers/i3-sway/i3.nix
new file mode 100644
index 00000000000..f7124e6fd23
--- /dev/null
+++ b/home-manager/modules/services/window-managers/i3-sway/i3.nix
@@ -0,0 +1,257 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.xsession.windowManager.i3;
+
+ commonOptions = import ./lib/options.nix {
+ inherit config lib cfg pkgs;
+ moduleName = "i3";
+ isGaps = cfg.package == pkgs.i3-gaps;
+ };
+
+ configModule = types.submodule {
+ options = {
+ inherit (commonOptions)
+ fonts window floating focus assigns modifier workspaceLayout
+ workspaceAutoBackAndForth keycodebindings colors bars startup gaps menu
+ terminal;
+
+ keybindings = mkOption {
+ type = types.attrsOf (types.nullOr types.str);
+ default = mapAttrs (n: mkOptionDefault) {
+ "${cfg.config.modifier}+Return" = "exec ${cfg.config.terminal}";
+ "${cfg.config.modifier}+Shift+q" = "kill";
+ "${cfg.config.modifier}+d" = "exec ${cfg.config.menu}";
+
+ "${cfg.config.modifier}+Left" = "focus left";
+ "${cfg.config.modifier}+Down" = "focus down";
+ "${cfg.config.modifier}+Up" = "focus up";
+ "${cfg.config.modifier}+Right" = "focus right";
+
+ "${cfg.config.modifier}+Shift+Left" = "move left";
+ "${cfg.config.modifier}+Shift+Down" = "move down";
+ "${cfg.config.modifier}+Shift+Up" = "move up";
+ "${cfg.config.modifier}+Shift+Right" = "move right";
+
+ "${cfg.config.modifier}+h" = "split h";
+ "${cfg.config.modifier}+v" = "split v";
+ "${cfg.config.modifier}+f" = "fullscreen toggle";
+
+ "${cfg.config.modifier}+s" = "layout stacking";
+ "${cfg.config.modifier}+w" = "layout tabbed";
+ "${cfg.config.modifier}+e" = "layout toggle split";
+
+ "${cfg.config.modifier}+Shift+space" = "floating toggle";
+ "${cfg.config.modifier}+space" = "focus mode_toggle";
+
+ "${cfg.config.modifier}+a" = "focus parent";
+
+ "${cfg.config.modifier}+Shift+minus" = "move scratchpad";
+ "${cfg.config.modifier}+minus" = "scratchpad show";
+
+ "${cfg.config.modifier}+1" = "workspace number 1";
+ "${cfg.config.modifier}+2" = "workspace number 2";
+ "${cfg.config.modifier}+3" = "workspace number 3";
+ "${cfg.config.modifier}+4" = "workspace number 4";
+ "${cfg.config.modifier}+5" = "workspace number 5";
+ "${cfg.config.modifier}+6" = "workspace number 6";
+ "${cfg.config.modifier}+7" = "workspace number 7";
+ "${cfg.config.modifier}+8" = "workspace number 8";
+ "${cfg.config.modifier}+9" = "workspace number 9";
+ "${cfg.config.modifier}+0" = "workspace number 10";
+
+ "${cfg.config.modifier}+Shift+1" =
+ "move container to workspace number 1";
+ "${cfg.config.modifier}+Shift+2" =
+ "move container to workspace number 2";
+ "${cfg.config.modifier}+Shift+3" =
+ "move container to workspace number 3";
+ "${cfg.config.modifier}+Shift+4" =
+ "move container to workspace number 4";
+ "${cfg.config.modifier}+Shift+5" =
+ "move container to workspace number 5";
+ "${cfg.config.modifier}+Shift+6" =
+ "move container to workspace number 6";
+ "${cfg.config.modifier}+Shift+7" =
+ "move container to workspace number 7";
+ "${cfg.config.modifier}+Shift+8" =
+ "move container to workspace number 8";
+ "${cfg.config.modifier}+Shift+9" =
+ "move container to workspace number 9";
+ "${cfg.config.modifier}+Shift+0" =
+ "move container to workspace number 10";
+
+ "${cfg.config.modifier}+Shift+c" = "reload";
+ "${cfg.config.modifier}+Shift+r" = "restart";
+ "${cfg.config.modifier}+Shift+e" =
+ "exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
+
+ "${cfg.config.modifier}+r" = "mode resize";
+ };
+ defaultText = "Default i3 keybindings.";
+ description = ''
+ An attribute set that assigns a key press to an action using a key symbol.
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
+ </para><para>
+ Consider to use <code>lib.mkOptionDefault</code> function to extend or override
+ default keybindings instead of specifying all of them from scratch.
+ '';
+ example = literalExample ''
+ let
+ modifier = config.xsession.windowManager.i3.config.modifier;
+ in lib.mkOptionDefault {
+ "''${modifier}+Return" = "exec i3-sensible-terminal";
+ "''${modifier}+Shift+q" = "kill";
+ "''${modifier}+d" = "exec \${pkgs.dmenu}/bin/dmenu_run";
+ }
+ '';
+ };
+
+ modes = mkOption {
+ type = types.attrsOf (types.attrsOf types.str);
+ default = {
+ resize = {
+ "Left" = "resize shrink width 10 px or 10 ppt";
+ "Down" = "resize grow height 10 px or 10 ppt";
+ "Up" = "resize shrink height 10 px or 10 ppt";
+ "Right" = "resize grow width 10 px or 10 ppt";
+ "Escape" = "mode default";
+ "Return" = "mode default";
+ };
+ };
+ description = ''
+ An attribute set that defines binding modes and keybindings
+ inside them
+
+ Only basic keybinding is supported (bindsym keycomb action),
+ for more advanced setup use 'i3.extraConfig'.
+ '';
+ };
+ };
+ };
+
+ commonFunctions = import ./lib/functions.nix {
+ inherit cfg lib;
+ moduleName = "i3";
+ };
+
+ inherit (commonFunctions)
+ keybindingsStr keycodebindingsStr modeStr assignStr barStr gapsStr
+ floatingCriteriaStr windowCommandsStr colorSetStr;
+
+ startupEntryStr = { command, always, notification, workspace, ... }: ''
+ ${if always then "exec_always" else "exec"} ${
+ if (notification && workspace == null) then "" else "--no-startup-id"
+ } ${
+ if (workspace == null) then
+ command
+ else
+ "i3-msg 'workspace ${workspace}; exec ${command}'"
+ }
+ '';
+
+ configFile = pkgs.writeText "i3.conf" ((if cfg.config != null then
+ with cfg.config; ''
+ font pango:${concatStringsSep ", " fonts}
+ floating_modifier ${floating.modifier}
+ new_window ${if window.titlebar then "normal" else "pixel"} ${
+ toString window.border
+ }
+ new_float ${if floating.titlebar then "normal" else "pixel"} ${
+ toString floating.border
+ }
+ hide_edge_borders ${window.hideEdgeBorders}
+ force_focus_wrapping ${if focus.forceWrapping then "yes" else "no"}
+ focus_follows_mouse ${if focus.followMouse then "yes" else "no"}
+ focus_on_window_activation ${focus.newWindow}
+ mouse_warping ${if focus.mouseWarping then "output" else "none"}
+ workspace_layout ${workspaceLayout}
+ workspace_auto_back_and_forth ${
+ if workspaceAutoBackAndForth then "yes" else "no"
+ }
+
+ client.focused ${colorSetStr colors.focused}
+ client.focused_inactive ${colorSetStr colors.focusedInactive}
+ client.unfocused ${colorSetStr colors.unfocused}
+ client.urgent ${colorSetStr colors.urgent}
+ client.placeholder ${colorSetStr colors.placeholder}
+ client.background ${colors.background}
+
+ ${keybindingsStr { inherit keybindings; }}
+ ${keycodebindingsStr keycodebindings}
+ ${concatStringsSep "\n" (mapAttrsToList modeStr modes)}
+ ${concatStringsSep "\n" (mapAttrsToList assignStr assigns)}
+ ${concatStringsSep "\n" (map barStr bars)}
+ ${optionalString (gaps != null) gapsStr}
+ ${concatStringsSep "\n" (map floatingCriteriaStr floating.criteria)}
+ ${concatStringsSep "\n" (map windowCommandsStr window.commands)}
+ ${concatStringsSep "\n" (map startupEntryStr startup)}
+ ''
+ else
+ "") + "\n" + cfg.extraConfig);
+
+in {
+ options = {
+ xsession.windowManager.i3 = {
+ enable = mkEnableOption "i3 window manager.";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.i3;
+ defaultText = literalExample "pkgs.i3";
+ example = literalExample "pkgs.i3-gaps";
+ description = ''
+ i3 package to use.
+ If 'i3.config.gaps' settings are specified, 'pkgs.i3-gaps' will be set as a default package.
+ '';
+ };
+
+ config = mkOption {
+ type = types.nullOr configModule;
+ default = { };
+ description = "i3 configuration options.";
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description =
+ "Extra configuration lines to add to ~/.config/i3/config.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ home.packages = [ cfg.package ];
+ xsession.windowManager.command = "${cfg.package}/bin/i3";
+ xdg.configFile."i3/config" = {
+ source = configFile;
+ onChange = ''
+ i3Socket=''${XDG_RUNTIME_DIR:-/run/user/$UID}/i3/ipc-socket.*
+ if [ -S $i3Socket ]; then
+ echo "Reloading i3"
+ $DRY_RUN_CMD ${cfg.package}/bin/i3-msg -s $i3Socket reload 1>/dev/null
+ fi
+ '';
+ };
+ }
+
+ (mkIf (cfg.config != null) {
+ xsession.windowManager.i3.package =
+ mkDefault (if (cfg.config.gaps != null) then pkgs.i3-gaps else pkgs.i3);
+ })
+
+ (mkIf (cfg.config != null
+ && (any (s: s.workspace != null) cfg.config.startup)) {
+ warnings = [
+ ("'xsession.windowManager.i3.config.startup.*.workspace' is deprecated, "
+ + "use 'xsession.windowManager.i3.config.assigns' instead."
+ + "See https://github.com/rycee/home-manager/issues/265.")
+ ];
+ })
+ ]);
+}
diff --git a/home-manager/modules/services/window-managers/i3-sway/lib/functions.nix b/home-manager/modules/services/window-managers/i3-sway/lib/functions.nix
new file mode 100644
index 00000000000..9391e6e92fc
--- /dev/null
+++ b/home-manager/modules/services/window-managers/i3-sway/lib/functions.nix
@@ -0,0 +1,127 @@
+{ cfg, lib, moduleName }:
+
+with lib;
+
+rec {
+ criteriaStr = criteria:
+ "[${
+ concatStringsSep " " (mapAttrsToList (k: v: ''${k}="${v}"'') criteria)
+ }]";
+
+ keybindingsStr = { keybindings, bindsymArgs ? "" }:
+ concatStringsSep "\n" (mapAttrsToList (keycomb: action:
+ optionalString (action != null) "bindsym ${
+ lib.optionalString (bindsymArgs != "") "${bindsymArgs} "
+ }${keycomb} ${action}") keybindings);
+
+ keycodebindingsStr = keycodebindings:
+ concatStringsSep "\n" (mapAttrsToList (keycomb: action:
+ optionalString (action != null) "bindcode ${keycomb} ${action}")
+ keycodebindings);
+
+ colorSetStr = c:
+ concatStringsSep " " [
+ c.border
+ c.background
+ c.text
+ c.indicator
+ c.childBorder
+ ];
+ barColorSetStr = c: concatStringsSep " " [ c.border c.background c.text ];
+
+ modeStr = name: keybindings: ''
+ mode "${name}" {
+ ${keybindingsStr { inherit keybindings; }}
+ }
+ '';
+
+ assignStr = workspace: criteria:
+ concatStringsSep "\n"
+ (map (c: "assign ${criteriaStr c} ${workspace}") criteria);
+
+ barStr = { id, fonts, mode, hiddenState, position, workspaceButtons
+ , workspaceNumbers, command, statusCommand, colors, trayOutput, extraConfig
+ , ... }:
+ let colorsNotNull = lib.filterAttrs (n: v: v != null) colors != { };
+ in ''
+ bar {
+ ${optionalString (id != null) "id ${id}"}
+ ${
+ optionalString (fonts != [ ])
+ "font pango:${concatStringsSep ", " fonts}"
+ }
+ ${optionalString (mode != null) "mode ${mode}"}
+ ${optionalString (hiddenState != null) "hidden_state ${hiddenState}"}
+ ${optionalString (position != null) "position ${position}"}
+ ${
+ optionalString (statusCommand != null)
+ "status_command ${statusCommand}"
+ }
+ ${moduleName}bar_command ${command}
+ ${
+ optionalString (workspaceButtons != null)
+ "workspace_buttons ${if workspaceButtons then "yes" else "no"}"
+ }
+ ${
+ optionalString (workspaceNumbers != null)
+ "strip_workspace_numbers ${if !workspaceNumbers then "yes" else "no"}"
+ }
+ ${optionalString (trayOutput != null) "tray_output ${trayOutput}"}
+ ${optionalString colorsNotNull "colors {"}
+ ${
+ optionalString (colors.background != null)
+ "background ${colors.background}"
+ }
+ ${
+ optionalString (colors.statusline != null)
+ "statusline ${colors.statusline}"
+ }
+ ${
+ optionalString (colors.separator != null)
+ "separator ${colors.separator}"
+ }
+ ${
+ optionalString (colors.focusedWorkspace != null)
+ "focused_workspace ${barColorSetStr colors.focusedWorkspace}"
+ }
+ ${
+ optionalString (colors.activeWorkspace != null)
+ "active_workspace ${barColorSetStr colors.activeWorkspace}"
+ }
+ ${
+ optionalString (colors.inactiveWorkspace != null)
+ "inactive_workspace ${barColorSetStr colors.inactiveWorkspace}"
+ }
+ ${
+ optionalString (colors.urgentWorkspace != null)
+ "urgent_workspace ${barColorSetStr colors.urgentWorkspace}"
+ }
+ ${
+ optionalString (colors.bindingMode != null)
+ "binding_mode ${barColorSetStr colors.bindingMode}"
+ }
+ ${optionalString colorsNotNull "}"}
+ ${extraConfig}
+ }
+ '';
+
+ gapsStr = with cfg.config.gaps; ''
+ ${optionalString (inner != null) "gaps inner ${toString inner}"}
+ ${optionalString (outer != null) "gaps outer ${toString outer}"}
+ ${optionalString (horizontal != null)
+ "gaps horizontal ${toString horizontal}"}
+ ${optionalString (vertical != null) "gaps vertical ${toString vertical}"}
+ ${optionalString (top != null) "gaps top ${toString top}"}
+ ${optionalString (bottom != null) "gaps bottom ${toString bottom}"}
+ ${optionalString (left != null) "gaps left ${toString left}"}
+ ${optionalString (right != null) "gaps right ${toString right}"}
+
+ ${optionalString smartGaps "smart_gaps on"}
+ ${optionalString (smartBorders != "off") "smart_borders ${smartBorders}"}
+ '';
+
+ floatingCriteriaStr = criteria:
+ "for_window ${criteriaStr criteria} floating enable";
+ windowCommandsStr = { command, criteria, ... }:
+ "for_window ${criteriaStr criteria} ${command}";
+}
diff --git a/home-manager/modules/services/window-managers/i3-sway/lib/options.nix b/home-manager/modules/services/window-managers/i3-sway/lib/options.nix
new file mode 100644
index 00000000000..edfdcd4feae
--- /dev/null
+++ b/home-manager/modules/services/window-managers/i3-sway/lib/options.nix
@@ -0,0 +1,763 @@
+{ config, lib, moduleName, cfg, pkgs, capitalModuleName ? moduleName
+, isGaps ? true }:
+
+with lib;
+
+let
+ fonts = mkOption {
+ type = types.listOf types.str;
+ default = [ "monospace 8" ];
+ description = ''
+ Font list used for window titles. Only FreeType fonts are supported.
+ The order here is important (e.g. icons font should go before the one used for text).
+ '';
+ example = [ "FontAwesome 10" "Terminus 10" ];
+ };
+
+ startupModule = types.submodule {
+ options = {
+ command = mkOption {
+ type = types.str;
+ description = "Command that will be executed on startup.";
+ };
+
+ always = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to run command on each ${moduleName} restart.";
+ };
+ } // optionalAttrs (moduleName == "i3") {
+ notification = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to enable startup-notification support for the command.
+ See <option>--no-startup-id</option> option description in the i3 user guide.
+ '';
+ };
+
+ workspace = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Launch application on a particular workspace. DEPRECATED:
+ Use <varname><link linkend="opt-xsession.windowManager.i3.config.assigns">xsession.windowManager.i3.config.assigns</link></varname>
+ instead. See <link xlink:href="https://github.com/rycee/home-manager/issues/265"/>.
+ '';
+ };
+ };
+
+ };
+
+ barModule = types.submodule {
+ options = let
+ versionAtLeast2009 = versionAtLeast config.home.stateVersion "20.09";
+ mkNullableOption = { type, default, ... }@args:
+ mkOption (args // optionalAttrs versionAtLeast2009 {
+ type = types.nullOr type;
+ default = null;
+ example = default;
+ } // {
+ defaultText = literalExample ''
+ ${
+ if isString default then default else "See code"
+ } for state version < 20.09,
+ null for state version ≥ 20.09
+ '';
+ });
+ in {
+ fonts = fonts // optionalAttrs versionAtLeast2009 { default = [ ]; };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Extra configuration lines for this bar.";
+ };
+
+ id = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Specifies the bar ID for the configured bar instance.
+ If this option is missing, the ID is set to bar-x, where x corresponds
+ to the position of the embedding bar block in the config file.
+ '';
+ };
+
+ mode = mkNullableOption {
+ type = types.enum [ "dock" "hide" "invisible" ];
+ default = "dock";
+ description = "Bar visibility mode.";
+ };
+
+ hiddenState = mkNullableOption {
+ type = types.enum [ "hide" "show" ];
+ default = "hide";
+ description = "The default bar mode when 'bar.mode' == 'hide'.";
+ };
+
+ position = mkNullableOption {
+ type = types.enum [ "top" "bottom" ];
+ default = "bottom";
+ description = "The edge of the screen ${moduleName}bar should show up.";
+ };
+
+ workspaceButtons = mkNullableOption {
+ type = types.bool;
+ default = true;
+ description = "Whether workspace buttons should be shown or not.";
+ };
+
+ workspaceNumbers = mkNullableOption {
+ type = types.bool;
+ default = true;
+ description =
+ "Whether workspace numbers should be displayed within the workspace buttons.";
+ };
+
+ command = mkOption {
+ type = types.str;
+ default = "${cfg.package}/bin/${moduleName}bar";
+ defaultText = "i3bar";
+ description = "Command that will be used to start a bar.";
+ example = if moduleName == "i3" then
+ "\${pkgs.i3-gaps}/bin/i3bar -t"
+ else
+ "\${pkgs.waybar}/bin/waybar";
+ };
+
+ statusCommand = mkOption {
+ type = types.nullOr types.str;
+ default =
+ if versionAtLeast2009 then null else "${pkgs.i3status}/bin/i3status";
+ example = "i3status";
+ description = "Command that will be used to get status lines.";
+ };
+
+ colors = mkOption {
+ type = types.submodule {
+ options = {
+ background = mkNullableOption {
+ type = types.str;
+ default = "#000000";
+ description = "Background color of the bar.";
+ };
+
+ statusline = mkNullableOption {
+ type = types.str;
+ default = "#ffffff";
+ description = "Text color to be used for the statusline.";
+ };
+
+ separator = mkNullableOption {
+ type = types.str;
+ default = "#666666";
+ description = "Text color to be used for the separator.";
+ };
+
+ focusedWorkspace = mkNullableOption {
+ type = barColorSetModule;
+ default = {
+ border = "#4c7899";
+ background = "#285577";
+ text = "#ffffff";
+ };
+ description = ''
+ Border, background and text color for a workspace button when the workspace has focus.
+ '';
+ };
+
+ activeWorkspace = mkNullableOption {
+ type = barColorSetModule;
+ default = {
+ border = "#333333";
+ background = "#5f676a";
+ text = "#ffffff";
+ };
+ description = ''
+ Border, background and text color for a workspace button when the workspace is active.
+ '';
+ };
+
+ inactiveWorkspace = mkNullableOption {
+ type = barColorSetModule;
+ default = {
+ border = "#333333";
+ background = "#222222";
+ text = "#888888";
+ };
+ description = ''
+ Border, background and text color for a workspace button when the workspace does not
+ have focus and is not active.
+ '';
+ };
+
+ urgentWorkspace = mkNullableOption {
+ type = barColorSetModule;
+ default = {
+ border = "#2f343a";
+ background = "#900000";
+ text = "#ffffff";
+ };
+ description = ''
+ Border, background and text color for a workspace button when the workspace contains
+ a window with the urgency hint set.
+ '';
+ };
+
+ bindingMode = mkNullableOption {
+ type = barColorSetModule;
+ default = {
+ border = "#2f343a";
+ background = "#900000";
+ text = "#ffffff";
+ };
+ description =
+ "Border, background and text color for the binding mode indicator";
+ };
+ };
+ };
+ default = { };
+ description = ''
+ Bar color settings. All color classes can be specified using submodules
+ with 'border', 'background', 'text', fields and RGB color hex-codes as values.
+ See default values for the reference.
+ Note that 'background', 'status', and 'separator' parameters take a single RGB value.
+
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#_colors"/>.
+ '';
+ };
+
+ trayOutput = mkNullableOption {
+ type = types.str;
+ default = "primary";
+ description = "Where to output tray.";
+ };
+ };
+ };
+
+ barColorSetModule = types.submodule {
+ options = {
+ border = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ background = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ text = mkOption {
+ type = types.str;
+ visible = false;
+ };
+ };
+ };
+
+ colorSetModule = types.submodule {
+ options = {
+ border = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ childBorder = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ background = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ text = mkOption {
+ type = types.str;
+ visible = false;
+ };
+
+ indicator = mkOption {
+ type = types.str;
+ visible = false;
+ };
+ };
+ };
+
+ windowCommandModule = types.submodule {
+ options = {
+ command = mkOption {
+ type = types.str;
+ description = "${capitalModuleName}wm command to execute.";
+ example = "border pixel 1";
+ };
+
+ criteria = mkOption {
+ type = criteriaModule;
+ description =
+ "Criteria of the windows on which command should be executed.";
+ example = { title = "x200: ~/work"; };
+ };
+ };
+ };
+
+ criteriaModule = types.attrsOf types.str;
+in {
+ inherit fonts;
+
+ window = mkOption {
+ type = types.submodule {
+ options = {
+ titlebar = mkOption {
+ type = types.bool;
+ default = !isGaps;
+ defaultText = if moduleName == "i3" then
+ "xsession.windowManager.i3.package != nixpkgs.i3-gaps (titlebar should be disabled for i3-gaps)"
+ else
+ "false";
+ description = "Whether to show window titlebars.";
+ };
+
+ border = mkOption {
+ type = types.int;
+ default = 2;
+ description = "Window border width.";
+ };
+
+ hideEdgeBorders = mkOption {
+ type = types.enum [ "none" "vertical" "horizontal" "both" "smart" ];
+ default = "none";
+ description = "Hide window borders adjacent to the screen edges.";
+ };
+
+ commands = mkOption {
+ type = types.listOf windowCommandModule;
+ default = [ ];
+ description = ''
+ List of commands that should be executed on specific windows.
+ See <option>for_window</option> ${moduleName}wm option documentation.
+ '';
+ example = [{
+ command = "border pixel 1";
+ criteria = { class = "XTerm"; };
+ }];
+ };
+ };
+ };
+ default = { };
+ description = "Window titlebar and border settings.";
+ };
+
+ floating = mkOption {
+ type = types.submodule {
+ options = {
+ titlebar = mkOption {
+ type = types.bool;
+ default = !isGaps;
+ defaultText = if moduleName == "i3" then
+ "xsession.windowManager.i3.package != nixpkgs.i3-gaps (titlebar should be disabled for i3-gaps)"
+ else
+ "false";
+ description = "Whether to show floating window titlebars.";
+ };
+
+ border = mkOption {
+ type = types.int;
+ default = 2;
+ description = "Floating windows border width.";
+ };
+
+ modifier = mkOption {
+ type =
+ types.enum [ "Shift" "Control" "Mod1" "Mod2" "Mod3" "Mod4" "Mod5" ];
+ default = cfg.config.modifier;
+ defaultText = "${moduleName}.config.modifier";
+ description =
+ "Modifier key that can be used to drag floating windows.";
+ example = "Mod4";
+ };
+
+ criteria = mkOption {
+ type = types.listOf criteriaModule;
+ default = [ ];
+ description =
+ "List of criteria for windows that should be opened in a floating mode.";
+ example = [
+ { "title" = "Steam - Update News"; }
+ { "class" = "Pavucontrol"; }
+ ];
+ };
+ };
+ };
+ default = { };
+ description = "Floating window settings.";
+ };
+
+ focus = mkOption {
+ type = types.submodule {
+ options = {
+ newWindow = mkOption {
+ type = types.enum [ "smart" "urgent" "focus" "none" ];
+ default = "smart";
+ description = ''
+ This option modifies focus behavior on new window activation.
+
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#focus_on_window_activation"/>
+ '';
+ example = "none";
+ };
+
+ followMouse = mkOption {
+ type = if moduleName == "sway" then
+ types.either (types.enum [ "yes" "no" "always" ]) types.bool
+ else
+ types.bool;
+ default = if moduleName == "sway" then "yes" else true;
+ description = "Whether focus should follow the mouse.";
+ apply = val:
+ if (moduleName == "sway" && isBool val) then
+ (if val then "yes" else "no")
+ else
+ val;
+ };
+
+ forceWrapping = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to force focus wrapping in tabbed or stacked container.
+
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#_focus_wrapping"/>
+ '';
+ };
+
+ mouseWarping = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether mouse cursor should be warped to the center of the window when switching focus
+ to a window on a different output.
+ '';
+ };
+ };
+ };
+ default = { };
+ description = "Focus related settings.";
+ };
+
+ assigns = mkOption {
+ type = types.attrsOf (types.listOf criteriaModule);
+ default = { };
+ description = ''
+ An attribute set that assigns applications to workspaces based
+ on criteria.
+ '';
+ example = literalExample ''
+ {
+ "1: web" = [{ class = "^Firefox$"; }];
+ "0: extra" = [{ class = "^Firefox$"; window_role = "About"; }];
+ }
+ '';
+ };
+
+ modifier = mkOption {
+ type = types.enum [ "Shift" "Control" "Mod1" "Mod2" "Mod3" "Mod4" "Mod5" ];
+ default = "Mod1";
+ description = "Modifier key that is used for all default keybindings.";
+ example = "Mod4";
+ };
+
+ workspaceLayout = mkOption {
+ type = types.enum [ "default" "stacked" "tabbed" ];
+ default = "default";
+ example = "tabbed";
+ description = ''
+ The mode in which new containers on workspace level will
+ start.
+ '';
+ };
+
+ workspaceAutoBackAndForth = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Assume you are on workspace "1: www" and switch to "2: IM" using
+ mod+2 because somebody sent you a message. You don’t need to remember
+ where you came from now, you can just press $mod+2 again to switch
+ back to "1: www".
+ '';
+ };
+
+ keycodebindings = mkOption {
+ type = types.attrsOf (types.nullOr types.str);
+ default = { };
+ description = ''
+ An attribute set that assigns keypress to an action using key code.
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
+ '';
+ example = { "214" = "exec /bin/script.sh"; };
+ };
+
+ colors = mkOption {
+ type = types.submodule {
+ options = {
+ background = mkOption {
+ type = types.str;
+ default = "#ffffff";
+ description = ''
+ Background color of the window. Only applications which do not cover
+ the whole area expose the color.
+ '';
+ };
+
+ focused = mkOption {
+ type = colorSetModule;
+ default = {
+ border = "#4c7899";
+ background = "#285577";
+ text = "#ffffff";
+ indicator = "#2e9ef4";
+ childBorder = "#285577";
+ };
+ description = "A window which currently has the focus.";
+ };
+
+ focusedInactive = mkOption {
+ type = colorSetModule;
+ default = {
+ border = "#333333";
+ background = "#5f676a";
+ text = "#ffffff";
+ indicator = "#484e50";
+ childBorder = "#5f676a";
+ };
+ description = ''
+ A window which is the focused one of its container,
+ but it does not have the focus at the moment.
+ '';
+ };
+
+ unfocused = mkOption {
+ type = colorSetModule;
+ default = {
+ border = "#333333";
+ background = "#222222";
+ text = "#888888";
+ indicator = "#292d2e";
+ childBorder = "#222222";
+ };
+ description = "A window which is not focused.";
+ };
+
+ urgent = mkOption {
+ type = colorSetModule;
+ default = {
+ border = "#2f343a";
+ background = "#900000";
+ text = "#ffffff";
+ indicator = "#900000";
+ childBorder = "#900000";
+ };
+ description = "A window which has its urgency hint activated.";
+ };
+
+ placeholder = mkOption {
+ type = colorSetModule;
+ default = {
+ border = "#000000";
+ background = "#0c0c0c";
+ text = "#ffffff";
+ indicator = "#000000";
+ childBorder = "#0c0c0c";
+ };
+ description = ''
+ Background and text color are used to draw placeholder window
+ contents (when restoring layouts). Border and indicator are ignored.
+ '';
+ };
+ };
+ };
+ default = { };
+ description = ''
+ Color settings. All color classes can be specified using submodules
+ with 'border', 'background', 'text', 'indicator' and 'childBorder' fields
+ and RGB color hex-codes as values. See default values for the reference.
+ Note that '${moduleName}.config.colors.background' parameter takes a single RGB value.
+
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#_changing_colors"/>.
+ '';
+ };
+
+ bars = mkOption {
+ type = types.listOf barModule;
+ default = if versionAtLeast config.home.stateVersion "20.09" then [{
+ mode = "dock";
+ hiddenState = "hide";
+ position = "bottom";
+ workspaceButtons = true;
+ workspaceNumbers = true;
+ statusCommand = "${pkgs.i3status}/bin/i3status";
+ fonts = [ "monospace 8" ];
+ trayOutput = "primary";
+ colors = {
+ background = "#000000";
+ statusline = "#ffffff";
+ separator = "#666666";
+ focusedWorkspace = {
+ border = "#4c7899";
+ background = "#285577";
+ text = "#ffffff";
+ };
+ activeWorkspace = {
+ border = "#333333";
+ background = "#5f676a";
+ text = "#ffffff";
+ };
+ inactiveWorkspace = {
+ border = "#333333";
+ background = "#222222";
+ text = "#888888";
+ };
+ urgentWorkspace = {
+ border = "#2f343a";
+ background = "#900000";
+ text = "#ffffff";
+ };
+ bindingMode = {
+ border = "#2f343a";
+ background = "#900000";
+ text = "#ffffff";
+ };
+ };
+ }] else
+ [ { } ];
+ description = ''
+ ${capitalModuleName} bars settings blocks. Set to empty list to remove bars completely.
+ '';
+ };
+
+ startup = mkOption {
+ type = types.listOf startupModule;
+ default = [ ];
+ description = ''
+ Commands that should be executed at startup.
+
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#_automatically_starting_applications_on_i3_startup"/>.
+ '';
+ example = literalExample ''
+ [
+ { command = "systemctl --user restart polybar"; always = true; notification = false; }
+ { command = "dropbox start"; notification = false; }
+ { command = "firefox"; workspace = "1: web"; }
+ ];
+ '';
+ };
+
+ gaps = mkOption {
+ type = types.nullOr (types.submodule {
+ options = {
+ inner = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Inner gaps value.";
+ example = 12;
+ };
+
+ outer = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Outer gaps value.";
+ example = 5;
+ };
+
+ horizontal = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Horizontal gaps value.";
+ example = 5;
+ };
+
+ vertical = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Vertical gaps value.";
+ example = 5;
+ };
+
+ top = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Top gaps value.";
+ example = 5;
+ };
+
+ left = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Left gaps value.";
+ example = 5;
+ };
+
+ bottom = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Bottom gaps value.";
+ example = 5;
+ };
+
+ right = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = "Right gaps value.";
+ example = 5;
+ };
+
+ smartGaps = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ This option controls whether to disable all gaps (outer and inner)
+ on workspace with a single container.
+ '';
+ example = true;
+ };
+
+ smartBorders = mkOption {
+ type = types.enum [ "on" "off" "no_gaps" ];
+ default = "off";
+ description = ''
+ This option controls whether to disable container borders on
+ workspace with a single container.
+ '';
+ };
+ };
+ });
+ default = null;
+ description = if moduleName == "sway" then ''
+ Gaps related settings.
+ '' else ''
+ i3Gaps related settings. The i3-gaps package must be used for these features to work.
+ '';
+ };
+
+ terminal = mkOption {
+ type = types.str;
+ default = if moduleName == "i3" then
+ "i3-sensible-terminal"
+ else
+ "${pkgs.rxvt-unicode-unwrapped}/bin/urxvt";
+ description = "Default terminal to run.";
+ example = "alacritty";
+ };
+
+ menu = mkOption {
+ type = types.str;
+ default = if moduleName == "sway" then
+ "${pkgs.dmenu}/bin/dmenu_path | ${pkgs.dmenu}/bin/dmenu | ${pkgs.findutils}/bin/xargs swaymsg exec --"
+ else
+ "${pkgs.dmenu}/bin/dmenu_run";
+ description = "Default launcher to use.";
+ example = "bemenu-run";
+ };
+}
diff --git a/home-manager/modules/services/window-managers/i3-sway/sway.nix b/home-manager/modules/services/window-managers/i3-sway/sway.nix
new file mode 100644
index 00000000000..8f0ee608104
--- /dev/null
+++ b/home-manager/modules/services/window-managers/i3-sway/sway.nix
@@ -0,0 +1,416 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.wayland.windowManager.sway;
+
+ commonOptions = import ./lib/options.nix {
+ inherit config lib cfg pkgs;
+ moduleName = "sway";
+ capitalModuleName = "Sway";
+ };
+
+ configModule = types.submodule {
+ options = {
+ inherit (commonOptions)
+ fonts window floating focus assigns workspaceLayout
+ workspaceAutoBackAndForth modifier keycodebindings colors bars startup
+ gaps menu terminal;
+
+ left = mkOption {
+ type = types.str;
+ default = "h";
+ description = "Home row direction key for moving left.";
+ };
+
+ down = mkOption {
+ type = types.str;
+ default = "j";
+ description = "Home row direction key for moving down.";
+ };
+
+ up = mkOption {
+ type = types.str;
+ default = "k";
+ description = "Home row direction key for moving up.";
+ };
+
+ right = mkOption {
+ type = types.str;
+ default = "l";
+ description = "Home row direction key for moving right.";
+ };
+
+ keybindings = mkOption {
+ type = types.attrsOf (types.nullOr types.str);
+ default = mapAttrs (n: mkOptionDefault) {
+ "${cfg.config.modifier}+Return" = "exec ${cfg.config.terminal}";
+ "${cfg.config.modifier}+Shift+q" = "kill";
+ "${cfg.config.modifier}+d" = "exec ${cfg.config.menu}";
+
+ "${cfg.config.modifier}+${cfg.config.left}" = "focus left";
+ "${cfg.config.modifier}+${cfg.config.down}" = "focus down";
+ "${cfg.config.modifier}+${cfg.config.up}" = "focus up";
+ "${cfg.config.modifier}+${cfg.config.right}" = "focus right";
+
+ "${cfg.config.modifier}+Left" = "focus left";
+ "${cfg.config.modifier}+Down" = "focus down";
+ "${cfg.config.modifier}+Up" = "focus up";
+ "${cfg.config.modifier}+Right" = "focus right";
+
+ "${cfg.config.modifier}+Shift+${cfg.config.left}" = "move left";
+ "${cfg.config.modifier}+Shift+${cfg.config.down}" = "move down";
+ "${cfg.config.modifier}+Shift+${cfg.config.up}" = "move up";
+ "${cfg.config.modifier}+Shift+${cfg.config.right}" = "move right";
+
+ "${cfg.config.modifier}+Shift+Left" = "move left";
+ "${cfg.config.modifier}+Shift+Down" = "move down";
+ "${cfg.config.modifier}+Shift+Up" = "move up";
+ "${cfg.config.modifier}+Shift+Right" = "move right";
+
+ "${cfg.config.modifier}+b" = "splith";
+ "${cfg.config.modifier}+v" = "splitv";
+ "${cfg.config.modifier}+f" = "fullscreen toggle";
+ "${cfg.config.modifier}+a" = "focus parent";
+
+ "${cfg.config.modifier}+s" = "layout stacking";
+ "${cfg.config.modifier}+w" = "layout tabbed";
+ "${cfg.config.modifier}+e" = "layout toggle split";
+
+ "${cfg.config.modifier}+Shift+space" = "floating toggle";
+ "${cfg.config.modifier}+space" = "focus mode_toggle";
+
+ "${cfg.config.modifier}+1" = "workspace number 1";
+ "${cfg.config.modifier}+2" = "workspace number 2";
+ "${cfg.config.modifier}+3" = "workspace number 3";
+ "${cfg.config.modifier}+4" = "workspace number 4";
+ "${cfg.config.modifier}+5" = "workspace number 5";
+ "${cfg.config.modifier}+6" = "workspace number 6";
+ "${cfg.config.modifier}+7" = "workspace number 7";
+ "${cfg.config.modifier}+8" = "workspace number 8";
+ "${cfg.config.modifier}+9" = "workspace number 9";
+
+ "${cfg.config.modifier}+Shift+1" =
+ "move container to workspace number 1";
+ "${cfg.config.modifier}+Shift+2" =
+ "move container to workspace number 2";
+ "${cfg.config.modifier}+Shift+3" =
+ "move container to workspace number 3";
+ "${cfg.config.modifier}+Shift+4" =
+ "move container to workspace number 4";
+ "${cfg.config.modifier}+Shift+5" =
+ "move container to workspace number 5";
+ "${cfg.config.modifier}+Shift+6" =
+ "move container to workspace number 6";
+ "${cfg.config.modifier}+Shift+7" =
+ "move container to workspace number 7";
+ "${cfg.config.modifier}+Shift+8" =
+ "move container to workspace number 8";
+ "${cfg.config.modifier}+Shift+9" =
+ "move container to workspace number 9";
+
+ "${cfg.config.modifier}+Shift+minus" = "move scratchpad";
+ "${cfg.config.modifier}+minus" = "scratchpad show";
+
+ "${cfg.config.modifier}+Shift+c" = "reload";
+ "${cfg.config.modifier}+Shift+e" =
+ "exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
+
+ "${cfg.config.modifier}+r" = "mode resize";
+ };
+ defaultText = "Default sway keybindings.";
+ description = ''
+ An attribute set that assigns a key press to an action using a key symbol.
+ See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
+ </para><para>
+ Consider to use <code>lib.mkOptionDefault</code> function to extend or override
+ default keybindings instead of specifying all of them from scratch.
+ '';
+ example = literalExample ''
+ let
+ modifier = config.wayland.windowManager.sway.config.modifier;
+ in lib.mkOptionDefault {
+ "''${modifier}+Return" = "exec ${cfg.config.terminal}";
+ "''${modifier}+Shift+q" = "kill";
+ "''${modifier}+d" = "exec ${cfg.config.menu}";
+ }
+ '';
+ };
+
+ bindkeysToCode = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Whether to make use of <option>--to-code</option> in keybindings.
+ '';
+ };
+
+ input = mkOption {
+ type = types.attrsOf (types.attrsOf types.str);
+ default = { };
+ example = { "*" = { xkb_variant = "dvorak"; }; };
+ description = ''
+ An attribute set that defines input modules. See man sway_input for options.
+ '';
+ };
+
+ output = mkOption {
+ type = types.attrsOf (types.attrsOf types.str);
+ default = { };
+ example = { "HDMI-A-2" = { bg = "~/path/to/background.png fill"; }; };
+ description = ''
+ An attribute set that defines output modules. See man sway_output for options.
+ '';
+ };
+
+ modes = mkOption {
+ type = types.attrsOf (types.attrsOf types.str);
+ default = {
+ resize = {
+ "${cfg.config.left}" = "resize shrink width 10 px";
+ "${cfg.config.down}" = "resize grow height 10 px";
+ "${cfg.config.up}" = "resize shrink height 10 px";
+ "${cfg.config.right}" = "resize grow width 10 px";
+ "Left" = "resize shrink width 10 px";
+ "Down" = "resize grow height 10 px";
+ "Up" = "resize shrink height 10 px";
+ "Right" = "resize grow width 10 px";
+ "Escape" = "mode default";
+ "Return" = "mode default";
+ };
+ };
+ description = ''
+ An attribute set that defines binding modes and keybindings
+ inside them
+
+ Only basic keybinding is supported (bindsym keycomb action),
+ for more advanced setup use 'sway.extraConfig'.
+ '';
+ };
+ };
+ };
+
+ wrapperOptions = types.submodule {
+ options = let
+ mkWrapperFeature = default: description:
+ mkOption {
+ type = types.bool;
+ inherit default;
+ example = !default;
+ description = "Whether to make use of the ${description}";
+ };
+ in {
+ base = mkWrapperFeature true ''
+ base wrapper to execute extra session commands and prepend a
+ dbus-run-session to the sway command.
+ '';
+ gtk = mkWrapperFeature false ''
+ wrapGAppsHook wrapper to execute sway with required environment
+ variables for GTK applications.
+ '';
+ };
+ };
+
+ commonFunctions = import ./lib/functions.nix {
+ inherit cfg lib;
+ moduleName = "sway";
+ };
+
+ inherit (commonFunctions)
+ keybindingsStr keycodebindingsStr modeStr assignStr barStr gapsStr
+ floatingCriteriaStr windowCommandsStr colorSetStr;
+
+ startupEntryStr = { command, always, ... }: ''
+ ${if always then "exec_always" else "exec"} ${command}
+ '';
+
+ inputStr = name: attrs: ''
+ input "${name}" {
+ ${concatStringsSep "\n"
+ (mapAttrsToList (name: value: "${name} ${value}") attrs)}
+ }
+ '';
+
+ outputStr = name: attrs: ''
+ output "${name}" {
+ ${concatStringsSep "\n"
+ (mapAttrsToList (name: value: "${name} ${value}") attrs)}
+ }
+ '';
+
+ configFile = pkgs.writeText "sway.conf" ((if cfg.config != null then
+ with cfg.config; ''
+ font pango:${concatStringsSep ", " fonts}
+ floating_modifier ${floating.modifier}
+ default_border ${if window.titlebar then "normal" else "pixel"} ${
+ toString window.border
+ }
+ default_floating_border ${
+ if floating.titlebar then "normal" else "pixel"
+ } ${toString floating.border}
+ hide_edge_borders ${window.hideEdgeBorders}
+ focus_wrapping ${if focus.forceWrapping then "yes" else "no"}
+ focus_follows_mouse ${focus.followMouse}
+ focus_on_window_activation ${focus.newWindow}
+ mouse_warping ${if focus.mouseWarping then "output" else "none"}
+ workspace_layout ${workspaceLayout}
+ workspace_auto_back_and_forth ${
+ if workspaceAutoBackAndForth then "yes" else "no"
+ }
+
+ client.focused ${colorSetStr colors.focused}
+ client.focused_inactive ${colorSetStr colors.focusedInactive}
+ client.unfocused ${colorSetStr colors.unfocused}
+ client.urgent ${colorSetStr colors.urgent}
+ client.placeholder ${colorSetStr colors.placeholder}
+ client.background ${colors.background}
+
+ ${keybindingsStr {
+ inherit keybindings;
+ bindsymArgs =
+ lib.optionalString (cfg.config.bindkeysToCode) "--to-code";
+ }}
+ ${keycodebindingsStr keycodebindings}
+ ${concatStringsSep "\n" (mapAttrsToList inputStr input)}
+ ${concatStringsSep "\n" (mapAttrsToList outputStr output)}
+ ${concatStringsSep "\n" (mapAttrsToList modeStr modes)}
+ ${concatStringsSep "\n" (mapAttrsToList assignStr assigns)}
+ ${concatStringsSep "\n" (map barStr bars)}
+ ${optionalString (gaps != null) gapsStr}
+ ${concatStringsSep "\n" (map floatingCriteriaStr floating.criteria)}
+ ${concatStringsSep "\n" (map windowCommandsStr window.commands)}
+ ${concatStringsSep "\n" (map startupEntryStr startup)}
+ ''
+ else
+ "") + "\n" + (if cfg.systemdIntegration then ''
+ exec "systemctl --user import-environment; systemctl --user start sway-session.target"
+ '' else
+ "") + cfg.extraConfig);
+
+ defaultSwayPackage = pkgs.sway.override {
+ extraSessionCommands = cfg.extraSessionCommands;
+ extraOptions = cfg.extraOptions;
+ withBaseWrapper = cfg.wrapperFeatures.base;
+ withGtkWrapper = cfg.wrapperFeatures.gtk;
+ };
+
+in {
+ meta.maintainers = [ maintainers.alexarice ];
+
+ options.wayland.windowManager.sway = {
+ enable = mkEnableOption "sway wayland compositor";
+
+ package = mkOption {
+ type = with types; nullOr package;
+ default = defaultSwayPackage;
+ defaultText = literalExample "${pkgs.sway}";
+ description = ''
+ Sway package to use. Will override the options
+ 'wrapperFeatures', 'extraSessionCommands', and 'extraOptions'.
+ Set to <code>null</code> to not add any Sway package to your
+ path. This should be done if you want to use the NixOS Sway
+ module to install Sway.
+ '';
+ };
+
+ systemdIntegration = mkOption {
+ type = types.bool;
+ default = pkgs.stdenv.isLinux;
+ example = false;
+ description = ''
+ Whether to enable <filename>sway-session.target</filename> on
+ sway startup. This links to
+ <filename>graphical-session.target</filename>.
+ '';
+ };
+
+ xwayland = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Enable xwayland, which is needed for the default configuration of sway.
+ '';
+ };
+
+ wrapperFeatures = mkOption {
+ type = wrapperOptions;
+ default = { };
+ example = { gtk = true; };
+ description = ''
+ Attribute set of features to enable in the wrapper.
+ '';
+ };
+
+ extraSessionCommands = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ export SDL_VIDEODRIVER=wayland
+ # needs qt5.qtwayland in systemPackages
+ export QT_QPA_PLATFORM=wayland
+ export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
+ # Fix for some Java AWT applications (e.g. Android Studio),
+ # use this if they aren't displayed properly:
+ export _JAVA_AWT_WM_NONREPARENTING=1
+ '';
+ description = ''
+ Shell commands executed just before Sway is started.
+ '';
+ };
+
+ extraOptions = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [
+ "--verbose"
+ "--debug"
+ "--unsupported-gpu"
+ "--my-next-gpu-wont-be-nvidia"
+ ];
+ description = ''
+ Command line arguments passed to launch Sway. Please DO NOT report
+ issues if you use an unsupported GPU (proprietary drivers).
+ '';
+ };
+
+ config = mkOption {
+ type = types.nullOr configModule;
+ default = { };
+ description = "Sway configuration options.";
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description =
+ "Extra configuration lines to add to ~/.config/sway/config.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = optional (cfg.package != null) cfg.package
+ ++ optional cfg.xwayland pkgs.xwayland;
+ xdg.configFile."sway/config" = {
+ source = configFile;
+ onChange = ''
+ swaySocket=''${XDG_RUNTIME_DIR:-/run/user/$UID}/sway-ipc.$UID.$(${pkgs.procps}/bin/pgrep -x sway).sock
+ if [ -S $swaySocket ]; then
+ echo "Reloading sway"
+ $DRY_RUN_CMD ${pkgs.sway}/bin/swaymsg -s $swaySocket reload
+ fi
+ '';
+ };
+ systemd.user.targets.sway-session = mkIf cfg.systemdIntegration {
+ Unit = {
+ Description = "sway compositor session";
+ Documentation = [ "man:systemd.special(7)" ];
+ BindsTo = [ "graphical-session.target" ];
+ Wants = [ "graphical-session-pre.target" ];
+ After = [ "graphical-session-pre.target" ];
+ };
+ };
+ };
+}
diff --git a/home-manager/modules/services/window-managers/i3.nix b/home-manager/modules/services/window-managers/i3.nix
deleted file mode 100644
index 7a4ec90b1cd..00000000000
--- a/home-manager/modules/services/window-managers/i3.nix
+++ /dev/null
@@ -1,856 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
- cfg = config.xsession.windowManager.i3;
-
- commonOptions = {
- fonts = mkOption {
- type = types.listOf types.str;
- default = ["monospace 8"];
- description = ''
- Font list used for window titles. Only FreeType fonts are supported.
- The order here is improtant (e.g. icons font should go before the one used for text).
- '';
- example = [ "FontAwesome 10" "Terminus 10" ];
- };
- };
-
- startupModule = types.submodule {
- options = {
- command = mkOption {
- type = types.str;
- description = "Command that will be executed on startup.";
- };
-
- always = mkOption {
- type = types.bool;
- default = false;
- description = "Whether to run command on each i3 restart.";
- };
-
- notification = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Whether to enable startup-notification support for the command.
- See <option>--no-startup-id</option> option description in the i3 user guide.
- '';
- };
-
- workspace = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = ''
- Launch application on a particular workspace. DEPRECATED:
- Use <varname><link linkend="opt-xsession.windowManager.i3.config.assigns">xsession.windowManager.i3.config.assigns</link></varname>
- instead. See <link xlink:href="https://github.com/rycee/home-manager/issues/265"/>.
- '';
- };
- };
- };
-
- barColorSetModule = types.submodule {
- options = {
- border = mkOption {
- type = types.str;
- visible = false;
- };
-
- background = mkOption {
- type = types.str;
- visible = false;
- };
-
- text = mkOption {
- type = types.str;
- visible = false;
- };
- };
- };
-
- colorSetModule = types.submodule {
- options = {
- border = mkOption {
- type = types.str;
- visible = false;
- };
-
- childBorder = mkOption {
- type = types.str;
- visible = false;
- };
-
- background = mkOption {
- type = types.str;
- visible = false;
- };
-
- text = mkOption {
- type = types.str;
- visible = false;
- };
-
- indicator = mkOption {
- type = types.str;
- visible = false;
- };
- };
- };
-
- barModule = types.submodule {
- options = {
- inherit (commonOptions) fonts;
-
- extraConfig = mkOption {
- type = types.lines;
- default = "";
- description = "Extra configuration lines for this bar.";
- };
-
- id = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = ''
- Specifies the bar ID for the configured bar instance.
- If this option is missing, the ID is set to bar-x, where x corresponds
- to the position of the embedding bar block in the config file.
- '';
- };
-
- mode = mkOption {
- type = types.enum [ "dock" "hide" "invisible" ];
- default = "dock";
- description = "Bar visibility mode.";
- };
-
- hiddenState = mkOption {
- type = types.enum [ "hide" "show" ];
- default = "hide";
- description = "The default bar mode when 'bar.mode' == 'hide'.";
- };
-
- position = mkOption {
- type = types.enum [ "top" "bottom" ];
- default = "bottom";
- description = "The edge of the screen i3bar should show up.";
- };
-
- workspaceButtons = mkOption {
- type = types.bool;
- default = true;
- description = "Whether workspace buttons should be shown or not.";
- };
-
- workspaceNumbers = mkOption {
- type = types.bool;
- default = true;
- description = "Whether workspace numbers should be displayed within the workspace buttons.";
- };
-
- command = mkOption {
- type = types.str;
- default = "${cfg.package}/bin/i3bar";
- defaultText = "i3bar";
- description = "Command that will be used to start a bar.";
- example = "\${pkgs.i3-gaps}/bin/i3bar -t";
- };
-
- statusCommand = mkOption {
- type = types.str;
- default = "${pkgs.i3status}/bin/i3status";
- description = "Command that will be used to get status lines.";
- };
-
- colors = mkOption {
- type = types.submodule {
- options = {
- background = mkOption {
- type = types.str;
- default = "#000000";
- description = "Background color of the bar.";
- };
-
- statusline = mkOption {
- type = types.str;
- default = "#ffffff";
- description = "Text color to be used for the statusline.";
- };
-
- separator = mkOption {
- type = types.str;
- default = "#666666";
- description = "Text color to be used for the separator.";
- };
-
- focusedWorkspace = mkOption {
- type = barColorSetModule;
- default = { border = "#4c7899"; background = "#285577"; text = "#ffffff"; };
- description = ''
- Border, background and text color for a workspace button when the workspace has focus.
- '';
- };
-
- activeWorkspace = mkOption {
- type = barColorSetModule;
- default = { border = "#333333"; background = "#5f676a"; text = "#ffffff"; };
- description = ''
- Border, background and text color for a workspace button when the workspace is active.
- '';
- };
-
- inactiveWorkspace = mkOption {
- type = barColorSetModule;
- default = { border = "#333333"; background = "#222222"; text = "#888888"; };
- description = ''
- Border, background and text color for a workspace button when the workspace does not
- have focus and is not active.
- '';
- };
-
- urgentWorkspace = mkOption {
- type = barColorSetModule;
- default = { border = "#2f343a"; background = "#900000"; text = "#ffffff"; };
- description = ''
- Border, background and text color for a workspace button when the workspace contains
- a window with the urgency hint set.
- '';
- };
-
- bindingMode = mkOption {
- type = barColorSetModule;
- default = { border = "#2f343a"; background = "#900000"; text = "#ffffff"; };
- description = "Border, background and text color for the binding mode indicator";
- };
- };
- };
- default = {};
- description = ''
- Bar color settings. All color classes can be specified using submodules
- with 'border', 'background', 'text', fields and RGB color hex-codes as values.
- See default values for the reference.
- Note that 'background', 'status', and 'separator' parameters take a single RGB value.
-
- See <link xlink:href="https://i3wm.org/docs/userguide.html#_colors"/>.
- '';
- };
-
- trayOutput = mkOption {
- type = types.str;
- default = "primary";
- description = "Where to output tray.";
- };
- };
- };
-
- windowCommandModule = types.submodule {
- options = {
- command = mkOption {
- type = types.str;
- description = "i3wm command to execute.";
- example = "border pixel 1";
- };
-
- criteria = mkOption {
- type = criteriaModule;
- description = "Criteria of the windows on which command should be executed.";
- example = { title = "x200: ~/work"; };
- };
- };
- };
-
- criteriaModule = types.attrsOf types.str;
-
- configModule = types.submodule {
- options = {
- inherit (commonOptions) fonts;
-
- window = mkOption {
- type = types.submodule {
- options = {
- titlebar = mkOption {
- type = types.bool;
- default = cfg.package != pkgs.i3-gaps;
- defaultText = "xsession.windowManager.i3.package != nixpkgs.i3-gaps (titlebar should be disabled for i3-gaps)";
- description = "Whether to show window titlebars.";
- };
-
- border = mkOption {
- type = types.int;
- default = 2;
- description = "Window border width.";
- };
-
- hideEdgeBorders = mkOption {
- type = types.enum [ "none" "vertical" "horizontal" "both" "smart" ];
- default = "none";
- description = "Hide window borders adjacent to the screen edges.";
- };
-
- commands = mkOption {
- type = types.listOf windowCommandModule;
- default = [];
- description = ''
- List of commands that should be executed on specific windows.
- See <option>for_window</option> i3wm option documentation.
- '';
- example = [ { command = "border pixel 1"; criteria = { class = "XTerm"; }; } ];
- };
- };
- };
- default = {};
- description = "Window titlebar and border settings.";
- };
-
- floating = mkOption {
- type = types.submodule {
- options = {
- titlebar = mkOption {
- type = types.bool;
- default = cfg.package != pkgs.i3-gaps;
- defaultText = "xsession.windowManager.i3.package != nixpkgs.i3-gaps (titlebar should be disabled for i3-gaps)";
- description = "Whether to show floating window titlebars.";
- };
-
- border = mkOption {
- type = types.int;
- default = 2;
- description = "Floating windows border width.";
- };
-
- modifier = mkOption {
- type = types.enum [ "Shift" "Control" "Mod1" "Mod2" "Mod3" "Mod4" "Mod5" ];
- default = cfg.config.modifier;
- defaultText = "i3.config.modifier";
- description = "Modifier key that can be used to drag floating windows.";
- example = "Mod4";
- };
-
- criteria = mkOption {
- type = types.listOf criteriaModule;
- default = [];
- description = "List of criteria for windows that should be opened in a floating mode.";
- example = [ {"title" = "Steam - Update News";} {"class" = "Pavucontrol";} ];
- };
- };
- };
- default = {};
- description = "Floating window settings.";
- };
-
- focus = mkOption {
- type = types.submodule {
- options = {
- newWindow = mkOption {
- type = types.enum [ "smart" "urgent" "focus" "none" ];
- default = "smart";
- description = ''
- This option modifies focus behavior on new window activation.
-
- See <link xlink:href="https://i3wm.org/docs/userguide.html#focus_on_window_activation"/>
- '';
- example = "none";
- };
-
- followMouse = mkOption {
- type = types.bool;
- default = true;
- description = "Whether focus should follow the mouse.";
- };
-
- forceWrapping = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to force focus wrapping in tabbed or stacked container.
-
- See <link xlink:href="https://i3wm.org/docs/userguide.html#_focus_wrapping"/>
- '';
- };
-
- mouseWarping = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Whether mouse cursor should be warped to the center of the window when switching focus
- to a window on a different output.
- '';
- };
- };
- };
- default = {};
- description = "Focus related settings.";
- };
-
- assigns = mkOption {
- type = types.attrsOf (types.listOf criteriaModule);
- default = {};
- description = ''
- An attribute set that assigns applications to workspaces based
- on criteria.
- '';
- example = literalExample ''
- {
- "1: web" = [{ class = "^Firefox$"; }];
- "0: extra" = [{ class = "^Firefox$"; window_role = "About"; }];
- }
- '';
- };
-
- modifier = mkOption {
- type = types.enum [ "Shift" "Control" "Mod1" "Mod2" "Mod3" "Mod4" "Mod5" ];
- default = "Mod1";
- description = "Modifier key that is used for all default keybindings.";
- example = "Mod4";
- };
-
- workspaceLayout = mkOption {
- type = types.enum [ "default" "stacked" "tabbed" ];
- default = "default";
- example = "tabbed";
- description = ''
- The mode in which new containers on workspace level will
- start.
- '';
- };
-
- workspaceAutoBackAndForth = mkOption {
- type = types.bool;
- default = false;
- example = true;
- description = ''
- Assume you are on workspace "1: www" and switch to "2: IM" using
- mod+2 because somebody sent you a message. You don’t need to remember
- where you came from now, you can just press $mod+2 again to switch
- back to "1: www".
- '';
- };
-
- keybindings = mkOption {
- type = types.attrsOf (types.nullOr types.str);
- default = mapAttrs (n: mkOptionDefault) {
- "${cfg.config.modifier}+Return" = "exec i3-sensible-terminal";
- "${cfg.config.modifier}+Shift+q" = "kill";
- "${cfg.config.modifier}+d" = "exec ${pkgs.dmenu}/bin/dmenu_run";
-
- "${cfg.config.modifier}+Left" = "focus left";
- "${cfg.config.modifier}+Down" = "focus down";
- "${cfg.config.modifier}+Up" = "focus up";
- "${cfg.config.modifier}+Right" = "focus right";
-
- "${cfg.config.modifier}+Shift+Left" = "move left";
- "${cfg.config.modifier}+Shift+Down" = "move down";
- "${cfg.config.modifier}+Shift+Up" = "move up";
- "${cfg.config.modifier}+Shift+Right" = "move right";
-
- "${cfg.config.modifier}+h" = "split h";
- "${cfg.config.modifier}+v" = "split v";
- "${cfg.config.modifier}+f" = "fullscreen toggle";
-
- "${cfg.config.modifier}+s" = "layout stacking";
- "${cfg.config.modifier}+w" = "layout tabbed";
- "${cfg.config.modifier}+e" = "layout toggle split";
-
- "${cfg.config.modifier}+Shift+space" = "floating toggle";
- "${cfg.config.modifier}+space" = "focus mode_toggle";
-
- "${cfg.config.modifier}+a" = "focus parent";
-
- "${cfg.config.modifier}+Shift+minus" = "move scratchpad";
- "${cfg.config.modifier}+minus" = "scratchpad show";
-
- "${cfg.config.modifier}+1" = "workspace number 1";
- "${cfg.config.modifier}+2" = "workspace number 2";
- "${cfg.config.modifier}+3" = "workspace number 3";
- "${cfg.config.modifier}+4" = "workspace number 4";
- "${cfg.config.modifier}+5" = "workspace number 5";
- "${cfg.config.modifier}+6" = "workspace number 6";
- "${cfg.config.modifier}+7" = "workspace number 7";
- "${cfg.config.modifier}+8" = "workspace number 8";
- "${cfg.config.modifier}+9" = "workspace number 9";
- "${cfg.config.modifier}+0" = "workspace number 10";
-
- "${cfg.config.modifier}+Shift+1" = "move container to workspace number 1";
- "${cfg.config.modifier}+Shift+2" = "move container to workspace number 2";
- "${cfg.config.modifier}+Shift+3" = "move container to workspace number 3";
- "${cfg.config.modifier}+Shift+4" = "move container to workspace number 4";
- "${cfg.config.modifier}+Shift+5" = "move container to workspace number 5";
- "${cfg.config.modifier}+Shift+6" = "move container to workspace number 6";
- "${cfg.config.modifier}+Shift+7" = "move container to workspace number 7";
- "${cfg.config.modifier}+Shift+8" = "move container to workspace number 8";
- "${cfg.config.modifier}+Shift+9" = "move container to workspace number 9";
- "${cfg.config.modifier}+Shift+0" = "move container to workspace number 10";
-
- "${cfg.config.modifier}+Shift+c" = "reload";
- "${cfg.config.modifier}+Shift+r" = "restart";
- "${cfg.config.modifier}+Shift+e" = "exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
-
- "${cfg.config.modifier}+r" = "mode resize";
- };
- defaultText = "Default i3 keybindings.";
- description = ''
- An attribute set that assigns a key press to an action using a key symbol.
- See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
- </para><para>
- Consider to use <code>lib.mkOptionDefault</code> function to extend or override
- default keybindings instead of specifying all of them from scratch.
- '';
- example = literalExample ''
- let
- modifier = xsession.windowManager.i3.config.modifier;
- in
-
- lib.mkOptionDefault {
- "''${modifier}+Return" = "exec i3-sensible-terminal";
- "''${modifier}+Shift+q" = "kill";
- "''${modifier}+d" = "exec \${pkgs.dmenu}/bin/dmenu_run";
- }
- '';
- };
-
- keycodebindings = mkOption {
- type = types.attrsOf (types.nullOr types.str);
- default = {};
- description = ''
- An attribute set that assigns keypress to an action using key code.
- See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
- '';
- example = { "214" = "exec --no-startup-id /bin/script.sh"; };
- };
-
- colors = mkOption {
- type = types.submodule {
- options = {
- background = mkOption {
- type = types.str;
- default = "#ffffff";
- description = ''
- Background color of the window. Only applications which do not cover
- the whole area expose the color.
- '';
- };
-
- focused = mkOption {
- type = colorSetModule;
- default = {
- border = "#4c7899"; background = "#285577"; text = "#ffffff";
- indicator = "#2e9ef4"; childBorder = "#285577";
- };
- description = "A window which currently has the focus.";
- };
-
- focusedInactive = mkOption {
- type = colorSetModule;
- default = {
- border = "#333333"; background = "#5f676a"; text = "#ffffff";
- indicator = "#484e50"; childBorder = "#5f676a";
- };
- description = ''
- A window which is the focused one of its container,
- but it does not have the focus at the moment.
- '';
- };
-
- unfocused = mkOption {
- type = colorSetModule;
- default = {
- border = "#333333"; background = "#222222"; text = "#888888";
- indicator = "#292d2e"; childBorder = "#222222";
- };
- description = "A window which is not focused.";
- };
-
- urgent = mkOption {
- type = colorSetModule;
- default = {
- border = "#2f343a"; background = "#900000"; text = "#ffffff";
- indicator = "#900000"; childBorder = "#900000";
- };
- description = "A window which has its urgency hint activated.";
- };
-
- placeholder = mkOption {
- type = colorSetModule;
- default = {
- border = "#000000"; background = "#0c0c0c"; text = "#ffffff";
- indicator = "#000000"; childBorder = "#0c0c0c";
- };
- description = ''
- Background and text color are used to draw placeholder window
- contents (when restoring layouts). Border and indicator are ignored.
- '';
- };
- };
- };
- default = {};
- description = ''
- Color settings. All color classes can be specified using submodules
- with 'border', 'background', 'text', 'indicator' and 'childBorder' fields
- and RGB color hex-codes as values. See default values for the reference.
- Note that 'i3.config.colors.background' parameter takes a single RGB value.
-
- See <link xlink:href="https://i3wm.org/docs/userguide.html#_changing_colors"/>.
- '';
- };
-
- modes = mkOption {
- type = types.attrsOf (types.attrsOf types.str);
- default = {
- resize = {
- "Left" = "resize shrink width 10 px or 10 ppt";
- "Down" = "resize grow height 10 px or 10 ppt";
- "Up" = "resize shrink height 10 px or 10 ppt";
- "Right" = "resize grow width 10 px or 10 ppt";
- "Escape" = "mode default";
- "Return" = "mode default";
- };
- };
- description = ''
- An attribute set that defines binding modes and keybindings
- inside them
-
- Only basic keybinding is supported (bindsym keycomb action),
- for more advanced setup use 'i3.extraConfig'.
- '';
- };
-
- bars = mkOption {
- type = types.listOf barModule;
- default = [{}];
- description = ''
- i3 bars settings blocks. Set to empty list to remove bars completely.
- '';
- };
-
- startup = mkOption {
- type = types.listOf startupModule;
- default = [];
- description = ''
- Commands that should be executed at startup.
-
- See <link xlink:href="https://i3wm.org/docs/userguide.html#_automatically_starting_applications_on_i3_startup"/>.
- '';
- example = literalExample ''
- [
- { command = "systemctl --user restart polybar"; always = true; notification = false; }
- { command = "dropbox start"; notification = false; }
- { command = "firefox"; workspace = "1: web"; }
- ];
- '';
- };
-
- gaps = mkOption {
- type = types.nullOr (types.submodule {
- options = {
- inner = mkOption {
- type = types.nullOr types.int;
- default = null;
- description = "Inner gaps value.";
- example = 12;
- };
-
- outer = mkOption {
- type = types.nullOr types.int;
- default = null;
- description = "Outer gaps value.";
- example = 5;
- };
-
- smartGaps = mkOption {
- type = types.bool;
- default = false;
- description = ''
- This option controls whether to disable all gaps (outer and inner)
- on workspace with a single container.
- '';
- example = true;
- };
-
- smartBorders = mkOption {
- type = types.enum [ "on" "off" "no_gaps" ];
- default = "off";
- description = ''
- This option controls whether to disable container borders on
- workspace with a single container.
- '';
- };
- };
- });
- default = null;
- description = ''
- i3gaps related settings.
- Note that i3-gaps package should be set for this options to take effect.
- '';
- };
- };
- };
-
- keybindingsStr = keybindings: concatStringsSep "\n" (
- mapAttrsToList (keycomb: action: optionalString (action != null) "bindsym ${keycomb} ${action}") keybindings
- );
-
- keycodebindingsStr = keycodebindings: concatStringsSep "\n" (
- mapAttrsToList (keycomb: action: optionalString (action != null) "bindcode ${keycomb} ${action}") keycodebindings
- );
-
- colorSetStr = c: concatStringsSep " " [ c.border c.background c.text c.indicator c.childBorder ];
- barColorSetStr = c: concatStringsSep " " [ c.border c.background c.text ];
-
- criteriaStr = criteria: "[${concatStringsSep " " (mapAttrsToList (k: v: ''${k}="${v}"'') criteria)}]";
-
- modeStr = name: keybindings: ''
- mode "${name}" {
- ${keybindingsStr keybindings}
- }
- '';
-
- assignStr = workspace: criteria: concatStringsSep "\n" (
- map (c: "assign ${criteriaStr c} ${workspace}") criteria
- );
-
- barStr = {
- id, fonts, mode, hiddenState, position, workspaceButtons,
- workspaceNumbers, command, statusCommand, colors, trayOutput, extraConfig, ...
- }: ''
- bar {
- ${optionalString (id != null) "id ${id}"}
- font pango:${concatStringsSep ", " fonts}
- mode ${mode}
- hidden_state ${hiddenState}
- position ${position}
- status_command ${statusCommand}
- i3bar_command ${command}
- workspace_buttons ${if workspaceButtons then "yes" else "no"}
- strip_workspace_numbers ${if !workspaceNumbers then "yes" else "no"}
- tray_output ${trayOutput}
- colors {
- background ${colors.background}
- statusline ${colors.statusline}
- separator ${colors.separator}
- focused_workspace ${barColorSetStr colors.focusedWorkspace}
- active_workspace ${barColorSetStr colors.activeWorkspace}
- inactive_workspace ${barColorSetStr colors.inactiveWorkspace}
- urgent_workspace ${barColorSetStr colors.urgentWorkspace}
- binding_mode ${barColorSetStr colors.bindingMode}
- }
- ${extraConfig}
- }
- '';
-
- gapsStr = with cfg.config.gaps; ''
- ${optionalString (inner != null) "gaps inner ${toString inner}"}
- ${optionalString (outer != null) "gaps outer ${toString outer}"}
- ${optionalString smartGaps "smart_gaps on"}
- ${optionalString (smartBorders != "off") "smart_borders ${smartBorders}"}
- '';
-
- floatingCriteriaStr = criteria: "for_window ${criteriaStr criteria} floating enable";
- windowCommandsStr = { command, criteria, ... }: "for_window ${criteriaStr criteria} ${command}";
-
- startupEntryStr = { command, always, notification, workspace, ... }: ''
- ${if always then "exec_always" else "exec"} ${
- if (notification && workspace == null) then "" else "--no-startup-id"
- } ${
- if (workspace == null) then
- command
- else
- "i3-msg 'workspace ${workspace}; exec ${command}'"
- }
- '';
-
- configFile = pkgs.writeText "i3.conf" ((if cfg.config != null then with cfg.config; ''
- font pango:${concatStringsSep ", " fonts}
- floating_modifier ${floating.modifier}
- new_window ${if window.titlebar then "normal" else "pixel"} ${toString window.border}
- new_float ${if floating.titlebar then "normal" else "pixel"} ${toString floating.border}
- hide_edge_borders ${window.hideEdgeBorders}
- force_focus_wrapping ${if focus.forceWrapping then "yes" else "no"}
- focus_follows_mouse ${if focus.followMouse then "yes" else "no"}
- focus_on_window_activation ${focus.newWindow}
- mouse_warping ${if focus.mouseWarping then "output" else "none"}
- workspace_layout ${workspaceLayout}
- workspace_auto_back_and_forth ${if workspaceAutoBackAndForth then "yes" else "no"}
-
- client.focused ${colorSetStr colors.focused}
- client.focused_inactive ${colorSetStr colors.focusedInactive}
- client.unfocused ${colorSetStr colors.unfocused}
- client.urgent ${colorSetStr colors.urgent}
- client.placeholder ${colorSetStr colors.placeholder}
- client.background ${colors.background}
-
- ${keybindingsStr keybindings}
- ${keycodebindingsStr keycodebindings}
- ${concatStringsSep "\n" (mapAttrsToList modeStr modes)}
- ${concatStringsSep "\n" (mapAttrsToList assignStr assigns)}
- ${concatStringsSep "\n" (map barStr bars)}
- ${optionalString (gaps != null) gapsStr}
- ${concatStringsSep "\n" (map floatingCriteriaStr floating.criteria)}
- ${concatStringsSep "\n" (map windowCommandsStr window.commands)}
- ${concatStringsSep "\n" (map startupEntryStr startup)}
- '' else "") + "\n" + cfg.extraConfig);
-
-in
-
-{
- options = {
- xsession.windowManager.i3 = {
- enable = mkEnableOption "i3 window manager.";
-
- package = mkOption {
- type = types.package;
- default = pkgs.i3;
- defaultText = literalExample "pkgs.i3";
- example = literalExample "pkgs.i3-gaps";
- description = ''
- i3 package to use.
- If 'i3.config.gaps' settings are specified, 'pkgs.i3-gaps' will be set as a default package.
- '';
- };
-
- config = mkOption {
- type = types.nullOr configModule;
- default = {};
- description = "i3 configuration options.";
- };
-
- extraConfig = mkOption {
- type = types.lines;
- default = "";
- description = "Extra configuration lines to add to ~/.config/i3/config.";
- };
- };
- };
-
- config = mkIf cfg.enable (mkMerge [
- {
- home.packages = [ cfg.package ];
- xsession.windowManager.command = "${cfg.package}/bin/i3";
- xdg.configFile."i3/config" = {
- source = configFile;
- onChange = ''
- i3Socket=''${XDG_RUNTIME_DIR:-/run/user/$UID}/i3/ipc-socket.*
- if [ -S $i3Socket ]; then
- echo "Reloading i3"
- $DRY_RUN_CMD ${cfg.package}/bin/i3-msg -s $i3Socket reload 1>/dev/null
- fi
- '';
- };
- }
-
- (mkIf (cfg.config != null) {
- xsession.windowManager.i3.package = mkDefault (
- if (cfg.config.gaps != null) then pkgs.i3-gaps else pkgs.i3
- );
- })
-
- (mkIf (cfg.config != null && (any (s: s.workspace != null) cfg.config.startup)) {
- warnings = [
- ("'xsession.windowManager.i3.config.startup.*.workspace' is deprecated, "
- + "use 'xsession.windowManager.i3.config.assigns' instead."
- + "See https://github.com/rycee/home-manager/issues/265.")
- ];
- })
- ]);
-}
diff --git a/home-manager/modules/services/xscreensaver.nix b/home-manager/modules/services/xscreensaver.nix
index 73a365aa730..ac6194e70c1 100644
--- a/home-manager/modules/services/xscreensaver.nix
+++ b/home-manager/modules/services/xscreensaver.nix
@@ -40,6 +40,10 @@ in {
Description = "XScreenSaver";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
+
+ # Make sure the service is restarted if the settings change.
+ X-Restart-Triggers =
+ [ (builtins.hashString "md5" (builtins.toJSON cfg.settings)) ];
};
Service = {
diff --git a/home-manager/modules/systemd-activate.rb b/home-manager/modules/systemd-activate.rb
index 8382c840e93..31d06d8fc19 100644
--- a/home-manager/modules/systemd-activate.rb
+++ b/home-manager/modules/systemd-activate.rb
@@ -1,6 +1,5 @@
require 'set'
require 'open3'
-require 'shellwords'
@dry_run = ENV['DRY_RUN']
@verbose = ENV['VERBOSE']
@@ -28,33 +27,36 @@ def setup_services(old_gen_path, new_gen_path, start_timeout_ms_string)
exit if old_services.empty? && new_services.empty?
+ all_services = get_active_targets_units(new_units_path)
+ maybe_changed = all_services & old_services
+ changed_services = get_changed_services(old_units_path, new_units_path, maybe_changed)
+ unchanged_oneshots = get_oneshot_services(maybe_changed - changed_services)
+
# These services should be running when this script is finished
- services_to_run = get_services_to_run(new_units_path)
- maybe_changed_services = services_to_run & old_services
+ services_to_run = all_services - unchanged_oneshots
# Only stop active services, otherwise we might get a 'service not loaded' error
# for inactive services that were removed in the current generation.
to_stop = get_active_units(old_services - new_services)
- to_restart = get_changed_services(old_units_path, new_units_path, maybe_changed_services)
+ to_restart = changed_services
to_start = get_inactive_units(services_to_run - to_restart)
- raise "daemon-reload failed" unless run_cmd('systemctl --user daemon-reload')
+ raise "daemon-reload failed" unless run_cmd('systemctl', '--user', 'daemon-reload')
- # Exclude services that aren't allowed to be manually started or stopped
+ # Exclude units that shouldn't be (re)started or stopped
no_manual_start, no_manual_stop, no_restart = get_restricted_units(to_stop + to_restart + to_start)
- to_stop -= no_manual_stop + no_restart
+ notify_skipped_units(to_restart & no_restart)
+ to_stop -= no_manual_stop
to_restart -= no_manual_stop + no_manual_start + no_restart
to_start -= no_manual_start
- puts "Not restarting: #{no_restart.join(' ')}" unless no_restart.empty?
-
if to_stop.empty? && to_start.empty? && to_restart.empty?
print_service_msg("All services are already running", services_to_run)
else
puts "Setting up services" if @verbose
- systemctl('stop', to_stop)
- systemctl('start', to_start)
- systemctl('restart', to_restart)
+ systemctl_action('stop', to_stop)
+ systemctl_action('start', to_start)
+ systemctl_action('restart', to_restart)
started_services = to_start + to_restart
if start_timeout_ms > 0 && !started_services.empty? && !@dry_run
failed = wait_and_get_failed_services(started_services, start_timeout_ms)
@@ -76,7 +78,7 @@ def get_services(dir)
end
def get_service_files(dir)
- Dir.chdir(dir) { Dir['*.{service,socket}'] }
+ Dir.chdir(dir) { Dir['*[^@].{service,socket,timer}'] }
end
def get_changed_services(dir_a, dir_b, services)
@@ -89,24 +91,24 @@ end
TargetDirRegexp = /^(.*\.target)\.wants$/
-# @return all services wanted by active targets
-def get_services_to_run(units_dir)
+# @return all units wanted by active targets
+def get_active_targets_units(units_dir)
return Set.new unless Dir.exists?(units_dir)
targets = Dir.entries(units_dir).map { |entry| entry[TargetDirRegexp, 1] }.compact
active_targets = get_active_units(targets)
- services_to_run = active_targets.map do |target|
+ active_units = active_targets.map do |target|
get_service_files(File.join(units_dir, "#{target}.wants"))
end.flatten
- Set.new(services_to_run)
+ Set.new(active_units)
end
# @return true on success
-def run_cmd(cmd)
+def run_cmd(*cmd)
print_cmd cmd
- @dry_run || system(cmd)
+ @dry_run || system(*cmd)
end
-def systemctl(cmd, services)
+def systemctl_action(cmd, services)
return if services.empty?
verb = (cmd == 'stop') ? 'Stopping' : "#{cmd.capitalize}ing"
@@ -114,7 +116,7 @@ def systemctl(cmd, services)
cmd = ['systemctl', '--user', cmd, *services]
if @dry_run
- puts cmd
+ puts cmd.join(' ')
return
end
@@ -131,32 +133,44 @@ def systemctl(cmd, services)
end
end
+def systemctl(*cmd)
+ output, _ = Open3.capture2('systemctl', '--user', *cmd)
+ output
+end
+
def print_cmd(cmd)
- puts cmd if @verbose || @dry_run
+ puts [*cmd].join(' ') if @verbose || @dry_run
end
def get_active_units(units)
- get_units_by_activity(units, true)
+ filter_units(units) { |state| state == 'active' }
end
def get_inactive_units(units)
- get_units_by_activity(units, false)
+ filter_units(units) { |state| state != 'active' }
+end
+
+def get_failed_units(units)
+ filter_units(units) { |state| state == 'failed' }
end
-def get_units_by_activity(units, active)
+def filter_units(units)
return [] if units.empty?
- units = units.to_a
- is_active = `systemctl --user is-active #{units.shelljoin}`.split
+ states = systemctl('is-active', *units).split
+ units.select.with_index { |_, i| yield states[i] }
+end
+
+def get_oneshot_services(units)
+ return [] if units.empty?
+ types = systemctl('show', '-p', 'Type', *units).split
units.select.with_index do |_, i|
- (is_active[i] == 'active') == active
+ types[i] == 'Type=oneshot'
end
end
def get_restricted_units(units)
- units = units.to_a
- infos = `systemctl --user show -p RefuseManualStart -p RefuseManualStop #{units.shelljoin}`
+ infos = systemctl('show', '-p', 'RefuseManualStart', '-p', 'RefuseManualStop', *units)
.split("\n\n")
- no_restart = []
no_manual_start = []
no_manual_stop = []
infos.zip(units).each do |info, unit|
@@ -164,14 +178,9 @@ def get_restricted_units(units)
no_manual_start << unit if no_start.end_with?('yes')
no_manual_stop << unit if no_stop.end_with?('yes')
end
- # Regular expression that indicates that a service should not be
- # restarted even if a change has been detected.
- restartRe = /^[ \t]*X-RestartIfChanged[ \t]*=[ \t]*false[ \t]*(?:#.*)?$/
- units.each do |unit|
- if `systemctl --user cat #{unit.shellescape}` =~ restartRe
- no_restart << unit
- end
- end
+ # Get units that should not be restarted even if a change has been detected.
+ no_restart_regexp = /^\s*X-RestartIfChanged\s*=\s*false\b/
+ no_restart = units.select { |unit| systemctl('cat', unit) =~ no_restart_regexp }
[no_manual_start, no_manual_stop, no_restart]
end
@@ -180,13 +189,13 @@ def wait_and_get_failed_services(services, start_timeout_ms)
# Force the previous message to always be visible before sleeping
STDOUT.flush
sleep(start_timeout_ms / 1000.0)
- get_inactive_units(services)
+ get_failed_units(services)
end
def show_failed_services_status(services)
puts
services.each do |service|
- run_cmd("systemctl --user status #{service.shellescape}")
+ run_cmd('systemctl', '--user', 'status', service)
puts
end
end
@@ -200,4 +209,8 @@ def print_service_msg(msg, services)
end
end
+def notify_skipped_units(no_restart)
+ puts "Not restarting: #{no_restart.join(' ')}" unless no_restart.empty?
+end
+
setup_services(*ARGV)
diff --git a/home-manager/modules/systemd.nix b/home-manager/modules/systemd.nix
index 56164020577..66fffadf737 100644
--- a/home-manager/modules/systemd.nix
+++ b/home-manager/modules/systemd.nix
@@ -228,7 +228,7 @@ in
# running this from the NixOS module then XDG_RUNTIME_DIR is not
# set and systemd commands will fail. We'll therefore have to
# set it ourselves in that case.
- home.activation.reloadSystemD = hm.dag.entryAfter ["linkGeneration"] (
+ home.activation.reloadSystemd = hm.dag.entryAfter ["linkGeneration"] (
let
autoReloadCmd = ''
${pkgs.ruby}/bin/ruby ${./systemd-activate.rb} \
@@ -249,7 +249,7 @@ in
if [[ $systemdStatus == 'running' || $systemdStatus == 'degraded' ]]; then
if [[ $systemdStatus == 'degraded' ]]; then
warnEcho "The user systemd session is degraded:"
- ${systemctl} --user --state=failed
+ ${systemctl} --user --no-pager --state=failed
warnEcho "Attempting to reload services anyway..."
fi
diff --git a/home-manager/modules/targets/darwin.nix b/home-manager/modules/targets/darwin.nix
new file mode 100644
index 00000000000..cd7d8e289cf
--- /dev/null
+++ b/home-manager/modules/targets/darwin.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+{
+ # Disabled for now due to conflicting behavior with nix-darwin. See
+ # https://github.com/rycee/home-manager/issues/1341#issuecomment-687286866
+ config = lib.mkIf (false && pkgs.stdenv.hostPlatform.isDarwin) {
+ # Install MacOS applications to the user environment.
+ home.file."Applications/Home Manager Apps".source = let
+ apps = pkgs.buildEnv {
+ name = "home-manager-applications";
+ paths = config.home.packages;
+ pathsToLink = "/Applications";
+ };
+ in "${apps}/Applications";
+ };
+}
diff --git a/home-manager/modules/targets/generic-linux.nix b/home-manager/modules/targets/generic-linux.nix
new file mode 100644
index 00000000000..47fcc87b3c0
--- /dev/null
+++ b/home-manager/modules/targets/generic-linux.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ profileDirectory = config.home.profileDirectory;
+
+in {
+ options.targets.genericLinux = {
+ enable = mkEnableOption "" // {
+ description = ''
+ Whether to enable settings that make Home Manager work better on
+ GNU/Linux distributions other than NixOS.
+ '';
+ };
+
+ extraXdgDataDirs = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "/usr/share" "/usr/local/share" ];
+ description = ''
+ List of directory names to add to <envar>XDG_DATA_DIRS</envar>.
+ '';
+ };
+ };
+
+ config = mkIf config.targets.genericLinux.enable {
+ home.sessionVariables = let
+ profiles =
+ [ "\${NIX_STATE_DIR:-/nix/var/nix}/profiles/default" profileDirectory ];
+ dataDirs = concatStringsSep ":"
+ (map (profile: "${profile}/share") profiles
+ ++ config.targets.genericLinux.extraXdgDataDirs);
+ in { XDG_DATA_DIRS = "${dataDirs}\${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS"; };
+
+ home.sessionVariablesExtra = ''
+ . "${pkgs.nix}/etc/profile.d/nix.sh"
+ '';
+
+ # We need to source both nix.sh and hm-session-vars.sh as noted in
+ # https://github.com/rycee/home-manager/pull/797#issuecomment-544783247
+ programs.bash.initExtra = ''
+ . "${pkgs.nix}/etc/profile.d/nix.sh"
+ . "${profileDirectory}/etc/profile.d/hm-session-vars.sh"
+ '';
+
+ systemd.user.sessionVariables = {
+ NIX_PATH = "$HOME/.nix-defexpr/channels\${NIX_PATH:+:}$NIX_PATH";
+ };
+ };
+}
diff --git a/home-manager/modules/xresources.nix b/home-manager/modules/xresources.nix
index b74d671befb..dc59e50c46e 100644
--- a/home-manager/modules/xresources.nix
+++ b/home-manager/modules/xresources.nix
@@ -28,7 +28,11 @@ in {
options = {
xresources.properties = mkOption {
- type = types.nullOr types.attrs;
+ type = with types;
+ let
+ prim = either bool (either int str);
+ entry = either prim (listOf prim);
+ in nullOr (attrsOf entry);
default = null;
example = literalExample ''
{
@@ -71,17 +75,18 @@ in {
};
};
- config = mkIf (cfg.properties != null || cfg.extraConfig != "") {
- home.file.".Xresources" = {
- text = concatStringsSep "\n" ([ ]
- ++ optional (cfg.extraConfig != "") cfg.extraConfig
- ++ optionals (cfg.properties != null)
- (mapAttrsToList formatLine cfg.properties)) + "\n";
- onChange = ''
- if [[ -v DISPLAY ]] ; then
- $DRY_RUN_CMD ${pkgs.xorg.xrdb}/bin/xrdb -merge $HOME/.Xresources
- fi
- '';
+ config = mkIf ((cfg.properties != null && cfg.properties != { })
+ || cfg.extraConfig != "") {
+ home.file.".Xresources" = {
+ text = concatStringsSep "\n" ([ ]
+ ++ optional (cfg.extraConfig != "") cfg.extraConfig
+ ++ optionals (cfg.properties != null)
+ (mapAttrsToList formatLine cfg.properties)) + "\n";
+ onChange = ''
+ if [[ -v DISPLAY ]] ; then
+ $DRY_RUN_CMD ${pkgs.xorg.xrdb}/bin/xrdb -merge $HOME/.Xresources
+ fi
+ '';
+ };
};
- };
}
diff --git a/home-manager/nix-darwin/default.nix b/home-manager/nix-darwin/default.nix
index 24f042a7f0b..12d02174332 100644
--- a/home-manager/nix-darwin/default.nix
+++ b/home-manager/nix-darwin/default.nix
@@ -10,11 +10,12 @@ let
hmModule = types.submoduleWith {
specialArgs = { lib = extendedLib; };
- modules = [(
- {name, ...}: {
+ modules = [
+ ({ name, ... }: {
imports = import ../modules/modules.nix {
inherit pkgs;
lib = extendedLib;
+ useNixpkgsModule = !cfg.useGlobalPkgs;
};
config = {
@@ -24,8 +25,8 @@ let
home.username = config.users.users.${name}.name;
home.homeDirectory = config.users.users.${name}.home;
};
- }
- )];
+ })
+ ];
};
in
@@ -38,6 +39,24 @@ in
<option>users.users.‹name?›.packages</option> option.
'';
+ useGlobalPkgs = mkEnableOption ''
+ using the system configuration's <literal>pkgs</literal>
+ argument in Home Manager. This disables the Home Manager
+ options <option>nixpkgs.*</option>
+ '';
+
+ backupFileExtension = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "backup";
+ description = ''
+ On activation move existing files by appending the given
+ file extension rather than exiting with an error.
+ '';
+ };
+
+ verbose = mkEnableOption "verbose output on activation";
+
users = mkOption {
type = types.attrsOf hmModule;
default = {};
@@ -68,14 +87,21 @@ in
users.users = mkIf cfg.useUserPackages (
mapAttrs (username: usercfg: {
- packages = usercfg.home.packages;
+ packages = [ usercfg.home.path ];
}) cfg.users
);
+ environment.pathsToLink = mkIf cfg.useUserPackages [ "/etc/profile.d" ];
+
system.activationScripts.postActivation.text =
concatStringsSep "\n" (mapAttrsToList (username: usercfg: ''
echo Activating home-manager configuration for ${username}
- sudo -u ${username} -i ${usercfg.home.activationPackage}/activate
+ sudo -u ${username} -i ${pkgs.writeShellScript "activation-${username}" ''
+ ${lib.optionalString (cfg.backupFileExtension != null)
+ "export HOME_MANAGER_BACKUP_EXT=${lib.escapeShellArg cfg.backupFileExtension}"}
+ ${lib.optionalString cfg.verbose "export VERBOSE=1"}
+ exec ${usercfg.home.activationPackage}/activate
+ ''}
'') cfg.users);
};
}
diff --git a/home-manager/nixos/default.nix b/home-manager/nixos/default.nix
index f4e417bda71..9aedf618c51 100644
--- a/home-manager/nixos/default.nix
+++ b/home-manager/nixos/default.nix
@@ -15,6 +15,7 @@ let
imports = import ../modules/modules.nix {
inherit pkgs;
lib = extendedLib;
+ useNixpkgsModule = !cfg.useGlobalPkgs;
};
config = {
@@ -45,6 +46,12 @@ in {
<option>users.users.‹name?›.packages</option> option.
'';
+ useGlobalPkgs = mkEnableOption ''
+ using the system configuration's <literal>pkgs</literal>
+ argument in Home Manager. This disables the Home Manager
+ options <option>nixpkgs.*</option>
+ '';
+
backupFileExtension = mkOption {
type = types.nullOr types.str;
default = null;
@@ -78,9 +85,11 @@ in {
})));
users.users = mkIf cfg.useUserPackages
- (mapAttrs (username: usercfg: { packages = usercfg.home.packages; })
+ (mapAttrs (username: usercfg: { packages = [ usercfg.home.path ]; })
cfg.users);
+ environment.pathsToLink = mkIf cfg.useUserPackages [ "/etc/profile.d" ];
+
systemd.services = mapAttrs' (_: usercfg:
let username = usercfg.home.username;
in nameValuePair ("home-manager-${utils.escapeSystemdPath username}") {
@@ -91,6 +100,8 @@ in {
environment = serviceEnvironment;
+ unitConfig = { RequiresMountsFor = usercfg.home.homeDirectory; };
+
serviceConfig = {
User = usercfg.home.username;
Type = "oneshot";
diff --git a/home-manager/tests/default.nix b/home-manager/tests/default.nix
index 49c27239730..a4a4da0c94c 100644
--- a/home-manager/tests/default.nix
+++ b/home-manager/tests/default.nix
@@ -7,14 +7,26 @@ let
nmt = pkgs.fetchFromGitLab {
owner = "rycee";
repo = "nmt";
- rev = "6f866d1acb89fa15cd3b62baa052deae1f685c0c";
- sha256 = "1qr1shhapjn4nnd4k6hml69ri8vgz4l8lakjll5hc516shs9a9nn";
+ rev = "8e130d655ec396ce165763c95bbf4ac429810ca8";
+ sha256 = "1jbljr06kg1ycdn24hj8xap16axq11rhb6hm4949fz48n57pwwps";
};
modules = import ../modules/modules.nix {
inherit lib pkgs;
check = false;
- };
+ } ++ [
+ {
+ # Fix impurities. Without these some of the user's environment
+ # will leak into the tests through `builtins.getEnv`.
+ xdg.enable = true;
+ home.username = "hm-user";
+ home.homeDirectory = "/home/hm-user";
+
+ # Avoid including documentation since this will cause
+ # unnecessary rebuilds of the tests.
+ manual.manpages.enable = false;
+ }
+ ];
in
@@ -27,28 +39,64 @@ import nmt {
./modules/home-environment
./modules/misc/fontconfig
./modules/programs/alacritty
+ ./modules/programs/alot
+ ./modules/programs/aria2
./modules/programs/bash
./modules/programs/browserpass
+ ./modules/programs/dircolors
+ ./modules/programs/direnv
+ ./modules/programs/fish
./modules/programs/git
./modules/programs/gpg
+ ./modules/programs/i3status
+ ./modules/programs/kakoune
+ ./modules/programs/lf
+ ./modules/programs/lieer
+ ./modules/programs/man
./modules/programs/mbsync
+ ./modules/programs/ncmpcpp
+ ./modules/programs/ne
./modules/programs/neomutt
./modules/programs/newsboat
+ ./modules/programs/nushell
+ ./modules/programs/qutebrowser
./modules/programs/readline
+ ./modules/programs/powerline-go
./modules/programs/ssh
+ ./modules/programs/starship
./modules/programs/texlive
./modules/programs/tmux
+ ./modules/programs/vscode
+ ./modules/programs/zplug
./modules/programs/zsh
./modules/xresources
+ ] ++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [
+ ./modules/targets-darwin
] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [
+ ./meta # Suffices to run on one platform.
+ ./modules/misc/debug
+ ./modules/misc/numlock
./modules/misc/pam
./modules/misc/xdg
./modules/misc/xsession
+ ./modules/programs/abook
+ ./modules/programs/autorandr
+ ./modules/services/dropbox
+ ./modules/services/emacs
+ ./modules/services/dropbox
./modules/programs/firefox
./modules/programs/getmail
+ ./modules/services/lieer
+ ./modules/programs/ncmpcpp-linux
./modules/programs/rofi
+ ./modules/programs/waybar
+ ./modules/services/kanshi
+ ./modules/services/polybar
./modules/services/sxhkd
+ ./modules/services/fluidsynth
./modules/services/window-managers/i3
+ ./modules/services/window-managers/sway
./modules/systemd
+ ./modules/targets-linux
]);
}
diff --git a/home-manager/tests/lib/types/dag-submodule.nix b/home-manager/tests/lib/types/dag-submodule.nix
new file mode 100644
index 00000000000..552e804acbc
--- /dev/null
+++ b/home-manager/tests/lib/types/dag-submodule.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ dag = config.lib.dag;
+
+ result = let
+ sorted = dag.topoSort config.tested.dag;
+ data = map (e: "${e.name}:${e.data.name}") sorted.result;
+ in concatStringsSep "\n" data + "\n";
+
+in {
+ options.tested.dag = mkOption {
+ type = hm.types.dagOf (types.submodule ({ dagName, ... }: {
+ options.name = mkOption { type = types.str; };
+ config.name = "dn-${dagName}";
+ }));
+ };
+
+ config = {
+ tested.dag = {
+ after = { };
+ before = dag.entryBefore [ "after" ] { };
+ between = dag.entryBetween [ "after" ] [ "before" ] { };
+ };
+
+ home.file."result.txt".text = result;
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/result.txt \
+ ${
+ pkgs.writeText "result.txt" ''
+ before:dn-before
+ between:dn-between
+ after:dn-after
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/lib/types/default.nix b/home-manager/tests/lib/types/default.nix
index 9fce65f88f0..acb56501272 100644
--- a/home-manager/tests/lib/types/default.nix
+++ b/home-manager/tests/lib/types/default.nix
@@ -1,4 +1,7 @@
{
+ lib-types-dag-submodule = ./dag-submodule.nix;
lib-types-dag-merge = ./dag-merge.nix;
lib-types-list-or-dag-merge = ./list-or-dag-merge.nix;
+
+ lib-types-gvariant-merge = ./gvariant-merge.nix;
}
diff --git a/home-manager/tests/lib/types/gvariant-merge.nix b/home-manager/tests/lib/types/gvariant-merge.nix
new file mode 100644
index 00000000000..867534c1f14
--- /dev/null
+++ b/home-manager/tests/lib/types/gvariant-merge.nix
@@ -0,0 +1,62 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+in {
+ options.examples = mkOption { type = types.attrsOf hm.types.gvariant; };
+
+ config = {
+ examples = with hm.gvariant;
+ mkMerge [
+ { bool = true; }
+ { bool = true; }
+
+ { float = 3.14; }
+
+ { int = 42; }
+ { int = 42; }
+
+ { list = [ "one" ]; }
+ { list = mkArray type.string [ "two" ]; }
+
+ { emptyArray1 = [ ]; }
+ { emptyArray2 = mkEmptyArray type.uint32; }
+
+ { string = "foo"; }
+ { string = "foo"; }
+ { escapedString = "' \\"; }
+
+ { tuple = mkTuple [ 1 [ "foo" ] ]; }
+
+ { maybe1 = mkNothing type.string; }
+ { maybe2 = mkJust (mkUint32 4); }
+ ];
+
+ home.file."result.txt".text = let
+ mkLine = n: v: "${n} = ${toString (hm.gvariant.mkValue v)}";
+ result = concatStringsSep "\n" (mapAttrsToList mkLine config.examples);
+ in result + "\n";
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/result.txt \
+ ${
+ pkgs.writeText "expected.txt" ''
+ bool = true
+ emptyArray1 = @as []
+ emptyArray2 = @as []
+ escapedString = '\' \\'
+ float = 3.140000
+ int = 42
+ list = @as ['one','two']
+ maybe1 = @ms nothing
+ maybe2 = just @u 4
+ string = 'foo'
+ tuple = @(ias) (1,@as ['foo'])
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/meta/default.nix b/home-manager/tests/meta/default.nix
new file mode 100644
index 00000000000..0f02fcfb453
--- /dev/null
+++ b/home-manager/tests/meta/default.nix
@@ -0,0 +1 @@
+{ meta-formatting = ./formatting.nix; }
diff --git a/home-manager/tests/meta/formatting.nix b/home-manager/tests/meta/formatting.nix
new file mode 100644
index 00000000000..2d5800c53cd
--- /dev/null
+++ b/home-manager/tests/meta/formatting.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ pinnedNixpkgs = builtins.fetchTarball {
+ url =
+ "https://github.com/NixOS/nixpkgs/archive/05f0934825c2a0750d4888c4735f9420c906b388.tar.gz";
+ sha256 = "1g8c2w0661qn89ajp44znmwfmghbbiygvdzq0rzlvlpdiz28v6gy";
+ };
+
+ pinnedPkgs = import pinnedNixpkgs { };
+
+in {
+ config = {
+ nmt.script = ''
+ PATH="${with pinnedPkgs; lib.makeBinPath [ findutils nixfmt ]}:$PATH"
+ cd ${../..}
+ if ! ${pkgs.runtimeShell} format -c; then
+ fail "${''
+ Expected source code to be formatted with nixfmt but it was not.
+ This error can be resolved by running the './format' in the project root directory.''}"
+ fi
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/accounts/email-test-accounts.nix b/home-manager/tests/modules/accounts/email-test-accounts.nix
index 9c9c90cf8fe..9a4e0b8e72f 100644
--- a/home-manager/tests/modules/accounts/email-test-accounts.nix
+++ b/home-manager/tests/modules/accounts/email-test-accounts.nix
@@ -21,6 +21,7 @@
passwordCommand = "password-command 2";
imap.host = "imap.example.org";
smtp.host = "smtp.example.org";
+ smtp.tls.useStartTls = true;
};
};
};
diff --git a/home-manager/tests/modules/files/default.nix b/home-manager/tests/modules/files/default.nix
index 04c61d3b886..6f1ef24b810 100644
--- a/home-manager/tests/modules/files/default.nix
+++ b/home-manager/tests/modules/files/default.nix
@@ -1,6 +1,8 @@
{
files-executable = ./executable.nix;
files-hidden-source = ./hidden-source.nix;
+ files-out-of-store-symlink = ./out-of-store-symlink.nix;
files-source-with-spaces = ./source-with-spaces.nix;
+ files-target-with-shellvar = ./target-with-shellvar.nix;
files-text = ./text.nix;
}
diff --git a/home-manager/tests/modules/files/out-of-store-symlink.nix b/home-manager/tests/modules/files/out-of-store-symlink.nix
new file mode 100644
index 00000000000..af274aaebec
--- /dev/null
+++ b/home-manager/tests/modules/files/out-of-store-symlink.nix
@@ -0,0 +1,29 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+ filePath = ./. + "/source with spaces!";
+
+in {
+ config = {
+ home.file."oos".source = config.lib.file.mkOutOfStoreSymlink filePath;
+
+ nmt.script = ''
+ assertLinkExists "home-files/oos"
+
+ storePath="$(readlink $TESTED/home-files/oos)"
+
+ if [[ ! -L $storePath ]]; then
+ fail "Expected $storePath to be a symbolic link, but it was not."
+ fi
+
+ actual="$(readlink "$storePath")"
+ expected="${toString filePath}"
+ if [[ $actual != $expected ]]; then
+ fail "Symlink home-files/oos should point to $expected via the Nix store, but it actually points to $actual."
+ fi
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/files/target-with-shellvar.nix b/home-manager/tests/modules/files/target-with-shellvar.nix
new file mode 100644
index 00000000000..c54946eb9eb
--- /dev/null
+++ b/home-manager/tests/modules/files/target-with-shellvar.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ home.file."$HOME/$FOO/bar baz".text = "blah";
+
+ nmt.script = ''
+ assertFileExists 'home-files/$HOME/$FOO/bar baz';
+ assertFileContent 'home-files/$HOME/$FOO/bar baz' \
+ ${pkgs.writeText "expected" "blah"}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/home-environment/session-variables-expected.txt b/home-manager/tests/modules/home-environment/session-variables-expected.txt
index 5c3868c3901..4e18e2b31a8 100644
--- a/home-manager/tests/modules/home-environment/session-variables-expected.txt
+++ b/home-manager/tests/modules/home-environment/session-variables-expected.txt
@@ -4,3 +4,6 @@ export __HM_SESS_VARS_SOURCED=1
export V1="v1"
export V2="v2-v1"
+export XDG_CACHE_HOME="/home/hm-user/.cache"
+export XDG_CONFIG_HOME="/home/hm-user/.config"
+export XDG_DATA_HOME="/home/hm-user/.local/share"
diff --git a/home-manager/tests/modules/misc/debug/default.nix b/home-manager/tests/modules/misc/debug/default.nix
new file mode 100644
index 00000000000..b42462d0802
--- /dev/null
+++ b/home-manager/tests/modules/misc/debug/default.nix
@@ -0,0 +1,25 @@
+{
+ debug = { pkgs, config, lib, ... }: {
+ home.enableDebugInfo = true;
+ home.packages = with pkgs; [ curl gdb ];
+
+ nmt.script = ''
+ [ -L $TESTED/home-path/lib/debug/curl ] \
+ || fail "Debug-symbols for pkgs.curl should exist in \`/home-path/lib/debug'!"
+
+ #source $TESTED/home-path/etc/profile.d/hm-session-vars.sh
+ #[[ "$NIX_DEBUG_INFO_DIRS" =~ /lib/debug$ ]] \
+ #|| fail "Invalid NIX_DEBUG_INFO_DIRS!"
+ assertFileExists home-path/etc/profile.d/hm-session-vars.sh
+ assertFileRegex home-path/etc/profile.d/hm-session-vars.sh \
+ 'NIX_DEBUG_INFO_DIRS=.*/lib/debug'
+
+ # We need to override NIX_DEBUG_INFO_DIRS here as $HOME evalutes to the home
+ # of the user who executes this testcase :/
+ { echo quit | PATH="$TESTED/home-path/bin''${PATH:+:}$PATH" NIX_DEBUG_INFO_DIRS=$TESTED/home-path/lib/debug \
+ gdb curl 2>&1 | \
+ grep 'Reading symbols from ${builtins.storeDir}/'; \
+ } || fail "Failed to read debug symbols from curl in gdb"
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/misc/fontconfig/default.nix b/home-manager/tests/modules/misc/fontconfig/default.nix
index b669e1c343c..bea306165c4 100644
--- a/home-manager/tests/modules/misc/fontconfig/default.nix
+++ b/home-manager/tests/modules/misc/fontconfig/default.nix
@@ -1,5 +1,22 @@
{
fontconfig-no-font-package = ./no-font-package.nix;
fontconfig-single-font-package = ./single-font-package.nix;
- fontconfig-multiple-font-packages = ./multiple-font-packages.nix;
+ # Disabled due to test failing with message
+ #
+ # Expected directory home-path/lib/fontconfig/cache to exist but it was not found.
+ #
+ # Verbose output from fc-cache:
+ #
+ # Font directories:
+ # /nix/store/da…g5-home-manager-path/lib/X11/fonts
+ # /nix/store/da…g5-home-manager-path/share/fonts
+ # /nix/store/da…g5-home-manager-path/share/fonts/truetype
+ # /nix/store/da…g5-home-manager-path/lib/X11/fonts: skipping, no such directory
+ # /nix/store/da…g5-home-manager-path/share/fonts: caching, new cache contents: 1 fonts, 1 dirs
+ # /nix/store/da…g5-home-manager-path/share/fonts/truetype: caching, new cache contents: 3 fonts, 0 dirs
+ # /nix/store/da…g5-home-manager-path/share/fonts/truetype: skipping, looped directory detected
+ # /nix/store/da…g5-home-manager-path/lib/fontconfig/cache: cleaning cache directory
+ # /nix/store/da…g5-home-manager-path/lib/fontconfig/cache: invalid cache file: 786068e7df13f7c2105017ef3d78e351-x86_64.cache-7
+ # /nix/store/da…g5-home-manager-path/lib/fontconfig/cache: invalid cache file: 4766193978ddda4bd196f2b98c00fb00-x86_64.cache-7
+ #fontconfig-multiple-font-packages = ./multiple-font-packages.nix;
}
diff --git a/home-manager/tests/modules/misc/numlock/default.nix b/home-manager/tests/modules/misc/numlock/default.nix
new file mode 100644
index 00000000000..47ca563fec0
--- /dev/null
+++ b/home-manager/tests/modules/misc/numlock/default.nix
@@ -0,0 +1 @@
+{ numlock = ./numlock.nix; }
diff --git a/home-manager/tests/modules/misc/numlock/numlock.nix b/home-manager/tests/modules/misc/numlock/numlock.nix
new file mode 100644
index 00000000000..aa468c212c3
--- /dev/null
+++ b/home-manager/tests/modules/misc/numlock/numlock.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ xsession.numlock.enable = true;
+
+ nixpkgs.overlays = [
+ (self: super: { numlockx = pkgs.writeScriptBin "dummy-numlockx" ""; })
+ ];
+
+ nmt.script = ''
+ serviceFile=home-files/.config/systemd/user/numlockx.service
+ assertFileExists $serviceFile
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/misc/xsession/basic-xprofile-expected.txt b/home-manager/tests/modules/misc/xsession/basic-xprofile-expected.txt
index 05733a974ff..282f8f5c5de 100644
--- a/home-manager/tests/modules/misc/xsession/basic-xprofile-expected.txt
+++ b/home-manager/tests/modules/misc/xsession/basic-xprofile-expected.txt
@@ -1,4 +1,4 @@
-. "/test-home/.nix-profile/etc/profile.d/hm-session-vars.sh"
+. "/home/hm-user/.nix-profile/etc/profile.d/hm-session-vars.sh"
if [ -e "$HOME/.profile" ]; then
. "$HOME/.profile"
diff --git a/home-manager/tests/modules/misc/xsession/basic.nix b/home-manager/tests/modules/misc/xsession/basic.nix
index 39df6362380..d6756291cfe 100644
--- a/home-manager/tests/modules/misc/xsession/basic.nix
+++ b/home-manager/tests/modules/misc/xsession/basic.nix
@@ -4,8 +4,6 @@ with lib;
{
config = {
- home.homeDirectory = "/test-home";
-
xsession = {
enable = true;
windowManager.command = "window manager command";
diff --git a/home-manager/tests/modules/misc/xsession/keyboard-without-layout.nix b/home-manager/tests/modules/misc/xsession/keyboard-without-layout.nix
index 015efe6154a..90038cfd034 100644
--- a/home-manager/tests/modules/misc/xsession/keyboard-without-layout.nix
+++ b/home-manager/tests/modules/misc/xsession/keyboard-without-layout.nix
@@ -6,8 +6,6 @@ with lib;
config = {
home.stateVersion = "19.09";
- home.homeDirectory = "/test-home";
-
home.keyboard = { options = [ "ctrl:nocaps" "altwin:no_win" ]; };
xsession = {
diff --git a/home-manager/tests/modules/programs/abook/default.nix b/home-manager/tests/modules/programs/abook/default.nix
new file mode 100644
index 00000000000..12ad238ed25
--- /dev/null
+++ b/home-manager/tests/modules/programs/abook/default.nix
@@ -0,0 +1,4 @@
+{
+ abook-no-settings = ./no-settings.nix;
+ abook-with-settings = ./with-settings.nix;
+}
diff --git a/home-manager/tests/modules/programs/abook/no-settings.nix b/home-manager/tests/modules/programs/abook/no-settings.nix
new file mode 100644
index 00000000000..ad04acd2365
--- /dev/null
+++ b/home-manager/tests/modules/programs/abook/no-settings.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.abook.enable = true;
+
+ nixpkgs.overlays =
+ [ (self: super: { abook = pkgs.writeScriptBin "dummy-abook" ""; }) ];
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/abook/abookrc
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/abook/with-settings.cfg b/home-manager/tests/modules/programs/abook/with-settings.cfg
new file mode 100644
index 00000000000..d73445f4d83
--- /dev/null
+++ b/home-manager/tests/modules/programs/abook/with-settings.cfg
@@ -0,0 +1,21 @@
+# Generated by Home Manager.
+# See http://abook.sourceforge.net/
+
+# Defining a new custom field
+# -----------------------------
+
+field pager = Pager
+field address_lines = Address, list
+field birthday = Birthday, date
+
+# Defining a view/tab
+# ---------------------
+
+view CONTACT = name, email
+view ADDRESS = address_lines, city, state, zip, country
+view PHONE = phone, workphone, pager, mobile, fax
+view OTHER = url, birthday
+
+# Automatically save database on exit
+set autosave=true
+
diff --git a/home-manager/tests/modules/programs/abook/with-settings.nix b/home-manager/tests/modules/programs/abook/with-settings.nix
new file mode 100644
index 00000000000..3cb02a2666c
--- /dev/null
+++ b/home-manager/tests/modules/programs/abook/with-settings.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.abook = {
+ enable = true;
+
+ extraConfig = ''
+ # Defining a new custom field
+ # -----------------------------
+
+ field pager = Pager
+ field address_lines = Address, list
+ field birthday = Birthday, date
+
+ # Defining a view/tab
+ # ---------------------
+
+ view CONTACT = name, email
+ view ADDRESS = address_lines, city, state, zip, country
+ view PHONE = phone, workphone, pager, mobile, fax
+ view OTHER = url, birthday
+
+ # Automatically save database on exit
+ set autosave=true
+ '';
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { abook = pkgs.writeScriptBin "dummy-abook" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/abook/abookrc
+ assertFileContent home-files/.config/abook/abookrc ${./with-settings.cfg}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/alacritty/example-settings.nix b/home-manager/tests/modules/programs/alacritty/example-settings.nix
index 46be1064ce6..c3671aa0ca3 100644
--- a/home-manager/tests/modules/programs/alacritty/example-settings.nix
+++ b/home-manager/tests/modules/programs/alacritty/example-settings.nix
@@ -6,6 +6,7 @@ with lib;
config = {
programs.alacritty = {
enable = true;
+ package = pkgs.writeScriptBin "dummy-alacritty" "";
settings = {
window.dimensions = {
@@ -21,10 +22,6 @@ with lib;
};
};
- nixpkgs.overlays = [
- (self: super: { alacritty = pkgs.writeScriptBin "dummy-alacritty" ""; })
- ];
-
nmt.script = ''
assertFileContent \
home-files/.config/alacritty/alacritty.yml \
diff --git a/home-manager/tests/modules/programs/alot/alot-expected.conf b/home-manager/tests/modules/programs/alot/alot-expected.conf
new file mode 100644
index 00000000000..6d3ace4a372
--- /dev/null
+++ b/home-manager/tests/modules/programs/alot/alot-expected.conf
@@ -0,0 +1,37 @@
+# Generated by Home Manager.
+# See http://alot.readthedocs.io/en/latest/configuration/config_options.html
+
+auto_remove_unread = True
+handle_mouse = True
+initial_command = search tag:inbox AND NOT tag:killed
+prefer_plaintext = True
+
+
+[tags]
+[bindings]
+
+
+[[bufferlist]]
+
+[[search]]
+
+[[envelope]]
+
+[[taglist]]
+
+[[thread]]
+
+
+[accounts]
+
+[[hm@example.com]]
+address=hm@example.com
+draft_box=maildir:///home/hm-user/Mail/hm@example.com/Drafts
+realname=H. M. Test
+sendmail_command=
+sent_box=maildir:///home/hm-user/Mail/hm@example.com/Sent
+auto_remove_unread = True
+ask_subject = False
+handle_mouse = True
+
+[[[abook]]]
diff --git a/home-manager/tests/modules/programs/alot/alot.nix b/home-manager/tests/modules/programs/alot/alot.nix
new file mode 100644
index 00000000000..40028b7aac6
--- /dev/null
+++ b/home-manager/tests/modules/programs/alot/alot.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports = [ ../../accounts/email-test-accounts.nix ];
+
+ config = {
+ accounts.email.accounts = {
+ "hm@example.com" = {
+ primary = true;
+ notmuch.enable = true;
+ alot = {
+ contactCompletion = { };
+ extraConfig = ''
+ auto_remove_unread = True
+ ask_subject = False
+ handle_mouse = True
+ '';
+ };
+ imap.port = 993;
+ };
+ };
+
+ programs.alot = { enable = true; };
+
+ nixpkgs.overlays =
+ [ (self: super: { alot = pkgs.writeScriptBin "dummy-alot" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/alot/config
+ assertFileContent home-files/.config/alot/config ${./alot-expected.conf}
+ '';
+ };
+}
+
diff --git a/home-manager/tests/modules/programs/alot/default.nix b/home-manager/tests/modules/programs/alot/default.nix
new file mode 100644
index 00000000000..9c912fdb43a
--- /dev/null
+++ b/home-manager/tests/modules/programs/alot/default.nix
@@ -0,0 +1 @@
+{ alot = ./alot.nix; }
diff --git a/home-manager/tests/modules/programs/aria2/default.nix b/home-manager/tests/modules/programs/aria2/default.nix
new file mode 100644
index 00000000000..2964841d098
--- /dev/null
+++ b/home-manager/tests/modules/programs/aria2/default.nix
@@ -0,0 +1 @@
+{ aria2-settings = ./settings.nix; }
diff --git a/home-manager/tests/modules/programs/aria2/settings.nix b/home-manager/tests/modules/programs/aria2/settings.nix
new file mode 100644
index 00000000000..0b5a52d90c5
--- /dev/null
+++ b/home-manager/tests/modules/programs/aria2/settings.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.aria2 = {
+ enable = true;
+
+ settings = {
+ listen-port = 60000;
+ dht-listen-port = 60000;
+ seed-ratio = 1.0;
+ max-upload-limit = "50K";
+ ftp-pasv = true;
+ };
+
+ extraConfig = ''
+ # Extra aria2 configuration.
+ '';
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { aria2 = pkgs.writeScriptBin "dummy-aria2" ""; }) ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/aria2/aria2.conf \
+ ${
+ pkgs.writeText "aria2-expected-config.conf" ''
+ dht-listen-port=60000
+ ftp-pasv=true
+ listen-port=60000
+ max-upload-limit=50K
+ seed-ratio=1.000000
+ # Extra aria2 configuration.
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/autorandr/basic-configuration.conf b/home-manager/tests/modules/programs/autorandr/basic-configuration.conf
new file mode 100644
index 00000000000..681574cf204
--- /dev/null
+++ b/home-manager/tests/modules/programs/autorandr/basic-configuration.conf
@@ -0,0 +1,10 @@
+output DP1
+off
+
+output DP2
+pos 0x0
+crtc 0
+primary
+mode 1920x1080
+transform 0.600000,0.000000,0.000000,0.000000,0.600000,0.000000,0.000000,0.000000,1.000000
+scale 2x4 \ No newline at end of file
diff --git a/home-manager/tests/modules/programs/autorandr/basic-configuration.nix b/home-manager/tests/modules/programs/autorandr/basic-configuration.nix
new file mode 100644
index 00000000000..190511016fe
--- /dev/null
+++ b/home-manager/tests/modules/programs/autorandr/basic-configuration.nix
@@ -0,0 +1,48 @@
+{ config, pkgs, ... }:
+
+{
+ config = {
+ programs.autorandr = {
+ enable = true;
+ profiles = {
+ default = {
+ fingerprint = {
+ DP1 = "XXX";
+ DP2 = "YYY";
+ };
+ config = {
+ DP1.enable = false;
+ DP2 = {
+ crtc = 0;
+ primary = true;
+ position = "0x0";
+ mode = "1920x1080";
+ scale = {
+ x = 2;
+ y = 4;
+ };
+ transform = [
+ [ 0.6 0.0 0.0 ] # a b c
+ [ 0.0 0.6 0.0 ] # d e f
+ [ 0.0 0.0 1.0 ] # g h i
+ ];
+ };
+ };
+ };
+ };
+ };
+
+ nmt.script = ''
+ config=home-files/.config/autorandr/default/config
+ setup=home-files/.config/autorandr/default/setup
+
+ assertFileExists $setup
+ assertFileRegex $setup 'DP1 XXX'
+ assertFileRegex $setup 'DP2 YYY'
+
+ assertFileExists $config
+ assertFileContent $config \
+ ${./basic-configuration.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/autorandr/default.nix b/home-manager/tests/modules/programs/autorandr/default.nix
new file mode 100644
index 00000000000..5f12d409380
--- /dev/null
+++ b/home-manager/tests/modules/programs/autorandr/default.nix
@@ -0,0 +1 @@
+{ autorandr-basic-configuration = ./basic-configuration.nix; }
diff --git a/home-manager/tests/modules/programs/bash/session-variables-expected.txt b/home-manager/tests/modules/programs/bash/session-variables-expected.txt
index e13d63d4c78..0d93217a915 100644
--- a/home-manager/tests/modules/programs/bash/session-variables-expected.txt
+++ b/home-manager/tests/modules/programs/bash/session-variables-expected.txt
@@ -1,6 +1,6 @@
# -*- mode: sh -*-
-. "/home/testuser/.nix-profile/etc/profile.d/hm-session-vars.sh"
+. "/home/hm-user/.nix-profile/etc/profile.d/hm-session-vars.sh"
export V1="v1"
export V2="v2-v1"
diff --git a/home-manager/tests/modules/programs/bash/session-variables.nix b/home-manager/tests/modules/programs/bash/session-variables.nix
index ea789a1d061..1ef65a34442 100644
--- a/home-manager/tests/modules/programs/bash/session-variables.nix
+++ b/home-manager/tests/modules/programs/bash/session-variables.nix
@@ -13,8 +13,6 @@ with lib;
};
};
- home.homeDirectory = "/home/testuser";
-
nmt.script = ''
assertFileExists home-files/.profile
assertFileContent \
diff --git a/home-manager/tests/modules/programs/dircolors/default.nix b/home-manager/tests/modules/programs/dircolors/default.nix
new file mode 100644
index 00000000000..a82e2b859af
--- /dev/null
+++ b/home-manager/tests/modules/programs/dircolors/default.nix
@@ -0,0 +1 @@
+{ dircolors-settings = ./settings.nix; }
diff --git a/home-manager/tests/modules/programs/dircolors/settings-expected.conf b/home-manager/tests/modules/programs/dircolors/settings-expected.conf
new file mode 100644
index 00000000000..17bc247c0c2
--- /dev/null
+++ b/home-manager/tests/modules/programs/dircolors/settings-expected.conf
@@ -0,0 +1,133 @@
+# Extra dircolors configuration.
+
+.7z 01;31
+.aac 00;36
+.ace 01;31
+.alz 01;31
+.arc 01;31
+.arj 01;31
+.asf 01;35
+.au 00;36
+.avi 01;35
+.bmp 01;35
+.bz 01;31
+.bz2 01;31
+.cab 01;31
+.cgm 01;35
+.cpio 01;31
+.csh 01;32
+.deb 01;31
+.dl 01;35
+.dwm 01;31
+.dz 01;31
+.ear 01;31
+.emf 01;35
+.esd 01;31
+.flac 00;36
+.flc 01;35
+.fli 01;35
+.flv 01;35
+.gif 01;35
+.gl 01;35
+.gz 01;31
+.jar 01;31
+.jpeg 01;35
+.jpg 01;35
+.lha 01;31
+.lrz 01;31
+.lz 01;31
+.lz4 01;31
+.lzh 01;31
+.lzma 01;31
+.lzo 01;31
+.m2v 01;35
+.m4a 00;36
+.m4v 01;35
+.mid 00;36
+.midi 00;36
+.mjpeg 01;35
+.mjpg 01;35
+.mka 00;36
+.mkv 01;35
+.mng 01;35
+.mov 01;35
+.mp3 00;36
+.mp4 01;35
+.mp4v 01;35
+.mpc 00;36
+.mpeg 01;35
+.mpg 01;35
+.nuv 01;35
+.oga 00;36
+.ogg 00;36
+.ogm 01;35
+.ogv 01;35
+.ogx 01;35
+.opus 00;36
+.pbm 01;35
+.pcx 01;35
+.pgm 01;35
+.png 01;35
+.ppm 01;35
+.qt 01;35
+.ra 00;36
+.rar 01;31
+.rm 01;35
+.rmvb 01;35
+.rpm 01;31
+.rz 01;31
+.sar 01;31
+.sh 01;32
+.spx 00;36
+.svg 01;35
+.svgz 01;35
+.swm 01;31
+.t7z 01;31
+.tar 01;31
+.taz 01;31
+.tbz 01;31
+.tbz2 01;31
+.tga 01;35
+.tgz 01;31
+.tif 01;35
+.tiff 01;35
+.tlz 01;31
+.txz 01;31
+.tz 01;31
+.tzo 01;31
+.tzst 01;31
+.vob 01;35
+.war 01;31
+.wav 00;36
+.webm 01;35
+.wim 01;31
+.wmv 01;35
+.xbm 01;35
+.xcf 01;35
+.xpm 01;35
+.xspf 00;36
+.xwd 01;35
+.xz 01;31
+.yuv 01;35
+.z 01;31
+.zip 01;31
+.zoo 01;31
+.zst 01;31
+BLK 40;33;01
+CAPABILITY 30;41
+CHR 40;33;01
+DIR 01;34
+DOOR 01;35
+EXEC 01;32
+FIFO 40;33
+LINK 01;36
+MISSING 00
+MULTIHARDLINK 00
+ORPHAN 40;31;01
+OTHER_WRITABLE 30;46
+RESET 0
+SETGID 30;43
+SETUID 37;41
+SOCK 01;35
+STICKY 37;44
+STICKY_OTHER_WRITABLE 30;42
diff --git a/home-manager/tests/modules/programs/dircolors/settings.nix b/home-manager/tests/modules/programs/dircolors/settings.nix
new file mode 100644
index 00000000000..9ca676ef9c0
--- /dev/null
+++ b/home-manager/tests/modules/programs/dircolors/settings.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.dircolors = {
+ enable = true;
+
+ settings = {
+ OTHER_WRITABLE = "30;46";
+ ".sh" = "01;32";
+ ".csh" = "01;32";
+ };
+
+ extraConfig = ''
+ # Extra dircolors configuration.
+ '';
+ };
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.dir_colors \
+ ${./settings-expected.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/direnv/bash.nix b/home-manager/tests/modules/programs/direnv/bash.nix
new file mode 100644
index 00000000000..db0d6b391f5
--- /dev/null
+++ b/home-manager/tests/modules/programs/direnv/bash.nix
@@ -0,0 +1,17 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.bash.enable = true;
+ programs.direnv.enable = true;
+
+ nmt.script = ''
+ assertFileExists home-files/.bashrc
+ assertFileRegex \
+ home-files/.bashrc \
+ 'eval "\$(/nix/store/.*direnv.*/bin/direnv hook bash)"'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/direnv/default.nix b/home-manager/tests/modules/programs/direnv/default.nix
new file mode 100644
index 00000000000..3efad2b6945
--- /dev/null
+++ b/home-manager/tests/modules/programs/direnv/default.nix
@@ -0,0 +1,6 @@
+{
+ direnv-bash = ./bash.nix;
+ direnv-nix-direnv = ./nix-direnv.nix;
+ direnv-stdlib = ./stdlib.nix;
+ direnv-stdlib-and-nix-direnv = ./stdlib-and-nix-direnv.nix;
+}
diff --git a/home-manager/tests/modules/programs/direnv/nix-direnv.nix b/home-manager/tests/modules/programs/direnv/nix-direnv.nix
new file mode 100644
index 00000000000..57b3907dda8
--- /dev/null
+++ b/home-manager/tests/modules/programs/direnv/nix-direnv.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.bash.enable = true;
+ programs.direnv.enable = true;
+ programs.direnv.enableNixDirenvIntegration = true;
+
+ nmt.script = ''
+ assertFileExists home-files/.bashrc
+ assertFileRegex \
+ home-files/.config/direnv/direnvrc \
+ 'source /nix/store/.*nix-direnv.*/share/nix-direnv/direnvrc'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/direnv/stdlib-and-nix-direnv.nix b/home-manager/tests/modules/programs/direnv/stdlib-and-nix-direnv.nix
new file mode 100644
index 00000000000..1dc224317a2
--- /dev/null
+++ b/home-manager/tests/modules/programs/direnv/stdlib-and-nix-direnv.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let expectedContent = "something important";
+in {
+ config = {
+ programs.bash.enable = true;
+ programs.direnv.enable = true;
+ programs.direnv.enableNixDirenvIntegration = true;
+ programs.direnv.stdlib = expectedContent;
+
+ nmt.script = ''
+ assertFileExists home-files/.bashrc
+ assertFileRegex \
+ home-files/.config/direnv/direnvrc \
+ 'source /nix/store/.*nix-direnv.*/share/nix-direnv/direnvrc'
+ assertFileRegex \
+ home-files/.config/direnv/direnvrc \
+ '${expectedContent}'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/direnv/stdlib.nix b/home-manager/tests/modules/programs/direnv/stdlib.nix
new file mode 100644
index 00000000000..1d06a0bc2a9
--- /dev/null
+++ b/home-manager/tests/modules/programs/direnv/stdlib.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let expectedContent = "something important";
+in {
+ config = {
+ programs.bash.enable = true;
+ programs.direnv.enable = true;
+ programs.direnv.stdlib = expectedContent;
+
+ nmt.script = ''
+ assertFileExists home-files/.bashrc
+ assertFileRegex \
+ home-files/.config/direnv/direnvrc \
+ '${expectedContent}'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/fish/default.nix b/home-manager/tests/modules/programs/fish/default.nix
new file mode 100644
index 00000000000..99fe8136700
--- /dev/null
+++ b/home-manager/tests/modules/programs/fish/default.nix
@@ -0,0 +1,5 @@
+{
+ fish-functions = ./functions.nix;
+ fish-no-functions = ./no-functions.nix;
+ fish-plugins = ./plugins.nix;
+}
diff --git a/home-manager/tests/modules/programs/fish/functions.nix b/home-manager/tests/modules/programs/fish/functions.nix
new file mode 100644
index 00000000000..424d0a288c7
--- /dev/null
+++ b/home-manager/tests/modules/programs/fish/functions.nix
@@ -0,0 +1,48 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ func = pkgs.writeText "func.fish" ''
+ function func
+ echo "Hello"
+ end
+ '';
+
+ funcEvent = pkgs.writeText "func-event.fish" ''
+ function func-event --on-event="fish_command_not_found"
+ echo "Not found!"
+ end
+ '';
+
+in {
+ config = {
+ programs.fish = {
+ enable = true;
+
+ functions = {
+ func = ''echo "Hello"'';
+ func-event = {
+ body = ''echo "Not found!"'';
+ onEvent = "fish_command_not_found";
+ };
+ };
+ };
+
+ nmt = {
+ description =
+ "if fish.function is set, check file exists and contents match";
+ script = ''
+ assertFileExists home-files/.config/fish/functions/func.fish
+ echo ${func}
+ assertFileContent home-files/.config/fish/functions/func.fish ${func}
+
+ assertFileExists home-files/.config/fish/functions/func-event.fish
+ echo ${funcEvent}
+ assertFileContent home-files/.config/fish/functions/func-event.fish ${funcEvent}
+ '';
+
+ };
+ };
+}
diff --git a/home-manager/tests/modules/programs/fish/no-functions.nix b/home-manager/tests/modules/programs/fish/no-functions.nix
new file mode 100644
index 00000000000..c817b388953
--- /dev/null
+++ b/home-manager/tests/modules/programs/fish/no-functions.nix
@@ -0,0 +1,22 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.fish = {
+ enable = true;
+
+ functions = { };
+ };
+
+ nmt = {
+ description =
+ "if fish.functions is blank, the functions folder should not exist.";
+ script = ''
+ assertPathNotExists home-files/.config/fish/functions
+ '';
+
+ };
+ };
+}
diff --git a/home-manager/tests/modules/programs/fish/plugins.nix b/home-manager/tests/modules/programs/fish/plugins.nix
new file mode 100644
index 00000000000..657c33f39bf
--- /dev/null
+++ b/home-manager/tests/modules/programs/fish/plugins.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ fooPluginSrc = pkgs.writeText "fooPluginSrc" "";
+
+ generatedConfdFile = pkgs.writeText "plugin-foo.fish" ''
+ # Plugin foo
+ set -l plugin_dir ${fooPluginSrc}
+
+ # Set paths to import plugin components
+ if test -d $plugin_dir/functions
+ set fish_function_path $fish_function_path[1] $plugin_dir/functions $fish_function_path[2..-1]
+ end
+
+ if test -d $plugin_dir/completions
+ set fish_complete_path $fish_complete_path[1] $plugin_dir/completions $fish_complete_path[2..-1]
+ end
+
+ # Source initialization code if it exists.
+ if test -d $plugin_dir/conf.d
+ for f in $plugin_dir/conf.d/*.fish
+ source $f
+ end
+ end
+
+ if test -f $plugin_dir/key_bindings.fish
+ source $plugin_dir/key_bindings.fish
+ end
+
+ if test -f $plugin_dir/init.fish
+ source $plugin_dir/init.fish
+ end
+ '';
+
+in {
+ config = {
+ programs.fish = {
+ enable = true;
+
+ plugins = [{
+ name = "foo";
+ src = fooPluginSrc;
+ }];
+ };
+
+ nmt = {
+ description =
+ "if fish.plugins set, check conf.d file exists and contents match";
+ script = ''
+ assertDirectoryExists home-files/.config/fish/conf.d
+ assertFileExists home-files/.config/fish/conf.d/plugin-foo.fish
+ assertFileContent home-files/.config/fish/conf.d/plugin-foo.fish ${generatedConfdFile}
+ '';
+
+ };
+ };
+}
diff --git a/home-manager/tests/modules/programs/getmail/getmail.nix b/home-manager/tests/modules/programs/getmail/getmail.nix
index fe10b98f981..b0d979c4672 100644
--- a/home-manager/tests/modules/programs/getmail/getmail.nix
+++ b/home-manager/tests/modules/programs/getmail/getmail.nix
@@ -6,9 +6,6 @@ with lib;
imports = [ ../../accounts/email-test-accounts.nix ];
config = {
- home.username = "hm-user";
- home.homeDirectory = "/home/hm-user";
-
accounts.email.accounts = {
"hm@example.com" = {
getmail = {
diff --git a/home-manager/tests/modules/programs/git/git-expected-include.conf b/home-manager/tests/modules/programs/git/git-expected-include.conf
new file mode 100644
index 00000000000..f05c7b6c7fc
--- /dev/null
+++ b/home-manager/tests/modules/programs/git/git-expected-include.conf
@@ -0,0 +1,3 @@
+[user]
+ email = "user@example.org"
+ name = "John Doe"
diff --git a/home-manager/tests/modules/programs/git/git-expected.conf b/home-manager/tests/modules/programs/git/git-expected.conf
index d02ebf31649..c3e534af058 100644
--- a/home-manager/tests/modules/programs/git/git-expected.conf
+++ b/home-manager/tests/modules/programs/git/git-expected.conf
@@ -1,42 +1,58 @@
[alias]
-a1=foo
-a2=baz
+ a1 = "foo"
+ a2 = "baz"
+ escapes = "\"\\n\t"
[commit]
-gpgSign=true
+ gpgSign = true
+
+[core]
+ pager = "@deltaCommand@"
+
+[delta]
+ features = "decorations"
+ whitespace-error-style = "22 reverse"
+
+[delta "decorations"]
+ commit-decoration-style = "bold yellow box ul"
+ file-decoration-style = "none"
+ file-style = "bold yellow ul"
[extra]
-boolean=true
-integer=38
-multiple=1
-multiple=2
-name=value
+ boolean = true
+ integer = 38
+ multiple = 1
+ multiple = 2
+ name = "value"
[extra "backcompat.with.dots"]
-previously=worked
+ previously = "worked"
[extra "subsection"]
-value=test
+ value = "test"
[filter "lfs"]
-clean=git-lfs clean -- %f
-process=git-lfs filter-process
-required=true
-smudge=git-lfs smudge -- %f
+ clean = "git-lfs clean -- %f"
+ process = "git-lfs filter-process"
+ required = true
+ smudge = "git-lfs smudge -- %f"
[gpg]
-program=path-to-gpg
+ program = "path-to-gpg"
+
+[interactive]
+ diffFilter = "@deltaCommand@ --color-only"
[user]
-email=user@example.org
-name=John Doe
-signingKey=00112233445566778899AABBCCDDEEFF
+ email = "user@example.org"
+ name = "John Doe"
+ signingKey = "00112233445566778899AABBCCDDEEFF"
[include]
-path=~/path/to/config.inc
+ path = "~/path/to/config.inc"
[includeIf "gitdir:~/src/dir"]
-path=~/path/to/conditional.inc
+ path = "~/path/to/conditional.inc"
[includeIf "gitdir:~/src/dir"]
-path=@git_include_path@
+ path = "@git_include_path@"
diff --git a/home-manager/tests/modules/programs/git/git-with-email-expected.conf b/home-manager/tests/modules/programs/git/git-with-email-expected.conf
index 01c1eec5823..f48b7c33334 100644
--- a/home-manager/tests/modules/programs/git/git-with-email-expected.conf
+++ b/home-manager/tests/modules/programs/git/git-with-email-expected.conf
@@ -1,15 +1,15 @@
[sendemail "hm-account"]
-from=hm@example.org
-smtpEncryption=tls
-smtpServer=smtp.example.org
-smtpUser=home.manager.jr
+ from = "hm@example.org"
+ smtpEncryption = "tls"
+ smtpServer = "smtp.example.org"
+ smtpUser = "home.manager.jr"
[sendemail "hm@example.com"]
-from=hm@example.com
-smtpEncryption=tls
-smtpServer=smtp.example.com
-smtpUser=home.manager
+ from = "hm@example.com"
+ smtpEncryption = "ssl"
+ smtpServer = "smtp.example.com"
+ smtpUser = "home.manager"
[user]
-email=hm@example.com
-name=H. M. Test
+ email = "hm@example.com"
+ name = "H. M. Test"
diff --git a/home-manager/tests/modules/programs/git/git-with-email.nix b/home-manager/tests/modules/programs/git/git-with-email.nix
index ca577eef4d3..d7ed7e185de 100644
--- a/home-manager/tests/modules/programs/git/git-with-email.nix
+++ b/home-manager/tests/modules/programs/git/git-with-email.nix
@@ -13,6 +13,8 @@ with lib;
userName = "H. M. Test";
};
+ home.stateVersion = "20.09";
+
nmt.script = ''
function assertGitConfig() {
local value
diff --git a/home-manager/tests/modules/programs/git/git-with-str-extra-config-expected.conf b/home-manager/tests/modules/programs/git/git-with-str-extra-config-expected.conf
index 957438de13a..071268e831c 100644
--- a/home-manager/tests/modules/programs/git/git-with-str-extra-config-expected.conf
+++ b/home-manager/tests/modules/programs/git/git-with-str-extra-config-expected.conf
@@ -1,5 +1,5 @@
This can be anything.
[user]
-email=user@example.org
-name=John Doe
+ email = "user@example.org"
+ name = "John Doe"
diff --git a/home-manager/tests/modules/programs/git/git.nix b/home-manager/tests/modules/programs/git/git.nix
index 7c0bf52de55..feefff54b61 100644
--- a/home-manager/tests/modules/programs/git/git.nix
+++ b/home-manager/tests/modules/programs/git/git.nix
@@ -15,8 +15,10 @@ let
pkgs.substituteAll {
src = path;
- git_include_path =
- pkgs.writeText "contents" (generators.toINI { } gitInclude);
+ deltaCommand = "${pkgs.gitAndTools.delta}/bin/delta";
+
+ git_include_path = pkgs.writeText "contents"
+ (builtins.readFile ./git-expected-include.conf);
};
in {
@@ -28,6 +30,7 @@ in {
aliases = {
a1 = "foo";
a2 = "bar";
+ escapes = ''"\n '';
};
extraConfig = {
extra = {
@@ -55,6 +58,18 @@ in {
userEmail = "user@example.org";
userName = "John Doe";
lfs.enable = true;
+ delta = {
+ enable = true;
+ options = {
+ features = "decorations";
+ whitespace-error-style = "22 reverse";
+ decorations = {
+ commit-decoration-style = "bold yellow box ul";
+ file-style = "bold yellow ul";
+ file-decoration-style = "none";
+ };
+ };
+ };
}
{
diff --git a/home-manager/tests/modules/programs/i3status/default.nix b/home-manager/tests/modules/programs/i3status/default.nix
new file mode 100644
index 00000000000..c8d55755861
--- /dev/null
+++ b/home-manager/tests/modules/programs/i3status/default.nix
@@ -0,0 +1,4 @@
+{
+ i3status-with-custom = ./with-custom.nix;
+ i3status-with-default = ./with-default.nix;
+}
diff --git a/home-manager/tests/modules/programs/i3status/with-custom.nix b/home-manager/tests/modules/programs/i3status/with-custom.nix
new file mode 100644
index 00000000000..4aa01773e4e
--- /dev/null
+++ b/home-manager/tests/modules/programs/i3status/with-custom.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.i3status = {
+ enable = true;
+ enableDefault = false;
+
+ general = {
+ colors = true;
+ color_good = "#e0e0e0";
+ color_degraded = "#d7ae00";
+ color_bad = "#f69d6a";
+ interval = 1;
+ };
+
+ modules = {
+ "volume master" = {
+ position = 1;
+ settings = {
+ format = "♪ %volume";
+ format_muted = "♪ muted (%volume)";
+ device = "pulse:1";
+ };
+ };
+ "disk /" = {
+ position = 2;
+ settings = { format = "/ %avail"; };
+ };
+ };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: { i3status = pkgs.writeScriptBin "dummy-i3status" ""; })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/i3status/config \
+ ${
+ pkgs.writeText "i3status-expected-config" ''
+ general {
+ color_bad = "#f69d6a"
+ color_degraded = "#d7ae00"
+ color_good = "#e0e0e0"
+ colors = true
+ interval = 1
+ }
+
+ order += "volume master"
+ order += "disk /"
+ disk / {
+ format = "/ %avail"
+ }
+
+ volume master {
+ device = "pulse:1"
+ format = "♪ %volume"
+ format_muted = "♪ muted (%volume)"
+ }
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/i3status/with-default.nix b/home-manager/tests/modules/programs/i3status/with-default.nix
new file mode 100644
index 00000000000..0b7e4ee2fac
--- /dev/null
+++ b/home-manager/tests/modules/programs/i3status/with-default.nix
@@ -0,0 +1,73 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.i3status = {
+ enable = true;
+ enableDefault = true;
+ };
+
+ nixpkgs.overlays = [
+ (self: super: { i3status = pkgs.writeScriptBin "dummy-i3status" ""; })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/i3status/config \
+ ${
+ pkgs.writeText "i3status-expected-config" ''
+ general {
+ colors = true
+ interval = 5
+ }
+
+ order += "ipv6"
+ order += "wireless _first_"
+ order += "ethernet _first_"
+ order += "battery all"
+ order += "disk /"
+ order += "load"
+ order += "memory"
+ order += "tztime local"
+ battery all {
+ format = "%status %percentage %remaining"
+ }
+
+ disk / {
+ format = "%avail"
+ }
+
+ ethernet _first_ {
+ format_down = "E: down"
+ format_up = "E: %ip (%speed)"
+ }
+
+ ipv6 {
+
+ }
+
+ load {
+ format = "%1min"
+ }
+
+ memory {
+ format = "%used | %available"
+ format_degraded = "MEMORY < %available"
+ threshold_degraded = "1G"
+ }
+
+ tztime local {
+ format = "%Y-%m-%d %H:%M:%S"
+ }
+
+ wireless _first_ {
+ format_down = "W: down"
+ format_up = "W: (%quality at %essid) %ip"
+ }
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/kakoune/default.nix b/home-manager/tests/modules/programs/kakoune/default.nix
new file mode 100644
index 00000000000..1e6e077df1f
--- /dev/null
+++ b/home-manager/tests/modules/programs/kakoune/default.nix
@@ -0,0 +1,7 @@
+{
+ kakoune-no-plugins = ./no-plugins.nix;
+ kakoune-use-plugins = ./use-plugins.nix;
+ kakoune-whitespace-highlighter = ./whitespace-highlighter.nix;
+ kakoune-whitespace-highlighter-corner-cases =
+ ./whitespace-highlighter-corner-cases.nix;
+}
diff --git a/home-manager/tests/modules/programs/kakoune/no-plugins.nix b/home-manager/tests/modules/programs/kakoune/no-plugins.nix
new file mode 100644
index 00000000000..76dea5440ac
--- /dev/null
+++ b/home-manager/tests/modules/programs/kakoune/no-plugins.nix
@@ -0,0 +1,13 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.kakoune = { enable = true; };
+
+ nmt.script = ''
+ assertFileNotRegex home-path/share/kak/plugins.kak . # file is empty
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/kakoune/use-plugins.nix b/home-manager/tests/modules/programs/kakoune/use-plugins.nix
new file mode 100644
index 00000000000..7b23627dbbd
--- /dev/null
+++ b/home-manager/tests/modules/programs/kakoune/use-plugins.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.kakoune = {
+ enable = true;
+ plugins = [ pkgs.kakounePlugins.kak-powerline ];
+ };
+
+ nmt.script = let plugins_kak = "home-path/share/kak/plugins.kak";
+ in ''
+ assertFileRegex ${plugins_kak} \
+ '^source "/nix/store/.*-kak-powerline/share/kak/autoload/plugins/powerline/.*.kak"$'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/kakoune/whitespace-highlighter-corner-cases.nix b/home-manager/tests/modules/programs/kakoune/whitespace-highlighter-corner-cases.nix
new file mode 100644
index 00000000000..142aaac8fe3
--- /dev/null
+++ b/home-manager/tests/modules/programs/kakoune/whitespace-highlighter-corner-cases.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.kakoune = {
+ enable = true;
+ config.showWhitespace = {
+ enable = true;
+ lineFeed = ''"'';
+ space = " ";
+ nonBreakingSpace = "' '"; # backwards compat
+ tab = "'";
+ # tabStop = <default>
+ };
+ };
+
+ nmt.script = ''
+ assertFileExists home-files/.config/kak/kakrc
+ assertFileContains home-files/.config/kak/kakrc \
+ "add-highlighter global/ show-whitespaces -tab \"'\" -spc ' ' -nbsp ' ' -lf '\"'"
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/kakoune/whitespace-highlighter.nix b/home-manager/tests/modules/programs/kakoune/whitespace-highlighter.nix
new file mode 100644
index 00000000000..514c26a118b
--- /dev/null
+++ b/home-manager/tests/modules/programs/kakoune/whitespace-highlighter.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.kakoune = {
+ enable = true;
+ config.showWhitespace = {
+ enable = true;
+ lineFeed = "1";
+ space = "2";
+ nonBreakingSpace = "3";
+ tab = "4";
+ tabStop = "5";
+ };
+ };
+
+ nmt.script = ''
+ assertFileExists home-files/.config/kak/kakrc
+ assertFileContains home-files/.config/kak/kakrc \
+ "add-highlighter global/ show-whitespaces -tab '4' -tabpad '5' -spc '2' -nbsp '3' -lf '1'"
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/lf/all-options.nix b/home-manager/tests/modules/programs/lf/all-options.nix
new file mode 100644
index 00000000000..a25467a26b7
--- /dev/null
+++ b/home-manager/tests/modules/programs/lf/all-options.nix
@@ -0,0 +1,86 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ pvScript = builtins.toFile "pv.sh" "cat $1";
+ expected = builtins.toFile "settings-expected" ''
+ set icons
+ set noignorecase
+ set ratios "2:2:3"
+ set tabstop 4
+
+ cmd added :echo "foo"
+ cmd multiline :{{
+ push gg
+ echo "bar"
+ push i
+ }}
+ cmd removed
+
+ map aa should-be-added
+ map ab
+
+ cmap <c-a> should-be-added
+ cmap <c-b>
+
+ set previewer ${pvScript}
+ map i ${"$"}${pvScript} "$f" | less -R
+
+
+
+ # More config...
+
+ '';
+in {
+ config = {
+ programs.lf = {
+ enable = true;
+
+ cmdKeybindings = {
+ "<c-a>" = "should-be-added";
+ "<c-b>" = null;
+ };
+
+ commands = {
+ added = '':echo "foo"'';
+ removed = null;
+ multiline = ''
+ :{{
+ push gg
+ echo "bar"
+ push i
+ }}'';
+ };
+
+ extraConfig = ''
+ # More config...
+ '';
+
+ keybindings = {
+ aa = "should-be-added";
+ ab = null;
+ };
+
+ previewer = {
+ keybinding = "i";
+ source = pvScript;
+ };
+
+ settings = {
+ ignorecase = false;
+ icons = true;
+ tabstop = 4;
+ ratios = "2:2:3";
+ };
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/lf/lfrc
+ assertFileContent home-files/.config/lf/lfrc ${expected}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/lf/default.nix b/home-manager/tests/modules/programs/lf/default.nix
new file mode 100644
index 00000000000..65cf24fcf60
--- /dev/null
+++ b/home-manager/tests/modules/programs/lf/default.nix
@@ -0,0 +1,5 @@
+{
+ lf-all-options = ./all-options.nix;
+ lf-minimal-options = ./minimal-options.nix;
+ lf-no-pv-keybind = ./no-pv-keybind.nix;
+}
diff --git a/home-manager/tests/modules/programs/lf/minimal-options.nix b/home-manager/tests/modules/programs/lf/minimal-options.nix
new file mode 100644
index 00000000000..b3c26ba9b57
--- /dev/null
+++ b/home-manager/tests/modules/programs/lf/minimal-options.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let expected = builtins.toFile "settings-expected" "\n\n\n\n\n\n\n\n\n\n\n";
+in {
+ config = {
+ programs.lf = { enable = true; };
+
+ nixpkgs.overlays =
+ [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/lf/lfrc
+ assertFileContent home-files/.config/lf/lfrc ${expected}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/lf/no-pv-keybind.nix b/home-manager/tests/modules/programs/lf/no-pv-keybind.nix
new file mode 100644
index 00000000000..524a41a3643
--- /dev/null
+++ b/home-manager/tests/modules/programs/lf/no-pv-keybind.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ pvScript = builtins.toFile "pv.sh" "cat $1";
+ expected = builtins.toFile "settings-expected" ''
+
+
+
+
+
+
+
+
+ set previewer ${pvScript}
+
+
+
+ # More config...
+
+ '';
+in {
+ config = {
+ programs.lf = {
+ enable = true;
+
+ extraConfig = ''
+ # More config...
+ '';
+
+ previewer = { source = pvScript; };
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/lf/lfrc
+ assertFileContent home-files/.config/lf/lfrc ${expected}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/lieer/default.nix b/home-manager/tests/modules/programs/lieer/default.nix
new file mode 100644
index 00000000000..16f8627cf0d
--- /dev/null
+++ b/home-manager/tests/modules/programs/lieer/default.nix
@@ -0,0 +1 @@
+{ lieer = ./lieer.nix; }
diff --git a/home-manager/tests/modules/programs/lieer/lieer-expected.json b/home-manager/tests/modules/programs/lieer/lieer-expected.json
new file mode 100644
index 00000000000..e7318f65dc3
--- /dev/null
+++ b/home-manager/tests/modules/programs/lieer/lieer-expected.json
@@ -0,0 +1 @@
+{"account":"hm@example.com","drop_non_existing_label":false,"ignore_remote_labels":["CATEGORY_FORUMS","CATEGORY_PROMOTIONS","CATEGORY_UPDATES","CATEGORY_SOCIAL","CATEGORY_PERSONAL"],"ignore_tags":[],"replace_slash_with_dot":false,"timeout":0}
diff --git a/home-manager/tests/modules/programs/lieer/lieer.nix b/home-manager/tests/modules/programs/lieer/lieer.nix
new file mode 100644
index 00000000000..2ce4fb4e031
--- /dev/null
+++ b/home-manager/tests/modules/programs/lieer/lieer.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports = [ ../../accounts/email-test-accounts.nix ];
+
+ config = {
+ programs.lieer.enable = true;
+
+ accounts.email.accounts = { "hm@example.com".lieer.enable = true; };
+
+ nixpkgs.overlays = [
+ (self: super: { gmailieer = pkgs.writeScriptBin "dummy-gmailieer" ""; })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/Mail/hm@example.com/.gmailieer.json
+ assertFileContent home-files/Mail/hm@example.com/.gmailieer.json \
+ ${./lieer-expected.json}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/man/apropos.nix b/home-manager/tests/modules/programs/man/apropos.nix
new file mode 100644
index 00000000000..a8ec35ead92
--- /dev/null
+++ b/home-manager/tests/modules/programs/man/apropos.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.man = {
+ enable = true;
+ generateCaches = true;
+ };
+
+ nmt.script = ''
+ assertFileExists home-files/.manpath
+
+ CACHE_DIR=$(cat $TESTED/home-files/.manpath | cut --delimiter=' ' --fields=3)
+
+ if [[ ! -f "$CACHE_DIR/index.bt" ]]; then
+ fail "Expected man cache files to exist (in $CACHE_DIR) but they were not found."
+ fi
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/man/default.nix b/home-manager/tests/modules/programs/man/default.nix
new file mode 100644
index 00000000000..2e9d340f98d
--- /dev/null
+++ b/home-manager/tests/modules/programs/man/default.nix
@@ -0,0 +1,4 @@
+{
+ man-apropos = ./apropos.nix;
+ man-no-manpath = ./no-manpath.nix;
+}
diff --git a/home-manager/tests/modules/programs/man/no-manpath.nix b/home-manager/tests/modules/programs/man/no-manpath.nix
new file mode 100644
index 00000000000..1b8cfb40c6e
--- /dev/null
+++ b/home-manager/tests/modules/programs/man/no-manpath.nix
@@ -0,0 +1,13 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.man = { enable = true; };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.manpath
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/mbsync/mbsync.nix b/home-manager/tests/modules/programs/mbsync/mbsync.nix
index 03a54c178f5..fa8e28cb4a6 100644
--- a/home-manager/tests/modules/programs/mbsync/mbsync.nix
+++ b/home-manager/tests/modules/programs/mbsync/mbsync.nix
@@ -6,9 +6,6 @@ with lib;
imports = [ ../../accounts/email-test-accounts.nix ];
config = {
- home.username = "hm-user";
- home.homeDirectory = "/home/hm-user";
-
programs.mbsync = {
enable = true;
groups.inboxes = {
diff --git a/home-manager/tests/modules/programs/ncmpcpp-linux/default.nix b/home-manager/tests/modules/programs/ncmpcpp-linux/default.nix
new file mode 100644
index 00000000000..b1185c85249
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp-linux/default.nix
@@ -0,0 +1 @@
+{ ncmpcpp-use-mpd-config = ./ncmpcpp-use-mpd-config.nix; }
diff --git a/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config-expected-config b/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config-expected-config
new file mode 100644
index 00000000000..8aa57d08f16
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config-expected-config
@@ -0,0 +1 @@
+mpd_music_dir=/home/user/music
diff --git a/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config.nix b/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config.nix
new file mode 100644
index 00000000000..5262f031472
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp-linux/ncmpcpp-use-mpd-config.nix
@@ -0,0 +1,25 @@
+{ pkgs, ... }:
+
+{
+ config = {
+ programs.ncmpcpp.enable = true;
+
+ services.mpd.enable = true;
+ services.mpd.musicDirectory = "/home/user/music";
+
+ nixpkgs.overlays = [
+ (self: super: {
+ ncmpcpp = pkgs.writeScriptBin "dummy-ncmpcpp" "";
+ mpd = pkgs.writeScriptBin "dummy-mpd" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/ncmpcpp/config \
+ ${./ncmpcpp-use-mpd-config-expected-config}
+
+ assertPathNotExists home-files/.config/ncmpcpp/bindings
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/ncmpcpp/default.nix b/home-manager/tests/modules/programs/ncmpcpp/default.nix
new file mode 100644
index 00000000000..c150b0d8248
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp/default.nix
@@ -0,0 +1,4 @@
+{
+ ncmpcpp-empty-settings = ./ncmpcpp-empty-settings.nix;
+ ncmpcpp-example-settings = ./ncmpcpp-example-settings.nix;
+}
diff --git a/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-empty-settings.nix b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-empty-settings.nix
new file mode 100644
index 00000000000..e5134002d3e
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-empty-settings.nix
@@ -0,0 +1,16 @@
+{ pkgs, ... }:
+
+{
+ config = {
+ programs.ncmpcpp.enable = true;
+
+ nixpkgs.overlays =
+ [ (self: super: { ncmpcpp = pkgs.writeScriptBin "dummy-ncmpcpp" ""; }) ];
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/ncmpcpp/config
+
+ assertPathNotExists home-files/.config/ncmpcpp/bindings
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-bindings b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-bindings
new file mode 100644
index 00000000000..a73bd129ffd
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-bindings
@@ -0,0 +1,16 @@
+def_key "j"
+ scroll_down
+def_key "k"
+ scroll_up
+def_key "J"
+ select_item
+ scroll_down
+def_key "K"
+ select_item
+ scroll_up
+def_key "x"
+ delete_playlist_items
+def_key "x"
+ delete_browser_items
+def_key "x"
+ delete_stored_playlist
diff --git a/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-config b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-config
new file mode 100644
index 00000000000..6aedb6110e4
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings-expected-config
@@ -0,0 +1,4 @@
+display_volume_level=no
+mpd_music_dir=/home/user/music
+playlist_disable_highlight_delay=0
+user_interface=alternative
diff --git a/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings.nix b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings.nix
new file mode 100644
index 00000000000..02a1f09c902
--- /dev/null
+++ b/home-manager/tests/modules/programs/ncmpcpp/ncmpcpp-example-settings.nix
@@ -0,0 +1,60 @@
+{ pkgs, ... }:
+
+{
+ config = {
+ programs.ncmpcpp = {
+ enable = true;
+ mpdMusicDir = "/home/user/music";
+
+ settings = {
+ user_interface = "alternative";
+ display_volume_level = false;
+ playlist_disable_highlight_delay = 0;
+ };
+
+ bindings = [
+ {
+ key = "j";
+ command = "scroll_down";
+ }
+ {
+ key = "k";
+ command = "scroll_up";
+ }
+ {
+ key = "J";
+ command = [ "select_item" "scroll_down" ];
+ }
+ {
+ key = "K";
+ command = [ "select_item" "scroll_up" ];
+ }
+ {
+ key = "x";
+ command = "delete_playlist_items";
+ }
+ {
+ key = "x";
+ command = "delete_browser_items";
+ }
+ {
+ key = "x";
+ command = "delete_stored_playlist";
+ }
+ ];
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { ncmpcpp = pkgs.writeScriptBin "dummy-ncmpcpp" ""; }) ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/ncmpcpp/config \
+ ${./ncmpcpp-example-settings-expected-config}
+
+ assertFileContent \
+ home-files/.config/ncmpcpp/bindings \
+ ${./ncmpcpp-example-settings-expected-bindings}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/ne/default.nix b/home-manager/tests/modules/programs/ne/default.nix
new file mode 100644
index 00000000000..7a1c843d432
--- /dev/null
+++ b/home-manager/tests/modules/programs/ne/default.nix
@@ -0,0 +1,4 @@
+{
+ ne-defprefs = ./defprefs.nix;
+ ne-passthroughs = ./passthroughs.nix;
+}
diff --git a/home-manager/tests/modules/programs/ne/defprefs.nix b/home-manager/tests/modules/programs/ne/defprefs.nix
new file mode 100644
index 00000000000..dce98b28648
--- /dev/null
+++ b/home-manager/tests/modules/programs/ne/defprefs.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ defpref = ''
+ defined through defaultPreferences
+ '';
+
+ autopref = ''
+ defined through automaticPreferences
+ '';
+
+in {
+ config = {
+ programs.ne = {
+ enable = true;
+ defaultPreferences = defpref;
+ automaticPreferences.".default" = autopref;
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { ne = pkgs.writeScriptBin "dummy-ne" ""; }) ];
+
+ nmt = {
+ description =
+ "Check that it gracefully handles the case of both defaultPreferences and automaticPreferences.'.default' being set, defaulting to the former.";
+ script = ''
+ assertFileExists home-files/.ne/.default#ap
+ assertFileContent home-files/.ne/.default#ap ${
+ builtins.toFile "defpref" defpref
+ }
+ '';
+ };
+ };
+}
diff --git a/home-manager/tests/modules/programs/ne/passthroughs.nix b/home-manager/tests/modules/programs/ne/passthroughs.nix
new file mode 100644
index 00000000000..4c129e94489
--- /dev/null
+++ b/home-manager/tests/modules/programs/ne/passthroughs.nix
@@ -0,0 +1,73 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ # Samples taken from the ne manual.
+ keybindings = ''
+ SEQ "\x1b[1;5D" 14A
+ KEY 14A HELP
+ '';
+
+ menus = ''
+ MENU "File"
+ ITEM "Open... ^O" Open
+ ITEM "Close " Close
+ ITEM "DoIt " Macro DoIt
+ '';
+
+ virtualExtensions = ''
+ sh 1 ^#!\s*/.*\b(bash|sh|ksh|zsh)\s*
+ csh 1 ^#!\s*/.*\b(csh|tcsh)\s*
+ pl 1 ^#!\s*/.*\bperl\b
+ py 1 ^#!\s*/.*\bpython[0-9]*\s*
+ rb 1 ^#!\s*/.*\bruby\s*
+ xml 1 ^<\?xml
+ '';
+
+ automaticPreferences = {
+ nix = ''
+ TAB 0
+ TS 2
+ '';
+ js = ''
+ TS 4
+ '';
+ };
+
+ checkFile = filename: contents: ''
+ assertFileExists home-files/.ne/${filename}
+ assertFileContent home-files/.ne/${filename} ${
+ builtins.toFile "checkFile" contents
+ }
+ '';
+
+in {
+ config = {
+ programs.ne = {
+ enable = true;
+ inherit keybindings;
+ inherit menus;
+ inherit virtualExtensions;
+ inherit automaticPreferences;
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { ne = pkgs.writeScriptBin "dummy-ne" ""; }) ];
+
+ nmt = {
+ description = "Check that configuration files are correctly written";
+ script = concatStringsSep "\n" [
+ (checkFile ".keys" keybindings)
+ (checkFile ".extensions" virtualExtensions)
+ (checkFile ".menus" menus)
+
+ # Generates a check command for each entry in automaticPreferences.
+ (concatStringsSep "\n" (mapAttrsToList
+ (extension: contents: checkFile "${extension}#ap" contents)
+ automaticPreferences))
+ ];
+ };
+ };
+}
diff --git a/home-manager/tests/modules/programs/neomutt/default.nix b/home-manager/tests/modules/programs/neomutt/default.nix
index 289f2705efa..aef9f37e02c 100644
--- a/home-manager/tests/modules/programs/neomutt/default.nix
+++ b/home-manager/tests/modules/programs/neomutt/default.nix
@@ -1 +1,4 @@
-{ neomutt-simple = ./neomutt.nix; }
+{
+ neomutt-simple = ./neomutt.nix;
+ neomutt-with-msmtp = ./neomutt-with-msmtp.nix;
+}
diff --git a/home-manager/tests/modules/programs/neomutt/hm-example.com-msmtp-expected.conf b/home-manager/tests/modules/programs/neomutt/hm-example.com-msmtp-expected.conf
new file mode 100644
index 00000000000..1850620f313
--- /dev/null
+++ b/home-manager/tests/modules/programs/neomutt/hm-example.com-msmtp-expected.conf
@@ -0,0 +1,33 @@
+# Generated by Home Manager.
+set ssl_force_tls = yes
+set certificate_file=/etc/ssl/certs/ca-certificates.crt
+
+# GPG section
+set crypt_use_gpgme = yes
+set crypt_autosign = no
+set pgp_use_gpg_agent = yes
+set mbox_type = Maildir
+set sort = "threads"
+
+# MTA section
+set sendmail='msmtpq --read-envelope-from --read-recipients'
+
+
+
+
+
+# MRA section
+set folder='/home/hm-user/Mail/hm@example.com'
+set from='hm@example.com'
+set postponed='+Drafts'
+set realname='H. M. Test'
+set record='+Sent'
+set spoolfile='+Inbox'
+set trash='+Trash'
+color status cyan default
+
+
+
+# Extra configuration
+color status cyan default
+
diff --git a/home-manager/tests/modules/programs/neomutt/neomutt-with-msmtp.nix b/home-manager/tests/modules/programs/neomutt/neomutt-with-msmtp.nix
new file mode 100644
index 00000000000..22f65599cf4
--- /dev/null
+++ b/home-manager/tests/modules/programs/neomutt/neomutt-with-msmtp.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports = [ ../../accounts/email-test-accounts.nix ];
+
+ config = {
+ accounts.email.accounts = {
+ "hm@example.com" = {
+ primary = true;
+ msmtp.enable = true;
+ neomutt = {
+ enable = true;
+ extraConfig = ''
+ color status cyan default
+ '';
+ };
+ imap.port = 993;
+ };
+ };
+
+ programs.neomutt.enable = true;
+
+ nixpkgs.overlays =
+ [ (self: super: { neomutt = pkgs.writeScriptBin "dummy-neomutt" ""; }) ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/neomutt/neomuttrc
+ assertFileExists home-files/.config/neomutt/hm@example.com
+ assertFileContent home-files/.config/neomutt/neomuttrc ${
+ ./neomutt-expected.conf
+ }
+ assertFileContent home-files/.config/neomutt/hm@example.com ${
+ ./hm-example.com-msmtp-expected.conf
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/neomutt/neomutt.nix b/home-manager/tests/modules/programs/neomutt/neomutt.nix
index 91cb9dca249..c0caa44af4a 100644
--- a/home-manager/tests/modules/programs/neomutt/neomutt.nix
+++ b/home-manager/tests/modules/programs/neomutt/neomutt.nix
@@ -6,11 +6,6 @@ with lib;
imports = [ ../../accounts/email-test-accounts.nix ];
config = {
- home.username = "hm-user";
- home.homeDirectory = "/home/hm-user";
- xdg.configHome = mkForce "/home/hm-user/.config";
- xdg.cacheHome = mkForce "/home/hm-user/.cache";
-
accounts.email.accounts = {
"hm@example.com" = {
primary = true;
diff --git a/home-manager/tests/modules/programs/newsboat/default.nix b/home-manager/tests/modules/programs/newsboat/default.nix
index 27f523a629c..f12f640ef29 100644
--- a/home-manager/tests/modules/programs/newsboat/default.nix
+++ b/home-manager/tests/modules/programs/newsboat/default.nix
@@ -1 +1,4 @@
-{ newsboat-basics = ./newsboat-basics.nix; }
+{
+ newsboat-basics = ./newsboat-basics.nix;
+ newsboat-basics-2003 = ./newsboat-basics-2003.nix;
+}
diff --git a/home-manager/tests/modules/programs/newsboat/newsboat-basics-2003.nix b/home-manager/tests/modules/programs/newsboat/newsboat-basics-2003.nix
new file mode 100644
index 00000000000..587a31bb255
--- /dev/null
+++ b/home-manager/tests/modules/programs/newsboat/newsboat-basics-2003.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ home.stateVersion = "20.03";
+
+ programs.newsboat = {
+ enable = true;
+
+ urls = [
+ {
+ url = "http://example.org/feed.xml";
+ tags = [ "tag1" "tag2" ];
+ title = "Cool feed";
+ }
+
+ { url = "http://example.org/feed2.xml"; }
+ ];
+
+ queries = { "foo" = ''rssurl =~ "example.com"''; };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: { newsboat = pkgs.writeScriptBin "dummy-newsboat" ""; })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.newsboat/urls \
+ ${./newsboat-basics-urls-2003.txt}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/newsboat/newsboat-basics-urls-2003.txt b/home-manager/tests/modules/programs/newsboat/newsboat-basics-urls-2003.txt
new file mode 100644
index 00000000000..68f81118053
--- /dev/null
+++ b/home-manager/tests/modules/programs/newsboat/newsboat-basics-urls-2003.txt
@@ -0,0 +1,3 @@
+"query:foo:rssurl =~ \"example.com\""
+http://example.org/feed.xml "tag1" "tag2" "~Cool feed"
+http://example.org/feed2.xml
diff --git a/home-manager/tests/modules/programs/nushell/default.nix b/home-manager/tests/modules/programs/nushell/default.nix
new file mode 100644
index 00000000000..5b3bfa1a5bf
--- /dev/null
+++ b/home-manager/tests/modules/programs/nushell/default.nix
@@ -0,0 +1 @@
+{ nushell-settings = ./settings.nix; }
diff --git a/home-manager/tests/modules/programs/nushell/settings-expected.toml b/home-manager/tests/modules/programs/nushell/settings-expected.toml
new file mode 100644
index 00000000000..87c5de2500d
--- /dev/null
+++ b/home-manager/tests/modules/programs/nushell/settings-expected.toml
@@ -0,0 +1,5 @@
+completion_mode = "circular"
+edit_mode = "vi"
+key_timeout = 10
+no_auto_pivot = true
+startup = ["alias la [] { ls -a }", "alias e [msg] { echo $msg }"]
diff --git a/home-manager/tests/modules/programs/nushell/settings.nix b/home-manager/tests/modules/programs/nushell/settings.nix
new file mode 100644
index 00000000000..09c192b3ba3
--- /dev/null
+++ b/home-manager/tests/modules/programs/nushell/settings.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.nushell = {
+ enable = true;
+
+ settings = mkMerge [
+ {
+ edit_mode = "vi";
+ startup = [ "alias la [] { ls -a }" ];
+ completion_mode = "circular";
+ key_timeout = 10;
+ }
+
+ {
+ startup = [ "alias e [msg] { echo $msg }" ];
+ no_auto_pivot = true;
+ }
+ ];
+ };
+
+ nixpkgs.overlays =
+ [ (self: super: { nushell = pkgs.writeScriptBin "dummy-nushell" ""; }) ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/nu/config.toml \
+ ${./settings-expected.toml}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/powerline-go/default.nix b/home-manager/tests/modules/programs/powerline-go/default.nix
new file mode 100644
index 00000000000..50febefbccd
--- /dev/null
+++ b/home-manager/tests/modules/programs/powerline-go/default.nix
@@ -0,0 +1 @@
+{ powerline-go-standard = ./standard.nix; }
diff --git a/home-manager/tests/modules/programs/powerline-go/standard.nix b/home-manager/tests/modules/programs/powerline-go/standard.nix
new file mode 100644
index 00000000000..b01fcb1a75d
--- /dev/null
+++ b/home-manager/tests/modules/programs/powerline-go/standard.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs = {
+ bash.enable = true;
+
+ powerline-go = {
+ enable = true;
+ newline = true;
+ modules = [ "nix-shell" ];
+ pathAliases = { "\\~/project/foo" = "prj-foo"; };
+ settings = {
+ ignore-repos = [ "/home/me/project1" "/home/me/project2" ];
+ };
+ };
+ };
+
+ nmt.script = ''
+ assertFileExists home-files/.bashrc
+ assertFileContains \
+ home-files/.bashrc \
+ '/bin/powerline-go -error $old_exit_status -modules nix-shell -newline -path-aliases \~/project/foo=prj-foo -ignore-repos /home/me/project1,/home/me/project2'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/qutebrowser/default.nix b/home-manager/tests/modules/programs/qutebrowser/default.nix
new file mode 100644
index 00000000000..581b4a5834e
--- /dev/null
+++ b/home-manager/tests/modules/programs/qutebrowser/default.nix
@@ -0,0 +1,4 @@
+{
+ qutebrowser-settings = ./settings.nix;
+ qutebrowser-keybindings = ./keybindings.nix;
+}
diff --git a/home-manager/tests/modules/programs/qutebrowser/keybindings.nix b/home-manager/tests/modules/programs/qutebrowser/keybindings.nix
new file mode 100644
index 00000000000..e89e44b46d9
--- /dev/null
+++ b/home-manager/tests/modules/programs/qutebrowser/keybindings.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.qutebrowser = {
+ enable = true;
+
+ enableDefaultBindings = false;
+
+ keyBindings = {
+ normal = {
+ "<Ctrl-v>" = "spawn mpv {url}";
+ ",l" = ''config-cycle spellcheck.languages ["en-GB"] ["en-US"]'';
+ };
+ prompt = { "<Ctrl-y>" = "prompt-yes"; };
+ };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ qutebrowser = pkgs.writeScriptBin "dummy-qutebrowser" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/qutebrowser/config.py \
+ ${
+ pkgs.writeText "qutebrowser-expected-config.py" ''
+ c.bindings.default = {}
+ config.bind(",l", "config-cycle spellcheck.languages [\"en-GB\"] [\"en-US\"]", mode="normal")
+ config.bind("<Ctrl-v>", "spawn mpv {url}", mode="normal")
+ config.bind("<Ctrl-y>", "prompt-yes", mode="prompt")''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/qutebrowser/settings.nix b/home-manager/tests/modules/programs/qutebrowser/settings.nix
new file mode 100644
index 00000000000..1f0f5db049b
--- /dev/null
+++ b/home-manager/tests/modules/programs/qutebrowser/settings.nix
@@ -0,0 +1,48 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.qutebrowser = {
+ enable = true;
+
+ settings = {
+ colors = {
+ hints = {
+ bg = "#000000";
+ fg = "#ffffff";
+ };
+ tabs.bar.bg = "#000000";
+ };
+ spellcheck.languages = [ "en-US" "sv-SE" ];
+ tabs.tabs_are_windows = true;
+ };
+
+ extraConfig = ''
+ # Extra qutebrowser configuration.
+ '';
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ qutebrowser = pkgs.writeScriptBin "dummy-qutebrowser" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/qutebrowser/config.py \
+ ${
+ pkgs.writeText "qutebrowser-expected-config.py" ''
+ c.colors.hints.bg = "#000000"
+ c.colors.hints.fg = "#ffffff"
+ c.colors.tabs.bar.bg = "#000000"
+ c.spellcheck.languages = ["en-US", "sv-SE"]
+ c.tabs.tabs_are_windows = True
+ # Extra qutebrowser configuration.
+ ''
+ }
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/rofi/assert-on-both-theme-and-colors.nix b/home-manager/tests/modules/programs/rofi/assert-on-both-theme-and-colors.nix
index 0f9cfc39a6c..f162a486b8b 100644
--- a/home-manager/tests/modules/programs/rofi/assert-on-both-theme-and-colors.nix
+++ b/home-manager/tests/modules/programs/rofi/assert-on-both-theme-and-colors.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
with lib;
@@ -20,6 +20,9 @@ with lib;
home.file.result.text = builtins.toJSON
(map (a: a.message) (filter (a: !a.assertion) config.assertions));
+ nixpkgs.overlays =
+ [ (self: super: { rofi = pkgs.writeScriptBin "dummy-rofi" ""; }) ];
+
nmt.script = ''
assertFileContent \
home-files/result \
diff --git a/home-manager/tests/modules/programs/ssh/default-config-expected.conf b/home-manager/tests/modules/programs/ssh/default-config-expected.conf
index 55748ea6c82..6885392b26c 100644
--- a/home-manager/tests/modules/programs/ssh/default-config-expected.conf
+++ b/home-manager/tests/modules/programs/ssh/default-config-expected.conf
@@ -6,6 +6,7 @@ Host *
ForwardAgent no
Compression no
ServerAliveInterval 0
+ ServerAliveCountMax 3
HashKnownHosts no
UserKnownHostsFile ~/.ssh/known_hosts
ControlMaster no
diff --git a/home-manager/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf b/home-manager/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf
index 5213d282c28..02268e8cb8b 100644
--- a/home-manager/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf
+++ b/home-manager/tests/modules/programs/ssh/forwards-dynamic-valid-bind-no-asserts-expected.conf
@@ -10,6 +10,7 @@ Host *
ForwardAgent no
Compression no
ServerAliveInterval 0
+ ServerAliveCountMax 3
HashKnownHosts no
UserKnownHostsFile ~/.ssh/known_hosts
ControlMaster no
diff --git a/home-manager/tests/modules/programs/ssh/match-blocks-attrs-expected.conf b/home-manager/tests/modules/programs/ssh/match-blocks-attrs-expected.conf
index f0d768375f0..e2c3290153d 100644
--- a/home-manager/tests/modules/programs/ssh/match-blocks-attrs-expected.conf
+++ b/home-manager/tests/modules/programs/ssh/match-blocks-attrs-expected.conf
@@ -10,16 +10,21 @@ Host abc
Host xyz
ServerAliveInterval 60
+ ServerAliveCountMax 10
IdentityFile file
LocalForward [localhost]:8080 [10.0.0.1]:80
RemoteForward [localhost]:8081 [10.0.0.2]:80
RemoteForward /run/user/1000/gnupg/S.gpg-agent.extra /run/user/1000/gnupg/S.gpg-agent
DynamicForward [localhost]:2839
+Host ordered
+ Port 1
+
Host *
ForwardAgent no
Compression no
ServerAliveInterval 0
+ ServerAliveCountMax 3
HashKnownHosts no
UserKnownHostsFile ~/.ssh/known_hosts
ControlMaster no
diff --git a/home-manager/tests/modules/programs/ssh/match-blocks-attrs.nix b/home-manager/tests/modules/programs/ssh/match-blocks-attrs.nix
index a84a703e89d..eaa20c6e32d 100644
--- a/home-manager/tests/modules/programs/ssh/match-blocks-attrs.nix
+++ b/home-manager/tests/modules/programs/ssh/match-blocks-attrs.nix
@@ -12,9 +12,12 @@ with lib;
proxyJump = "jump-host";
};
+ ordered = hm.dag.entryAfter [ "xyz" ] { port = 1; };
+
xyz = {
identityFile = "file";
serverAliveInterval = 60;
+ serverAliveCountMax = 10;
localForwards = [{
bind.port = 8080;
host.address = "10.0.0.1";
diff --git a/home-manager/tests/modules/programs/starship/default.nix b/home-manager/tests/modules/programs/starship/default.nix
new file mode 100644
index 00000000000..814aed65874
--- /dev/null
+++ b/home-manager/tests/modules/programs/starship/default.nix
@@ -0,0 +1 @@
+{ starship-settings = ./settings.nix; }
diff --git a/home-manager/tests/modules/programs/starship/settings-expected.toml b/home-manager/tests/modules/programs/starship/settings-expected.toml
new file mode 100644
index 00000000000..a4fb0ee55de
--- /dev/null
+++ b/home-manager/tests/modules/programs/starship/settings-expected.toml
@@ -0,0 +1,27 @@
+add_newline = false
+prompt_order = ["line_break", "package", "line_break", "character"]
+scan_timeout = 10
+
+[aws]
+disabled = true
+style = "bold blue"
+
+[battery]
+charging_symbol = "⚡️"
+
+[[battery.display]]
+style = "bold red"
+threshold = 10
+
+[[battery.display]]
+style = "bold yellow"
+threshold = 30
+
+[character]
+symbol = "➜"
+
+[memory_usage]
+threshold = -1
+
+[package]
+disabled = true
diff --git a/home-manager/tests/modules/programs/starship/settings.nix b/home-manager/tests/modules/programs/starship/settings.nix
new file mode 100644
index 00000000000..e7a27733d90
--- /dev/null
+++ b/home-manager/tests/modules/programs/starship/settings.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.starship = {
+ enable = true;
+
+ settings = mkMerge [
+ {
+ add_newline = false;
+ prompt_order = [ "line_break" "package" "line_break" "character" ];
+ scan_timeout = 10;
+ character.symbol = "➜";
+ package.disabled = true;
+ memory_usage.threshold = -1;
+ aws.style = "bold blue";
+ battery = {
+ charging_symbol = "⚡️";
+ display = [{
+ threshold = 10;
+ style = "bold red";
+ }];
+ };
+ }
+
+ {
+ aws.disabled = true;
+
+ battery.display = [{
+ threshold = 30;
+ style = "bold yellow";
+ }];
+ }
+ ];
+ };
+
+ nixpkgs.overlays = [
+ (self: super: { starship = pkgs.writeScriptBin "dummy-starship" ""; })
+ ];
+
+ nmt.script = ''
+ assertFileContent \
+ home-files/.config/starship.toml \
+ ${./settings-expected.toml}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/texlive/texlive-minimal.nix b/home-manager/tests/modules/programs/texlive/texlive-minimal.nix
index df143dbc660..1b13936beac 100644
--- a/home-manager/tests/modules/programs/texlive/texlive-minimal.nix
+++ b/home-manager/tests/modules/programs/texlive/texlive-minimal.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
with lib;
@@ -6,8 +6,22 @@ with lib;
config = {
programs.texlive.enable = true;
+ # Set up a minimal mocked texlive package set.
+ nixpkgs.overlays = [
+ (self: super: {
+ texlive = {
+ collection-basic = pkgs.writeTextDir "collection-basic" "";
+ combine = tpkgs:
+ pkgs.symlinkJoin {
+ name = "dummy-texlive-combine";
+ paths = attrValues tpkgs;
+ };
+ };
+ })
+ ];
+
nmt.script = ''
- assertFileExists home-path/bin/tex
+ assertFileExists home-path/collection-basic
'';
};
}
diff --git a/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.conf b/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.conf
index b599e603e4a..222f733d5f3 100644
--- a/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.conf
+++ b/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.conf
@@ -28,4 +28,3 @@ setw -g clock-mode-style 12
set -s escape-time 500
set -g history-limit 2000
-
diff --git a/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.nix b/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.nix
index e3d13a4b1b2..9c65ad6eee4 100644
--- a/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.nix
+++ b/home-manager/tests/modules/programs/tmux/disable-confirmation-prompt.nix
@@ -12,9 +12,7 @@ with lib;
nixpkgs.overlays = [
(self: super: {
tmuxPlugins = super.tmuxPlugins // {
- sensible = super.tmuxPlugins.sensible // {
- rtp = "@sensible_rtp@";
- };
+ sensible = super.tmuxPlugins.sensible // { rtp = "@sensible_rtp@"; };
};
})
];
diff --git a/home-manager/tests/modules/programs/tmux/emacs-with-plugins.conf b/home-manager/tests/modules/programs/tmux/emacs-with-plugins.conf
index 66b10183750..97e226316f4 100644
--- a/home-manager/tests/modules/programs/tmux/emacs-with-plugins.conf
+++ b/home-manager/tests/modules/programs/tmux/emacs-with-plugins.conf
@@ -28,8 +28,6 @@ setw -g clock-mode-style 24
set -s escape-time 500
set -g history-limit 2000
-
-
# ============================================= #
# Load plugins with Home Manager #
# --------------------------------------------- #
@@ -52,3 +50,4 @@ run-shell @tmuxplugin_prefix_highlight_rtp@
run-shell @tmuxplugin_fzf_tmux_url_rtp@
# ============================================= #
+
diff --git a/home-manager/tests/modules/programs/tmux/hm-session-vars.sh b/home-manager/tests/modules/programs/tmux/hm-session-vars.sh
deleted file mode 100644
index 40d9c24b50d..00000000000
--- a/home-manager/tests/modules/programs/tmux/hm-session-vars.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# Only source this once.
-if [ -n "$__HM_SESS_VARS_SOURCED" ]; then return; fi
-export __HM_SESS_VARS_SOURCED=1
-
-export TMUX_TMPDIR="${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}"
diff --git a/home-manager/tests/modules/programs/tmux/secure-socket-enabled.nix b/home-manager/tests/modules/programs/tmux/secure-socket-enabled.nix
index 34e9129c5a4..ca2de66310d 100644
--- a/home-manager/tests/modules/programs/tmux/secure-socket-enabled.nix
+++ b/home-manager/tests/modules/programs/tmux/secure-socket-enabled.nix
@@ -11,7 +11,8 @@ with lib;
nmt.script = ''
assertFileExists home-path/etc/profile.d/hm-session-vars.sh
- assertFileContent home-path/etc/profile.d/hm-session-vars.sh ${./hm-session-vars.sh}
+ assertFileContains home-path/etc/profile.d/hm-session-vars.sh \
+ 'export TMUX_TMPDIR="''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}"'
'';
};
}
diff --git a/home-manager/tests/modules/programs/tmux/vi-all-true.conf b/home-manager/tests/modules/programs/tmux/vi-all-true.conf
index 08e37e19b97..de1cbe06ebc 100644
--- a/home-manager/tests/modules/programs/tmux/vi-all-true.conf
+++ b/home-manager/tests/modules/programs/tmux/vi-all-true.conf
@@ -28,4 +28,3 @@ setw -g clock-mode-style 24
set -s escape-time 500
set -g history-limit 2000
-
diff --git a/home-manager/tests/modules/programs/vscode/default.nix b/home-manager/tests/modules/programs/vscode/default.nix
new file mode 100644
index 00000000000..70f6d2e7060
--- /dev/null
+++ b/home-manager/tests/modules/programs/vscode/default.nix
@@ -0,0 +1 @@
+{ vscode-keybindings = ./keybindings.nix; }
diff --git a/home-manager/tests/modules/programs/vscode/keybindings.nix b/home-manager/tests/modules/programs/vscode/keybindings.nix
new file mode 100644
index 00000000000..420b212dce9
--- /dev/null
+++ b/home-manager/tests/modules/programs/vscode/keybindings.nix
@@ -0,0 +1,53 @@
+# Test that keybdinings.json is created correctly.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ bindings = [
+ {
+ key = "ctrl+c";
+ command = "editor.action.clipboardCopyAction";
+ when = "textInputFocus && false";
+ }
+ {
+ key = "ctrl+c";
+ command = "deleteFile";
+ when = "";
+ }
+ {
+ key = "d";
+ command = "deleteFile";
+ when = "explorerViewletVisible";
+ }
+ ];
+
+ targetPath = if pkgs.stdenv.hostPlatform.isDarwin then
+ "Library/Application Support/Code/User/keybindings.json"
+ else
+ ".config/Code/User/keybindings.json";
+
+ expectedJson = pkgs.writeText "expected.json" (builtins.toJSON bindings);
+in {
+ config = {
+ programs.vscode = {
+ enable = true;
+ keybindings = bindings;
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ vscode = pkgs.runCommandLocal "vscode" { pname = "vscode"; } ''
+ mkdir -p $out/bin
+ touch $out/bin/code
+ chmod +x $out/bin/code;
+ '';
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists "home-files/${targetPath}"
+ assertFileContent "home-files/${targetPath}" "${expectedJson}"
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/waybar/broken-settings.nix b/home-manager/tests/modules/programs/waybar/broken-settings.nix
new file mode 100644
index 00000000000..68f0b90bfca
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/broken-settings.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+ expected = pkgs.writeText "expected-json" ''
+ [
+ {
+ "height": 26,
+ "layer": "top",
+ "modules-center": [
+ "sway/window"
+ ],
+ "modules-left": [
+ "sway/workspaces",
+ "sway/mode"
+ ],
+ "modules-right": [
+ "idle_inhibitor",
+ "pulseaudio",
+ "network",
+ "cpu",
+ "memory",
+ "backlight",
+ "tray",
+ "clock"
+ ],
+ "output": [
+ "DP-1",
+ "eDP-1",
+ "HEADLESS-1"
+ ],
+ "position": "top",
+ "sway/workspaces": {
+ "all-outputs": true
+ }
+ }
+ ]
+ '';
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ systemd.enable = true;
+ settings = [{
+ layer = "top";
+ position = "top";
+ height = 26;
+ output = [ "DP-1" "eDP-1" "HEADLESS-1" ];
+ modules-left = [ "sway/workspaces" "sway/mode" ];
+ modules-center = [ "sway/window" ];
+ modules-right = [
+ "idle_inhibitor"
+ "pulseaudio"
+ "network"
+ "cpu"
+ "memory"
+ "backlight"
+ "tray"
+ "clock"
+ ];
+
+ modules = { "sway/workspaces".all-outputs = true; };
+ }];
+ };
+
+ nmt.description = ''
+ Test for the broken configuration
+ https://github.com/rycee/home-manager/pull/1329#issuecomment-653253069
+ '';
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/style.css
+ assertFileContent \
+ home-files/.config/waybar/config \
+ ${expected}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/waybar/default.nix b/home-manager/tests/modules/programs/waybar/default.nix
new file mode 100644
index 00000000000..d50b1b8d368
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/default.nix
@@ -0,0 +1,8 @@
+{
+ waybar-systemd-with-graphical-session-target =
+ ./systemd-with-graphical-session-target.nix;
+ waybar-styling = ./styling.nix;
+ waybar-settings-complex = ./settings-complex.nix;
+ # Broken configuration from https://github.com/rycee/home-manager/pull/1329#issuecomment-653253069
+ waybar-broken-settings = ./broken-settings.nix;
+}
diff --git a/home-manager/tests/modules/programs/waybar/settings-complex-expected.json b/home-manager/tests/modules/programs/waybar/settings-complex-expected.json
new file mode 100644
index 00000000000..0d020c19c97
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/settings-complex-expected.json
@@ -0,0 +1,46 @@
+[
+ {
+ "custom/my-module": {
+ "exec": "@dummy@/bin/dummy",
+ "format": "hello from {}"
+ },
+ "height": 30,
+ "idle_inhibitor": {
+ "format": "{icon}"
+ },
+ "layer": "top",
+ "modules-center": [
+ "sway/window"
+ ],
+ "modules-left": [
+ "sway/workspaces",
+ "sway/mode",
+ "custom/my-module"
+ ],
+ "modules-right": [
+ "idle_inhibitor",
+ "pulseaudio",
+ "network",
+ "cpu",
+ "memory",
+ "backlight",
+ "tray",
+ "battery",
+ "clock"
+ ],
+ "output": [
+ "DP-1"
+ ],
+ "position": "top",
+ "sway/mode": {
+ "tooltip": false
+ },
+ "sway/window": {
+ "max-length": 120
+ },
+ "sway/workspaces": {
+ "all-outputs": true,
+ "disable-scroll": true
+ }
+ }
+]
diff --git a/home-manager/tests/modules/programs/waybar/settings-complex.nix b/home-manager/tests/modules/programs/waybar/settings-complex.nix
new file mode 100644
index 00000000000..750e52f4581
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/settings-complex.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ settings = [{
+ layer = "top";
+ position = "top";
+ height = 30;
+ output = [ "DP-1" ];
+ modules-left = [ "sway/workspaces" "sway/mode" "custom/my-module" ];
+ modules-center = [ "sway/window" ];
+ modules-right = [
+ "idle_inhibitor"
+ "pulseaudio"
+ "network"
+ "cpu"
+ "memory"
+ "backlight"
+ "tray"
+ "battery"
+ "clock"
+ ];
+
+ modules = {
+ "sway/workspaces" = {
+ disable-scroll = true;
+ all-outputs = true;
+ };
+ "sway/mode" = { tooltip = false; };
+ "sway/window" = { max-length = 120; };
+ "idle_inhibitor" = { format = "{icon}"; };
+ "custom/my-module" = {
+ format = "hello from {}";
+ exec = let
+ dummyScript =
+ pkgs.writeShellScriptBin "dummy" "echo within waybar" // {
+ outPath = "@dummy@";
+ };
+ in "${dummyScript}/bin/dummy";
+ };
+ };
+ }];
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/style.css
+ assertFileContent \
+ home-files/.config/waybar/config \
+ ${./settings-complex-expected.json}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/waybar/styling-expected.css b/home-manager/tests/modules/programs/waybar/styling-expected.css
new file mode 100644
index 00000000000..dc779e58770
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/styling-expected.css
@@ -0,0 +1,23 @@
+* {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ font-weight: bold;
+ color: #abb2bf;
+ font-size: 18px;
+ min-height: 0px;
+}
+window#waybar {
+ background: #16191C;
+ color: #aab2bf;
+}
+#window {
+ padding: 0 0px;
+}
+#workspaces button:hover {
+ box-shadow: inherit;
+ text-shadow: inherit;
+ background: #16191C;
+ border: #16191C;
+ padding: 0 3px;
+}
diff --git a/home-manager/tests/modules/programs/waybar/styling.nix b/home-manager/tests/modules/programs/waybar/styling.nix
new file mode 100644
index 00000000000..bd73f2aafd2
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/styling.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ style = ''
+ * {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ font-weight: bold;
+ color: #abb2bf;
+ font-size: 18px;
+ min-height: 0px;
+ }
+ window#waybar {
+ background: #16191C;
+ color: #aab2bf;
+ }
+ #window {
+ padding: 0 0px;
+ }
+ #workspaces button:hover {
+ box-shadow: inherit;
+ text-shadow: inherit;
+ background: #16191C;
+ border: #16191C;
+ padding: 0 3px;
+ }
+ '';
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/config
+ assertFileContent \
+ home-files/.config/waybar/style.css \
+ ${./styling-expected.css}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix b/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix
new file mode 100644
index 00000000000..e751d804dab
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ systemd.enable = true;
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/config
+ assertPathNotExists home-files/.config/waybar/style.css
+
+ assertFileContent \
+ home-files/.config/systemd/user/waybar.service \
+ ${./systemd-with-graphical-session-target.service}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.service b/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.service
new file mode 100644
index 00000000000..7d4c65214e8
--- /dev/null
+++ b/home-manager/tests/modules/programs/waybar/systemd-with-graphical-session-target.service
@@ -0,0 +1,16 @@
+[Install]
+WantedBy=graphical-session.target
+
+[Service]
+BusName=fr.arouillard.waybar
+ExecStart=@waybar@/bin/waybar
+Restart=always
+RestartSec=1sec
+Type=dbus
+
+[Unit]
+After=dbus.service
+Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
+Documentation=https://github.com/Alexays/Waybar/wiki
+PartOf=graphical-session.target
+Requisite=dbus.service
diff --git a/home-manager/tests/modules/programs/zplug/default.nix b/home-manager/tests/modules/programs/zplug/default.nix
new file mode 100644
index 00000000000..172f7cd5981
--- /dev/null
+++ b/home-manager/tests/modules/programs/zplug/default.nix
@@ -0,0 +1 @@
+{ zplug-modules = ./modules.nix; }
diff --git a/home-manager/tests/modules/programs/zplug/modules.nix b/home-manager/tests/modules/programs/zplug/modules.nix
new file mode 100644
index 00000000000..704c5c5e2ef
--- /dev/null
+++ b/home-manager/tests/modules/programs/zplug/modules.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ programs.zsh = {
+ enable = true;
+ zplug = {
+ enable = true;
+ plugins = [
+ {
+ name = "plugins/git";
+ tags = [ "from:oh-my-zsh" ];
+ }
+ {
+ name = "lib/clipboard";
+ tags = [ "from:oh-my-zsh" ''if:"[[ $OSTYPE == *darwin* ]]"'' ];
+ }
+ ];
+ };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ zsh = pkgs.writeScriptBin "dummy-zsh" "";
+ zplug = pkgs.writeScriptBin "dummy-zplug" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileRegex home-files/.zshrc \
+ '^source ${builtins.storeDir}/.*zplug.*/init\.zsh$'
+
+ assertFileContains home-files/.zshrc \
+ 'zplug "plugins/git", from:oh-my-zsh'
+
+ assertFileContains home-files/.zshrc \
+ 'zplug "lib/clipboard", from:oh-my-zsh, if:"[[ $OSTYPE == *darwin* ]]"'
+
+ assertFileContains home-files/.zshrc \
+ 'if ! zplug check; then
+ zplug install
+ fi'
+
+ assertFileRegex home-files/.zshrc \
+ '^zplug load$'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/dropbox/basic-configuration.nix b/home-manager/tests/modules/services/dropbox/basic-configuration.nix
new file mode 100644
index 00000000000..96a17cd7384
--- /dev/null
+++ b/home-manager/tests/modules/services/dropbox/basic-configuration.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, ... }:
+
+{
+ config = {
+ services.dropbox = {
+ enable = true;
+ path = "${config.home.homeDirectory}/dropbox";
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dropbox-cli = pkgs.writeScriptBin "dummy-dropbox-cli" "" // {
+ outPath = "@dropbox-cli@";
+ };
+ })
+ ];
+
+ nmt.script = ''
+ serviceFile=home-files/.config/systemd/user/dropbox.service
+
+ assertFileExists $serviceFile
+ '';
+
+ };
+}
diff --git a/home-manager/tests/modules/services/dropbox/default.nix b/home-manager/tests/modules/services/dropbox/default.nix
new file mode 100644
index 00000000000..ad519790355
--- /dev/null
+++ b/home-manager/tests/modules/services/dropbox/default.nix
@@ -0,0 +1 @@
+{ dropbox-basic-configuration = ./basic-configuration.nix; }
diff --git a/home-manager/tests/modules/services/emacs/default.nix b/home-manager/tests/modules/services/emacs/default.nix
new file mode 100644
index 00000000000..af27538d99d
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/default.nix
@@ -0,0 +1,5 @@
+{
+ emacs-service = ./emacs-service.nix;
+ emacs-socket-26 = ./emacs-socket-26.nix;
+ emacs-socket-27 = ./emacs-socket-27.nix;
+}
diff --git a/home-manager/tests/modules/services/emacs/emacs-emacsclient.desktop b/home-manager/tests/modules/services/emacs/emacs-emacsclient.desktop
new file mode 100644
index 00000000000..ab9849bb6b9
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-emacsclient.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+Exec=@emacs@/bin/emacsclient -c %F
+Terminal=false
+Name=Emacs Client
+Icon=emacs
+Comment=Edit text
+GenericName=Text Editor
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+Categories=Utility;TextEditor;
+StartupWMClass=Emacs
+
diff --git a/home-manager/tests/modules/services/emacs/emacs-service-emacs.service b/home-manager/tests/modules/services/emacs/emacs-service-emacs.service
new file mode 100644
index 00000000000..d8a618a2671
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-service-emacs.service
@@ -0,0 +1,12 @@
+[Install]
+WantedBy=default.target
+
+[Service]
+ExecStart=@runtimeShell@ -l -c "@emacs@/bin/emacs --fg-daemon"
+ExecStop=@emacs@/bin/emacsclient --eval '(kill-emacs 0)'
+Restart=on-failure
+
+[Unit]
+Description=Emacs: the extensible, self-documenting text editor
+Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
+X-RestartIfChanged=false
diff --git a/home-manager/tests/modules/services/emacs/emacs-service.nix b/home-manager/tests/modules/services/emacs/emacs-service.nix
new file mode 100644
index 00000000000..be27e9ab33d
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-service.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ nixpkgs.overlays = [
+ (self: super: rec {
+ emacs = pkgs.writeShellScriptBin "dummy-emacs" "" // {
+ outPath = "@emacs@";
+ };
+ emacsPackagesFor = _:
+ makeScope super.newScope (_: { emacsWithPackages = _: emacs; });
+ })
+ ];
+
+ programs.emacs.enable = true;
+ services.emacs.enable = true;
+ services.emacs.client.enable = true;
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/systemd/user/emacs.socket
+ assertFileExists home-files/.config/systemd/user/emacs.service
+ assertFileExists home-path/share/applications/emacsclient.desktop
+
+ assertFileContent home-files/.config/systemd/user/emacs.service \
+ ${
+ pkgs.substituteAll {
+ inherit (pkgs) runtimeShell;
+ src = ./emacs-service-emacs.service;
+ }
+ }
+ assertFileContent home-path/share/applications/emacsclient.desktop \
+ ${./emacs-emacsclient.desktop}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.service b/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.service
new file mode 100644
index 00000000000..2d731c7ee1a
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.service
@@ -0,0 +1,9 @@
+[Service]
+ExecStart=@runtimeShell@ -l -c "@emacs@/bin/emacs --fg-daemon='%T/emacs%U/server'"
+ExecStop=@emacs@/bin/emacsclient --eval '(kill-emacs 0)'
+Restart=on-failure
+
+[Unit]
+Description=Emacs: the extensible, self-documenting text editor
+Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
+X-RestartIfChanged=false
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.socket b/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.socket
new file mode 100644
index 00000000000..d2fa78e2265
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-26-emacs.socket
@@ -0,0 +1,12 @@
+[Install]
+WantedBy=sockets.target
+
+[Socket]
+DirectoryMode=0700
+FileDescriptorName=server
+ListenStream=%T/emacs%U/server
+SocketMode=0600
+
+[Unit]
+Description=Emacs: the extensible, self-documenting text editor
+Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-26.nix b/home-manager/tests/modules/services/emacs/emacs-socket-26.nix
new file mode 100644
index 00000000000..65f06159e4c
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-26.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ nixpkgs.overlays = [
+ (self: super: rec {
+ emacs = pkgs.writeShellScriptBin "dummy-emacs-26.3" "" // {
+ outPath = "@emacs@";
+ };
+ emacsPackagesFor = _:
+ makeScope super.newScope (_: { emacsWithPackages = _: emacs; });
+ })
+ ];
+
+ programs.emacs.enable = true;
+ services.emacs.enable = true;
+ services.emacs.client.enable = true;
+ services.emacs.socketActivation.enable = true;
+
+ nmt.script = ''
+ assertFileExists home-files/.config/systemd/user/emacs.socket
+ assertFileExists home-files/.config/systemd/user/emacs.service
+ assertFileExists home-path/share/applications/emacsclient.desktop
+
+ assertFileContent home-files/.config/systemd/user/emacs.socket \
+ ${./emacs-socket-26-emacs.socket}
+ assertFileContent home-files/.config/systemd/user/emacs.service \
+ ${
+ pkgs.substituteAll {
+ inherit (pkgs) runtimeShell;
+ src = ./emacs-socket-26-emacs.service;
+ }
+ }
+ assertFileContent home-path/share/applications/emacsclient.desktop \
+ ${./emacs-emacsclient.desktop}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.service b/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.service
new file mode 100644
index 00000000000..408a5d24b5c
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.service
@@ -0,0 +1,9 @@
+[Service]
+ExecStart=@runtimeShell@ -l -c "@emacs@/bin/emacs --fg-daemon='%t/emacs/server'"
+ExecStop=@emacs@/bin/emacsclient --eval '(kill-emacs 0)'
+Restart=on-failure
+
+[Unit]
+Description=Emacs: the extensible, self-documenting text editor
+Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
+X-RestartIfChanged=false
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.socket b/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.socket
new file mode 100644
index 00000000000..8fa68bf5911
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-27-emacs.socket
@@ -0,0 +1,12 @@
+[Install]
+WantedBy=sockets.target
+
+[Socket]
+DirectoryMode=0700
+FileDescriptorName=server
+ListenStream=%t/emacs/server
+SocketMode=0600
+
+[Unit]
+Description=Emacs: the extensible, self-documenting text editor
+Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
diff --git a/home-manager/tests/modules/services/emacs/emacs-socket-27.nix b/home-manager/tests/modules/services/emacs/emacs-socket-27.nix
new file mode 100644
index 00000000000..213dedca51c
--- /dev/null
+++ b/home-manager/tests/modules/services/emacs/emacs-socket-27.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+in {
+ config = {
+ nixpkgs.overlays = [
+ (self: super: rec {
+ emacs = pkgs.writeShellScriptBin "dummy-emacs-27.0.91" "" // {
+ outPath = "@emacs@";
+ };
+ emacsPackagesFor = _:
+ makeScope super.newScope (_: { emacsWithPackages = _: emacs; });
+ })
+ ];
+
+ programs.emacs.enable = true;
+ services.emacs.enable = true;
+ services.emacs.client.enable = true;
+ services.emacs.socketActivation.enable = true;
+
+ nmt.script = ''
+ assertFileExists home-files/.config/systemd/user/emacs.socket
+ assertFileExists home-files/.config/systemd/user/emacs.service
+ assertFileExists home-path/share/applications/emacsclient.desktop
+
+ assertFileContent home-files/.config/systemd/user/emacs.socket \
+ ${./emacs-socket-27-emacs.socket}
+ assertFileContent home-files/.config/systemd/user/emacs.service \
+ ${
+ pkgs.substituteAll {
+ inherit (pkgs) runtimeShell;
+ src = ./emacs-socket-27-emacs.service;
+ }
+ }
+ assertFileContent home-path/share/applications/emacsclient.desktop \
+ ${./emacs-emacsclient.desktop}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/fluidsynth/default.nix b/home-manager/tests/modules/services/fluidsynth/default.nix
new file mode 100644
index 00000000000..58e9c5dcc33
--- /dev/null
+++ b/home-manager/tests/modules/services/fluidsynth/default.nix
@@ -0,0 +1 @@
+{ fluidsynth = import ./service.nix; }
diff --git a/home-manager/tests/modules/services/fluidsynth/service.nix b/home-manager/tests/modules/services/fluidsynth/service.nix
new file mode 100644
index 00000000000..8d53e75c032
--- /dev/null
+++ b/home-manager/tests/modules/services/fluidsynth/service.nix
@@ -0,0 +1,24 @@
+{ config, pkgs, ... }: {
+ config = {
+ services.fluidsynth.enable = true;
+ services.fluidsynth.soundFont = "/path/to/soundFont";
+ services.fluidsynth.extraOptions = [ "--sample-rate 96000" ];
+
+ nixpkgs.overlays = [
+ (self: super: {
+ fluidsynth = pkgs.writeScriptBin "dummy-fluidsynth" "" // {
+ outPath = "@fluidsynth@";
+ };
+ })
+ ];
+
+ nmt.script = ''
+ serviceFile=home-files/.config/systemd/user/fluidsynth.service
+
+ assertFileExists $serviceFile
+
+ assertFileContains $serviceFile \
+ 'ExecStart=@fluidsynth@/bin/fluidsynth -a pulseaudio -si --sample-rate 96000 /path/to/soundFont'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/kanshi/basic-configuration.conf b/home-manager/tests/modules/services/kanshi/basic-configuration.conf
new file mode 100644
index 00000000000..9d6442b985b
--- /dev/null
+++ b/home-manager/tests/modules/services/kanshi/basic-configuration.conf
@@ -0,0 +1,15 @@
+profile desktop {
+ output "eDP-1" disable
+ output "Iiyama North America PLE2483H-DP" enable position 0,0
+ output "Iiyama North America PLE2483H-DP 1158765348486" enable mode 1920x1080 position 1920,0 scale 2.100000 transform flipped-270
+ exec echo "1 two 3"
+}
+
+profile nomad {
+ output "eDP-1" enable
+}
+
+profile test {
+ output "*" enable
+}
+
diff --git a/home-manager/tests/modules/services/kanshi/basic-configuration.nix b/home-manager/tests/modules/services/kanshi/basic-configuration.nix
new file mode 100644
index 00000000000..15fbbb9ceb1
--- /dev/null
+++ b/home-manager/tests/modules/services/kanshi/basic-configuration.nix
@@ -0,0 +1,52 @@
+{ config, pkgs, ... }: {
+ config = {
+ services.kanshi = {
+ enable = true;
+ package = pkgs.writeScriptBin "dummy-kanshi" "";
+ profiles = {
+ nomad = {
+ outputs = [{
+ criteria = "eDP-1";
+ status = "enable";
+ }];
+ };
+ desktop = {
+ exec = ''echo "1 two 3"'';
+ outputs = [
+ {
+ criteria = "eDP-1";
+ status = "disable";
+ }
+ {
+ criteria = "Iiyama North America PLE2483H-DP";
+ status = "enable";
+ position = "0,0";
+ }
+ {
+ criteria = "Iiyama North America PLE2483H-DP 1158765348486";
+ status = "enable";
+ position = "1920,0";
+ scale = 2.1;
+ mode = "1920x1080";
+ transform = "flipped-270";
+ }
+ ];
+ };
+ };
+ extraConfig = ''
+ profile test {
+ output "*" enable
+ }
+ '';
+ };
+
+ nmt.script = ''
+ serviceFile=home-files/.config/systemd/user/kanshi.service
+ assertFileExists $serviceFile
+
+ assertFileExists home-files/.config/kanshi/config
+ assertFileContent home-files/.config/kanshi/config \
+ ${./basic-configuration.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/kanshi/default.nix b/home-manager/tests/modules/services/kanshi/default.nix
new file mode 100644
index 00000000000..cb6b2a6b79f
--- /dev/null
+++ b/home-manager/tests/modules/services/kanshi/default.nix
@@ -0,0 +1 @@
+{ kanshi-basic-configuration = ./basic-configuration.nix; }
diff --git a/home-manager/tests/modules/services/lieer/default.nix b/home-manager/tests/modules/services/lieer/default.nix
new file mode 100644
index 00000000000..1d6b435a1ca
--- /dev/null
+++ b/home-manager/tests/modules/services/lieer/default.nix
@@ -0,0 +1 @@
+{ lieer-service = ./lieer-service.nix; }
diff --git a/home-manager/tests/modules/services/lieer/lieer-service-expected.service b/home-manager/tests/modules/services/lieer/lieer-service-expected.service
new file mode 100644
index 00000000000..1110e85c475
--- /dev/null
+++ b/home-manager/tests/modules/services/lieer/lieer-service-expected.service
@@ -0,0 +1,8 @@
+[Service]
+ExecStart=@lieer@/bin/gmi sync
+Type=oneshot
+WorkingDirectory=/home/hm-user/Mail/hm@example.com
+
+[Unit]
+ConditionPathExists=/home/hm-user/Mail/hm@example.com/.gmailieer.json
+Description=lieer Gmail synchronization for hm@example.com
diff --git a/home-manager/tests/modules/services/lieer/lieer-service-expected.timer b/home-manager/tests/modules/services/lieer/lieer-service-expected.timer
new file mode 100644
index 00000000000..cb059ea47c4
--- /dev/null
+++ b/home-manager/tests/modules/services/lieer/lieer-service-expected.timer
@@ -0,0 +1,9 @@
+[Install]
+WantedBy=timers.target
+
+[Timer]
+OnCalendar=*:0/5
+RandomizedDelaySec=30
+
+[Unit]
+Description=lieer Gmail synchronization for hm@example.com
diff --git a/home-manager/tests/modules/services/lieer/lieer-service.nix b/home-manager/tests/modules/services/lieer/lieer-service.nix
new file mode 100644
index 00000000000..03dcdc749b1
--- /dev/null
+++ b/home-manager/tests/modules/services/lieer/lieer-service.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports = [ ../../accounts/email-test-accounts.nix ];
+
+ config = {
+ services.lieer.enable = true;
+
+ accounts.email.accounts = {
+ "hm@example.com".lieer.enable = true;
+ "hm@example.com".lieer.sync.enable = true;
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ gmailieer = pkgs.writeScriptBin "dummy-gmailieer" "" // {
+ outPath = "@lieer@";
+ };
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/systemd/user/lieer-hm-example-com.service
+ assertFileExists home-files/.config/systemd/user/lieer-hm-example-com.timer
+
+ assertFileContent home-files/.config/systemd/user/lieer-hm-example-com.service \
+ ${./lieer-service-expected.service}
+ assertFileContent home-files/.config/systemd/user/lieer-hm-example-com.timer \
+ ${./lieer-service-expected.timer}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/polybar/basic-configuration.conf b/home-manager/tests/modules/services/polybar/basic-configuration.conf
new file mode 100644
index 00000000000..54448a705fc
--- /dev/null
+++ b/home-manager/tests/modules/services/polybar/basic-configuration.conf
@@ -0,0 +1,21 @@
+[bar/top]
+height=3%
+modules-center=date
+monitor=${env:MONITOR:eDP1}
+radius=0
+width=100%
+
+[module/date]
+date=%d.%m.%y
+internal=5
+label=%time% %date%
+time=%H:%M
+type=internal/date
+
+[module/date]
+type = internal/date
+interval = 5
+date = "%d.%m.%y"
+time = %H:%M
+format-prefix-foreground = ${colors.foreground-alt}
+label = %time% %date%
diff --git a/home-manager/tests/modules/services/polybar/basic-configuration.nix b/home-manager/tests/modules/services/polybar/basic-configuration.nix
new file mode 100644
index 00000000000..a8886dab6d5
--- /dev/null
+++ b/home-manager/tests/modules/services/polybar/basic-configuration.nix
@@ -0,0 +1,48 @@
+{ config, pkgs, ... }:
+
+{
+ config = {
+ services.polybar = {
+ enable = true;
+ package = pkgs.writeScriptBin "dummy-polybar" "";
+ script = "polybar bar &";
+ config = {
+ "bar/top" = {
+ monitor = "\${env:MONITOR:eDP1}";
+ width = "100%";
+ height = "3%";
+ radius = 0;
+ modules-center = "date";
+ };
+ "module/date" = {
+ type = "internal/date";
+ internal = 5;
+ date = "%d.%m.%y";
+ time = "%H:%M";
+ label = "%time% %date%";
+ };
+ };
+ extraConfig = ''
+ [module/date]
+ type = internal/date
+ interval = 5
+ date = "%d.%m.%y"
+ time = %H:%M
+ format-prefix-foreground = ''${colors.foreground-alt}
+ label = %time% %date%
+ '';
+ };
+
+ nmt.script = ''
+ serviceFile=home-files/.config/systemd/user/polybar.service
+
+ assertFileExists $serviceFile
+ assertFileRegex $serviceFile 'X-Restart-Triggers=.*polybar\.conf'
+ assertFileRegex $serviceFile 'ExecStart=.*/bin/polybar-start'
+
+ assertFileExists home-files/.config/polybar/config
+ assertFileContent home-files/.config/polybar/config \
+ ${./basic-configuration.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/polybar/default.nix b/home-manager/tests/modules/services/polybar/default.nix
new file mode 100644
index 00000000000..94d5d3cde20
--- /dev/null
+++ b/home-manager/tests/modules/services/polybar/default.nix
@@ -0,0 +1 @@
+{ polybar-basic-configuration = ./basic-configuration.nix; }
diff --git a/home-manager/tests/modules/services/sxhkd/configuration.nix b/home-manager/tests/modules/services/sxhkd/configuration.nix
index 992c4b18a94..03206a8d52b 100644
--- a/home-manager/tests/modules/services/sxhkd/configuration.nix
+++ b/home-manager/tests/modules/services/sxhkd/configuration.nix
@@ -1,4 +1,4 @@
-{ config, ... }: {
+{ config, pkgs, ... }: {
config = {
services.sxhkd = {
enable = true;
@@ -19,8 +19,11 @@
'';
};
+ nixpkgs.overlays =
+ [ (self: super: { sxhkd = pkgs.writeScriptBin "dummy-sxhkd" ""; }) ];
+
nmt.script = ''
- local sxhkdrc=home-files/.config/sxhkd/sxhkdrc
+ sxhkdrc=home-files/.config/sxhkd/sxhkdrc
assertFileExists $sxhkdrc
diff --git a/home-manager/tests/modules/services/sxhkd/service.nix b/home-manager/tests/modules/services/sxhkd/service.nix
index 46ce259a718..9b4fd70cc55 100644
--- a/home-manager/tests/modules/services/sxhkd/service.nix
+++ b/home-manager/tests/modules/services/sxhkd/service.nix
@@ -7,7 +7,7 @@
};
nmt.script = ''
- local serviceFile=home-files/.config/systemd/user/sxhkd.service
+ serviceFile=home-files/.config/systemd/user/sxhkd.service
assertFileExists $serviceFile
diff --git a/home-manager/tests/modules/services/window-managers/i3/default.nix b/home-manager/tests/modules/services/window-managers/i3/default.nix
index e239d6c07f1..c523dfcd04b 100644
--- a/home-manager/tests/modules/services/window-managers/i3/default.nix
+++ b/home-manager/tests/modules/services/window-managers/i3/default.nix
@@ -1 +1,4 @@
-{ i3-keybindings = ./i3-keybindings.nix; }
+{
+ i3-followmouse = ./i3-followmouse.nix;
+ i3-keybindings = ./i3-keybindings.nix;
+}
diff --git a/home-manager/tests/modules/services/window-managers/i3/i3-followmouse-expected.conf b/home-manager/tests/modules/services/window-managers/i3/i3-followmouse-expected.conf
new file mode 100644
index 00000000000..729605be469
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/i3/i3-followmouse-expected.conf
@@ -0,0 +1,105 @@
+font pango:monospace 8
+floating_modifier Mod1
+new_window normal 2
+new_float normal 2
+hide_edge_borders none
+force_focus_wrapping no
+focus_follows_mouse no
+focus_on_window_activation smart
+mouse_warping output
+workspace_layout default
+workspace_auto_back_and_forth no
+
+client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
+client.unfocused #333333 #222222 #888888 #292d2e #222222
+client.urgent #2f343a #900000 #ffffff #900000 #900000
+client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
+client.background #ffffff
+
+bindsym Mod1+0 workspace number 10
+bindsym Mod1+1 workspace number 1
+bindsym Mod1+2 workspace number 2
+bindsym Mod1+3 workspace number 3
+bindsym Mod1+4 workspace number 4
+bindsym Mod1+5 workspace number 5
+bindsym Mod1+6 workspace number 6
+bindsym Mod1+7 workspace number 7
+bindsym Mod1+8 workspace number 8
+bindsym Mod1+9 workspace number 9
+bindsym Mod1+Down focus down
+bindsym Mod1+Left focus left
+bindsym Mod1+Return exec i3-sensible-terminal
+bindsym Mod1+Right focus right
+bindsym Mod1+Shift+0 move container to workspace number 10
+bindsym Mod1+Shift+1 move container to workspace number 1
+bindsym Mod1+Shift+2 move container to workspace number 2
+bindsym Mod1+Shift+3 move container to workspace number 3
+bindsym Mod1+Shift+4 move container to workspace number 4
+bindsym Mod1+Shift+5 move container to workspace number 5
+bindsym Mod1+Shift+6 move container to workspace number 6
+bindsym Mod1+Shift+7 move container to workspace number 7
+bindsym Mod1+Shift+8 move container to workspace number 8
+bindsym Mod1+Shift+9 move container to workspace number 9
+bindsym Mod1+Shift+Down move down
+bindsym Mod1+Shift+Left move left
+bindsym Mod1+Shift+Right move right
+bindsym Mod1+Shift+Up move up
+bindsym Mod1+Shift+c reload
+bindsym Mod1+Shift+e exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'
+bindsym Mod1+Shift+minus move scratchpad
+bindsym Mod1+Shift+q kill
+bindsym Mod1+Shift+r restart
+bindsym Mod1+Shift+space floating toggle
+bindsym Mod1+Up focus up
+bindsym Mod1+a focus parent
+bindsym Mod1+d exec @dmenu@/bin/dmenu_run
+bindsym Mod1+e layout toggle split
+bindsym Mod1+f fullscreen toggle
+bindsym Mod1+h split h
+bindsym Mod1+minus scratchpad show
+bindsym Mod1+r mode resize
+bindsym Mod1+s layout stacking
+bindsym Mod1+space focus mode_toggle
+bindsym Mod1+v split v
+bindsym Mod1+w layout tabbed
+
+mode "resize" {
+bindsym Down resize grow height 10 px or 10 ppt
+bindsym Escape mode default
+bindsym Left resize shrink width 10 px or 10 ppt
+bindsym Return mode default
+bindsym Right resize grow width 10 px or 10 ppt
+bindsym Up resize shrink height 10 px or 10 ppt
+}
+
+
+bar {
+
+ font pango:monospace 8
+ mode dock
+ hidden_state hide
+ position bottom
+ status_command @i3status@/bin/i3status
+ i3bar_command @i3@/bin/i3bar
+ workspace_buttons yes
+ strip_workspace_numbers no
+ tray_output primary
+ colors {
+ background #000000
+ statusline #ffffff
+ separator #666666
+ focused_workspace #4c7899 #285577 #ffffff
+ active_workspace #333333 #5f676a #ffffff
+ inactive_workspace #333333 #222222 #888888
+ urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #2f343a #900000 #ffffff
+ }
+
+}
+
+
+
+
+
+
diff --git a/home-manager/tests/modules/services/window-managers/i3/i3-followmouse.nix b/home-manager/tests/modules/services/window-managers/i3/i3-followmouse.nix
new file mode 100644
index 00000000000..8d51e348877
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/i3/i3-followmouse.nix
@@ -0,0 +1,29 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+ config = {
+ xsession.windowManager.i3 = {
+ enable = true;
+
+ config.focus.followMouse = false;
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dmenu = super.dmenu // { outPath = "@dmenu@"; };
+
+ i3 = super.i3 // { outPath = "@i3@"; };
+
+ i3status = super.i3status // { outPath = "@i3status@"; };
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/i3/config
+ assertFileContent home-files/.config/i3/config \
+ ${./i3-followmouse-expected.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/window-managers/sway/default.nix b/home-manager/tests/modules/services/window-managers/sway/default.nix
new file mode 100644
index 00000000000..b9c0ab5e099
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/default.nix
@@ -0,0 +1,6 @@
+{
+ sway-default = ./sway-default.nix;
+ sway-post-2003 = ./sway-post-2003.nix;
+ sway-followmouse = ./sway-followmouse.nix;
+ sway-followmouse-legacy = ./sway-followmouse-legacy.nix;
+}
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-default.conf b/home-manager/tests/modules/services/window-managers/sway/sway-default.conf
new file mode 100644
index 00000000000..da5b1f47eef
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-default.conf
@@ -0,0 +1,117 @@
+font pango:monospace 8
+floating_modifier Mod1
+default_border pixel 2
+default_floating_border pixel 2
+hide_edge_borders none
+focus_wrapping no
+focus_follows_mouse yes
+focus_on_window_activation smart
+mouse_warping output
+workspace_layout default
+workspace_auto_back_and_forth no
+
+client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
+client.unfocused #333333 #222222 #888888 #292d2e #222222
+client.urgent #2f343a #900000 #ffffff #900000 #900000
+client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
+client.background #ffffff
+
+bindsym Mod1+1 workspace number 1
+bindsym Mod1+2 workspace number 2
+bindsym Mod1+3 workspace number 3
+bindsym Mod1+4 workspace number 4
+bindsym Mod1+5 workspace number 5
+bindsym Mod1+6 workspace number 6
+bindsym Mod1+7 workspace number 7
+bindsym Mod1+8 workspace number 8
+bindsym Mod1+9 workspace number 9
+bindsym Mod1+Down focus down
+bindsym Mod1+Left focus left
+bindsym Mod1+Return exec @rxvt-unicode-unwrapped@/bin/urxvt
+bindsym Mod1+Right focus right
+bindsym Mod1+Shift+1 move container to workspace number 1
+bindsym Mod1+Shift+2 move container to workspace number 2
+bindsym Mod1+Shift+3 move container to workspace number 3
+bindsym Mod1+Shift+4 move container to workspace number 4
+bindsym Mod1+Shift+5 move container to workspace number 5
+bindsym Mod1+Shift+6 move container to workspace number 6
+bindsym Mod1+Shift+7 move container to workspace number 7
+bindsym Mod1+Shift+8 move container to workspace number 8
+bindsym Mod1+Shift+9 move container to workspace number 9
+bindsym Mod1+Shift+Down move down
+bindsym Mod1+Shift+Left move left
+bindsym Mod1+Shift+Right move right
+bindsym Mod1+Shift+Up move up
+bindsym Mod1+Shift+c reload
+bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
+bindsym Mod1+Shift+h move left
+bindsym Mod1+Shift+j move down
+bindsym Mod1+Shift+k move up
+bindsym Mod1+Shift+l move right
+bindsym Mod1+Shift+minus move scratchpad
+bindsym Mod1+Shift+q kill
+bindsym Mod1+Shift+space floating toggle
+bindsym Mod1+Up focus up
+bindsym Mod1+a focus parent
+bindsym Mod1+b splith
+bindsym Mod1+d exec @dmenu@/bin/dmenu_run
+bindsym Mod1+e layout toggle split
+bindsym Mod1+f fullscreen toggle
+bindsym Mod1+h focus left
+bindsym Mod1+j focus down
+bindsym Mod1+k focus up
+bindsym Mod1+l focus right
+bindsym Mod1+minus scratchpad show
+bindsym Mod1+r mode resize
+bindsym Mod1+s layout stacking
+bindsym Mod1+space focus mode_toggle
+bindsym Mod1+v splitv
+bindsym Mod1+w layout tabbed
+
+
+
+mode "resize" {
+bindsym Down resize grow height 10 px
+bindsym Escape mode default
+bindsym Left resize shrink width 10 px
+bindsym Return mode default
+bindsym Right resize grow width 10 px
+bindsym Up resize shrink height 10 px
+bindsym h resize shrink width 10 px
+bindsym j resize grow height 10 px
+bindsym k resize shrink height 10 px
+bindsym l resize grow width 10 px
+}
+
+
+bar {
+
+ font pango:monospace 8
+ mode dock
+ hidden_state hide
+ position bottom
+ status_command @i3status@/bin/i3status
+ swaybar_command @sway/bin/swaybar
+ workspace_buttons yes
+ strip_workspace_numbers no
+ tray_output primary
+ colors {
+ background #000000
+ statusline #ffffff
+ separator #666666
+ focused_workspace #4c7899 #285577 #ffffff
+ active_workspace #333333 #5f676a #ffffff
+ inactive_workspace #333333 #222222 #888888
+ urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #2f343a #900000 #ffffff
+ }
+
+}
+
+
+
+
+
+
+exec "systemctl --user import-environment; systemctl --user start sway-session.target"
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-default.nix b/home-manager/tests/modules/services/window-managers/sway/sway-default.nix
new file mode 100644
index 00000000000..09c388c1c51
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-default.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ wayland.windowManager.sway = {
+ enable = true;
+ package = pkgs.runCommandLocal "dummy-package" { } "mkdir $out" // {
+ outPath = "@sway";
+ };
+ # overriding findutils causes issues
+ config.menu = "${pkgs.dmenu}/bin/dmenu_run";
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dummy-package = super.runCommandLocal "dummy-package" { } "mkdir $out";
+ dmenu = self.dummy-package // { outPath = "@dmenu@"; };
+ rxvt-unicode-unwrapped = self.dummy-package // {
+ outPath = "@rxvt-unicode-unwrapped@";
+ };
+ i3status = self.dummy-package // { outPath = "@i3status@"; };
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/sway/config
+ assertFileContent home-files/.config/sway/config \
+ ${./sway-default.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-expected.conf b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-expected.conf
new file mode 100644
index 00000000000..198ce4bd37f
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-expected.conf
@@ -0,0 +1,94 @@
+font pango:monospace 8
+floating_modifier Mod1
+default_border pixel 2
+default_floating_border pixel 2
+hide_edge_borders none
+focus_wrapping no
+focus_follows_mouse always
+focus_on_window_activation smart
+mouse_warping output
+workspace_layout default
+workspace_auto_back_and_forth no
+
+client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
+client.unfocused #333333 #222222 #888888 #292d2e #222222
+client.urgent #2f343a #900000 #ffffff #900000 #900000
+client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
+client.background #ffffff
+
+bindsym Mod1+1 workspace number 1
+bindsym Mod1+2 workspace number 2
+bindsym Mod1+3 workspace number 3
+bindsym Mod1+4 workspace number 4
+bindsym Mod1+5 workspace number 5
+bindsym Mod1+6 workspace number 6
+bindsym Mod1+7 workspace number 7
+bindsym Mod1+8 workspace number 8
+bindsym Mod1+9 workspace number 9
+bindsym Mod1+Down focus down
+bindsym Mod1+Left focus left
+bindsym Mod1+Return exec @rxvt-unicode-unwrapped@/bin/urxvt
+bindsym Mod1+Right focus right
+bindsym Mod1+Shift+1 move container to workspace number 1
+bindsym Mod1+Shift+2 move container to workspace number 2
+bindsym Mod1+Shift+3 move container to workspace number 3
+bindsym Mod1+Shift+4 move container to workspace number 4
+bindsym Mod1+Shift+5 move container to workspace number 5
+bindsym Mod1+Shift+6 move container to workspace number 6
+bindsym Mod1+Shift+7 move container to workspace number 7
+bindsym Mod1+Shift+8 move container to workspace number 8
+bindsym Mod1+Shift+9 move container to workspace number 9
+bindsym Mod1+Shift+Down move down
+bindsym Mod1+Shift+Left move left
+bindsym Mod1+Shift+Right move right
+bindsym Mod1+Shift+Up move up
+bindsym Mod1+Shift+c reload
+bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
+bindsym Mod1+Shift+h move left
+bindsym Mod1+Shift+j move down
+bindsym Mod1+Shift+k move up
+bindsym Mod1+Shift+l move right
+bindsym Mod1+Shift+minus move scratchpad
+bindsym Mod1+Shift+q kill
+bindsym Mod1+Shift+space floating toggle
+bindsym Mod1+Up focus up
+bindsym Mod1+a focus parent
+bindsym Mod1+b splith
+bindsym Mod1+d exec @dmenu@/bin/dmenu_run
+bindsym Mod1+e layout toggle split
+bindsym Mod1+f fullscreen toggle
+bindsym Mod1+h focus left
+bindsym Mod1+j focus down
+bindsym Mod1+k focus up
+bindsym Mod1+l focus right
+bindsym Mod1+minus scratchpad show
+bindsym Mod1+r mode resize
+bindsym Mod1+s layout stacking
+bindsym Mod1+space focus mode_toggle
+bindsym Mod1+v splitv
+bindsym Mod1+w layout tabbed
+
+
+
+mode "resize" {
+bindsym Down resize grow height 10 px
+bindsym Escape mode default
+bindsym Left resize shrink width 10 px
+bindsym Return mode default
+bindsym Right resize grow width 10 px
+bindsym Up resize shrink height 10 px
+bindsym h resize shrink width 10 px
+bindsym j resize grow height 10 px
+bindsym k resize shrink height 10 px
+bindsym l resize grow width 10 px
+}
+
+
+
+
+
+
+
+
+exec "systemctl --user import-environment; systemctl --user start sway-session.target"
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy-expected.conf b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy-expected.conf
new file mode 100644
index 00000000000..cbc55722a01
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy-expected.conf
@@ -0,0 +1,94 @@
+font pango:monospace 8
+floating_modifier Mod1
+default_border pixel 2
+default_floating_border pixel 2
+hide_edge_borders none
+focus_wrapping no
+focus_follows_mouse no
+focus_on_window_activation smart
+mouse_warping output
+workspace_layout default
+workspace_auto_back_and_forth no
+
+client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
+client.unfocused #333333 #222222 #888888 #292d2e #222222
+client.urgent #2f343a #900000 #ffffff #900000 #900000
+client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
+client.background #ffffff
+
+bindsym Mod1+1 workspace number 1
+bindsym Mod1+2 workspace number 2
+bindsym Mod1+3 workspace number 3
+bindsym Mod1+4 workspace number 4
+bindsym Mod1+5 workspace number 5
+bindsym Mod1+6 workspace number 6
+bindsym Mod1+7 workspace number 7
+bindsym Mod1+8 workspace number 8
+bindsym Mod1+9 workspace number 9
+bindsym Mod1+Down focus down
+bindsym Mod1+Left focus left
+bindsym Mod1+Return exec @rxvt-unicode-unwrapped@/bin/urxvt
+bindsym Mod1+Right focus right
+bindsym Mod1+Shift+1 move container to workspace number 1
+bindsym Mod1+Shift+2 move container to workspace number 2
+bindsym Mod1+Shift+3 move container to workspace number 3
+bindsym Mod1+Shift+4 move container to workspace number 4
+bindsym Mod1+Shift+5 move container to workspace number 5
+bindsym Mod1+Shift+6 move container to workspace number 6
+bindsym Mod1+Shift+7 move container to workspace number 7
+bindsym Mod1+Shift+8 move container to workspace number 8
+bindsym Mod1+Shift+9 move container to workspace number 9
+bindsym Mod1+Shift+Down move down
+bindsym Mod1+Shift+Left move left
+bindsym Mod1+Shift+Right move right
+bindsym Mod1+Shift+Up move up
+bindsym Mod1+Shift+c reload
+bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
+bindsym Mod1+Shift+h move left
+bindsym Mod1+Shift+j move down
+bindsym Mod1+Shift+k move up
+bindsym Mod1+Shift+l move right
+bindsym Mod1+Shift+minus move scratchpad
+bindsym Mod1+Shift+q kill
+bindsym Mod1+Shift+space floating toggle
+bindsym Mod1+Up focus up
+bindsym Mod1+a focus parent
+bindsym Mod1+b splith
+bindsym Mod1+d exec @dmenu@/bin/dmenu_run
+bindsym Mod1+e layout toggle split
+bindsym Mod1+f fullscreen toggle
+bindsym Mod1+h focus left
+bindsym Mod1+j focus down
+bindsym Mod1+k focus up
+bindsym Mod1+l focus right
+bindsym Mod1+minus scratchpad show
+bindsym Mod1+r mode resize
+bindsym Mod1+s layout stacking
+bindsym Mod1+space focus mode_toggle
+bindsym Mod1+v splitv
+bindsym Mod1+w layout tabbed
+
+
+
+mode "resize" {
+bindsym Down resize grow height 10 px
+bindsym Escape mode default
+bindsym Left resize shrink width 10 px
+bindsym Return mode default
+bindsym Right resize grow width 10 px
+bindsym Up resize shrink height 10 px
+bindsym h resize shrink width 10 px
+bindsym j resize grow height 10 px
+bindsym k resize shrink height 10 px
+bindsym l resize grow width 10 px
+}
+
+
+
+
+
+
+
+
+exec "systemctl --user import-environment; systemctl --user start sway-session.target"
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy.nix b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy.nix
new file mode 100644
index 00000000000..9b80a63bc59
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse-legacy.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ wayland.windowManager.sway = {
+ enable = true;
+
+ config = {
+ focus.followMouse = false;
+ menu = "${pkgs.dmenu}/bin/dmenu_run";
+ bars = [ ];
+ };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dmenu = super.dmenu // { outPath = "@dmenu@"; };
+ rxvt-unicode-unwrapped = super.rxvt-unicode-unwrapped // {
+ outPath = "@rxvt-unicode-unwrapped@";
+ };
+ sway-unwrapped =
+ pkgs.runCommandLocal "dummy-sway-unwrapped" { version = "1"; }
+ "mkdir $out";
+ swaybg = pkgs.writeScriptBin "dummy-swaybg" "";
+ xwayland = pkgs.writeScriptBin "xwayland" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/sway/config
+ assertFileContent home-files/.config/sway/config \
+ ${./sway-followmouse-legacy-expected.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-followmouse.nix b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse.nix
new file mode 100644
index 00000000000..e05b4e56fc9
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-followmouse.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ wayland.windowManager.sway = {
+ enable = true;
+
+ config = {
+ focus.followMouse = "always";
+ menu = "${pkgs.dmenu}/bin/dmenu_run";
+ bars = [ ];
+ };
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dmenu = super.dmenu // { outPath = "@dmenu@"; };
+ rxvt-unicode-unwrapped = super.rxvt-unicode-unwrapped // {
+ outPath = "@rxvt-unicode-unwrapped@";
+ };
+ sway-unwrapped =
+ pkgs.runCommandLocal "dummy-sway-unwrapped" { version = "1"; }
+ "mkdir $out";
+ swaybg = pkgs.writeScriptBin "dummy-swaybg" "";
+ xwayland = pkgs.writeScriptBin "xwayland" "";
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/sway/config
+ assertFileContent home-files/.config/sway/config \
+ ${./sway-followmouse-expected.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/services/window-managers/sway/sway-post-2003.nix b/home-manager/tests/modules/services/window-managers/sway/sway-post-2003.nix
new file mode 100644
index 00000000000..3eab6538e42
--- /dev/null
+++ b/home-manager/tests/modules/services/window-managers/sway/sway-post-2003.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ home.stateVersion = "20.09";
+
+ wayland.windowManager.sway = {
+ enable = true;
+ package = pkgs.runCommandLocal "dummy-package" { } "mkdir $out" // {
+ outPath = "@sway";
+ };
+ # overriding findutils causes issues
+ config.menu = "${pkgs.dmenu}/bin/dmenu_run";
+ };
+
+ nixpkgs.overlays = [
+ (self: super: {
+ dummy-package = super.runCommandLocal "dummy-package" { } "mkdir $out";
+ dmenu = self.dummy-package // { outPath = "@dmenu@"; };
+ rxvt-unicode-unwrapped = self.dummy-package // {
+ outPath = "@rxvt-unicode-unwrapped@";
+ };
+ i3status = self.dummy-package // { outPath = "@i3status@"; };
+ })
+ ];
+
+ nmt.script = ''
+ assertFileExists home-files/.config/sway/config
+ assertFileContent home-files/.config/sway/config \
+ ${./sway-default.conf}
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/systemd/services.nix b/home-manager/tests/modules/systemd/services.nix
index f1f7a42bb1a..ea9b2b4fb87 100644
--- a/home-manager/tests/modules/systemd/services.nix
+++ b/home-manager/tests/modules/systemd/services.nix
@@ -15,7 +15,7 @@ with lib;
};
nmt.script = ''
- local serviceFile=home-files/.config/systemd/user/test-service@.service
+ serviceFile=home-files/.config/systemd/user/test-service@.service
assertFileExists $serviceFile
assertFileContent $serviceFile ${./services-expected.conf}
'';
diff --git a/home-manager/tests/modules/systemd/session-variables.nix b/home-manager/tests/modules/systemd/session-variables.nix
index 7cfb4a6c7c3..b725827ce69 100644
--- a/home-manager/tests/modules/systemd/session-variables.nix
+++ b/home-manager/tests/modules/systemd/session-variables.nix
@@ -10,7 +10,7 @@ with lib;
};
nmt.script = ''
- local envFile=home-files/.config/environment.d/10-home-manager.conf
+ envFile=home-files/.config/environment.d/10-home-manager.conf
assertFileExists $envFile
assertFileContent $envFile ${./session-variables-expected.conf}
'';
diff --git a/home-manager/tests/modules/systemd/timers.nix b/home-manager/tests/modules/systemd/timers.nix
index eaa43f17695..1c0e2722299 100644
--- a/home-manager/tests/modules/systemd/timers.nix
+++ b/home-manager/tests/modules/systemd/timers.nix
@@ -13,8 +13,8 @@ with lib;
};
nmt.script = ''
- local unitDir=home-files/.config/systemd/user
- local timerFile=$unitDir/test-timer.timer
+ unitDir=home-files/.config/systemd/user
+ timerFile=$unitDir/test-timer.timer
assertFileExists $timerFile
assertFileContent $timerFile ${./timers-expected.conf}
diff --git a/home-manager/tests/modules/targets-darwin/darwin.nix b/home-manager/tests/modules/targets-darwin/darwin.nix
new file mode 100644
index 00000000000..511ae87fd98
--- /dev/null
+++ b/home-manager/tests/modules/targets-darwin/darwin.nix
@@ -0,0 +1,20 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ darwinTestApp = pkgs.runCommandLocal "target-darwin-example-app" { } ''
+ mkdir -p $out/Applications
+ touch $out/Applications/example-app
+ '';
+
+in {
+ config = {
+ home.packages = [ darwinTestApp ];
+
+ nmt.script = ''
+ assertFileExists 'home-files/Applications/Home Manager Apps/example-app'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/targets-darwin/default.nix b/home-manager/tests/modules/targets-darwin/default.nix
new file mode 100644
index 00000000000..5c04854ff2e
--- /dev/null
+++ b/home-manager/tests/modules/targets-darwin/default.nix
@@ -0,0 +1,5 @@
+{
+ # Disabled for now due to conflicting behavior with nix-darwin. See
+ # https://github.com/rycee/home-manager/issues/1341#issuecomment-687286866
+ #targets-darwin = ./darwin.nix;
+}
diff --git a/home-manager/tests/modules/targets-linux/default.nix b/home-manager/tests/modules/targets-linux/default.nix
new file mode 100644
index 00000000000..e13617ccb74
--- /dev/null
+++ b/home-manager/tests/modules/targets-linux/default.nix
@@ -0,0 +1 @@
+{ targets-generic-linux = ./generic-linux.nix; }
diff --git a/home-manager/tests/modules/targets-linux/generic-linux.nix b/home-manager/tests/modules/targets-linux/generic-linux.nix
new file mode 100644
index 00000000000..10481b5e988
--- /dev/null
+++ b/home-manager/tests/modules/targets-linux/generic-linux.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ config = {
+ targets.genericLinux = {
+ enable = true;
+ extraXdgDataDirs = [ "/foo" ];
+ };
+
+ nmt.script = ''
+ assertFileExists home-path/etc/profile.d/hm-session-vars.sh
+ assertFileContains \
+ home-path/etc/profile.d/hm-session-vars.sh \
+ 'export XDG_DATA_DIRS="''${NIX_STATE_DIR:-/nix/var/nix}/profiles/default/share:/home/hm-user/.nix-profile/share:/foo''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS"'
+ assertFileContains \
+ home-path/etc/profile.d/hm-session-vars.sh \
+ '. "${pkgs.nix}/etc/profile.d/nix.sh"'
+ '';
+ };
+}
diff --git a/home-manager/tests/modules/xresources/default.nix b/home-manager/tests/modules/xresources/default.nix
index afd15fbd300..70b3e6b4a9c 100644
--- a/home-manager/tests/modules/xresources/default.nix
+++ b/home-manager/tests/modules/xresources/default.nix
@@ -1 +1,4 @@
-{ xresources = ./xresources.nix; }
+{
+ xresources = ./xresources.nix;
+ xresources-empty-properties = ./empty.nix;
+}
diff --git a/home-manager/tests/modules/xresources/empty.nix b/home-manager/tests/modules/xresources/empty.nix
new file mode 100644
index 00000000000..9dd80176ff3
--- /dev/null
+++ b/home-manager/tests/modules/xresources/empty.nix
@@ -0,0 +1,13 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+ config = {
+ xresources.properties = { };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.Xresources
+ '';
+ };
+}