Compare commits

...

266 Commits

Author SHA1 Message Date
Robert Helgesson
c2945576b7 news: add entry for release-17.09 being unmaintained 2018-05-06 22:44:26 +02:00
Robert Helgesson
bea5f92ec5 ssh: move options to end of configuration file
This is needed to support overriding these options inside match
blocks. A new option `programs.ssh.extraOptionOverrides` has been
added to allow global overrides.

(cherry picked from commit 4205c91609)
2018-03-25 20:58:11 +02:00
Robert Helgesson
963de93c55 nixpkgs: expand description of nixpkgs.config and nixpkgs.overlays
(cherry picked from commit 75c4075345)
2018-03-24 20:30:53 +01:00
Gleb Peregud
eda46344a0 fzf: add enableZshIntegration option
When enabled this will extend user's `$HOME/.zshrc` with sourcing of fzf's
completion and key-bindings integration libraries.

(cherry picked from commit f8398339a3)
2018-03-14 17:44:58 +01:00
Gleb Peregud
5db56a320c gpg-agent: add enableExtraSocket and verbose options.
This option enables a GPG Agent restricted socket (aka "extra-socket"), which
can be used to forward GPG Agent over SSH.

Additionally `verbose` option enables verbose output of an `gpg-agent.service`
unit for easier debugging.

See: https://wiki.gnupg.org/AgentForwarding
(cherry picked from commit 9bf9e7ac5c)
2018-03-14 17:44:58 +01:00
Robert Helgesson
698094b4d6 activation-init: sanity check oldGenNum and oldGenPath
Something is terribly wrong if one is set but not the other so error
out with a message if that happens.

(cherry picked from commit 567b21b1d6)
2018-03-14 17:44:58 +01:00
Robert Helgesson
7943291ac4 fzf: add module
(cherry picked from commit fa7d63d9d1)
2018-03-14 17:44:58 +01:00
Robert Helgesson
59dee6a501 texlive: add option programs.texlive.package
This read-only option will hold a reference to the customized texlive
package.

(cherry picked from commit 46a94cce56)
2018-03-14 17:44:58 +01:00
Robert Helgesson
967d0f93a2 gpg-agent: do updatestartuptty only when SSH is enabled
Inspired by #163.

(cherry picked from commit bc50202d0d)
2018-03-14 17:44:58 +01:00
Tad Fisher
337168f47e unclutter: add module
(cherry picked from commit 8fc8e158e2)
2018-03-14 17:44:58 +01:00
Robert Helgesson
124acb089c home-manager: colorize only when connected to terminal
Before, the output of `home-manager generations` would be colorized
even when used in a pipeline.

(cherry picked from commit 06e7d087f2)
2018-03-03 22:02:17 +01:00
Robert Helgesson
b9382832ad xscreensaver: install the xscreensaver package
This is needed to make the xscreensaver tools available.

(cherry picked from commit fbff38de33)
2018-03-03 22:01:56 +01:00
Robert Helgesson
f7628e2996 home-environment: use nix-env from PATH
It is safest to use the system install of Nix since that will be
compatible with the running nix-daemon and/or databases.

Also add a printout of the used Nix version in the activation script
when running in verbose mode.

Fixes #218.

(cherry picked from commit 19b4002f25)
2018-02-27 20:34:44 +01:00
Robert Helgesson
467ba9cafd qsyncthingtray: remove deprecated option
(cherry picked from commit b47cc4bc66)
2018-02-26 22:33:20 +01:00
Robert Helgesson
6730c32c98 systemd: replace use of who command
Curiously the `who` command sometimes does not list logged-in users,
resulting in systemd not being reloaded. Instead we use

    systemctl --user is-system-running

to more directly detect whether systemd is running.

(cherry picked from commit e307ceeee7)
2018-02-26 22:33:12 +01:00
Robert Helgesson
b6affe8d57 pidgin: add module
(cherry picked from commit 4745c7a00d)
2018-02-20 22:02:34 +01:00
Robert Helgesson
ef5a8a5941 Add initial Travis-CI configuration
(cherry picked from commit 5c783e1a63)
2018-02-20 22:02:06 +01:00
Nikita Uvarov
de2c181eae i3: escape ${} in bars.command example
(cherry picked from commit 05ad0c9e06)
2018-02-20 22:02:06 +01:00
Matthieu Coudron
5fd31df9d3 i3: don't evaluate "command" example
else it attempts to build i3-gaps and fails on darwin see
https://github.com/rycee/home-manager/pull/214#issuecomment-366594833

(cherry picked from commit 6d7b5c9513)
2018-02-20 22:02:06 +01:00
Nikita Uvarov
61101184b4 i3: add missing bar options
New options are: id, commmand, workspaceNumbers, colors.
Fixes #210.

(cherry picked from commit de001e05da)
2018-02-20 22:02:06 +01:00
Matthieu Coudron
474478c4a3 neovim: add 'configure' flag
so that we have the same options as in nixpkgs.

(cherry picked from commit be60600a47)
2018-02-20 22:02:06 +01:00
Nikita Uvarov
afa865587e zsh: move env variables setting before oh-my-zsh
Fixes #207.
2018-02-10 19:23:10 +01:00
Robert Helgesson
9ea353569a Remove deprecated option home.sessionVariableSetter 2018-02-08 22:54:29 +01:00
Robert Helgesson
1bc59f7290 allow Home Manager to be used as a NixOS module
This is a NixOS module that is intended to be imported into a NixOS
system configuration. It allows the system users to be set up directly
from the system configuration.

The actual profile switch is performed by a oneshot systemd unit per
configured user that acts much like the regular `home-manager switch`
command.

With this implementation, the NixOS module does not work properly with
the `nixos-rebuild build-vm` command. This can be solved by using the
`users.users.<name?>.packages` option to install packages but this
does not work flawlessly with certain Nixpkgs packages. In particular,
for programs using the Qt libraries.
2018-02-07 20:50:01 +01:00
Christopher League
563a20fc82 xcursor: add module
This is a new module for configuring the X cursor theme.
2018-02-05 22:40:16 +01:00
Robert Helgesson
6cca1fc512 faq: add basic FAQ 2018-02-05 22:27:42 +01:00
Mogria
6833f96c14 rofi: add options to for location, xoffset & yoffset 2018-02-03 22:07:53 +01:00
Nikita Uvarov
2304c145f3 zsh: add system packages' completion path to fpath 2018-02-03 21:57:44 +01:00
Nikita Uvarov
fa6f697dbb zsh: move session variables export to zshrc
Unlike .zshenv, .zshrc file is sourced only by interactive shells.
2018-02-03 21:16:00 +01:00
Alistair Potts
91a98f919d stalonetray: add module
Adds a service for the Stalonetray system tray.

Configured through a 'config' attribute set, which writes space
separated key value pairs on successive lines to `~/.stalonetrayrc`.
2018-02-03 10:44:37 +01:00
Alistair Potts
616dbd67f7 mercurial: add module
Very simple module for hg based on programs.git, and is intended to have
compatible options. For simple setups, a user should be able to write
something like:

    {...}:
    let vcsconfig = {
            enable = true;
            userName = "John Smith";
            userEmail = "js@example.com";
            ignores = [ "*.swp" "*~" ];
        };
    in
    {
        programs.git       = vcsconfig // {...extra git config...};
        programs.mercurial = vcsconfig // {...extra hg confg...};
    }

For this reason, the ignore options are `ignores` for `syntax: glob`
and `ignoresRegexp` for `syntax: regexp` so that simple glob ignores
can (very likely) be shared with a git config, despite regular
expressions being the default for mercurial.
2018-02-02 20:52:54 +01:00
Robert Helgesson
6fc0fd315c syncthing: allow enabling tray independently 2018-02-01 06:59:22 +01:00
Robert Helgesson
9de2549dfb contributing: explain how to use local clone
Fixes #180.
2018-01-28 18:37:01 +01:00
Robert Helgesson
81fb420457 home-manager: switch NIX_PATH order
This new order allows overriding the home-manager path from the
command line using `home-manager -I home-manager=/a/b/c`.
2018-01-28 18:37:01 +01:00
Nikita Uvarov
8b77f1db2c syncthing: start tray service after bars
The QSyncthingTray service requires running tray providers such as
polybar and taffybar.
2018-01-27 17:18:39 +01:00
Robert Helgesson
a154e2ea1a readme: add basic rollback instructions 2018-01-27 10:22:54 +01:00
Robert Helgesson
5fe8d574ca home-manager: add shellcheck directives 2018-01-27 09:49:35 +01:00
Robert Helgesson
a9dc7fa7cc home-manager: improve the generation timestamp format 2018-01-27 09:47:30 +01:00
Robert Helgesson
9d3d7426aa license: bump year 2018-01-26 19:39:28 +01:00
Nikita Uvarov
a597c66afe syncthing: merge qsyncthingtray into the module 2018-01-25 21:52:01 +01:00
Mario Rodas
21fefbc8f6 home-manager: check whether a command is passed
"set -u" treats unset variables as an error, and $1 is unbound when no
command is passed.
2018-01-22 19:18:10 +01:00
Nadrieril
38020d9068 redshift: add option to start redshift tray applet 2018-01-22 19:09:13 +01:00
Nikita Uvarov
1b0a5eb54a polybar: fix the case when config value is a path
Polybar treats 'include-file' property differently.
In particular, its value can't be enclosed in
double quotes. Fixes #185.
2018-01-21 21:01:26 +01:00
André-Patrick Bubel
071f7aea82 qsyncthingtray: add module 2018-01-20 11:51:39 +01:00
Robert Helgesson
32b3f7f2d2 ssh: allow disabling compression in host block
Fixes #181.
2018-01-14 22:08:31 +01:00
Robert Helgesson
576217d33a gpg-agent: use gpgconf to set SSH socket path
Inspired by #163.
2018-01-14 15:58:59 +01:00
John Wiegley
b8b595c6b2 ssh: add a few more options 2018-01-13 12:47:30 +01:00
Nikita Uvarov
a93445f3fe zsh: add history.save option 2018-01-13 11:38:39 +01:00
Nikita Uvarov
dbcb3dd1ae zsh: fix HISTSIZE and HISTFILE configuration
HISTSIZE and HISTFILE should be set in ~/.zshrc and before
sourcing oh-my-zsh since otherwise it will be overridden.
Fixes #177.
2018-01-13 11:38:39 +01:00
Nadrieril
d6ab6ee370 ssh: add extraConfig option for non-standard options 2018-01-10 22:24:03 +01:00
John Wiegley
c9294e30d9 bash: add option historyFile 2018-01-09 22:34:20 +01:00
Robert Helgesson
d7715f71ad eclipse: add option enableLombok 2018-01-09 22:03:13 +01:00
Robert Helgesson
b2ed0a902b Merge branch 'session-var-cleanup' 2018-01-08 21:45:46 +01:00
Robert Helgesson
18159c85b9 home-environment: deprecate option home.sessionVariableSetter 2018-01-08 21:40:32 +01:00
Robert Helgesson
d7755de116 pam: add option pam.sessionVariables 2018-01-07 17:52:13 +01:00
Robert Helgesson
7631921366 zsh: source session variables script
This replaces the explicit set within the Z shell `zshenv` file.
2018-01-07 17:52:13 +01:00
Robert Helgesson
803abb58f9 bash: source session variables script
This replaces the explicit set within the Bash profile file.
2018-01-07 17:52:13 +01:00
Robert Helgesson
a3250dfac7 xsession: source session variables script 2018-01-07 17:52:13 +01:00
Robert Helgesson
4f9158e533 readme: add note about session variables file
If a user does not want to manage their shell configuration through
Home Manager then they have to manually make sure that the session
variables are set.
2018-01-07 17:52:13 +01:00
Robert Helgesson
e624b9aa6a home-environment: install hm-session-vars.sh file
This is a file containing all session variables exported using a
Bourne-compatible syntax.
2018-01-07 17:52:13 +01:00
Robert Helgesson
2fc1b9b5e0 zsh: use shell library 2018-01-07 17:52:12 +01:00
Robert Helgesson
026375da49 bash: use shell library 2018-01-07 17:51:51 +01:00
Robert Helgesson
58a629b02e lib/shell: add library of convenience functions
This library holds a few convenience functions for generating shell
code.
2018-01-07 16:59:22 +01:00
Robert Helgesson
df6590abfc home-environment: describe session variable values a bit 2018-01-07 16:59:22 +01:00
Robert Helgesson
33af9948e5 home-environment: describe session variable trickyness 2018-01-07 16:59:22 +01:00
Robert Helgesson
8ab6298f30 bash: do not export HIST* variables
These are interpreted by the shell itself and it does not make sense
to export them to sub-processes.
2018-01-07 16:54:29 +01:00
Robert Helgesson
78c308c835 bash: add option bashrcExtra
This variable adds some extra flexibility in constructing the
`~/.bashrc` file. Currently the option is hidden from public
documentation since the option name is provisional.
2018-01-07 15:15:32 +01:00
Robert Helgesson
8a2bf21cee bash: reword option descriptions 2018-01-07 15:04:57 +01:00
Robert Helgesson
59f44c1189 home-environment: run activation script in $HOME
This avoids issues when starting the activation script somewhere
inaccessible.
2018-01-05 08:09:18 +01:00
Robert Helgesson
02219dcd79 home-environment: minor code simplification 2018-01-05 08:05:53 +01:00
Robert Helgesson
f0d207f380 Add dag library to config.lib
Also replace all imports of `dag.nix` by the entry in `config.lib`.
2017-12-26 17:27:21 +01:00
Robert Helgesson
7dd09cecda gtk: remove deprecated options 2017-12-23 12:46:02 +01:00
Robert Helgesson
e75b68e391 home-environment: make username and home directory writable
In certain cases it makes sense to override the target username and
home directory. In particular, if you're building a configuration for
a remote profile.
2017-12-13 16:31:35 +01:00
Silvan Mosberger
8d360c5a57 systemd: remove filename hack 2017-12-12 18:05:04 +01:00
Cornelius Mika
f6900f0689 files: improve 'target not in $HOME' check
Check for prefix instead of inclusion.
2017-12-12 17:46:48 +01:00
Cornelius Mika
8759a5a63e systemd: add option to automatically start services 2017-12-11 18:25:49 +01:00
Cornelius Mika
52bdbc42bb systemd: move activation script to separate file
This makes the following commit more readable.
2017-12-11 18:08:33 +01:00
Robert Helgesson
28e00b68fd home-environment: optionally empty PATH in activation
This adds the option `home.emptyActivationPath` that, when enabled,
will cause the activation script to ignore the calling user's `PATH`.
The option is disabled by default to match current behavior but the
intent is to change this in the future to reduce risk of accidental
dependencies of the environment.
2017-12-11 17:03:34 +01:00
Robert Helgesson
2ff09158f3 systemd: fix systemctl command
The command's path should be taken from the configuration, not be
assumed to be in `PATH`.
2017-12-11 16:58:50 +01:00
Robert Helgesson
6764c26954 files: remove mode option
This option was deprecated >1 month ago and is therefore removed as
per the corresponding news entry.
2017-12-11 15:14:45 +01:00
Robert Helgesson
040159c02f modules: fix sorting 2017-12-11 13:51:22 +01:00
Robert Helgesson
61c6c83de4 modules: do not import modules
The `evalModules` function is smart enough to import modules and will
then also use correct file names.
2017-12-11 13:48:46 +01:00
Silvan Mosberger
0be32c9d42 xmonad: make package lower priority
This avoids a conflict for when the user has an xmonad package
installed through `haskellPackages.ghcWithPackages`, which is
necessary for wanting to load the xmonad config with ghc.
2017-12-08 11:48:05 +01:00
Gleb Peregud
aa1bf31bcb parcellite: add module
This adds a Parcellite service. It has no configuration options, since
the app has its own mutable preferences dialog, which unconditionally
replaces `~/.config/parcellite/parcelliterc` when preferences are
saved.
2017-12-08 00:40:28 +01:00
Roman Volosatovs
c023b0532a gpg-agent: add missing options 2017-12-02 19:44:53 +01:00
Silvan Mosberger
f8aaba6704 lib: add module 2017-12-02 18:11:53 +01:00
Robert Helgesson
26cf42049d readme: minor rewording 2017-11-29 23:53:49 +01:00
Robert Helgesson
06d4f39e7b home-manager: use shellHook to install
This changes the installation command from

    nix-shell $HM_PATH -A install --run 'home-manager switch'

to

    nix-shell $HM_PATH -A install

The added shell hook will print some useful information and run
`home-manager switch`.
2017-11-29 23:48:58 +01:00
Robert Helgesson
a1e36a9a37 xmonad: install xmonad command to profile
Fixes #153.
2017-11-29 23:40:43 +01:00
Tad Fisher
11da41e106 kbfs: add binaries to user profile
Add the binaries produced by 'pkgs.kbfs' to the profile, so that
the git-remote-keybase helper can work automatically with
'keybase://' remotes.
2017-11-29 23:35:50 +01:00
Tad Fisher
7a5b9152e9 keybase, kbfs: add modules 2017-11-26 23:11:37 +01:00
Robert Helgesson
7876d533cf gtk: fix erroneous variable reference 2017-11-24 22:25:36 +01:00
Robert Helgesson
e99de88c5c modules core: move modules list to own file
This is to simplify use of Home Manager as a NixOS module.
2017-11-24 21:58:16 +01:00
Robert Helgesson
bcb82da88f gtk: improve theme and font configuration
Specifically, allow the user to specify the package that provides the
theme or font.

Fixes #1.
2017-11-24 21:24:31 +01:00
Robert Helgesson
592fd61788 module/home-manager: remove modulesPath option
This option has been deprecated for a month and is removed according
to the news entry.
2017-11-24 13:51:21 +01:00
Florian Klink
3c875267af i3: config.modes.resize: add Return to defaults 2017-11-21 09:14:13 +01:00
Robert Helgesson
455ce37398 home-manager: avoid unnecessary copy 2017-11-20 14:02:28 +01:00
Robert Helgesson
64befb27eb news: minor formatting fix 2017-11-20 13:58:04 +01:00
Nikita Uvarov
177565567e i3: extend module
New options:
  i3.config.keycodebindings
  i3.config.window.commands
  i3.config.window.hideEdgeBorders
  i3.config.focus.mouseWarping
2017-11-20 11:50:20 +01:00
Nikita Uvarov
8045e56df2 i3: fix config.gaps.smartBorders default value 2017-11-17 09:43:48 +01:00
Nicolò Balzarotti
206a4e17b5 i3: fix small formatting error (missing space) 2017-11-17 09:31:37 +01:00
Florian Klink
2785bf9cb2 i3: correct example for config.floating.criteria
The parameter accepts a listOf criteriaModule
(which is types.attrs, not types.string)
2017-11-17 09:27:23 +01:00
Robert Helgesson
bc2f2ad546 systemd: honor RefuseManualStart and RefuseManualStop
Fixes https://github.com/rycee/home-manager/issues/140
2017-11-16 15:37:53 +01:00
Robert Helgesson
4fce730326 files: log when creating home file links 2017-11-15 18:31:04 +01:00
Robert Helgesson
9206f363ff files: fix order of activation actions
Specifically, move the cleanup phase to before we switch over the
generation links in `gcroots` and `profiles`.

Fixes https://github.com/rycee/home-manager/issues/134
2017-11-15 18:26:42 +01:00
Robert Helgesson
130e33a4e7 contributing: add basic style guidelines
In particular, refer to the Nixpkgs guidelines and add a note about
attribute names referencing packages in Nixpkgs.
2017-11-15 17:02:43 +01:00
Robert Helgesson
a36989a860 gnome-terminal: remove commented code 2017-11-15 17:02:19 +01:00
Nikita Uvarov
1946343d5b i3: add notification option to startup submodule
Fixes https://github.com/rycee/home-manager/issues/129.
2017-11-15 15:43:05 +01:00
Robert Helgesson
4ccf43d753 readme: use IcedTea in example 2017-11-13 00:38:26 +01:00
Robert Helgesson
7ca68c6492 emacs: minor cleanup of extra packages option 2017-11-13 00:03:49 +01:00
Matthieu Coudron
61a869a1f5 neovim: add module
This is a basic module that allows to configure different Neovim
providers than the system ones. Note, it does not generate any
`init.vim`.
2017-11-12 23:57:14 +01:00
Florian Klink
c718951e97 git: add option 'programs.git.ignores'
This allows the global Git ignores to be configured.
2017-11-12 14:22:59 +01:00
Robert Helgesson
04ea044917 home-manager: look for '--help' on command line
This is a special case to work around the lack of long options in
`getopts`.
2017-11-12 14:11:59 +01:00
Robert Helgesson
66219f23bb home-manager: add 'remove-generations' command
This command allows the user to immediately remove specific
generations from the profiles directory.
2017-11-12 14:07:41 +01:00
Robert Helgesson
187a12e90a home-manager: minor cleanup output from generations 2017-11-12 13:37:59 +01:00
Florian Klink
356c0bf751 git: use XDG config directory 2017-11-12 13:16:02 +01:00
Robert Helgesson
7a9c873093 files: add special handling of systemd files
Unfortunately systemd derives nonsensical unit names when the unit
file is a link to a link to a file. This commit ensures that any file
whose target path matches the pattern `*/systemd/user/*` will be
reachable with only one link hop.

This also reverts f52ec0df7c, which
contained a temporary fix. This commit is an improvements in that it
is more explicit and also handles unit files given directly as a home
file source.
2017-11-12 00:56:34 +01:00
Robert Helgesson
d7537777c3 files: improve keyword for inheriting executable bit 2017-11-12 00:13:29 +01:00
Robert Helgesson
145aefc9d1 files: simplify cleanup script slightly
The cleanup script now takes relative paths as arguments, not absolute
paths into the old generation. This uses a GNU specific feature of
find.
2017-11-11 14:21:11 +01:00
Robert Helgesson
2b2e20da24 dunst: use xdg.dataFile for D-Bus service file 2017-11-11 00:31:44 +01:00
Robert Helgesson
a977c79f9f xdg: add option 'xdg.dataFile'
This allows creating files within the user's XDG data directory.
2017-11-11 00:30:53 +01:00
Robert Helgesson
f52ec0df7c systemd: force copying of unit files
This is done by exploiting the fact that home files will be copied if
the executable bit of the source file and the target file is
different. This should be considered a hack until some nicer solution
is found.
2017-11-09 17:14:37 +01:00
Robert Helgesson
54043df8fb files: support recursive linking of directory 2017-11-07 17:18:00 +01:00
Robert Helgesson
fad1e108d8 dunst: add option services.dunst.iconTheme
Fixes #119
2017-11-07 17:13:51 +01:00
BjornMelgaard
fc3e82584b readme: expand preconditions installation step
Also fix markdown list item numbering.
2017-11-06 18:02:15 +01:00
Robert Helgesson
a0afb6ec8e Merge branch 'pr-59' 2017-11-06 14:29:18 +01:00
Robert Helgesson
549deb51d6 xdg: use fileType for xdg.configFile 2017-11-06 14:24:04 +01:00
Robert Helgesson
4f842d9f1b files: extract type of home.file into own file 2017-11-06 14:24:04 +01:00
Cornelius Mika
9627fe6be6 files: link home files instead of copying
Only copy files that need their execute bit changed or use the
deprecated `mode` option.
2017-11-06 14:24:04 +01:00
Cornelius Mika
b8ddb11796 use buildCommand for single phase builds 2017-11-06 14:24:04 +01:00
Cornelius Mika
f04cc227a6 home-environment: clean up activation script creation 2017-11-06 14:24:04 +01:00
Cornelius Mika
811bc1b8e5 files: extract common variable
Also improve the pattern used to determine whether a symlink target
points to a Home Manager file path.
2017-11-06 14:24:04 +01:00
Cornelius Mika
ccb291ce66 files: add option 'executable'
This also deprecates the `home.file.<name?>.mode` option, which is
misleading because the Nix store only allows modes 'r--' and 'r-x'.
2017-11-06 14:24:04 +01:00
Cornelius Mika
676f5c4b31 files: allow arbitrary paths as home file names
By sanitizing the home file name in the derivation name, the home file
name is no longer exposed to the naming restrictions for nix store
paths.

For example, it is now possible to define home files with spaces in
their names without providing a target or source attribute.
2017-11-06 10:34:45 +01:00
Robert Helgesson
14083a0857 home-manager: refuse build if CWD is read-only
This produces a clearer error message than produced by Nix.

Fixes #116.
2017-11-05 19:33:08 +01:00
Anton Strömkvist
b4f5b5556f zsh: Add options for remaining config files
`profileExtra`, `loginExtra` and `logoutExtra` for `zprofile`,
`zlogin`, and `zlogout` respectively
2017-11-02 12:53:43 +01:00
Robert Helgesson
fa4f9197ee home-manager: avoid import to improve error messages
When using `import` to inject the configuration into the module system
we lose the location in error messages, i.e., it just says
``<unknown-file>'`.
2017-11-02 11:19:12 +01:00
Robert Helgesson
bfb5a678d2 modules/home-manager: add warning about modulesPath 2017-11-02 10:32:25 +01:00
Robert Helgesson
d2572315ca i3: use XDG configuration directory
Fixes #111.
2017-11-01 09:51:18 +01:00
Nikita Uvarov
467b774d13 i3: add module 2017-10-31 16:10:42 +01:00
Robert Helgesson
78a1424582 xsession: remove xsession.windowManager option
This removes the deprecated use of `xsession.windowManager` as a
string.

This commit also adjusts the xmonad module to become a full module.
I.e., the backwards compatibility hack was removed.
2017-10-31 14:05:54 +01:00
Robert Helgesson
82d6aa0c97 xdg: fix use of base path 2017-10-31 12:26:39 +01:00
Nikita Uvarov
1213578eb7 zsh: turn fpath into a set
Forcing fpath to contain unique values increases startup speed by
eliminating extra work of processing duplicated folders.
In addition, it increases startup time when zsh is enabled in both
system and home configuration due to having the same fpath value
between different compinit calls.
Fixes https://github.com/rycee/home-manager/issues/108.
2017-10-30 13:53:40 +01:00
Ruben Maher
268d027770 modules/home-manager: fix syntax error
Fixes the error

    error: syntax error, unexpected $undefined, expecting IND_STR or
    DOLLAR_CURLY or IND_STRING_CLOSE, at
    .../home-manager/modules/programs/home-manager.nix:70:47
2017-10-30 10:47:16 +01:00
Robert Helgesson
f55fbe037a Merge branch 'add/xdg' 2017-10-29 02:00:28 +02:00
Robert Helgesson
3a95ff7435 xdg: add news entry 2017-10-29 01:59:34 +02:00
Robert Helgesson
d70715a635 use xdg.configFile for files in XDG config home 2017-10-29 01:59:34 +02:00
Robert Helgesson
54a9058ee0 xdg: add module
When enabled this module will cause Home Manager to manage the user
environment XDG variables. When disabled, then Home Manager will use
the XDG variables taken from the user environment.
2017-10-29 01:59:34 +02:00
Robert Helgesson
efb5256d28 home-manager: use XDG configuration directory 2017-10-29 01:59:34 +02:00
Robert Helgesson
a4c0fead1f files: be less verbose when linking a directory 2017-10-29 01:21:40 +02:00
Silvan Mosberger
35775b3bc5 fix typo 2017-10-24 13:12:18 +02:00
Robert Helgesson
7417d8e86e nixpkgs: add module 2017-10-24 01:29:35 +02:00
Robert Helgesson
df84c466c1 readme: update installation instructions
Also clean up usage section slightly.
2017-10-24 01:17:54 +02:00
Robert Helgesson
bf3a8c6383 home-manager: point <home-manager> to project root
Before this path would point to the modules path. Using the project
root instead makes it possible to set `<home-manager>` to point to a
downloadable archive of Home Manager. This should make it
significantly easier to install and keep Home Manager up to date.

To match this change we also deprecate the Home Manager option

    programs.home-manager.modulesPath

and instead ask users to use

    programs.home-manager.path
2017-10-24 01:14:22 +02:00
Ruben Maher
5605e46acb home-manager: fix typo 2017-10-22 10:59:09 +02:00
Robert Helgesson
3346c7f455 xsession: prepare for session in ~/.xprofile
This works around the way NixOS starts up the systemd graphical
session target.
2017-10-21 14:37:30 +02:00
pasqui23
b78b2b6b35 firefox: add enableIcedTea option 2017-10-21 12:09:39 +02:00
Silvan Mosberger
0f43d5df6a home-environment: add extraBuilderCommands option 2017-10-21 00:22:05 +02:00
Robert Helgesson
30b9d7f00e Use only tools from Nixpkgs in activation script
Note, we still pull in the user's `PATH` in case the user has defined
their own activation blocks that depend on additional tools.
Eventually this will be deprecated and removed.

