Initial import

This commit is contained in:
Robert Helgesson 2017-01-07 19:16:26 +01:00
parent e4c63eb66a
commit d7d02c3ce8
No known key found for this signature in database
GPG key ID: C3DB11069E65DC86
26 changed files with 1749 additions and 0 deletions

2
default.nix Normal file
View file

@ -0,0 +1,2 @@
# Simply defer to the home-manager script derivation.
import ./home-manager

39
home-manager/default.nix Normal file
View file

@ -0,0 +1,39 @@
{ pkgs }:
let
homeManagerExpr = pkgs.writeText "home-manager.nix" ''
{ pkgs ? import <nixpkgs> {}, confPath, modulesPath }:
let
env = import modulesPath {
configuration = import confPath;
pkgs = pkgs;
};
in
{
inherit (env) activation-script;
}
'';
in
pkgs.stdenv.mkDerivation {
name = "home-manager";
phases = [ "installPhase" ];
installPhase = ''
install -v -D -m755 ${./home-manager} $out/bin/home-manager
substituteInPlace $out/bin/home-manager \
--subst-var-by bash "${pkgs.bash}" \
--subst-var-by HOME_MANAGER_EXPR_PATH "${homeManagerExpr}"
'';
meta = with pkgs.stdenv.lib; {
description = "A user environment configurator";
maintainers = [ maintainers.rycee ];
platforms = platforms.linux;
};
}

62
home-manager/home-manager Normal file
View file

@ -0,0 +1,62 @@
#!@bash@/bin/bash
function doRebuild() {
if [[ -z "$1" ]] ; then
echo "Need to provide path to configuration file."
exit 1
fi
local wrkdir
wrkdir="$(mktemp -d)"
nix-build --show-trace \
"@HOME_MANAGER_EXPR_PATH@" \
--argstr modulesPath "$HOME/.nixpkgs/home-manager/modules" \
--argstr confPath "$1" \
-A activation-script \
-o "$wrkdir/activate"
"$wrkdir/activate/libexec/home-activate"
rm -rv "$wrkdir"
}
function doListGens() {
ls --color=yes -gG --sort time "/nix/var/nix/gcroots/per-user/$(whoami)" \
| cut -d' ' -f 4-
}
function doListPackages() {
local outPath
outPath="$(nix-env -q --out-path | grep -o '/.*home-manager-path$')"
nix-store -q --references "$outPath" | sed 's/[^-]*-//'
}
function doHelp() {
echo "Usage: $0 {help | rebuild CONF | generations | packages}"
echo
echo "Commands"
echo " help Print this help"
echo " rebuild Rebuild the current environment"
echo " generations List all home environment generations"
echo " packages List all packages installed in home-manager-path"
}
case $1 in
rebuild)
doRebuild "$2"
;;
generations)
doListGens
;;
packages)
doListPackages
;;
help|--help)
doHelp
;;
*)
echo "Unknown command: $1"
doHelp
exit 1
esac

47
modules/default.nix Normal file
View file

@ -0,0 +1,47 @@
{ configuration
, pkgs
, lib ? pkgs.stdenv.lib
}:
let
modules = [
./home-environment.nix
./programs/bash.nix
./programs/beets.nix
./programs/eclipse.nix
./programs/emacs.nix
./programs/git.nix
./programs/gnome-terminal.nix
./programs/lesspipe.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/taffybar.nix
./services/tahoe-lafs.nix
./services/udiskie.nix
./services/xscreensaver.nix
./systemd.nix
./xsession.nix
];
pkgsModule = {
config._module.args.pkgs = lib.mkForce pkgs;
};
module = lib.evalModules {
modules = [ configuration ] ++ modules ++ [ pkgsModule ];
};
in
{
inherit (module) options config;
activation-script = module.config.home.activationPackage;
home-path = module.config.home.path;
}

View file

