mirror of
https://github.com/nix-community/home-manager.git
synced 2026-01-11 17:39:37 +08:00
Compare commits
266 Commits
release-17
...
release-17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2945576b7 | ||
|
|
bea5f92ec5 | ||
|
|
963de93c55 | ||
|
|
eda46344a0 | ||
|
|
5db56a320c | ||
|
|
698094b4d6 | ||
|
|
7943291ac4 | ||
|
|
59dee6a501 | ||
|
|
967d0f93a2 | ||
|
|
337168f47e | ||
|
|
124acb089c | ||
|
|
b9382832ad | ||
|
|
f7628e2996 | ||
|
|
467ba9cafd | ||
|
|
6730c32c98 | ||
|
|
b6affe8d57 | ||
|
|
ef5a8a5941 | ||
|
|
de2c181eae | ||
|
|
5fd31df9d3 | ||
|
|
61101184b4 | ||
|
|
474478c4a3 | ||
|
|
afa865587e | ||
|
|
9ea353569a | ||
|
|
1bc59f7290 | ||
|
|
563a20fc82 | ||
|
|
6cca1fc512 | ||
|
|
6833f96c14 | ||
|
|
2304c145f3 | ||
|
|
fa6f697dbb | ||
|
|
91a98f919d | ||
|
|
616dbd67f7 | ||
|
|
6fc0fd315c | ||
|
|
9de2549dfb | ||
|
|
81fb420457 | ||
|
|
8b77f1db2c | ||
|
|
a154e2ea1a | ||
|
|
5fe8d574ca | ||
|
|
a9dc7fa7cc | ||
|
|
9d3d7426aa | ||
|
|
a597c66afe | ||
|
|
21fefbc8f6 | ||
|
|
38020d9068 | ||
|
|
1b0a5eb54a | ||
|
|
071f7aea82 | ||
|
|
32b3f7f2d2 | ||
|
|
576217d33a | ||
|
|
b8b595c6b2 | ||
|
|
a93445f3fe | ||
|
|
dbcb3dd1ae | ||
|
|
d6ab6ee370 | ||
|
|
c9294e30d9 | ||
|
|
d7715f71ad | ||
|
|
b2ed0a902b | ||
|
|
18159c85b9 | ||
|
|
d7755de116 | ||
|
|
7631921366 | ||
|
|
803abb58f9 | ||
|
|
a3250dfac7 | ||
|
|
4f9158e533 | ||
|
|
e624b9aa6a | ||
|
|
2fc1b9b5e0 | ||
|
|
026375da49 | ||
|
|
58a629b02e | ||
|
|
df6590abfc | ||
|
|
33af9948e5 | ||
|
|
8ab6298f30 | ||
|
|
78c308c835 | ||
|
|
8a2bf21cee | ||
|
|
59f44c1189 | ||
|
|
02219dcd79 | ||
|
|
f0d207f380 | ||
|
|
7dd09cecda | ||
|
|
e75b68e391 | ||
|
|
8d360c5a57 | ||
|
|
f6900f0689 | ||
|
|
8759a5a63e | ||
|
|
52bdbc42bb | ||
|
|
28e00b68fd | ||
|
|
2ff09158f3 | ||
|
|
6764c26954 | ||
|
|
040159c02f | ||
|
|
61c6c83de4 | ||
|
|
0be32c9d42 | ||
|
|
aa1bf31bcb | ||
|
|
c023b0532a | ||
|
|
f8aaba6704 | ||
|
|
26cf42049d | ||
|
|
06d4f39e7b | ||
|
|
a1e36a9a37 | ||
|
|
11da41e106 | ||
|
|
7a5b9152e9 | ||
|
|
7876d533cf | ||
|
|
e99de88c5c | ||
|
|
bcb82da88f | ||
|
|
592fd61788 | ||
|
|
3c875267af | ||
|
|
455ce37398 | ||
|
|
64befb27eb | ||
|
|
177565567e | ||
|
|
8045e56df2 | ||
|
|
206a4e17b5 | ||
|
|
2785bf9cb2 | ||
|
|
bc2f2ad546 | ||
|
|
4fce730326 | ||
|
|
9206f363ff | ||
|
|
130e33a4e7 | ||
|
|
a36989a860 | ||
|
|
1946343d5b | ||
|
|
4ccf43d753 | ||
|
|
7ca68c6492 | ||
|
|
61a869a1f5 | ||
|
|
c718951e97 | ||
|
|
04ea044917 | ||
|
|
66219f23bb | ||
|
|
187a12e90a | ||
|
|
356c0bf751 | ||
|
|
7a9c873093 | ||
|
|
d7537777c3 | ||
|
|
145aefc9d1 | ||
|
|
2b2e20da24 | ||
|
|
a977c79f9f | ||
|
|
f52ec0df7c | ||
|
|
54043df8fb | ||
|
|
fad1e108d8 | ||
|
|
fc3e82584b | ||
|
|
a0afb6ec8e | ||
|
|
549deb51d6 | ||
|
|
4f842d9f1b | ||
|
|
9627fe6be6 | ||
|
|
b8ddb11796 | ||
|
|
f04cc227a6 | ||
|
|
811bc1b8e5 | ||
|
|
ccb291ce66 | ||
|
|
676f5c4b31 | ||
|
|
14083a0857 | ||
|
|
b4f5b5556f | ||
|
|
fa4f9197ee | ||
|
|
bfb5a678d2 | ||
|
|
d2572315ca | ||
|
|
467b774d13 | ||
|
|
78a1424582 | ||
|
|
82d6aa0c97 | ||
|
|
1213578eb7 | ||
|
|
268d027770 | ||
|
|
f55fbe037a | ||
|
|
3a95ff7435 | ||
|
|
d70715a635 | ||
|
|
54a9058ee0 | ||
|
|
efb5256d28 | ||
|
|
a4c0fead1f | ||
|
|
35775b3bc5 | ||
|
|
7417d8e86e | ||
|
|
df84c466c1 | ||
|
|
bf3a8c6383 | ||
|
|
5605e46acb | ||
|
|
3346c7f455 | ||
|
|
b78b2b6b35 | ||
|
|
0f43d5df6a | ||
|
|
30b9d7f00e | ||
|
|
b9f49cee45 | ||
|
|
c144580c98 | ||
|
|
2ff8c12bf9 | ||
|
|
335cffe9a9 | ||
|
|
bc40ab378c | ||
|
|
d81276607c | ||
|
|
3bc3b34d97 | ||
|
|
f0a1d69f50 | ||
|
|
0672936134 | ||
|
|
3632478b8d | ||
|
|
c07fa70d58 | ||
|
|
ee7f2413ed | ||
|
|
12ebf21be5 | ||
|
|
691eea9b45 | ||
|
|
7e6f3364bc | ||
|
|
3f430627df | ||
|
|
3160c03843 | ||
|
|
420a3f4a01 | ||
|
|
9eb48312c7 | ||
|
|
469caa1a14 | ||
|
|
3aca8a938c | ||
|
|
01d46a1751 | ||
|
|
9c859d2655 | ||
|
|
fb5dbe13c2 | ||
|
|
e4c359d8b9 | ||
|
|
52256d7a73 | ||
|
|
aa974c0dc3 | ||
|
|
23d3539fcb | ||
|
|
bcff7274f4 | ||
|
|
e9deaf2ca5 | ||
|
|
e1bceb2adb | ||
|
|
34428fc709 | ||
|
|
393274d142 | ||
|
|
a8e08d14bb | ||
|
|
bf9b9026d9 | ||
|
|
0f096f9ad4 | ||
|
|
db55e596d2 | ||
|
|
82c5aa82d2 | ||
|
|
742d1889c5 | ||
|
|
61042c7606 | ||
|
|
3e4f7228a0 | ||
|
|
76e0e09aca | ||
|
|
e4deffcbe8 | ||
|
|
de5f902487 | ||
|
|
cab9237d95 | ||
|
|
6ecf9e091c | ||
|
|
aa69598b57 | ||
|
|
f47084968d | ||
|
|
6a8e8e92a7 | ||
|
|
c7edde6ca4 | ||
|
|
379e2c694b | ||
|
|
29d5f5d760 | ||
|
|
258bc85b9c | ||
|
|
fc1d4f5362 | ||
|
|
cda222d2ec | ||
|
|
07b4228988 | ||
|
|
ad1eee7aa5 | ||
|
|
9c1b3735b4 | ||
|
|
ab0338f6ae | ||
|
|
39fc16954b | ||
|
|
f5289c546e | ||
|
|
721f924e15 | ||
|
|
6611c16099 | ||
|
|
2c5151726c | ||
|
|
5eff7f38df | ||
|
|
e9ca4305a6 | ||
|
|
125deafc84 | ||
|
|
1445673e18 | ||
|
|
4a17d8ef97 | ||
|
|
b4fff6b9b7 | ||
|
|
2245b0ac94 | ||
|
|
e561beab44 | ||
|
|
3bcd9d747b | ||
|
|
85a71a3923 | ||
|
|
a30751464a | ||
|
|
1678548353 | ||
|
|
7218c45443 | ||
|
|
bd914d49f1 | ||
|
|
fed112e497 | ||
|
|
c3be4c4629 | ||
|
|
286d678785 | ||
|
|
42ae135d38 | ||
|
|
42f5d4404d | ||
|
|
5c098dc7ad | ||
|
|
3dba6fc95c | ||
|
|
1eee82272a | ||
|
|
da5b7bea78 | ||
|
|
910cdc0537 | ||
|
|
02a501705a | ||
|
|
a9d9fb5d75 | ||
|
|
ffbc7e723d | ||
|
|
3ef56576d3 | ||
|
|
cde8e02bf2 | ||
|
|
1d24e96074 | ||
|
|
be432c8654 | ||
|
|
3778a69fbe | ||
|
|
d807a5c314 | ||
|
|
5862a05fb1 | ||
|
|
dba14bfe90 | ||
|
|
2e257f40e6 | ||
|
|
cdb2bec909 | ||
|
|
dd5061d73b | ||
|
|
7a18a0fb34 | ||
|
|
28d3f74614 | ||
|
|
98c7b23178 | ||
|
|
89dc8c2004 | ||
|
|
e274fc732b |
13
.travis.yml
Normal file
13
.travis.yml
Normal 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
158
CONTRIBUTING.md
Normal 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
66
FAQ.md
Normal 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.
|
||||
2
LICENSE
2
LICENSE
@@ -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
158
README.md
@@ -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/
|
||||
|
||||
16
default.nix
16
default.nix
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
88
home-manager/home-manager.nix
Normal file
88
home-manager/home-manager.nix
Normal 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
37
home-manager/install.nix
Normal 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
|
||||
'';
|
||||
}
|
||||
""
|
||||
@@ -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
269
modules/files.nix
Normal 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
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
20
modules/lib/default.nix
Normal 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
111
modules/lib/file-type.nix
Normal 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;
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
));
|
||||
}
|
||||
@@ -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
11
modules/lib/shell.nix
Normal 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);
|
||||
}
|
||||
@@ -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>
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
41
modules/misc/fontconfig.nix
Normal file
41
modules/misc/fontconfig.nix
Normal 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>
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -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
607
modules/misc/news.nix
Normal 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
146
modules/misc/nixpkgs.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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
96
modules/misc/xdg.nix
Normal 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
100
modules/modules.nix
Normal 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 ]
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
80
modules/programs/browserpass.nix
Normal file
80
modules/programs/browserpass.nix
Normal 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);
|
||||
};
|
||||
}
|
||||
57
modules/programs/command-not-found/command-not-found.nix
Normal file
57
modules/programs/command-not-found/command-not-found.nix
Normal 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 ];
|
||||
};
|
||||
}
|
||||
44
modules/programs/command-not-found/command-not-found.pl
Normal file
44
modules/programs/command-not-found/command-not-found.pl
Normal 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;
|
||||
@@ -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;
|
||||
})
|
||||
];
|
||||
|
||||
@@ -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
41
modules/programs/feh.nix
Normal 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)}
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -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
91
modules/programs/fzf.nix
Normal 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
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
''
|
||||
);
|
||||
|
||||
55
modules/programs/home-manager.nix
Normal file
55
modules/programs/home-manager.nix
Normal 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
327
modules/programs/htop.nix
Normal 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}
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -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" ];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
22
modules/programs/man.nix
Normal 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" ];
|
||||
};
|
||||
}
|
||||
102
modules/programs/mercurial.nix
Normal file
102
modules/programs/mercurial.nix
Normal 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;
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
96
modules/programs/neovim.nix
Normal file
96
modules/programs/neovim.nix
Normal 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;
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
36
modules/programs/pidgin.nix
Normal file
36
modules/programs/pidgin.nix
Normal 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
303
modules/programs/rofi.nix
Normal 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}
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
364
modules/programs/termite.nix
Normal file
364
modules/programs/termite.nix
Normal 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}
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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
180
modules/programs/vim.nix
Normal 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
361
modules/programs/zsh.nix
Normal 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;
|
||||
})
|
||||
]);
|
||||
}
|
||||
37
modules/services/blueman-applet.nix
Normal file
37
modules/services/blueman-applet.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
223
modules/services/compton.nix
Normal file
223
modules/services/compton.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.gnome-keyring = {
|
||||
enable = mkEnableOption "GNOME Keyring";
|
||||
|
||||
@@ -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
67
modules/services/kbfs.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
with lib;
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.keepassx = {
|
||||
enable = mkEnableOption "the KeePassX password manager";
|
||||
|
||||
35
modules/services/keybase.nix
Normal file
35
modules/services/keybase.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
with lib;
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.network-manager-applet = {
|
||||
enable = mkEnableOption "the Network Manager applet";
|
||||
|
||||
29
modules/services/owncloud-client.nix
Normal file
29
modules/services/owncloud-client.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
47
modules/services/parcellite.nix
Normal file
47
modules/services/parcellite.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
151
modules/services/polybar.nix
Normal file
151
modules/services/polybar.nix
Normal 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
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.random-background = {
|
||||
enable = mkEnableOption "random desktop background";
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
|
||||
59
modules/services/screen-locker.nix
Normal file
59
modules/services/screen-locker.nix
Normal 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} &";
|
||||
};
|
||||
|
||||
}
|
||||
94
modules/services/stalonetray.nix
Normal file
94
modules/services/stalonetray.nix
Normal 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;
|
||||
})
|
||||
]);
|
||||
}
|
||||
67
modules/services/syncthing.nix
Normal file
67
modules/services/syncthing.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -9,6 +9,8 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.taffybar = {
|
||||
enable = mkEnableOption "Taffybar";
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
with lib;
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
services.tahoe-lafs = {
|
||||
enable = mkEnableOption "Tahoe-LAFS";
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
63
modules/services/unclutter.nix
Normal file
63
modules/services/unclutter.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
779
modules/services/window-managers/i3.nix
Normal file
779
modules/services/window-managers/i3.nix
Normal 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
|
||||
);
|
||||
})
|
||||
]);
|
||||
}
|
||||
114
modules/services/window-managers/xmonad.nix
Normal file
114
modules/services/window-managers/xmonad.nix
Normal 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
|
||||
'';
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -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
189
modules/systemd-activate.rb
Normal 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
114
modules/systemd-activate.sh
Normal 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
|
||||
@@ -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
76
modules/xcursor.nix
Normal 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;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -17,6 +17,8 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
options = {
|
||||
xresources.properties = mkOption {
|
||||
type = types.nullOr types.attrs;
|
||||
|
||||
@@ -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
57
nixos/default.nix
Normal 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
3
overlay.nix
Normal file
@@ -0,0 +1,3 @@
|
||||
self: super: {
|
||||
home-manager = import ./home-manager { pkgs = super; };
|
||||
}
|
||||
Reference in New Issue
Block a user