See #99.
2017-10-20 19:04:33 +02:00
Robert Helgesson
b9f49cee45 home-environment: use makeBinPath for activation PATH 2017-10-19 22:44:02 +02:00
Robert Helgesson
c144580c98 xsession: warn about windowManager option removal 2017-10-19 11:45:04 +02:00
Robert Helgesson
2ff8c12bf9 home-manager: change platforms to unix 2017-10-18 00:45:29 +02:00
Robert Helgesson
335cffe9a9 man: install man, not man-db
This may help with installing on Darwin.
2017-10-18 00:33:31 +02:00
Robert Helgesson
bc40ab378c home-manager: add license field 2017-10-16 18:40:34 +02:00
Silvan Mosberger
d81276607c files: support absolute home directory path 2017-10-15 17:15:16 +02:00
Silvan Mosberger
3bc3b34d97 home-environment: add username and homeDirectory options 2017-10-15 17:15:16 +02:00
Silvan Mosberger
f0a1d69f50 Separate home files module from home-environment.nix 2017-10-15 17:14:32 +02:00
Robert Helgesson
0672936134 info: add "info" to extra outputs to install 2017-10-15 16:03:35 +02:00
Robert Helgesson
3632478b8d man: add module 2017-10-15 16:01:41 +02:00
Robert Helgesson
c07fa70d58 home-environment: add option home.extraOutputsToInstall 2017-10-15 15:58:34 +02:00
Nikita Uvarov
ee7f2413ed zsh: use new option to set internal session vars 2017-10-13 16:34:02 +02:00
Robert Helgesson
12ebf21be5 bash: add sessionVariables option 2017-10-12 15:06:51 +02:00
Nikita Uvarov
691eea9b45 zsh: add sessionVariables option 2017-10-12 14:14:06 +02:00
Nikita Uvarov
7e6f3364bc blueman-applet: add note about required system service 2017-10-11 13:05:47 +02:00
Robert Helgesson
3f430627df fontconfig: add module 2017-10-10 20:29:22 +02:00
Nikita Uvarov
3160c03843 dunst: implement settings parameter 2017-10-09 14:51:07 +02:00
Nikita Uvarov
420a3f4a01 vim: add more vim settings
New settings: copyindent, hidden, ignorecase, modeline, smartcase.
2017-10-09 14:39:56 +02:00
Nikita Uvarov
9eb48312c7 polybar: enclose strings in double quotes
This fixes the case when there are trailing spaces in
string values which can be used for elements padding.
2017-10-09 14:21:43 +02:00
Nikita Uvarov
469caa1a14 polybar: add module 2017-10-07 12:40:08 +02:00
Robert Helgesson
3aca8a938c gpg-agent: use full path to gpg-connect-agent 2017-10-05 19:54:09 +02:00
Robert Helgesson
01d46a1751 readme: current NixOS stable is version 17.09 2017-10-04 20:45:20 +02:00
Robert Helgesson
9c859d2655 xmonad: add module
Adapted from #78 and originally authored by Infinisil.
2017-10-04 20:36:31 +02:00
Robert Helgesson
fb5dbe13c2 readme: minor fixes 2017-10-04 00:24:59 +02:00
Robert Helgesson
e4c359d8b9 udiskie: add a few configuration options
The new options allow some control over automounting, notifications,
and the tray icon.

This commit also changes the defaults to automatically mount new
devices, udiskie was previously told not to automount. The change in
behavior is to closer match the default options.
2017-10-02 13:25:31 +02:00
Nikita Uvarov
52256d7a73 rofi: add fullscreen option 2017-09-30 14:14:07 +02:00
Robert Helgesson
aa974c0dc3 vim: add option programs.vim.settings
This option gathers basic Vim options into a single place. The idea is
to allow many options without making the Home Manager documentation
too verbose.

This also deprecates the options `programs.vim.lineNumbers` and
`programs.vim.tabSize`.

Fixes #69.
2017-09-30 13:30:42 +02:00
Robert Helgesson
23d3539fcb xsession: deprecate xsession.windowManager
The intention is for the `xsession.windowManager` option to be
available for full modules in the future. The option
`xsession.windowManager.command` should now be used to specify the
window manager startup command.
2017-09-30 12:48:47 +02:00
Robert Helgesson
bcff7274f4 vim, zsh: use DocBook links in description 2017-09-30 12:10:52 +02:00
Nikita Uvarov
e9deaf2ca5 rofi: add module 2017-09-28 15:39:25 +02:00
Robert Helgesson
e1bceb2adb readme: add 'console' syntax highlighting 2017-09-27 13:30:05 +02:00
Linus Heckemann
34428fc709 Add overlay and instructions for using it 2017-09-27 13:27:40 +02:00
Nikita Uvarov
393274d142 command-not-found: add module 2017-09-27 09:49:32 +02:00
Robert Helgesson
a8e08d14bb Mark rycee as maintainer for a bunch of modules 2017-09-26 23:40:31 +02:00
Silvan Mosberger
bf9b9026d9 compton: extend module 2017-09-26 14:06:13 +02:00
Nikita Uvarov
0f096f9ad4 git: change extraConfig from lines to attrs 2017-09-22 23:32:38 +02:00
Silvan Mosberger
db55e596d2 zsh: refine module
- fix part of zsh config being built even though cfg.enable is false
- fix .zshenv sourcing when ZDOTDIR already set

and some other minor adjustments
2017-09-22 23:27:49 +02:00
Nikita Uvarov
82c5aa82d2 readme: add man page info 2017-09-21 13:32:16 +02:00
Robert Helgesson
742d1889c5 lib: make dag.nix take lib as argument 2017-09-21 13:19:29 +02:00
Robert Helgesson
61042c7606 lib: use generators from Nixpkgs 2017-09-21 13:18:33 +02:00
Nikita Uvarov
3e4f7228a0 screen-locker: add module 2017-09-20 17:16:21 +02:00
Robert Helgesson
76e0e09aca emacs: allow custom Emacs package 2017-09-19 23:38:18 +02:00
Silvan Mosberger
e4deffcbe8 vim: add package option
This adds a readonly package option which will be set to the resulting
configured vim package, so it can be refered to by other configuration.
An example would be home.sessionVariables.EDITOR =
config.programs.vim.package + "/bin/vim".
2017-09-18 09:22:01 +02:00
Silvan Mosberger
de5f902487 zsh: add custom dotDir parameter 2017-09-16 21:30:47 +02:00
Robert Helgesson
cab9237d95 Add initial CONTRIBUTING file 2017-09-15 16:22:58 +02:00
Robert Helgesson
6ecf9e091c home-environment: fail if a home.file is outside $HOME 2017-09-13 15:23:43 +02:00
Nikita Uvarov
aa69598b57 compton: add module 2017-09-13 14:40:30 +02:00
Robert Helgesson
f47084968d news: minor cleanup
This puts entries in chronological order and adds a comment describing
how to get a suitable time stamp.
2017-09-12 18:08:44 +02:00
Nikita Uvarov
6a8e8e92a7 blueman-applet: add module 2017-09-12 16:44:16 +02:00
Nikita Uvarov
c7edde6ca4 zsh: add user nix-profile dir to fpath
Fixes zsh plugins installed to nix user profile via nixpkgs.
2017-09-12 15:39:36 +02:00
Nikita Uvarov
379e2c694b zsh: move aliases definitions after initExtra
Same motivation as in https://github.com/NixOS/nixpkgs/pull/28378.
zsh.initExtra parameter can be used by external modules which can
redefine user aliases. This change will give user-defined aliases
the highest priority.
2017-09-12 15:21:38 +02:00
Robin Stumm
29d5f5d760 zsh: fix double compinit slowdown with oh-my-zsh
Integrate oh-my-zsh into zsh module
to be able to control invocation order.
2017-09-12 14:52:04 +02:00
Robin Stumm
258bc85b9c zsh: add plugins submodule
To pass compinit security checks,
plugins are liked into ~/zsh/plugins folder.
This also solves issues with a slow start up,
see https://github.com/rycee/home-manager/pull/56#issuecomment-328057513.
2017-09-12 14:42:38 +02:00
Silvan Mosberger
fc1d4f5362 ssh: allow attrset matchBlock 2017-09-12 08:54:18 +02:00
Robert Helgesson
cda222d2ec home-manager: present news even if assertion failed 2017-09-09 17:14:07 +02:00
Robert Helgesson
07b4228988 README: add instructions for release-17.03 branch
(cherry picked from commit 46f0338092)
2017-09-08 12:38:05 +02:00
Robert Helgesson
ad1eee7aa5 home-manager: minor news build cleanups
- Rename the build function.

- Specify the built attribute in the build function.

- Make the news build silent.
2017-09-06 23:44:58 +02:00
Robert Helgesson
9c1b3735b4 home-manager: add news sub-command
This command allows the user to examine the news items generated by
the news module. See #52.

Many thanks to @nonsequitur and @uvNikita for suggestions and
improvements.
2017-09-05 11:37:07 +02:00
Robert Helgesson
ab0338f6ae news: add module
This new module adds a "news" feature to Home Manager. See #52.

Many thanks to @nonsequitur and @uvNikita for suggestions and
improvements.
2017-09-05 11:36:33 +02:00
Robert Helgesson
39fc16954b home-manager: make sure switch generation is GC root
Using `--no-out-link` is convenient but it does not set up a GC root,
so an unfortunately timed GC could remove the generation before
activation completes. Many thanks to @nonsequitur for noting this
problem.
2017-09-04 22:19:56 +02:00
Nikita Uvarov
f5289c546e feh: add module 2017-09-04 21:36:06 +02:00
Jean Potier
721f924e15 zsh: remove search for installed completions
1. It slows down the initial start: it takes around 2s at first launch,
and around 0.25s for the following launches;
2. It seems to be redundant since just installing zsh package gives
working completions with correct $fpath set.
2017-09-04 11:45:41 +02:00
Robert Helgesson
6611c16099 home-manager: print errors to stderr 2017-09-01 10:24:01 +02:00
Nikita Uvarov
2c5151726c vim: add module 2017-08-28 21:59:38 +02:00
Nikita Uvarov
5eff7f38df home-manager: remove escaping
The Nix code that was extracted to its own file erroneously included
escaping of "${".
2017-08-28 17:56:56 +02:00
Robert Helgesson
e9ca4305a6 home-manager: move Nix code to own file 2017-08-28 11:37:34 +02:00
Robert Helgesson
125deafc84 home-manager: add explanatory comment 2017-08-27 17:44:23 +02:00
Robert Helgesson
1445673e18 home-manager: temporarily re-add attribute 2017-08-27 17:13:06 +02:00
Robert Helgesson
4a17d8ef97 home-manager: remove unused attribute 2017-08-27 13:13:43 +02:00
Robert Helgesson
b4fff6b9b7 home-manager: minor attribute rename
The "activation-script" attribute doesn't actually point directly at
the activation script. Renamed the attribute to be more descriptive.
2017-08-27 13:04:39 +02:00
Robert Helgesson
2245b0ac94 home-manager: simplify use of nix-build output
There is no need to specify an out link when switching to a new
generation since nix-build prints the store path on standard output.
Similarly, when just building a generation we specify no out link
since nix-build will use "result" by default.
2017-08-27 12:55:30 +02:00
Robert Helgesson
e561beab44 home-environment: include home path in generation directory
Technically not necessary but it was a bit silly to leave out this
important directory from the generation directory. This also makes it
more convenient to browse the installed packages after a
`home-manager build`.
2017-08-27 00:18:13 +02:00
Nikita Uvarov
3bcd9d747b owncloud-client: add module 2017-08-26 22:56:26 +02:00
Nikita Uvarov
85a71a3923 oh-my-zsh: set ZSH_CACHE_DIR 2017-08-26 18:24:52 +02:00
Robert Helgesson
a30751464a info: use XDG_CACHE_HOME if defined 2017-08-26 12:10:14 +02:00
Nikita Uvarov
1678548353 zsh: set HELPDIR 2017-08-24 13:13:35 +02:00
Nikita Uvarov
7218c45443 zsh: add completions to fpath 2017-08-24 13:13:34 +02:00
Nikita Uvarov
bd914d49f1 zsh: add history submodule 2017-08-24 13:13:34 +02:00
Robert Helgesson
fed112e497 git: simplify submodule slightly 2017-08-24 01:03:01 +02:00
Nikita Uvarov
c3be4c4629 termite: add module 2017-08-23 23:31:47 +02:00
Cornelius Mika
286d678785 systemd: don't fail on activation when services changed
The diff command exits with status 1 when detecting differences.
Because of 'set -e', this caused the activation to fail.
2017-08-23 20:07:06 +02:00
Nikita Uvarov
42ae135d38 gpg-agent: add zsh support 2017-08-22 10:10:13 +02:00
Richard Yang
42f5d4404d home-environment: use relative latest profile link
Using a relative path prevents the latest version from being garbage
collected.
2017-08-22 09:23:03 +02:00
Cornelius Mika
5c098dc7ad lib-bash: always print message announcing a dry run 2017-08-21 18:50:21 +02:00
Cornelius Mika
3dba6fc95c home-environment: replace superfluous spaces in debug messages 2017-08-21 18:50:21 +02:00
Cornelius Mika
1eee82272a home-environment: only notify about path deletion on verbose output 2017-08-21 18:50:21 +02:00
Cornelius Mika
da5b7bea78 home-environment: fix error when deleting empty directories
With --ignore-fail-on-non-empty, non-emptiness is the only failure
that gets ignored by rmdir. In the case that rmdir reaches $HOME and
considers deleting it, it will detect insufficient permissions and
subsequently exit with an error, even if $HOME is not empty.

Prevent this by calling rmdir with a relative path that excludes
$HOME.
2017-08-21 18:43:17 +02:00
Nikita Uvarov
910cdc0537 zsh: use .zshenv for env vars 2017-08-21 18:00:58 +02:00
Cornelius Mika
02a501705a home-manager: show full script path on activation error
Run the activation script in its original nix-store location so that
Bash error messages show the real script location instead of 'wrkdir',
which gets deleted right after the script exits.
2017-08-20 09:50:42 +02:00
Cornelius Mika
a9d9fb5d75 home-manager: exit with an error on build failure
Because 'set -e' has no effect on commands that run in an if condition,
the script was always exiting with no error when 'doBuild' failed.

As a bonus, $wrkdir is now always removed after building.
2017-08-20 09:50:37 +02:00
Cornelius Mika
ffbc7e723d home-manager: add config file attribute 2017-08-18 21:37:07 +02:00
Nikita Uvarov
3ef56576d3 oh-my-zsh: add module 2017-08-16 15:44:27 +02:00
Nikita Uvarov
cde8e02bf2 zsh: add module 2017-08-16 13:58:30 +02:00
Silvan Mosberger
1d24e96074 htop: use types.coercedTo 2017-08-02 22:22:54 +02:00
Robert Helgesson
be432c8654 ssh: add control persist option 2017-07-29 17:47:18 +02:00
Silvan Mosberger
3778a69fbe htop: add module 2017-07-24 11:42:45 +02:00
Robert Helgesson
d807a5c314 home-environment: fix cleanup of user replaced directories
We must only follow the symbolic link once (i.e., not use the `-e`
option) since otherwise the pattern will not match when
`home.file.xyz.source` is a directory.
2017-07-22 00:38:12 +02:00
Robert Helgesson
5862a05fb1 home-environment: avoid harmless but scary error message 2017-07-22 00:18:31 +02:00
Robert Helgesson
dba14bfe90 manual: fix man pages build on unstable NixOS 2017-07-21 21:10:32 +02:00
Robert Helgesson
2e257f40e6 home-manager: remove manually installed home-manager
If the `home-manager` module is enabled then check if the
`home-manager` package is installed using `nix-env -i` and if so then
it is automatically uninstalled before the new package environment,
which includes home-manager, is installed.
2017-07-19 00:12:34 +02:00
Robert Helgesson
cdb2bec909 syncthing: expand service description
This models the user service on the upstream systemd file.
2017-07-18 13:49:02 +02:00
Utku Demir
dd5061d73b Add syncthing service 2017-07-18 12:50:30 +02:00
Robert Helgesson
7a18a0fb34 home-manager: add module
This module is a module to install and configure the home-manager
tool. By managing the home-manager tool through the Home Manager
module system it will be installed/updated on configuration
activation.
2017-07-11 20:53:42 +02:00
Robert Helgesson
28d3f74614 home-manager: allow a user-defined third module path
The user-defined path will be used if present, otherwise the two
"fallback" defaults (in `.nixpkgs` and `.config/nixpkgs`) will be
used.
2017-07-11 20:53:42 +02:00
Robert Helgesson
98c7b23178 home-manager: remove unnecessary variable setting
The `NIX_PATH` variable is set by the `setHomeManagerModulesPath`
function so it is unnecessary to set it again.
2017-07-11 19:48:49 +02:00
Robin Stumm
89dc8c2004 home-environment: fix home.activation.checkLinkTargets
Problem
-------

We resolve symlinks from inside `/nix/store/HASH-home-manager-files`
into the nix store as `/nix/store/HASH-DRVNAME` which does not match
the pattern.

This happened to me because I pull in some repos in via `home.file`.
The `home-manager-files` derivation links to the repo's derivation in
the nix store. For example:

    let nanorcs = fetchFromGitHub {
      owner = "scopatz";
      repo = "nanorc";
      …
    }; in [
      {
        target = ".nano";
        source = nanorcs;
      }
      {
        target = ".nanorc";
        source = "${nanorcs}/nanorc";
      }
    ]

Solution
--------