@ -0,0 +1,314 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home;
languageSubModule = types.submodule {
options = {
base = mkOption {
type = types.str;
description = ''
The language to use unless overridden by a more specific option.
'';
};
address = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The language to use for addresses.
'';
};
monetary = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The language to use for formatting currencies and money amounts.
'';
};
paper = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The language to use for paper sizes.
'';
};
time = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The language to use for formatting times.
'';
};
};
};
keyboardSubModule = types.submodule {
options = {
layout = mkOption {
type = types.str;
default = "us";
description = ''
Keyboard layout.
'';
};
model = mkOption {
type = types.str;
default = "pc104";
example = "presario";
description = ''
Keyboard model.
'';
};
options = mkOption {
type = types.listOf types.str;
default = [];
example = ["grp:caps_toggle" "grp_led:scroll"];
description = ''
X keyboard options; layout switching goes here.
'';
};
variant = mkOption {
type = types.str;
default = "";
example = "colemak";
description = ''
X keyboard variant.
'';
};
};
};
in
{
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 $HOME.";
};
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.";
};
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.language = mkOption {
type = languageSubModule;
default = {};
description = "Language configuration.";
};
home.keyboard = mkOption {
type = keyboardSubModule;
default = {};
description = "Keyboard configuration.";
};
home.sessionVariables = mkOption {
default = {};
type = types.attrs;
description = "Environment variables to always set at login.";
};
home.packages = mkOption {
type = types.listOf types.package;
default = [];
description = "The set of packages to appear in the user environment.";
};
home.path = mkOption {
internal = true;
description = "The derivation installing the user packages.";
};
home.activation = mkOption {
internal = true;
default = {};
type = types.attrs;
description = "Activation scripts for the home environment.";
};
home.activationPackage = mkOption {
internal = true;
type = types.package;
description = "The package containing the complete activation script.";
};
};
config = {
home.sessionVariables =
let
maybeSet = name: value:
listToAttrs (optional (value != null) { inherit name value; });
in
(maybeSet "LANG" cfg.language.base)
//
(maybeSet "LC_ADDRESS" cfg.language.address)
//
(maybeSet "LC_MONETARY" cfg.language.monetary)
//
(maybeSet "LC_PAPER" cfg.language.paper)
//
(maybeSet "LC_TIME" cfg.language.time);
home.activation.linkages =
let
pak = pkgs.stdenv.mkDerivation {
name = "home-environment";
phases = [ "installPhase" ];
installPhase =
concatStringsSep "\n" (
mapAttrsToList (name: value:
"install -v -D -m${value.mode} ${value.source} $out/${value.target}"
) cfg.file
);
};
link = pkgs.writeText "link" ''
for sourcePath in "$@" ; do
basePath="''${sourcePath#/nix/store/*-home-environment/}"
targetPath="$HOME/$basePath"
mkdir -vp "$(dirname "$targetPath")"
ln -vsf "$sourcePath" "$targetPath"
done
'';
cleanup = pkgs.writeText "cleanup" ''
for sourcePath in "$@" ; do
basePath="''${sourcePath#/nix/store/*-home-environment/}"
targetPath="$HOME/$basePath"
echo -n "Checking $targetPath"
if [[ -f "${pak}/$basePath" ]] ; then
echo " exists"
else
echo " gone (deleting)"
rm -v "$targetPath"
rmdir --ignore-fail-on-non-empty -v -p "$(dirname "$targetPath")"
fi
done
'';
in
''
function setupVars() {
local gcPath="/nix/var/nix/gcroots/per-user/$(whoami)"
local greatestGenNum=( \
$(find "$gcPath" -name 'home-*' \
| sed 's/^.*-\([0-9]*\)$/\1/' \
| sort -rn \
| head -1) \
)
if [[ -n "$greatestGenNum" ]] ; then
oldGenNum=$greatestGenNum
newGenNum=$(($oldGenNum + 1))
oldGenPath="$(readlink -e "$gcPath/home-$oldGenNum")"
else
newGenNum=1
fi
newGenPath="${pak}";
newGenGcPath="$gcPath/home-$newGenNum"
}
# Set some vars, these can be used later on as well.
setupVars
if [[ "$oldGenPath" != "$newGenPath" ]] ; then
ln -sfv "$newGenPath" "$newGenGcPath"
find "$newGenPath" -type f -print0 | xargs -0 bash ${link}
if [[ -n "$oldGenPath" ]] ; then
echo "Cleaning up orphan links from $HOME"
find "$oldGenPath" -type f -print0 | xargs -0 bash ${cleanup}
fi
else
echo "Same home files as previous generation ... doing nothing"
fi
'';
home.activation.installPackages =
''
nix-env -i ${cfg.path}
'';
home.activationPackage =
let
addHeader = n: v:
v // {
text = ''
echo Activating ${n}
${v.text}
'';
};
toDepString = n: v: if isString v then noDepEntry v else v;
activationWithDeps =
mapAttrs addHeader (mapAttrs toDepString cfg.activation);
activationCmds =
textClosureMap id activationWithDeps (attrNames activationWithDeps);
sf = pkgs.writeText "activation-script" ''
#!${pkgs.stdenv.shell}
${activationCmds}
'';
in
pkgs.stdenv.mkDerivation {
name = "activate-home";
phases = [ "installPhase" ];
installPhase = ''
install -v -D -m755 ${sf} $out/libexec/home-activate
'';
};
home.path = pkgs.buildEnv {
name = "home-manager-path";
paths = cfg.packages;
meta = {
description = "Environment of packages installed through home-manager";
};
};
};
}