Call `readlink` without `-e` to obtain only the first redirection from
`~` to `/nix/store/HASH-home-manager-files`.
2017-07-08 00:38:57 +02:00
Robin Stumm
e274fc732b browserpass: add module (#16)
* browserpass: add module

* apply some review requests

* browserpass: update to 1.0.5

* browserpass: install from Nixpkgs using `home.file`
2017-06-30 22:45:09 +02:00
85 changed files with 7599 additions and 732 deletions

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
language: nix
os:
- linux
- osx
before_script:
- mkdir -m 0755 -p /nix/var/nix/{profiles,gcroots}/per-user/$USER
- mkdir -p ~/.config/nixpkgs
- echo "{}" > ~/.config/nixpkgs/home.nix
script:
nix-shell . -A install

158
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,158 @@
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 should follow the [Nixpkgs syntax
guidelines][]. 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
(without any condition) that has a message along the lines of
> A new service is available: 'services.foo'.
or
> A new program configuration is available: 'program.foo'.
depending on the type of module.
[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
[Nixpkgs syntax guidelines]: https://nixos.org/nixpkgs/manual/#sec-syntax

66
FAQ.md Normal file
View File

@@ -0,0 +1,66 @@
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.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Robert Helgesson
Copyright (c) 2017-2018 Robert Helgesson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

158
README.md
View File

@@ -19,14 +19,14 @@ 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][] version 17.03 (the current stable
version), it may or may not work on other Linux distributions and
NixOS versions.
Home Manager targets [NixOS][] unstable and NixOS version 17.09 (the
current stable version), it may or may not work on other Linux
distributions and NixOS versions.
Also, the `home-manager` tool does not explicitly support rollbacks at
the moment so if your home directory gets messed up you'll have to fix
it yourself (you can attempt to run the activation script for the
desired generation).
it yourself. See the [rollbacks](#rollbacks) section for instructions
on how to manually perform a rollback.
Now when your expectations have been built up and you are eager to try
all this out you can go ahead and read the rest of this text.
@@ -39,53 +39,95 @@ 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.
2. Clone the Home Manager repository into the `~/.config/nixpkgs`
directory:
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`. 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.
```
$ git clone https://github.com/rycee/home-manager ~/.config/nixpkgs/home-manager
2. Assign a temporary variable holding the URL to the appropriate
archive. Typically this is
```console
$ HM_PATH=https://github.com/rycee/home-manager/archive/master.tar.gz
```
3. Add Home Manager to your user's Nixpkgs, for example by adding it
to the `packageOverrides` section in your
`~/.config/nixpkgs/config.nix` file:
or
```nix
```console
$ HM_PATH=https://github.com/rycee/home-manager/archive/release-17.09.tar.gz
```
depending on whether you follow Nixpkgs unstable or version 17.09.
3. Create an initial Home Manager configuration file:
```console
$ cat > ~/.config/nixpkgs/home.nix <<EOF
{
packageOverrides = pkgs: rec {
home-manager = import ./home-manager { inherit pkgs; };
};
programs.home-manager.enable = true;
programs.home-manager.path = $HM_PATH;
}
EOF
```
4. Install the `home-manager` package:
4. Install Home Manager and create the first Home Manager generation:
```console
$ nix-shell $HM_PATH -A install
```
Home Manager should now be active and available in your user
environment.
5. If you do not plan on having Home Manager manage your shell
configuration then you must source the
```
$ nix-env -f '<nixpkgs>' -iA home-manager
installing home-manager
"$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
```
file in your shell configuration. Unfortunately, we currently only
support POSIX.2-like shells such as [Bash][] or [Z shell][].
For example, if you use Bash then add
```bash
. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
```
to your `~/.profile` file.
Note, because the `HM_PATH` variable above points to the live Home
Manager repository you will automatically get updates whenever you
build a new generation. If you dislike automatic updates then perform
a Git clone of the desired branch and instead do the above steps with
`HM_PATH` set to the _absolute path_ of your clone.
Usage
-----
The `home-manager` package installs a tool that is conveniently called
`home-manager`. This tool can apply configurations to your home
Home Manager is typically managed through the `home-manager` tool.
This tool can, for example, apply configurations to your home
directory, list user packages installed by the tool, and list the
configuration generations.
As an example, let us set up a very simple configuration that installs
the htop and fortune packages, installs Emacs with a few extra
packages enabled, installs Firefox with Adobe Flash enabled, and
enables the user gpg-agent service.
As an example, let us expand the initial configuration file from the
installation above to install the htop and fortune packages, install
Emacs with a few extra packages enabled, install Firefox with the
IcedTea plugin enabled, and enable the user gpg-agent service.
First create a file `~/.config/nixpkgs/home.nix` containing
To satisfy the above setup we should elaborate the
`~/.config/nixpkgs/home.nix` file as follows:
```nix
{ pkgs, ... }:
@@ -106,7 +148,7 @@ First create a file `~/.config/nixpkgs/home.nix` containing
programs.firefox = {
enable = true;
enableAdobeFlash = true;
enableIcedTea = true;
};
services.gpg-agent = {
@@ -114,28 +156,74 @@ First create a file `~/.config/nixpkgs/home.nix` containing
defaultCacheTtl = 1800;
enableSshSupport = true;
};
programs.home-manager = {
enable = true;
path = "…";
};
}
```
To activate this configuration you can then run
```
```console
$ home-manager switch
```
or if you are not feeling so lucky,
```
```console
$ home-manager build
```
which will create a `result` link to a directory containing an
activation script and the generated home directory files.
To see available configuration options with descriptions and usage
examples run
```console
$ man home-configuration.nix
```
Rollbacks
---------
While the `home-manager` tool does not explicitly support rollbacks at
the moment it is relatively easy to perform one manually. The steps to
do so are
1. Run `home-manager generations` to determine which generation you
wish to rollback to:
```console
$ home-manager generations
2018-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation
2018-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation
2018-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
2017-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
2017-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
```
2. Copy the Nix store path of the generation you chose, e.g.,
/nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
for generation 763.
3. Run the `activate` script inside the copied store path:
```console
$ /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation/activate
Starting home manager activation
```
Keeping your ~ safe from harm
-----------------------------
To configure programs and services the Home Manager must write various
To configure programs and services Home Manager must write various
things to your home directory. To prevent overwriting any existing
files when switching to a new generation, Home Manager will attempt to
detect collisions between existing files and generated files. If any
@@ -162,7 +250,7 @@ For example, suppose you have a wonderful, painstakingly created
to your configuration. Attempting to switch to the generation will
then result in
```
```console
$ home-manager switch
Activating checkLinkTargets
@@ -196,7 +284,7 @@ in your system configuration and
# …
xsession.enable = true;
xsession.windowManager = "…";
xsession.windowManager.command = "…";
# …
}
@@ -204,6 +292,10 @@ in your system configuration and
in your Home Manager configuration.
[Bash]: https://www.gnu.org/software/bash/
[Nix]: https://nixos.org/nix/
[NixOS]: https://nixos.org/
[Nixpkgs]: https://nixos.org/nixpkgs/
[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/

View File

@@ -1,2 +1,14 @@
# Simply defer to the home-manager script derivation.
import ./home-manager
{ pkgs ? import <nixpkgs> {} }:
rec {
home-manager = import ./home-manager {
inherit pkgs;
path = toString ./.;
};
install = import ./home-manager/install.nix {
inherit home-manager pkgs;
};
nixos = import ./nixos;
}

View File

@@ -1,41 +1,34 @@
{ pkgs, modulesPath ? "$HOME/.config/nixpkgs/home-manager/modules" }:
{ pkgs
# Extra path to Home Manager. If set then this path will be tried
# before `$HOME/.config/nixpkgs/home-manager` and
# `$HOME/.nixpkgs/home-manager`.
, path ? null
}:
let
homeManagerExpr = pkgs.writeText "home-manager.nix" ''
{ pkgs ? import <nixpkgs> {}, confPath }:
let
env = import <home-manager> {
configuration = import confPath;
pkgs = pkgs;
};
in
{
inherit (env) activation-script;
}
'';
pathStr = if path == null then "" else path;
in
pkgs.stdenv.mkDerivation {
name = "home-manager";
phases = [ "installPhase" ];
installPhase = ''
buildCommand = ''
install -v -D -m755 ${./home-manager} $out/bin/home-manager
substituteInPlace $out/bin/home-manager \
--subst-var-by bash "${pkgs.bash}" \
--subst-var-by coreutils "${pkgs.coreutils}" \
--subst-var-by MODULES_PATH '${modulesPath}' \
--subst-var-by HOME_MANAGER_EXPR_PATH "${homeManagerExpr}"
--subst-var-by less "${pkgs.less}" \
--subst-var-by HOME_MANAGER_PATH '${pathStr}'
'';
meta = with pkgs.stdenv.lib; {
description = "A user environment configurator";
maintainers = [ maintainers.rycee ];
platforms = platforms.linux;
platforms = platforms.unix;
license = licenses.mit;
};
}

View File

@@ -3,10 +3,15 @@
# This code explicitly requires GNU Core Utilities and we therefore
# need to ensure they are prioritized over any other similarly named
# tools on the system.
PATH=@coreutils@/bin:$PATH
PATH=@coreutils@/bin:@less@/bin${PATH:+:}$PATH
set -euo pipefail
function errorEcho() {
# shellcheck disable=2048,2086
echo $* >&2
}
# Attempts to set the HOME_MANAGER_CONFIG global variable.
#
# If no configuration file can be found then this function will print
@@ -14,7 +19,7 @@ set -euo pipefail
function setConfigFile() {
if [[ -v HOME_MANAGER_CONFIG ]] ; then
if [[ ! -e "$HOME_MANAGER_CONFIG" ]] ; then
echo "No configure file found at $HOME_MANAGER_CONFIG"
errorEcho "No configuration file found at $HOME_MANAGER_CONFIG"
exit 1
fi
@@ -22,8 +27,9 @@ function setConfigFile() {
return
fi
local defaultConfFile="${XDG_CONFIG_HOME:-$HOME/.config}/nixpkgs/home.nix"
local confFile
for confFile in "$HOME/.config/nixpkgs/home.nix" \
for confFile in "$defaultConfFile" \
"$HOME/.nixpkgs/home.nix" ; do
if [[ -e "$confFile" ]] ; then
HOME_MANAGER_CONFIG="$confFile"
@@ -31,46 +37,28 @@ function setConfigFile() {
fi
done
echo "No configuration file found. " \
"Please create one at ~/.config/nixpkgs/home.nix"
errorEcho "No configuration file found." \
"Please create one at $defaultConfFile"
exit 1
}
function setHomeManagerModulesPath() {
local modulesPath
for modulesPath in "@MODULES_PATH@" \
"$HOME/.nixpkgs/home-manager/modules" ; do
if [[ -e "$modulesPath" ]] ; then
export NIX_PATH="$NIX_PATH${NIX_PATH:+:}home-manager=$modulesPath"
function setHomeManagerNixPath() {
local path
for path in "@HOME_MANAGER_PATH@" \
"${XDG_CONFIG_HOME:-$HOME/.config}/nixpkgs/home-manager" \
"$HOME/.nixpkgs/home-manager" ; do
if [[ -e "$path" || "$path" =~ ^https?:// ]] ; then
export NIX_PATH="home-manager=$path${NIX_PATH:+:}$NIX_PATH"
return
fi
done
}
function doBuild() {
if [[ -z "$1" ]]; then
echo "Need to provide generation output path."
exit 1
fi
if [[ -e "$1" ]]; then
echo "The output path $1 already exists."
exit 1
fi
function doBuildAttr() {
setConfigFile
setHomeManagerModulesPath
setHomeManagerNixPath
output="$(realpath "$1")"
if [[ $? -ne 0 ]]; then
exit 1
fi
export NIX_PATH="$NIX_PATH${NIX_PATH:+:}home-manager=@MODULES_PATH@"
local extraArgs
extraArgs=""
local extraArgs="$*"
for p in "${EXTRA_NIX_PATH[@]}"; do
extraArgs="$extraArgs -I $p"
@@ -80,28 +68,141 @@ function doBuild() {
extraArgs="$extraArgs --show-trace"
fi
nix-build $extraArgs \
"@HOME_MANAGER_EXPR_PATH@" \
--argstr confPath "$HOME_MANAGER_CONFIG" \
-A activation-script \
-o "$output"
# shellcheck disable=2086
nix-build \
"<home-manager/home-manager/home-manager.nix>" \
$extraArgs \
--argstr confPath "$HOME_MANAGER_CONFIG" \
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
}
# Presents news to the user. Takes as argument the path to a "news
# info" file as generated by `buildNews`.
function presentNews() {
local infoFile="$1"
# shellcheck source=/dev/null
. "$infoFile"
# shellcheck disable=2154
if [[ $newsNumUnread -eq 0 ]]; then
return
elif [[ "$newsDisplay" == "silent" ]]; then
return
elif [[ "$newsDisplay" == "notify" ]]; then
local msg
if [[ $newsNumUnread -eq 1 ]]; then
msg="There is an unread and relevant news item.\n"
msg+="Read it by running the command '$(basename "$0") news'."
else
msg="There are $newsNumUnread unread and relevant news items.\n"
msg+="Read them by running the command '$(basename "$0") news'."
fi
# Not actually an error but here stdout is reserved for
# nix-build output.
errorEcho
errorEcho -e "$msg"
errorEcho
if [[ -v DISPLAY ]] && type -P notify-send > /dev/null; then
notify-send "Home Manager" "$msg"
fi
elif [[ "$newsDisplay" == "show" ]]; then
doShowNews --unread
else
errorEcho "Unknown 'news.display' setting '$newsDisplay'."
fi
}
function doBuild() {
if [[ ! -w . ]]; then
errorEcho "Cannot run build in read-only directory";
return 1
fi
local newsInfo
newsInfo=$(buildNews)
local exitCode
doBuildAttr -A activationPackage \
&& exitCode=0 || exitCode=1
presentNews "$newsInfo"
return $exitCode
}
function doSwitch() {
local newsInfo
newsInfo=$(buildNews)
local generation
local exitCode=0
local wrkdir
# Build the generation and run the activate script. Note, we
# specify an output link so that it is treated as a GC root. This
# prevents an unfortunately timed GC from removing the generation
# before activation completes.
wrkdir="$(mktemp -d)"
generation=$(doBuildAttr -o "$wrkdir/result" -A activationPackage) \
&& $generation/activate || exitCode=1
if doBuild "$wrkdir/generation" ; then
"$wrkdir/generation/activate"
fi
# Because the previous command never fails, the script keeps
# running and $wrkdir is always removed.
rm -r "$wrkdir"
presentNews "$newsInfo"
return $exitCode
}
function doListGens() {
# Whether to colorize the generations output.
local color="never"
if [[ -t 1 ]]; then
color="always"
fi
pushd "/nix/var/nix/profiles/per-user/$USER" > /dev/null
ls --color=yes -gG --sort time home-manager-*-link \
| cut -d' ' -f 4-
# shellcheck disable=2012
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
| cut -d' ' -f 4- \
| sed -E 's/home-manager-([[:digit:]]*)-link/: id \1/'
popd > /dev/null
}
# Removes linked generations. Takes as arguments identifiers of
# generations to remove.
function doRmGenerations() {
if [[ -v VERBOSE ]]; then
export VERBOSE_ARG="--verbose"
else
export VERBOSE_ARG=""
fi
if [[ -v DRY_RUN ]] ; then
export DRY_RUN_CMD=echo
else
export DRY_RUN_CMD=""
fi
pushd "/nix/var/nix/profiles/per-user/$USER" > /dev/null
for generationId in "$@"; do
local linkName="home-manager-$generationId-link"
if [[ ! -e $linkName ]]; then
errorEcho "No generation with ID $generationId"
elif [[ $linkName == $(readlink home-manager) ]]; then
errorEcho "Cannot remove the current generation $generationId"
else
echo Removing generation $generationId
$DRY_RUN_CMD rm $VERBOSE_ARG $linkName
fi
done
popd > /dev/null
}
@@ -111,7 +212,63 @@ function doListPackages() {
if [[ -n "$outPath" ]] ; then
nix-store -q --references "$outPath" | sed 's/[^-]*-//'
else
echo "No home-manager packages seem to be installed."
errorEcho "No home-manager packages seem to be installed."
fi
}
function newsReadIdsFile() {
local dataDir="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
local path="$dataDir/news-read-ids"
# If the path doesn't exist then we should create it, otherwise
# Nix will error out when we attempt to use builtins.readFile.
if [[ ! -f "$path" ]]; then
mkdir -p "$dataDir"
touch "$path"
fi
echo "$path"
}
# Builds news meta information to be sourced into this script.
#
# Note, we suppress build output to remove unnecessary verbosity. We
# also use "no out link" to avoid the need for a build directory
# (although this exposes the risk of GC removing the result before we
# manage to source it).
function buildNews() {
doBuildAttr --quiet \
--attr newsInfo \
--no-out-link \
--arg check false \
--argstr newsReadIdsFile "$(newsReadIdsFile)"
}
function doShowNews() {
local infoFile
infoFile=$(buildNews) || return 1
# shellcheck source=/dev/null
. "$infoFile"
# shellcheck disable=2154
case $1 in
--all)
${PAGER:-less} "$newsFileAll"
;;
--unread)
${PAGER:-less} "$newsFileUnread"
;;
*)
errorEcho "Unknown argument $1"
return 1
esac
# shellcheck disable=2154
if [[ -s "$newsUnreadIdsFile" ]]; then
local newsReadIdsFile
newsReadIdsFile="$(newsReadIdsFile)"
cat "$newsUnreadIdsFile" >> "$newsReadIdsFile"
fi
}
@@ -122,22 +279,45 @@ function doHelp() {
echo
echo " -f FILE The home configuration file."
echo " Default is '~/.config/nixpkgs/home.nix'."
echo " -A ATTRIBUTE Optional attribute that selects a configuration"
echo " expression in the configuration file."
echo " -I PATH Add a path to the Nix expression search path."
echo " -v Verbose output"
echo " -n Do a dry run, only prints what actions would be taken"
echo " -h Print this help"
echo
echo "Commands"
echo
echo " help Print this help"
echo
echo " build Build configuration into result directory"
echo
echo " switch Build and activate configuration"
echo
echo " generations List all home environment generations"
echo
echo " remove-generations ID..."
echo " Remove indicated generations. Use 'generations' command to"
echo " find suitable generation numbers."
echo
echo " packages List all packages installed in home-manager-path"
echo
echo " news Show news entries in a pager"
}
EXTRA_NIX_PATH=()
HOME_MANAGER_CONFIG_ATTRIBUTE=""
while getopts f:I:vnh opt; do
# As a special case, if the user has given --help anywhere on the
# command line then print help and exit.
for arg in "$@"; do
if [[ $arg == "--help" ]]; then
doHelp
exit 0
fi
done
while getopts f:I:A:vnh opt; do
case $opt in
f)
HOME_MANAGER_CONFIG="$OPTARG"
@@ -145,6 +325,9 @@ while getopts f:I:vnh opt; do
I)
EXTRA_NIX_PATH+=("$OPTARG")
;;
A)
HOME_MANAGER_CONFIG_ATTRIBUTE="$OPTARG"
;;
v)
export VERBOSE=1
;;
@@ -156,7 +339,7 @@ while getopts f:I:vnh opt; do
exit 0
;;
*)
echo "Unknown option -$OPTARG" >&2
errorEcho "Unknown option -$OPTARG"
doHelp >&2
exit 1
;;
@@ -166,11 +349,17 @@ done
# Get rid of the options.
shift "$((OPTIND-1))"
cmd="$*"
if [[ $# -eq 0 ]]; then
doHelp >&2
exit 1
fi
cmd="$1"
shift 1
case "$cmd" in
build)
doBuild "result"
doBuild
;;
switch)
doSwitch
@@ -178,14 +367,20 @@ case "$cmd" in
generations)
doListGens
;;
remove-generations)
doRmGenerations "$@"
;;
packages)
doListPackages
;;
news)
doShowNews --all
;;
help|--help)
doHelp
;;
*)
echo "Unknown command: $cmd"
errorEcho "Unknown command: $cmd"
doHelp >&2
exit 1
;;

View File

@@ -0,0 +1,88 @@
{ pkgs ? import <nixpkgs> {}
, confPath
, confAttr
, check ? true
, newsReadIdsFile ? null
}:
with pkgs.lib;
let
env = import <home-manager/modules> {
configuration =
if confAttr == ""
then confPath
else (import confPath).${confAttr};
pkgs = pkgs;
check = check;
};
newsReadIds =
if newsReadIdsFile == null
then {}
else
let
ids = splitString "\n" (fileContents newsReadIdsFile);
in
builtins.listToAttrs (map (id: { name = id; value = null; }) ids);
newsIsRead = entry: builtins.hasAttr entry.id newsReadIds;
newsFiltered =
let
pred = entry: entry.condition && ! newsIsRead entry;
in
filter pred env.newsEntries;
newsNumUnread = length newsFiltered;
newsFileUnread = pkgs.writeText "news-unread.txt" (
concatMapStringsSep "\n\n" (entry:
let
time = replaceStrings ["T"] [" "] (removeSuffix "+00:00" entry.time);
in
''
* ${time}
${replaceStrings ["\n"] ["\n "] entry.message}
''
) newsFiltered
);
newsFileAll = pkgs.writeText "news-all.txt" (
concatMapStringsSep "\n\n" (entry:
let
flag = if newsIsRead entry then "read" else "unread";
time = replaceStrings ["T"] [" "] (removeSuffix "+00:00" entry.time);
in
''
* ${time} [${flag}]
${replaceStrings ["\n"] ["\n "] entry.message}
''
) env.newsEntries
);
# File where each line corresponds to an unread news entry
# identifier. If non-empty then the file ends in "\n".
newsUnreadIdsFile = pkgs.writeText "news-unread-ids" (
let
text = concatMapStringsSep "\n" (entry: entry.id) newsFiltered;
in
text + optionalString (text != "") "\n"
);
newsInfo = pkgs.writeText "news-info.sh" ''
local newsNumUnread=${toString newsNumUnread}
local newsDisplay="${env.newsDisplay}"
local newsFileAll="${newsFileAll}"
local newsFileUnread="${newsFileUnread}"
local newsUnreadIdsFile="${newsUnreadIdsFile}"
'';
in
{
inherit (env) activationPackage;
inherit newsInfo;
}

37
home-manager/install.nix Normal file
View File

@@ -0,0 +1,37 @@
{ home-manager, pkgs }:
pkgs.runCommand
"home-manager-install"
{
propagatedBuildInputs = [ home-manager ];
shellHook = ''
echo
echo "Creating initial Home Manager generation..."
echo
if home-manager switch; then
cat <<EOF
All done! The home-manager tool should now be installed and you
can edit
''${XDG_CONFIG_HOME:-~/.config}/nixpkgs/home.nix
to configure Home Manager. Run 'man home-configuration.nix' to
see all available options.
EOF
exit 0
else
cat <<EOF
Uh oh, the installation failed! Please create an issue at
https://github.com/rycee/home-manager/issues
if the error seems to be the fault of Home Manager.
EOF
exit 1
fi
'';
}
""

View File

@@ -1,46 +1,15 @@
{ configuration
, pkgs
, lib ? pkgs.stdenv.lib
# Whether to check that each option has a matching declaration.
, check ? true
}:
with lib;
let
modules = [
./home-environment.nix
./manual.nix
./misc/gtk.nix
./misc/pam.nix
./programs/bash.nix
./programs/beets.nix
./programs/eclipse.nix
./programs/emacs.nix
./programs/firefox.nix
./programs/git.nix
./programs/gnome-terminal.nix
./programs/info.nix
./programs/lesspipe.nix
./programs/ssh.nix
./programs/texlive.nix
./services/dunst.nix
./services/gnome-keyring.nix
./services/gpg-agent.nix
./services/keepassx.nix
./services/network-manager-applet.nix
./services/random-background.nix
./services/redshift.nix
./services/taffybar.nix
./services/tahoe-lafs.nix
./services/udiskie.nix
./services/xscreensaver.nix
./systemd.nix
./xresources.nix
./xsession.nix
<nixpkgs/nixos/modules/misc/assertions.nix>
<nixpkgs/nixos/modules/misc/meta.nix>
];
collectFailed = cfg:
map (x: x.message) (filter (x: !x.assertion) cfg.assertions);
@@ -50,23 +19,19 @@ let
in
fold f res res.config.warnings;
pkgsModule = {
config._module.args.pkgs = lib.mkForce pkgs;
config._module.args.baseModules = modules;
rawModule = lib.evalModules {
modules =
[ configuration ]
++ (import ./modules.nix { inherit check lib pkgs; });
};
module = showWarnings (
let
mod = lib.evalModules {
modules = [ configuration ] ++ modules ++ [ pkgsModule ];
};
failed = collectFailed mod.config;
failed = collectFailed rawModule.config;
failedStr = concatStringsSep "\n" (map (x: "- ${x}") failed);
in
if failed == []
then mod
then rawModule
else throw "\nFailed assertions:\n${failedStr}"
);
@@ -75,6 +40,14 @@ in
{
inherit (module) options config;
activationPackage = module.config.home.activationPackage;
# For backwards compatibility. Please use activationPackage instead.
activation-script = module.config.home.activationPackage;
home-path = module.config.home.path;
newsDisplay = rawModule.config.news.display;
newsEntries =
sort (a: b: a.time > b.time) (
filter (a: a.condition) rawModule.config.news.entries
);
}

269
modules/files.nix Normal file
View File

@@ -0,0 +1,269 @@
{ pkgs, config, lib, ... }:
with lib;
let
cfg = config.home.file;
dag = config.lib.dag;
homeDirectory = config.home.homeDirectory;
fileType = (import lib/file-type.nix {
inherit homeDirectory lib pkgs;
}).fileType;
# A symbolic link whose target path matches this pattern will be
# considered part of a Home Manager generation.
homeFilePattern = "${builtins.storeDir}/*-home-manager-files/*";
in
{
options = {
home.file = mkOption {
description = "Attribute set of files to link into the user home.";
default = {};
type = fileType "<envar>HOME</envar>" homeDirectory;
};
home-files = mkOption {
type = types.package;
internal = true;
description = "Package to contain all home files";
};
};
config = {
assertions = [
(let
badFiles =
filter (f: hasPrefix "." (baseNameOf f))
(map (v: toString v.source)
(attrValues cfg));
badFilesStr = toString badFiles;
in
{
assertion = badFiles == [];
message = "Source file names must not start with '.': ${badFilesStr}";
})
];
# This verifies that the links we are about to create will not
# overwrite an existing file.
home.activation.checkLinkTargets = dag.entryBefore ["writeBoundary"] (
let
check = pkgs.writeText "check" ''
. ${./lib-bash/color-echo.sh}
newGenFiles="$1"
shift
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" \
&& ! "$(readlink "$targetPath")" == ${homeFilePattern} ]] ; then
errorEcho "Existing file '$targetPath' is in the way"
collision=1
fi
done
if [[ -v collision ]] ; then
errorEcho "Please move the above files and try again"
exit 1
fi
'';
in
''
function checkNewGenCollision() {
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" -type f -print0 -or -type l -print0 \
| xargs -0 bash ${check} "$newGenFiles"
}
checkNewGenCollision || exit 1
''
);
# This activation script will
#
# 1. Remove files from the old generation that are not in the new
# generation.
#
# 2. Switch over the Home Manager gcroot and current profile
# links.
#
# 3. Symlink files from the new generation into $HOME.
#
# This order is needed to ensure that we always know which links
# belong to which generation. Specifically, if we're moving from
# generation A to generation B having sets of home file links FA
# and FB, respectively then cleaning before linking produces state
# transitions similar to
#
# FA → FA ∩ FB → (FA ∩ FB) FB = FB
#
# and a failure during the intermediate state FA ∩ FB will not
# result in lost links because this set of links are in both the
# source and target generation.
home.activation.linkGeneration = dag.entryAfter ["writeBoundary"] (
let
link = pkgs.writeText "link" ''
newGenFiles="$1"
shift
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
$DRY_RUN_CMD ln -nsf $VERBOSE_ARG "$sourcePath" "$targetPath"
done
'';
cleanup = pkgs.writeText "cleanup" ''
. ${./lib-bash/color-echo.sh}
newGenFiles="$1"
shift 1
for relativePath in "$@" ; do
targetPath="$HOME/$relativePath"
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."
else
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
$DRY_RUN_CMD rm $VERBOSE_ARG "$targetPath"
# Recursively delete empty parent directories.
targetDir="$(dirname "$relativePath")"
if [[ "$targetDir" != "." ]] ; then
pushd "$HOME" > /dev/null
# Call rmdir with a relative path excluding $HOME.
# Otherwise, it might try to delete $HOME and exit
# with a permission error.
$DRY_RUN_CMD rmdir $VERBOSE_ARG \
-p --ignore-fail-on-non-empty \
"$targetDir"
popd > /dev/null
fi
fi
done
'';
in
''
function linkNewGen() {
echo "Creating home file links in $HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" -type f -print0 -or -type l -print0 \
| xargs -0 bash ${link} "$newGenFiles"
}
function cleanOldGen() {
if [[ ! -v oldGenPath ]] ; then
return
fi
echo "Cleaning up orphan links from $HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
# Apply the cleanup script on each leaf in the old
# generation. The find command below will print the
# relative path of the entry.
find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \
| xargs -0 bash ${cleanup} "$newGenFiles"
}
cleanOldGen
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 ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenGcPath"
else
echo "No change so reusing latest profile generation $oldGenNum"
fi
linkNewGen
''
);
home-files = pkgs.stdenv.mkDerivation {
name = "home-manager-files";
nativeBuildInputs = [ pkgs.xlibs.lndir ];
# Symlink directories and files that have the right execute bit.
# Copy files that need their execute bit changed.
buildCommand = ''
mkdir -p $out
function insertFile() {
local source="$1"
local relTarget="$2"
local executable="$3"
local recursive="$4"
# Figure out the real absolute path to the target.
local target
target="$(realpath -m "$out/$relTarget")"
# Target path must be within $HOME.
if [[ ! $target == $out* ]] ; then
echo "Error installing file '$relTarget' outside \$HOME" >&2
exit 1
fi
mkdir -p "$(dirname "$target")"
if [[ -d $source ]]; then
if [[ $recursive ]]; then
mkdir -p "$target"
lndir -silent "$source" "$target"
else
ln -s "$source" "$target"
fi
else
[[ -x $source ]] && isExecutable=1 || isExecutable=""
# Link the file into the home file directory if possible,
# i.e., if the executable bit of the source is the same we
# expect for the target. Otherwise, we copy the file and
# set the executable bit to the expected value.
if [[ $executable == inherit || $isExecutable == $executable ]]; then
ln -s "$source" "$target"
else
cp "$source" "$target"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else
chmod -x "$target"
fi
fi
fi
}
'' + concatStrings (
mapAttrsToList (n: v: ''
insertFile "${v.source}" \
"${v.target}" \
"${if v.executable == null
then "inherit"
else builtins.toString v.executable}" \
"${builtins.toString v.recursive}"
'') cfg
);
};
};
}

View File

@@ -1,12 +1,13 @@
{ config, lib, pkgs, ... }:
with lib;
with import ./lib/dag.nix;
let
cfg = config.home;
dag = config.lib.dag;
languageSubModule = types.submodule {
options = {
base = mkOption {
@@ -93,53 +94,31 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
imports = [
(mkRemovedOptionModule [ "home" "sessionVariableSetter" ] ''
Session variables are now always set through the shell. This is
done automatically if the shell configuration is managed by Home
Manager. If not, then you must source the
~/.nix-profile/etc/profile.d/hm-session-vars.sh
file yourself.
'')
];
options = {
home.file = mkOption {
description = "Attribute set of files to link into the user home.";
default = {};
type = types.loaOf (types.submodule (
{ name, config, ... }: {
options = {
target = mkOption {
type = types.str;
description = ''
Path to target file relative to <envar>HOME</envar>.
'';
};
home.username = mkOption {
type = types.str;
defaultText = "$USER";
description = "The user's username.";
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = "Text of the file.";
};
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.
'';
};
mode = mkOption {
type = types.str;
default = "444";
description = "The permissions to apply to the file.";
};
};
config = {
target = mkDefault name;
source = mkIf (config.text != null) (
let name' = "user-etc-" + baseNameOf name;
in mkDefault (pkgs.writeText name' config.text)
);
};
})
);
home.homeDirectory = mkOption {
type = types.path;
defaultText = "$HOME";
description = "The user's home directory.";
};
home.language = mkOption {
@@ -160,22 +139,34 @@ in
example = { EDITOR = "emacs"; GS_OPTIONS = "-sPAPERSIZE=a4"; };
description = ''
Environment variables to always set at login.
'';
};
home.sessionVariableSetter = mkOption {
default = "bash";
type = types.enum [ "pam" "bash" ];
example = "pam";
description = ''
Identifies the module that should set the session variables.
</para><para>
If "bash" is set then <varname>config.bash.enable</varname>
must also be enabled.
The values may refer to other environment variables using
POSIX.2 style variable references. For example, a variable
<varname>parameter</varname> may be referenced as
<code>$parameter</code> or <code>''${parameter}</code>. A
default value <literal>foo</literal> may be given as per
<code>''${parameter:-foo}</code> and, similarly, an alternate
value <literal>bar</literal> can be given as per
<code>''${parameter:+bar}</code>.
</para><para>
If "pam" is set then PAM must be used to set the system
environment. Also mind that typical environment variables
might not be set by the time PAM starts up.
Note, these variables may be set in any order so no session
variable may have a runtime dependency on another session
variable. In particular code like
<programlisting>
home.sessionVariables = {
FOO = "Hello";
BAR = "$FOO World!";
};
</programlisting>
may not work as expected. If you need to reference another
session variable, then do so inside Nix instead. The above
example then becomes
<programlisting>
home.sessionVariables = {
FOO = "Hello";
BAR = "''${config.home.sessionVariables.FOO} World!";
};
</programlisting>
'';
};
@@ -185,11 +176,33 @@ in
description = "The set of packages to appear in the user environment.";
};
home.extraOutputsToInstall = mkOption {
type = types.listOf types.str;
default = [];
example = [ "doc" "info" "devdoc" ];
description = ''
List of additional package outputs of the packages
<varname>home.packages</varname> that should be installed into
the user environment.
'';
};
home.path = mkOption {
internal = true;
description = "The derivation installing the user packages.";
};
home.emptyActivationPath = mkOption {
internal = true;
default = false;
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.
'';
};
home.activation = mkOption {
internal = true;
default = {};
@@ -210,27 +223,35 @@ in
type = types.package;
description = "The package containing the complete activation script.";
};
home.extraBuilderCommands = mkOption {
type = types.lines;
default = "";
internal = true;
description = ''
Extra commands to run in the Home Manager generation builder.
'';
};
};
config = {
assertions = [
(let
badFiles =
filter (f: hasPrefix "." (baseNameOf f))
(map (v: toString v.source)
(attrValues cfg.file));
badFilesStr = toString badFiles;
in
{
assertion = badFiles == [];
message = "Source file names must not start with '.': ${badFilesStr}";
})
{
assertion = config.home.username != "";
message = "Username could not be determined";
}
{
assertion = config.home.homeDirectory != "";
message = "Home directory could not be determined";
}
];
home.username = mkDefault (builtins.getEnv "USER");
home.homeDirectory = mkDefault (builtins.getEnv "HOME");
home.sessionVariables =
let
maybeSet = name: value:
listToAttrs (optional (value != null) { inherit name value; });
maybeSet = n: v: optionalAttrs (v != null) { ${n} = v; };
in
(maybeSet "LANG" cfg.language.base)
//
@@ -242,122 +263,29 @@ in
//
(maybeSet "LC_TIME" cfg.language.time);
home.packages = [
# Provide a file holding all session variables.
(
pkgs.writeTextFile {
name = "hm-session-vars.sh";
destination = "/etc/profile.d/hm-session-vars.sh";
text = ''
# Only source this once.
if [ -n "$__HM_SESS_VARS_SOURCED" ]; then return; fi
export __HM_SESS_VARS_SOURCED=1
${config.lib.shell.exportAll cfg.sessionVariables}
'';
}
)
];
# A dummy entry acting as a boundary between the activation
# script's "check" and the "write" phases.
home.activation.writeBoundary = dagEntryAnywhere "";
home.activation.writeBoundary = dag.entryAnywhere "";
# This verifies that the links we are about to create will not
# overwrite an existing file.
home.activation.checkLinkTargets = dagEntryBefore ["writeBoundary"] (
let
pattern = "-home-manager-files/";
check = pkgs.writeText "check" ''
. ${./lib-bash/color-echo.sh}
newGenFiles="$1"
shift
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" \
&& ! "$(readlink -e "$targetPath")" =~ "${pattern}" ]] ; then
errorEcho "Existing file '$targetPath' is in the way"
collision=1
fi
done
if [[ -v collision ]] ; then
errorEcho "Please move the above files and try again"
exit 1
fi
'';
in
''
function checkNewGenCollision() {
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" -type f -print0 -or -type l -print0 \
| xargs -0 bash ${check} "$newGenFiles"
}
checkNewGenCollision || exit 1
''
);
home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] (
let
pattern = "-home-manager-files/";
link = pkgs.writeText "link" ''
newGenFiles="$1"
shift
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
$DRY_RUN_CMD ln -nsf $VERBOSE_ARG "$sourcePath" "$targetPath"
done
'';
cleanup = pkgs.writeText "cleanup" ''
. ${./lib-bash/color-echo.sh}
newGenFiles="$1"
oldGenFiles="$2"
shift 2
for sourcePath in "$@" ; do
relativePath="''${sourcePath#$oldGenFiles/}"
targetPath="$HOME/$relativePath"
if [[ -e "$newGenFiles/$relativePath" ]] ; then
$VERBOSE_ECHO "Checking $targetPath exists"
elif [[ ! "$(readlink -e "$targetPath")" =~ "${pattern}" ]] ; then
warnEcho "Path '$targetPath' not link into Home Manager generation. Skipping delete."
else
echo "Checking $targetPath gone (deleting)"
$DRY_RUN_CMD rm $VERBOSE_ARG "$targetPath"
$DRY_RUN_CMD rmdir --ignore-fail-on-non-empty \
$VERBOSE_ARG -p "$(dirname "$targetPath")"
fi
done
'';
in
''
function linkNewGen() {
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" -type f -print0 -or -type l -print0 \
| xargs -0 bash ${link} "$newGenFiles"
}
function cleanOldGen() {
if [[ ! -v oldGenPath ]] ; then
return
fi
echo "Cleaning up orphan links from $HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
find "$oldGenFiles" -type f -print0 -or -type l -print0 \
| xargs -0 bash ${cleanup} "$newGenFiles" "$oldGenFiles"
}
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 "$newGenProfilePath" "$genProfilePath"
$DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenGcPath"
else
echo "No change so reusing latest profile generation $oldGenNum"
fi
linkNewGen
cleanOldGen
''
);
home.activation.installPackages = dagEntryAfter ["writeBoundary"] ''
# Install packages to the user environment.
home.activation.installPackages = dag.entryAfter ["writeBoundary"] ''
$DRY_RUN_CMD nix-env -i ${cfg.path}
'';
@@ -367,7 +295,7 @@ in
noteEcho Activating ${res.name}
${res.data}
'';
sortedCommands = dagTopoSort cfg.activation;
sortedCommands = dag.topoSort cfg.activation;
activationCmds =
if sortedCommands ? result then
concatStringsSep "\n" (map mkCmd sortedCommands.result)
@@ -375,16 +303,28 @@ in
abort ("Dependency cycle in activation script: "
+ builtins.toJSON sortedCommands);
sf = pkgs.writeText "activation-script" ''
# Programs that always should be available on the activation
# script's PATH.
activationBinPaths = lib.makeBinPath [
pkgs.bash
pkgs.coreutils
pkgs.diffutils # For `cmp` and `diff`.
pkgs.findutils
pkgs.gnugrep
pkgs.gnused
pkgs.ncurses # For `tput`.
]
+ optionalString (!cfg.emptyActivationPath) "\${PATH:+:}$PATH";
activationScript = pkgs.writeScript "activation-script" ''
#!${pkgs.stdenv.shell}
set -eu
set -o pipefail
# This code explicitly requires GNU Core Utilities and Bash.
# We therefore need to ensure they are prioritized over any
# other similarly named tools on the system.
export PATH="${pkgs.coreutils}/bin:${pkgs.bash}/bin:$PATH"
cd $HOME
export PATH="${activationBinPaths}"
. ${./lib-bash/color-echo.sh}
@@ -392,40 +332,22 @@ in
${activationCmds}
'';
home-files = pkgs.stdenv.mkDerivation {
name = "home-manager-files";
phases = [ "installPhase" ];
installPhase =
"mkdir -p $out\n" +
concatStringsSep "\n" (
mapAttrsToList (n: v:
''
if [ -d "${v.source}" ]; then
mkdir -pv "$(dirname "$out/${v.target}")"
ln -sv "${v.source}" "$out/${v.target}"
else
install -D -m${v.mode} "${v.source}" "$out/${v.target}"
fi
''
) cfg.file
);
};
in
pkgs.stdenv.mkDerivation {
name = "home-manager-generation";
phases = [ "installPhase" ];
buildCommand = ''
mkdir -p $out
installPhase = ''
install -D -m755 ${sf} $out/activate
cp ${activationScript} $out/activate
substituteInPlace $out/activate \
--subst-var-by GENERATION_DIR $out
ln -s ${home-files} $out/home-files
ln -s ${config.home-files} $out/home-files
ln -s ${cfg.path} $out/home-path
${cfg.extraBuilderCommands}
'';
};
@@ -433,6 +355,7 @@ in
name = "home-manager-path";
paths = cfg.packages;
inherit (cfg) extraOutputsToInstall;
meta = {
description = "Environment of packages installed through home-manager";

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env bash
function setupVars() {
local profilesPath="/nix/var/nix/profiles/per-user/$USER"
local gcPath="/nix/var/nix/gcroots/per-user/$USER"
@@ -9,27 +11,38 @@ function setupVars() {
| sort -rn \
| head -1)
if [[ -n "$greatestGenNum" ]] ; then
if [[ -n $greatestGenNum ]] ; then
oldGenNum=$greatestGenNum
newGenNum=$((oldGenNum + 1))
else
newGenNum=1
fi
if [[ -e "$gcPath/current-home" ]] ; then
if [[ -e $gcPath/current-home ]] ; then
oldGenPath="$(readlink -e "$gcPath/current-home")"
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 " '${oldGenNum:-}' and '${oldGenPath:-}'"
errorEcho "If you don't mind losing previous profile generations then"
errorEcho "the easiest solution is probably to run"
errorEcho " rm $profilesPath/home-manager*"
errorEcho " rm $gcPath/current-home"
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"
}
setupVars
echo "Starting home manager activation"
if [[ -v VERBOSE ]]; then
export VERBOSE_ECHO=echo
export VERBOSE_ARG="--verbose"
@@ -38,14 +51,23 @@ else
export VERBOSE_ARG=""
fi
echo "Starting home manager activation"
setupVars
if [[ -v DRY_RUN ]] ; then
$VERBOSE_ECHO "This is a dry run"
echo "This is a dry run"
export DRY_RUN_CMD=echo
else
$VERBOSE_ECHO "This is a live run"
export DRY_RUN_CMD=""
fi
if [[ -v VERBOSE ]]; then
echo -n "Using Nix version: "
nix-env --version
fi
$VERBOSE_ECHO "Activation variables:"
if [[ -v oldGenNum ]] ; then
$VERBOSE_ECHO " oldGenNum=$oldGenNum"

View File

@@ -7,9 +7,9 @@
# - the addition of the function `dagEntryBefore` indicating a
# "wanted by" relationship.
with import <nixpkgs/lib/strings.nix>;
with import <nixpkgs/lib/attrsets.nix>;
with import <nixpkgs/lib/lists.nix>;
{ lib }:
with lib;
rec {

20
modules/lib/default.nix Normal file
View File

@@ -0,0 +1,20 @@
{ lib }:
{
dag =
let
d = import ./dag.nix { inherit lib; };
in
{
empty = d.emptyDag;
isDag = d.isDag;
topoSort = d.dagTopoSort;
map = d.dagMap;
entryAnywhere = d.dagEntryAnywhere;
entryBetween = d.dagEntryBetween;
entryAfter = d.dagEntryAfter;
entryBefore = d.dagEntryBefore;
};
shell = import ./shell.nix { inherit lib; };
}

111
modules/lib/file-type.nix Normal file
View File

@@ -0,0 +1,111 @@
{ homeDirectory, lib, pkgs }:
with lib;
let
# Figures out a valid Nix store name for the given path.
storeFileName = path:
let
# All characters that are considered safe. Note "-" is not
# included to avoid "-" followed by digit being interpreted as a
# version.
safeChars =
[ "+" "." "_" "?" "=" ]
++ lowerChars
++ upperChars
++ stringToCharacters "0123456789";
empties = l: genList (x: "") (length l);
unsafeInName = stringToCharacters (
replaceStrings safeChars (empties safeChars) path
);
safeName = replaceStrings unsafeInName (empties unsafeInName) path;
in
"home_file_" + safeName;
in
{
# Constructs a type suitable for a `home.file` like option. The
# target path may be either absolute or relative, in which case it
# is relative the `basePath` argument (which itself must be an
# absolute path).
#
# Arguments:
# - basePathDesc docbook compatible description of the base path
# - basePath the file base path
fileType = basePathDesc: basePath: types.loaOf (types.submodule (
{ name, config, ... }: {
options = {
target = mkOption {
type = types.str;
apply = p:
let
absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
in
removePrefix (homeDirectory + "/") absPath;
description = ''
Path to target file relative to ${basePathDesc}.
'';
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = "Text of the file.";
};
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.
'';
};
executable = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Set the execute bit. If <literal>null</literal>, defaults to the mode
of the <varname>source</varname> file or to <literal>false</literal>
for files created through the <varname>text</varname> option.
'';
};
recursive = mkOption {
type = types.bool;
default = false;
description = ''
If the file source is a directory, then this option
determines whether the directory should be recursively
linked to the target location. This option has no effect
if the source is a file.
</para><para>
If <literal>false</literal> (the default) then the target
will be a symbolic link to the source directory. If
<literal>true</literal> then the target will be a
directory structure matching the source's but whose leafs
are symbolic links to the files of the source directory.
'';
};
};
config = {
target = mkDefault name;
source = mkIf (config.text != null) (
mkDefault (pkgs.writeTextFile {
inherit (config) executable text;
name = storeFileName name;
})
);
};
}
));
}

View File

@@ -1,93 +0,0 @@
/* Functions that generate widespread file
* formats from nix data structures.
*
* They all follow a similar interface:
* generator { config-attrs } data
*
* Tests can be found in ./tests.nix
* Documentation in the manual, #sec-generators
*/
with import <nixpkgs/lib/trivial.nix>;
let
libStr = import <nixpkgs/lib/strings.nix>;
libAttr = import <nixpkgs/lib/attrsets.nix>;
flipMapAttrs = flip libAttr.mapAttrs;
in
rec {
/* Generate a line of key k and value v, separated by
* character sep. If sep appears in k, it is escaped.
* Helper for synaxes with different separators.
*
* mkKeyValueDefault ":" "f:oo" "bar"
* > "f\:oo:bar"
*/
mkKeyValueDefault = sep: k: v:
"${libStr.escape [sep] k}${sep}${toString v}";
/* Generate a key-value-style config file from an attrset.
*
* mkKeyValue is the same as in toINI.
*/
toKeyValue = {
mkKeyValue ? mkKeyValueDefault "="
}: attrs:
let mkLine = k: v: mkKeyValue k v + "\n";
in libStr.concatStrings (libAttr.mapAttrsToList mkLine attrs);
/* Generate an INI-style config file from an
* attrset of sections to an attrset of key-value pairs.
*
* generators.toINI {} {
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
* baz = { "also, integers" = 42; };
* }
*
*> [baz]
*> also, integers=42
*>
*> [foo]
*> ciao=bar
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
*
* The mk* configuration attributes can generically change
* the way sections and key-value strings are generated.
*
* For more examples see the test cases in ./tests.nix.
*/
toINI = {
# apply transformations (e.g. escapes) to section names
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
# format a setting line from key and value
mkKeyValue ? mkKeyValueDefault "="
}: attrsOfAttrs:
let
# map function to string for each key val
mapAttrsToStringsSep = sep: mapFn: attrs:
libStr.concatStringsSep sep
(libAttr.mapAttrsToList mapFn attrs);
mkSection = sectName: sectValues: ''
[${mkSectionName sectName}]
'' + toKeyValue { inherit mkKeyValue; } sectValues;
in
# map input to ini sections
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
/* Generates JSON from an arbitrary (non-function) value.
* For more information see the documentation of the builtin.
*/
toJSON = {}: builtins.toJSON;
/* YAML has been a strict superset of JSON since 1.2, so we
* use toJSON. Before it only had a few differences referring
* to implicit typing rules, so it should work with older
* parsers as well.
*/
toYAML = {}@args: toJSON args;
}

11
modules/lib/shell.nix Normal file
View File

@@ -0,0 +1,11 @@
{ lib }:
rec {
# Produces a Bourne shell like variable export statement.
export = n: v: "export ${n}=\"${toString v}\"";
# Given an attribute set containing shell variable names and their
# assignment, this function produces a string containing an export
# statement for each set entry.
exportAll = vars: lib.concatStringsSep "\n" (lib.mapAttrsToList export vars);
}

View File

@@ -60,9 +60,22 @@ in
};
config = mkIf config.manual.manpages.enable {
# To fix error during manpage build.
meta.doc = builtins.toFile "nothingness" "<para></para>";
home.packages = [ homeEnvironmentManPages ];
};
# To fix error during manpage build.
meta = {
maintainers = [ maintainers.rycee ];
doc = builtins.toFile "nothingness" ''
<chapter 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="sec-nothing">
<title>this is just to make the docs compile</title>
<para xml:id="sec-grsecurity"></para>
<para xml:id="sec-emacs-docbook-xml"></para>
</chapter>
'';
};
}

View File

@@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fonts.fontconfig;
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
fonts.fontconfig = {
enableProfileFonts = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Configure fontconfig to discover fonts installed through
<varname>home.packages</varname> and
<command>nix-env</command>.
</para><para>
Note, this is only necessary on non-NixOS systems.
'';
};
};
};
config = mkIf cfg.enableProfileFonts {
xdg.configFile."fontconfig/conf.d/10-nix-profile-fonts.conf".text = ''
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
<dir>~/.nix-profile/lib/X11/fonts</dir>
<dir>~/.nix-profile/share/fonts</dir>
</fontconfig>
'';
};
}

View File

@@ -8,7 +8,7 @@ let
cfg2 = config.gtk.gtk2;
cfg3 = config.gtk.gtk3;
toGtk3Ini = (import ../lib/generators.nix).toINI {
toGtk3Ini = generators.toINI {
mkKeyValue = key: value:
let
value' =
@@ -27,34 +27,77 @@ let
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.
'';
};
};
};
themeType = types.submodule {
options = {
package = mkOption {
type = types.nullOr types.package;
default = null;
example = literalExample "pkgs.gnome3.gnome_themes_standard";
description = ''
Package providing the theme. This package will be installed
to your profile. If <literal>null</literal> then the theme
is assumed to already be available in your profile.
'';
};
name = mkOption {
type = types.str;
example = "Adwaita";
description = "The name of the theme within the package.";
};
};
};
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
gtk = {
enable = mkEnableOption "GTK 2/3 configuration";
fontName = mkOption {
type = types.nullOr types.str;
font = mkOption {
type = types.nullOr fontType;
default = null;
example = "DejaVu Sans 8";
description = ''
The font to use in GTK+ 2/3 applications.
'';
};
themeName = mkOption {
type = types.nullOr types.str;
iconTheme = mkOption {
type = types.nullOr themeType;
default = null;
example = "Vertex-Dark";
description = "The name of the GTK+2/3 theme to use.";
description = "The icon theme to use.";
};
iconThemeName = mkOption {
type = types.nullOr types.str;
theme = mkOption {
type = types.nullOr themeType;
default = null;
example = "Tango";
description = "The name of the icon theme to use.";
description = "The GTK+2/3 theme to use.";
};
gtk2 = mkOption {
@@ -107,25 +150,34 @@ in
config = mkIf cfg.enable (
let
ini =
optionalAttrs (cfg.fontName != null)
{ gtk-font-name = cfg.fontName; }
optionalAttrs (cfg.font != null)
{ gtk-font-name = cfg.font.name; }
//
optionalAttrs (cfg.themeName != null)
{ gtk-theme-name = cfg.themeName; }
optionalAttrs (cfg.theme != null)
{ gtk-theme-name = cfg.theme.name; }
//
optionalAttrs (cfg.iconThemeName != null)
{ gtk-icon-theme-name = cfg.iconThemeName; };
optionalAttrs (cfg.iconTheme != null)
{ gtk-icon-theme-name = 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;
home.file.".config/gtk-3.0/settings.ini".text =
xdg.configFile."gtk-3.0/settings.ini".text =
toGtk3Ini { Settings = ini // cfg3.extraConfig; };
home.file.".config/gtk-3.0/gtk.css".text = cfg3.extraCss;
xdg.configFile."gtk-3.0/gtk.css".text = cfg3.extraCss;
}
);
}

607
modules/misc/news.nix Normal file
View File

@@ -0,0 +1,607 @@
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.news;
entryModule = types.submodule ({ config, ... }: {
options = {
id = mkOption {
internal = true;
type = types.str;
description = ''
A unique entry identifier. By default it is a base16
formatted hash of the entry message.
'';
};
time = mkOption {
internal = true;
type = types.str;
example = "2017-07-10T21:55:04+00:00";
description = ''
News entry time stamp in ISO-8601 format. Must be in UTC
(ending in '+00:00').
'';
};
condition = mkOption {
internal = true;
default = true;
description = "Whether the news entry should be active.";
};
message = mkOption {
internal = true;
type = types.str;
description = "The news entry content.";
};
};
config = {
id = mkDefault (builtins.hashString "sha256" config.message);
};
});
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
news = {
display = mkOption {
type = types.enum [ "silent" "notify" "show" ];
default = "notify";
description = ''
How unread and relevant news should be presented when
running <command>home-manager build</command> and
<command>home-manager switch</command>.
</para><para>
The options are
<variablelist>
<varlistentry>
<term><literal>silent</literal></term>
<listitem>
<para>
Do not print anything during build or switch. The
<command>home-manager news</command> command still
works for viewing the entries.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>notify</literal></term>
<listitem>
<para>
The number of unread and relevant news entries will be
printed to standard output. The <command>home-manager
news</command> command can later be used to view the
entries.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>show</literal></term>
<listitem>
<para>
A pager showing unread news entries is opened.
</para>
</listitem>
</varlistentry>
</variablelist>
'';
};
entries = mkOption {
internal = true;
type = types.listOf entryModule;
default = [];
description = "News entries.";
};
};
};
config = {
# Add news entries in chronological order (i.e., latest time
# should be at the bottom of the list). The time should be
# formatted as given in the output of
#
# date --iso-8601=second --universal
#
news.entries = [
{
time = "2017-09-01T10:56:28+00:00";
message = ''
Hello! This is a news entry and it represents an
experimental new feature of Home Manager. The idea is to
inform you when something of importance happens in Home
Manager or its modules.
We will try to not disturb you about the same news more than
once so the next time you run
home-manager switch
or
home-manager build
it should not notify you about this text again.
News items may be conditional and will then only show if the
condition holds, for example if they are relevant to your
configuration.
If you want to see all relevant news then please use the
home-manager news
command.
Since this is an experimental feature any positive or
negative feedback would be greatly appreciated. For example,
by commenting in https://git.io/v5BJL.
'';
}
{
time = "2017-09-10T22:15:19+00:00";
condition = config.programs.zsh.enable;
message = ''
Home Manager now offers its own minimal zsh plugin manager
under the 'programs.zsh.plugins' option path. By statically
sourcing your plugins it achieves no startup overhead.
'';
}
{
time = "2017-09-12T13:11:48+00:00";
condition = (
config.programs.zsh.enable &&
config.programs.zsh.shellAliases != {}
);
message = ''
Aliases defined in 'programs.zsh.shellAliases'
are now have the highest priority. Such aliases will
not be redefined by the code in 'programs.zsh.initExtra'
or any external plugins.
'';
}
{
time = "2017-09-12T14:22:18+00:00";
message = ''
A new service is available: 'services.blueman-applet'.
'';
}
{
time = "2017-09-13T11:30:22+00:00";
message = ''
A new service is available: 'services.compton'.
'';
}
{
time = "2017-09-20T14:47:14+00:00";
message = ''
A new service is available: 'services.screen-locker'.
'';
}
{
time = "2017-09-22T12:09:01+00:00";
condition = isString config.programs.git.extraConfig;
message = ''
The 'programs.git.extraConfig' parameter now accepts
attributes instead of strings which allows more flexible
configuration.
The string parameter type will be deprecated in the future,
please change your configuration file accordingly.
For example, if your configuration includes
programs.git.extraConfig = '''
[core]
editor = vim
''';
then you can now change it to
programs.git.extraConfig = {
core = {
editor = "vim";
};
};
'';
}
{
time = "2017-09-27T07:28:54+00:00";
message = ''
A new program module is available: 'programs.command-not-found'.
Note, this differs from the NixOS system command-not-found
tool in that NIX_AUTO_INSTALL is not supported.
'';
}
{
time = "2017-09-28T12:39:36+00:00";
message = ''
A new program module is available: 'programs.rofi';
'';
}
{
time = "2017-09-30T09:44:18+00:00";
condition = with config.programs.vim;
enable && (tabSize != null || lineNumbers != null);
message = ''
The options 'programs.vim.tabSize' and 'programs.vim.lineNumbers' have
been deprecated and will be removed in the near future.
The new and preferred way to configure tab size and line numbers is to
use the more general 'programs.vim.settings' option. Specifically,
instead of
- 'programs.vim.lineNumbers' use 'programs.vim.settings.number', and
- 'programs.vim.tabSize' use 'programs.vim.settings.tabstop' and
'programs.vim.settings.shiftwidth'.
'';
}
{
time = "2017-10-02T11:15:03+00:00";
condition = config.services.udiskie.enable;
message = ''
The udiskie service now defaults to automatically mounting
new devices. Previous behavior was to not automatically
mount. To restore this previous behavior add
services.udiskie.automount = false;
to your Home Manager configuration.
'';
}
{
time = "2017-10-04T18:36:07+00:00";
message = ''
A new module is available: 'xsession.windowManager.xmonad'.
'';
}
{
time = "2017-10-06T08:21:43+00:00";
message = ''
A new service is available: 'services.polybar'.
'';
}
{
time = "2017-10-09T16:38:34+00:00";
message = ''
A new module is available: 'fonts.fontconfig'.
In particular, the Boolean option
fonts.fontconfig.enableProfileFonts
was added for those who do not use NixOS and want to install
font packages using 'nix-env' or 'home.packages'. If you are
using NixOS then you do not need to enable this option.
'';
}
{
time = "2017-10-12T11:21:45+00:00";
condition = config.programs.zsh.enable;
message = ''
A new option in zsh module is available: 'programs.zsh.sessionVariables'.
This option can be used to set zsh specific session variables which
will be set only on zsh launch.
'';
}
{
time = "2017-10-15T13:59:47+00:00";
message = ''
A new module is available: 'programs.man'.
This module is enabled by default and makes sure that manual
pages are installed for packages in 'home.packages'.
'';
}
{
time = "2017-10-20T12:15:27+00:00";
condition = with config.systemd.user;
services != {} || sockets != {} || targets != {} || timers != {};
message = ''
Home Manager's interaction with systemd is now done using
'systemctl' from Nixpkgs, not the 'systemctl' in '$PATH'.
If you are using a distribution whose systemd is
incompatible with the version in Nixpkgs then you can
override this behavior by adding
systemd.user.systemctlPath = "/usr/bin/systemctl"
to your configuration. Home Manager will then use your
chosen version.
'';
}
{
time = "2017-10-23T23:10:29+00:00";
condition = !config.programs.home-manager.enable;
message = ''
Unfortunately, due to some internal restructuring it is no
longer possible to install the home-manager command when
having
home-manager = import ./home-manager { inherit pkgs; };
in the '~/.config/nixpkgs/config.nix' package override
section. Attempting to use the above override will now
result in the error "cannot coerce a set to a string".
To resolve this please delete the override from the
'config.nix' file and either link the Home Manager overlay
$ ln -s ~/.config/nixpkgs/home-manager/overlay.nix \
~/.config/nixpkgs/overlays/home-manager.nix
or add
programs.home-manager.enable = true;
to your Home Manager configuration. The latter is
recommended as the home-manager tool then is updated
automatically whenever you do a switch.
'';
}
{
time = "2017-10-23T23:26:17+00:00";
message = ''
A new module is available: 'nixpkgs'.
Like the identically named NixOS module, this allows you to
set Nixpkgs options and define Nixpkgs overlays. Note, the
changes you make here will not automatically apply to Nix
commands run outside Home Manager.
'';
}
{
time = "2017-10-28T23:39:55+00:00";
message = ''
A new module is available: 'xdg'.
If enabled, this module allows configuration of the XDG base
directory paths.
Whether the module is enabled or not, it also offers the
option 'xdg.configFile', which acts much like 'home.file'
except the target path is relative to the XDG configuration
directory. That is, unless `XDG_CONFIG_HOME` is configured
otherwise, the assignment
xdg.configFile.hello.text = "hello world";
will result in a file '$HOME/.config/hello'.
Most modules in Home Manager that previously were hard coded
to write configuration to '$HOME/.config' now use this
option and will therefore honor the XDG configuration
directory.
'';
}
{
time = "2017-10-31T11:46:07+00:00";
message = ''
A new window manager module is available: 'xsession.windowManager.i3'.
'';
}
{
time = "2017-11-12T00:18:59+00:00";
message = ''
A new program module is available: 'programs.neovim'.
'';
}
{
time = "2017-11-14T19:56:49+00:00";
condition = with config.xsession.windowManager; (
i3.enable && i3.config != null && i3.config.startup != []
);
message = ''
A new 'notification' option was added to
xsession.windowManager.i3.startup submodule.
Startup commands are now executed with the startup-notification
support enabled by default. Please, set 'notification' to false
where --no-startup-id option is necessary.
'';
}
{
time = "2017-11-17T10:36:10+00:00";
condition = config.xsession.windowManager.i3.enable;
message = ''
The i3 window manager module has been extended with the following options:
i3.config.keycodebindings
i3.config.window.commands
i3.config.window.hideEdgeBorders
i3.config.focus.mouseWarping
'';
}
{
time = "2017-11-26T21:57:23+00:00";
message = ''
Two new modules are available:
'services.kbfs' and 'services.keybase'
'';
}
{
time = "2017-12-07T22:23:11+00:00";
message = ''
A new module is available: 'services.parcellite'
'';
}
{
time = "2017-12-11T17:23:12+00:00";
condition = config.home.activation ? reloadSystemD;
message = ''
The Boolean option 'systemd.user.startServices' is now
available. When enabled the current naive systemd unit
reload logic is replaced by a more sophisticated one that
attempts to automatically start, stop, and restart units as
necessary.
'';
}
{
time = "2018-02-02T11:15:00+00:00";
message = ''
A new program configuration is available: 'programs.mercurial'
'';
}
{
time = "2018-02-03T10:00:00+00:00";
message = ''
A new module is available: 'services.stalonetray'
'';
}
{
time = "2018-02-04T22:58:49+00:00";
condition = config.xsession.enable;
message = ''
A new option 'xsession.pointerCursor' is now available. It
allows specifying the pointer cursor theme and size. The
settings will be applied in the xsession, Xresources, and
GTK configurations.
'';
}
{
time = "2018-02-06T20:23:34+00:00";
message = ''
It is now possible to use Home Manager as a NixOS module.
This allows you to prepare user environments from the system
configuration file, which often is more convenient than
using the 'home-manager' tool. It also opens up additional
possibilities, for example, to automatically configure user
environments in NixOS declarative containers or on systems
deployed through NixOps.
This feature should be considered experimental for now and
some critial limitations apply. For example, it is currently
not possible to use 'nixos-rebuild build-vm' when using the
Home Manager NixOS module. That said, it should be
reasonably robust and stable for simpler use cases.
To make Home Manager available in your NixOS system
configuration you can add
imports = [
"''${builtins.fetchTarball https://github.com/rycee/home-manager/archive/master.tar.gz}/nixos"
];
to your 'configuration.nix' file. This will introduce a new
NixOS option called 'home-manager.users' whose type is an
attribute set mapping user names to Home Manager
configurations.
For example, a NixOS configuration may include the lines
users.users.eve.isNormalUser = true;
home-manager.users.eve = {
home.packages = [ pkgs.atool pkgs.httpie ];
programs.bash.enable = true;
};
and after a 'nixos-rebuild switch' the user eve's
environment should include a basic Bash configuration and
the packages atool and httpie.
More detailed documentation on the intricacies of this new
feature is slowly forthcoming.
'';
}
{
time = "2018-02-19T21:45:26+00:00";
message = ''
A new module is available: 'programs.pidgin'
'';
}
{
time = "2018-03-04T06:54:26+00:00";
message = ''
A new module is available: 'services.unclutter'
'';
}
{
time = "2018-03-07T21:38:27+00:00";
message = ''
A new module is available: 'programs.fzf'.
'';
}
{
time = "2018-03-25T06:49:57+00:00";
condition = with config.programs.ssh; enable && matchBlocks != {};
message = ''
Options set through the 'programs.ssh' module are now placed
at the end of the SSH configuration file. This was done to
make it possible to override global options such as
'ForwardAgent' or 'Compression' inside a host match block.
If you truly need to override an SSH option across all match
blocks then the new option
programs.ssh.extraOptionOverrides
can be used.
'';
}
{
time = "2018-05-06T20:36:51+00:00";
message = ''
The 17.09 branch of Home Manager is unmaintained and will
not receive any further updates.
The current stable branch of Home Manager is now
release-18.03 and matches (as the name suggest) the NixOS
and Nixpkgs 18.03 versions.
Thanks for using Home Manager!
'';
}
];
};
}

146
modules/misc/nixpkgs.nix Normal file
View File

@@ -0,0 +1,146 @@
# Adapted from Nixpkgs.
{ config, lib, pkgs, ... }:
with lib;
let
isConfig = x:
builtins.isAttrs x || builtins.isFunction x;
optCall = f: x:
if builtins.isFunction f
then f x
else f;
mergeConfig = lhs_: rhs_:
let
lhs = optCall lhs_ { inherit pkgs; };
rhs = optCall rhs_ { inherit pkgs; };
in
lhs // rhs //
optionalAttrs (lhs ? packageOverrides) {
packageOverrides = pkgs:
optCall lhs.packageOverrides pkgs //
optCall (attrByPath ["packageOverrides"] ({}) rhs) pkgs;
} //
optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides = pkgs:
optCall lhs.perlPackageOverrides pkgs //
optCall (attrByPath ["perlPackageOverrides"] ({}) rhs) pkgs;
};
configType = mkOptionType {
name = "nixpkgs-config";
description = "nixpkgs config";
check = traceValIfNot isConfig;
merge = args: fold (def: mergeConfig def.value) {};
};
overlayType = mkOptionType {
name = "nixpkgs-overlay";
description = "nixpkgs overlay";
check = builtins.isFunction;
merge = lib.mergeOneOption;
};
_pkgs = import <nixpkgs> (
filterAttrs (n: v: v != null) config.nixpkgs
);
in
{
options.nixpkgs = {
config = mkOption {
default = null;
example = { allowBroken = true; };
type = types.nullOr configType;
description = ''
The configuration of the Nix Packages collection. (For
details, see the Nixpkgs documentation.) It allows you to set
package configuration options.
</para><para>
If <literal>null</literal>, then configuration is taken from
the fallback location, for example,
<filename>~/.config/nixpkgs/config.nix</filename>.
</para><para>
Note, this option will not apply outside your Home Manager
configuration like when installing manually through
<command>nix-env</command>. If you want to apply it both
inside and outside Home Manager you can put it in a separate
file and include something like
<programlisting>
nixpkgs.config = import ./nixpkgs-config.nix;
xdg.configFile."nixpkgs/config.nix".source =
./nixpkgs-config.nix;
</programlisting>
in your Home Manager configuration.
'';
};
overlays = mkOption {
default = null;
example = literalExample
''
[ (self: super: {
openssh = super.openssh.override {
hpnSupport = true;
withKerberos = true;
kerberos = self.libkrb5;
};
};
) ]
'';
type = types.nullOr (types.listOf overlayType);
description = ''
List of overlays to use with the Nix Packages collection. (For
details, see the Nixpkgs documentation.) It allows you to
override packages globally. This is a function that takes as
an argument the <emphasis>original</emphasis> Nixpkgs. The
first argument should be used for finding dependencies, and
the second should be used for overriding recipes.
</para><para>
If <literal>null</literal>, then the overlays are taken from
the fallback location, for example,
<filename>~/.config/nixpkgs/overlays</filename>.
</para><para>
Like <varname>nixpkgs.config</varname> this option only
applies within the Home Manager configuration. See
<varname>nixpkgs.config</varname> for a suggested setup that
works both internally and externally.
'';
};
system = mkOption {
type = types.str;
example = "i686-linux";
internal = true;
description = ''
Specifies the Nix platform type for which the user environment
should be built. If unset, it defaults to the platform type of
your host system. Specifying this option is useful when doing
distributed multi-platform deployment, or when building
virtual machines.
'';
};
};
config = {
_module.args = {
pkgs = _pkgs;
pkgs_i686 = _pkgs.pkgsi686Linux;
};
};
}

View File

@@ -4,17 +4,33 @@ with lib;
let
homeCfg = config.home;
vars = config.pam.sessionVariables;
in
{
options = {};
meta.maintainers = [ maintainers.rycee ];
config = mkIf (homeCfg.sessionVariableSetter == "pam") {
options = {
pam.sessionVariables = mkOption {
default = {};
type = types.attrs;
example = { EDITOR = "vim"; };
description = ''
Environment variables that will be set for the PAM session.
The variable values must be as described in
<citerefentry>
<refentrytitle>pam_env.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>.
'';
};
};
config = mkIf (vars != {}) {
home.file.".pam_environment".text =
concatStringsSep "\n" (
mapAttrsToList (n: v: "${n} OVERRIDE=${v}") homeCfg.sessionVariables
mapAttrsToList (n: v: "${n} OVERRIDE=${toString v}") vars
) + "\n";
};
}

96
modules/misc/xdg.nix Normal file
View File

@@ -0,0 +1,96 @@
{ options, config, lib, pkgs, ... }:
with lib;
let
cfg = config.xdg;
fileType = (import ../lib/file-type.nix {
inherit (config.home) homeDirectory;
inherit lib pkgs;
}).fileType;
defaultCacheHome = "${config.home.homeDirectory}/.cache";
defaultConfigHome = "${config.home.homeDirectory}/.config";
defaultDataHome = "${config.home.homeDirectory}/.local/share";
getXdgDir = name: fallback:
let
value = builtins.getEnv name;
in
if value != "" then value else fallback;
in
{
options.xdg = {
enable = mkEnableOption "management of XDG base directories";
cacheHome = mkOption {
type = types.path;
defaultText = "~/.cache";
description = ''
Absolute path to directory holding application caches.
'';
};
configFile = mkOption {
type = fileType "<varname>xdg.configHome</varname>" cfg.configHome;
default = {};
description = ''
Attribute set of files to link into the user's XDG
configuration home.
'';
};
configHome = mkOption {
type = types.path;
defaultText = "~/.config";
description = ''
Absolute path to directory holding application configurations.
'';
};
dataFile = mkOption {
type = fileType "<varname>xdg.dataHome</varname>" cfg.dataHome;
default = {};
description = ''
Attribute set of files to link into the user's XDG
data home.
'';
};
dataHome = mkOption {
type = types.path;
defaultText = "~/.local/share";
description = ''
Absolute path to directory holding application data.
'';
};
};
config = mkMerge [
(mkIf cfg.enable {
xdg.cacheHome = mkDefault defaultCacheHome;
xdg.configHome = mkDefault defaultConfigHome;
xdg.dataHome = mkDefault defaultDataHome;
home.sessionVariables = {
XDG_CACHE_HOME = cfg.cacheHome;
XDG_CONFIG_HOME = cfg.configHome;
XDG_DATA_HOME = cfg.dataHome;
};
})
(mkIf (!cfg.enable) {
xdg.cacheHome = getXdgDir "XDG_CACHE_HOME" defaultCacheHome;
xdg.configHome = getXdgDir "XDG_CONFIG_HOME" defaultConfigHome;
xdg.dataHome = getXdgDir "XDG_DATA_HOME" defaultDataHome;
})
{
home.file = mkMerge [ cfg.configFile cfg.dataFile ];
}
];
}

100
modules/modules.nix Normal file
View File

@@ -0,0 +1,100 @@
{ pkgs
, lib
# Whether to enable module type checking.
, check ? true
# Whether these modules are inside a NixOS submodule.
, nixosSubmodule ? false
}:
with lib;
let
modules = [
./files.nix
./home-environment.nix
./manual.nix
./misc/fontconfig.nix
./misc/gtk.nix
./misc/news.nix
./misc/nixpkgs.nix
./misc/pam.nix
./misc/xdg.nix
./programs/bash.nix
./programs/beets.nix
./programs/browserpass.nix
./programs/command-not-found/command-not-found.nix
./programs/eclipse.nix
./programs/emacs.nix
./programs/feh.nix
./programs/firefox.nix
./programs/fzf.nix
./programs/git.nix
./programs/gnome-terminal.nix
./programs/home-manager.nix
./programs/htop.nix
./programs/info.nix
./programs/lesspipe.nix
./programs/man.nix
./programs/mercurial.nix
./programs/neovim.nix
./programs/pidgin.nix
./programs/rofi.nix
./programs/ssh.nix
./programs/termite.nix
./programs/texlive.nix
./programs/vim.nix
./programs/zsh.nix
./services/blueman-applet.nix
./services/compton.nix
./services/dunst.nix
./services/gnome-keyring.nix
./services/gpg-agent.nix
./services/kbfs.nix
./services/keepassx.nix
./services/keybase.nix
./services/network-manager-applet.nix
./services/owncloud-client.nix
./services/parcellite.nix
./services/polybar.nix
./services/random-background.nix
./services/redshift.nix
./services/screen-locker.nix
./services/stalonetray.nix
./services/syncthing.nix
./services/taffybar.nix
./services/tahoe-lafs.nix
./services/udiskie.nix
./services/unclutter.nix
./services/window-managers/i3.nix
./services/window-managers/xmonad.nix
./services/xscreensaver.nix
./systemd.nix
./xcursor.nix
./xresources.nix
./xsession.nix
<nixpkgs/nixos/modules/misc/assertions.nix>
<nixpkgs/nixos/modules/misc/lib.nix>
<nixpkgs/nixos/modules/misc/meta.nix>
];
pkgsModule = {
options.nixosSubmodule = mkOption {
type = types.bool;
internal = true;
readOnly = true;
};
config._module.args.baseModules = modules;
config._module.args.pkgs = lib.mkDefault pkgs;
config._module.check = check;
config.lib = import ./lib { inherit lib; };
config.nixosSubmodule = nixosSubmodule;
config.nixpkgs.system = mkDefault pkgs.system;
};
in
modules ++ [ pkgsModule ]

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.bash = {
enable = mkEnableOption "GNU Bourne-Again SHell";
@@ -19,6 +21,12 @@ in
description = "Number of history lines to keep in memory.";
};
historyFile = mkOption {
type = types.str;
default = "$HOME/.bash_history";
description = "Location of the bash history file.";
};
historyFileSize = mkOption {
type = types.int;
default = 100000;
@@ -62,6 +70,15 @@ in
description = "Shell options to set.";
};
sessionVariables = mkOption {
default = {};
type = types.attrs;
example = { MAILCHECK = 30; };
description = ''
Environment variables that will be set for the Bash session.
'';
};
shellAliases = mkOption {
default = {};
example = { ll = "ls -l"; ".." = "cd .."; };
@@ -82,13 +99,30 @@ in
profileExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to .profile.";
description = ''
Extra commands that should be run when initializing a login
shell.
'';
};
bashrcExtra = mkOption {
# Hide for now, may want to rename in the future.
visible = false;
default = "";
type = types.lines;
description = ''
Extra commands that should be added to
<filename>~/.bashrc</filename>.
'';
};
initExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to .bashrc.";
description = ''
Extra commands that should be run when initializing an
interactive shell.
'';
};
};
};
@@ -103,17 +137,39 @@ in
map (v: "shopt -s ${v}") cfg.shellOptions
);
export = n: v: "export ${n}=\"${toString v}\"";
exportIfNonNull = n: v: optionalString (v != null) (export n v);
exportIfNonEmpty = n: v: optionalString (v != "") (export n v);
sessionVarsStr = config.lib.shell.exportAll cfg.sessionVariables;
histControlStr = concatStringsSep ":" cfg.historyControl;
histIgnoreStr = concatStringsSep ":" cfg.historyIgnore;
envVarsStr = concatStringsSep "\n" (
mapAttrsToList export config.home.sessionVariables
);
historyControlStr =
concatStringsSep "\n" (mapAttrsToList (n: v: "${n}=${v}") (
{
HISTFILE = "\"${cfg.historyFile}\"";
HISTFILESIZE = toString cfg.historyFileSize;
HISTSIZE = toString cfg.historySize;
}
// optionalAttrs (cfg.historyControl != []) {
HISTCONTROL = concatStringsSep ":" cfg.historyControl;
}
// optionalAttrs (cfg.historyIgnore != []) {
HISTIGNORE = concatStringsSep ":" cfg.historyIgnore;
}
));
in mkIf cfg.enable {
programs.bash.bashrcExtra = ''
# Commands that should be applied only for interactive shells.
if [[ -n $PS1 ]]; then
${historyControlStr}
${shoptsStr}
${aliasesStr}
${cfg.initExtra}
${optionalString cfg.enableAutojump
". ${pkgs.autojump}/share/autojump/autojump.bash"}
fi
'';
home.file.".bash_profile".text = ''
# -*- mode: sh -*-
@@ -127,8 +183,9 @@ in
home.file.".profile".text = ''
# -*- mode: sh -*-
${optionalString (config.home.sessionVariableSetter == "bash")
envVarsStr}
. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
${sessionVarsStr}
${cfg.profileExtra}
'';
@@ -136,22 +193,7 @@ in
home.file.".bashrc".text = ''
# -*- mode: sh -*-
# Skip if not running interactively.
[ -z "$PS1" ] && return
${export "HISTSIZE" cfg.historySize}
${export "HISTFILESIZE" cfg.historyFileSize}
${exportIfNonEmpty "HISTCONTROL" histControlStr}
${exportIfNonEmpty "HISTIGNORE" histIgnoreStr}
${shoptsStr}
${aliasesStr}
${cfg.initExtra}
${optionalString cfg.enableAutojump
". ${pkgs.autojump}/share/autojump/autojump.bash"}
${cfg.bashrcExtra}
'';
home.packages =

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.beets = {
settings = mkOption {
@@ -25,7 +27,7 @@ in
config = mkIf (cfg.settings != {}) {
home.packages = [ pkgs.beets ];
home.file.".config/beets/config.yaml".text =
xdg.configFile."beets/config.yaml".text =
builtins.toJSON config.programs.beets.settings;
};
}

View File

@@ -0,0 +1,80 @@
{ config, lib, pkgs, ... }:
with lib;
let
browsers = [
"chrome"
"chromium"
"firefox"
"vivaldi"
];
in {
options = {
programs.browserpass = {
enable = mkEnableOption "the browserpass extension host application";
browsers = mkOption {
type = types.listOf (types.enum browsers);
default = browsers;
example = [ "firefox" ];
description = "Which browsers to install browserpass for";
};
};
};
config = mkIf config.programs.browserpass.enable {
home.file = builtins.concatLists (with pkgs.stdenv; map (x:
if x == "chrome" then
let dir = if isDarwin
then "Library/Application Support/Google/Chrome/NativeMessagingHosts"
else ".config/google-chrome/NativeMessagingHosts";
in [
{
target = "${dir}/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-host.json";
}
{
target = "${dir}/../policies/managed/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-policy.json";
}
]
else if x == "chromium" then
let dir = if isDarwin
then "Library/Application Support/Chromium/NativeMessagingHosts"
else ".config/chromium/NativeMessagingHosts";
in [
{
target = "${dir}/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-host.json";
}
{
target = "${dir}/../policies/managed/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-policy.json";
}
]
else if x == "firefox" then
[ {
target = (if isDarwin
then "Library/Application Support/Mozilla/NativeMessagingHosts"
else ".mozilla/native-messaging-hosts")
+ "/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/lib/mozilla/native-messaging-hosts/com.dannyvankooten.browserpass.json";
} ]
else if x == "vivaldi" then
let dir = if isDarwin
then "Library/Application Support/Vivaldi/NativeMessagingHosts"
else ".config/vivaldi/NativeMessagingHosts";
in [
{
target = "${dir}/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-host.json";
}
{
target = "${dir}/../policies/managed/com.dannyvankooten.browserpass.json";
source = "${pkgs.browserpass}/etc/chrome-policy.json";
}
]
else throw "unknown browser ${x}") config.programs.browserpass.browsers);
};
}

View File

@@ -0,0 +1,57 @@
# Adapted from Nixpkgs.
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.command-not-found;
commandNotFound = pkgs.substituteAll {
name = "command-not-found";
dir = "bin";
src = ./command-not-found.pl;
isExecutable = true;
inherit (pkgs) perl;
inherit (cfg) dbPath;
perlFlags = concatStrings (map (path: "-I ${path}/lib/perl5/site_perl ")
[ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite pkgs.perlPackages.StringShellQuote ]);
};
shInit = commandNotFoundHandlerName: ''
# This function is called whenever a command is not found.
${commandNotFoundHandlerName}() {
local p=${commandNotFound}/bin/command-not-found
if [ -x $p -a -f ${cfg.dbPath} ]; then
# Run the helper program.
$p "$@"
else
echo "$1: command not found" >&2
return 127
fi
}
'';
in
{
options.programs.command-not-found = {
enable = mkEnableOption "command-not-found hook for interactive shell";
dbPath = mkOption {
default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ;
description = ''
Absolute path to <filename>programs.sqlite</filename>. By
default this file will be provided by your channel
(nixexprs.tar.xz).
'';
type = types.path;
};
};
config = mkIf cfg.enable {
programs.bash.initExtra = shInit "command_not_found_handle";
programs.zsh.initExtra = shInit "command_not_found_handler";
home.packages = [ commandNotFound ];
};
}

View File

@@ -0,0 +1,44 @@
#! @perl@/bin/perl -w @perlFlags@
use strict;
use DBI;
use DBD::SQLite;
use String::ShellQuote;
use Config;
my $program = $ARGV[0];
my $dbPath = "@dbPath@";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
or die "cannot open database `$dbPath'";
$dbh->{RaiseError} = 0;
$dbh->{PrintError} = 0;
my $system = $ENV{"NIX_SYSTEM"} // $Config{myarchname};
my $res = $dbh->selectall_arrayref(
"select package from Programs where system = ? and name = ?",
{ Slice => {} }, $system, $program);
if (!defined $res || scalar @$res == 0) {
print STDERR "$program: command not found\n";
} elsif (scalar @$res == 1) {
my $package = @$res[0]->{package};
if ($ENV{"NIX_AUTO_RUN"} // "") {
exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV));
} else {
print STDERR <<EOF;
The program $program is currently not installed. You can install it by typing:
nix-env -iA nixos.$package
EOF
}
} else {
print STDERR <<EOF;
The program $program is currently not installed. It is provided by
several packages. You can install it by typing one of the following:
EOF
print STDERR " nix-env -iA nixos.$_->{package}\n" foreach @$res;
}
exit 127;