View file

@ -0,0 +1,93 @@
/* 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;
}

159
modules/programs/bash.nix Normal file
View file

@ -0,0 +1,159 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.bash;
in
{
options = {
programs.bash = {
enable = mkEnableOption "GNU Bourne-Again SHell";
historySize = mkOption {
type = types.int;
default = 10000;
description = "Number of history lines to keep in memory.";
};
historyFileSize = mkOption {
type = types.int;
default = 100000;
description = "Number of history lines to keep on file.";
};
historyControl = mkOption {
type = types.listOf (types.enum [
"erasedups"
"ignoredups"
"ignorespace"
]);
default = [];
description = "Controlling how commands are saved on the history list.";
};
historyIgnore = mkOption {
type = types.listOf types.str;
default = [];
description = "List of commands that should not be saved to the history list.";
};
shellOptions = mkOption {
type = types.listOf types.str;
default = [
# Append to history file rather than replacing it.
"histappend"
# check the window size after each command and, if
# necessary, update the values of LINES and COLUMNS.
"checkwinsize"
# Extended globbing.
"extglob"
"globstar"
# Warn if closing shell with running jobs.
"checkjobs"
];
description = "Shell options to set.";
};
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. The
aliases are added to all users' shells.
'';
type = types.attrs;
};
enableAutojump = mkOption {
default = false;
type = types.bool;
description = "Enable the autojump navigation tool.";
};
profileExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to .profile.";
};
initExtra = mkOption {
default = "";
type = types.lines;
description = "Extra commands that should be added to .bashrc.";
};
};
};
config = (
let
aliasesStr = concatStringsSep "\n" (
mapAttrsToList (k: v: "alias ${k}='${v}'") cfg.shellAliases
);
shoptsStr = concatStringsSep "\n" (
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);
histControlStr = concatStringsSep ":" cfg.historyControl;
histIgnoreStr = concatStringsSep ":" cfg.historyIgnore;
envVarsStr = concatStringsSep "\n" (
mapAttrsToList export config.home.sessionVariables
);
in mkIf cfg.enable {
home.file.".bash_profile".text = ''
# -*- mode: sh -*-
# include .profile if it exists
[[ -f ~/.profile ]] && . ~/.profile
# include .bashrc if it exists
[[ -f ~/.bashrc ]] && . ~/.bashrc
'';
home.file.".profile".text = ''
# -*- mode: sh -*-
${envVarsStr}
${cfg.profileExtra}
'';
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"}
'';
home.packages =
optional (cfg.enableAutojump) pkgs.autojump;
}
);
}

View file

@ -0,0 +1,28 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.beets;
in
{
options = {
programs.beets = {
settings = mkOption {
type = types.attrs;
default = {};
description = "Configuration written to ~/.config/beets/config.yaml";
};
};
};
config = mkIf (cfg.settings != {}) {
home.packages = [ pkgs.beets ];
home.file.".config/beets/config.yaml".text =
builtins.toJSON config.programs.beets.settings;
};
}

View file

@ -0,0 +1,39 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.eclipse;
in
{
options = {
programs.eclipse = {
enable = mkEnableOption "Eclipse";
jvmArgs = mkOption {
type = types.listOf types.str;
default = [];
description = "JVM arguments to use for the Eclipse process.";
};
plugins = mkOption {
type = types.listOf types.package;
default = [];
description = "Plugins that should be added to Eclipse.";
};
};
};
config = mkIf cfg.enable {
home.packages = [
(pkgs.eclipses.eclipseWithPlugins {
eclipse = pkgs.eclipses.eclipse-platform;
jvmArgs = cfg.jvmArgs;
plugins = cfg.plugins;
})
];
};
}

View file

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.emacs;
in
{
options = {
programs.emacs = {
enable = mkEnableOption "Emacs";
extraPackages = mkOption {
default = self: [];
example = literalExample ''
epkgs: [ epkgs.emms epkgs.magit ]
'';
description = "Extra packages available to Emacs.";
};
};
};
config = mkIf cfg.enable {
home.packages = [ (pkgs.emacsWithPackages cfg.extraPackages) ];
};
}

102
modules/programs/git.nix Normal file
View file

@ -0,0 +1,102 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.git;
toINI = (import ../lib/generators.nix).toINI {};
signModule = types.submodule (
{ ... }: {
options = {
key = mkOption {
type = types.str;
default = null;
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
{
options = {
programs.git = {
enable = mkEnableOption "Git";
package = mkOption {
type = types.package;
default = pkgs.git;
defaultText = "pkgs.git";
description = "Git 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 = "Git aliases to define.";
};
signing = mkOption {
type = types.nullOr signModule;
default = null;
description = "Options related to signing commits using GnuPG.";
};
extraConfig = mkOption {
type = types.lines;
default = null;
description = "Additional configuration to add.";
};
};
};
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
{
home.packages = [ cfg.package ];
home.file.".gitconfig".text = toINI ini + "\n" + cfg.extraConfig;
}
);
}

View file

@ -0,0 +1,186 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.gnome-terminal;
profileColorsSubModule = types.submodule (
{ ... }: {
options = {
foregroundColor = mkOption {
type = types.str;
description = "The foreground color.";
};
backgroundColor = mkOption {
type = types.str;
description = "The background color.";
};
boldColor = mkOption {
default = null;
type = types.nullOr types.str;
description = "The bold color, null to use same as foreground.";
};
palette = mkOption {
type = types.listOf types.str;
description = "The terminal palette.";
};
};
}
);
profileSubModule = types.submodule (
{ name, config, ... }: {
options = {
default = mkOption {
default = false;
type = types.bool;
description = "Whether this should be the default profile.";
};
visibleName = mkOption {
type = types.str;
description = "The profile name.";
};
colors = mkOption {
default = null;
type = types.nullOr profileColorsSubModule;
description = "The terminal colors, null to use system default.";
};
cursorShape = mkOption {
default = "block";
type = types.enum [ "block" "ibeam" "underline" ];
description = "The cursor shape.";
};
font = mkOption {
default = null;
type = types.nullOr types.str;
description = "The font name, null to use system default.";
};
scrollOnOutput = mkOption {
default = true;
type = types.bool;
description = "Whether to scroll when output is written.";
};
showScrollbar = mkOption {
default = true;
type = types.bool;
description = "Whether the scroll bar should be visible.";
};
scrollbackLines = mkOption {
default = 10000;
type = types.nullOr types.int;
description =
''
The number of scrollback lines to keep, null for infinite.
'';
};
};
}
);
toINI = (import ../lib/generators.nix).toINI { mkKeyValue = mkIniKeyValue; };
mkIniKeyValue = key: value:
let
tweakVal = v:
if isString v then "'${v}'"
else if isList v then "[" + concatStringsSep "," (map tweakVal v) + "]"
else if isBool v && v then "true"
else if isBool v && !v then "false"
else toString v;
in
"${key}=${tweakVal value}";
buildProfileSet = pcfg:
{
visible-name = pcfg.visibleName;
scrollbar-policy = if pcfg.showScrollbar then "always" else "never";
scrollback-lines = pcfg.scrollbackLines;
cursor-shape = pcfg.cursorShape;
}
// (
if (pcfg.font == null)
then { use-system-font = true; }
else { use-system-font = false; font = pcfg.font; }
) // (
if (pcfg.colors == null)
then { use-theme-colors = true; }
else (
{
use-theme-colors = false;
foreground-color = pcfg.colors.foregroundColor;
background-color = pcfg.colors.backgroundColor;
palette = pcfg.colors.palette;
}
// (
if (pcfg.colors.boldColor == null)
then { bold-color-same-as-fg = true; }
else {
bold-color-same-as-fg = false;
bold-color = pcfg.colors.boldColor;
}
)
)
);
buildIniSet = cfg:
{
"/" = {
default-show-menubar = cfg.showMenubar;
schema-version = 3;
};
}
//
{
"profiles:" = {
default = head (attrNames (filterAttrs (n: v: v.default) cfg.profile));
list = attrNames cfg.profile; #mapAttrsToList (n: v: n) cfg.profile;
};
}
//
mapAttrs' (name: value:
nameValuePair ("profiles:/:${name}") (buildProfileSet value)
) cfg.profile;
in
{
options = {
programs.gnome-terminal = {
enable = mkEnableOption "Gnome Terminal";
showMenubar = mkOption {
default = true;
type = types.bool;
description = "Whether to show the menubar by default";
};
profile = mkOption {
default = {};
type = types.loaOf profileSubModule;
};
};
};
config = mkIf cfg.enable {
home.activation.gnome-terminal =
let
sf = pkgs.writeText "gnome-terminal.ini" (toINI (buildIniSet cfg));
dconfPath = "/org/gnome/terminal/legacy/";
in
''
${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} < ${sf}
'';
};
}

View file

@ -0,0 +1,17 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
programs.lesspipe = {
enable = mkEnableOption "lesspipe preprocessor for less";
};
};
config = mkIf config.programs.lesspipe.enable {
home.sessionVariables = {
LESSOPEN = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
};
};
}

View file

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.texlive;
in
{
options = {
programs.texlive = {
enable = mkEnableOption "Texlive";
extraPackages = mkOption {
default = self: {};
example = literalExample ''
tpkgs: { inherit (tpkgs) collection-fontsrecommended algorithms; }
'';
description = "Extra packages available to Texlive.";
};
};
};
config = mkIf cfg.enable {
home.packages = [
(pkgs.texlive.combine (cfg.extraPackages pkgs.texlive))
];
};
}

View file

@ -0,0 +1,35 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.dunst = {
enable = mkEnableOption "the dunst notification daemon";
settings = mkOption {
type = types.attrs;
default = {};
description = "Configuration written to ~/.config/dunstrc";
};
};
};
config = mkIf config.services.dunst.enable {
systemd.user.services.dunst = {
Unit = {
Description = "Dunst notification daemon";
};
Install = {
WantedBy = [ "xorg.target" ];
};
Service = {
# Type = "dbus";
# BusName = "org.freedesktop.Notifications";
ExecStart = "${pkgs.dunst}/bin/dunst";
};
};
};
}

View file

@ -0,0 +1,52 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.gnome-keyring;
in
{
options = {
services.gnome-keyring = {
enable = mkEnableOption "GNOME Keyring";
components = mkOption {
type = types.listOf (types.enum ["pkcs11" "secrets" "ssh"]);
default = [];
description = ''
The GNOME keyring components to start. If empty then the
default set of components will be started.
'';
};
};
};
config = mkIf cfg.enable {
systemd.user.services.gnome-keyring = {
Unit = {
Description = "GNOME Keyring";
};
Service = {
ExecStart =
let
args = concatStringsSep " " (
[ "--start" "--foreground" ]
++ optional (cfg.components != []) (
"--components=" + concatStringsSep "," cfg.components
)
);
in
"${pkgs.gnome3.gnome_keyring}/bin/gnome-keyring-daemon ${args}";
Restart = "on-abort";
};
Install = {
WantedBy = [ "xorg.target" ];
};
};
};
}

View file

@ -0,0 +1,69 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.gpg-agent;
in
{
options = {
services.gpg-agent = {
enable = mkEnableOption "GnuPG private key agent";
defaultCacheTtl = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Set the time a cache entry 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.";
};
};
};
config = mkIf cfg.enable {
home.file.".gnupg/gpg-agent.conf".text = concatStringsSep "\n" (
optional cfg.enableSshSupport
"enable-ssh-support"
++
optional (cfg.defaultCacheTtl != null)
"default-cache-ttl ${toString cfg.defaultCacheTtl}"
);
home.sessionVariables =
optionalAttrs cfg.enableSshSupport {
SSH_AUTH_SOCK = "\${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh";
};
programs.bash.initExtra = ''
GPG_TTY="$(tty)"
export GPG_TTY
gpg-connect-agent updatestartuptty /bye > /dev/null
'';
systemd.user.services.gpg-agent = {
Unit = {
Description = "GnuPG private key agent";
IgnoreOnIsolate = true;
};
Service = {
Type = "forking";
ExecStart = "${pkgs.gnupg}/bin/gpg-agent --daemon --use-standard-socket";
Restart = "on-abort";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

View file

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.keepassx = {
enable = mkEnableOption "the KeePassX password manager";
};
};
config = mkIf config.services.keepassx.enable {
systemd.user.services.keepassx = {
Unit = {
Description = "KeePassX password manager";
};
Install = {
WantedBy = [ "xorg.target" ];
};
Service = {
ExecStart = "${pkgs.keepassx}/bin/keepassx -min -lock";
};
};
};
}

View file

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.network-manager-applet = {
enable = mkEnableOption "the Network Manager applet";
};
};
config = mkIf config.services.network-manager-applet.enable {
systemd.user.services.network-manager-applet = {
Unit = {
Description = "Network Manager applet";
};
Install = {
WantedBy = [ "xorg.target" ];
};
Service = {
ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet --sm-disable";
};
};
};
}

View file

@ -0,0 +1,74 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.random-background;
in
{
options = {
services.random-background = {
enable = mkEnableOption "random desktop background";
imageDirectory = mkOption {
type = types.str;
description =
''
The directory of images from which a background should be
chosen. Should be formatted in a way understood by
systemd, e.g., '%h' is the home directory.
'';
};
interval = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The duration between changing background image, set to null
to only set background when logging in.
Should be formatted as a duration understood by systemd.
'';
};
};
};
config = mkIf cfg.enable (
mkMerge ([
{
systemd.user.services.random-background = {
Unit = {
Description = "Set random desktop background using feh";
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.feh}/bin/feh --randomize --bg-fill %h/backgrounds/";
IOSchedulingClass = "idle";
};
Install = {
WantedBy = [ "xorg.target" ];
};
};
}
(mkIf (cfg.interval != null) {
systemd.user.timers.random-background = {
Unit = {
Description = "Set random desktop background using feh";
};
Timer = {
OnUnitActiveSec = cfg.interval;
};
Install = {
WantedBy = [ "timers.target" ];
};
};
})
]));
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.taffybar;
in
{
options = {
services.taffybar = {
enable = mkEnableOption "Taffybar";
package = mkOption {
default = pkgs.taffybar;
defaultText = "pkgs.taffybar";
type = types.package;
example = literalExample "pkgs.taffybar";
description = "The package to use for the Taffybar binary.";
};
};
};
config = mkIf config.services.taffybar.enable {
systemd.user.services.taffybar = {
Unit = {
Description = "Taffybar desktop bar";
};
Service = {
ExecStart = "${cfg.package}/bin/taffybar";
};
Install = {
WantedBy = [ "xorg.target" ];
};
};
};
}

View file

@ -0,0 +1,23 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.tahoe-lafs = {
enable = mkEnableOption "Tahoe-LAFS";
};
};
config = mkIf config.services.tahoe-lafs.enable {
systemd.user.services.tahoe-lafs = {
Unit = {
Description = "Tahoe-LAFS";
};
Service = {
ExecStart = "${pkgs.tahoelafs}/bin/tahoe run -C %h/.tahoe";
};
};
};
}

View file

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.udiskie = {
enable = mkEnableOption "Udiskie mount daemon";
};
};
config = mkIf config.services.udiskie.enable {
systemd.user.services.udiskie = {
Unit = {
Description = "Udiskie mount daemon";
Requires = [ "taffybar.service" ];
After = [ "taffybar.service" ];
};
Service = {
ExecStart = "${pkgs.pythonPackages.udiskie}/bin/udiskie -2 -A -n -s";
};
Install = {
WantedBy = [ "xorg.target" ];
};
};
};
}

View file

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
with lib;
{
options = {
services.xscreensaver = {
enable = mkEnableOption "XScreenSaver";
};
};
config = mkIf config.services.xscreensaver.enable {
systemd.user.services.xscreensaver = {
Unit = {
Description = "XScreenSaver";
};
Service = {
ExecStart = "${pkgs.xscreensaver}/bin/xscreensaver -no-splash";
};
Install = {
WantedBy = [ "xorg.target" ];
};
};
};
}

119
modules/systemd.nix Normal file
View file

@ -0,0 +1,119 @@
{ config, lib, pkgs, ... }:
with lib;
let
toSystemdIni = (import lib/generators.nix).toINI {
mkKeyValue = key: value:
let
value' =
if isBool value then (if value then "true" else "false")
else toString value;
in
"${key}=${value'}";
};
buildService = style: name: serviceCfg:
let
source = pkgs.writeText "${name}.${style}" (toSystemdIni serviceCfg);
wantedBy = target:
{
name = ".config/systemd/user/${target}.wants/${name}.${style}";
value = { inherit source; };
};
in
singleton {
name = ".config/systemd/user/${name}.${style}";
value = { inherit source; };
}
++
map wantedBy (serviceCfg.Install.WantedBy or []);
buildServices = style: serviceCfgs:
concatLists (mapAttrsToList (buildService style) serviceCfgs);
in
{
options = {
systemd.user = {
services = mkOption {
default = {};
type = types.attrs;
description = "Definition of systemd per-user service units.";
};
timers = mkOption {
default = {};
type = types.attrs;
description = "Definition of systemd per-user timers";
};
};
};
config = {
home.file =
listToAttrs (
(buildServices "service" config.systemd.user.services)
++
(buildServices "timer" config.systemd.user.timers)
);
home.activation.reloadSystemD = stringAfter ["linkages"] ''
function systemdPostReload() {
local servicesDiffFile="$(mktemp)"
diff \
--new-line-format='+%L' \
--old-line-format='-%L' \
--unchanged-line-format=' %L' \
<(basename -a $(echo "$oldGenPath/.config/systemd/user/*.service") | sort) \
<(basename -a $(echo "$newGenPath/.config/systemd/user/*.service") | sort) \
> $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 \
"$oldGenPath/.config/systemd/user/$f" \
"$newGenPath/.config/systemd/user/$f" ; then
echo "Adding '$f' to restart list";
toRestart+=("$f")
fi
done
rm $servicesDiffFile
sugg=""
if [[ -n "''${toRestart[@]}" ]] ; then
sugg="$sugg\nsystemctl --user restart ''${toRestart[@]}"
fi
if [[ -n "''${toStop[@]}" ]] ; then
sugg="$sugg\nsystemctl --user stop ''${toStop[@]}"
fi
if [[ -n "''${toStart[@]}" ]] ; then
sugg="$sugg\nsystemctl --user start ''${toStart[@]}"
fi
if [[ -n "$sugg" ]] ; then
echo "Suggested commands:"
echo -e "$sugg"
fi
}
if [[ "$oldGenPath" != "$newGenPath" ]] ; then
systemctl --user daemon-reload
systemdPostReload
fi
'';
};
}

77
modules/xsession.nix Normal file
View file

@ -0,0 +1,77 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.xsession;
in
{
options = {
xsession = {
enable = mkEnableOption "X Session";
windowManager = mkOption {
default = {};
type = types.str;
description = "Path to window manager to exec.";
};
initExtra = mkOption {
type = types.lines;
default = "";
description = "Extra shell commands to run during initialization.";
};
};
};
config = mkIf cfg.enable {
systemd.user.services.setxkbmap = {
Unit = {
Description = "Set up keyboard in X";
};
Install = {
WantedBy = [ "xorg.target" ];
};
Service = {
Type = "oneshot";
ExecStart =
let
args = concatStringsSep " " (
[
"-layout '${config.home.keyboard.layout}'"
"-variant '${config.home.keyboard.variant}'"
] ++
(map (v: "-option '${v}'") config.home.keyboard.options)
);
in
"${pkgs.xorg.setxkbmap}/bin/setxkbmap ${args}";
};
};
home.file.".xsession" = {
mode = "555";
text = ''
# Rely on Bash to set session variables.
. "$HOME/.profile"
systemctl --user import-environment DBUS_SESSION_BUS_ADDRESS
systemctl --user import-environment DISPLAY
systemctl --user import-environment SSH_AUTH_SOCK
systemctl --user import-environment XDG_DATA_DIRS
systemctl --user import-environment XDG_RUNTIME_DIR
systemctl --user start xorg.target
${cfg.initExtra}
${cfg.windowManager}
systemctl --user stop xorg.target
'';
};
};
}