View File

@@ -9,10 +9,22 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.eclipse = {
enable = mkEnableOption "Eclipse";
enableLombok = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to enable the Lombok Java Agent in Eclipse. This is
necessary to use the Lombok class annotations.
'';
};
jvmArgs = mkOption {
type = types.listOf types.str;
default = [];
@@ -31,7 +43,10 @@ in
home.packages = [
(pkgs.eclipses.eclipseWithPlugins {
eclipse = pkgs.eclipses.eclipse-platform;
jvmArgs = cfg.jvmArgs;
jvmArgs =
cfg.jvmArgs
++ optional cfg.enableLombok
"-javaagent:${pkgs.lombok}/share/java/lombok.jar";
plugins = cfg.plugins;
})
];

View File

@@ -6,24 +6,37 @@ let
cfg = config.programs.emacs;
# Copied from all-packages.nix.
emacsPackages = pkgs.emacsPackagesNgGen cfg.package;
emacsWithPackages = emacsPackages.emacsWithPackages;
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.emacs = {
enable = mkEnableOption "Emacs";
package = mkOption {
type = types.package;
default = pkgs.emacs;
defaultText = "pkgs.emacs";
example = literalExample "pkgs.emacs25-nox";
description = "The Emacs package to use.";
};
extraPackages = mkOption {
default = self: [];
example = literalExample ''
epkgs: [ epkgs.emms epkgs.magit ]
'';
defaultText = "epkgs: []";
example = literalExample "epkgs: [ epkgs.emms epkgs.magit ]";
description = "Extra packages available to Emacs.";
};
};
};
config = mkIf cfg.enable {
home.packages = [ (pkgs.emacsWithPackages cfg.extraPackages) ];
home.packages = [ (emacsWithPackages cfg.extraPackages) ];
};
}

41
modules/programs/feh.nix Normal file
View File

@@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.feh;
disableBinding = func: key: func;
enableBinding = func: key: "${func} ${key}";
in
{
options.programs.feh = {
enable = mkEnableOption "feh - a fast and light image viewer";
keybindings = mkOption {
default = {};
type = types.attrs;
example = { zoom_in = "plus"; zoom_out = "minus"; };
description = ''
Set keybindings.
See <link xlink:href="https://man.finalrewind.org/1/feh/#x4b455953"/> for
default bindings and available commands.
'';
};
};
config = mkIf cfg.enable {
home.packages = [ pkgs.feh ];
xdg.configFile."feh/keys".text = ''
# Disable default keybindings
${concatStringsSep "\n" (mapAttrsToList disableBinding cfg.keybindings)}
# Enable new keybindings
${concatStringsSep "\n" (mapAttrsToList enableBinding cfg.keybindings)}
'';
};
}

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.firefox = {
enable = mkEnableOption "Firefox";
@@ -31,6 +33,12 @@ in
default = false;
description = "Whether to enable the unfree Google Talk plugin.";
};
enableIcedTea = mkOption {
type = types.bool;
default = false;
description = "Whether to enable the Java applet plugin.";
};
};
};
@@ -44,6 +52,7 @@ in
fcfg = setAttrByPath [browserName] {
enableAdobeFlash = cfg.enableAdobeFlash;
enableGoogleTalkPlugin = cfg.enableGoogleTalk;
icedtea = cfg.enableIcedTea;
};
wrapper = pkgs.wrapFirefox.override {

91
modules/programs/fzf.nix Normal file
View File

@@ -0,0 +1,91 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.fzf;
in
{
options.programs.fzf = {
enable = mkEnableOption "fzf - a command-line fuzzy finder";
defaultOptions = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--height 40%" "--border" ];
description = ''
Extra command line options given to fzf by default.
'';
};
fileWidgetOptions = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--preview 'head {}'" ];
description = ''
Command line options for the CTRL-T keybinding.
'';
};
changeDirWidgetOptions = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--preview 'tree -C {} | head -200'" ];
description = ''
Command line options for the ALT-C keybinding.
'';
};
historyWidgetOptions = mkOption {
type = types.listOf types.str;
default = [];
example = [ "--sort" "--exact" ];
description = ''
Command line options for the CTRL-R keybinding.
'';
};
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.
'';
};
};
config = mkIf cfg.enable {
home.packages = [ pkgs.fzf ];
home.sessionVariables =
mapAttrs (n: v: toString v) (
filterAttrs (n: v: v != []) {
FZF_ALT_C_OPTS = cfg.changeDirWidgetOptions;
FZF_CTRL_R_OPTS = cfg.historyWidgetOptions;
FZF_CTRL_T_OPTS = cfg.fileWidgetOptions;
FZF_DEFAULT_OPTS = cfg.defaultOptions;
}
);
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
. ${pkgs.fzf}/share/fzf/completion.bash
. ${pkgs.fzf}/share/fzf/key-bindings.bash
'';
programs.zsh.initExtra = mkIf cfg.enableZshIntegration ''
. ${pkgs.fzf}/share/fzf/completion.zsh
. ${pkgs.fzf}/share/fzf/key-bindings.zsh
'';
};
}

View File

@@ -6,35 +6,33 @@ let
cfg = config.programs.git;
toINI = (import ../lib/generators.nix).toINI {};
signModule = types.submodule (
{ ... }: {
options = {
key = mkOption {
type = types.str;
description = "The default GPG signing key fingerprint.";
};
signByDefault = mkOption {
type = types.bool;
default = false;
description = "Whether commits should be signed by default.";
};
gpgPath = mkOption {
type = types.str;
default = "${pkgs.gnupg}/bin/gpg2";
defaultText = "\${pkgs.gnupg}/bin/gpg2";
description = "Path to GnuPG binary to use.";
};
signModule = types.submodule {
options = {
key = mkOption {
type = types.str;
description = "The default GPG signing key fingerprint.";
};
}
);
signByDefault = mkOption {
type = types.bool;
default = false;
description = "Whether commits should be signed by default.";
};
gpgPath = mkOption {
type = types.str;
default = "${pkgs.gnupg}/bin/gpg2";
defaultText = "\${pkgs.gnupg}/bin/gpg2";
description = "Path to GnuPG binary to use.";
};
};
};
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.git = {
enable = mkEnableOption "Git";
@@ -69,33 +67,63 @@ in
};
extraConfig = mkOption {
type = types.lines;
default = "";
type = types.either types.attrs types.lines;
default = {};
description = "Additional configuration to add.";
};
iniContent = mkOption {
type = types.attrsOf types.attrs;
internal = true;
};
ignores = mkOption {
type = types.listOf types.str;
default = [];
example = [ "*~" "*.swp" ];
description = "List of paths that should be globally ignored.";
};
};
};
config = mkIf cfg.enable (
let
ini = {
user = {
name = cfg.userName;
email = cfg.userEmail;
} // optionalAttrs (cfg.signing != null) {
signingKey = cfg.signing.key;
};
} // optionalAttrs (cfg.signing != null) {
commit.gpgSign = cfg.signing.signByDefault;
gpg.program = cfg.signing.gpgPath;
} // optionalAttrs (cfg.aliases != {}) {
alias = cfg.aliases;
};
in
mkMerge [
{
home.packages = [ cfg.package ];
home.file.".gitconfig".text = toINI ini + "\n" + cfg.extraConfig;
programs.git.iniContent.user = {
name = cfg.userName;
email = cfg.userEmail;
};
xdg.configFile = {
"git/config".text = generators.toINI {} cfg.iniContent;
"git/ignore" = mkIf (cfg.ignores != []) {
text = concatStringsSep "\n" cfg.ignores + "\n";
};
};
}
(mkIf (cfg.signing != null) {
programs.git.iniContent = {
user.signingKey = cfg.signing.key;
commit.gpgSign = cfg.signing.signByDefault;
gpg.program = cfg.signing.gpgPath;
};
})
(mkIf (cfg.aliases != {}) {
programs.git.iniContent.alias = cfg.aliases;
})
(mkIf (lib.isAttrs cfg.extraConfig) {
programs.git.iniContent = cfg.extraConfig;
})
(mkIf (lib.isString cfg.extraConfig) {
xdg.configFile."git/config".text = cfg.extraConfig;
})
]
);
}

View File

@@ -1,12 +1,13 @@
{ config, lib, pkgs, ... }:
with lib;
with import ../lib/dag.nix;
let
cfg = config.programs.gnome-terminal;
dag = config.lib.dag;
profileColorsSubModule = types.submodule (
{ ... }: {
options = {
@@ -90,7 +91,7 @@ let
}
);
toINI = (import ../lib/generators.nix).toINI { mkKeyValue = mkIniKeyValue; };
toDconfIni = generators.toINI { mkKeyValue = mkIniKeyValue; };
mkIniKeyValue = key: value:
let
@@ -146,7 +147,7 @@ let
{
"profiles:" = {
default = head (attrNames (filterAttrs (n: v: v.default) cfg.profile));
list = attrNames cfg.profile; #mapAttrsToList (n: v: n) cfg.profile;
list = attrNames cfg.profile;
};
}
//
@@ -157,6 +158,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.gnome-terminal = {
enable = mkEnableOption "Gnome Terminal";
@@ -179,16 +182,17 @@ in
home.packages = [ pkgs.gnome3.gnome_terminal ];
# The dconf service needs to be installed and prepared.
home.activation.gnomeTerminal = dagEntryAfter ["installPackages"] (
home.activation.gnomeTerminal = dag.entryAfter ["installPackages"] (
let
sf = pkgs.writeText "gnome-terminal.ini" (toINI (buildIniSet cfg));
iniText = toDconfIni (buildIniSet cfg);
iniFile = pkgs.writeText "gnome-terminal.ini" iniText;
dconfPath = "/org/gnome/terminal/legacy/";
in
''
if [[ -v DRY_RUN ]]; then
echo ${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} "<" ${sf}
echo ${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} "<" ${iniFile}
else
${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} < ${sf}
${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} < ${iniFile}
fi
''
);

View File

@@ -0,0 +1,55 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.home-manager;
dag = config.lib.dag;
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.home-manager = {
enable = mkEnableOption "Home Manager";
path = mkOption {
type = types.nullOr types.str;
default = null;
example = "$HOME/devel/home-manager";
description = ''
The default path to use for Home Manager. If this path does
not exist then
<filename>$HOME/.config/nixpkgs/home-manager</filename> and
<filename>$HOME/.nixpkgs/home-manager</filename> will be
attempted.
'';
};
};
};
config = mkIf (cfg.enable && !config.nixosSubmodule) {
home.packages = [
(import ../../home-manager {
inherit pkgs;
inherit (cfg) path;
})
];
# Uninstall manually installed home-manager, if such exists.
# Without this a file collision error will be printed.
home.activation.uninstallHomeManager =
dag.entryBetween [ "installPackages" ] [ "writeBoundary" ] ''
if nix-env -q | grep -q "^home-manager$" ; then
$DRY_RUN_CMD nix-env -e home-manager
echo "You can now remove the 'home-manager' packageOverride"
echo "or overlay in '~/.config/nixpkgs/', if you want."
fi
'';
};
}

327
modules/programs/htop.nix Normal file
View File

@@ -0,0 +1,327 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.htop;
list = xs: concatMapStrings (x: "${toString x} ") xs;
bool = b: if b then "1" else "0";
fields = {
PID = 0;
COMM = 1;
STATE = 2;
PPID = 3;
PGRP = 4;
SESSION = 5;
TTY_NR = 6;
TPGID = 7;
MINFLT = 9;
MAJFLT = 11;
PRIORITY = 17;
NICE = 18;
STARTTIME = 20;
PROCESSOR = 37;
M_SIZE = 38;
M_RESIDENT = 39;
ST_UID = 45;
PERCENT_CPU = 46;
PERCENT_MEM = 47;
USER = 48;
TIME = 49;
NLWP = 50;
TGID = 51;
CMINFLT = 10;
CMAJFLT = 12;
UTIME = 13;
STIME = 14;
CUTIME = 15;
CSTIME = 16;
M_SHARE = 40;
M_TRS = 41;
M_DRS = 42;
M_LRS = 43;
M_DT = 44;
CTID = 99;
VPID = 100;
VXID = 102;
RCHAR = 102;
WCHAR = 103;
SYSCR = 104;
SYSCW = 105;
RBYTES = 106;
WBYTES = 107;
CNCLWB = 108;
IO_READ_RATE = 109;
IO_WRITE_RATE = 110;
IO_RATE = 111;
CGROUP = 112;
OOM = 113;
IO_PRIORITY = 114;
};
# Mapping from names to defaults
meters = {
Clock = 2;
LoadAverage = 2;
Load = 2;
Memory = 1;
Swap = 1;
Tasks = 2;
Uptime = 2;
Battery = 2;
Hostname = 2;
AllCPUs = 1;
AllCPUs2 = 1;
LeftCPUs = 1;
RightCPUs = 1;
LeftCPUs2 = 1;
RightCPUs2 = 1;
Blank = 2;
CPU = 1;
"CPU(1)"= 1;
"CPU(2)" = 1;
"CPU(3)" = 1;
"CPU(4)" = 1;
};
singleMeterType = types.coercedTo
(types.enum (attrNames meters))
(m: { kind = m; mode = meters.${m}; })
(types.submodule {
options = {
kind = mkOption {
type = types.enum (attrNames meters);
example = "AllCPUs";
description = "What kind of meter.";
};
mode = mkOption {
type = types.enum [ 1 2 3 4 ];
example = 2;
description = "Which mode the meter should use, one of 1(Bar) 2(Text) 3(Graph) 4(LED).";
};
};
});
meterType = types.submodule {
options = {
left = mkOption {
description = "Meters shown in the left header.";
default = [ "AllCPUs" "Memory" "Swap" ];
example = [
"Memory"
"LeftCPUs2"
"RightCPUs2"
{ kind = "CPU"; mode = 3; }
];
type = types.listOf singleMeterType;
};
right = mkOption {
description = "Meters shown in the right header.";
default = [ "Tasks" "LoadAverage" "Uptime" ];
example = [
{ kind = "Clock"; mode = 4; }
"Uptime"
"Tasks"
];
type = types.listOf singleMeterType;
};
};
};
in
{
options.programs.htop = {
enable = mkEnableOption "htop";
fields = mkOption {
type = types.listOf (types.enum (attrNames fields));
default = [ "PID" "USER" "PRIORITY" "NICE" "M_SIZE" "M_RESIDENT" "M_SHARE" "STATE" "PERCENT_CPU" "PERCENT_MEM" "TIME" "COMM" ];
example = [ "PID" "USER" "PRIORITY" "PERCENT_CPU" "M_RESIDENT" "PERCENT_MEM" "TIME" "COMM" ];
description = "Active fields shown in the table.";
};
sortKey = mkOption {
type = types.enum (attrNames fields);
default = "PERCENT_CPU";
example = "TIME";
description = "Which field to use for sorting.";
};
sortDescending = mkOption {
type = types.bool;
default = true;
description = "Whether to sort descending or not.";
};
hideThreads = mkOption {
type = types.bool;
default = false;
description = "Hide threads.";
};
hideKernelThreads = mkOption {
type = types.bool;
default = true;
description = "Hide kernel threads.";
};
hideUserlandThreads = mkOption {
type = types.bool;
default = false;
description = "Hide userland process threads.";
};
shadowOtherUsers = mkOption {
type = types.bool;
default = false;
description = "Shadow other users' processes.";
};
showThreadNames = mkOption {
type = types.bool;
default = false;
description = "Show custom thread names.";
};
showProgramPath = mkOption {
type = types.bool;
default = true;
description = "Show program path.";
};
highlightBaseName = mkOption {
type = types.bool;
default = false;
description = "Highlight program <quote>basename</quote>.";
};
highlightMegabytes = mkOption {
type = types.bool;
default = true;
description = "Highlight large numbers in memory counters.";
};
highlightThreads = mkOption {
type = types.bool;
default = true;
description = "Display threads in a different color.";
};
treeView = mkOption {
type = types.bool;
default = false;
description = "Tree view.";
};
headerMargin = mkOption {
type = types.bool;
default = true;
description = "Leave a margin around header.";
};
detailedCpuTime = mkOption {
type = types.bool;
default = false;
description = "Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest).";
};
cpuCountFromZero = mkOption {
type = types.bool;
default = false;
description = "Count CPUs from 0 instead of 1.";
};
updateProcessNames = mkOption {
type = types.bool;
default = false;
description = "Update process names on every refresh.";
};
accountGuestInCpuMeter = mkOption {
type = types.bool;
default = false;
description = "Add guest time in CPU meter percentage.";
};
colorScheme = mkOption {
type = types.enum [ 0 1 2 3 4 5 6 ];
default = 0;
example = 6;
description = "Which color scheme to use.";
};
delay = mkOption {
type = types.int;
default = 15;
example = 2;
description = "Set the delay between updates, in tenths of seconds.";
};
meters = mkOption {
description = "Meters shown in the header.";
default = {
left = [ "AllCPUs" "Memory" "Swap" ];
right = [ "Tasks" "LoadAverage" "Uptime" ];
};
example = {
left = [
"Memory"
"CPU"
"LeftCPUs2"
"RightCPUs2"
{ kind = "CPU"; mode = 3; }
];
right = [
{ kind = "Clock"; mode = 4; }
"Uptime"
"Tasks"
"LoadAverage"
{ kind = "Battery"; mode = 1; }
];
};
type = meterType;
};
};
config = mkIf cfg.enable {
home.packages = [ pkgs.htop ];
xdg.configFile."htop/htoprc".text = let
leftMeters = map (m: m.kind) cfg.meters.left;
leftModes = map (m: m.mode) cfg.meters.left;
rightMeters = map (m: m.kind) cfg.meters.right;
rightModes = map (m: m.mode) cfg.meters.right;
in ''
# This file is regenerated by home-manager
# when options are changed in the config
fields=${list (map (n: fields.${n}) cfg.fields)}
sort_key=${toString (fields.${cfg.sortKey})}
sort_direction=${bool cfg.sortDescending}
hide_threads=${bool cfg.hideThreads}
hide_kernel_threads=${bool cfg.hideKernelThreads}
hide_userland_threads=${bool cfg.hideUserlandThreads}
shadow_other_users=${bool cfg.shadowOtherUsers}
show_thread_names=${bool cfg.showThreadNames}
show_program_path=${bool cfg.showProgramPath}
highlight_base_name=${bool cfg.highlightBaseName}
highlight_megabytes=${bool cfg.highlightMegabytes}
highlight_threads=${bool cfg.highlightThreads}
tree_view=${bool cfg.treeView}
header_margin=${bool cfg.headerMargin}
detailed_cpu_time=${bool cfg.detailedCpuTime}
cpu_count_from_zero=${bool cfg.cpuCountFromZero}
update_process_names=${bool cfg.updateProcessNames}
account_guest_in_cpu_meter=${bool cfg.accountGuestInCpuMeter}
color_scheme=${toString cfg.colorScheme}
delay=${toString cfg.delay}
left_meters=${list leftMeters}
left_meter_modes=${list leftModes}
right_meters=${list rightMeters}
right_meter_modes=${list rightModes}
'';
};
}

View File

@@ -21,11 +21,13 @@
{ config, lib, pkgs, ... }:
with lib;
with import ../lib/dag.nix;
let
cfg = config.programs.info;
dag = config.lib.dag;
# Indexes info files found in this location
homeInfoPath = "$HOME/.nix-profile/share/info";
@@ -42,7 +44,7 @@ in
enable = mkEnableOption "GNU Info";
homeInfoDirLocation = mkOption {
default = "$HOME/.cache/info";
default = "\${XDG_CACHE_HOME:-$HOME/.cache}/info";
description = ''
Directory in which to store the info <filename>dir</filename>
file within your home.
@@ -52,17 +54,12 @@ in
};
config = mkIf cfg.enable {
assertions = [{
assertion = config.home.sessionVariableSetter != "pam";
message = ''
The info module does not work with PAM as a session variable setter.
'';
}];
home.sessionVariables.INFOPATH =
"${cfg.homeInfoDirLocation}\${INFOPATH:+:}\${INFOPATH}";
home.activation.createHomeInfoDir = dagEntryAfter ["installPackages"] ''
home.activation.createHomeInfoDir = 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
@@ -70,8 +67,12 @@ in
-exec $DRY_RUN_CMD ${infoPkg}/bin/install-info '{}' \
"${cfg.homeInfoDirLocation}/dir" \;
fi
export PATH="$oPATH"
unset oPATH
'';
home.packages = [infoPkg];
home.packages = [ infoPkg ];
home.extraOutputsToInstall = [ "info" ];
};
}

View File

@@ -3,6 +3,8 @@
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.lesspipe = {
enable = mkEnableOption "lesspipe preprocessor for less";

22
modules/programs/man.nix Normal file
View File

@@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
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>.
'';
};
};
config = mkIf config.programs.man.enable {
home.packages = [ pkgs.man ];
home.extraOutputsToInstall = [ "man" ];
};
}

View File

@@ -0,0 +1,102 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.mercurial;
in
{
options = {
programs.mercurial = {
enable = mkEnableOption "Mercurial";
package = mkOption {
type = types.package;
default = pkgs.mercurial;
defaultText = "pkgs.mercurial";
description = "Mercurial package to install.";
};
userName = mkOption {
type = types.str;
description = "Default user name to use.";
};
userEmail = mkOption {
type = types.str;
description = "Default user email to use.";
};
aliases = mkOption {
type = types.attrs;
default = {};
description = "Mercurial aliases to define.";
};
extraConfig = mkOption {
type = types.either types.attrs types.lines;
default = {};
description = "Additional configuration to add.";
};
iniContent = mkOption {
type = types.attrsOf types.attrs;
internal = true;
};
ignores = mkOption {
type = types.listOf types.str;
default = [];
example = [ "*~" "*.swp" ];
description = "List of globs for files to be globally ignored.";
};
ignoresRegexp = mkOption {
type = types.listOf types.str;
default = [];
example = [ "^.*~$" "^.*\\.swp$" ];
description =
"List of regular expressions for files to be globally ignored.";
};
};
};
config = mkIf cfg.enable (
mkMerge [
{
home.packages = [ cfg.package ];
programs.mercurial.iniContent.ui = {
username = cfg.userName + " <" + cfg.userEmail + ">";
};
xdg.configFile."hg/hgrc".text = generators.toINI {} cfg.iniContent;
}
(mkIf (cfg.ignores != [] || cfg.ignoresRegexp != []) {
programs.mercurial.iniContent.ui.ignore =
"${config.xdg.configHome}/hg/hgignore_global";
xdg.configFile."hg/hgignore_global".text =
"syntax: glob\n" + concatStringsSep "\n" cfg.ignores + "\n" +
"syntax: regexp\n" + concatStringsSep "\n" cfg.ignoresRegexp + "\n";
})
(mkIf (cfg.aliases != {}) {
programs.mercurial.iniContent.alias = cfg.aliases;
})
(mkIf (lib.isAttrs cfg.extraConfig) {
programs.mercurial.iniContent = cfg.extraConfig;
})
(mkIf (lib.isString cfg.extraConfig) {
xdg.configFile."hg/hgrc".text = cfg.extraConfig;
})
]
);
}

View File

@@ -0,0 +1,96 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.neovim;
in
{
options = {
programs.neovim = {
enable = mkEnableOption "Neovim";
withPython = mkOption {
type = types.bool;
default = true;
description = ''
Enable Python 2 provider. Set to <literal>true</literal> to
use Python 2 plugins.
'';
};
extraPythonPackages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExample "with pkgs.python2Packages; [ pandas jedi ]";
description = ''
List here Python 2 packages required for your plugins to
work.
'';
};
withRuby = mkOption {
type = types.nullOr types.bool;
default = true;
description = ''
Enable ruby provider.
'';
};
withPython3 = mkOption {
type = types.bool;
default = true;
description = ''
Enable Python 3 provider. Set to <literal>true</literal> to
use Python 3 plugins.
'';
};
extraPython3Packages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExample
"with pkgs.python3Packages; [ python-language-server ]";
description = ''
List here Python 3 packages required for your plugins to work.
'';
};
configure = mkOption {
type = types.nullOr types.attrs;
default = null;
example = literalExample ''
configure = {
customRC = $''''
" here your custom configuration goes!
$'''';
packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
start = [ fugitive ];
# manually loadable by calling `:packadd $plugin-name`
opt = [ ];
};
};
'';
description = ''
Generate your init file from your list of plugins and custom commands,
and loads it from the store via <command>nvim -u /nix/store/hash-vimrc</command>
'';
};
};
};
config = mkIf cfg.enable {
home.packages = [
(pkgs.neovim.override {
inherit (cfg)
extraPython3Packages withPython3
extraPythonPackages withPython
withRuby configure;
})
];
};
}

View File

@@ -0,0 +1,36 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.pidgin;
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.pidgin = {
enable = mkEnableOption "Pidgin messaging client";
package = mkOption {
type = types.package;
default = pkgs.pidgin;
defaultText = "pkgs.pidgin";
description = "The Pidgin package to use.";
};
plugins = mkOption {
default = [];
example = literalExample "[ pkgs.pidgin-otr pkgs.pidgin-osd ]";
description = "Plugins that should be available to Pidgin.";
};
};
};
config = mkIf cfg.enable {
home.packages = [ (cfg.package.override { inherit (cfg) plugins; }) ];
};
}

303
modules/programs/rofi.nix Normal file
View File

@@ -0,0 +1,303 @@
{ config, lib, pkgs, ... }:
with lib;
with builtins;
let
cfg = config.programs.rofi;
colorOption = description: mkOption {
type = types.string;
description = description;
};
rowColorSubmodule = types.submodule {
options = {
background = colorOption "Background color";
foreground = colorOption "Foreground color";
backgroundAlt = colorOption "Alternative background color";
highlight = mkOption {
type = types.submodule {
options = {
background = colorOption "Highlight background color";
foreground = colorOption "Highlight foreground color";
};
};
description = "Color settings for highlighted row.";
};
};
};
windowColorSubmodule = types.submodule {
options = {
background = colorOption "Window background color";
border = colorOption "Window border color";
separator = colorOption "Separator color";
};
};
colorsSubmodule = types.submodule {
options = {
window = mkOption {
default = null;
type = windowColorSubmodule;
description = "Window color settings.";
};
rows = mkOption {
default = null;
type = types.submodule {
options = {
normal = mkOption {
default = null;
type = types.nullOr rowColorSubmodule;
description = "Normal row color settings.";
};
active = mkOption {
default = null;
type = types.nullOr rowColorSubmodule;
description = "Active row color settings.";
};
urgent = mkOption {
default = null;
type = types.nullOr rowColorSubmodule;
description = "Urgent row color settings.";
};
};
};
description = "Rows color settings.";
};
};
};
valueToString = value:
if isBool value
then (if value then "true" else "else")
else toString value;
windowColorsToString = window: concatStringsSep ", " (with window; [
background
border
separator
]);
rowsColorsToString = rows: ''
${optionalString
(rows.normal != null)
(setOption "color-normal" (rowColorsToString rows.normal))}
${optionalString
(rows.active != null)
(setOption "color-active" (rowColorsToString rows.active))}
${optionalString
(rows.urgent != null)
(setOption "color-urgent" (rowColorsToString rows.urgent))}
'';
rowColorsToString = row: concatStringsSep ", " (with row; [
background
foreground
backgroundAlt
highlight.background
highlight.foreground
]);
setOption = name: value:
optionalString (value != null) "rofi.${name}: ${valueToString value}";
setColorScheme = colors: optionalString (colors != null) ''
${optionalString
(colors.window != null)
setOption "color-window" (windowColorsToString colors.window)}
${optionalString
(colors.rows != null)
(rowsColorsToString colors.rows)}
'';
locationsMap = {
center = 0;
top-left = 1;
top = 2;
top-right = 3;
right = 4;
bottom-right = 5;
bottom = 6;
bottom-left = 7;
left = 8;
};
in
{
options.programs.rofi = {
enable = mkEnableOption "Rofi: A window switcher, application launcher and dmenu replacement";
width = mkOption {
default = null;
type = types.nullOr types.int;
description = "Window width";
example = 100;
};
lines = mkOption {
default = null;
type = types.nullOr types.int;
description = "Number of lines";
example = 10;
};
borderWidth = mkOption {
default = null;
type = types.nullOr types.int;
description = "Border width";
example = 1;
};
rowHeight = mkOption {
default = null;
type = types.nullOr types.int;
description = "Row height (in chars)";
example = 1;
};
padding = mkOption {
default = null;
type = types.nullOr types.int;
description = "Padding";
example = 400;
};
font = mkOption {
default = null;
type = types.nullOr types.string;
example = "Droid Sans Mono 14";
description = "Font to use.";
};
scrollbar = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Whether to show a scrollbar.";
};
terminal = mkOption {
default = null;
type = types.nullOr types.string;
description = ''
Path to the terminal which will be used to run console applications
'';
example = "\${pkgs.gnome3.gnome_terminal}/bin/gnome-terminal";
};
separator = mkOption {
default = null;
type = types.nullOr (types.enum [ "none" "dash" "solid" ]);
description = "Separator style";
example = "solid";
};
cycle = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Whether to cycle through the results list.";
};
fullscreen = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Whether to run rofi fullscreen.";
};
location = mkOption {
default = "center";
type = types.enum (builtins.attrNames locationsMap);
description = "The location rofi appears on the screen.";
};
xoffset = mkOption {
default = 0;
type = types.int;
description = ''
Offset in the x-axis in pixels relative to the chosen location.
'';
};
yoffset = mkOption {
default = 0;
type = types.int;
description = ''
Offset in the y-axis in pixels relative to the chosen location.
'';
};
colors = mkOption {
default = null;
type = types.nullOr colorsSubmodule;
description = ''
Color scheme settings.
Colors can be specified in CSS color formats.
'';
example = literalExample ''
colors = {
window = {
background = "argb:583a4c54";
border = "argb:582a373e";
separator = "#c3c6c8";
};
rows = {
normal = {
background = "argb:58455a64";
foreground = "#fafbfc";
backgroundAlt = "argb:58455a64";
highlight = {
background = "#00bcd4";
foreground = "#fafbfc";
};
};
};
};
'';
};
configPath = mkOption {
default = ".config/rofi/config";
type = types.string;
description = "Path where to put generated configuration file.";
};
extraConfig = mkOption {
default = "";
type = types.lines;
description = "Additional configuration to add.";
};
};
config = mkIf cfg.enable {
home.packages = [ pkgs.rofi ];
home.file."${cfg.configPath}".text = ''
${setOption "width" cfg.width}
${setOption "lines" cfg.lines}
${setOption "font" cfg.font}
${setOption "bw" cfg.borderWidth}
${setOption "eh" cfg.rowHeight}
${setOption "padding" cfg.padding}
${setOption "separator-style" cfg.separator}
${setOption "hide-scrollbar" (
if (cfg.scrollbar != null)
then (! cfg.scrollbar)
else cfg.scrollbar
)}
${setOption "terminal" cfg.terminal}
${setOption "cycle" cfg.cycle}
${setOption "fullscreen" cfg.fullscreen}
${setOption "location" (builtins.getAttr cfg.location locationsMap)}
${setOption "xoffset" cfg.xoffset}
${setOption "yoffset" cfg.yoffset}
${setColorScheme cfg.colors}
${cfg.extraConfig}
'';
};
}

View File

@@ -8,7 +8,7 @@ let
yn = flag: if flag then "yes" else "no";
matchBlockModule = types.submodule {
matchBlockModule = types.submodule ({ name, ... }: {
options = {
host = mkOption {
type = types.str;
@@ -81,6 +81,15 @@ let
"Set timeout in seconds after which response will be requested.";
};
compression = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Specifies whether to use compression. Omitted from the host
block when <literal>null</literal>.
'';
};
checkHostIP = mkOption {
type = types.bool;
default = true;
@@ -95,8 +104,16 @@ let
default = null;
description = "The command to use to connect to the server.";
};
extraOptions = mkOption {
type = types.attrsOf types.str;
default = {};
description = "Extra configuration options for the host.";
};
};
};
config.host = mkDefault name;
});
matchBlockStr = cf: concatStringsSep "\n" (
["Host ${cf.host}"]
@@ -109,13 +126,17 @@ let
++ optional (cf.hostname != null) " HostName ${cf.hostname}"
++ optional (cf.serverAliveInterval != 0)
" ServerAliveInterval ${toString cf.serverAliveInterval}"
++ optional (cf.compression != null) " Compression ${yn cf.compression}"
++ optional (!cf.checkHostIP) " CheckHostIP no"
++ optional (cf.proxyCommand != null) " ProxyCommand ${cf.proxyCommand}"
++ mapAttrsToList (n: v: " ${n} ${v}") cf.extraOptions
);
in
{
meta.maintainers = [ maintainers.rycee ];
options.programs.ssh = {
enable = mkEnableOption "SSH client configuration";
@@ -128,6 +149,44 @@ in
'';
};
compression = mkOption {
default = false;
type = types.bool;
description = "Specifies whether to use compression.";
};
serverAliveInterval = mkOption {
type = types.int;
default = 0;
description = ''
Set default timeout in seconds after which response will be requested.
'';
};
hashKnownHosts = mkOption {
default = false;
type = types.bool;
description = ''
Indicates that
<citerefentry>
<refentrytitle>ssh</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry>
should hash host names and addresses when they are added to
the known hosts file.
'';
};
userKnownHostsFile = mkOption {
type = types.str;
default = "~/.ssh/known_hosts";
description = ''
Specifies one or more files to use for the user host key
database, separated by whitespace. The default is
<filename>~/.ssh/known_hosts</filename>.
'';
};
controlMaster = mkOption {
default = "no";
type = types.enum ["yes" "no" "ask" "auto" "autoask"];
@@ -144,22 +203,78 @@ in
'';
};
matchBlocks = mkOption {
type = types.listOf matchBlockModule;
default = [];
controlPersist = mkOption {
type = types.str;
default = "no";
example = "10m";
description = ''
Specify per-host settings.
Whether control socket should remain open in the background.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Extra configuration.
'';
};
extraOptionOverrides = mkOption {
type = types.attrsOf types.str;
default = {};
description = ''
Extra SSH configuration options that take precedence over any
host specific configuration.
'';
};
matchBlocks = mkOption {
type = types.loaOf matchBlockModule;
default = {};
example = literalExample ''
{
"john.example.com" = {
hostname = "example.com";
user = "john";
};
foo = {
hostname = "example.com";
identityFile = "/home/john/.ssh/foo_rsa";
};
};
'';
description = ''
Specify per-host settings. Note, if the order of rules matter
then this must be a list. See
<citerefentry>
<refentrytitle>ssh_config</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>.
'';
};
};
config = mkIf cfg.enable {
home.file.".ssh/config".text = ''
ForwardAgent ${yn cfg.forwardAgent}
ControlMaster ${cfg.controlMaster}
ControlPath ${cfg.controlPath}
${concatStringsSep "\n" (
mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides)}
${concatStringsSep "\n\n" (map matchBlockStr cfg.matchBlocks)}
${concatStringsSep "\n\n" (
map matchBlockStr (
builtins.attrValues cfg.matchBlocks))}
Host *
ForwardAgent ${yn cfg.forwardAgent}
Compression ${yn cfg.compression}
ServerAliveInterval ${toString cfg.serverAliveInterval}
HashKnownHosts ${yn cfg.hashKnownHosts}
UserKnownHostsFile ${cfg.userKnownHostsFile}
ControlMaster ${cfg.controlMaster}
ControlPath ${cfg.controlPath}
ControlPersist ${cfg.controlPersist}
${replaceStrings ["\n"] ["\n "] cfg.extraConfig}
'';
};
}

View File

@@ -0,0 +1,364 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.termite;
in
{
options = {
programs.termite = {
enable = mkEnableOption "Termite VTE-based terminal";
allowBold = mkOption {
default = null;
type = types.nullOr types.bool;
description = ''
Allow the output of bold characters when the bold escape sequence appears.
'';
};
audibleBell = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Have the terminal beep on the terminal bell.";
};
clickableUrl = mkOption {
default = null;
type = types.nullOr types.bool;
description = ''
Auto-detected URLs can be clicked on to open them in your browser.
Only enabled if a browser is configured or detected.
'';
};
dynamicTitle = mkOption {
default = null;
type = types.nullOr types.bool;
description = ''
Settings dynamic title allows the terminal and the shell to
update the terminal's title.
'';
};
fullscreen = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Enables entering fullscreen mode by pressing F11.";
};
mouseAutohide = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Automatically hide the mouse pointer when you start typing.";
};
scrollOnOutput = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Scroll to the bottom when the shell generates output.";
};
scrollOnKeystroke = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Scroll to the bottom automatically when a key is pressed.";
};
searchWrap = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Search from top again when you hit the bottom.";
};
urgentOnBell = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Sets the window as urgent on the terminal bell.";
};
font = mkOption {
default = null;
example = "Monospace 12";
type = types.nullOr types.str;
description = "The font description for the terminal's font.";
};
geometry = mkOption {
default = null;
example = "640x480";
type = types.nullOr types.str;
description = "The default window geometry for new terminal windows.";
};
iconName = mkOption {
default = null;
example = "terminal";
type = types.nullOr types.str;
description = "The name of the icon to be used for the terminal process.";
};
scrollbackLines = mkOption {
default = null;
example = 10000;
type = types.nullOr types.int;
description = "Set the number of lines to limit the terminal's scrollback.";
};
browser = mkOption {
default = null;
type = types.nullOr types.str;
example = "${pkgs.xdg_utils}/xdg-open";
description = ''
Set the default browser for opening links. If its not set, $BROWSER is read.
If that's not set, url hints will be disabled.
'';
};
cursorBlink = mkOption {
default = null;
example = "system";
type = types.nullOr (types.enum [ "system" "on" "off" ]);
description = ''
Specify the how the terminal's cursor should behave.
Accepts system to respect the gtk global configuration,
on and off to explicitly enable or disable them.
'';
};
cursorShape = mkOption {
default = null;
example = "block";
type = types.nullOr (types.enum [ "block" "underline" "ibeam" ]);
description = ''
Specify how the cursor should look. Accepts block, ibeam and underline.
'';
};
filterUnmatchedUrls = mkOption {
default = null;
type = types.nullOr types.bool;
description = "Whether to hide url hints not matching input in url hints mode.";
};
modifyOtherKeys = mkOption {
default = null;
type = types.nullOr types.bool;
description = ''
Emit escape sequences for extra keys,
like the modifyOtherKeys resource for xterm(1).
'';
};
sizeHints = mkOption {
default = null;
type = types.nullOr types.bool;
description = ''
Enable size hints. Locks the terminal resizing
to increments of the terminal's cell size.
Requires a window manager that respects scroll hints.
'';
};
scrollbar = mkOption {
default = null;
type = types.nullOr (types.enum [ "off" "left" "right" ]);
description = "Scroll to the bottom when the shell generates output.";
};
backgroundColor = mkOption {
default = null;
example = "rgba(63, 63, 63, 0.8)";
type = types.nullOr types.str;
description = "Background color value.";
};
cursorColor = mkOption {
default = null;
example = "#dcdccc";
type = types.nullOr types.str;
description = "Cursor color value.";
};
cursorForegroundColor = mkOption {
default = null;
example = "#dcdccc";
type = types.nullOr types.str;
description = "Cursor foreground color value.";
};
foregroundColor = mkOption {
default = null;
example = "#dcdccc";
type = types.nullOr types.str;
description = "Foreground color value.";
};
foregroundBoldColor = mkOption {
default = null;
example = "#ffffff";
type = types.nullOr types.str;
description = "Foreground bold color value.";
};
highlightColor = mkOption {
default = null;
example = "#2f2f2f";
type = types.nullOr types.str;
description = "highlight color value.";
};
hintsActiveBackgroundColor = mkOption {
default = null;
example = "#3f3f3f";
type = types.nullOr types.str;
description = "Hints active background color value.";
};
hintsActiveForegroundColor = mkOption {
default = null;
example = "#e68080";
type = types.nullOr types.str;
description = "Hints active foreground color value.";
};
hintsBackgroundColor = mkOption {
default = null;
example = "#3f3f3f";
type = types.nullOr types.str;
description = "Hints background color value.";
};
hintsForegroundColor = mkOption {
default = null;
example = "#dcdccc";
type = types.nullOr types.str;
description = "Hints foreground color value.";
};
hintsBorderColor = mkOption {
default = null;
example = "#3f3f3f";
type = types.nullOr types.str;
description = "Hints border color value.";
};
hintsBorderWidth = mkOption {
default = null;
example = "0.5";
type = types.nullOr types.str;
description = "Hints border width.";
};
hintsFont = mkOption {
default = null;
example = "Monospace 12";
type = types.nullOr types.str;
description = "The font description for the hints font.";
};
hintsPadding = mkOption {
default = null;
example = 2;
type = types.nullOr types.int;
description = "Hints padding.";
};
hintsRoundness = mkOption {
default = null;
example = "0.2";
type = types.nullOr types.str;
description = "Hints roundness.";
};
optionsExtra = mkOption {
default = "";
example = "fullscreen = true";
type = types.lines;
description = "Extra options that should be added to [options] section.";
};
colorsExtra = mkOption {
default = "";
example = ''
color0 = #3f3f3f
color1 = #705050
color2 = #60b48a
'';
type = types.lines;
description = "Extra colors options that should be added to [colors] section.";
};
hintsExtra = mkOption {
default = "";
example = "border = #3f3f3f";
type = types.lines;
description = "Extra hints options that should be added to [hints] section.";
};
};
};
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.termite ];
xdg.configFile."termite/config".text = ''
[options]
${optionalBoolean "allow_bold" cfg.allowBold}
${optionalBoolean "audible_bell" cfg.audibleBell}
${optionalString "browser" cfg.browser}
${optionalBoolean "clickable_url" cfg.clickableUrl}
${optionalString "cursor_blink" cfg.cursorBlink}
${optionalString "cursor_shape" cfg.cursorShape}
${optionalBoolean "dynamic_title" cfg.dynamicTitle}
${optionalBoolean "filter_unmatched_urls" cfg.filterUnmatchedUrls}
${optionalString "font" cfg.font}
${optionalBoolean "fullscreen" cfg.fullscreen}
${optionalString "geometry" cfg.geometry}
${optionalString "icon_name" cfg.iconName}
${optionalBoolean "modify_other_keys" cfg.modifyOtherKeys}
${optionalBoolean "mouse_autohide" cfg.mouseAutohide}
${optionalBoolean "scroll_on_keystroke" cfg.scrollOnKeystroke}
${optionalBoolean "scroll_on_output" cfg.scrollOnOutput}
${optionalInteger "scrollback_lines" cfg.scrollbackLines}
${optionalString "scrollbar" cfg.scrollbar}
${optionalBoolean "search_wrap" cfg.searchWrap}
${optionalBoolean "size_hints" cfg.sizeHints}
${optionalBoolean "urgent_on_bell" cfg.urgentOnBell}
${cfg.optionsExtra}
[colors]
${optionalString "background" cfg.backgroundColor}
${optionalString "cursor" cfg.cursorColor}
${optionalString "cursor_foreground" cfg.cursorForegroundColor}
${optionalString "foreground" cfg.foregroundColor}
${optionalString "foregroundBold" cfg.foregroundBoldColor}
${optionalString "highlight" cfg.highlightColor}
${cfg.colorsExtra}
[hints]
${optionalString "active_background" cfg.hintsActiveBackgroundColor}
${optionalString "active_foreground" cfg.hintsActiveForegroundColor}
${optionalString "background" cfg.hintsBackgroundColor}
${optionalString "border" cfg.hintsBorderColor}
${optionalInteger "border_width" cfg.hintsBorderWidth}
${optionalString "font" cfg.hintsFont}
${optionalString "foreground" cfg.hintsForegroundColor}
${optionalInteger "padding" cfg.hintsPadding}
${optionalInteger "roundness" cfg.hintsRoundness}
${cfg.hintsExtra}
'';
}
);
}

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
programs.texlive = {
enable = mkEnableOption "Texlive";
@@ -20,13 +22,18 @@ in
'';
description = "Extra packages available to Texlive.";
};
package = mkOption {
type = types.package;
description = "Resulting customized Texlive package.";
readOnly = true;
};
};
};
config = mkIf cfg.enable {
home.packages = [
(pkgs.texlive.combine (cfg.extraPackages pkgs.texlive))
];
home.packages = [ cfg.package ];
programs.texlive.package =
pkgs.texlive.combine (cfg.extraPackages pkgs.texlive);
};
}

180
modules/programs/vim.nix Normal file
View File

@@ -0,0 +1,180 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.vim;
defaultPlugins = [ "sensible" ];
knownSettings = {
background = types.enum [ "dark" "light" ];
copyindent = types.bool;
expandtab = types.bool;
hidden = types.bool;
history = types.int;
ignorecase = types.bool;
modeline = types.bool;
number = types.bool;
relativenumber = types.bool;
shiftwidth = types.int;
smartcase = types.bool;
tabstop = types.int;
};
vimSettingsType = types.submodule {
options =
let
opt = name: type: mkOption {
type = types.nullOr type;
default = null;
visible = false;
};
in
mapAttrs opt knownSettings;
};
setExpr = name: value:
let
v =
if isBool value then (if value then "" else "no") + name
else name + "=" + toString value;
in
optionalString (value != null) ("set " + v);
in
{
options = {
programs.vim = {
enable = mkEnableOption "Vim";
lineNumbers = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show line numbers. DEPRECATED: Use
<varname>programs.vim.settings.number</varname>.
'';
};
tabSize = mkOption {
type = types.nullOr types.int;
default = null;
example = 4;
description = ''
Set tab size and shift width to a specified number of
spaces. DEPRECATED: Use
<varname>programs.vim.settings.tabstop</varname> and
<varname>programs.vim.settings.shiftwidth</varname>.
'';
};
plugins = mkOption {
type = types.listOf types.str;
default = defaultPlugins;
example = [ "YankRing" ];
description = ''
List of vim plugins to install. For supported plugins see:
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vim-plugins/vim-plugin-names"/>.
'';
};
settings = mkOption {
type = vimSettingsType;
default = {};
example = literalExample ''
{
expandtab = true;
history = 1000;
background = "dark";
}
'';
description = ''
At attribute set of Vim 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 Vim documentation for detailed descriptions of these
options. Note, use <varname>extraConfig</varname> to
manually set any options not listed above.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example = ''
set nocompatible
set nobackup
'';
description = "Custom .vimrc lines";
};
package = mkOption {
type = types.package;
description = "Resulting customized vim package";
readOnly = true;
};
};
};
config = (
let
customRC = ''
${concatStringsSep "\n" (
filter (v: v != "") (
mapAttrsToList setExpr (
builtins.intersectAttrs knownSettings cfg.settings)))}
${cfg.extraConfig}
'';
vim = pkgs.vim_configurable.customize {
name = "vim";
vimrcConfig.customRC = customRC;
vimrcConfig.vam.knownPlugins = pkgs.vimPlugins;
vimrcConfig.vam.pluginDictionaries = [
{ names = defaultPlugins ++ cfg.plugins; }
];
};
in mkIf cfg.enable (mkMerge [
{
programs.vim.package = vim;
home.packages = [ cfg.package ];
}
(mkIf (cfg.lineNumbers != null) {
warnings = [
("'programs.vim.lineNumbers' is deprecated, "
+ "use 'programs.vim.settings.number'")
];
programs.vim.settings.number = cfg.lineNumbers;
})
(mkIf (cfg.tabSize != null) {
warnings = [
("'programs.vim.tabSize' is deprecated, use "
+ "'programs.vim.settings.tabstop' and "
+ "'programs.vim.settings.shiftwidth'")
];
programs.vim.settings.tabstop = cfg.tabSize;
programs.vim.settings.shiftwidth = cfg.tabSize;
})
])
);
}

361
modules/programs/zsh.nix Normal file
View File

@@ -0,0 +1,361 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.zsh;
relToDotDir = file: (optionalString (cfg.dotDir != null) (cfg.dotDir + "/")) + file;
pluginsDir = if cfg.dotDir != null then
relToDotDir "plugins" else ".zsh/plugins";
envVarsStr = config.lib.shell.exportAll cfg.sessionVariables;
aliasesStr = concatStringsSep "\n" (
mapAttrsToList (k: v: "alias ${k}='${v}'") cfg.shellAliases
);
zdotdir = "$HOME/" + cfg.dotDir;
historyModule = types.submodule ({ config, ... }: {
options = {
size = mkOption {
type = types.int;
default = 10000;
description = "Number of history lines to keep.";
};
save = mkOption {
type = types.int;
defaultText = 10000;
default = config.size;
description = "Number of history lines to save.";
};
path = mkOption {
type = types.str;
default = relToDotDir ".zsh_history";
defaultText = ".zsh_history";
description = "History file location";
};
ignoreDups = mkOption {
type = types.bool;
default = true;
description = ''
Do not enter command lines into the history list
if they are duplicates of the previous event.
'';
};
share = mkOption {
type = types.bool;
default = true;
description = "Share command history between zsh sessions.";
};
};
});
pluginModule = types.submodule ({ config, ... }: {
options = {
src = mkOption {
type = types.path;
description = ''
Path to the plugin folder.
Will be added to <envar>fpath</envar> and <envar>PATH</envar>.
'';
};
name = mkOption {
type = types.str;
description = ''
The name of the plugin.
Don't forget to add <option>file</option>
if the script name does not follow convention.
'';
};
file = mkOption {
type = types.str;
description = "The plugin script to source.";
};
};
config.file = mkDefault "${config.name}.plugin.zsh";
});
ohMyZshModule = types.submodule {
options = {
enable = mkEnableOption "oh-my-zsh";
plugins = mkOption {
default = [];
example = [ "git" "sudo" ];
type = types.listOf types.str;
description = ''
List of oh-my-zsh plugins
'';
};
custom = mkOption {
default = "";
type = types.str;
example = "$HOME/my_customizations";
description = ''
Path to a custom oh-my-zsh package to override config of
oh-my-zsh. See <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization"/>
for more information.
'';
};
theme = mkOption {
default = "";
example = "robbyrussell";
type = types.str;
description = ''
Name of the theme to be used by oh-my-zsh.
'';
};
};
};
in
{
options = {
programs.zsh = {
enable = mkEnableOption "Z shell (Zsh)";
dotDir = mkOption {
default = null;
example = ".config/zsh";
description = ''
Directory where the zsh configuration and more should be located,
relative to the users home directory. The default is the home
directory.
'';
type = types.nullOr types.str;
};
shellAliases = mkOption {
default = {};
example = { 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.
'';
type = types.attrs;
};
enableCompletion = mkOption {
default = true;
description = ''
Enable zsh completion. Don't forget to add
<programlisting>
environment.pathsToLink = [ "/share/zsh" ];
</programlisting>
to your system configuration to get completion for system packages (e.g. systemd).
'';
type = types.bool;
};
enableAutosuggestions = mkOption {
default = false;
description = "Enable zsh autosuggestions";
};
history = mkOption {
type = historyModule;
default = {};
description = "Options related to commands history configuration.";
};
sessionVariables = mkOption {
default = {};
type = types.attrs;
example = { MAILCHECK = 30; };
description = "Environment variables that will be set for zsh session.";
};
initExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to <filename>.zshrc</filename>.";
};
profileExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to <filename>.zprofile</filename>.";
};
loginExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to <filename>.zlogin</filename>.";
};
logoutExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to <filename>.zlogout</filename>.";
};
plugins = mkOption {
type = types.listOf pluginModule;
default = [];
example = literalExample ''
[
{
# will source zsh-autosuggestions.plugin.zsh
name = "zsh-autosuggestions";
src = pkgs.fetchFromGitHub {
owner = "zsh-users";
repo = "zsh-autosuggestions";
rev = "v0.4.0";
sha256 = "0z6i9wjjklb4lvr7zjhbphibsyx51psv50gm07mbb0kj9058j6kc";
};
}
{
name = "enhancd";
file = "init.sh";
src = pkgs.fetchFromGitHub {
owner = "b4b4r07";
repo = "enhancd";
rev = "v2.2.1";
sha256 = "0iqa9j09fwm6nj5rpip87x3hnvbbz9w9ajgm6wkrd5fls8fn8i5g";
};
}
]
'';
description = "Plugins to source in <filename>.zshrc</filename>.";
};
oh-my-zsh = mkOption {
type = ohMyZshModule;
default = {};
description = "Options to configure oh-my-zsh.";
};
};
};
config = mkIf cfg.enable (mkMerge [
(mkIf (cfg.profileExtra != "") {
home.file."${relToDotDir ".zprofile"}".text = cfg.profileExtra;
})
(mkIf (cfg.loginExtra != "") {
home.file."${relToDotDir ".zlogin"}".text = cfg.loginExtra;
})
(mkIf (cfg.logoutExtra != "") {
home.file."${relToDotDir ".zlogout"}".text = cfg.logoutExtra;
})
(mkIf cfg.oh-my-zsh.enable {
home.file."${relToDotDir ".zshenv"}".text = ''
ZSH="${pkgs.oh-my-zsh}/share/oh-my-zsh";
ZSH_CACHE_DIR="''${XDG_CACHE_HOME:-''$HOME/.cache}/oh-my-zsh";
'';
})
(mkIf (cfg.dotDir != null) {
home.file."${relToDotDir ".zshenv"}".text = ''
ZDOTDIR=${zdotdir}
'';
# When dotDir is set, only use ~/.zshenv to source ZDOTDIR/.zshenv,
# This is so that if ZDOTDIR happens to be
# already set correctly (by e.g. spawning a zsh inside a zsh), all env
# vars still get exported
home.file.".zshenv".text = ''
source ${zdotdir}/.zshenv
'';
})
{
home.packages = with pkgs; [ zsh ]
++ optional cfg.enableCompletion nix-zsh-completions
++ optional cfg.oh-my-zsh.enable oh-my-zsh;
home.file."${relToDotDir ".zshrc"}".text = ''
typeset -U path cdpath fpath manpath
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
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
${concatStrings (map (plugin: ''
path+="$HOME/${pluginsDir}/${plugin.name}"
fpath+="$HOME/${pluginsDir}/${plugin.name}"
'') cfg.plugins)}
${optionalString cfg.enableCompletion "autoload -U compinit && compinit"}
${optionalString cfg.enableAutosuggestions
"source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
}
# Environment variables
. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
${envVarsStr}
${optionalString cfg.oh-my-zsh.enable ''
# oh-my-zsh configuration generated by NixOS
${optionalString (cfg.oh-my-zsh.plugins != [])
"plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})"
}
${optionalString (cfg.oh-my-zsh.custom != "")
"ZSH_CUSTOM=\"${cfg.oh-my-zsh.custom}\""
}
${optionalString (cfg.oh-my-zsh.theme != "")
"ZSH_THEME=\"${cfg.oh-my-zsh.theme}\""
}
source $ZSH/oh-my-zsh.sh
''}
${concatStrings (map (plugin: ''
source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}"
'') cfg.plugins)}
# History options should be set in .zshrc and after oh-my-zsh sourcing.
# See https://github.com/rycee/home-manager/issues/177.
HISTSIZE="${toString cfg.history.size}"
HISTFILE="$HOME/${cfg.history.path}"
SAVEHIST="${toString cfg.history.save}"
setopt HIST_FCNTL_LOCK
${if cfg.history.ignoreDups then "setopt" else "unsetopt"} HIST_IGNORE_DUPS
${if cfg.history.share then "setopt" else "unsetopt"} SHARE_HISTORY
${cfg.initExtra}
# Aliases
${aliasesStr}
'';
}
(mkIf cfg.oh-my-zsh.enable {
# Oh-My-Zsh calls compinit during initialization,
# calling it twice causes sight start up slowdown
# as all $fpath entries will be traversed again.
programs.zsh.enableCompletion = mkForce false;
})
(mkIf (cfg.plugins != []) {
# Many plugins require compinit to be called
# but allow the user to opt out.
programs.zsh.enableCompletion = mkDefault true;
home.file = map (plugin: {
target = "${pluginsDir}/${plugin.name}";
source = plugin.src;
}) cfg.plugins;
})
]);
}

View File

@@ -0,0 +1,37 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.blueman-applet = {
enable = mkEnableOption ''
Blueman applet.
Note, for the applet to work, 'blueman' package should also be installed system-wide
since it requires running 'blueman-mechanism' service activated via dbus.
You can add it to the dbus packages in system configuration:
services.dbus.packages = [ pkgs.blueman ];
'';
};
};
config = mkIf config.services.blueman-applet.enable {
systemd.user.services.blueman-applet = {
Unit = {
Description = "Blueman applet";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.blueman}/bin/blueman-applet";
};
};
};
}

View File

@@ -0,0 +1,223 @@
{ 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};
'' + ''
# opacity
active-opacity = ${cfg.activeOpacity};
inactive-opacity = ${cfg.inactiveOpacity};
menu-opacity = ${cfg.menuOpacity};
# 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";
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 <literal>compton(1)</literal> 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 = ''
Left and right offset 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 <literal>compton(1)</literal> man page for more examples.
'';
};
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.
'';
};
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 <literal>compton(1)</literal> man page 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 = "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.
'';
};
};
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" ];
};
Service = {
ExecStart = "${cfg.package}/bin/compton --config ${configFile}";
Restart = "always";
RestartSec = 3;
};
};
};
}

View File

@@ -2,35 +2,158 @@
with lib;
let
cfg = config.services.dunst;
toDunstIni = generators.toINI {
mkKeyValue = key: value:
let
value' =
if isBool value then (if value then "yes" else "no")
else if isString value then "\"${value}\""
else toString value;
in
"${key}=${value'}";
};
themeType = types.submodule {
options = {
package = mkOption {
type = types.package;
example = literalExample "pkgs.gnome3.adwaita-icon-theme";
description = "Package providing the theme.";
};
name = mkOption {
type = types.str;
example = "Adwaita";
description = "The name of the theme within the package.";
};
size = mkOption {
type = types.str;
default = "32x32";
example = "16x16";
description = "The desired icon size.";
};
};
};
hicolorTheme = {
package = pkgs.hicolor_icon_theme;
name = "hicolor";
size = "32x32";
};
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.dunst = {
enable = mkEnableOption "the dunst notification daemon";
iconTheme = mkOption {
type = themeType;
default = hicolorTheme;
description = "Set the icon theme.";
};
settings = mkOption {
type = types.attrs;
type = types.attrsOf types.attrs;
default = {};
description = "Configuration written to ~/.config/dunstrc";
example = literalExample ''
{
global = {
geometry = "300x5-30+50";
transparency = 10;
frame_color = "#eceff1";
font = "Droid Sans 9";
};
urgency_normal = {
background = "#37474f";
foreground = "#eceff1";
timeout = 10;
};
};
'';
};
};
};
config = mkIf config.services.dunst.enable {
home.file.".local/share/dbus-1/services/org.knopwob.dunst.service".source =
"${pkgs.dunst}/share/dbus-1/services/org.knopwob.dunst.service";
config = mkIf cfg.enable (
mkMerge [
{
xdg.dataFile."dbus-1/services/org.knopwob.dunst.service".source =
"${pkgs.dunst}/share/dbus-1/services/org.knopwob.dunst.service";
systemd.user.services.dunst = {
Unit = {
Description = "Dunst notification daemon";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
services.dunst.settings.global.icon_folders =
let
useCustomTheme =
cfg.iconTheme.package != hicolorTheme.package
|| cfg.iconTheme.name != hicolorTheme.name
|| cfg.iconTheme.size != hicolorTheme.size;
Service = {
Type = "dbus";
BusName = "org.freedesktop.Notifications";
ExecStart = "${pkgs.dunst}/bin/dunst";
};
};
};
basePaths = [
"/run/current-system/sw"
"${config.home.homeDirectory}/.nix-profile"
cfg.iconTheme.package
] ++ optional useCustomTheme hicolorTheme.package;
themes =
[
cfg.iconTheme
] ++ optional useCustomTheme (
hicolorTheme // { size = cfg.iconTheme.size; }
);
categories = [
"actions"
"animations"
"apps"
"categories"
"devices"
"emblems"
"emotes"
"filesystem"
"intl"
"mimetypes"
"places"
"status"
"stock"
];
in
concatStringsSep ":" (
concatMap (theme:
concatMap (basePath:
map (category:
"${basePath}/share/icons/${theme.name}/${theme.size}/${category}"
) categories
) basePaths
) themes
);
systemd.user.services.dunst = {
Unit = {
Description = "Dunst notification daemon";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "dbus";
BusName = "org.freedesktop.Notifications";
ExecStart = "${pkgs.dunst}/bin/dunst";
};
};
}
(mkIf (cfg.settings != {}) {
xdg.configFile."dunst/dunstrc".text = toDunstIni cfg.settings;
})
]
);
}

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.gnome-keyring = {
enable = mkEnableOption "GNOME Keyring";

View File

@@ -6,9 +6,18 @@ let
cfg = config.services.gpg-agent;
gpgInitStr = ''
GPG_TTY="$(tty)"
export GPG_TTY
''
+ optionalString cfg.enableSshSupport
"${pkgs.gnupg}/bin/gpg-connect-agent updatestartuptty /bye > /dev/null";
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.gpg-agent = {
enable = mkEnableOption "GnuPG private key agent";
@@ -17,14 +26,65 @@ in
type = types.nullOr types.int;
default = null;
description = ''
Set the time a cache entry is valid to the given number of seconds.
Set the time a cache entry is valid to the given number of
seconds.
'';
};
defaultCacheTtlSsh = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Set the time a cache entry used for SSH keys is valid to the
given number of seconds.
'';
};
enableSshSupport = mkOption {
type = types.bool;
default = false;
description = "Whether to use the GnuPG key agent for SSH keys.";
description = ''
Whether to use the GnuPG key agent for SSH keys.
'';
};
enableExtraSocket = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable extra socket of the GnuPG key agent (useful for GPG
Agent forwarding).
'';
};
verbose = mkOption {
type = types.bool;
default = false;
description = ''
Whether to produce verbose output.
'';
};
grabKeyboardAndMouse = mkOption {
type = types.bool;
default = true;
description = ''
Tell the pinentry to grab the keyboard and mouse. This
option should in general be used to avoid X-sniffing
attacks. When disabled, this option passes
<option>no-grab</option> setting to gpg-agent.
'';
};
enableScDaemon = mkOption {
type = types.bool;
default = true;
description = ''
Make use of the scdaemon tool. This option has the effect of
enabling the ability to do smartcard operations. When
disabled, this option passes
<option>disable-scdaemon</option> setting to gpg-agent.
'';
};
};
};
@@ -32,23 +92,26 @@ in
config = mkIf cfg.enable (mkMerge [
{
home.file.".gnupg/gpg-agent.conf".text = concatStringsSep "\n" (
optional cfg.enableSshSupport
"enable-ssh-support"
optional (cfg.enableSshSupport) "enable-ssh-support"
++
optional (!cfg.grabKeyboardAndMouse) "no-grab"
++
optional (!cfg.enableScDaemon) "disable-scdaemon"
++
optional (cfg.defaultCacheTtl != null)
"default-cache-ttl ${toString cfg.defaultCacheTtl}"
++
optional (cfg.defaultCacheTtlSsh != null)
"default-cache-ttl-ssh ${toString cfg.defaultCacheTtlSsh}"
);
home.sessionVariables =
optionalAttrs cfg.enableSshSupport {
SSH_AUTH_SOCK = "\${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh";
SSH_AUTH_SOCK = "$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket)";
};
programs.bash.initExtra = ''
GPG_TTY="$(tty)"
export GPG_TTY
gpg-connect-agent updatestartuptty /bye > /dev/null
'';
programs.bash.initExtra = gpgInitStr;
programs.zsh.initExtra = gpgInitStr;
}
# The systemd units below are direct translations of the
@@ -69,7 +132,8 @@ in
};
Service = {
ExecStart = "${pkgs.gnupg}/bin/gpg-agent --supervised";
ExecStart = "${pkgs.gnupg}/bin/gpg-agent --supervised"
+ optionalString cfg.verbose " --verbose";
ExecReload = "${pkgs.gnupg}/bin/gpgconf --reload gpg-agent";
};
};
@@ -113,5 +177,26 @@ in
};
};
})
(mkIf cfg.enableExtraSocket {
systemd.user.sockets.gpg-agent-extra = {
Unit = {
Description = "GnuPG cryptographic agent and passphrase cache (restricted)";
Documentation = "man:gpg-agent(1) man:ssh(1)";
};
Socket = {
ListenStream = "%t/gnupg/S.gpg-agent.extra";
FileDescriptorName = "extra";
Service = "gpg-agent.service";
SocketMode = "0600";
DirectoryMode = "0700";
};
Install = {
WantedBy = [ "sockets.target" ];
};
};
})
]);
}

67
modules/services/kbfs.nix Normal file
View File

@@ -0,0 +1,67 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.kbfs;
in
{
options = {
services.kbfs = {
enable = mkEnableOption "Keybase File System";
mountPoint = mkOption {
type = types.str;
default = "keybase";
description = ''
Mount point for the Keybase filesystem, relative to
<envar>HOME</envar>.
'';
};
extraFlags = mkOption {
type = types.listOf types.str;
default = [];
example = [
"-label kbfs"
"-mount-type normal"
];
description = ''
Additional flags to pass to the Keybase filesystem on launch.
'';
};
};
};
config = mkIf cfg.enable {
systemd.user.services.kbfs = {
Unit = {
Description = "Keybase File System";
Requires = [ "keybase.service" ];
After = [ "keybase.service" ];
};
Service =
let
mountPoint = "\"%h/${cfg.mountPoint}\"";
in {
Environment = "PATH=/run/wrappers KEYBASE_SYSTEMD=1";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountPoint}";
ExecStart ="${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${mountPoint}";
ExecStopPost = "/run/wrappers/bin/fusermount -u ${mountPoint}";
Restart = "on-failure";
PrivateTmp = true;
};
Install = {
WantedBy = [ "default.target" ];
};
};
home.packages = [ pkgs.kbfs ];
services.keybase.enable = true;
};
}

View File

@@ -3,6 +3,8 @@
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.keepassx = {
enable = mkEnableOption "the KeePassX password manager";

View File

@@ -0,0 +1,35 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.keybase;
in
{
options = {
services.keybase = {
enable = mkEnableOption "Keybase";
};
};
config = mkIf cfg.enable {
systemd.user.services.keybase = {
Unit = {
Description = "Keybase service";
};
Service = {
ExecStart = "${pkgs.keybase}/bin/keybase service --auto-forked";
Restart = "on-failure";
PrivateTmp = true;
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

View File

@@ -3,6 +3,8 @@
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.network-manager-applet = {
enable = mkEnableOption "the Network Manager applet";

View File

@@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.owncloud-client = {
enable = mkEnableOption "Owncloud Client";
};
};
config = mkIf config.services.owncloud-client.enable {
systemd.user.services.owncloud-client = {
Unit = {
Description = "Owncloud Client";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.owncloud-client}/bin/owncloud";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View File

@@ -0,0 +1,47 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.parcellite;
package = pkgs.parcellite;
in
{
meta.maintainers = [ maintainers.gleber ];
options = {
services.parcellite = {
enable = mkEnableOption "Parcellite";
};
};
config = mkIf cfg.enable {
home.packages = [ package ];
systemd.user.services.parcellite = {
Unit = {
Description = "Lightweight GTK+ clipboard manager";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
Service = {
# PATH have been added in nixpkgs.parcellite, keeping it here for
# backward compatibility. XDG_DATA_DIRS is necessary to make it pick up
# icons correctly.
Environment = ''
PATH=${package}/bin:${pkgs.which}/bin:${pkgs.xdotool}/bin XDG_DATA_DIRS=${pkgs.hicolor_icon_theme}/share
'';
ExecStart = "${package}/bin/parcellite";
Restart = "on-abort";
};
};
};
}

View File

@@ -0,0 +1,151 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.polybar;
dag = config.lib.dag;
toPolybarIni = generators.toINI {
mkKeyValue = key: value:
let
value' =
if isBool value then (if value then "true" else "false")
else if (isString value && key != "include-file") then ''"${value}"''
else toString value;
in
"${key}=${value'}";
};
configFile = pkgs.writeText "polybar.conf"
(toPolybarIni cfg.config + "\n" + cfg.extraConfig);
script = ''
#!${pkgs.stdenv.shell}
${cfg.script}
'';
in
{
options = {
services.polybar = {
enable = mkEnableOption "Polybar status bar";
package = mkOption {
type = types.package;
default = pkgs.polybar;
defaultText = "pkgs.polybar";
description = "Polybar package to install.";
example = literalExample ''
pkgs.polybar.override {
i3GapsSupport = true;
alsaSupport = true;
iwSupport = true;
githubSupport = true;
}
'';
};
config = mkOption {
type = types.coercedTo
types.path
(p: { "section/base" = { include-file = "${p}"; }; })
(types.attrsOf types.attrs);
description = ''
Polybar configuration. Can be either path to a file, or set of attibutes
that will be used to create the final configuration.
'';
default = {};
example = literalExample ''
{
"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 = mkOption {
type = types.lines;
description = "Additional configuration to add.";
default = "";
example = ''
[module/date]
type = internal/date
interval = 5
date = "%d.%m.%y"
time = %H:%M
format-prefix-foreground = \''${colors.foreground-alt}
label = %time% %date%
'';
};
script = mkOption {
type = types.lines;
description = ''
This script will be used to start the polybars.
Set all necessary environment variables here and start all bars.
It can be assumed that <command>polybar</command> executable is in the <envar>PATH</envar>.
Note, this script must start all bars in the background and then terminate.
'';
example = "polybar bar &";
};
};
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
xdg.configFile."polybar/config".source = configFile;
systemd.user.services.polybar = {
Unit = {
Description = "Polybar status bar";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "forking";
Environment = "PATH=${cfg.package}/bin";
ExecStart = ''${pkgs.writeScriptBin "polybar-start" script}/bin/polybar-start'';
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
home.activation.checkPolybar = dag.entryBefore [ "linkGeneration" ] ''
if ! cmp --quiet \
"${configFile}" \
"$HOME/.config/polybar/config"; then
polybarChanged=1
fi
'';
home.activation.applyPolybar = dag.entryAfter [ "reloadSystemD" ] ''
if [[ -v polybarChanged && -v DISPLAY ]]; then
echo "Restarting polybar"
${config.systemd.user.systemctlPath} --user restart polybar.service
fi
'';
};
}

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.random-background = {
enable = mkEnableOption "random desktop background";

View File

@@ -8,7 +8,10 @@ let
cfg = config.services.redshift;
in {
in
{
meta.maintainers = [ maintainers.rycee ];
options.services.redshift = {
enable = mkOption {
@@ -84,6 +87,15 @@ in {
'';
};
tray = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Start the redshift-gtk tray applet.
'';
};
extraOptions = mkOption {
type = types.listOf types.str;
default = [];
@@ -115,8 +127,9 @@ in {
"-t ${toString cfg.temperature.day}:${toString cfg.temperature.night}"
"-b ${toString cfg.brightness.day}:${toString cfg.brightness.night}"
] ++ cfg.extraOptions;
command = if cfg.tray then "redshift-gtk" else "redshift";
in
"${cfg.package}/bin/redshift ${concatStringsSep " " args}";
"${cfg.package}/bin/${command} ${concatStringsSep " " args}";
RestartSec = 3;
Restart = "always";
};

View File

@@ -0,0 +1,59 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.screen-locker;
in {
options.services.screen-locker = {
enable = mkEnableOption "screen locker for X session";
lockCmd = mkOption {
type = types.str;
description = "Locker command to run.";
example = "\${pkgs.i3lock}/bin/i3lock -n -c 000000";
};
inactiveInterval = mkOption {
type = types.int;
default = 10;
description = ''
Inactive time interval in minutes after which session will be locked.
The minimum is 1 minute, and the maximum is 1 hour.
See <link xlink:href="https://linux.die.net/man/1/xautolock"/>.
'';
};
};
config = mkIf cfg.enable {
systemd.user.services.xautolock-session = {
Unit = {
Description = "xautolock, session locker service";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
Service = {
ExecStart = ''
${pkgs.xautolock}/bin/xautolock \
-detectsleep \
-time ${toString cfg.inactiveInterval} \
-locker '${pkgs.systemd}/bin/loginctl lock-session $XDG_SESSION_ID'
'';
};
};
# xss-lock will run specified screen locker when the session is locked via loginctl
# can't be started as a systemd service,
# see https://bitbucket.org/raymonad/xss-lock/issues/13/allow-operation-as-systemd-user-unit
xsession.initExtra = "${pkgs.xss-lock}/bin/xss-lock -- ${cfg.lockCmd} &";
};
}

View File

@@ -0,0 +1,94 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.stalonetray;
in
{
options = {
services.stalonetray = {
enable = mkEnableOption "Stalonetray system tray";
package = mkOption {
default = pkgs.stalonetray;
defaultText = "pkgs.stalonetray";
type = types.package;
example = literalExample "pkgs.stalonetray";
description = "The package to use for the Stalonetray binary.";
};
config = mkOption {
type = with types;
attrsOf (nullOr (either str (either bool int)));
description = ''
Stalonetray configuration as a set of attributes.
'';
default = {};
example = {
geometry = "3x1-600+0";
decorations = null;
icon_size = 30;
sticky = true;
background = "#cccccc";
};
};
extraConfig = mkOption {
type = types.lines;
description = "Additional configuration lines for stalonetrayrc.";
default = "";
example = ''
geometry 3x1-600+0
decorations none
icon_size 30
sticky true
background "#cccccc"
'';
};
};
};
config = mkIf cfg.enable (mkMerge [
{
home.packages = [ cfg.package ];
systemd.user.services.stalonetray = {
Unit = {
Description = "Stalonetray system tray";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${cfg.package}/bin/stalonetray";
Restart = "on-failure";
};
};
}
(mkIf (cfg.config != {}) {
home.file.".stalonetrayrc".text =
let
valueToString = v:
if isBool v then (if v then "true" else "false")
else if (v==null) then "none"
else ''"${toString v}"'';
in
concatStrings (
mapAttrsToList (k: v: "${k} ${valueToString v}\n") cfg.config
);
})
(mkIf (cfg.extraConfig != "") {
home.file.".stalonetrayrc".text = cfg.extraConfig;
})
]);
}

View File

@@ -0,0 +1,67 @@
{ config, lib, pkgs, ... }:
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.syncthing = {
enable = mkEnableOption "Syncthing continuous file synchronization";
tray = mkOption {
type = types.bool;
default = false;
description = "Whether to enable QSyncthingTray service.";
};
};
};
config = mkMerge [
(mkIf config.services.syncthing.enable {
systemd.user.services = {
syncthing = {
Unit = {
Description = "Syncthing - Open Source Continuous File Synchronization";
Documentation = "man:syncthing(1)";
After = [ "network.target" ];
};
Service = {
ExecStart = "${pkgs.syncthing}/bin/syncthing -no-browser -no-restart -logflags=0";
Restart = "on-failure";
SuccessExitStatus = [ 3 4 ];
RestartForceExitStatus = [ 3 4 ];
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
})
(mkIf config.services.syncthing.tray {
systemd.user.services = {
qsyncthingtray = {
Unit = {
Description = "QSyncthingTray";
After = [ "graphical-session-pre.target"
"polybar.service"
"taffybar.service"
"stalonetray.service" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.qsyncthingtray}/bin/QSyncthingTray";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
})
];
}

View File

@@ -9,6 +9,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.taffybar = {
enable = mkEnableOption "Taffybar";

View File

@@ -3,6 +3,8 @@
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.tahoe-lafs = {
enable = mkEnableOption "Tahoe-LAFS";

View File

@@ -2,23 +2,78 @@
with lib;
let
cfg = config.services.udiskie;
commandArgs =
concatStringsSep " " (
map (opt: "-" + opt) [
(if cfg.automount then "a" else "A")
(if cfg.notify then "n" else "N")
({ always = "t"; auto = "s"; never = "T"; }.${cfg.tray})
]
);
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.udiskie = {
enable = mkEnableOption "Udiskie mount daemon";
enable = mkEnableOption "udiskie mount daemon";
automount = mkOption {
type = types.bool;
default = true;
description = "Whether to automatically mount new devices.";
};
notify = mkOption {
type = types.bool;
default = true;
description = "Whether to show pop-up notifications.";
};
tray = mkOption {
type = types.enum [ "always" "auto" "never" ];
default = "auto";
description = ''
Whether to display tray icon.
</para><para>
The options are
<variablelist>
<varlistentry>
<term><literal>always</literal></term>
<listitem><para>Always show tray icon.</para></listitem>
</varlistentry>
<varlistentry>
<term><literal>auto</literal></term>
<listitem><para>
Show tray icon only when there is a device available.
</para></listitem>
</varlistentry>
<varlistentry>
<term><literal>never</literal></term>
<listitem><para>Never show tray icon.</para></listitem>
</varlistentry>
</variablelist>
'';
};
};
};
config = mkIf config.services.udiskie.enable {
systemd.user.services.udiskie = {
Unit = {
Description = "Udiskie mount daemon";
Description = "udiskie mount daemon";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.pythonPackages.udiskie}/bin/udiskie -2 -A -n -s";
ExecStart = "${pkgs.pythonPackages.udiskie}/bin/udiskie -2 ${commandArgs}";
};
Install = {

View File

@@ -0,0 +1,63 @@
{ config, lib, pkgs, ... }:
with lib;
let cfg = config.services.unclutter;
in {
options.services.unclutter = {
enable = mkEnableOption "unclutter";
package = mkOption {
description = "unclutter derivation to use.";
type = types.package;
default = pkgs.unclutter-xfixes;
defaultText = "pkgs.unclutter-xfixes";
};
timeout = mkOption {
description = "Number of seconds before the cursor is marked inactive.";
type = types.int;
default = 1;
};
threshold = mkOption {
description = "Minimum number of pixels considered cursor movement.";
type = types.int;
default = 1;
};
extraOptions = mkOption {
description = "More arguments to pass to the unclutter command.";
type = types.listOf types.str;
default = [ ];
example = [ "exclude-root" "ignore-scrolling" ];
};
};
config = mkIf cfg.enable {
systemd.user.services.unclutter = {
Unit = {
Description = "unclutter";
After = [ "graphical-session-pre.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = ''
${cfg.package}/bin/unclutter \
--timeout ${toString cfg.timeout} \
--jitter ${toString (cfg.threshold - 1)} \
${concatMapStrings (x: " --${x}") cfg.extraOptions}
'';
RestartSec = 3;
Restart = "always";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View File

@@ -0,0 +1,779 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.xsession.windowManager.i3;
dag = config.lib.dag;
startupModule = types.submodule {
options = {
command = mkOption {
type = types.string;
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.string;
default = null;
description = "Launch application on a particular workspace.";
};
};
};
barColorSetModule = types.submodule {
options = {
border = mkOption {
type = types.string;
visible = false;
};
background = mkOption {
type = types.string;
visible = false;
};
text = mkOption {
type = types.string;
visible = false;
};
};
};
colorSetModule = types.submodule {
options = {
border = mkOption {
type = types.string;
visible = false;
};
childBorder = mkOption {
type = types.string;
visible = false;
};
background = mkOption {
type = types.string;
visible = false;
};
text = mkOption {
type = types.string;
visible = false;
};
indicator = mkOption {
type = types.string;
visible = false;
};
};
};
barModule = types.submodule {
options = {
id = mkOption {
type = types.nullOr types.string;
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.string;
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.string;
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.string;
default = "#000000";
description = "Background color of the bar.";
};
statusline = mkOption {
type = types.string;
default = "#ffffff";
description = "Text color to be used for the statusline.";
};
separator = mkOption {
type = types.string;
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"/>.
'';
};
};
};
windowCommandModule = types.submodule {
options = {
command = mkOption {
type = types.string;
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.attrs;
configModule = types.submodule {
options = {
fonts = mkOption {
type = types.listOf types.string;
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" ];
};
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 = "Mod1";
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 assignes applications to workspaces based
on criteria.
'';
example = literalExample ''
{
"1: web" = [{ class = "^Firefox$"; }];
"0: extra" = [{ class = "^Firefox$"; window_role = "About"; }];
}
'';
};
keybindings = mkOption {
type = types.attrs;
default = {
"Mod1+Return" = "exec i3-sensible-terminal";
"Mod1+Shift+q" = "kill";
"Mod1+d" = "exec ${pkgs.dmenu}/bin/dmenu_run";
"Mod1+Left" = "focus left";
"Mod1+Down" = "focus down";
"Mod1+Up" = "focus up";
"Mod1+Right" = "focus right";
"Mod1+h" = "split h";
"Mod1+v" = "split v";
"Mod1+f" = "fullscreen toggle";
"Mod1+s" = "layout stacking";
"Mod1+w" = "layout tabbed";
"Mod1+e" = "layout toggle split";
"Mod1+Shift+space" = "floating toggle";
"Mod1+1" = "workspace 1";
"Mod1+2" = "workspace 2";
"Mod1+3" = "workspace 3";
"Mod1+4" = "workspace 4";
"Mod1+5" = "workspace 5";
"Mod1+6" = "workspace 6";
"Mod1+7" = "workspace 7";
"Mod1+8" = "workspace 8";
"Mod1+9" = "workspace 9";
"Mod1+Shift+1" = "move container to workspace 1";
"Mod1+Shift+2" = "move container to workspace 2";
"Mod1+Shift+3" = "move container to workspace 3";
"Mod1+Shift+4" = "move container to workspace 4";
"Mod1+Shift+5" = "move container to workspace 5";
"Mod1+Shift+6" = "move container to workspace 6";
"Mod1+Shift+7" = "move container to workspace 7";
"Mod1+Shift+8" = "move container to workspace 8";
"Mod1+Shift+9" = "move container to workspace 9";
"Mod1+Shift+c" = "reload";
"Mod1+Shift+r" = "restart";
"Mod1+Shift+e" = "exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
"Mod1+r" = "mode resize";
};
defaultText = "Default i3 keybindings.";
description = ''
An attribute set that assignes key press to an action using key symbol.
See <link xlink:href="https://i3wm.org/docs/userguide.html#keybindings"/>.
'';
example = literalExample ''
{
"Mod1+Return" = "exec i3-sensible-terminal";
"Mod1+Shift+q" = "kill";
"Mod1+d" = "exec ${pkgs.dmenu}/bin/dmenu_run";
}
'';
};
keycodebindings = mkOption {
type = types.attrs;
default = {};
description = ''
An attribute set that assignes 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.string;
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.attrs;
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: "bindsym ${keycomb} ${action}") keybindings
);
keycodebindingsStr = keycodebindings: concatStringsSep "\n" (
mapAttrsToList (keycomb: action: "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, mode, hiddenState, position, workspaceButtons,
workspaceNumbers, command, statusCommand, colors, ...
}: ''
bar {
${optionalString (id != null) "id ${id}"}
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"}
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}
}
}
'';
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"}
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 = "pkgs.i3";
example = "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;
home.activation.checkI3 = dag.entryBefore [ "linkGeneration" ] ''
if ! cmp --quiet \
"${configFile}" \
"${config.xdg.configHome}/i3/config"; then
i3Changed=1
fi
'';
home.activation.reloadI3 = dag.entryAfter [ "linkGeneration" ] ''
if [[ -v i3Changed && -v DISPLAY ]]; then
echo "Reloading i3"
${cfg.package}/bin/i3-msg 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
);
})
]);
}

View File

@@ -0,0 +1,114 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.xsession.windowManager.xmonad;
dag = config.lib.dag;
xmonad = pkgs.xmonad-with-packages.override {
ghcWithPackages = cfg.haskellPackages.ghcWithPackages;
packages = self:
cfg.extraPackages self
++ optionals cfg.enableContribAndExtras [
self.xmonad-contrib self.xmonad-extras
];
};
in
{
options = {
xsession.windowManager.xmonad = {
enable = mkEnableOption "xmonad window manager";
haskellPackages = mkOption {
default = pkgs.haskellPackages;
defaultText = "pkgs.haskellPackages";
example = literalExample "pkgs.haskell.packages.ghc784";
description = ''
The <varname>haskellPackages</varname> used to build xmonad
and other packages. This can be used to change the GHC
version used to build xmonad and the packages listed in
<varname>extraPackages</varname>.
'';
};
extraPackages = mkOption {
default = self: [];
defaultText = "self: []";
example = literalExample ''
haskellPackages: [
haskellPackages.xmonad-contrib
haskellPackages.monad-logger
]
'';
description = ''
Extra packages available to GHC when rebuilding xmonad. The
value must be a function which receives the attribute set
defined in <varname>haskellPackages</varname> as the sole
argument.
'';
};
enableContribAndExtras = mkOption {
default = false;
type = types.bool;
description = "Enable xmonad-{contrib,extras} in xmonad.";
};
config = mkOption {
type = types.nullOr types.path;
default = null;
example = literalExample ''
pkgs.writeText "xmonad.hs" '''
import XMonad
main = xmonad defaultConfig
{ terminal = "urxvt"
, modMask = mod4Mask
, borderWidth = 3
}
'''
'';
description = ''
The configuration file to be used for xmonad. This must be
an absolute path or <literal>null</literal> in which case
<filename>~/.xmonad/xmonad.hs</filename> will not be managed
by Home Manager.
'';
};
};
};
config = mkIf cfg.enable (mkMerge [
{
home.packages = [ (lowPrio xmonad) ];
xsession.windowManager.command = "${xmonad}/bin/xmonad";
}
(mkIf (cfg.config != null) {
home.file.".xmonad/xmonad.hs".source = cfg.config;
home.activation.checkXmonad = dag.entryBefore [ "linkGeneration" ] ''
if ! cmp --quiet "${cfg.config}" "$HOME/.xmonad/xmonad.hs"; then
xmonadChanged=1
fi
'';
home.activation.applyXmonad = dag.entryAfter [ "linkGeneration" ] ''
if [[ -v xmonadChanged ]]; then
echo "Recompiling xmonad"
${config.xsession.windowManager.command} --recompile
# Attempt to restart xmonad if X is running.
if [[ -v DISPLAY ]] ; then
echo "Restarting xmonad"
${config.xsession.windowManager.command} --restart
fi
fi
'';
})
]);
}

View File

@@ -3,6 +3,8 @@
with lib;
{
meta.maintainers = [ maintainers.rycee ];
options = {
services.xscreensaver = {
enable = mkEnableOption "XScreenSaver";
@@ -10,6 +12,9 @@ with lib;
};
config = mkIf config.services.xscreensaver.enable {
# To make the xscreensaver-command tool available.
home.packages = [ pkgs.xscreensaver ];
systemd.user.services.xscreensaver = {
Unit = {
Description = "XScreenSaver";

189
modules/systemd-activate.rb Normal file
View File

@@ -0,0 +1,189 @@
require 'set'
require 'open3'
@dry_run = ENV['DRY_RUN']
@verbose = ENV['VERBOSE']
UnitsDir = 'home-files/.config/systemd/user'
# 1. Stop all services from the old generation that are not present in the new generation.
# 2. Ensure all services from the new generation that are wanted by active targets are running:
# - Start services that are not already running.
# - Restart services whose unit config files have changed between generations.
# 3. If any services were (re)started, wait 'start_timeout_ms' and report services
# that failed to start. This helps debugging quickly failing services.
#
# Whenever service failures are detected, show the output of
# 'systemd --home status' for the affected services.
#
def setup_services(old_gen_path, new_gen_path, start_timeout_ms_string)
start_timeout_ms = start_timeout_ms_string.to_i
old_units_path = File.join(old_gen_path, UnitsDir) unless old_gen_path.empty?
new_units_path = File.join(new_gen_path, UnitsDir)
old_services = get_services(old_units_path)
new_services = get_services(new_units_path)
exit if old_services.empty? && new_services.empty?
services_to_run = get_services_to_run(new_units_path)
maybe_changed_services = services_to_run & old_services
# 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_start = get_inactive_units(services_to_run - to_restart)
raise "daemon-reload failed" unless run_cmd('systemctl --user daemon-reload')
# Exclude services that aren't allowed to be manually started or stopped
no_manual_start, no_manual_stop = get_restricted_units(to_stop + to_restart + to_start)
to_stop -= no_manual_stop
to_restart -= no_manual_stop + no_manual_start
to_start -= no_manual_start
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)
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)
if failed.empty?
print_service_msg("All services are running", services_to_run)
else
puts
puts "Error. These services failed to start:", failed
show_failed_services_status(failed)
exit 1
end
end
end
end
def get_services(dir)
services = get_service_files(dir) if dir && Dir.exists?(dir)
Set.new(services)
end
def get_service_files(dir)
Dir.chdir(dir) { Dir['*.service'] }
end
def get_changed_services(dir_a, dir_b, services)
services.select do |service|
a = File.join(dir_a, service)
b = File.join(dir_b, service)
(File.size(a) != File.size(b)) || (File.read(a) != File.read(b))
end
end
TargetDirRegexp = /^(.*\.target)\.wants$/
# @return all services wanted by active targets
def get_services_to_run(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|
get_service_files(File.join(units_dir, "#{target}.wants"))
end.flatten
Set.new(services_to_run)
end
# @return true on success
def run_cmd(cmd)
print_cmd cmd
@dry_run || system(cmd)
end
def systemctl(cmd, services)
return if services.empty?
verb = (cmd == 'stop') ? 'Stopping' : "#{cmd.capitalize}ing"
puts "#{verb}: #{services.join(' ')}"
cmd = ['systemctl', '--user', cmd, *services]
if @dry_run
puts cmd
return
end
output, status = Open3.capture2e(*cmd)
print output
# Show status for failed services
unless status.success?
# Due to a bug in systemd, the '--user' argument is not always provided
output.scan(/systemctl (?:--user )?(status .*?)['"]/).flatten.each do |status_cmd|
puts
run_cmd("systemctl --user #{status_cmd}")
end
exit 1
end
end
def print_cmd(cmd)
puts cmd if @verbose || @dry_run
end
def get_active_units(units)
get_units_by_activity(units, true)
end
def get_inactive_units(units)
get_units_by_activity(units, false)
end
def get_units_by_activity(units, active)
return [] if units.empty?
units = units.to_a
is_active = `systemctl --user is-active #{units.join(' ')}`.split
units.select.with_index do |_, i|
(is_active[i] == 'active') == active
end
end
def get_restricted_units(units)
infos = `systemctl --user show -p RefuseManualStart -p RefuseManualStop #{units.to_a.join(' ')}`
.split("\n\n")
no_manual_start = []
no_manual_stop = []
infos.zip(units).each do |info, unit|
no_start, no_stop = info.split("\n")
no_manual_start << unit if no_start.end_with?('yes')
no_manual_stop << unit if no_stop.end_with?('yes')
end
[no_manual_start, no_manual_stop]
end
def wait_and_get_failed_services(services, start_timeout_ms)
puts "Waiting #{start_timeout_ms} ms for services to fail"
# Force the previous message to always be visible before sleeping
STDOUT.flush
sleep(start_timeout_ms / 1000.0)
get_inactive_units(services)
end
def show_failed_services_status(services)
puts
services.each do |service|
run_cmd("systemctl --user status #{service}")
puts
end
end
def print_service_msg(msg, services)
return if services.empty?
if @verbose
puts "#{msg}:", services.to_a
else
puts msg
end
end
setup_services(*ARGV)

114
modules/systemd-activate.sh Normal file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env bash
function isStartable() {
local service="$1"
[[ $(systemctl --user show -p RefuseManualStart "$service") == *=no ]]
}
function isStoppable() {
if [[ -v oldGenPath ]] ; then
local service="$1"
[[ $(systemctl --user show -p RefuseManualStop "$service") == *=no ]]
fi
}
function systemdPostReload() {
local workDir
workDir="$(mktemp -d)"
if [[ -v oldGenPath ]] ; then
local oldUserServicePath="$oldGenPath/home-files/.config/systemd/user"
fi
local newUserServicePath="$newGenPath/home-files/.config/systemd/user"
local oldServiceFiles="$workDir/old-files"
local newServiceFiles="$workDir/new-files"
local servicesDiffFile="$workDir/diff-files"
if [[ ! (-v oldUserServicePath && -d "$oldUserServicePath") \
&& ! -d "$newUserServicePath" ]]; then
return
fi
if [[ ! (-v oldUserServicePath && -d "$oldUserServicePath") ]]; then
touch "$oldServiceFiles"
else
find "$oldUserServicePath" \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$oldServiceFiles"
fi
if [[ ! -d "$newUserServicePath" ]]; then
touch "$newServiceFiles"
else
find "$newUserServicePath" \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$newServiceFiles"
fi
diff \
--new-line-format='+%L' \
--old-line-format='-%L' \
--unchanged-line-format=' %L' \
"$oldServiceFiles" "$newServiceFiles" \
> "$servicesDiffFile" || true
local -a maybeRestart=( $(grep '^ ' "$servicesDiffFile" | cut -c2-) )
local -a maybeStop=( $(grep '^-' "$servicesDiffFile" | cut -c2-) )
local -a maybeStart=( $(grep '^+' "$servicesDiffFile" | cut -c2-) )
local -a toRestart=( )
local -a toStop=( )
local -a toStart=( )
for f in "${maybeRestart[@]}" ; do
if isStoppable "$f" \
&& isStartable "$f" \
&& systemctl --quiet --user is-active "$f" \
&& ! cmp --quiet \
"$oldUserServicePath/$f" \
"$newUserServicePath/$f" ; then
toRestart+=("$f")
fi
done
for f in "${maybeStop[@]}" ; do
if isStoppable "$f" ; then
toStop+=("$f")
fi
done
for f in "${maybeStart[@]}" ; do
if isStartable "$f" ; then
toStart+=("$f")
fi
done
rm -r "$workDir"
local sugg=""
if [[ -n "${toRestart[@]}" ]] ; then
sugg="${sugg}systemctl --user restart ${toRestart[@]}\n"
fi
if [[ -n "${toStop[@]}" ]] ; then
sugg="${sugg}systemctl --user stop ${toStop[@]}\n"
fi
if [[ -n "${toStart[@]}" ]] ; then
sugg="${sugg}systemctl --user start ${toStart[@]}\n"
fi
if [[ -n "$sugg" ]] ; then
echo "Suggested commands:"
echo -n -e "$sugg"
fi
}
oldGenPath="$1"
newGenPath="$2"
$DRY_RUN_CMD systemctl --user daemon-reload
systemdPostReload

View File

@@ -1,18 +1,19 @@
{ config, lib, pkgs, ... }:
with lib;
with import ./lib/dag.nix;
let
cfg = config.systemd.user;
dag = config.lib.dag;
enabled = cfg.services != {}
|| cfg.sockets != {}
|| cfg.targets != {}
|| cfg.timers != {};
toSystemdIni = (import lib/generators.nix).toINI {
toSystemdIni = generators.toINI {
mkKeyValue = key: value:
let
value' =
@@ -24,16 +25,21 @@ let
buildService = style: name: serviceCfg:
let
source = pkgs.writeText "${name}.${style}" (toSystemdIni serviceCfg);
filename = "${name}.${style}";
# Needed because systemd derives unit names from the ultimate
# link target.
source = pkgs.writeTextDir filename (toSystemdIni serviceCfg)
+ "/" + filename;
wantedBy = target:
{
name = ".config/systemd/user/${target}.wants/${name}.${style}";
name = "systemd/user/${target}.wants/${filename}";
value = { inherit source; };
};
in
singleton {
name = ".config/systemd/user/${name}.${style}";
name = "systemd/user/${filename}";
value = { inherit source; };
}
++
@@ -42,11 +48,26 @@ let
buildServices = style: serviceCfgs:
concatLists (mapAttrsToList (buildService style) serviceCfgs);
servicesStartTimeoutMs = builtins.toString cfg.servicesStartTimeoutMs;
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
systemd.user = {
systemctlPath = mkOption {
default = "${pkgs.systemd}/bin/systemctl";
defaultText = "\${pkgs.systemd}/bin/systemctl";
type = types.str;
description = ''
Absolute path to the <command>systemctl</command> tool. This
option may need to be set if running Home Manager on a
non-NixOS distribution.
'';
};
services = mkOption {
default = {};
type = types.attrs;
@@ -70,6 +91,25 @@ in
type = types.attrs;
description = "Definition of systemd per-user timers";
};
startServices = mkOption {
default = false;
type = types.bool;
description = ''
Start all services that are wanted by active targets.
Additionally, stop obsolete services from the previous
generation.
'';
};
servicesStartTimeoutMs = mkOption {
default = 0;
type = types.int;
description = ''
How long to wait for started services to fail until their
start is considered successful.
'';
};
};
};
@@ -94,7 +134,7 @@ in
# If we run under a Linux system we assume that systemd is
# available, in particular we assume that systemctl is in PATH.
(mkIf pkgs.stdenv.isLinux {
home.file =
xdg.configFile =
listToAttrs (
(buildServices "service" cfg.services)
++
@@ -105,89 +145,33 @@ in
(buildServices "timer" cfg.timers)
);
home.activation.reloadSystemD = dagEntryAfter ["linkGeneration"] ''
function systemdPostReload() {
local workDir
workDir="$(mktemp -d)"
# Run systemd service reload if user is logged in. If we're
# 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 = dag.entryAfter ["linkGeneration"] (
let
autoReloadCmd = ''
${pkgs.ruby}/bin/ruby ${./systemd-activate.rb} \
"''${oldGenPath=}" "$newGenPath" "${servicesStartTimeoutMs}"
'';
if [[ -v oldGenPath ]] ; then
local oldUserServicePath="$oldGenPath/home-files/.config/systemd/user"
fi
legacyReloadCmd = ''
bash ${./systemd-activate.sh} "''${oldGenPath=}" "$newGenPath"
'';
local newUserServicePath="$newGenPath/home-files/.config/systemd/user"
local oldServiceFiles="$workDir/old-files"
local newServiceFiles="$workDir/new-files"
local servicesDiffFile="$workDir/diff-files"
if [[ ! (-v oldUserServicePath && -d "$oldUserServicePath") \
&& ! -d "$newUserServicePath" ]]; then
return
fi
if [[ ! (-v oldUserServicePath && -d "$oldUserServicePath") ]]; then
touch "$oldServiceFiles"
else
find "$oldUserServicePath" \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$oldServiceFiles"
fi
if [[ ! -d "$newUserServicePath" ]]; then
touch "$newServiceFiles"
else
find "$newUserServicePath" \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$newServiceFiles"
fi
diff \
--new-line-format='+%L' \
--old-line-format='-%L' \
--unchanged-line-format=' %L' \
"$oldServiceFiles" "$newServiceFiles" \
> $servicesDiffFile
local -a maybeRestart=( $(grep '^ ' $servicesDiffFile | cut -c2-) )
local -a toStop=( $(grep '^-' $servicesDiffFile | cut -c2-) )
local -a toStart=( $(grep '^+' $servicesDiffFile | cut -c2-) )
local -a toRestart=( )
for f in ''${maybeRestart[@]} ; do
if systemctl --quiet --user is-active "$f" \
&& ! cmp --quiet \
"$oldUserServicePath/$f" \
"$newUserServicePath/$f" ; then
toRestart+=("$f")
ensureRuntimeDir = "XDG_RUNTIME_DIR=\${XDG_RUNTIME_DIR:-/run/user/$(id -u)}";
in
''
if ${ensureRuntimeDir} ${cfg.systemctlPath} --quiet --user is-system-running 2> /dev/null; then
${ensureRuntimeDir} \
PATH=${dirOf cfg.systemctlPath}:$PATH \
${if cfg.startServices then autoReloadCmd else legacyReloadCmd}
else
echo "User systemd daemon not running. Skipping reload."
fi
done
rm -r $workDir
local sugg=""
if [[ -n "''${toRestart[@]}" ]] ; then
sugg="''${sugg}systemctl --user restart ''${toRestart[@]}\n"
fi
if [[ -n "''${toStop[@]}" ]] ; then
sugg="''${sugg}systemctl --user stop ''${toStop[@]}\n"
fi
if [[ -n "''${toStart[@]}" ]] ; then
sugg="''${sugg}systemctl --user start ''${toStart[@]}\n"
fi
if [[ -n "$sugg" ]] ; then
echo "Suggested commands:"
echo -n -e "$sugg"
fi
}
$DRY_RUN_CMD systemctl --user daemon-reload
systemdPostReload
'';
''
);
})
];
}

76
modules/xcursor.nix Normal file
View File

@@ -0,0 +1,76 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.xsession.pointerCursor;
cursorType = types.submodule {
options = {
package = mkOption {
type = types.package;
example = literalExample "pkgs.vanilla-dmz";
description = "Package providing the cursor theme.";
};
name = mkOption {
type = types.str;
example = "Vanilla-DMZ";
description = "The cursor name within the package.";
};
size = mkOption {
type = types.int;
default = 32;
example = 64;
description = "The cursor size.";
};
};
};
in
{
meta.maintainers = [ maintainers.league ];
options = {
xsession.pointerCursor = mkOption {
type = types.nullOr cursorType;
default = null;
description = ''
The X cursor theme and settings. The package
<varname>xorg.xcursorthemes</varname> contains cursors named
whiteglass, redglass, and handhelds. The package
<varname>vanilla-dmz</varname> contains cursors named Vanilla-DMZ
and Vanilla-DMZ-AA. Note: handhelds does not seem to work at
custom sizes.
'';
};
};
config = mkIf (cfg != null) {
home.packages = [cfg.package];
xsession.initExtra = ''
${pkgs.xorg.xsetroot}/bin/xsetroot -xcf ${cfg.package}/share/icons/${cfg.name}/cursors/X_cursor ${toString cfg.size}
'';
xresources.properties = {
"Xcursor.theme" = cfg.name;
"Xcursor.size" = cfg.size;
};
gtk.gtk2.extraConfig = ''
gtk-cursor-theme-name="${cfg.name}"
gtk-cursor-theme-size=${toString cfg.size}
'';
gtk.gtk3.extraConfig = {
"gtk-cursor-theme-name" = cfg.name;
"gtk-cursor-theme-size" = cfg.size;
};
};
}

View File

@@ -17,6 +17,8 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
xresources.properties = mkOption {
type = types.nullOr types.attrs;

View File

@@ -9,11 +9,13 @@ let
in
{
meta.maintainers = [ maintainers.rycee ];
options = {
xsession = {
enable = mkEnableOption "X Session";
windowManager = mkOption {
windowManager.command = mkOption {
type = types.str;
example = literalExample ''
let
@@ -23,7 +25,15 @@ in
in
"''${xmonad}/bin/xmonad";
'';
description = "Path to window manager to exec.";
description = ''
Window manager start command.
'';
};
profileExtra = mkOption {
type = types.lines;
default = "";
description = "Extra shell commands to run before session start.";
};
initExtra = mkOption {
@@ -71,14 +81,16 @@ in
};
};
home.file.".xsession" = {
mode = "555";
text = ''
home.file.".xprofile".text = ''
. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh"
if [[ -e "$HOME/.profile" ]]; then
. "$HOME/.profile"
fi
# If there are any running services from a previous session.
# Need to run this in xprofile because the NixOS xsession
# script starts up graphical-session.target.
systemctl --user stop graphical-session.target graphical-session-pre.target
systemctl --user import-environment DBUS_SESSION_BUS_ADDRESS
@@ -87,12 +99,26 @@ in
systemctl --user import-environment XAUTHORITY
systemctl --user import-environment XDG_DATA_DIRS
systemctl --user import-environment XDG_RUNTIME_DIR
systemctl --user import-environment XDG_SESSION_ID
${cfg.profileExtra}
export HM_XPROFILE_SOURCED=1
'';
home.file.".xsession" = {
executable = true;
text = ''
if [[ ! -v HM_XPROFILE_SOURCED ]]; then
. ~/.xprofile
fi
unset HM_XPROFILE_SOURCED
systemctl --user start hm-graphical-session.target
${cfg.initExtra}
${cfg.windowManager}
${cfg.windowManager.command}
systemctl --user stop graphical-session.target
systemctl --user stop graphical-session-pre.target

57
nixos/default.nix Normal file
View File

@@ -0,0 +1,57 @@
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.home-manager;
hmModule = types.submodule ({name, ...}: {
imports = import ../modules/modules.nix {
inherit lib pkgs;
nixosSubmodule = true;
};
config = {
home.username = config.users.users.${name}.name;
home.homeDirectory = config.users.users.${name}.home;
};
});
in
{
options = {
home-manager.users = mkOption {
type = types.attrsOf hmModule;
default = {};
description = ''
Per-user Home Manager configuration.
'';
};
};
config = mkIf (cfg.users != {}) {
systemd.services = mapAttrs' (username: usercfg:
nameValuePair ("home-manager-${utils.escapeSystemdPath username}") {
description = "Home Manager environment for ${username}";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = username;
Type = "oneshot";
RemainAfterExit = "yes";
SyslogIdentifier = "hm-activate-${username}";
# The activation script is run by a login shell to make sure
# that the user is given a sane Nix environment.
ExecStart = pkgs.writeScript "activate-${username}" ''
#! ${pkgs.stdenv.shell} -el
echo Activating home-manager configuration for ${username}
exec ${usercfg.home.activationPackage}/activate
'';
};
}
) cfg.users;
};
}

3
overlay.nix Normal file
View File

@@ -0,0 +1,3 @@
self: super: {
home-manager = import ./home-manager { pkgs = super; };
}